volume_up

A critical vulnerability was discovered in React Server Components (Next.js). Our systems remain protected but we advise to update packages to newest version. Learn More

volume_up

A critical vulnerability was discovered in React Server Components (Next.js). Our systems remain protected but we advise to update packages to newest version. Learn More

file size within xhtmlString property

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

Hi Klinga

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

#90667
Sep 16, 2014 5:45
error 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.