Esempio n. 1
0
        //--- Methods ---
        public void SendNoticeToAdmin(string subject, string content, MimeType mime)
        {
            // TODO (arnec): should this be using the email service?
            UserBE adminUser = UserBL.GetAdmin();

            if (adminUser == null)
            {
                throw new SiteNoAdminFatalException();
            }
            string smtphost = string.Empty;
            int    smtpport = 0;

            if (smtphost == string.Empty)
            {
                throw new SiteConflictException(DekiResources.SMTP_SERVER_NOT_CONFIGURED());
            }
            if (string.IsNullOrEmpty(adminUser.Email))
            {
                throw new SiteConflictException(DekiResources.ADMIN_EMAIL_NOT_SET());
            }
            var smtpclient = new SmtpClient();
            var msg        = new MailMessage();

            msg.To.Add(adminUser.Email);
            msg.From        = new MailAddress(_user.Email, _user.Name);
            msg.Subject     = _dekiInstance.SiteName + ": " + subject;
            msg.Body        = content;
            smtpclient.Host = smtphost;
            if (smtpport != 0)
            {
                smtpclient.Port = smtpport;
            }
            smtpclient.Send(msg);
        }
Esempio n. 2
0
        public static XDoc GetCommentXmlAsAtom(IList <CommentBE> comments, XUri feedUri, PageBE page)
        {
            var       resources = DekiContext.Current.Resources;
            string    title     = resources.Localize(DekiResources.COMMENT_FOR(page.Title.AsUserFriendlyName()));
            XAtomFeed feed      = new XAtomFeed(title, feedUri, DateTime.UtcNow);

            feed.AddLink(PageBL.GetUriUi(page), XAtomBase.LinkRelation.Alternate, MimeType.XHTML, null, page.Title.AsUserFriendlyName());
            feed.Id = feedUri;

            foreach (CommentBE c in comments)
            {
                UserBE posterUser = UserBL.GetUserById(c.PosterUserId);
                title = c.Title;
                if (string.IsNullOrEmpty(title))
                {
                    title = resources.Localize(DekiResources.COMMENT_BY_TO(posterUser.Name, page.Title.AsUserFriendlyName()));
                }
                XAtomEntry entry = feed.StartEntry(title, c.CreateDate, (c.LastEditDate == null || c.LastEditDate == DateTime.MinValue) ? c.CreateDate : c.LastEditDate.Value);
                entry.Id = GetUri(c);
                entry.AddAuthor(posterUser.Name, UserBL.GetUriUiHomePage(posterUser), posterUser.Email);
                MimeType commentMimetype;
                MimeType.TryParse(c.ContentMimeType, out commentMimetype);
                entry.AddContent(c.Content);

                XUri entryLink = PageBL.GetUriUi(page).WithFragment("comment" + c.Number);
                entry.AddLink(entryLink, XAtomBase.LinkRelation.Alternate, null, null, null);
                entry.AddLink(GetUri(c).At("content"), XAtomBase.LinkRelation.Enclosure, commentMimetype, c.Content.Length, "content");
                feed.End();
            }

            return(feed);
        }
