SymSoft Solutions https://www.symsoftsolutions.com High Performance Websites for Enterprises Tue, 14 Jul 2020 21:01:55 +0000 en-US hourly 1 https://wordpress.org/?v=5.4.1 Design Systems for Enterprises (Benefits, Challenges, and Best Practices) https://www.symsoftsolutions.com/blog/2020/02/12/design-systems-for-enterprises-benefits-challenges-and-best-practices/ Wed, 12 Feb 2020 08:53:58 +0000 https://www.symsoftsolutions.com/?p=4434 Design systems have emerged as a popular tool for organizations in recent years because of their ability to support business goals and communicate brand values by providing a unified and consistent design language to work with. With this integrated set of tools and guidelines, organizations are able to effectively utilize design across the entire product line, making decisions easier, saving more time, and optimizing the end-user experience.

What are Design Systems?

InVision’s Design Systems Handbook defines a design system as “a collection of reusable components, guided by clear standards, that can be assembled to build any number of applications.”

Although this definition sounds clear and simple, it is still easy to confuse a design system with a pattern library or a style guide. So, what are the differences?

  1. A pattern library, in a literal sense, is a collection (or a library) of design patterns and user interface components that are used to build an application or a website.
  2. A style guide refers to documentations that define the basics in a design system — such as brand language, or examples of use for different interfaces and content elements.
  3. A design system is a combination of these two, but it also goes further by including design guidelines and principles to support better user experience, facilitate healthy design system development, and organizational acceptance.

 

A diagram of design system elements.

Style guides, pattern libraries, and design rules are all part of the design system.

Examples of Design Systems

 

Salesforce’s Lightning Design System

Salesforce’s Lightning Design System has a clearly structured menu consisting of design guidelines, accessibility guidelines, component library, utilities, etc.

Examples of Style Guides

 

Style guide of California Community Colleges

The style guide of California Community Colleges website documents guidelines and rules of the brand basics, such as color, logo, typography, etc.

Examples of Pattern Libraries

 

CEC Pattern Library

California Energy Commission’s pattern library documents the user interface patterns and components as well as the development code to be used on their website. For the purpose of creating pattern libraries for our recent projects, SymSoft’s front-end engineers used Fractal, an open-source library for building and documenting interface components.

Benefits of Design Systems

Last year our design team was collaborating with the CalPERS User Experience Team to build a design system for the Web, based on the existing brand guidelines and organizational objectives. By integrating brand language with design assets as well as design principles and guidelines, the CalPERS design system provided the in-house team with a sustainable model to build products such as myCalPERS, as well as to communicate with other product teams.

Designers discussing brand elements of the smud.org website

A good design system can bring extensive benefits to organizations, including:

  1. Brand recognition — A good design system communicates a company’s value. Building a design system is beneficial for strengthening brand recognition and conveying the brand’s core values.
  2. Improved communication and collaboration — Well-defined design principles and clearly documented standards can help different disciplines understand why and how to use the components and assets. In this case, design systems can enhance communication and collaboration across disciplines, such as designers, developers, marketing specialists, and executives.
  3. Increased speed and productivity — Design systems can increase speed and work productivity. Since the user interface is broken down into smaller reusable components, design and development teams don’t have to reinvent the wheel, especially if the elements are already tested and validated.
  4. Space to focus on solving user needs — As the process of building a product becomes faster and more efficient, designers save more time to invest in understanding and solving user needs, as well as exploring more possibilities in the product.
  5. Better user experience — By utilizing a common design language provided by the design system, the product team is more likely to create a better user experience through the use of consistent and proven patterns.

As you can see, there are true benefits of using a design system because it can enhance brand recognition, team communication, and work efficiency, as well as achieve better time allocation and user experience. In the next section, we will explain the process of actually building a design system.

Building a Design System

There are six main steps for creating and maintaining a design system, including purpose identification, system audit, asset creation, visual language establishment, rationale documentation, and future improvement. These are not definitive, and by all means not set in stone, but based on our experience work quite well as a general framework for creating successful and usable design systems.

Step 1: Identify your needs

Just like in any other project that solves a problem, identify the needs and purposes of the design system. Who will use it? How often? Does it make sense to them? What are the expectations?

We like how Yesenia Perez-Cruz, author of “Expressive Design Systems,” starts her design system projects by talking to the people who are involved or will be actually using the system. For example, she would focus on talking to program and product managers, or designers and developers. From that, she defines a purpose statement — whether it states what kind of design system is needed, or if a design system is not the right answer for the organization’s needs, which in some cases, is true.

If there is a need for a design system, we want it to communicate brand values. Part of identifying needs, is, coming up with design rules and principles, which are the foundation for any well-built design system.

Alla Kholmatova’s book “Design Systems,” outlines four steps for defining design principles:

  1. Start with the larger goal of the product and try to match the principles to that goal
  2. Gather a team to come up with shared values
  3. Be clear about who the audience is
  4. Constantly test and improve design principles

Once the needs are clearly defined, proceed to the next step, the system audit.

(Sometimes, all you need to define the needs is a simple list of design principles. And even if the design system already exists, it might not be clearly or completely articulated. This is perfectly fine too. In such cases, we can take a step back and realize that the organization primarily needs a flexible pattern library that will provide brand and user experience consistency across digital properties. In any case, use the above guidelines to better understand the gap between where you at right now and where you want to be.)

Step 2: Audit the system

Conduct a design audit by reviewing current designs and taking an inventory of design assets being used throughout the product line.

A good way to perform a thorough audit is to collect screenshots of current design elements, and then categorize these elements and document them into one place, whether it’s a spreadsheet or a design tool file. A choice of a design tool really doesn’t matter much at this point (but if you want to see what we like about some of the popular design tools, read our article Digital Product Design Tools We Love).

Laptop showing a list of components in a spreadsheet

After creating the current design inventory, make a plan for creating the user interface library, also known as a pattern library, which is the core of the design system.

Step 3: Create a component library

One way to start creating the component library is by breaking down the work into different layers.

There is a general rule of component hierarchy to follow, which is also mentioned by Yesenia Perez-Cruz.  She suggests dividing items into basic components, composite components, containers (or regions), and layout (or template), very similar to the Atomic Design methodology invented by Brad Frost.

Start by documenting the basics, which are the “smallest possible elements that can’t be broken down any further,” such as buttons and links. If you are familiar with Atomic Design, this would correspond to atoms. An example of an atom is a button, a form field, or a headline.

Buttons

The composite components (or molecules in Atomic Design language) are a combination of basic components (or atoms), adapted to more specific use cases. If a button and a form field are basic components, a composite would be a web form.

Password fields

Containers (also known as regions, or organisms in Atomic Design language) are simply areas that contain several composite components, whereas layouts (or templates) refer to how all the elements on a page are arranged. These two big picture layers are helpful for demonstrating how the content should be laid out on the page.

One common example is creating a featured content component that can host sub-components such as an image, video, map embed, or a list of items.

As you can imagine, the biggest challenge is providing flexibility, so that the team that uses these components has enough options to customize their layouts, presentation, and communication. Our own approach is to create (jargon alert!) context agnostic components that are compatible with any other component, or in many cases can be dropped into other components. (Context agnostic components work and look well next to any other component, or position on the interface. Think of different LEGO bricks that can be attached to one another to form any number of shapes, because their connectors, the little bumps, and holes, are mutually compatible.)

Step 4: Establish a visual identity

Every organization has its unique style and brand identity, so it’s important to establish a visual identity in order to strengthen the brand name and communicate values.

Erik Spiekermann, a famous type designer and author of many world-known brands, stated that a typeface plus a color is everything you need to establish a brand identity. Many other brand experts add a space into the mix. If these basic building blocks are carried over through the components, we can achieve visual consistency and brand recognition.

Example: New York Times vs Cereal

For example, with different density and weight in layout, as well as other visual choices for the building blocks, The New York Times and Cereal convey two completely different look-and-feel.

New York Times page

With relatively tight spacing and more serious type choices, the New York Times establishes its news site identity.

 

Read Cereal Website screenshot

Cereal has wider spacing and more artsy choices of color and typography, which conveys its identity as a travel and style magazine.

We can start establishing visual language across the interface to make our brand more recognizable by finding traits that best represent our brand identity. By fragmenting existing brand assets into basic elements — such as typography, shape, color, texture, and aspect ratios — we can identify which parts of that visual DNA can be replicated in the interface components.

 

CEC Illustration

Here is an example of how our design team took traits of print collaterals from the California Energy Commission and brought them to life through interface designs. We first identified distinctive graphic design elements, such as the pastel colors, diagonal shapes, dashed lines, as well as bold typography, and then created components that would work well in a digital interface context.

Example: California Energy Commission

California Energy Commission Website

Triangles and sun rays were elements found in many Energy Commission publications and high-profile reports…

 

California Energy Commission Pattern Library

The previous screenshot might make you think that the diagonal shapes are part of the component that follows, but, in fact, the shape belongs to the hero slider component.

Example: Sacramento Municipal Utility District (SMUD)

SMUD Illustration

Here is another example of how our design team broke up SMUD’s brand guidelines into graphic elements and transformed them into web page designs. Key elements included the color palette, the orange circle from the logo and iconography, and the SMUD wave that was incorporated into the website dropdown menu and the footer.

 

SMUD website branded mega menu

SMUD website features a branded mega menu…

 

SMUD website footer branding

… as well as a branded website footer.

 

Step 5: Propagate shared language

When different disciplines work on the same product, they need to share the same language about how the design system works. Clear documentation of the “why” and “how” behind each component is essential. Skipping this step might cause misunderstandings within the team, and people might not even embrace the design system because they don’t fully understand it and don’t know how to use it properly.

SymSoft team collaboration

Basically, shared guidelines for design, usability, accessibility, and development for the system, result in coherent user experience. Again, Alla Kholmatova suggests a useful checklist on communicating a shared design language within the team:

  • Setting up a pattern wall to showcase the design system
  • Referring to the name of the pattern in daily conversations
  • Introducing the design system to new employees,
  • Organizing regular design system catch-ups
  • Encouraging collaboration
  • Keeping a glossary of the used terms

Step 6: Evolve your design system

Completing the first version of your design system doesn’t mean we’re done with the work. Design systems are constantly evolving, as we keep iterating and improving our products. In this case, maintaining, as well as improving the system is another important mission for the team. A good way to improve our design system is to conduct testing sessions on real products to see how it performs.

