Quantcast
Channel: Optimizely Search & Navigation
Viewing all 6894 articles
Browse latest View live

How would you improve this code?

$
0
0

Here's the scenario; we use a page's teaser image to populate meta tags such as og:image or twitter:image so that if someone were to try and share one of our pages on social media, that is the image which is auto-populated in whatever sharing method / tool they are using. We love a good SVG, but as it turns out, some of the social sharing tools don't. Facebook for example won't render SVGs and so that image is either left blank when someone shares or that sharer has to be diligent enough to manually select an image - doesn't happen that often.

There were a few avenues we could explore as solutions to this. We looked at just having one static image for sharing - company logo perhaps, which seemed a bit boring. Or possibly a suite of static images which could be swapped based on page type. That seemed a bit more flexible and dynamic, but if we wanted dynamic then why not do something with the SVG itself? Like converting the SVG to a PNG on the fly! We looked around online, but couldn't see anyone who'd already developed this before and it sounded like the most fun approach so that's what we went with.

The last thing to note is that we're running EPiServer 11 in DXC which means Azure Blobs!

In _Root.cshtml we had code that looked like this:

<meta name="twitter:image" content="@Model.CurrentPage.TeaserImage.ContentExternalUrl(System.Globalization.CultureInfo.InvariantCulture, true)" />

We wanted to replace the call to ContentExternalUrl() with some helper method which would check for the presence of an SVG and convert that to a suitably sized PNG and deliver that to the browser. Thinking ahead, we expected that conversion process to impact server performance to some degree so we only wanted to do the work when needed. With that in mind we wanted the helper method to check to see if we'd already done the conversion work before and deliver that image instead of doing it again and again.

We looked around online and decided that the package Svg v2.4.2 could do what we wanted.

I'll paste the code below which is reasonably commented, but the _Root.cshtml now contains:

<meta name="twitter:image" content="@SocialImageSharingHelper.ConvertImage(Model.CurrentPage.TeaserImage, ".png", Model.CurrentPage.ContentLink).ContentExternalUrl(System.Globalization.CultureInfo.InvariantCulture, true)" />

and here is the helper:

public static class SocialImageSharingHelper
    {
        //Summary:
        //  This helper is intended to be used in _Root.cshtml
        //  When pages have a teaser image in SVG format they are not
        //  always shared through social media in a desired way as
        //  we set the teaser image to the meta property og-url
        //
        //Parameters:
        //  imageReference:
        //      The ContentReference of the image to be converted.
        //
        //  fileExtension:
        //      string value of the file type the image is to be converted to. Must include the .
        //
        //  imageFolderReference:
        //      The ContentReference to the folder the current image is held.
        //
        //Returns:
        //      The ContentReference to the converted image, if conversion has happened. Else the original ContentReference
        //
        //TODO convert this to a service and use Dependency Injection (construction)
        public static ContentReference ConvertImage(ContentReference imageReference, string fileExtension, ContentReference imageFolderReference)
        {
            var contentRepository = ServiceLocator.Current.GetInstance<IContentRepository>();
            var blobFactory = ServiceLocator.Current.GetInstance<IBlobFactory>();
            var contentAssetHelper = ServiceLocator.Current.GetInstance<ContentAssetHelper>();
            var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();
            //get the current image
            ImageData currentImage = contentRepository.Get<ImageData>(imageReference);
            //get the path to the current image
            string currentImagePath = ServiceLocator.Current.GetInstance<UrlResolver>().GetVirtualPath(imageReference).GetUrl(true);
            //get the extension of the file
            string extension = Path.GetExtension(currentImagePath);
            //if the file isn't an SVG then we don't need to convert, return early with the original ContentReference
            if (extension != ".svg")
            {
                return imageReference;
            }
            //we've got this far so we know we want to convert, unless the work has already been done!
            //TODO: Check to see if converted file already exists and if so return that reference
            //GetBlob only works with Uri id, so we can't just use a file name.
            //explore the current page's assets folder (as this is where we would have stored any converted files)
            string theFileWeAreLookingFor = $"{Path.GetFileNameWithoutExtension(currentImagePath)}{fileExtension}";
            var folder = contentAssetHelper.GetAssetFolder(imageFolderReference);
            foreach (var item in contentLoader.GetChildren<MediaData>(folder.ContentLink))
            {
                //check if item is the converted file we were looking for
                if ( item.Name.Equals(theFileWeAreLookingFor))
                {
                    return item.ContentLink;
                }
            }
            SvgDocument svgDocument;
            try
            {
                //SVgDocument.Open() likes to read files on disk and as ours are in Azure they aren't on disk, so we have
                //to use the overload for Stream
                //use the BlobFactory to get the image as an array of bytes
                byte[] blobBytes = blobFactory.GetBlob(currentImage.BinaryData.ID).ReadAllBytes();
                //read the byte array into memory as a stream
                using (MemoryStream ms = new MemoryStream(blobBytes))
                {
                    svgDocument = SvgDocument.Open<SvgDocument>(ms);
                }
            }
            catch(FileNotFoundException exception)
            {
                //something went wrong
                Console.Write(exception);
                return imageReference;
            }
            //imageFile is the blob container, location and data etc...
            var imageFile = contentRepository.GetDefault<ImageFile>(contentAssetHelper.GetAssetFolder(imageFolderReference).ContentLink);
            //Render the SVG as Bitmap using .Draw()
            //passing in 0 as the width element to keep aspect ratio
            var svgFileContents = svgDocument.Draw(0, 100);
            //Convert the Bitmap rendering
            ImageConverter converter = new ImageConverter();
            byte[] data = (byte[])converter.ConvertTo(svgFileContents, typeof(byte[]));
            //Create a blog with the file extension and everything.
            var blob = blobFactory.CreateBlob(imageFile.BinaryDataContainer, fileExtension);
            //File the blob with byte array data
            using (var s = blob.OpenWrite())
            {
                var w = new StreamWriter(s);
                w.BaseStream.Write(data, 0, data.Length);
                w.Flush(); //flush the writer once we're done
            }
            //assign the converted blob data
            imageFile.BinaryData = blob;
            /* Set the name of the file */
            imageFile.Name = $"{Path.GetFileNameWithoutExtension(currentImagePath)}{fileExtension}";
            /*add alt text otherwise the SaveAction.Publish will error*/
            imageFile.Description = $"{currentImage.Name} - This image was autogenerated.";
            //imageFile.Description = $"{currentImage.Description} - This image was autogenerated.";
            /* Consider providing an async .Save as per DXC Training page 62*/
            ContentReference convertedImage = contentRepository.Save(imageFile, SaveAction.Publish, AccessLevel.NoAccess);
            return convertedImage;
        }
    }