Esempio n. 3
0
 //--- Constructors ---
 protected AttachmentBL()
 {
     _dekiContext = DekiContext.Current;
     _session     = DbUtils.CurrentSession;
     _resources   = _dekiContext.Resources;
     _resourceBL  = ResourceBL.Instance;
 }
        public void Localizing_non_existent_key_retuns_error_string()
        {
            _resourceManagerMock.Setup(x => x.GetString("System.API.Error.rating_invalid_score", CultureInfo.InvariantCulture, null))
            .Returns((string)null).AtMostOnce().Verifiable();
            var resource = DekiResources.RATING_INVALID_SCORE();

            Assert.AreEqual("[MISSING: System.API.Error.rating_invalid_score]", _resources.Localize(resource));
            _resourceManagerMock.VerifyAll();
        }
        public void Can_localize_resource_with_arguments()
        {
            _resourceManagerMock.Setup(x => x.GetString("System.API.Error.property_concurrency_error", CultureInfo.InvariantCulture, null))
            .Returns("{0}").AtMostOnce().Verifiable();
            var resource = DekiResources.PROPERTY_CONCURRENCY_ERROR(1234);

            Assert.AreEqual("1234", _resources.Localize(resource));
            _resourceManagerMock.VerifyAll();
        }
        public void Can_localize_no_arg_resource()
        {
            _resourceManagerMock.Setup(x => x.GetString("System.API.Error.rating_invalid_score", CultureInfo.InvariantCulture, null))
            .Returns("foo").AtMostOnce().Verifiable();
            var resource = DekiResources.RATING_INVALID_SCORE();

            Assert.AreEqual("foo", _resources.Localize(resource));
            _resourceManagerMock.VerifyAll();
        }
Esempio n. 7
0
        public void RestoreAttachment(ResourceBE attachmentToRestore, PageBE toPage, DateTime timestamp, uint transactionId)
        {
            if (toPage == null || toPage.ID == 0)
            {
                ArchiveBE archivesMatchingPageId = _session.Archive_GetPageHeadById(attachmentToRestore.ParentPageId.Value);
                if (archivesMatchingPageId == null)
                {
                    throw new AttachmentRestoreFailedNoParentFatalException();
                }
                else
                {
                    toPage = PageBL.GetPageByTitle(archivesMatchingPageId.Title);
                    if (0 == toPage.ID)
                    {
                        PageBL.Save(toPage, _resources.Localize(DekiResources.RESTORE_ATTACHMENT_NEW_PAGE_TEXT()), DekiMimeType.DEKI_TEXT, null);
                    }
                }
            }

            string filename = attachmentToRestore.Name;

            //Check for name conflicts on target page
            ResourceBE conflictingFile = GetPageAttachment(toPage.ID, filename);

            if (conflictingFile != null)
            {
                //rename the restored file
                filename        = string.Format("{0}(restored {1}){2}", attachmentToRestore.FilenameWithoutExtension, DateTime.Now.ToString("g"), string.IsNullOrEmpty(attachmentToRestore.FilenameExtension) ? string.Empty : "." + attachmentToRestore.FilenameExtension);
                conflictingFile = GetPageAttachment(toPage.ID, filename);
                if (conflictingFile != null)
                {
                    throw new AttachmentRestoreNameConflictException();
                }
            }

            //Build new revision for restored file
            attachmentToRestore = BuildRevForRestore(attachmentToRestore, toPage, filename, transactionId);

            //Insert new revision into DB
            attachmentToRestore = SaveResource(attachmentToRestore);

            //Recent Changes
            RecentChangeBL.AddFileRecentChange(_dekiContext.Now, toPage, _dekiContext.User, DekiResources.FILE_RESTORED(attachmentToRestore.Name), transactionId);
            _dekiContext.Instance.EventSink.AttachmentRestore(_dekiContext.Now, attachmentToRestore, _dekiContext.User);
        }
        public string Localize(DekiResources resources)
        {
            var _builder = new StringBuilder();

            foreach (var item in _resourceChain)
            {
                var resource = item as DekiResource;
                if (resource == null)
                {
                    _builder.Append(item);
                }
                else
                {
                    _builder.Append(resources.Localize(resource));
                }
            }
            return(_builder.ToString());
        }
        public void Can_localize_string_resource_mixed_builder()
        {
            var resourceManagerMock = new Mock <IPlainTextResourceManager>();
            var resources           = new DekiResources(resourceManagerMock.Object, CultureInfo.InvariantCulture);

            resourceManagerMock.Setup(x => x.GetString("x", CultureInfo.InvariantCulture, null))
            .Returns("abc").AtMostOnce().Verifiable();
            resourceManagerMock.Setup(x => x.GetString("y", CultureInfo.InvariantCulture, null))
            .Returns("xyz").AtMostOnce().Verifiable();
            var b = new DekiResourceBuilder();

            b.Append(new DekiResource("x"));
            b.Append("+");
            b.Append(new DekiResource("y"));
            b.Append("-");
            Assert.AreEqual("abc+xyz-", b.Localize(resources));
            resourceManagerMock.VerifyAll();
        }