Challenges with Design Systems

Design systems can be beneficial for a company if it’s robust and well-established. However, there are challenges we need to face or traps we need to be aware of, before having a good design system.

  1. Different opinions and scope discrepancy — When starting to create a design system, we need to build a multi-disciplinary team and have all the key staff on board. In this process, we might confront different opinions and discrepancies regarding the scope, depth or other aspects of the design system. In this case, it’s important to conduct thorough research on users and products and analyze the organization’s current situation, so that we can come up with the most appropriate plan and back up our plan with data and facts.
  2. Too rigid or overly complicated — While building the design system, we might encounter the challenge of how to build it so that it could actually be used and inspire more improvements. If we build a design system that’s not flexible enough, it would be more likely to inhibit creativity. Or if our design system turns out to be overly complicated, people might not even use it.  So we need to create flexible components and be clear and precise when setting our rules and standards.
  3. Missing communication on the latest updates — Another challenge we might face when building the design system is how to keep everyone on the same page. Since creating a design system is not a one-man effort and we do want people to be involved, it could be hard to keep all the related disciplines updated. In this case, communication is very important. We should try to make the whole process more transparent; for example, we can keep a spreadsheet documenting the progress of each component, so that people would know which components are done whereas others are in progress.
  4. Maintenance — After we finish building the design system, we still have to think about how we maintain and evolve the system because the design system should be changing over time with our products. Yesenia Perez-Cruz, in her book, “Expressive Design Systems,” mentioned three ways to manage design systems. First, we can rotate people who maintain the design system so that we always have fresh minds on which part of the system could be improved. The second way is to enable the product team to give feedback on the current design system because they are the ones that are most familiar with users and products. Lastly, we can encourage contributions from everyone. For example, IBM’s Carbon Design System has this approach of having a “How to Contribute” section, allowing people to make contributions to the design system.

 

IBM’s Carbon design system

Carbon Design System’s “How to Contribute” section with guidelines on how to participate in the maintenance and evolution of a design system.

Conclusion

When we work with clients who don’t have an existing design system, getting started on a new project means a dedicated time to align with different teams that have stakes or full ownership of the design specifications, HTML & CSS code, accessibility compliance, etc. For large organizations we work with, the time can add up significantly.

Thus, a good design system not only benefits designers but also does good to other disciplines of the organization. Development teams and design teams can share the same language, which enables better communication and decision-making, while project managers are more aware of the design choices we made, which bridges the technical knowledge gap and enables better justifications.

Now that the design system provides organizations a better way of optimizing the product design process, we might wonder where the future of design could go.

Design systems provide designers more freedom and time to create and explore. How can we make design systems more flexible? How can we make the innovation process more streamlined? Can we combine design systems with AI technology to achieve higher efficiency? These are the questions that we have. In the age of modern, user-centered organizations, we shall strive to innovate the way we see, feel and do designs.

(md, cm, yk, ma, xl)

 

Resources

  1. InVision’s Design System Handbook
  2. A Book Apart: Expressive Design Systems by Yesenia Perez-Cruz
  3. Design Systems Book by Alla Kholmatova
  4. Design System Gallery
  5. A Comprehensive Guide to Design Systems
  6. The Future of Design Systems (Youtube)
  7. Atomic Design by Brad Frost
  8. Design Systems in Adobe XD
  9. Selling a Design System at Your Company
  10. Figma’s Design Systems
  11. Design Systems at Medium.com
  12. Distinct Design Systems
]]>
Digital Product Design Tools We ❤ https://www.symsoftsolutions.com/blog/2020/02/05/digital-product-design-tools-we-love/ Wed, 05 Feb 2020 10:00:20 +0000 https://www.symsoftsolutions.com/?p=4429 About three years ago, the SymSoft Design team started to design our products and client solutions in an increasingly systematic fashion, looking at the whole map of the interface, rather than individual pages or components. We didn’t know back then that this would become, de facto, the standard way we approach every interaction design project. Fast forward to today, and a few successful projects later, the clients themselves expect a design system — or at least a pattern library — in place, in order to maintain brand experience across multiple digital channels and properties.

The benefits of creating a cohesive, yet reusable system are endless. Be it a full-blown design system, a style guide, or a pattern library, thinking systematically means thinking future proof.

Systematic approaches to design require tighter collaboration and sharing between different functions, primarily the brand managers, designers of all stripes, and technology folks, but also product owners, program managers, and executive stakeholders. Thankfully, there are a few solid software solutions to choose from that can make the process more seamless.

What follows is not a comprehensive list of tools, but rather a list of selected tools that we anticipate will soon become mainstream digital product design tools if they aren’t already. In this article, we will take a look at:

  1. Adobe XD
  2. Figma
  3. InVision Studio
  4. Sketch

(Other existing tools might be as valuable as the ones that made it to our list. We would definitely like to hear about your experience and learn about the advantages of other solutions.)

Before we dive deeper into why we like each tool, it’s important to remember that we are a tool-agnostic design team, which means that everyone gets to choose the best tool for a job, as long as they can export it to a common format and share it with the rest of the team. And yes, sometimes we will go into Keynote to quickly mock up a series of screens, or even design a high fidelity interface.

Overview of Tools

Adobe XD Figma InVision Studio Sketch
Learnability Easy Intermediate Advanced Easy
Platform Mac, Windows Mac, Windows, Linux, Web Mac, Windows Mac
Advanced Prototyping Basic Animation, Screen Recording Basic Animation, Animation Preview Advanced Animation Basic Animation
Collaboration Share and Comment Live Collaboration Live Collaboration with Freehand Available with Plugins
Code View Link Code Mode Link Available with Plugins
Component Reusability High High High High
Export / Compatibility jpg, png, pdf, svg jpg, png, pdf, svg jpg, png, svg jpg, png, pdf, svg, tiff, webp, eps
Convenient Sharing Share for Review / Development Share Source File / Prototype Share Prototype on InVision Share on Sketch Cloud

Adobe XD

Adobe XD file for the California Energy Commission website

Year of Release 2019
Download Link https://www.adobe.com/products/xd.html
Tutorials Link https://letsxd.com/?promoid=8WLD53GJ&mv=other
Brands that Use it / Popularity Microsoft, Dropbox, AT&T, DICE, SONOS, etc.
Roadmap / Future Features (from Adobe Blog)
  1. Design and prototyping: new design features (bulleted lists, etc.), intelligent tools, components enhancements (support for component states, etc.), new prototyping features (more granular control, etc.)
  2. Adobe Cloud: Creative Cloud Libraries, improved discoverability of plugins, etc.
  3. Collaboration: shared projects, extend real-time collaboration
  4. Design Systems: improve assets panel, enable design system documentation, provide additional control over sharing permissions, versioning, and updating design systems, support for style guides, etc.

Why we love it?

Adobe XD is the newest among the four tools mentioned in this article. It easily made its presence known because it was originated by Adobe, probably the most well-known design software ecosystem. We have built products in Adobe XD for previous projects that include websites and design systems, even for large organizations like CalPERS. It’s useful for communicating with clients and building design systems, and also easy to learn and adapt to.

Convenient client communication

All the design software mentioned in this article has a sharing option. Adobe XD supports two types of sharing — Share for Review and Share for Development. We use the Share for Review to receive client feedback, especially because they can annotate the prototype screens and add comments, whereas the Share for Development is intended for developers to view the code.

Adobe XD stands out for us as the most convenient for client communication because it’s the only one of the four that allows users to comment without signing up for an account. Another welcome feature is passcode protection, which keeps our projects more secure.

XD also has the most convenient prototype recording capability — accessible through a button in the top right of the preview window, with which designers can easily record the interaction and send it to the client for review.

Good for building design systems

The assets panel of Adobe XD makes it unique compared to the other three tools by having reusable components as well as colors and character styles all in one place. As a result of utilizing this, we can easily build a component library and a style guide consisting of the color and type systems.

Moreover, Adobe XD supports linked assets. This enables designers to import assets from an Adobe Cloud document and simultaneously use and update those assets across different projects. This was especially helpful when we were building the aforementioned design system for CalPERS. The ability to import linked components, colors and typography styles altogether into documents dramatically improves the workflow and keeps all the styles up-to-date.

Color palette Character Style Components

Zero learning curve

We found Adobe XD the easiest to learn and adapt to, especially for those designers who have experience using other Adobe products, namely Illustrator and Photoshop. With almost identical shortcuts and a very similar look-and-feel (for example, tools panel on the left, in vertical order), it reduces the learning curve for any experienced designer.

What we miss?

While XD is great for easy client communication and feedback, building design systems, and is easy to learn, there is room for improvement in the prototype mode.

Advanced interaction

Currently, XD only supports basic interactions such as tap and drag (by mouse). It doesn’t allow more advanced triggers like mouse over, double click, or swipe to different directions.

However, one thing to note is that XD is the only tool among the four that has a voice trigger, which is handy for prototyping conversational user interfaces. For example, Siri, Alexa, Google Home, and many more applications and devices are using the voice user interface to improve the overall customer experience.

Better connected design and prototype modes

Another potential issue with XD is that it separates the design and prototype modes in a way that sometimes stifles the workflow and makes our process disconnected.

For example, imagine this task — we want to create a menu bar component in the design mode that directs us to the same artboards wherever we use this component. In XD, it’s hard to accomplish that because when we switch to the prototype mode after we designed and used this component, we need to link everything over and over again.

Better artboard organization

XD only supports a single page with multiple artboards, whereas other design software usually supports multiple pages with multiple artboards. This could work for smaller projects, but for larger projects that need to be more organized, a single page might not be sufficient. Keeping a large number of artboards in one place could make it difficult to organize and navigate.

Figma

Designer working on the California Community Colleges Chancellor’s Office website mockup in Figma.app

Year of Release 2016
Download Link https://www.figma.com/downloads/
Tutorials Link https://www.figma.com/best-practices/
Brands that Use it / Popularity Twitter, Dropbox, Zoom, Microsoft, Github, etc.
Roadmap / Future Features (from Figma Editorial)
  1. More innovative features that boost creative productivity by removing the manual, repetitive parts of design work
  2. More features that work great with the entire product team, including engineers, product managers, and other folks critical to the development process
  3. A richer, more open design community – Figma Community. Enable more creators to add more resources