Lot's of borrowed code up and typos in there which we've ungracefully smashed together until the errors went away and converted PNGs were delivered to the browser!

So, as the title says, how would you improve this code? Any thoughts or constructive critisisms are most welcome.

Alex


Determine if Find index is connected or 401 in code

$
0
0

My developer index timed out and this presented a great oppertunity for me to pop out friendly errors instead of a 401 in case we run into a network glitch in production

I'm trying to find a way to determine of the index invalid or unreachable  (we are using on-prem and there is a chance we can't access the index) so that I can present a friendly error instead of a system 401 error.   

The example code has a function to determine of the search in configured

private bool SearchIndexIsConfigured(EPiServer.Find.Configuration configuration)
        {
            return (!configuration.ServiceUrl.IsNullOrEmpty()&& !configuration.ServiceUrl.Contains("YOUR_URI")&& !configuration.DefaultIndex.IsNullOrEmpty()&& !configuration.DefaultIndex.Equals("YOUR_INDEX"));
        }

This works great, but it doesn't account for if the service is running or a 401.  Is there anything I can do to check for this?  The only thing I can see is the settings is the query settings times out.  For now I can play with that, but I'm certain there is a more graceful way to handle this.

Thanks!

Component ClientResources Path

$
0
0

Hello,

I have been banging my head against the wall for a while now trying to figure out what I am doing wrong here. I am trying to package my dashboard component, but when I try to reference the dojo script, it throws a 404 because it is looking in the ~/Episerver/Shell/{version}/ folder instead of my packaged component folder. Looking at the component package it all seems to be structured correctly in ~/modules/_protected/{package_name}. I am assuming its something I am doing wrong with my module.config but I can't seem to narrow it down.

<?xml version="1.0" encoding="utf-8"?><module productName="componentName" clientResourceRelativePath="1.0.9" loadFromBin="false" tags="EPiServerModulePackage"><assemblies><add assembly="company.componentName" /></assemblies><dojo><paths><add name="componentName" path="ClientResources/Scripts" /></paths></dojo><clientModule><moduleDependencies><add dependency="Shell" /><add dependency="CMS" /></moduleDependencies></clientModule></module>

Can anyone point me in the right direction?

Thanks

Scheduled Publish

$
0
0