Esempio n. 10
0
        internal static DekiResource GetPageDiffSummary(PageBE page, string before, string beforeMime, string after, string afterMime, int maxDelta)
        {
            ParserResult beforeDoc = DekiXmlParser.Parse(page, beforeMime, page.Language, before, ParserMode.EDIT, false, -1, null, null);
            ParserResult afterDoc  = DekiXmlParser.Parse(page, afterMime, page.Language, after, ParserMode.EDIT, false, -1, null, null);

            Tuplet <ArrayDiffKind, XDocDiff.Token>[] diff = XDocDiff.Diff(beforeDoc.MainBody, afterDoc.MainBody, maxDelta);
            if (diff == null)
            {
                return(DekiResources.PAGE_DIFF_TOO_LARGE());
            }

            // create change summary
            int added;
            int removed;
            int attributes;
            int structural;

            return(GetChangeSummary(diff, out added, out removed, out attributes, out structural));
        }
        //--- Class Methods ---

        // TODO: if we need more arguments, need to make the dispatcher smarter to pass in the desired arguments, so that the other handler
        // signatures don't have to change
        public static DreamMessage Map(Exception exception, DekiResources resources)
        {
            var        exceptionType = exception.GetType();
            MethodInfo handler;

            lock (_handlers) {
                if (!_handlers.TryGetValue(exceptionType, out handler))
                {
                    handler = (from method in typeof(DekiExceptionMapper).GetMethods(BindingFlags.NonPublic | BindingFlags.Static)
                               let parameters = method.GetParameters()
                                                where parameters.Length == 2 && parameters[0].ParameterType.IsAssignableFrom(exceptionType)
                                                let depth = GetInheritanceChain(parameters[0].ParameterType, 0)
                                                            orderby depth descending
                                                            select method).FirstOrDefault();
                    if (handler == null)
                    {
                        return(null);
                    }
                    _handlers[exceptionType] = handler;
                }
            }
            return((DreamMessage)handler.Invoke(null, new object[] { exception, resources }));
        }
