SaaS CMS has officially launched! Learn more now.

file size within xhtmlString property

Vote:
 

Hi All,

I have a requirement to automatically attach the file type and size to links that are inserted via an editor in an editable area.

For example: change:my link

To:    my link (PDF 230kb)

What do you think is the best way to do this?

I've thought of:

  • attaching to the page saving event and altering the links there
  • adding a function to tinymce to insert on input / change
  • using jquery to find pdf links and using a service ajax style to return the document type and size

All seem to have a lot of negatives!

Can anyone think of a better way?

Cheers,
Paul

#90504
Sep 11, 2014 3:26
Vote:
 

Hi All, I resolved this by hooking into the page saving event.

The following code will automatically add ( ) eg. (PDF 203Kb) to any pdf,doc and docx links within the page content.

As the page saves:

  • Iterate through all page properties and filter the Xhtmlstring fields
  • Using the HTMLAgilityPack, locate all links
  • Load the document 
  • Filter only pdf, doc and docx
  • Attach file size and type to the link
  • Update the property

You could do this onPublish, but I chose onSaving as I wanted the editor/approvers to see exactly what the page would look like.

First install the HTMLAgilityPack from here I just put the dll in the bin folder and referenced it.

Here is the initialization code:

[InitializableModule]
    [ModuleDependency(typeof (EPiServer.Web.InitializationModule))]
    public class InitializationModule : IInitializableModule
    {     


      public void Initialize(InitializationEngine context)
        {
            if (!_eventsAttached)
            {
                DataFactory.Instance.SavingPage += PageSavingPageHandler;
                _eventsAttached = true;
            }
        }

       private void PageSavingPageHandler(object sender, PageEventArgs e)
        {
            // Go through each property looking for xhtmlString properties
            foreach (var prop in e.Page.Property)
            {
                if (prop.PropertyValueType.FullName == "EPiServer.Core.XhtmlString")
                {
                    // Use the HTML agility pack to find links within the HTML
                    HtmlAgilityPack.HtmlDocument theHtml = new HtmlDocument();
                    theHtml.LoadHtml(prop.Value.ToString());
                    var rootVal = theHtml.DocumentNode;
                    var linksInHtml = rootVal.SelectNodes("//*/a/@href");
                    if (linksInHtml != null && linksInHtml.Count > 0)
                    {
                        foreach (var fullLink in linksInHtml)
                        {
                            // Get the link. Internal link is in format:
                            // link/.aspx?id=
                            Match match =
                                new Regex(@"/link/.*?.aspx\?id=([0-9].*)\&").Match(fullLink.Attributes["href"].Value);
                            var docId = 0;
                            if (match.Success)
                            {
                                int.TryParse(match.Groups[1].Value, out docId);
                            }
                            // if we have a document ID then get the document and check extension
                            if (docId > 0)
                            {
                                var  contRef = new ContentReference(docId);
                                var contentRepository = ServiceLocator.Current.GetInstance();
                                var media = contentRepository.Get(contRef);
                                var isPdf = media.RouteSegment.ToLower().EndsWith(".pdf");
                                var isDoc = media.RouteSegment.ToLower().EndsWith(".doc");
                                var isDocX = media.RouteSegment.ToLower().EndsWith(".docx");
                                if (isDoc || isPdf || isDocX)
                                {
                                    // check if it already has a size and type and remove it
                                    // as the link may have been re-linked or the document updated
                                    // having a different size.
                                    fullLink.InnerHtml = Regex.Replace(fullLink.InnerHtml,
                                                                       @"\([A-Za-z]{3,4} [0-9]*[A-Za-z]{0,2}\)$",
                                                                       string.Empty);
                                    if (contRef.ID > 0)
                                    {
                                        int size = 0;
                                        if (media.BinaryData!=null)
                                        {
                                            using (var stream = media.BinaryData.OpenRead())
                                            {
                                                size = (int) stream.Length;
                                            }
                                        }

                                        if (size > 0)
                                        {
                                            if (isDoc)
                                            {
                                                fullLink.InnerHtml +=
                                                    string.Format(new FileSizeFormatProvider(),
                                                                  " (DOC {0:fs})", size);
                                            }
                                            else if (isPdf)
                                            {
                                                fullLink.InnerHtml +=
                                                    string.Format(new FileSizeFormatProvider(),
                                                                  " (PDF {0:fs})", size);
                                            }
                                            else
                                            {
                                                fullLink.InnerHtml +=
                                                    string.Format(new FileSizeFormatProvider(),
                                                                  " (DOCX {0:fs})", size);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    // reset the property value to the new value
                    e.Page.Property[prop.Name].Value = theHtml.DocumentNode.InnerHtml;
                }
            }
        }
}

Here is the FileSizeFormatProvider class (This is from the net, slightly modified, original and kudos here)

    public class FileSizeFormatProvider : IFormatProvider, ICustomFormatter
    {
        public object GetFormat(Type formatType)
        {
            if (formatType == typeof(ICustomFormatter)) return this;
            return null;
        }

        private const string fileSizeFormat = "fs";
        private const Decimal OneKiloByte = 1024M;
        private const Decimal OneMegaByte = OneKiloByte * 1024M;
        private const Decimal OneGigaByte = OneMegaByte * 1024M;

        public string Format(string format, object arg, IFormatProvider formatProvider)
        {
            if (format == null || !format.StartsWith(fileSizeFormat))
            {
                return defaultFormat(format, arg, formatProvider);
            }

            if (arg is string)
            {
                return defaultFormat(format, arg, formatProvider);
            }

            Decimal size;

            try
            {
                size = Convert.ToDecimal(arg);
            }
            catch (InvalidCastException)
            {
                return defaultFormat(format, arg, formatProvider);
            }

            string suffix;
            if (size > OneGigaByte)
            {
                size /= OneGigaByte;
                suffix = "Gb";
            }
            else if (size > OneMegaByte)
            {
                size /= OneMegaByte;
                suffix = "Mb";
            }
            else if (size > OneKiloByte)
            {
                size /= OneKiloByte;
                suffix = "Kb";
            }
            else
            {
                suffix = "b";
            }


            return String.Format("{0}" + "{1}", Convert.ToInt32(size), suffix);

        }

        private static string defaultFormat(string format, object arg, IFormatProvider formatProvider)
        {
            IFormattable formattableArg = arg as IFormattable;
            if (formattableArg != null)
            {
                return formattableArg.ToString(format, formatProvider);
            }
            return arg.ToString();
        }

    }

That's it.

Potentially, a similar method could be used to add other automated content when saving the page.

Hope it helps someone!

Cheers,
Paul

#90608
Sep 15, 2014 7:10
Vote:
 

Hi Klinga

Thank you for sharing such nice solution to us. I really appreciate for your sharing. 

#90667
Sep 16, 2014 5:45
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.