Hey,

Recently my schedule publish has just stopped working.  Sometimes it invalidates on remote servers but the current server hasn't been invalidated and some of the post publish code hadn't completed running.  Looking at http://joelabrahamsson.com/how-episerver-cms-caches-pagedata-objects/ and other pages, they suggest that EpiServer delayed publish does not invalidate the cache on the other servers.  Is this correct? and if so does anyone know why?

Strange behavior when editing links in TinyMCE

$
0
0

We have encountered some strange behavior when editing links in the TinyMCE editor. When creating a link using the "Page" radio button and entering "Remaining url" the information is not preserved correctly. The behavior is only reproducible inside a TinyMCE editor, not on Url properties.

It occurs on a freshly installed Alloy site with all the latest updates, namely:

Episerver 11.11, TinyMCE 2.7.1

How to reproduce:
1. In Cms Edit: open a page with a XhtmlString property (such as About us on Alloy)
2. Enter some text and select Insert link
3. Choose Page and select a page
4. Enter a value in Remaining Url
5. Publish

At this point the link works correctly

6. Reload Cms Edit window
7. Click Insert/edit

At this point the link is changed to External

8. Edit any part of the XhtmlString to trigger a Save then Publish


The link no longer works and instead looks something like on the public site: /EPiServer/CMS/Content/en/alloy-plan,,6/test?epieditmode=False&amp;epsremainingpath=test

Is this a known bug? Is there a workaround?

Putting 2 contentarea side-by-side

$
0
0

Hi, I am new on Episerver. This is my 1st project. I may be asking a very simple question.

I am puttting 2 content areas side-by-side.

<div class="row">
@Html.PropertyFor(x => x.CurrentPage.LeftContentArea,
new{CssClass = "col"})
<div class="w-100 d-sm-none">&nbsp;</div>
@Html.PropertyFor(x => x.CurrentPage.RecentlyUpdateContentArea,
new{CssClass = "col"})
</div>

In CMS edit mode, they go on-and-on downward. Is there any render setting I need to put in?

Thanks.

Issue with link validator

$
0
0

There seems to be an issue with how the "Link Validation" scheduled job defines what is a broken link.

How to reproduce:

  • Add pages to a contentarea
  • Delete one of the pages that is added to the contentarea (and possibly the trash needs to be emptied). The deleted page is now no longer visible in the contentarea.
  • Run link validation tool and check result in the "Link status" report.

The "link" to the deleted page is now reported as broken. Which is a bit confusing because if you go to the page with the contentarea everything looks good, no broken references.

If the editor e.g. re-order the pages in the content area (actually just need to move it to the same place), and then re-run the link validation tool, there is no "broken" link anymore.

Nor really sure how it should work, but the current way is not very good :-). We have a client that have hundreds of these "broken" links in the Link status report.

Restricting environment access IPV6


Commerce Manager - Pagination in custom business foundation object views

$
0
0

Hi,

I was wondering if it is possible to get pagination in views for custom Business Foundation objects in Commerce Manager.

For example: when you extend a Business Foundation object (e.g. CustomerContact) with a list you will see a new clickable item on the left side (just like addresses, orders, etc.). When the view is loaded all the data is loaded at once, with no pagination. Also the html contains the data, but the scrollbar is not visible. So scrolling through the list is not possible.

I have seen pagination for Addresses(Organization and CustomerContacts), Orders(CustomerContacts), Customers (Organization). But not yet for view for custom BF objects. 

Is it posible to get the pagination there?

Regards,

Lisanne

Localization service : multiple fallback languages

$
0
0

Hi Everyone, 

I'm currently looking into providing multiple fallback languages (based on the fallback languages configured per page in EPiServer CMS) when retrieving translate labels from the localization service. 

The idea was to get the fallbacks for the current page, and effectively "test" to see if a label was available in a given fallback language - if it isn't, try the next one, and so on. 

The problem I have is that when making a call such as this one:

var labelInLanguage = LocalizationProvider.Current.GetStringByCulture(label, language);

Inside GetStringByCulture in the localization service I see:

CultureInfo actualFallbackCulture = this.GetActualFallbackCulture(fallbackBehavior, culture);
string localizedString;
if (this.TryGetStringByCulture(resourceKey, normalizedKey, culture, actualFallbackCulture, out localizedString))
return localizedString;
return this.GetMissingFallbackResourceValue(fallbackBehavior, fallback, resourceKey, culture);