Why we love it?

Figma stands out from other design tools by offering access on different platforms — Mac, Windows, Linux, and most importantly, the Web. According to its CCO, Amanda Kleha, Figma actually originated from the idea that “design should live on the Web.” Figma is unique in its ability to store everything in the Cloud so that the team can always find the latest version. It also offers a few other benefits that we don’t find in other tools including live collaboration, interaction preview, and instant code mode.

Highly collaborative

Unlike other design tools that follow the share-comment structure for collaboration or communication purposes, Figma takes a different approach by offering the live collaboration feature. In other words, it allows multiple users to work on the same file at the same time. This feature may seem strange for some users, but it could be useful for projects that need several designers working on the same file.

We are under the impression that Figma is striving to build a highly collaborative environment for users because the Comment feature is built into both design and development modes —  it’s even highlighted in the top tool panel to make communicating handier. Commenting can be “live” too. When multiple users are working on the same document, they are able to give feedback and respond to comments with a single click.

Animation preview

We simply love this feature! When comparing the animation and interaction features among these tools, we realized most of them have very similar capabilities and logic. But what makes Figma unique is that it offers previews for animations. This increases the learnability of this tool and is especially helpful for those who are relatively new to the animation terms.

animation preview

Three unique modes

While other design tools primarily have two modes for users to work in — Design Mode and Development Mode, Figma also incorporates a Code Mode. In the Code Mode, when we click on an element, we are able to see its CSS, iOS, and Android code without an additional step.

In this case, designers and developers have a chance to learn from each other – designers getting more familiar with code, and developers can always have the most up-to-date design file to build the product from.

What we miss?

In general, Figma is a very powerful design tool because I cannot say there is one thing about it that significantly bothers me, so here I would like to mention several aspects in which it can be improved.

Local copy

Unlike other design tools that allow users to save files into local drive, Figma takes a different approach by storing everything on the Web. This is good because everything is auto-saved and the teams always have access to the updated files. However, for teams working only in the Cloud, this could be problematic because everything is based on the Internet. Basically, if there’s no Internet connection, there’s no progress.

Privacy

Figma’s emphasis on the Web could also be a potential issue in terms of privacy. Organizations might fear that putting their projects and collaborating in the Cloud would be less private.

Another issue is that some designers might prefer working on their own and don’t want others to see what they are working on currently. In this case, Figma’s live collaboration feature might not work for this group of designers.

Layers Organization

The pages, artboards, and layers panel on the right of Figma’s design interface could be a little clearer and more legible. There could be a more obvious separation between pages and artboards. Also, Figma doesn’t have the option to search for specific layers, as XD does. For a large project that involves multiple pages and artboards, searching for specific layers to make a change could save us some time.

InVision Studio

Year of Release 2018
Download Link https://www.invisionapp.com/studio
Tutorials Link https://www.invisionapp.com/studio/learn
Brands that Use it / Popularity Stripe, Airbnb, Amazon, Slack, Lyft, HBO, IBM, Netflix, CapitalOne, etc.
Roadmap / Future Features (from InVision Talks) Major investments into tools and collaboration fragmentation, version management, company-wide source of truth.

Why we love it?

InVision has been extensively used as a powerful prototyping tool combined with other interface design tools to help design teams achieve intended results. It released its own design tool, InVision Studio, in 2018, so now we can design and prototype on the same platform. Since InVision was famous for being a prototyping tool originally, InVision Studio keeps this advantage by offering many powerful animations and interactions. Moreover, InVision has developed its own design collaboration suite, which makes it the most complete solution on the market today.

Rapid prototyping

InVision Studio is no doubt the best in terms of prototyping among all the design tools on the list. It allows interactions triggered by mouse, finger, keyboard, and timer. More options, such as hold tap and double-tap, which are not seen in other design tools, are also available in InVision Studio.

Prototyping has been made simpler in Invision Studio also because we can add interaction at any point without switching to a prototyping mode. Another convenient feature is an automatic frame (iPhone, iPad, etc.) when we prototype screens, unlike Adobe XD that needs us to manually add frames.

Advanced animation

Besides rapid prototyping features, InVision Studio also offers advanced animation capabilities. For example, after we set up a motion transition to an artboard or layer, we can take a step further by using the Edit Timeline function to have more granular control over the speed and timing of each component that we want to animate. In this case, it allows us to create more customized animation to fit our needs.

InVision ecosystem

InVision has developed its ecosystem by offering a complete solution including design, prototyping, workflow tracking, design handoff, and advanced animations. For teams working on a complete product building cycle, InVision offers complete support from planning, to design, to communication, to delivery.

In the early stage of the product cycle, we can use InVision Boards for inspiration and ideation. When we want to create storyboards, low-fidelity prototypes or collaborate with other team members, we can use Freehand to help with the workflow.

What we miss?

With powerful design prototyping features all in one mode, InVision Studio’s interface appears to be more complicated than other tools. Thus, it can be improved in its usability and performance.

Relatively low learnability

InVision Studio is different from other design tools, first, in its look-and-feel: it features a dark theme and a futuristic style, which might be hard for some users to adapt to. The tool icons are also not very intuitive so it takes time to learn how each of them functions.

Better panel usability

InVision Studio somehow sacrifices its usability for the sleekness of its interface. For example, it doesn’t have a very clear tools panel like other design software does; rather, we need to perform an additional step by clicking on the “add” icon first to see what tools it has.

Another example is that it only has an alphabetical list of components. You have to click on each component to see what it is, whereas all other software provides previews of the components.

Interaction indicators

Unlike other design tools that separate design and prototype into two different modes, InVision Studio has only one mode that incorporates the interaction feature. Some people think this is more convenient because you don’t have to switch between the two modes. However, InVision Studio doesn’t provide enough indication for already created interactions – we need to click on each layer or artboard to see if it has interactions that have been previously set up.

Sketch

Year of Release 2010
Download Link https://www.sketch.com/get/
Tutorials Link https://www.sketch.com/docs/
Brands that Use it / Popularity Apple, Facebook, Google, Nintendo, Porsche, Stripe, etc.
Roadmap / Future Features (from Sketch Blog)
  1. Launch Sketch for Teams: better collaboration across organizations, features include Mentions, Projects, Cloud Libraries, etc.
  2. New Design Features: Components Panel, Popover, etc.
  3. Sketch Community
  4. Cloud Inspector: developer handoffs
  5. Assistant: automatically check designs
  6. Better prototyping features: modals, better sharing, hover states, hide hotspots, saved scrolling position, etc.
  7. Live Collaboration

Why we love it?

Sketch revolutionized the way we design user interface when it was first launched in 2010. After that, a number of different design and prototyping tools inspired by it appeared on the market. Although there are many tools out there that have their own advantages, Sketch still keeps its place as the top-used user interface design tool today, according to UXTool’s Design Tools Survey. Sketch not only has robust functionalities for interface design but also has the most extensive plugins and resources on the internet.

UX Survey

According to UXTool’s 2019 Design Tools Survey, Sketch was rated as the most used tool for user flow, wireframing, UI design, prototyping, and design systems.

Customizable Toolbar

One feature that sets Sketch apart from other design tools is its customizable toolbar. The other tools on the list all have a default toolbar either on the top or to the left of the interface. This convenient feature allows designers to choose what tools are in the toolbar, providing a custom fit to different design habits, and the ability to tailor it to different design needs.

For example, a UI Designer might stay with the default toolbar options, but a Graphic Designer or Icon Designer might want different toolbar options that are more suitable for vector arts.

Customizable toolbar

Extensive Resources

Since Sketch is the very first design software that’s created for user interface design, it has the most extensive resources on the Internet for us to learn and use. There are many different websites focusing on providing Sketch resources to download, including Sketch App Resources, Sketch Repo, etc.

Not to mention, it also has massive third-party plugins to improve its functionality and performance. For example, we can combine Sketch with advanced animation plugins to make better prototypes and we can use third-party plugins like Zeplin for design handoffs. This advantage is very important because we can mix and match plugins to create our own Sketch to best fit our needs.

Templates

Sketch has another convenient feature called Templates, which allows us to create new files from a template, including Android icon design, iOS app icon, material design, prototyping tutorial, smart layout tutorial, and web design. Using a template to create designs can save time when figuring out the correct dimensions, grids, layout options, etc.

What we miss?

Although Sketch has powerful design features and is the most-used interface design tool, it’s still not perfect. There are multiple ways it could be improved in order to be used by more people and to create a more seamless user experience.

Other platforms

Unlike other design tools on the list that support at least two platforms, Sketch is a Mac-only application. Its strongest rival, Figma (Figma was rated number two in the top-used user interface design software survey in 2019),  supports four platforms: Mac, Windows, Linux, and the Web, which makes it accessible from almost all platforms available.

Version control

Another feature we miss in Sketch is version control. It’s highly dependent on third-party plugins, such as Abstract and Plant, for versioning. On the other hand, Figma has a built-in feature called Version History, which allows users to see all the auto-saved versions and enables the retrieval of earlier versions. And for those who prefer intentionally saving a version of the file, Figma also allows for manually saving a version.

Collaboration features

Although Sketch has a powerful plugin library, it’s still easier to have built-in collaboration features. Currently, Sketch doesn’t support sharing, commenting, real-time collaboration, or design handoff, whereas other design software has these features built-in. However, according to “What’s Next for Sketch in 2020?” it plans to launch built-in collaboration features this year.

Conclusion

The launch of Sketch has inspired more creation and development in design software. Each tool has its own pros and cons, so organizations can choose what tool to use by analyzing the budget, project scope, and design need. Hopefully, this article can provide a new perspective to your understanding of these design tools and help you choose the right tool for you. Please let us know what you think of these tools. We’d love to hear your opinions!

FYI, SymSoft is currently hiring Interaction Designers. If this is you, our team is eager to meet you!

(md, cm, yk, ma, xl)

 

