//--- Class Methods --- public static NotificationUpdateRecord FromDocument(XDoc doc) { var record = new NotificationUpdateRecord(doc["@wikiid"].AsText, doc["@userid"].AsUInt.Value); foreach(var page in doc["page"]) { record.Add(page["@id"].AsUInt.Value, page["@modified"].AsDate.Value); } return record; }
//--- Class Methods --- public static NotificationUpdateRecord FromDocument(XDoc doc) { var record = new NotificationUpdateRecord(doc["@wikiid"].AsText, doc["@userid"].AsUInt.Value); foreach (var page in doc["page"]) { record.Add(page["@id"].AsUInt.Value, page["@modified"].AsDate.Value); } return(record); }
//--- Methods --- public void Enqueue(string wikiid, uint userId, uint pageId, DateTime modificationDate) { lock(_pending) { NotificationUpdateRecord pending; string key = wikiid + ":" + userId; if(!_pending.TryGetValue(key, out pending)) { pending = new NotificationUpdateRecord(wikiid, userId); _pending[key] = pending; _queue.Enqueue(new Tuplet<DateTime, string>(DateTime.UtcNow.Add(_delay), key)); } pending.Add(pageId, modificationDate); } }
//--- Methods --- public void Enqueue(string wikiid, uint userId, uint pageId, DateTime modificationDate) { lock (_pending) { NotificationUpdateRecord pending; string key = wikiid + ":" + userId; if (!_pending.TryGetValue(key, out pending)) { pending = new NotificationUpdateRecord(wikiid, userId); _pending[key] = pending; _queue.Enqueue(new Tuplet <DateTime, string>(DateTime.UtcNow.Add(_delay), key)); } pending.Add(pageId, modificationDate); } }
private void Dispatch(NotificationUpdateRecord updateRecord, Action completionCallback) { Coroutine.Invoke(_callback, updateRecord, new Result()).WhenDone( r => { completionCallback(); if (r.HasException) { _log.ErrorExceptionMethodCall(r.Exception, "DispatchFromQueue", string.Format("dispatch for user '{0}' encountered an error", updateRecord.UserId)); } else { _log.DebugFormat("finished dispatch of update record for user '{0}'", updateRecord.UserId); } }); }
private Yield SendEmail(NotificationUpdateRecord updateRecord, Result result) { bool userChanged = false; Plug deki = _deki.With("apikey", _apikey).WithCookieJar(Cookies); _log.DebugFormat("trying to dispatch email to user {0} for wiki '{1}'", updateRecord.UserId, updateRecord.WikiId); bool createUser = false; UserInfo userInfo = _subscriptions.GetUser(updateRecord.WikiId, updateRecord.UserId, false); if(userInfo == null) { createUser = true; _log.DebugFormat("user is gone from subscriptions. Trying to re-fetch", updateRecord.UserId, updateRecord.WikiId); } if(userInfo == null || !userInfo.IsValidated) { // need to refetch user info to make sure we have DreamMessage userMsg = null; yield return deki.At("users", updateRecord.UserId.ToString()).WithHeader("X-Deki-Site", "id=" + updateRecord.WikiId) .Get(new Result<DreamMessage>()) .Set(x => userMsg = x); if(!userMsg.IsSuccessful) { _log.DebugFormat("unable to fetch user {0}, skipping delivery: {1}", updateRecord.UserId, userMsg.Status); result.Return(); yield break; } var userDoc = userMsg.ToDocument(); try { userInfo = GetUserInfo(userDoc, updateRecord.WikiId, createUser); } catch(UserException e) { _log.DebugFormat("unable to re-validate user {0}, skipping delivery: {1}", updateRecord.UserId, e.Message); result.Return(); yield break; } userInfo.Save(); } SiteInfo siteInfo = _subscriptions.GetSiteInfo(updateRecord.WikiId); if(!siteInfo.IsValidated) { // lazy loading site information Result<DreamMessage> siteResult; yield return siteResult = deki.At("site", "settings").WithHeader("X-Deki-Site", "id=" + updateRecord.WikiId).GetAsync(); DreamMessage site = siteResult.Value; if(!site.IsSuccessful) { _log.WarnFormat("unable to fetch site data for deki '{0}', skipping delivery: {1}", updateRecord.WikiId, site.Status); result.Return(); yield break; } XDoc siteDoc = site.ToDocument(); siteInfo.Sitename = siteDoc["ui/sitename"].AsText; siteInfo.EmailFromAddress = siteDoc["page-subscription/from-address"].AsText; siteInfo.EmailFormat = siteDoc["page-subscription/email-format"].AsText; if(string.IsNullOrEmpty(siteInfo.EmailFromAddress)) { siteInfo.EmailFromAddress = siteDoc["admin/email"].AsText; } siteInfo.Culture = CultureUtil.GetNonNeutralCulture(siteDoc["ui/language"].AsText) ?? CultureInfo.GetCultureInfo("en-us"); if(!siteInfo.IsValidated) { _log.WarnFormat("unable to get required data from site settings, cannot send email"); if(string.IsNullOrEmpty(siteInfo.Sitename)) { _log.WarnFormat("missing ui/sitename"); } if(string.IsNullOrEmpty(siteInfo.EmailFromAddress)) { _log.WarnFormat("missing page-subscription/from-address"); } result.Return(); yield break; } } CultureInfo culture = CultureUtil.GetNonNeutralCulture(userInfo.Culture, siteInfo.Culture); string subject = string.Format("[{0}] {1}", siteInfo.Sitename, _resourceManager.GetString("Notification.Page.email-subject", culture, "Site Modified")); XDoc email = new XDoc("email") .Attr("configuration", siteInfo.WikiId) .Elem("to", userInfo.Email) .Elem("from", siteInfo.EmailFromAddress) .Elem("subject", subject) .Start("pages"); string header = _resourceManager.GetString("Notification.Page.email-header", culture, "The following pages have changed:"); StringBuilder plainBody = new StringBuilder(); plainBody.AppendFormat("{0}\r\n\r\n", header); XDoc htmlBody = new XDoc("body") .Attr("html", true) .Elem("h2", header); foreach(Tuplet<uint, DateTime, bool> record in updateRecord.Pages) { // TODO (arnec): Need to revalidate that the user is still allowed to see that page // TODO (arnec): Should check that the user is still subscribed to this page uint pageId = record.Item1; email.Elem("pageid", pageId); Result<PageChangeData> dataResult; yield return dataResult = Coroutine.Invoke(_cache.GetPageData, pageId, userInfo.WikiId, record.Item2, culture, userInfo.Timezone, new Result<PageChangeData>()); PageChangeData data = dataResult.Value; if(data == null) { _log.WarnFormat("Unable to fetch page change data for page {0}", pageId); continue; } htmlBody.AddAll(data.HtmlBody.Elements); plainBody.Append(data.PlainTextBody); if(!record.Item3) { continue; } userInfo.RemoveResource(pageId); userChanged = true; } email.End(); if(!StringUtil.EqualsInvariantIgnoreCase(siteInfo.EmailFormat, "html")) { email.Elem("body", plainBody.ToString()); } if(!StringUtil.EqualsInvariantIgnoreCase(siteInfo.EmailFormat, "plaintext")) { email.Add(htmlBody); } _log.DebugFormat("dispatching email for user '{0}'", userInfo.Id); yield return _emailer.WithCookieJar(Cookies).PostAsync(email).Catch(); if(userChanged) { userInfo.Save(); } result.Return(); yield break; }
private Yield ExpirationCallback(NotificationUpdateRecord record, Result result) { expirationResult.Return(record); result.Return(); yield break; }
private Yield SendEmail(NotificationUpdateRecord updateRecord, Result result) { yield return Self.WithCookieJar(Cookies).At("__sendnotification").Post(updateRecord.ToDocument(), new Result<DreamMessage>()); result.Return(); }
private void Dispatch(NotificationUpdateRecord updateRecord, Action completionCallback) { Coroutine.Invoke(_callback, updateRecord, new Result()).WhenDone( r => { completionCallback(); if(r.HasException) { _log.ErrorExceptionMethodCall(r.Exception, "DispatchFromQueue", string.Format("dispatch for user '{0}' encountered an error", updateRecord.UserId)); } else { _log.DebugFormat("finished dispatch of update record for user '{0}'", updateRecord.UserId); } }); }