I have hundreds of different random URLs coming in, all documents in libs, without any other parameters from different farms and different site collections and sites, goal is to download a file as a binary array from SharePoint.
So e.g. incoming url = http://a.b.c.d.e/f.g/h.i/j/k/l/m.docx .
So how to get the (a) correct site collection root url (b) site root url (c) library root url from this? The only way I now think of is slowly stripping off each part of the url until e.g. .Rootfolder no longer gives an exception... or the other way around slowly adding bits by the first part of the url until rootfolder nog longers gives an exception then query for subwebs etc..
The point is that ClientContext constructor accepts the url of web/site only.
But if the url will be specified in the following format:
http://site/web/documents/file.docx
then the exception System.Net.WebException will occur.
The following example demonstrates how to resolve ClientContext from request Url:
public static class ClientContextUtilities
{
/// <summary>
/// Resolve client context
/// </summary>
/// <param name="requestUri"></param>
/// <param name="context"></param>
/// <param name="credentials"></param>
/// <returns></returns>
public static bool TryResolveClientContext(Uri requestUri, out ClientContext context, ICredentials credentials)
{
context = null;
var baseUrl = requestUri.GetLeftPart(UriPartial.Authority);
for (int i = requestUri.Segments.Length; i >= 0; i--)
{
var path = string.Join(string.Empty, requestUri.Segments.Take(i));
string url = string.Format("{0}{1}", baseUrl, path);
try
{
context = new ClientContext(url);
if (credentials != null)
context.Credentials = credentials;
context.ExecuteQuery();
return true;
}
catch (Exception ex) {}
}
return false;
}
}
Usage
ClientContext context;
if (ClientContextUtilities.TryResolveClientContext(requestUri, out context, null))
{
using (context)
{
var baseUrl = requestUri.GetLeftPart(UriPartial.Authority);
var fileServerRelativeUrl = requestUri.ToString().Replace(baseUrl, string.Empty);
var file = context.Web.GetFileByServerRelativeUrl(fileServerRelativeUrl);
context.Load(file);
context.Load(context.Web);
context.Load(context.Site);
context.ExecuteQuery();
}
}
Since your goal is to download a file, there is pretty straightforward way to accomplish it without parsing url parts.
For example, using WebClient.DownloadFile Method:
private static void DownloadFile(Uri fileUri, ICredentials credentials, string localFileName)
{
using(var client = new WebClient())
{
client.Credentials = credentials;
client.DownloadFile(fileUri, localFileName);
}
}
I have made a working method but it seems elaborate, so any suggestions for improvement are welcome just to "download file if one of the specific columns has value "yes":
public void getDocument(Document doc)
{
// get the filename
Uri uri = new Uri(doc.uri);
doc.filename = "";
doc.filename = System.IO.Path.GetFileName(uri.LocalPath);
//string fullPathWithoutFileName = docUri.Replace(filename, "");
// would also include ?a&b so:
string[] splitDocUri = doc.uri.Split('/');
string fullPathWithoutFileName = "";
for (int i = 0; i < splitDocUri.Length -1; i++)
{
fullPathWithoutFileName += (splitDocUri[i] + '/');
}
// get via "_api/contextinfo" the context info
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(fullPathWithoutFileName + "_api/contextinfo");
req.Method = "POST";
req.Accept = "application/json; odata=verbose";
req.Credentials = new NetworkCredential(doc.username, doc.password, doc.domain);
req.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED","f");
req.ContentLength = 0;
BypassCertificateError();
HttpWebResponse rp = (HttpWebResponse)req.GetResponse();
Stream postStream = rp.GetResponseStream();
StreamReader postReader = new StreamReader(postStream);
string results = postReader.ReadToEnd();
// Now parse out some values needs system.web.extensions
JavaScriptSerializer jss = new JavaScriptSerializer();
var d = jss.Deserialize<dynamic>(results);
string formDigestValue = d["d"]["GetContextWebInformation"]["FormDigestValue"];
// the full url to the website e.g. "http://server:7777/level1/level 2"
string webFullUrl = d["d"]["GetContextWebInformation"]["WebFullUrl"];
// the full url to the site collection e.g. "http://server:7777"
string siteFullUrl = d["d"]["GetContextWebInformation"]["SiteFullUrl"];
// now we can create a context
ClientContext ctx = new ClientContext(webFullUrl);
ctx.ExecutingWebRequest +=
new EventHandler<WebRequestEventArgs>(ctx_MixedAuthRequest);
BypassCertificateError();
ctx.AuthenticationMode = ClientAuthenticationMode.Default;
ctx.Credentials = new NetworkCredential(doc.username, doc.password, doc.domain);
// Get the List
Microsoft.SharePoint.Client.File file = ctx.Web.GetFileByServerRelativeUrl(uri.AbsolutePath);
List list = file.ListItemAllFields.ParentList;
ctx.Load(list);
ctx.ExecuteQuery();
// execute a CAML query against it
CamlQuery camlQuery = new CamlQuery();
camlQuery.ViewXml =
"<View><Query><Where><Eq><FieldRef Name='FileLeafRef'/>" +
"<Value Type='Text'>" + doc.filename + "</Value></Eq></Where>" +
"<RowLimit>1</RowLimit></Query></View>";
ListItemCollection listItems = list.GetItems(camlQuery);
ctx.Load(listItems);
try {
ctx.ExecuteQuery();
}
catch
{
// e.g. : no access or the listname as incorrectly deduced
throw;
}
// and now retrieve the items needed
if (listItems.Count == 1)
{
ListItem item = listItems[0];
// some more checking from testColumn to decide if to download yes/no
string testColumn;
if (item.IsPropertyAvailable("testColumn")) {
testColumn = (string)item["testColumn"];
}
FileInformation fileInformation =
Microsoft.SharePoint.Client.File.OpenBinaryDirect(ctx,
(string)item["FileRef"]);
doc.bytes = ReadFully(fileInformation.Stream);
}
else
{
doc.errormessage = "Error: No document found";
}
}
Related
So here's the question. I have a browser that I'm developing using CefSharp, and I'm trying to get the browser to properly authenticate and pass the UserInfo back to the website that's requesting a login. Below is the button that they are clicking on the website:
As it is written now, I catch this login event in the PopUpHandler class which implements the ILifeSpanHandler interface. I catch it specifically in the OnBeforePopup method, and run the default OAuth class that I got off of the internet. I've modified the OAuth class a bit to implement a custom cefsharp browser pop-up to get the user to input their credentials.
public bool OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName,
WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo,
IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser)
{
foreach (var allowedSite in Helper.AllowedPopups)
{
if (targetUrl.Contains(allowedSite))
{
if (targetUrl.Contains("login") && targetUrl.Contains("oauth"))
{
OAuth that = new OAuth();
that.AuthorizeWithGoogle();
newBrowser = null;
return true;
}
Helper.OpenNewTab(targetUrl, true);
newBrowser = null;
return true;
}
}
string askingURL = Helper.GetWebsiteName(targetUrl);
AllowPopUpNotifier allowThis = new AllowPopUpNotifier(askingURL);
if (allowThis.ShowDialog() == true)
{
Helper.AllowedPopups.Add(askingURL);
if (targetUrl.Contains("login") && targetUrl.Contains("oauth"))
{
var newPopUpWindow = new CustomPopUpWindow(targetUrl);
newPopUpWindow.Show();
newBrowser = null;
return true;
}
Helper.OpenNewTab(targetUrl, true);
newBrowser = null;
return true;
}
else
{
newBrowser = null;
return true;
}
}
Then, the OAuth class that I've implemented looks like this:
public class OAuth
{
//client configuration;
const string clientID = "581786658708-elflankerquo1a6vsckabbhn25hclla0.apps.googleusercontent.com";
const string clientSecret = "3f6NggMbPtrmIBpgx-MK2xXK";
//const string clientID = "1020802206989-hsvquqebqgrj8kj4b387lcnosos4manc.apps.googleusercontent.com";
//const string clientSecret = "5k_uICJwwq25oV6-BM22310V";
const string authorizationEndpoint = "https://accounts.google.com/o/oauth2/v2/auth";
const string tokenEndpoint = "https://www.googleapis.com/oauth2/v4/token";
const string userInfoEndpoint = "https://www.googleapis.com/oauth2/v3/userinfo";
//ref http://stackoverflow.com/a/3978040
public static int GetRandomUnusedPort()
{
var listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
var port = ((IPEndPoint)listener.LocalEndpoint).Port;
listener.Stop();
return port;
}
public async Task<UserCredential> getUserCredential()
{
UserCredential credential;
string[] scopes = new string[] { }; // user basic profile
//Read client id and client secret from Web config file
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = "1020802206989-hsvquqebqgrj8kj4b387lcnosos4manc.apps.googleusercontent.com",
ClientSecret = "3f6NggMbPtrmIBpgx-MK2xXK"
}, scopes,
"user", CancellationToken.None, new FileDataStore("Auth.Api.Store"));
return credential;
}
public async void AuthorizeWithGoogle()
{
//Generates state and PKCE values.
string state = randomDataBase64url(32);
string code_verifier = randomDataBase64url(32);
string code_challenge = base64urlencodeNoPadding(sha256(code_verifier));
const string code_challenge_method = "S256";
//Creates a redirect URI using an available port on the loopback address.
string redirectURI = string.Format("http://{0}:{1}/", IPAddress.Loopback, GetRandomUnusedPort());
Debug.WriteLine("redirect URI: " + redirectURI);
//Creates an HttpListener to listen for requests on that redirect URI.
var http = new HttpListener();
http.Prefixes.Add(redirectURI);
Debug.WriteLine("Listening..");
http.Start();
//Creates the OAuth 2.0 authorization request.
string authorizationRequest = string.Format("{0}?response_type=code&scope=openid%20profile&redirect_uri={1}&client_id={2}&state={3}&code_challenge={4}&code_challenge_method={5}",
authorizationEndpoint,
Uri.EscapeDataString(redirectURI),
clientID,
state,
code_challenge,
code_challenge_method);
//Opens request in the browser.
//Process.Start(authorizationRequest);
//var newBrowser = new WebBrowser();
var that = new CustomPopUpWindow(authorizationRequest);
//that.browser.Address = authorizationRequest;
//newBrowser.Source = new Uri(authorizationRequest, UriKind.RelativeOrAbsolute);
//newBrowser.Load(authorizationRequest);
//that.Content = newBrowser;
that.Show();
//Waits for the OAuth authorization response.
var context = await http.GetContextAsync();
// Brings this app back to the foreground.
var mainWindow = Helper.GetMainWindow();
Application.Current.Dispatcher.Invoke(() =>
{
mainWindow.Activate();
});
//Sends an HTTP response to the browser.
var response = context.Response;
string responseString = string.Format("<html><head><meta http-equiv='refresh' content='10;url=https://google.com'></head><body>Please return to the app.</body></html>");
var buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
var responseOutput = response.OutputStream;
Task responseTask = responseOutput.WriteAsync(buffer, 0, buffer.Length).ContinueWith((task) =>
{
responseOutput.Close();
http.Stop();
Console.WriteLine("HTTP server stopped.");
});
//Checks for errors.
if (context.Request.QueryString.Get("error") != null)
{
Debug.WriteLine(String.Format("OAuth authorization error: {0}.", context.Request.QueryString.Get("error")));
Helper.GoogleAuthorized = false;
CustomMessageBox.Show("Google login not authorized. Check your login credentials and try again.");
return;
}
if (context.Request.QueryString.Get("code") == null
|| context.Request.QueryString.Get("state") == null)
{
Debug.WriteLine("Malformed authorization response. " + context.Request.QueryString);
Helper.GoogleAuthorized = false;
CustomMessageBox.Show("Google login not authorized. Check your login credentials and try again.");
return;
}
//extracts the code
var code = context.Request.QueryString.Get("code");
var incoming_state = context.Request.QueryString.Get("state");
//Compares the receieved state to the expected value, to ensure that
//this app made the request which resulted in authorization.
if (incoming_state != state)
{
Debug.WriteLine(String.Format("Received request with invalid state ({0})", incoming_state));
Helper.GoogleAuthorized = false;
CustomMessageBox.Show("Google login not authorized. Check your login credentials and try again.");
return;
}
Debug.WriteLine("Authorization code: " + code);
//Starts the code exchange at the Token Endpoint.
performCodeExchange(code, code_verifier, redirectURI);
}
async void performCodeExchange(string code, string code_verifier, string redirectURI)
{
Debug.WriteLine("Exchanging code for tokens...");
//builds the request
string tokenRequestURI = "https://www.googleapis.com/oauth2/v4/token";
string tokenRequestBody = string.Format("code={0}&redirect_uri={1}&client_id={2}&code_verifier={3}&client_secret={4}&scope=&grant_type=authorization_code",
code,
System.Uri.EscapeDataString(redirectURI),
clientID,
code_verifier,
clientSecret
);
//sends the request
HttpWebRequest tokenRequest = (HttpWebRequest)WebRequest.Create(tokenRequestURI);
tokenRequest.Method = "POST";
tokenRequest.ContentType = "application/x-www-form-urlencoded";
tokenRequest.Accept = "Accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
byte[] _byteVersion = Encoding.ASCII.GetBytes(tokenRequestBody);
tokenRequest.ContentLength = _byteVersion.Length;
Stream stream = tokenRequest.GetRequestStream();
await stream.WriteAsync(_byteVersion, 0, _byteVersion.Length);
stream.Close();
try
{
//gets the response
WebResponse tokenResponse = await tokenRequest.GetResponseAsync();
using (StreamReader reader = new StreamReader(tokenResponse.GetResponseStream()))
{
//reads response body
string responseText = await reader.ReadToEndAsync();
Debug.WriteLine(responseText);
//converts to dictionary
Dictionary<string, string> tokenEndpointDecoded = JsonConvert.DeserializeObject<Dictionary<string, string>>(responseText);
string access_token = tokenEndpointDecoded["access_token"];
userinfoCall(access_token);
}
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.ProtocolError)
{
var response = ex.Response as HttpWebResponse;
if (response != null)
{
Debug.WriteLine("HTTP: " + response.StatusCode);
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
//reads response body
string responseText = await reader.ReadToEndAsync();
Debug.WriteLine(responseText);
Helper.GoogleAuthorized = false;
CustomMessageBox.Show("Google login not authorized. Check your login credentials and try again.");
return;
}
}
}
}
}
async void userinfoCall(string access_token)
{
Debug.WriteLine("Making API Call to Userinfo...");
//builds the request
string userinfoRequestURI = "https://www.googleapis.com/oauth2/v3/userinfo";
//sends the request
HttpWebRequest userinfoRequest = (HttpWebRequest)WebRequest.Create(userinfoRequestURI);
userinfoRequest.Method = "GET";
userinfoRequest.Headers.Add(string.Format("Authorization: Bearer {0}", access_token));
userinfoRequest.ContentType = "application/x-www-form-urlencoded";
userinfoRequest.Accept = "Accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
//gets the response
WebResponse userinfoResponse = await userinfoRequest.GetResponseAsync();
using (StreamReader userinfoResponseReader = new StreamReader(userinfoResponse.GetResponseStream()))
{
//reads response body
string userinfoResponseText = await userinfoResponseReader.ReadToEndAsync();
Debug.WriteLine(userinfoResponseText);
}
}
/// <summary>
/// Appends the given string to the on-screen log, and the debug console.
/// </summary>
/// <param name = "output" > string to be appended</param>
public void output(string output)
{
//textBoxOutput.Text = textBoxOutput.Text + output + Environment.NewLine;
Console.WriteLine(output);
}
/// <summary>
/// Returns URI-safe data with a given input length.
/// </summary>
/// <param name = "length" > Input length(nb.output will be longer)</param>
/// <returns></returns>
public static string randomDataBase64url(uint length)
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] bytes = new byte[length];
rng.GetBytes(bytes);
return base64urlencodeNoPadding(bytes);
}
/// <summary>
/// Returns the SHA256 hash of the input string.
/// </summary>
/// <param name = "inputStirng" ></ param >
/// < returns ></ returns >
public static byte[] sha256(string inputStirng)
{
byte[] bytes = Encoding.ASCII.GetBytes(inputStirng);
SHA256Managed sha256 = new SHA256Managed();
return sha256.ComputeHash(bytes);
}
/// <summary>
/// Base64url no-padding encodes the given input buffer.
/// </summary>
/// <param name = "buffer" ></ param >
/// < returns ></ returns >
public static string base64urlencodeNoPadding(byte[] buffer)
{
string base64 = Convert.ToBase64String(buffer);
//Converts base64 to base64url.
base64 = base64.Replace("+", "-");
base64 = base64.Replace("/", "_");
//Strips padding.
base64 = base64.Replace("=", "");
return base64;
}
}
}
This gets me the UserInfo, and writes it on the "Debug.WriteLine(userinfoResponseText);" line in the OAuth class. However, I don't know how to redirect it back to the website that initially called the pop-up. Can anybody help? I'm so lost.
In My WPF App i have some pages, and I need to check for example:
new Uri("Pages/Page2.xaml", UriKind.Relative)
exist or not, I tried somethig semilar to this, just from Absolute replaced to Relative
bool IsRelativeUrl(string url)
{
Uri result;
return Uri.TryCreate(url, UriKind.Relative, out result);
}
Then Is printed:
string url = "Pages/Page2.xaml";
MessageBox.Show(IsRelativeUrl(url).ToString());
And it says always true, even for non existing pages
You can't use an Uri to determine whether a resource exists. You need to look for the compiled BAML resource:
bool IsRelativeUrl(string url)
{
Assembly assembly = Assembly.GetExecutingAssembly();
string[] resources = assembly.GetManifestResourceNames();
//Stream bamlStream = null;
foreach (string resourceName in resources)
{
ManifestResourceInfo info = assembly.GetManifestResourceInfo(resourceName);
if (info.ResourceLocation != ResourceLocation.ContainedInAnotherAssembly)
{
using (Stream resourceStream = assembly.GetManifestResourceStream(resourceName))
using (ResourceReader reader = new ResourceReader(resourceStream))
{
foreach (DictionaryEntry entry in reader)
{
if (entry.Key.ToString().Equals(url.ToLower()))
return true;
}
}
}
}
return false;
}
Usage:
string url = "Pages/Page2.baml"; //<-- note the file extension
MessageBox.Show(IsRelativeUrl(url).ToString());
Model
public class Mail
{
public int Id { get; set; }
public string Tema { get; set; }
public string Note { get; set; }
public string Date { get; set; }
public string Email { get; set; }
}
Client IMAP
using (var client = new ImapClient())
{
var Emails = _context.Settings.FirstOrDefault();
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
client.Connect(Emails.Imap,Convert.ToInt32(Emails.ImapPort), true);
client.Authenticate(Emails.Login, Emails.Password);
client.Inbox.Open(FolderAccess.ReadOnly);
var inbox = client.Inbox;
var messages = inbox.Fetch(0, -1, MessageSummaryItems.All | MessageSummaryItems.Full | MessageSummaryItems.UniqueId | MessageSummaryItems.BodyStructure);
if (messages.Where(c => c.Flags.Value.HasFlag(MessageFlags.Seen) != true).Count() > 0)
{
foreach (var message in messages.Where(c => c.Flags.Value.HasFlag(MessageFlags.Seen) != true))
{
var mime = inbox.GetBodyPart(message.UniqueId, message.HtmlBody);
Mail mail = new Mail();
mail.Date = DateTime.Now;
mail.Note = Convert.ToString(mime);
mail.Tema = message.Envelope.Subject.ToString();
mail.Email = Convert.ToString(message.Date);
_context.Add(mail);
await _context.SaveChangesAsync();
break;
}
}
client.Disconnect(true);
}
as a result, I get an unreadable string in the Note
Content-Type: text/html; charset="utf-8"
Content-Transfer-Encoding: base64
PGh0bWw+PGhlYWQ+PG1ldGEgaHR0cC1lcXVpdj0iQ29udGVudC1UeXBlIiBjb250ZW50
PSJ0ZXh0L2h0bWw7Ij48dGl0bGU+0JzQldCU0JjQkNCb0J7Qk9CY0K88L3RpdGxlPjxz
dHlsZT4uYmR7Zm9udDogMTI1JS8xMTUlIGFyaWFsLCBzYW5zLXNlcmlmOyBjb2xvcjog
IzAwM0U1MDsgbWFyZ2luLXRvcDogMTJweH0gcHtmb250OiAxMDAlIGFyaWFsLCBzYW5z
LXNlcmlmOyBjb2xvcjogIzAwM0U1MDsgbWFyZ2luLXRvcDogMTJweH0gdGR7Zm9udDog
ODAlLzExNSUgYXJpYWwsIHNhbnMtc2VyaWY7IGNvbG9yOiAjMDAzRTUwOyBtYXJnaW4t
dG9wOiAxMnB4fSB0ZC5oZHJ7Zm9udDogYm9sZCAxMXB4LzE0cHggYXJpYWwsIHNhbnMt
c2VyaWY7IGNvbG9yOiAjMWIxYjU2OyBtYXJnaW4tdG9wOiAxMnB4OyBsaW5lLWhlaWdo
dDoxNXB4O30gLnNlYXJjaF9obHtjb2xvcjogI2ZmZmZmZjtiYWNrZ3JvdW5kLWNvbG9y
OiAjMDAzODQ4O308L3N0eWxlPjwvaGVhZD48Ym9keSBiZ2NvbG9yPSNGRkZGRkY+PHRh
......
after "Content-Transfer-Encoding: base64" we see the html file in bass64. Is there any way MileKit will record only the file data itself.
You are essentially calling MimePart.ToString()... and, well, as far as MimeKit knows, you want the MIME as a string.
But that's not what you actually want.
What you want is the actual text content.
Here's what you need to do in your loop:
var html = (TextPart) inbox.GetBodyPart (message.UniqueId, message.HtmlBody);
Mail mail = new Mail();
mail.Date = DateTime.Now;
mail.Note = html.Text;
...
A TextPart is a subclass of MimePart that represents text/* MIME parts (including text/html) and has a few nice convenient API's that can be used to get (or set) the textual content of the MimePart as a string rather than having to deal with streams - most notably, the .Text property.
That should answer your question, but I'm not done because your code could use a few more changes.
Instead of asking MailKit to Fetch() the metadata for every single message in your inbox, why not request only what you need?
Since you clearly don't care about messages that have already been seen, you could take advantage of IMAP's SEARCH functionality, like so:
var uids = inbox.Search (SearchQuery.NotSeen);
if (uids.Count > 0)
{
var messages = inbox.Fetch (uids, MessageSummaryItems.UniqueId | MessageSummaryItems.Envelope | MessageSummaryItems.Flags | MessageSummaryItems.BodyStructure);
foreach (var message in messages.Where (c => !c.Flags.Value.HasFlag (MessageFlags.Seen))
{
// ...
}
}
Note: You still need to use the Where() filter because it's possible for message flags to change (by another client) between the Search() and the Fetch(). I also changed the MessageSummaryItems to be only the fields that you are actually using.
You can also get rid of your if (messages.Where(c => c.Flags.Value.HasFlag(MessageFlags.Seen) != true).Count() > 0) statement because it is redundant and requires a full pass at the returned data when the very next thing you do is to loop over the data anyway.
And since you break; out of the loop as soon as you find the very first message that hasn't been seen, why not do this instead?
var message = messages.FirstOrDefault (x => !x.Flags.Value.HasFlag (MessageFlags.Seen));
if (message != null)
{
var html = (TextPart) inbox.GetBodyPart(message.UniqueId, message.HtmlBody);
Mail mail = new Mail();
mail.Date = DateTime.Now;
mail.Note = html.Text;
mail.Tema = message.Envelope.Subject;
mail.Email = Convert.ToString(message.Envelope.Date);
_context.Add(mail);
await _context.SaveChangesAsync();
}
The end result is:
using (var client = new ImapClient())
{
var Emails = _context.Settings.FirstOrDefault();
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
client.Connect(Emails.Imap,Convert.ToInt32(Emails.ImapPort), true);
client.Authenticate(Emails.Login, Emails.Password);
client.Inbox.Open(FolderAccess.ReadOnly);
var inbox = client.Inbox;
var uids = inbox.Search (SearchQuery.NotSeen);
if (uids.Count > 0)
{
var messages = inbox.Fetch (uids, MessageSummaryItems.UniqueId | MessageSummaryItems.Envelope | MessageSummaryItems.Flags | MessageSummaryItems.BodyStructure);
var message = messages.FirstOrDefault (x => !x.Flags.Value.HasFlag (MessageFlags.Seen));
if (message != null)
{
var html = (TextPart) inbox.GetBodyPart(message.UniqueId, message.HtmlBody);
Mail mail = new Mail();
mail.Date = DateTime.Now;
mail.Note = html.Text;
mail.Tema = message.Envelope.Subject;
mail.Email = Convert.ToString(message.Envelope.Date);
_context.Add(mail);
await _context.SaveChangesAsync();
}
}
client.Disconnect(true);
}
The last change you'll need to make to the above code is what to do if the message doesn't have an HTML body. What if the message only has a text/plain body? Or what if it doesn't contain any text/* parts at all?
You'll need to figure out what you want to do when that happens...
How can I get the name (including namespace prefix) of root element in message (1st element in SOAP body) and add this information to SOAP header.
Is it possible to do so with outbound interceptor and in which phase is the namespace prefix available? Or is there other way how to do so?
EDIT:
I am able to get the element with its namespace prefix via message.getContent(OutputStream.class) but I don't want to modify row XML. Is there way how can I get namespace (e.g. from JAXB object in message) and set its namespace prefix? Then I can use the element name and my prefix in header.
I created interceptor where I get message (as JAXB object) and then I use reflection to get its root element (because it is not root element) from ObjectFactory. Then I get its namespace and name and I use it for SOAP header and I set namespace prefix for the namespace. My handleMessage method in interceptor is like the following:
public void handleMessage(SoapMessage message) throws Fault {
String rootElementNamespace = null;
String rootElementName = null;
Set<Class<?>> formats = message.getContentFormats();
List<?> messageContent = message.getContent(List.class);
Object responseMessage = null;
for (Object o : messageContent){
if (o != null && o.getClass().getAnnotation(XmlType.class) != null){
responseMessage = o;
break;
}
}
if(responseMessage == null){
return;
}
Class<? extends Object> messageContentClass = messageContent.get(0).getClass();
String packageOfMessageContentClass = messageContent.get(0).getClass().getPackage().getName();
try {
Class<?> objectFactory = Class.forName(packageOfMessageContentClass + ".ObjectFactory");
Method[] objectFactoryMethods = objectFactory.getMethods();
Method createMessageMethod = null;
for (Method m : objectFactoryMethods){
if (m.getParameterTypes().length == 1 && m.getParameterTypes()[0].equals(messageContentClass)){
createMessageMethod = m;
break;
}
}
if(createMessageMethod == null){
return;
}
XmlElementDecl xmlTypeAnnotation = createMessageMethod.getAnnotation(XmlElementDecl.class);
rootElementNamespace = xmlTypeAnnotation.namespace();
rootElementName = xmlTypeAnnotation.name();
} catch (ClassNotFoundException e) {
//...
}
Map<String, String> hmap = new HashMap<String, String>();
hmap.put(this.ROOT_PREFIX, rootElementNamespace);
message.put("soap.env.ns.map", hmap);
message.put("disable.outputstream.optimization", true);
try {
Header header = getMyHeader(this.ROOT_PREFIX + ":" + rootElementName); //method creates header with required info
message.getHeaders().add(header);
} catch (JAXBException | DatatypeConfigurationException e) {
//...
}
}
Better solution is welcome...
I'm trying to display a tweet on the backside of a live tile, when I set it as BackContent it's way too big.... Is there any way to lower the font size?
EDIT:
Claus, Now i'm having trouble getting the tile to display and I can't get any info on why it's not working due to the nature of your ImageOpened call, I can't step through it with the debugger....
In my TileGenerator class, this works:
public static void GenerateTestTile(string strTweet, string strScreenName, string tileTitle)
{
// Define the tile's address. This is where you navigate, when the tile is clicked.
var address = "/MainPage.xaml?TileID=6";
// Check if a tile with the same address already exists
//var tile = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri.ToString() == address);
var tile = ShellTile.ActiveTiles.First();
// Define our tile data.
var tileData = new StandardTileData
{
BackTitle = strScreenName,
BackContent = strTweet
};
// If the file already exists, update it.
if (tile != null)
{
tile.Update(tileData);
}
else
{
// Otherwise, create a new tile.
ShellTile.Create(new Uri(address, UriKind.Relative), tileData);
}
}
But this doesn't (exact method taken from your example), it doesn't do anything to the tile at all...
public static void GenerateExampleTile(string timeOfDay, string temperature, Uri cloudImagePath, string tileTitle)
{
// Setup the font style for our tile.
var fontFamily = new FontFamily("Segoe WP");
var fontForeground = new SolidColorBrush(Colors.White);
var tileSize = new Size(173, 173);
// Create a background rectagle for a custom colour background.
var backgroundRectangle = new Rectangle();
backgroundRectangle.Width = tileSize.Width;
backgroundRectangle.Height = tileSize.Height;
backgroundRectangle.Fill = new SolidColorBrush(Colors.Blue);
// Load our 'cloud' image.
var source = new BitmapImage(cloudImagePath);
source.CreateOptions = BitmapCreateOptions.None;
source.ImageOpened += (sender, e) => // This is important. The image can't be rendered before it's loaded.
{
// Create our image as a control, so it can be rendered to the WriteableBitmap.
var cloudImage = new Image();
cloudImage.Source = source;
cloudImage.Width = 100;
cloudImage.Height = 64;
// TextBlock for the time of the day.
TextBlock timeOfDayTextBlock = new TextBlock();
timeOfDayTextBlock.Text = timeOfDay;
timeOfDayTextBlock.FontSize = 20;
timeOfDayTextBlock.Foreground = fontForeground;
timeOfDayTextBlock.FontFamily = fontFamily;
// Temperature TextBlock.
TextBlock temperatureTextBlock = new TextBlock();
temperatureTextBlock.Text = temperature + '°';
temperatureTextBlock.FontSize = 30;
temperatureTextBlock.Foreground = fontForeground;
temperatureTextBlock.FontFamily = fontFamily;
// Define the filename for our tile. Take note that a tile image *must* be saved in /Shared/ShellContent
// or otherwise it won't display.
var tileImage = string.Format("/Shared/ShellContent/{0}.jpg", timeOfDay);
// Define the path to the isolatedstorage, so we can load our generated tile from there.
var isoStoreTileImage = string.Format("isostore:{0}", tileImage);
// Open the ISF store,
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
{
// Create our bitmap, in our selected dimension.
var bitmap = new WriteableBitmap((int)tileSize.Width, (int)tileSize.Height);
// Render our background. Remember the renders are in the same order as XAML,
// so whatever is rendered first, is rendered behind the next element.
bitmap.Render(backgroundRectangle, new TranslateTransform());
// Render our cloud image
bitmap.Render(cloudImage, new TranslateTransform()
{
X = 8, // Left margin offset.
Y = 54 // Top margin offset.
});
// Render the temperature text.
bitmap.Render(temperatureTextBlock, new TranslateTransform()
{
X = 124,
Y = 63
});
// Render the time of the day text.
bitmap.Render(timeOfDayTextBlock, new TranslateTransform()
{
X = 12,
Y = 6
});
// Create a stream to store our file in.
var stream = store.CreateFile(tileImage);
// Invalidate the bitmap to make it actually render.
bitmap.Invalidate();
// Save it to our stream.
bitmap.SaveJpeg(stream, 173, 173, 0, 100);
// Close the stream, and by that saving the file to the ISF.
stream.Close();
}
// Define the tile's address. This is where you navigate, when the tile is clicked.
var address = "/MainPage.xaml?TileID=" + timeOfDay;
// Check if a tile with the same address already exists
var tile = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri.ToString() == address);
// Define our tile data.
var tileData = new StandardTileData
{
BackgroundImage = new Uri(isoStoreTileImage, UriKind.Absolute),
Title = tileTitle,
};
// If the file already exists, update it.
if (tile != null)
{
tile.Update(tileData);
}
else
{
// Otherwise, create a new tile.
ShellTile.Create(new Uri(address, UriKind.Relative), tileData);
}
};
}
Both methods are being called in this way....
public class ScheduledAgent : ScheduledTaskAgent
{
...
/// <summary>
/// Agent that runs a scheduled task
/// </summary>
/// <param name="task">
/// The invoked task
/// </param>
/// <remarks>
/// This method is called when a periodic or resource intensive task is invoked
/// </remarks>
protected override void OnInvoke(ScheduledTask task)
{
LoadWatchList();
}
//WATCH LIST
private void LoadWatchList()
{
if (HasConnectivity)
{
GetWatchListTweetsFromTwitter(CurrentWatchListID);
}
}
public void GetWatchListTweetsFromTwitter(int list_id)
{
WebClient wcWatchListTimeline = new WebClient();
wcWatchListTimeline.DownloadStringCompleted += new DownloadStringCompletedEventHandler(wcWatchListTimeline_DownloadStringCompleted);
wcWatchListTimeline.DownloadStringAsync(new System.Uri("https://api.twitter.com/1/lists/statuses.xml?per_page=1&list_id=" + list_id));
}
void wcWatchListTimeline_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
String strTweet = "content";
String strScreenName = "title";
if (e.Error != null)
{
strScreenName = "error";
strTweet = e.Error.Message;
}
else
{
XElement tweet = XElement.Parse(e.Result);
Tweet thisTweet = GetTweet(tweet);
if (thisTweet != null)
{
strTweet = thisTweet.text;
strScreenName = thisTweet.screen_name;
}
}
// TAKEN FROM EXAMPLE FOR TESTING - NOT WORKING
string timeOfday = "morning";
string temperature = "99";
string location = "San Antonio";
Uri cloudImagePath = new Uri("Images/tweetEmpty.png", UriKind.Relative);
Deployment.Current.Dispatcher.BeginInvoke(() => TileGenerator.GenerateExampleTile(timeOfday, temperature, cloudImagePath, "mainTile"));
//WORKING
//Deployment.Current.Dispatcher.BeginInvoke(() => TileGenerator.GenerateTile(strTweet, strScreenName, "mainTile"));
NotifyComplete();
}
protected Tweet GetTweet(XElement Xdata)
{
List<Tweet> listTweets = (from tweet in Xdata.Descendants("status")
select new Tweet
{
screen_name = tweet.Element("user").Element("screen_name").Value,
text = tweet.Element("text").Value
}).ToList<Tweet>();
if (listTweets.Count > 0)
{
return listTweets[0];
}
else
{
return null;
}
}
}
Only by creating a custom image, and using that as the background for the tile.
Updated: How To: Live Tile with Scheduled Agent