Resources

  1. Best UI Design Tools 2019: Your Guide
  2. Figma vs InVision Studio
  3. Adobe XD vs Sketch, InVision Studio & Figma (2019’s In-depth Comparison) Which one?
  4. Figma vs InVision in 2019 – Comparison UI Design Tools
  5. 2019 Design Tools Survey
  6. Learn and Master Adobe XD
  7. Figma Best Practices
  8. Learn Sketch
  9. Master the Power of InVision Studio
]]>
Sitecore error “Attempted to load invalid xml” or “ERROR First 200 characters: >” https://www.symsoftsolutions.com/blog/2020/01/17/sitecore-error-attempted-to-load-invalid-xml-or-error-first-200-characters/ Fri, 17 Jan 2020 21:57:41 +0000 https://www.symsoftsolutions.com/?p=4415 When we were migrating from Sitecore 8.2 to 9.1.1 we found the following exception in our AppInsights Logs.
Also, non-admin users were reporting that they would receive a fatal error that looks like the following:

We weren’t sure what was happening but in the logs, we saw that it was being triggered during Item Locking and Indexing.
After some research, it was apparent that this was being caused by invalid XML in the lock field. Which looks like this

This issue can appear in the Sitecore logs in the following ways:

Error 1:
416 16:06:36 ERROR First 200 characters: >

Error 2:
416 16:06:36 ERROR Call stack: at Sitecore.MainUtil.GetCallStack() at Sitecore.Xml.XmlUtil.LoadXml(String xml) at Sitecore.Data.Locking.ItemLocking.GetLockField() at Sitecore.Data.Locking.ItemLocking.GetOwner() at Sitecore.ContentSearch.ComputedFields.ParsedLockOwner.ComputeFieldValue(IIndexable indexable) at Sitecore.ContentSearch.AbstractDocumentBuilder`1.<>c__DisplayClass57_0.<AddComputedIndexFieldsInParallel>b__0(IComputedIndexField computedIndexField, ParallelLoopState parallelLoopState) at System.Threading.Tasks.Parallel.<>c__DisplayClass42_0`2.<PartitionerForEachWorker>b__1() at System.Threading.Tasks.Task.InnerInvokeWithArg(Task childTask) at System.Threading.Tasks.Task.<>c__DisplayClass176_0.<ExecuteSelfReplicating>b__0(Object ) at System.Threading.Tasks.Task.Execute() at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot) at System.Threading.Tasks.Task.ExecuteEntry(Boolean bPreventDoubleExecution) at System.Threading.ThreadPoolWorkQueue.Dispatch()

Error 3:
10144 16:06:36 ERROR Attempted to load invalid xml. Exception: System.Xml.XmlException Message: Data at the root level is invalid. Line 1, position 1. Source: System.Xml at System.Xml.XmlTextReaderImpl.Throw(Exception e) at System.Xml.XmlTextReaderImpl.ParseRootLevelWhitespace() at System.Xml.XmlTextReaderImpl.ParseDocumentContent() at System.Xml.XmlLoader.Load(XmlDocument doc, XmlReader reader, Boolean preserveWhitespace) at System.Xml.XmlDocument.Load(XmlReader reader) at System.Xml.XmlDocument.LoadXml(String xml) at Sitecore.Xml.XmlUtil.LoadXml(String xml)

Solution:
We knew that we needed to find out how many and which Sitecore Items had this issue so that we could go and fix them through the Content Editor.  We decided that SQL was out best shot!

Run the following query in your Master database. The query returns the list of Items that have Invalid XML in the __Lock Field

SELECT TOP (1000) [Id]
,[ItemId]
,(select Name from Items where FieldId = ID) as FieldName
,[Value]
,[Created]
,[Updated]
FROM [Fields]
where [Value] = '>' or [Value] = '<'

In my case, it returned 1 record in the master database for the __lock field.

I then use the content editor and searched for the Item that was affected, and cleared out the lock field, as an admin of course! One its cleared out make sure you save and publish.

If you are still receiving the error after clearing the fields, execute the same query on the VersionedFields and the SharedFields Table:

SELECT TOP 100 ItemId,(select Name from Items where FieldId = ID) as FieldName, LEN([Value]) as 'Length'
FROM [VersionedFields]
where ([Value] like '<%' or [Value] like '<' or [Value] like '%<')
Order by [Length]

SELECT TOP 100 ItemId,(select Name from Items where FieldId = ID) as FieldName, LEN([Value]) as 'Length'
FROM [SharedFields]
where ([Value] like '<%' or [Value] like '<' or [Value] like '%<')
Order by [Length]

Hopefully, this helps!

]]>
Update Contact using FXM javascript API https://www.symsoftsolutions.com/blog/2020/01/08/update-contact-using-fxm-javascript-api/ Wed, 08 Jan 2020 23:35:27 +0000 https://www.symsoftsolutions.com/?p=4381 This blog demonstrates how to add or update Sitecore contact using FXM JavaScript API. Federated Experience Manager (FXM) application allows you to add Sitecore content on external non-Sitecore websites and track visitor interactions, generate analytics information as if it was a Sitecore site. It also allows us to leverage Sitecore personalization based on contact Identified. However, we had the following question – How can we identify the contact from the Non-Sitecore site using FXM, and then use the Sitecore Personalization engine to deliver targeted content?. This is what we came up with.

Sitecore FXM uses what they call a Beacon script to track and record the current users’ behavior (i.e interactions). Including this script will allow you to execute the following methods to track events, goals, outcomes, and campaigns. However, there are no available methods to identify a contact or update a contact’s facet data. This functionality is needed when we have a Non-Sitecore site using FXM that is also the application responsible for logging the user in, and then personalizing content based on a given attribute stored in xDb.

  1. SCBeacon.trackEvent
  2. SCBeacon.trackGoal
  3. SCBeacon.trackOutcome
  4. SCBeacon.trackCampaign

Looking at Sitecore 9.1 configs for FXM in Sitecore.FXM.config and Sitecore FXM assembly lead us to the TriggerEventsProcessor which is responsible for handling calls the Beacon method’s referrenced above.

<tracking.triggerpageevent>

  <processor type="Sitecore.FXM.Pipelines.Tracking.TriggerPageEvent.TriggerEventsProcessor, Sitecore.FXM" resolve="true" />

</tracking.triggerpageevent>
public class TriggerEventsProcessor : ITriggerPageEventProcessor
  {
    private readonly IAnalyticsDefinitionsRepository _analyticsDefinitionsRepository;
    public TriggerEventsProcessor(IAnalyticsDefinitionsRepository analyticsDefinitionsRepository)
    {
      Assert.ArgumentNotNull((object) analyticsDefinitionsRepository, nameof (analyticsDefinitionsRepository));
      this._analyticsDefinitionsRepository = analyticsDefinitionsRepository;
    }
    public void Process(TriggerPageEventArgs args)
    {
      IPageContext currentPageContext = args.CurrentPageContext;
      Assert.IsNotNull((object) currentPageContext, "The current page is not tracked in the current session. No events can be triggered.");
      ITrackDescriptor trackDescriptor = args.TrackDescriptor;
      if (trackDescriptor is EventTrackDescriptor)
      {
        EventTrackDescriptor eventTrackDescriptor = (EventTrackDescriptor) trackDescriptor;
        this.TriggerPageEvent((IDefinition) eventTrackDescriptor.Definition, currentPageContext, eventTrackDescriptor.Data, eventTrackDescriptor.DataKey, eventTrackDescriptor.Extras);
      }
      else if (trackDescriptor is GoalTrackDescriptor)
      {
        GoalTrackDescriptor goalTrackDescriptor = (GoalTrackDescriptor) trackDescriptor;
        this.TriggerPageEvent((IDefinition) goalTrackDescriptor.Definition, currentPageContext, goalTrackDescriptor.Data, goalTrackDescriptor.DataKey, goalTrackDescriptor.Extras);
      }
      else if (trackDescriptor is CampaignTrackDescriptor)
      {
        CampaignTrackDescriptor campaignTrackDescriptor = (CampaignTrackDescriptor) trackDescriptor;
        currentPageContext.TriggerCampaign(campaignTrackDescriptor.Definition);
      }
      else if (trackDescriptor is OutcomeTrackDescriptor)
      {
        OutcomeTrackDescriptor outcomeTrackDescriptor = (OutcomeTrackDescriptor) trackDescriptor;
        OutcomeData outcome = new OutcomeData(outcomeTrackDescriptor.Definition, outcomeTrackDescriptor.CurrencyCode, outcomeTrackDescriptor.Monetary);
        IDictionary<string, string> extras = outcomeTrackDescriptor.Extras;
        if (extras != null && extras.Count > 0)
          outcome.CustomValues["FXM_CustomValues"] = (object) outcomeTrackDescriptor.Extras;
        currentPageContext.RegisterOutcome(outcome);
      }
      else
      {
        if (!(trackDescriptor is ElementMatcherTrackDescriptor))
          return;
        this.TriggerElement(((ElementMatcherTrackDescriptor) trackDescriptor).Item, currentPageContext);
      }
    }
  }

Taking a look at the processor, SCBeacon.trackEvent does exactly what it says and tracks specific actions but does not handle any contact identifications or contact updates.

The next step is to create the custom process by extending (inheriting) the Sitecore default TriggerEventsProcessor with the following code:

public class TriggerFXMEventsProcessor : Sitecore.FXM.Pipelines.Tracking.TriggerPageEvent.TriggerEventsProcessor
    {
        //private readonly IAnalyticsDefinitionsRepository _analyticsDefinitionsRepository;
        public TriggerFXMEventsProcessor(IAnalyticsDefinitionsRepository analyticsDefinitionsRepository) : base(analyticsDefinitionsRepository)
        {
        }
        public new void Process(TriggerPageEventArgs args)
        {
            IPageContext currentPageContext = args.CurrentPageContext;
            Assert.IsNotNull((object)currentPageContext, "The current page is not tracked in the current session. No events can be triggered.");
            ITrackDescriptor trackDescriptor = args.TrackDescriptor;
            if (trackDescriptor is EventTrackDescriptor)
            {
                EventTrackDescriptor eventTrackDescriptor = (EventTrackDescriptor)trackDescriptor;
                if (eventTrackDescriptor.Definition.Id.Equals(new Guid(("{A6202545-323F-42ED-B904-81079C762524}")))
                {
                    if (Sitecore.Analytics.Tracker.Current != null && Sitecore.Analytics.Tracker.Current.Contact != null)
                    {
                        UpdateandIdenifyContact(eventTrackDescriptor);
                    }
                    else
                    {
                        Log.Info("cannot found the contact for the current interaction", this);
                    }
                }
                else
                {
                    this.TriggerPageEvent((IDefinition)eventTrackDescriptor.Definition, currentPageContext, eventTrackDescriptor.Data, eventTrackDescriptor.DataKey, eventTrackDescriptor.Extras);
                }
            }
            else if (trackDescriptor is GoalTrackDescriptor)
            {
                GoalTrackDescriptor goalTrackDescriptor = (GoalTrackDescriptor)trackDescriptor;
                this.TriggerPageEvent((IDefinition)goalTrackDescriptor.Definition, currentPageContext, goalTrackDescriptor.Data, goalTrackDescriptor.DataKey, goalTrackDescriptor.Extras);
            }
            else if (trackDescriptor is CampaignTrackDescriptor)
            {
                CampaignTrackDescriptor campaignTrackDescriptor = (CampaignTrackDescriptor)trackDescriptor;
                currentPageContext.TriggerCampaign(campaignTrackDescriptor.Definition);
            }
            else if (trackDescriptor is OutcomeTrackDescriptor)
            {
                OutcomeTrackDescriptor outcomeTrackDescriptor = (OutcomeTrackDescriptor)trackDescriptor;
                OutcomeData outcome = new OutcomeData(outcomeTrackDescriptor.Definition, outcomeTrackDescriptor.CurrencyCode, outcomeTrackDescriptor.Monetary);
                IDictionary<string, string> extras = outcomeTrackDescriptor.Extras;
                if (extras != null && extras.Count > 0)
                    outcome.CustomValues["FXM_CustomValues"] = (object)outcomeTrackDescriptor.Extras;
                currentPageContext.RegisterOutcome(outcome);
            }
            else
            {
                if (!(trackDescriptor is ElementMatcherTrackDescriptor))
                    return;
                this.TriggerElement(((ElementMatcherTrackDescriptor)trackDescriptor).Item, currentPageContext);
            }
        }

        private void UpdateandIdenifyContact(EventTrackDescriptor eventTrackDescriptor)
        {
            try
            {
                Assert.IsTrue(eventTrackDescriptor.Extras.Any(), "FXM JS API - Parameter not passed");
                if (eventTrackDescriptor.Extras != null && eventTrackDescriptor.Extras.Any())
                {
                    var values = eventTrackDescriptor.Extras;
                    Assert.IsTrue(values.ContainsKey("uniqueId"), "FXM JS API - Parameter - uniqueId not passed");
                    Assert.IsNotNullOrEmpty(values["uniqueId"], "FXM JS API - Parameter - uniqueId in Empty");
                    if (values.ContainsKey("uniqueId") && !string.IsNullOrEmpty(values["uniqueId"]))
                    {
                        using (XConnectClient client = SitecoreXConnectClientConfiguration.GetClient())
                        {
                            ContactExpandOptions Facets = new ContactExpandOptions(new string[] {
                            PersonalInformation.DefaultFacetKey,
                            EmailAddressList.DefaultFacetKey,
                            PayLoad.DefaultFacetKey
                            });
                            Sitecore.XConnect.Contact contact = client.Get(new IdentifiedContactReference("ContactPayload", values["uniqueId"].ToString().ToLowerInvariant()), Facets);
                            PersonalInformation PersonalFacet;
                            EmailAddressList EmailAddressListFacet = null;
                            PayLoad PayLoadFacet = null;
                            var manager = Sitecore.Configuration.Factory.CreateObject("tracking/contactManager", true) as Sitecore.Analytics.Tracking.ContactManager;
                            if (contact == null) //if the contact is not existing in the system
                            {
                                contact = new Sitecore.XConnect.Contact(new ContactIdentifier("ContactPayload", values["uniqueId"].ToString().ToLowerInvariant(), ContactIdentifierType.Known));
                                client.AddContact(contact);
                                PayLoadFacet = new PayLoad();
                                FillPayload(PayLoadFacet, eventTrackDescriptor);
                                if (!string.IsNullOrEmpty(PayLoadFacet.FirstName))
                                {
                                    PersonalFacet = new PersonalInformation()
                                    {
                                        FirstName = PayLoadFacet.FirstName,
                                        LastName = PayLoadFacet.LastName
                                    };
                                    client.SetFacet(contact, PersonalInformation.DefaultFacetKey, PersonalFacet);
                                }
                                if (!string.IsNullOrEmpty(PayLoadFacet.Email))
                                {
                                    EmailAddressListFacet = new EmailAddressList(new EmailAddress(PayLoadFacet.Email, true), "Preferred");
                                    client.SetFacet(contact, EmailAddressList.DefaultFacetKey, EmailAddressListFacet);
                                }
                            }
                            else
                            {
                                PayLoadFacet = contact.GetFacet(PayLoad.DefaultFacetKey);
                                if (PayLoadFacet == null)
                                    PayLoadFacet = new PayLoad();
                                FillPayload(PayLoadFacet, eventTrackDescriptor);
                                if (!string.IsNullOrEmpty(PayLoadFacet.FirstName))
                                {
                                    PersonalFacet = contact.GetFacet(PersonalInformation.DefaultFacetKey);
                                    if (PersonalFacet == null)
                                    {
                                        PersonalFacet = new PersonalInformation()
                                        {
                                            FirstName = PayLoadFacet.FirstName,
                                            LastName = PayLoadFacet.LastName
                                        };
                                    }
                                    else
                                    {
                                        PersonalFacet.FirstName = PayLoadFacet.FirstName;
                                        PersonalFacet.LastName = PayLoadFacet.LastName;
                                    }
                                    client.SetFacet(contact, PersonalInformation.DefaultFacetKey, PersonalFacet);
                                }
                                if (!string.IsNullOrEmpty(PayLoadFacet.Email))
                                {
                                    EmailAddressListFacet = contact.GetFacet(EmailAddressList.DefaultFacetKey);
                                    if (EmailAddressListFacet == null)
                                        EmailAddressListFacet = new EmailAddressList(new EmailAddress(PayLoadFacet.Email, true), "Preferred");
                                    else
                                        EmailAddressListFacet.PreferredEmail = new EmailAddress(PayLoadFacet.Email, true);

                                    client.SetFacet(contact, EmailAddressList.DefaultFacetKey, EmailAddressListFacet);
                                }
                            }
                            client.SetFacet(contact, PayLoad.DefaultFacetKey, PayLoadFacet);
                            client.Submit();
                            manager.RemoveFromSession(Tracker.Current.Session.Contact.ContactId);
                            Tracker.Current.Session.IdentifyAs("ContactPayload", values["uniqueId"].ToString().ToLowerInvariant());
                            Tracker.Current.Session.Contact = manager.LoadContact(Tracker.Current.Contact.ContactId);
                            Log.Info("FXM JS API added contact "+ Tracker.Current.Contact.ContactId.ToString() + " for " + values["uniqueId"].ToString().ToLowerInvariant(), "FXM JS API");
                        }
                    }
                    else
                    {
                       Log.Error("FXM JS API - Parameter - uniqueId not passed", "FXM JS API");
                    }
                }
                else
                {
                    Log.Error("FXM JS API - Parameters not passed", "FXM JS API");
                }
            }
            catch (XdbExecutionException ex)
            {
                Log.Error("XDB Exception while adding or updating contacts " + ex.Message, ex, this);
                throw ex;
            }
            catch (System.AggregateException ae)
            {
                foreach (var e in ae.Flatten().InnerExceptions)
                {
                    Log.Error("Aggregate Error while adding or updating contacts " + e.Message, e, this);
                }
                throw ae;
            }
            catch (Exception ex)
            {
                Log.Error("Exception while adding or updating contacts " + ex.Message, ex ,this);
                throw ex;
            }
        }
        private void FillPayload(PayLoad payloadinfo, EventTrackDescriptor eventTrackDescriptor)
        {
            var values = eventTrackDescriptor.Extras;
            payloadinfo.UniqueId = values.ContainsKey("uniqueId") ? values["uniqueId"].ToString().ToLowerInvariant() : string.Empty;
            payloadinfo.CustomerName = values.ContainsKey("customerName") ? values["customerName"].ToString() : string.Empty;
            payloadinfo.FirstName = values.ContainsKey("firstName") ? values["firstName"].ToString() : string.Empty;
            payloadinfo.LastName = values.ContainsKey("lastName") ? values["lastName"].ToString() : string.Empty;
            payloadinfo.Email = values.ContainsKey("email") ? values["email"].ToString() : string.Empty;         
            payloadinfo.Language = values.ContainsKey("language") ? values["language"].ToString().ToLowerInvariant() : "en";
        }
    }

As you could see that even though I have inherited the Sitecore default TriggerEventsProcessor, I have used Process() method as is, instead of overriding with a check for the event’s definition as CONTACTEVENT({A6202545-323F-42ED-B904-81079C762524}) to update and Identify Contact. This will make sure that the default page event is not triggered when there is a huge amount of call to update contact via trackevent of beacon script from an external site.
The next step is to register the custom processor in Sitecore configuration using Sitecore config patching standard practice.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/" >
 <sitecore>
  <pipelines>
   <group name="FXM" groupName="FXM" >
    <pipelines>
     <tracking.triggerpageevent>
      <processor type="Project.Standalone.FXM.Pipelines.Tracking.TriggerFXMEventsProcessor, Project.Standalone" patch:instead="processor[@type='Sitecore.FXM.Pipelines.Tracking.TriggerPageEvent.TriggerEventsProcessor, Sitecore.FXM']" resolve="true"/>
     </tracking.triggerpageevent>
    </pipelines>
   </group>
  </pipelines>
 </sitecore>
</configuration>

Once the custom processor is registered, we need to create the CONTACTEVENT ({A6202545-323F-42ED-B904-81079C762524}) page event items in Sitecore. Navigate to /Sitecore/system/Settings/Analytics/Page Events path in the content tree and create page event as described below.
Contact Event

Now we can call the trackevent method after the load of the beacon script. There are different ways (refer to Sitecore documentation) to pass the updated contact data to the page event, it also depends on your implementation. I decided to use an extra dictionary of EventTrackDescriptor to send the contact details. To test you can call the trackEvent method in the developer console of your browser, once FXM script is loaded on the External Site.

SCBeacon.trackEvent("contactevent", {
                     xuniqueId: "0001181764-288089",
                     xcustomerName: "NICOLE TREJOS",
                     xfirstName: "NICOLE",
                     xlastName: "TREJOS",
                     xemail: "NICOLE.TREJOS@test.com",
                     xlanguage: "en"
                   });

You can see the new contact added using Experience Profile.
Experience Profile

]]>
Sitecore 9.X Intranet Login using Azure Active Directory https://www.symsoftsolutions.com/blog/2020/01/02/sitecore-9-azure-active-directory/ Thu, 02 Jan 2020 19:01:34 +0000 https://www.symsoftsolutions.com/?p=4333