GetActualFallbackCulture will give me the invariant culture for my culture I've provided - in the of trying to get a string for en-US it will give me EN and use this in the TrygetStringByCulture call just below it.
So it seems like I will always get a value back from the localization service regardless of if I specify no fallbacks in the provider.

The above logic looks inevitable no matter what localization configuration I use, which means I'd have no way to know if the fallback language I've provided has an actual localized label, or if its the result of the invariant culture being used.

Any help or insight is much appreciated!

Thanks,

Paul

Make LinkItem a valid property type

$
0
0

LinkItemCollection is great but if you only want one link it would be nice to be able to create a LinkItem property.

Hiding Submit Button Until Form is Filled In

$
0
0

Is there a way to disable/hide the submit button until all fields are filled in? With the latest forms update, the Dependencies tab shows up on all the form fields but the submit button. Is this something that was omitted intentionally, or should I be seeing this tab on the submit configuration as well?

How to add control browser popups and select catalog in custom property

$
0
0

Hi,

I want to add a control to show popup and select a catalog node in my custom property but i just found a control episerver:inputpagereference and this control can't brown catalog

Does anyone have solution for that?

Thanks,

Thao Pham

Episerver Forms 4.6.0 missing some method in DataExportingController

$
0
0

Hello, 

I have a custom funtion to export submit form as an excel file. It was working well untill I upgraded epi forms to version 4.6.0, then all pages on my site that using epi froms not working at all. It seem like those method were gone in DataExportingController version 4.6.0

protected virtual DataTable CreateTable(IEnumerable friendlyNames);
protected virtual DataExporterBase GetExporterFromExtension(string extension);
protected virtual string GetValidColumnName(string rawName);

And I need those methods. Does anyone know if epi they changed name or move to somewhere else?

Regards,

/Katharina

Real-time update error after upgrading to Episerver 11.11.3.0

$
0
0

Hello,

After upgrading to Episerver 11.11.3.0 I get the "real-time update" error when switching from the admin panel to the edit panel.

Websockets is enabled on the server. A similar Episerver site which was recently upgraded to 11.11.2.0 does not show this error.

Any ideas, anyone?

John Ligtenberg 


Add support for culture specific properties on MediaData

$
0
0

As far as I know there's no official way of adding culture specific properties to MediaData content.
For example: I would like to add a culture specific description and alt text to an image and I'd like to translate that.

Do you have any suggestions on how to do this, is support for this on the roadmap somewhere?

I know about these options but I was wondering if there's any better alternative (as I don't think media is indexed in different languages, for example): 
https://www.patrickvankleef.com/2014/06/16/use-culture-specific-mediadata-properties/ 
https://gregwiechec.com/2015/07/localizable-media-assets/ 
https://support.episerver.com/hc/en-us/articles/360000495192

Commerce Tracking

$
0
0

Commerce 12.11.0

Hi

Does anyone know if the  CommerceTrackingAttribute can be used on Web Api controller actions ? And by default sends the User tracking data i.e Name and email of logged in user ?

Also whether using the TrackingDataFactory Create methods automatically constructs the User tracking data from the logged in user ?

Thanks

Issue whith TinyMCE 2 and inline blocks

$
0
0

We have problem with Tinymce : when you drop a block in a XHTML it has div tags around it even if the block in view mode is only showing a value.

Tinymce adds p tag around witch results in wrong html for the property.

I managed to add a setting to tinymce but it's only working when the editor drops the block.

When editing a xhmtl with a block in it, Tinymce changes the html despite the setting:  .AddSetting("valid_children", "+p[div]") .

Has someone managed to solve the problem with block div in p tags in xthml properties?

Return page when getting hit on the name of a block on that specific page?

$
0
0

Hi!

I have the following scenario:

We are using Unified Search and Find .NET API-version: 12.6.2.0

We have a page type called ThemePage. On that page we have a content area in which resides a block of type SimpleTextBlock. Let us say the block has "lorem ipsum" in both name and content.

The page type is indexed, the block type is indexed and IndexingInContentAreas is set.

How can I get the contents of the block indexed on the page?

On the same page there is a content area containing several pages that have been dragged-and-dropped, and they seem to get indexed.

Appreciate any help and hopefully you understand what I mean :-)

Include langue in the Link Status report

$
0
0

The Link Status report shows if there is a boken link on a specifik page/block, but it doesn't say on which language. It's really cumbersome to chase down that broken link if you have 20+ languages... 
I guess this is a 15 min fix since the language id is available in tblContentSoftlink.

Thanks!

Viewing all 6894 articles
Browse latest View live