Esempio n. 12
0
        /// <summary>
        /// Move this attachment to the target page.
        /// </summary>
        /// <remarks>
        /// This will fail if destination page has a file with the same name.
        /// </remarks>
        /// <param name="sourcePage">Current file location</param>
        /// <param name="targetPage">Target file location. May be same as sourcepage for rename</param>
        /// <param name="name">New filename or null for no change</param>
        /// <returns></returns>
        public ResourceBE MoveAttachment(ResourceBE attachment, PageBE sourcePage, PageBE targetPage, string name, bool loggingEnabled)
        {
            //TODO MaxM: Connect with a changeset
            uint changeSetId = 0;

            attachment.AssertHeadRevision();

            bool move   = targetPage != null && targetPage.ID != sourcePage.ID;
            bool rename = name != null && !name.EqualsInvariant(attachment.Name);

            //Just return the current revision if no change is being made
            if (!move && !rename)
            {
                return(attachment);
            }

            //validate filename
            if (rename)
            {
                name = ValidateFileName(name);
            }

            //Check the resource exists on the target (may be same as source page) with new name (if given) or current name
            ResourceBE existingAttachment = GetPageAttachment((targetPage ?? sourcePage).ID, name ?? attachment.Name);

            if (existingAttachment != null)
            {
                throw new AttachmentExistsOnPageConflictException(name ?? attachment.Name, (targetPage ?? sourcePage).Title.AsUserFriendlyName());
            }

            //Performing a move?
            if (move)
            {
                _dekiContext.Instance.Storage.MoveFile(attachment, targetPage);  //Perform the IStorage move (should be a no-op)
            }

            //Build the new revision
            ResourceBE newRevision = BuildRevForMoveAndRename(attachment, targetPage, name, changeSetId);

            //Insert new revision into DB
            try {
                newRevision = SaveResource(newRevision);
            } catch {
                //failed to save the revision, undo the file move with the IStorage. (Should be a no-op)
                if (move)
                {
                    _dekiContext.Instance.Storage.MoveFile(attachment, sourcePage);
                }
                throw;
                //NOTE MaxM: file rename does not even touch IStorage. No need to undo it
            }

            //Notification for file move
            if (loggingEnabled)
            {
                if (move)
                {
                    RecentChangeBL.AddFileRecentChange(_dekiContext.Now, sourcePage, _dekiContext.User, DekiResources.FILE_MOVED_TO(attachment.Name, targetPage.Title.AsPrefixedUserFriendlyPath()), changeSetId);
                    RecentChangeBL.AddFileRecentChange(_dekiContext.Now, targetPage, _dekiContext.User, DekiResources.FILE_MOVED_FROM(attachment.Name, sourcePage.Title.AsPrefixedUserFriendlyPath()), changeSetId);
                }
                if (rename)
                {
                    RecentChangeBL.AddFileRecentChange(_dekiContext.Now, sourcePage, _dekiContext.User, DekiResources.FILE_RENAMED_TO(attachment.Name, name), changeSetId);
                }
            }

            //Notification for file rename and move use same event
            _dekiContext.Instance.EventSink.AttachmentMove(_dekiContext.Now, attachment, sourcePage, _dekiContext.User);

            return(newRevision);
        }
 private static DreamMessage Map(TooManyResultsException e, DekiResources resources)
 {
     return(DreamMessage.Forbidden(resources.Localize(DekiResources.SITE_TOO_BIG_TO_GENERATE_SITEMAP())));
 }