Summary

I am using Sitecore for a Multisite that is already hosting two publicly available sites. We wanted to create a new intranet site using the same instance of Sitecore. Since this is an internal site one of the requirements was to secure all content using Azure Active Directory, keep in mind we are not talking about the Sitecore Client, but the actual site. So to sum up the challenge: Secure the intranet using Azure Active Directory while leaving the other two public sites available to all. We also wanted to use Microsoft Graph API (Delegated) to update their intranet profile immediately after the user logs in using AAD and using an OAuth Token, but we will tackle that in a later blog post.

The following steps will be outlined below:

  • Turning on Sitecore’s Federated Authentication
  • Building a custom IdentityProvidersProcessor for Azure AD or OpenId
  • Coding Azure AD Identity Provider
  • Mapping Claims
  • Creating a Sitecore User Builder
  • Setup the AppRegistration in Azure Active Directory
  • Forcing Intranet Site to use login

Turning on Sitecore’s Federated Authentication

The following config will enable Sitecore’s federated authentication. This configuration is also located in an example file located in \\App_Config\\Include\\Examples\\Sitecore.Owin.Authentication.Enabler.example. I decided to create my own patch file and install it in the Include folder.

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/"  xmlns:env="http://www.sitecore.net/xmlconfig/env/">
  <sitecore>
        <settings>
            <setting name="FederatedAuthentication.Enabled">
              <patch:attribute name="value">true</patch:attribute>
            </setting>
        </settings>
        <services>
            <register serviceType="Sitecore.Abstractions.BaseAuthenticationManager, Sitecore.Kernel"
                      implementationType="Sitecore.Owin.Authentication.Security.AuthenticationManager, Sitecore.Owin.Authentication"
                      lifetime="Singleton" />
            <register serviceType="Sitecore.Abstractions.BaseTicketManager, Sitecore.Kernel"
                      implementationType="Sitecore.Owin.Authentication.Security.TicketManager, Sitecore.Owin.Authentication"
                      lifetime="Singleton" />
            <register serviceType="Sitecore.Abstractions.BasePreviewManager, Sitecore.Kernel"
                      implementationType="Sitecore.Owin.Authentication.Publishing.PreviewManager, Sitecore.Owin.Authentication"
                      lifetime="Singleton" />
        </services>
    </sitecore>
</configuration>

IdentityProvidersProcessor for Azure AD using the OpenId Protocol

The following Class inherits Sitecore’s IdentityProvidersProcessor

using Owin;
using Sitecore.Configuration;
using Sitecore.Diagnostics;
using Sitecore.Owin.Authentication.Configuration;
using Sitecore.Owin.Authentication.Pipelines.IdentityProviders;
using Sitecore.Owin.Authentication.Services;
using System.Globalization;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.OpenIdConnect;

namespace Foundation.Auth.Processors
{
    public class AzureADIdentityProviderProcessor : IdentityProvidersProcessor
    {

    }
}
The ProviderProcessor should override the IdentityProviderName property and the ProcessCore method

protected override void ProcessCore(IdentityProvidersArgs args)
{
            Assert.ArgumentNotNull(args, nameof(args));

            var identityProvider = this.GetIdentityProvider();
            var authenticationType = this.GetAuthenticationType();

            string aadInstance = Settings.GetSetting("AADInstance");
            string tenant = Settings.GetSetting("Tenant");
            string clientId = Settings.GetSetting("ClientId");
            string postLogoutRedirectURI = Settings.GetSetting("PostLogoutRedirectURI");
            string redirectURI = Settings.GetSetting("RedirectURI");
            string authority = string.Format(CultureInfo.InvariantCulture, aadInstance, tenant);

            args.App.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
            {
                Caption = identityProvider.Caption,
                AuthenticationType = authenticationType,
                AuthenticationMode = AuthenticationMode.Passive,
                ClientId = clientId,
                Authority = authority,
                PostLogoutRedirectUri = postLogoutRedirectURI,
                RedirectUri = redirectURI,
                Scope = "offline_access",

                // Watch for Events
                Notifications = new OpenIdConnectAuthenticationNotifications
                {
                    // When everything is passed
                    SecurityTokenValidated = async notification =>
                    {
                        // Get the Ident object from Ticket
                        var identity = notification.AuthenticationTicket.Identity;

                        // Use Sitecore Claim Transformation Service to generate additional claims like role or admin
                        foreach (var claimTransformationService in identityProvider.Transformations)
                        {
                            claimTransformationService.Transform(identity,
                                new TransformationContext(FederatedAuthenticationConfiguration,identityProvider));
                        }

                        // Create new Auth Ticket
                        notification.AuthenticationTicket = new AuthenticationTicket(identity, notification.AuthenticationTicket.Properties);

                        //Returns blank task
                        return; 
                    }

                }
            });
        }

The ProcessCore method should look familiar if you have used OWIN middleware in other .NET projects and used IAppBuilder. This method sets up the OpenId connect provider and also handles the SecurityTokenValidated event. This is also where we set up the endpoints and all the properties needed from the Azure AD Tenant. These settings are not hard coded, but pulled in from Sitecore settings config.

Within the SecurityTokenValidated anonymous function, Sitecore’s Claims transformation service is being used to map claims returned from AAD and mapped to Sitecore Claims. These newly created Sitecore claims will later be used to create a Sitecore user and log them in.

The following are examples of the settings:

<sitecore role:require="Standalone or ContentDelivery or ContentManagement">
    
    <!-- Common Settings Should be same for each Environment -->
    <settings>
      <setting name="AADInstance" value="https://login.microsoftonline.com/{0}" />
      <setting name="AuthorityUri" value="https://login.microsoftonline.com/common/" />
    </settings>
    
    <!-- Settings for Local -->
    <settings env:require="Local">
      <setting name="ClientId" value="XXXXX-3586-XXX-XXX-4XXXXXd2a18" />
      <setting name="ClientSecret" value="Goblyhash" />
      <setting name="Tenant" value="yourtenant.com" />
      <setting name="PostLogoutRedirectURI" value="https://intranet.local/sitecore/login"/>
      <setting name="RedirectURI" value="https://intranet.local/sitecore" />
    </settings>

User Builder

Because we are using Sitecore Federated authentication, the creation of Sitecore users is handled automatically. Sitecore does, however, give you the opportunity to inject your own custom user builder. This is great because I wanted to be able to control which domain the intranet users were assigned.

I inherited the DefaultExternalUserBuilder then rewrote the domain name in the CreateUniqueUserName method.

using Microsoft.AspNet.Identity;
using Sitecore.Diagnostics;
using Sitecore.Owin.Authentication.Configuration;
using Sitecore.Owin.Authentication.Identity;
using System;

namespace Foundation.Auth.UserBuilder
{
    public class IntranetUserBuilder : Sitecore.Owin.Authentication.Services.DefaultExternalUserBuilder
    {
        public IntranetUserBuilder(bool isPersistentUser) :base(isPersistentUser)
        {
        }

        public IntranetUserBuilder(string isPersistentUser) : base(bool.Parse(isPersistentUser))
        {
        }

        protected override string CreateUniqueUserName(UserManager userManager, Microsoft.AspNet.Identity.Owin.ExternalLoginInfo externalLoginInfo)
        {
            Assert.ArgumentNotNull((object)userManager, nameof(userManager));
            Assert.ArgumentNotNull((object)externalLoginInfo, nameof(externalLoginInfo));
            IdentityProvider identityProvider = this.FederatedAuthenticationConfiguration.GetIdentityProvider(externalLoginInfo.ExternalIdentity);
            if (identityProvider == null)
                throw new InvalidOperationException("Unable to retrieve identity provider for given identity");
            string domain = identityProvider.Domain;
            string email = externalLoginInfo.DefaultUserName;

            // return email and domain
            return $"{domain}\\\\\\\\{email}";

        }

    }
}

Configuring the User Builder

Now that we have created a custom user builder we will need to teach Sitecore how to use it, with the following config, although the IdentityProvidersPerSites element also tells Sitecore which Site Definitions are using the custom IdentityProvider

<identityProvidersPerSites>
        <mapEntry name="all" type="Sitecore.Owin.Authentication.Collections.IdentityProvidersPerSitesMapEntry, Sitecore.Owin.Authentication">
          <sites hint="list">
            <site>intranet</site>
          </sites>
          <!-- Registered identity providers for above providers -->
          <identityProviders hint="list:AddIdentityProvider">
            <identityProvider ref="federatedAuthentication/identityProviders/identityProvider[@id='azureAD']" />
          </identityProviders>
          <!-- ExternalUserBuilder is what creates a user with custom username in Sitecore and assigns roles based on claim transformation configured above -->
          <externalUserBuilder type="Foundation.Auth.UserBuilder.IntranetUserBuilder, Foundation.Auth">
            <param desc="isPersistentUser">true</param>
          </externalUserBuilder>
        </mapEntry>
      </identityProvidersPerSites>
</federatedAuthentication>

Note that the externalUserBuilder is pointed to our custom user builder. Also note that the parameter isPersistentUser is set to true which tells Sitecore’s user builder to create a permanent user rather then a virtual user in Sitecore. Refer to Sitecore’s documentation about when to use Virtual Users or Persistent Users.

Configuring the Auth

We need to tell Sitecore to use our Azure Identity Provider with the following configuration.

<pipelines>
      <owin.identityProviders>
        <!-- This is the custom processor that gets executed when azure AD posts the token to Sitecore -->
        <processor type="Foundation.Auth.Processors.AzureADIdentityProviderProcessor, Foundation.Auth" resolve="true" />
      </owin.identityProviders>
</pipelines>

And the following configuration tells Sitecore how to map Claims from AAD into Sitecore.

