I need to be able to send an email from a silverlight client-side application.
I've got this working by implementing a webservice which is consumed by the application.
The problem is that now I need to be able to add an attachment to the emails that are being sent.
I have read various posts, tried a dozen times to figure it out by myself, but to no prevail.
So now I find myself wondering if this is even possible?
The main issue is that the collection of attachments needs to be serializable. So, going by this, ObservableCollection - of type(FileInfo) is not working, ObservableCollection - of type (object) is not working... I've tried using List - of type(Stream), which serializes, but then i do not know how to create the file on the webservice side, as the stream-object does not have a name (which is the first thing I tried to assign to the Attachment object which will then be added to the message.attachments)... I'm kind of stuck in a rut here.
Can anybody maybe shed some light on this please?
I figured out how to do this, and it wasn't really as difficult as it appeared.
Create the following in your webservice-namespace:
`
[Serializable]
public class MyAttachment
{
[DataMember]
public string Name { get; set; }
[DataMember]
public byte[] Bytes { get; set; }
}`
Then add the following to your web-method parameters:
MyAttachment[] attachment
Add the following in the execution blocks of your web-method:`
foreach (var item in attachment)
{
Stream attachmentStream = new MemoryStream(item.Bytes);
Attachment at = new Attachment(attachmentStream, item.Name);
msg.Attachments.Add(at);
}`
Create the following property (or something similar) at your client-side:
`
private ObservableCollection<ServiceProxy.MyAttachment> _attachmentCollection;
public ObservableCollection<ServiceProxy.MyAttachment> AttachmentCollection
{
get { return _attachmentCollection; }
set { _attachmentCollection = value; NotifyOfPropertyChange(() => AttachmentCollection); }
}`
New up the public property (AttachmentCollection) in the constructor.
Add the following where your OpenFileDialog is supposed to return files:`
if (openFileDialog.File != null)
{
foreach (FileInfo fi in openFileDialog.Files)
{
var tempItem = new ServiceProxy.MyAttachment();
tempItem.Name = fi.Name;
var source = fi.OpenRead();
byte[] byteArray = new byte[source.Length];
fi.OpenRead().Read(byteArray, 0, (int)source.Length);
tempItem.Bytes = byteArray;
source.Close();
AttachmentCollection.Add(tempItem);
}
}`
Then finally where you call your web-method to send the email, add the following (or something similar):
MailSvr.SendMailAsync(FromAddress, ToAddress, Subject, MessageBody, AttachmentCollection);
This works for me, the attachment is sent with the mail, with all of its data exactly like the original file.
Related
If you customize an autoroute part you have the option to recreate the url on each save.
The help text under this option says:
"Automatically regenerate when editing content
This option will cause the Url to automatically be regenerated when you edit existing content and publish it again, otherwise it will always keep the old route, or you have to perform bulk update in the Autoroute admin."
I have digged all around but I cannot find anywhere an "Autoroute admin".
Is it really there?
It was a proposed feature never implemented?
Any idea to do a bulk update even without an Admin page?
Thanks
EDIT after #joshb suggestion...
I have tried to implement a bulk operation in my controller.
var MyContents = _contentManager.Query<MyContentPart, MyContentPartRecord>().List().ToList();
foreach (var MyContent in MyContents) {
var autoroutePart = recipe.ContentItem.As<AutoroutePart>();
autoroutePart.UseCustomPattern = false;
autoroutePart.DisplayAlias = _autorouteService.GenerateAlias(autoroutePart);
_contentManager.Publish(autoroutePart.ContentItem);
}
In this way it recreates all aliases for the types that contain the given part MyContentPart.
With some more work this code can be encapsulated in a command or in a new tab in Alias UI.
After finished the current project I'm doing I will try that...
You could create a module and implement a command that does a bulk update. Shouldn't be too much work if you're comfortable creating modules. You'll need to implement DefaultOrchardCommandHandler and inject IContentManager to get all the parts you're interested in.
Enable Alias UI in the modules section will give you the admin section for managing routes, however I'm not sure what kind of bulk updates it offers
Publishing the ContentItem will do nothing if it is already Published (as it was in my case).
Instead, one could call the PublishAlias method on the AutorouteService. I ended up with a Controller, something like this:
using Orchard;
using Orchard.Autoroute.Models;
using Orchard.Autoroute.Services;
using Orchard.ContentManagement;
using Orchard.Localization;
using Orchard.Security;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
namespace MyNamespace.MyModule.Controllers {
public class AutorouteBulkUpdateController : Controller {
private readonly IOrchardServices _orchardServices;
private readonly IAutorouteService _autorouteService;
private Localizer T { get; set; }
public AutorouteBulkUpdateController(IOrchardServices orchardServices, IAutorouteService autorouteService) {
_orchardServices = orchardServices;
_autorouteService = autorouteService;
T = NullLocalizer.Instance;
}
public ActionResult Index() {
if (!_orchardServices.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage settings"))) {
return new HttpUnauthorizedResult();
}
int count = 0;
IEnumerable<AutoroutePart> contents;
do {
//contents = _orchardServices.ContentManager.Query<AutoroutePart>(VersionOptions.Latest, new string[] { "Page" }).Slice(count * 100, 100).ToList();
contents = _orchardServices.ContentManager.Query<AutoroutePart>(VersionOptions.Latest).Slice(count * 100, 100).ToList();
foreach (var autoroutePart in contents) {
var alias = _autorouteService.GenerateAlias(autoroutePart);
if (autoroutePart.DisplayAlias != alias) {
autoroutePart.UseCustomPattern = false;
autoroutePart.DisplayAlias = alias;
_autorouteService.PublishAlias(autoroutePart);
}
}
_orchardServices.TransactionManager.RequireNew();
_orchardServices.ContentManager.Clear();
count += 1;
} while (contents.Any());
return null;
}
}
}
I've seen a lot of topics about this, but unfortunately I believe that each case is a different case (or most of them), and I really would love some experts opinion about my case in particular since I cannot make my code work even after reading through some of the other topics.
Situation: I am using an Ajax Request call in jQuery to a WebService method I have created in an WebApi project together with a MVC 4 Application.
My WebService controller class looks like the default, like this:
public class AdditionalInfoController : ApiController
{
//GET api/AdditionalInfo
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
//GET api/AdditionalInfo/5
public string Get(int id)
{
return "value";
}
//PUT api/AdditionalInfo/5
public void Put(int id)
{
string test = "";
}
}
My Ajax Request from jQuery looks like this:
function GetAdditionalInfo(obj)
{
var request = jQuery.ajax({
url: "/api/AdditionalInfo/Get",
type: "GET",
data: { id: obj.id },
datatype: "json",
async: false,
beforeSend: function () {
},
complete: function () {
}
})
.done(function (a,b,c) {
alert("Additional info was retrieved successfully!");
})
.fail(function (a,b,c) {
alert("An error happened while trying to get the additional info!");
});
}
My WebAPIConfig file looks like this:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
And last but not least, this is my problem: this error message keeps appearing when I browse the returned data variable in .fail and this is what is written:
"{
"Message":"No HTTP resource was found that matches the request URI 'http://localhost:59096/api/AdditionalInfo/Get?id=1'.",
"MessageDetail":"No type was found that matches the controller named 'AdditionalInfo'."
}"
I would really appreciate it if someone could help me as soon as possible. Thanks in advance!
Best regards,
Mad
Looking at the error looks like Web API is unable to find the controller 'type' AdditionalInfo. Web API uses assemblies resolver to scan through the assemblies and finds out the controller types. In your case for some reason its unable to find your 'AdditionalInfo' controller probably because it has some problem loading the assembly having this controller.
Try the following and see if there are any errors logged in your EventLog. If you notice any errors then probably you should check if your controllers are present in those assemblies.
Make the following change in Web.config to view errors in EventLog
<system.diagnostics>
<trace autoflush="false" indentsize="4">
<listeners>
<add name="myListener"
type="System.Diagnostics.EventLogTraceListener"
initializeData="WebApiDiagnostics" />
</listeners>
</trace>
</system.diagnostics>
In your WebApiConfig.cs, you can do the following:
IAssembliesResolver assembliesResolver = config.Services.GetAssembliesResolver();
ICollection<Assembly> assemblies = assembliesResolver.GetAssemblies();
StringBuilder errorsBuilder = new StringBuilder();
foreach (Assembly assembly in assemblies)
{
Type[] exportedTypes = null;
if (assembly == null || assembly.IsDynamic)
{
// can't call GetExportedTypes on a dynamic assembly
continue;
}
try
{
exportedTypes = assembly.GetExportedTypes();
}
catch (ReflectionTypeLoadException ex)
{
exportedTypes = ex.Types;
}
catch (Exception ex)
{
errorsBuilder.AppendLine(ex.ToString());
}
}
if (errorsBuilder.Length > 0)
{
//Log errors into Event Log
Trace.TraceError(errorsBuilder.ToString());
}
BTW, some of the above code is actually from the DefaultHttpControllerTypesResolver which Web API uses to resolve the controller types.
http://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Http/Dispatcher/DefaultHttpControllerTypeResolver.cs
Edited:
One more scenario where you could hit this problem is if your controller is nested inside another class. This was a bug which was fixed later though.
Ok, so I believe I found out what was going on. I am not entirely certain, but at least my problem got fixed.
Simply by changing what was inside of the "data" field in the Ajax call and I have created a class for an object in the application to hold the whole data. It seems that for some reason the method could not have the syntax "Get(int ID)".
Instead, I did something like "Get( object)" and in the Ajax Request something like "data: obj.ID" and voila, it worked.
Also, since the framework is picky about the names of the REST methods (Get, Post, Put and Delete), I changed the name of the method to something else (like Retrieve or something).
Hopefully this will help someone in the future as well.
Best regards,
Mad
Be sure that you have the same parameter names in your methods (int id) as well as in your WebApiConfig/RouteConfig. Try it by changing
public string Get(int id)
{
return "hello";
}
to
public string Get(int? id = null)
{
return "hello";
}
I had the same problem. with me it happens due to a crush in the visual studio (2012). I had the controller file open in visual studio but it wasn't a part of my solution - I couldn't find him in the controllers directory in the solution explorer.
I just added the file to the solution by right clicking on controllers directory => add => existing item.
that fixed the problem for me.
if that doesn't work maybe try to delete the controller and add a new one with the same code . . .
Im trying to execute/consume a webservice and wondering if I am using the correct data type to return the results. String seems to work, but I receive an empty string. The service should be returning a simple string value without XML. There is a working version written in JS below, I have been asked to recreate it in Apex.
JS version (Working) - executed when a button is clicked
{!REQUIRESCRIPT("/soap/ajax/24.0/connection.js")}
{!REQUIRESCRIPT("/soap/ajax/24.0/apex.js")}
var xfolder = "TestFolder"
var parentid = "22K22"
var myvar = sforce.apex.execute("myWS","invokeWs", {folderName:xfolder,ObjectID:parentid});
window.alert('LiveLink folder created: ' + myvar);
APEX version (not working)
public with sharing class myTest {
public String getWSXMLResult() {
String tmpFolderName2 = 'TestFolder';
String tmpObjectID2 = '22K22';
String myWSXMLResult = myWS.invokeWs(tmpFolderName2,tmpObjectID2);
System.debug('XIXWS|' + myWSXMLResult);
return myWSXMLResult;
}
}
One thing I just noted while typing this out. I didn't specify the argument names for invokeWs, just the values..do I need to specify those values in the call to the WS? Such as..
myWS.invokeWs(folderName=tmpFolderName2,ObjectID=tmpObjectID2); -- this errors out btw
Thanks again everyone.
I'm working on an app that requires extracting data from an xml web service, then I want to store that data (images+titles+datetime ...) to display it on my app then select an item and navigate to another page that displays more info about this item.
Is there a detailed tutorial that explains the parsing and storing process clearly (with the threads) because I'm gonna need it a lot for my app.Thanks!
I usually use this method, but didn't always get me what i want:
var doc = XDocument.Load(new StringReader(e.Result));
var items = from c in doc.Descendants("item")
select new RSSitem()
{
Title = c.Element("title").Value,
Photo = c.Element("img").Attribute("src").Value,
Description = c.Element("description").Value,
Link = c.Element("link").Value,
};
ListBoxNews.ItemsSource = items;
Sounds like you are in over your head (based on the vague nature of your question). So I'm offering my advise to get up to speed, so you can get started and ask a question that we can help give a definitive answer to.
With WP7 and .NET you shouldn't really have to do much manual parsing of Web Services. You should be able to add a Service Reference and generate a proxy which will handle this for you. This will also generate business objects for the data returned by your service.
Once you have that done, you can look into Windows Phone Navigation which should help you transition between pages in your application.
To consume web services:
String baseUri = “your service URI";
WebClient wc = new WebClient();
public MainPage()
{
InitializeComponent();
wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(wc_downloadstringcompleted);
// event handler that will handle the ‘downloadstringsompleted’ event
wc.DownloadStringAsync(new Uri(baseUri));
// this method will download your string URI asynchronously
}
void wc_downloadstringcompleted(Object sender, DownloadStringCompletedEventArgs e)
{
// method will get fired after URI download completes
// writes your every code here
}
To parse the data:
using (XmlReader reader = XmlReader.Create(new StringReader(xmlString)))
{
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
break;
case XmlNodeType.Text:
break;
case XmlNodeType.EndElement:
break;
}
}
}
}
To store in isolated storage: http://msdn.microsoft.com/en-us/library/system.io.isolatedstorage.isolatedstoragesettings%28v=vs.95%29.aspx
For navigation:
NavigationService.Navigate(new Uri("/SecondPage.xaml?msg=" + navigationstring, UriKind.Relative));
We manage several ASP.NET MVC client web sites, which all use a data annotation like the following to validate customer email addresses (I haven't included the regex here, for readability):
[Required(ErrorMessage="Email is required")]
[RegularExpression(#"MYREGEX", ErrorMessage = "Email address is not valid")]
public string Email { get; set; }
What I would like to do is to centralise this regular expression, so that if we make a change to it, all of the sites immediately pick it up and we don't have to manually change it in each one.
The problem is that the regex argument of the data annotation must be a constant, so I cannot assign a value I've retrieved from a config file or database at runtime (which was my first thought).
Can anyone help me with a clever solution to this—or failing that, an alternative approach which will work to achieve the same goal? Or does this just require us to write a specialist custom validation attribute which will accept non-constant values?
The easiest way is to write a custom ValidationAttribute that inherits from RegularExpressionAttribute, so something like:
public class EmailAttribute : RegularExpressionAttribute
{
public EmailAttribute()
: base(GetRegex())
{ }
private static string GetRegex()
{
// TODO: Go off and get your RegEx here
return #"^[\w-]+(\.[\w-]+)*#([a-z0-9-]+(\.[a-z0-9-]+)*?\.[a-z]{2,6}|(\d{1,3}\.){3}\d{1,3})(:\d{4})?$";
}
}
That way, you still maintain use of the built in Regex validation but you can customise it. You'd just simply use it like:
[Email(ErrorMessage = "Please use a valid email address")]
Lastly, to get to client side validation to work, you would simply add the following in your Application_Start method within Global.asax, to tell MVC to use the normal regular expression validation for this validator:
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(EmailAttribute), typeof(RegularExpressionAttributeAdapter));
Checkout ScotGu's [Email] attribute (Step 4: Creating a Custom [Email] Validation Attribute).
Do you really want to put the regex in database/config file, or do you just want to centralise them? If you just want to put the regex together, you can just define and use constants like
public class ValidationRegularExpressions {
public const string Regex1 = "...";
public const string Regex2 = "...";
}
Maybe you want to manage the regexes in external files, you can write a MSBuild task to do the replacement when you build for production.
If you REALLY want to change the validation regex at runtime, define your own ValidationAttribute, like
[RegexByKey("MyKey", ErrorMessage = "Email address is not valid")]
public string Email { get; set; }
It's just a piece of code to write:
public class RegexByKeyAttribute : ValidationAttribute {
public RegexByKey(string key) {
...
}
// override some methods
public override bool IsValid(object value) {
...
}
}
Or even just:
public class RegexByKeyAttribute : RegularExpressionAttribute {
public RegexByKey(string key) : base(LoadRegex(key)) { }
// Be careful to cache the regex is this operation is expensive.
private static string LoadRegex(string key) { ... }
}
Hope it's helpful: http://msdn.microsoft.com/en-us/library/cc668224.aspx
Why not just write you own ValidationAttribute?
http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.validationattribute.aspx
Then you can configure that thing to pull the regex from a registry setting... config file... database... etc... etc..
How to: Customize Data Field Validation in the Data Model Using Custom