Esempio n. 14
0
        public ResourceBE AddAttachment(ResourceBE existingRevision, Stream filestream, long filesize, MimeType mimeType, PageBE targetPage, string userDescription, string fileName, bool isMsWebDav)
        {
            if (_dekiContext.Instance.MaxFileSize < filesize)
            {
                throw new AttachmentMaxFileSizeAllowedInvalidArgumentException(_dekiContext.Instance.MaxFileSize);
            }
            var saveFileName = ValidateFileName(fileName);

            if (existingRevision != null)
            {
                if (!saveFileName.EqualsInvariant(existingRevision.Name))
                {
                    // An existing file is getting renamed. Make sure no file exists with the new name
                    var existingAttachment = GetPageAttachment(targetPage.ID, saveFileName);
                    if (existingAttachment != null)
                    {
                        throw new AttachmentExistsOnPageConflictException(saveFileName, targetPage.Title.AsUserFriendlyName());
                    }
                }
            }

            // If file is found but has been deleted, create a new file.
            if (existingRevision != null && existingRevision.ResourceIsDeleted)
            {
                existingRevision = null;
            }
            if (isMsWebDav)
            {
                _log.DebugFormat("Upload client is MD WebDAV, provided mimetype is: {0}", mimeType);
                var extensionFileType = MimeType.FromFileExtension(Path.GetExtension(saveFileName));
                if (!extensionFileType.Match(mimeType) || extensionFileType == MimeType.DefaultMimeType)
                {
                    mimeType = existingRevision == null ? extensionFileType : existingRevision.MimeType;
                    _log.DebugFormat("using mimetype '{0}' instead", mimeType);
                }
            }
            ResourceBE attachment;
            var        resourceContents = new ResourceContentBE((uint)filesize, mimeType);
            var        isUpdate         = false;

            if (existingRevision == null)
            {
                attachment = _resourceBL.BuildRevForNewResource((uint)targetPage.ID, ResourceBE.ParentType.PAGE, saveFileName, mimeType, (uint)filesize, null, ResourceBE.Type.FILE, _dekiContext.User.ID, resourceContents);
            }
            else
            {
                isUpdate   = true;
                attachment = BuildRevForContentUpdate(existingRevision, mimeType, (uint)filesize, null, saveFileName, resourceContents);
            }

            // rewrite mimetype to text/plain for certain extensions
            string extension = attachment.FilenameExtension;

            if (_dekiContext.Instance.FileExtensionForceAsTextList.Any(forcedExtensions => extension == forcedExtensions))
            {
                attachment.MimeType = MimeType.TEXT;
            }

            // Insert the attachment into the DB
            attachment = SaveResource(attachment);
            try {
                // Save file to storage provider
                _dekiContext.Instance.Storage.PutFile(attachment, SizeType.ORIGINAL, new StreamInfo(filestream, filesize, mimeType));
            } catch (Exception x) {
                _dekiContext.Instance.Log.WarnExceptionFormat(x, "Failed to save attachment to storage provider");

                // Upon save failure, delete the record from the db.
                _session.Resources_DeleteRevision(attachment.ResourceId, attachment.Revision);
                throw;
            }

            // Set description property
            if (!string.IsNullOrEmpty(userDescription))
            {
                attachment = SetDescription(attachment, userDescription);
            }

            // For images resolve width/height (if not in imagemagick's blacklist)
            attachment = IdentifyUnknownImage(attachment);

            // Pre render thumbnails of images
            AttachmentPreviewBL.PreSaveAllPreviews(attachment);
            PageBL.Touch(targetPage, DateTime.UtcNow);

            //TODO MaxM: Connect with transaction
            RecentChangeBL.AddFileRecentChange(targetPage.Touched, targetPage, _dekiContext.User, DekiResources.FILE_ADDED(attachment.Name), 0);
            if (isUpdate)
            {
                _dekiContext.Instance.EventSink.AttachmentUpdate(_dekiContext.Now, attachment, _dekiContext.User);
            }
            else
            {
                _dekiContext.Instance.EventSink.AttachmentCreate(_dekiContext.Now, attachment, _dekiContext.User);
            }
            return(attachment);
        }
 private static DreamMessage Map(AttachmentPreviewBadImageFatalException e, DekiResources resources)
 {
     return(new DreamMessage(DreamStatus.InternalError, null, e.PreviewMimeType, e.PreviewImage));
 }
 public ConflictSubClass() : base(DekiResources.LANGUAGE_SET_TALK())
 {
 }
 public BadCallSubClass() : base(DekiResources.LANGUAGE_SET_TALK())
 {
 }
 public MissingSubClass() : base(DekiResources.LANGUAGE_SET_TALK())
 {
 }
 private static DreamMessage Map(MindTouchAccessDeniedException e, DekiResources resources)
 {
     return(DreamMessage.AccessDenied(e.AuthRealm, resources.Localize(e.Resource)));
 }
 //--- Misc exception handlers ---
 private static DreamMessage Map(MindTouchInvalidOperationException e, DekiResources resources)
 {
     return(DreamMessage.BadRequest(e.Message));
 }
 private static DreamMessage Map(MindTouchNotFoundException e, DekiResources resources)
 {
     return(DreamMessage.NotFound(resources.Localize(e.Resource)));
 }
 private static DreamMessage Map(MindTouchConflictException e, DekiResources resources)
 {
     return(DreamMessage.Conflict(resources.Localize(e.Resource)));
 }
 private static DreamMessage Map(MindTouchFatalCallException e, DekiResources resources)
 {
     return(DreamMessage.InternalError(resources.Localize(e.Resource)));
 }
 //--- generic ResourcedMindTouchException handlers ---
 private static DreamMessage Map(MindTouchInvalidCallException e, DekiResources resources)
 {
     return(DreamMessage.BadRequest(resources.Localize(e.Resource)));
 }
Esempio n. 25
0
 public void Setup() {
     _resourceManagerMock = new Mock<IPlainTextResourceManager>();
     _resources = new DekiResources(_resourceManagerMock.Object, CultureInfo.InvariantCulture);
 }