<federatedAuthentication>
      <!-- Property initializer assigns claim values to sitecore user properties -->
      <propertyInitializer type="Sitecore.Owin.Authentication.Services.PropertyInitializer, Sitecore.Owin.Authentication">
        <patch:attribute name="type">Foundation.Auth.UserBuilder.UserPropertyInitializer, Foundation.Auth</patch:attribute>
        <maps hint="list">
          <map name="email claim" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication">
            <data hint="raw:AddData">
              <!--claim name-->
              <source name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn" />
              <!--property name-->
              <target name="Email" />
            </data>
          </map>
          <map name="Name claim" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication">
            <data hint="raw:AddData">
              <!--claim name-->
              <source name="name" />
              <!--property name-->
              <target name="Fullname" />
            </data>
          </map>
          <map name="Other claim" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication">
            <data hint="raw:AddData">
              <!--claim name-->
              <source name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn" />
              <!--property name-->
              <target name="Comment" />
            </data>
          </map>
        </maps>
      </propertyInitializer>
    </federatedAuthentication>

The example above maps Azure’s UPN to Sitecore’s Email field and Azure’s Name Field into Sitecore’s Fullname Field

Register the new Identity Provider with Sitecore

<identityProviders hin="list:AddIdentityProvider">
        <identityProvider id="azureAD" type="Sitecore.Owin.Authentication.Configuration.DefaultIdentityProvider, Sitecore.Owin.Authentication">
          <param desc="name">$(id)</param>
          <param desc="domainManager" type="Sitecore.Abstractions.BaseDomainManager" resolve="true" />
          <caption>Sign-in with Azure Active Directory</caption>
          <domain>intranet</domain>
          <icon>/sitecore/shell/themes/standard/Images/24x24/msazure.png</icon>
          <transformations hint="list:AddTransformation">
            <!-- you need to have and Idp Claim for this to work -->
            <transformation name="Idp Claim" ref="federatedAuthentication/sharedTransformations/setIdpClaim" />
            <!-- This is to transform your Azure group into Sitecore Role. The claim value below is the object id of the role that needs to be copied from Azure -->
            <transformation name="devRole" type="Sitecore.Owin.Authentication.Services.DefaultTransformation, Sitecore.Owin.Authentication">
              <sources hint="raw:AddSource">
                <claim name="groups" value="9ae0af64-3b5b-45b7-b6e8-62a613250824" />
              </sources>
              <targets hint="raw:AddTarget">
                <claim name="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" value="Sitecore\\Developer" />
              </targets>
              <keepSource>true</keepSource>
            </transformation>
          </transformations>
        </identityProvider>
      </identityProviders>
Assign Identity Providers the intranet site

<identityProvidersPerSites>
        <mapEntry name="all" type="Sitecore.Owin.Authentication.Collections.IdentityProvidersPerSitesMapEntry, Sitecore.Owin.Authentication">
          <sites hint="list">
            <site>intranet</site>
          </sites>
          <!-- Registered identity providers for above providers -->
          <identityProviders hint="list:AddIdentityProvider">
            <identityProvider ref="federatedAuthentication/identityProviders/identityProvider[@id='azureAD']" />
          </identityProviders>
        </mapEntry>
      </identityProvidersPerSites>

Forcing a Login

On the Site Definition we need to tell Sitecore where the login Page is located effectively putting that site behind a login wall. We did not want to the Sitecore Login page as a Idp Portal so I whipped up a custom aspx page that is publicly available

Here is the SiteDefinition Example

      <site name="intranet" patch:before="site[@name='website']"
         enableTracking="true"
         scheme="https"
         hostName="www.intranet.com"
         virtualFolder="/"
         physicalFolder="/"
         rootPath="/sitecore/content"
         startItem="/Intranet"
         database="web"
         domain="intranet"
         allowDebug="true"
         cacheHtml="true"
         filteredItemsCacheSize="10MB"
         enablePreview="true"
         enableWebEdit="true"
         enableDebugger="true"
         disableClientData="false"
         requireLogin="true"
         loginPage="/sitecore/login/IntranetLogin.aspx"  />

And the following code is how the login functions

[AllowDependencyInjection]
    public partial class IntranetLogin : Page
    {
        // The Injected CorePipeline
        private BaseCorePipelineManager CorePipelineManager;

        public IntranetLogin()
        {
        }      

       public IntranetLogin(BaseCorePipelineManager corePipelineManager)
        {
            this.CorePipelineManager = corePipelineManager;
        }

        protected override void OnInit(EventArgs e)
        {
            RedirectToIdp();
            base.OnInit(e);
        }

        internal void RedirectToIdp()
        {
            //Run Pipeline to get list of external logins
            GetSignInUrlInfoArgs args = new GetSignInUrlInfoArgs("intranet", WebUtil.GetQueryString("returnUrl"));
            GetSignInUrlInfoPipeline.Run(this.CorePipelineManager, args);

            //Get link to IDP
            var redirectToIdp = args.Result.First().Href;

            //Response.Redirect(redirectToIdp);
           PostRedirect(redirectToIdp);

       }

       private void PostRedirect(string url)
        {
            Response.Clear();
            var sb = new System.Text.StringBuilder();
            sb.Append("<html>");
            sb.AppendFormat("<body onload='document.forms[0].submit()'>");
            sb.AppendFormat("<form action='{0}' method='post'>", url);
            sb.Append("</form>");
            sb.Append("</body>");
            sb.Append("</html>");
            Response.Write(sb.ToString());
            Response.End();
       }
       
      internal SignInUrlInfo PrepareSignInInfo(SignInUrlInfo info)
      {
            int num = info.Href.IndexOf("?", StringComparison.Ordinal);
            string href = info.Href;
            if (num > -1)
            {
               NameValueCollection queryString1 = HttpUtility.ParseQueryString(info.Href.Substring(num + 1));
                NameValueCollection queryString2 = HttpUtility.ParseQueryString(string.Empty);
                foreach (string key in queryString1.Keys)
                    queryString2[key] = HttpUtility.UrlEncode(queryString1[key]);
                href = info.Href.Substring(0, num + 1) + (object)queryString2;
            }
            return new SignInUrlInfo(info.IdentityProvider, href, info.Caption, info.Icon);
        }

}
Once everything is all wired up, the login should work as expected. Finito!
]]>
We collaborated with CalPERS on redesigning the myCalPERS self-service website https://www.symsoftsolutions.com/blog/2019/12/31/we-collaborated-with-calpers-on-redesigning-the-mycalpers-self-service-website/ Tue, 31 Dec 2019 20:42:22 +0000 https://www.symsoftsolutions.com/?p=4327 We are proud to announce our design team’s collaboration with the CalPERS’s User Experience team to deliver the myCalPERS website redesign.

Contributions include building a pattern library and creating a style guide on top of the existing brand guidelines, as well as designing and developing mobile-friendly WCAG 2.1 AA accessibility compliant interface utilizing the best practices in HTML, CSS, and JavaScript development.

CalPERS manages the largest public pension fund in the United States. Their members span a diverse range of abilities, making this project laser focused on providing streamlined access to information and digital services.

The project team worked closely with a dedicated team of accessibility experts to optimize key experiences such as at-a-glance status information (reusable info panels), plan comparison (complex data tables), account setup and beneficiary management (multipage forms), and calculators (specialized tools).

Learn more at New Mobile-Friendly myCalPERS Opens Possibilities.

]]>
Solr POST request not working in Sitecore 9.1 https://www.symsoftsolutions.com/blog/2019/12/30/solr-post-request-not-working-in-sitecore-9-1/ Mon, 30 Dec 2019 22:20:50 +0000 https://www.symsoftsolutions.com/?p=4312 We recently had to upgrade one of our client’s Sitecore website from version 8.2 to 9.1. During the upgrade we noticed that a POST request that was being used for one of the search components stopped working. The following error was being written to the Sitecore logs:

“Error: Invalid URI: The Uri string is too long.

The remote server returned an error: (414) Request-URI Too Long”

Looking at the configurations for SItecore 8.2 we noticed that Solr’s default request was set to POST by using a Sitecore support package as POST requests were not officially supported for that version of Sitecore. They were using the support package because they faced the same issue of the request size being too large for a GET request. And, this support package no longer seemed to work with version 9.1.

After looking around online I found articles pointing to a setting “ContentSearch.Solr.SendPostRequests” in the configuration file “C:\inetpub\wwwroot\trc91.local\App_Config\Sitecore\ContentSearch\Sitecore.ContentSearch.Solr.DefaultIndexConfiguration.config” and setting the value to true, should enable POST requests on Solr. Unfortunately, this didn’t work as we noticed that the search query was still being sent as a GET request. So, it seemed to have been working only for version 9.0 and not 9.1 (I have not tested this on 9.0).

Therefore, our next step was to reach out to Sitecore Support and they confirmed that the setting mentioned above doesn’t work and they have no plans of fixing it in the near future.

Instead, they gave us a workaround, to change the “requestHeaderSIze” attribute value to a higher value in the following file on your Solr server “[solr]/server/etc” (we set the value to 29000). Setting this value will allow Solr to support larger sizes for GET requests. It should look like this:

<Set name=”requestHeaderSize”><Property name=”solr.jetty.request.header.size” default=”29000″ /></Set>

We tried this workaround and we could confirm that the search component started working again. I hope this blog will help other Sitecore developers who might face a similar issue.

]]>
Advanced Search Experience with Sitecore and Solr https://www.symsoftsolutions.com/blog/2019/04/01/advanced-search-experience-with-sitecore-and-solr/ Mon, 01 Apr 2019 21:28:12 +0000 https://www.symsoftsolutions.com/?p=4166 Google-like search experience is so ubiquitous nowadays, that as end users we hardly ever notice it. It allows us to misspell words, expand our exploration beyond the initial scope, or simply provide answers to specific questions. Yet, those familiar with the Tesler’s Law of Preservation of Complexity know that most simple user experiences are in many cases quite elaborate pieces of elaborate design and engineering.

This article will provide you with a few examples of the advanced search experiences that are integrated with the Sitecore-based solutions we deliver to our customers, such as SMUD, DWR and DGS.

Sitecore Content Search API is responsible for communicating with search engine software such as Solr, a popular enterprise-level platform built on Apache Lucene™. Out of the box, the Content Search API retrieves the basic list of results, which works perfectly if the end user knows exactly what they are looking for. It’s a little less convenient for people who are not quite sure what they need, or simply don’t even know how to phrase the query.

SymSoft improves the original Sitecore Content Search API by adding methods that can retrieve other information — such as alternative query suggestion, or query term highlights in the list of results. This creates the user experience similar to the one provided by Google Search.

Let’s see them in action.

Alternative Query Suggestion

Alternative query suggestion will attempt to search content with the original keyword. In case there are no results to display, it will suggest an alternative query results based on similar existing words or phrases. Below is an example of the alternative query suggestion on a live website for the California Department of Water Quality.

Screenshot of DWR website search showcasing suggested alternative query results

Stemmed Search

Stemmed search improves user experience by expanding query to include all results that are related to the base form of the word used in the query. In the example below, using keywords with the same root — university and universities — will retrieve the same list of pages and documents (the root of the word university is universit).

Screenshots of DWR search results page.

Query Term Highlighting

Query term highlighting is useful in cases when the search query refers to multiple different contexts. Highlighted query terms help users understand the context around the keyword they were looking up. In the example below, it is easy to find documents that are related to different water management plans.

Screenshit of DWR search results showcasing keyword highlighting

Conclusion

This post hopefully illustrates how different advanced search functionalities can improve search experience for the end user. Furthermore, it demonstrates that a Sitecore-based website can be set up and customized to include advanced search features, especially when paired with a powerful search platform such as Solr.

]]>
Tips to Make Sitecore load faster on development machines https://www.symsoftsolutions.com/blog/2019/01/22/tips-to-make-sitecore-load-faster-on-development-machines/ Tue, 22 Jan 2019 22:21:33 +0000 https://www.symsoftsolutions.com/?p=3899 I have found during my experience with Sitecore that it can take a long time to load when the system first initializes (Happens after publishing code or changing a configuration). This becomes frustrating for developers who are creating the website as for testing they have to wait for some time even while making small code changes. After looking at the log files and seeing what steps take the most time to run, I was able to do some research online and find out that I could change a few Sitecore configurations to make the system load faster. The changes I made are for Sitecore 9.0.1 but similar changes can be made for earlier or later versions of Sitecore. Below is a compilation of all the changes I made to make my Sitecore instance load faster.
1) Add optimizeCompilations=”true” to the compilation tag in web.config.
Replace the line

<compilation defaultLanguage="c#" debug="false" targetFramework="4.6.2”>

With

<compilation defaultLanguage="c#" debug="false" targetFramework="4.6.2" optimizeCompilations="true">

2) In Sitecore.Xdb.config located in \App_Config\Sitecore\Marketing.xDB turn off tracking and analytics by setting Xdb.Enabled and Xdb.Tracking.Enabled to false
3) Turn off device detection in \App_Config\Sitecore\DeviceDetection.Client\Sitecore.CES.DeviceDetection.config by setting DeviceDetection.Enabled to false
4) Patch the following configuration to make the sure the agent doesn’t run every 10 seconds. It appears to be related to list management. (Source: https://community.sitecore.net/developers/f/5/t/8920 )

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
 <sitecore role:require="Standalone or ContentManagement">
 <scheduling>
 <agent type="Sitecore.ListManagement.Operations.UpdateListOperationsAgent, Sitecore.ListManagement">
 <patch:attribute name="interval">00:30:00</patch:attribute>
 </agent>
 </scheduling>
 </sitecore>
</configuration>

Note: This should only be done on development machines and not in production. Also make sure your website is not making use of the features discussed below such as tracking, analytics or device detection.
Similarly, if later versions of Sitecore include features that slow down the system, you can track down what steps are taking longer to run by looking at the log files and doing a little research on these steps.

]]>
Five factors that make products usable https://www.symsoftsolutions.com/blog/2018/09/17/five-factors-that-make-products-usable/ Mon, 17 Sep 2018 18:58:03 +0000 https://www.symsoftsolutions.com/?p=3730

The terms usability and user experience (UX) appeared in the early 90’s and since then you can hear more and more people talking about them or referring to them as a “must have” in the design of a software solution. And they cannot be more right.

It is a common mistake, though, to mix up the meaning of usability with UX. After reading this article you’ll be able to tell others that those terms are definitely not the same thing. Spread the knowledge!

The usability of a product is one of the many parts that shapes its UX along with user research, information architecture, content strategy, visual design, and interaction design.
According to the ISO 9241-11:2018 (Ergonomics of human-system interaction) describes usability as “the extent to which a product can be used by specified users to achieve specified goals, with effectiveness, efficiency and satisfaction in a specified user context”.

It’s not the only definition we can find about usability but is basically telling us that making a product usable is not only about making it easy and simple, it actually involves more than that, and here we will talk about it starting with the five characteristics that all usable products have. Let’s review them one by one:

  1. Effectiveness
  2. Efficiency
  3. Engagement
  4. Error Tolerance
  5. Ease of Learning

Effectiveness

Effectiveness is about the high degree of accuracy under which users can complete their goals. The product has to be able to support the user while performing tasks.
For example, validating each field of a form accordingly (the postal code field has to be 5 characters long and only contain numbers) and be informative while doing it so, this can reduce data entry errors and help the user finish the task correctly.

It is also important to choose the right language to communicate and give instructions to the user. The clearer and simpler the language is you’re increasing the probability of understandability and also making the right impact on the user; this involves all the existent content in the product, even the error pages! Using the right level of technical jargon can make the difference; there are several studies that show how you communicate to the user can improve usability even by a 124% and therefore your effectiveness increases, too.

When improving the navigation to increase effectiveness, the right balance of redundancy can sometimes be what you need, like having a shortcut to the best seller products on an e-commerce site or having breadcrumbs along with the navigation menu, and even placing the same content on different sections of the site can help effectiveness as the users are more likely to reach their objectives if they have multiple paths to perform the same task; plus it can also reduce the overall efficiency on those tasks, just be sure to find the balance and not bloat your site and annoy your users.

Nordstrom, for example, has a category of “Women” clothing that you can find in the main navigation menu, but they also show it with a picture and a link underneath to browse in the same category.

Efficiency

Efficiency must not be mistaken for effectiveness as they are quite different and our goal is aim to have both of them. Effectiveness, as we covered above, is about the accuracy of the user to complete a task, while efficiency is how fast can the user finish the task. It’s all about speed!

When you’re reviewing efficiency you have to look at the numbers of steps (clicks/keystrokes) needed to achieve the objective. If they can be reduced but of course, without compromising the effectiveness of the task it can help develop a more efficient processes; for example labeling buttons very clearly so the user can be sure of what to do next or creating shortcuts.

Image credit: uxmovement

It’s also important to understand the environment under which the users are commonly interacting with your product. The navigation, layout and shortcuts are very different both visually and interactively if they’re using a smartphone vs a desktop computer.

Take for example MailChimp’s web and mobile page shown below, as they follow the same structure and the content also remains the same; but the main menu on the mobile site is not longer displayed as a horizontal navigation but as a hamburger menu containing the same sections. The layout changed to a more simplified version of the desktop site and the shortcuts were only maintained for the sign up option and the search.

Engagement

Engagement happens when the user finds your product enjoyable and satisfactory to use. Yes, aesthetics and great UI elements start to have relevance here, but they’re not the only factors implicated in creating a gratifying product that users like to interact with.

We can say with certainty that engagement is not only about looking good; it’s also about looking right and letting users achieve their goals, among other marketing strategies. Having the proper layouts, readable typography and ease of navigation are also characteristics that will allow to deliver the best interaction making the product engaging.

Here are two examples of engaging websites, that show not only great visual elements, but also play with interactive elements. They have easy to use navigation, and they communicate their message crystal-clear.

WWF (2012) and Logitech

Error Tolerance

Not one single interface ever created can be clean of errors as you can’t control the whole ecosystem under which the product is being used and certainly human error is only natural.
However, what we can do when designing a product, is try to minimize errors from occurring but if an error does occur make sure the users can quickly and easily recover from it and get back to what they were doing.In Human Computer Interaction (HCI) this is known as error tolerance.

Being tolerant to error means to make everything in our power to design a product in which is easy to achieve tasks and without letting the users get confused and do the wrong thing, for example:

  1. Making all the navigation elements clear and visible so the users can know where they’re at and where to go next.
  2. The right language comes into play again: communicate everything with a simple language.
  3. The actions performed have to be consistent throughout the product to reduce the probability of mistakes.
  4. Limit the options to only correct choices.
  5. And always provide feedback, like in the image below.

Ease of Learning

When a product requires the users to remember a lot of information or learn to do several things in order to be able to use it, it’s really hard for them to stick and engage with the product regularly. On the contrary if we have a product that lets the user learn to use it easily, the interaction will come as something natural the next time they use it.

The ease of learning also applies when a product is releasing new features or renewing functionality, you want your returning users to be happy with the improvements you make instead of being frustrated because everything has changed and it doesn’t work as it used to.
The solution for ease of learning is designing products matching the user’s mental models (representation of something existing in the real world; for example the trash can icon is the image of a real world trash can). Creating an “introduction” to the product that explains the features and tools it has, with the right language and the right visual aid can help improving the ease of learning.

For example, the app to find food trucks around town, Diamond Plate shows in just a few screens the whole concept of the product and its features, letting the user create a mental model of the app before even using it. As a result, when it’s time to interact with it, the users can easily navigate and make the best use of the app by reaching their goals.

Always test, and then test some more

Wouldn’t it be great to know, before releasing your product, that all of your hours spent working on it will finally pay off and users will get it? With usability testing you can. Knowing how your users will actually react to your product and stop developing ideas based on assumptions is something that can be accomplished only by approaching real users.

Usability testing starts by planning ahead of time some specific tasks that will be given to the users to complete. Once your out in the world, approaching a user can make you feel unease but from my experience I can tell that people are always eager to play with gadgets and help you out by giving you feedback. You made the introduction of the product and told them about the tasks, now it comes the best part, while they’re interacting with the product you need to pay close attention to their behavior: watch their body language, facial expressions, emotions and encourage them to speak up whatever comes to their mind while using the product. As a result, you’ll have both quantitative and qualitative data to explore and make the right changes to improve your product or continue doing what you’re doing.

More than just easy to use

Usability is more than just having a product easy to use. Making sure that it’s effective, efficient, engaging, error tolerant and easy to learn will increase the probability of your product succeeding. To be even more sure of its success you would need to run usability testing throughout the development phase. The bottom line is that usability should always be a priority.

By paying attention to the five key usability factors, and regularly including users to test your ideas, you’ll be able to meet their needs by creating usable products that allows them to solve everyday problems.

]]>