Esempio n. 26
0
 //--- Constructors ---
 protected AttachmentBL() {
     _dekiContext = DekiContext.Current;
     _session = DbUtils.CurrentSession;
     _resources = _dekiContext.Resources;
     _resourceBL = ResourceBL.Instance;
 }
Esempio n. 27
0
        private static void TruncateList(XDoc list, int selectedIndex, bool hidden)
        {
            var resources = DekiContext.Current.Resources;
            int count     = list.ListLength;

            // check if the selected node is a phantom node (i.e. it's not in the list, but must be accounted for)
            bool phantom = false;

            if (selectedIndex < 0)
            {
                phantom = true;
                ++count;
                selectedIndex = ~selectedIndex;
            }
            int max_list_count = DekiContext.Current.Instance.NavMaxItems;
            int firstVisible;
            int lastVisible;

            if (max_list_count <= 0)
            {
                firstVisible = 0;
                lastVisible  = count - 1;
            }
            else
            {
                firstVisible = Math.Max(0, selectedIndex - max_list_count / 2);
                lastVisible  = Math.Min(count - 1, selectedIndex + max_list_count / 2);
                firstVisible = Math.Max(0, Math.Min(firstVisible, lastVisible - max_list_count + 1));
                lastVisible  = Math.Min(count - 1, firstVisible + max_list_count - 1);
            }
            if (firstVisible == 1)
            {
                firstVisible = 0;
            }
            if (lastVisible == (count - 2))
            {
                lastVisible = count - 1;
            }

            // set nodes as hidden
            List <string> firstClosed = new List <string>();
            List <string> lastClosed  = new List <string>();
            XDoc          first       = null;
            XDoc          last        = null;
            int           counter     = 0;

            foreach (XDoc current in list)
            {
                if (!phantom || (counter != selectedIndex))
                {
                    if (counter < firstVisible)
                    {
                        XDoc css = current["@class"];
                        if (first == null)
                        {
                            first = current;
                        }
                        else
                        {
                            css.ReplaceValue(css.Contents + (hidden ? " hiddenNode" : " closedNode hiddenNode"));
                            firstClosed.Add(current["@id"].Contents);
                        }
                    }
                    else if (counter > lastVisible)
                    {
                        XDoc css = current["@class"];
                        if (last == null)
                        {
                            last = current;
                        }
                        else
                        {
                            css.ReplaceValue(css.Contents + (hidden ? " hiddenNode" : " closedNode hiddenNode"));
                            lastClosed.Add(current["@id"].Contents);
                        }
                    }
                }
                ++counter;
            }

            // convert first and last node into '...' nodes
            if (first != null)
            {
                first["@class"].ReplaceValue(first["@class"].Contents + " moreNodes");
                XDoc address = first["a"];
                first.Attr("content", address.AsXmlNode.InnerText);
                first.Attr("contentTitle", address["@title"].Contents);
                first.Attr("hiddenNodes", string.Join(",", firstClosed.ToArray()));
                address.ReplaceValue(string.Empty);
                address["span"].Remove();
                address.Start("span").Attr("class", "more").Value("...").End();
                address["@title"].ReplaceValue(resources.Localize(DekiResources.MORE_DOT_DOT_DOT()));
            }
            if (last != null)
            {
                last["@class"].ReplaceValue(last["@class"].Contents + " moreNodes");
                XDoc address = last["a"];
                last.Attr("content", address.AsXmlNode.InnerText);
                last.Attr("contentTitle", address["@title"].Contents);
                last.Attr("hiddenNodes", string.Join(",", lastClosed.ToArray()));
                address.ReplaceValue(string.Empty);
                address["span"].Remove();
                address.Start("span").Attr("class", "more").Value("...").End();
                address["@title"].ReplaceValue(resources.Localize(DekiResources.MORE_DOT_DOT_DOT()));
            }
        }
 public void Setup()
 {
     _resourceManagerMock = new Mock <IPlainTextResourceManager>();
     _resources           = new DekiResources(_resourceManagerMock.Object, CultureInfo.InvariantCulture);
 }
Esempio n. 29
0
 public void Can_localize_string_resource_mixed_builder() {
     var resourceManagerMock = new Mock<IPlainTextResourceManager>();
     var resources = new DekiResources(resourceManagerMock.Object, CultureInfo.InvariantCulture);
     resourceManagerMock.Setup(x => x.GetString("x", CultureInfo.InvariantCulture, null))
         .Returns("abc").AtMostOnce().Verifiable();
     resourceManagerMock.Setup(x => x.GetString("y", CultureInfo.InvariantCulture, null))
         .Returns("xyz").AtMostOnce().Verifiable();
     var b = new DekiResourceBuilder();
     b.Append(new DekiResource("x"));
     b.Append("+");
     b.Append(new DekiResource("y"));
     b.Append("-");
     Assert.AreEqual("abc+xyz-",b.Localize(resources));
     resourceManagerMock.VerifyAll();
 }
 public FatalSubClass() : base(DekiResources.LANGUAGE_SET_TALK())
 {
 }
Esempio n. 31
0
        private DekiResourceBuilder CompareTagSets(List <TagBE> current, List <TagBE> proposed, out List <TagBE> added, out List <uint> removed)
        {
            // perform a subtraction of tags (in both directions) to determine which tags were added and removed from one set to another
            added   = new List <TagBE>();
            removed = new List <uint>();
            TagComparer tagComparer = new TagComparer();

            current.Sort(tagComparer);
            proposed.Sort(tagComparer);

            // determine which pages have been added
            StringBuilder addedSummary = new StringBuilder();
            List <TagBE>  addedList    = new List <TagBE>();

            foreach (TagBE proposedTag in proposed)
            {
                if ((current.BinarySearch(proposedTag, tagComparer) < 0))
                {
                    // only add the tag if is not already being added
                    bool includeTag = (added.BinarySearch(proposedTag, tagComparer) < 0);
                    if (includeTag && (TagType.TEXT == proposedTag.Type))
                    {
                        TagBE proposedTagDefine = new TagBE();
                        proposedTagDefine.Type = TagType.DEFINE;
                        proposedTagDefine.Name = proposedTag.Name;
                        if (0 <= proposed.BinarySearch(proposedTagDefine, tagComparer))
                        {
                            includeTag = false;
                        }
                    }
                    if (includeTag)
                    {
                        added.Add(proposedTag);
                        if (1 < added.Count)
                        {
                            addedSummary.Append(", ");
                        }
                        addedSummary.Append(proposedTag.PrefixedName);
                    }
                }
            }

            // determine which pages have been removed
            StringBuilder removedSummary = new StringBuilder();

            foreach (TagBE currentTag in current)
            {
                if (proposed.BinarySearch(currentTag, tagComparer) < 0)
                {
                    removed.Add(currentTag.Id);
                    if (1 < removed.Count)
                    {
                        removedSummary.Append(", ");
                    }
                    removedSummary.Append(currentTag.PrefixedName);
                }
            }

            // create a diff summary string
            var diffSummary = new DekiResourceBuilder();

            if (0 < addedSummary.Length)
            {
                diffSummary.Append(DekiResources.TAG_ADDED(addedSummary.ToString()));
            }
            if (0 < removedSummary.Length)
            {
                if (!diffSummary.IsEmpty)
                {
                    diffSummary.Append(" ");
                }
                diffSummary.Append(DekiResources.TAG_REMOVED(removedSummary.ToString()));
            }
            return(diffSummary);
        }
 private static DreamMessage Map(MindTouchForbiddenException e, DekiResources resources)
 {
     return(DreamMessage.Forbidden(resources.Localize(e.Resource)));
 }
 private static DreamMessage Map(DekiLicenseException e, DekiResources resources)
 {
     return(DreamMessage.BadRequest(e.Message));
 }