16179 words
summerlin.co

Context Help Message Component

Add the LC to the page layout of a record, and set the primary, secondary, and/or tertiary values to the API names of the field values to use. The values returned by these field names are used to construct the URL to retrieve a Markdown file from, using the Jumpstart public Github repo as the base:

https://resources.jstart.org/salesforce-help/{sObject API Name}/{value of primary field}/{value of secondary field}/{value of tertiary field}.md

The sObject name will be used as a root, with the custom object naming scheme deleted so that "Custom__c" will be sent as "custom". The last of the variables set will be used as the file name, with a ".md" appended onto the end. Spaces in the field value will be replaced with underscores. If there is no primary field entered, it will try to load https://resources.jstart.org/salesforce-help/{sObject API Name}/index.md to display generic help.

The Markdown is then loaded and parsed and added to the component.



Coding Standards and Strong Suggestions

The House Before The Drapes

Outcomes before inputs. Process before pages. Layout before colorscheme. Architecture before details. Ask why five times. Think and plan for the overall goals of a system or project, then think of how each audience will accomplish those goals. That should inform your interface whether it is a graphical user interface or just a single new button. Then look at how to make it well integrated and visually appealing. And being visually appealing does matter, because user experience is influenced by their attraction and comfort with the interface, which affects their effeciency with the tool.

Convention Over Configuration

Attempt to keep systems as consistent as possible by allowing the most limited number of customizations necessary to achieve the outcome. This does mean that conventions should be decided ahead of time to accomodate the real needs of users to track and evaluate different specific scenarios, but single audience customizations should be critiqued on the basis of legal/program requirements and effort to implement first.

Don't Reinvent the Wheel

When possible prefer frameworks and packages that are widely used, supported, and tested. No matter your language or system, someone has created a calendar for it already. No matter what you're working with, there is a way to handle validations you can implement. Let your work be focused on the processes that achieve the specific goals of the system, not the general goals of any system. As much as possible, keep using the same frameworks so that you can build mastery of a common set of tools. However don't be afraid to look at new frameworks to see where they might be superior or a better fit - or if they have good ideas you can steal.

Control Your Code

Filenames with -v2 is not version control. Using a version control system backs up your code, let's you branch to experiment, selectively return to earlier code choices, and work in teams without overwriting each other. Version control data can be profiled to understand the overall picture of change in systems, ways it has grown and changed, which helps in planning next steps and time allotment for work to be done. Also, by forcing certain working habits it makes it harder for bad/rushed choices to be made, and easier to be recovered from.

Gotta Keep 'Em Separated

Keep in mind a separation of concerns - data and code should be separate and independent. Specific record references should not be hard-coded into the project itself, but instead identified using flags or conditions stored on the record itself. For instance, if trying to make an exception for a child who only reads books upside down you shouldn't code if(Child.Id equals 100) then..., instead describe the condition on the Child record and test that with if(Child.readsUpsideDown equals true) then.... Doing this prevents having to search code when record values change, and allows for the possibility of more than one record needing that requirement in the future. This also means that if data needs to be migrated or updated it will not break existing code, and vice versa. Code should deal with properties of data, not particular data. Another way to sum it up is to say data should reflect meaning, code should only reflect process.

One Thing At A Time

When tracking information, the entities being processed should be normalized down to their constiuent independent parts. If something is likely to change year after year, or can/does exist independently of an aspect of its definition, it should likely be stored as its own record then related back to the unchanging part. For instance, a child may participate in a classroom program, but the child and classroom will likely exist after the program has ended for the year. So there should be a separate record for the child's personal information, a record for the classroom itself, and a record for the relationship between the child and the classroom with the details of that years experience. This also holds for properties of an entity. If a child's parent's give consent to be texted, the consent and the phone number should be stored separately, rather than relying on the presence of the phone number to imply consent. This will help the record be more tolerant to changing processes and more intuitively represent something in the real world in the data structures of the program.

Similarly functions and processes should do a minimal amount of data change, instead favor multiple smaller functions that can be chained to change data in sequence. This will help with troubleshooting as issues can be identified more clearly, and code can be reused more easily.

Explicit Is Better Implicit

Similarly, any data, status, or process should spell out what it is and what it means. Data itself (whether in variables or in a database) should be clearly labelled, and ideally calculated fields or columns should be present to define conditional compound values, rather than relying on if/else's in the code. This is obviously not always possible, but as much as it is it is better to have all meaningful information present where the data is stored rather than relying on later interpretation by code. Specifically in Salesforce and MySQL a formula can be implemented to combine other data values so that the conditional value is clear. Instead of creating a script that says if(ConsentTexting and ConsentMedia) then... you can create a formula/generated field called ConsentAll that equals (ConsentTexting and ConsentMedia) and then test ConsentAll in the code. That way you have just one place to check in the future if other forms of Consent need to be checked. Again, this isn't always possible, but a good practice if you can.

Clear Names Full Words, Can't Lose

No acronyms. Minimal jargon. Whole words. Assume a beginners mind. Not only will you reduce your own cognitive load in developing, but you will make it far easier for new people to support or use your project. Resist the temptation of "everyone knows what this means" in anything less than national or international standards, because you are building not just for the people we already have, but people who have not yet joined.

CamelCase or under_score but nothing more

All code from variables to function names should be in written CamelCase or under_score and should be consistent across the system/project. The only time to switch standards is when a character limit is imposed in some way to prevent being consistent, as in the case when you are using under_score but run out of room naming a field. You should try to use the same naming scheme as the system you are working in, such as how Salesforce uses under_score for most of its fieldnames.

DRY Up Your Code

Don't Repeat Yourself. If you notice a significant chunk of very similar code repeating, it is probably a good idea to make that a function on its own and call it where it shows up. That may mean generalizing a small part and calling it with a parameter. This helps reduce overall code size which increases readability and manageability. It also allows for easier updates, bug fixes, and building new features - less code, less to debug. Balance matters though - don't try to DRY two or three lines of code on their own and don't DRY to the detriment of clarity and readability.

Reduce, Reuse, Recycle

It isn't just good for the environment. Write your code in such a way that you might be able to use it again, either within the same project or in another. By using processes that focus on small data changes at a time (reducing) and moving repeated code to their own modules (reusing) you can recycle that code and not only save time but prevent new bugs coming up in new code.

Don't Optimize Prematurely, But Optimize

Trying to optimize too soon can lead to getting stuck in development loops. A little like an author retooling a paragraph over and over, they might never finish the book. It is better to plow through, get the minimum viable product out and then optimize. And you should optimize, to be sure - but doing it too soon may make you optimize a pattern you actually don't use very frequently, while missing a larger pattern that you can't see until the project is more complete.

The Principle of Least Privilege

At all times the minimum amount of data access should be observed. We may not be able to imagine any of our people doing something malicious, but security is not about what we can imagine - it is about what we haven't even guessed. Accidents happen, accounts are subverted, and people get angry - security is just making sure that no matter the cause, information stays safe. Good security is not a pair of handcuffs on users, it is an umbrella over their heads. Identifiying exactly what people need access to for their role helps simplify their experience and reduce the possibility of accidents. In this way reducing access is actually increasing ease and comfort of use.

Validation Isn't Just For Feelings

Data should always be validated twice - once on the front end to serve the user and help them have a better and more confident experience, and once on the back end to ensure the data is sane and accurate. Front-end validation should display specific warnings, ideal located on the fields that the issue exists on. Things like valid date ranges, sane number scales, and field length should be implement along with data type checks (numbers for numbers, text for text) and required fields. Things like placeholder values or below the field help text are great to assist the user in getting it right the first time. As you consider process and layout, think, "How can I help prevent users from doing something by accident?"

On the back end data should be santitized (cleared of potentially problematic data) and validated again. It is possible in most systems for data to come in from multiple points of entry, some of which (like a web form) can be easily manipulated or corrupted. Back end validations are much like implicit data - best implemented where the data is stored. MySQL column valdiations and Salesforce record validations help make sure that the very last step in the process before being saved is a validation. Errors when validations fail on the back end must be bubbled up to the front end so that the user or program has a chance to react to it, at a minimum informing the user data was not saved.

On Error Cause Terror

Processes and validations should never fail silenty. An error should strongly draw the users attention and raise a flag that something needs to be dealt with. Errors must be handled, whether that is a front-end message that just says "Error, contact system administrator" or a more complete handler for something that is user fixable. Most functions should use some variety of try/catch processing to allow users to understand what has happened to their data. If it is something they can fix, like a data validation failure, then the interface should give them that option without reloading the page or losing their current state. If it is an issue they cannot fix, then ideally it should notify the appropriate support staff automatically with a well defined error message, or else give them the data to pass on a detailed error report.

Lovely Little Layout Logic

  • Visual breathing room not dead space
  • Big text is better than small text
  • Respect muscle memory
  • Once drawn on screen don't move
  • Mobile First but only for mobile things
  • Consistent colors for similar subjects
  • Apply Occam's Razor: "Entities should not be multiplied without necessity" - show only what the user needs to accomplish the system's goal

Dance Like Nobody's Watching, Code Like Everyone Is

Keep in mind that it is very likely someone else will need to read what you have written, follow your logic, and understand your goals. Use comments like salt - sprinkle on to enhance, but don't overdo it. When possible have others immediately look at your code and give you feedback on what makes sense, and what doesn't. Don't be afraid to use other people as a rubber duck - and even better, then can talk back. Keep even your future self in mind, who will surely read present-you's code and say "What were you thinking?!"

Context Switching Costs Something

When working on any project, give yourself time and space to really focus. Programming is not a discrete action like answering an email or finishing a form. It requires a continuous effort to think through a complicated system from end to end. Entities, relationships, processes, and audiences are all a part of every step of programming, and every time you have to take your mind out of that flow you lose momentum. When asked to do something else, the effort to complete the request may be very small, but the cost of having to clear your mind of one set of information, load another, and then switch back is huge. Multitasking is a myth - the human brain doesn't actually multitask as we commonly think of it. What we do is juggle contexts, and like most juggling it is difficult and often ends up with someone dropping the ball. Give yourself time, and protect the time to get your task done - it actually benefits even someone with "a quick question".

NED19: Keynote Education Transformation through Trust Networks

Phil Kourmany

State of industry

“The illiterate of the 21st century will not be those who cannot read and write, but those who cannot learn, unlearn, and relearn.”
Alvin Toffer

  • Not episodic learning, constant learning and back and forth
  • UofMaryland 96% ready, IBM saying 11%
  • silos exist on all campuses
  • Conductor of Information Orchestra

Small scale innovation - low to no budget (ummm, middle)

Seton Hill - first on iPads everywhere

  • spent year going over why tech was failing them before the iPad program launch
    • changed relationship with device - like "your kids photos are on here" to explain why passwords more
    • Seton's infrastructre in 2010:
    • IT/Finance went from last to first, because they listened and updated
    • found value proposition for different users
    • They all had one mission - Recruit and Educate~ Students - and any staff person could tell you that

Large Scale innovation - Texas sized budget

  • $100mil
  • project failed probably because not enough communication
  • worked with Admiral "Make your bed"
  • ERPs integrated into a profile for their students -- changed into conversation on student success and worked backwards to DB
    • integration of LMS, ERP, career system, into one cohesive experience for students
    • students in Rio Grande Valley got to benefit
  • 2015 - they were working on blockchain to track student skills

Salesforce for DX in education

Emergent Technologies for DX

SF Blockchain

  • computable badges - OBIv2
    • Trailhead/AWS educate/others certify completion
  • tools of transformation
    • JDX - JobSchema
    • mapping curriculum learning objectives - Course (something) to let translate skills to employer
  • to share data with other orgs, traceable
    • could be used across nonprofits for....something
    • cost?
    • student tracking - they get one block record, synced across orgs, updated with education as they go, they get access to show an employer, etc
  • Sawtooth
    • Hyperledger implementation
    • Smart Contract, Consensus, something else
    • Open itself, others can integrate into it
    • retains partial data

NED19: Building a Template for Success

@validationfools
https://www.salesforceben.com/custom-metadata-types/

Custom Metadata and Flow

  • NISC (Michelle's company)
    • 800 members (customers)
    • 75+ products (220+ SKUs)
    • 11+6 staff
  • Contracts get technical
    • Created SOS, Services Outisde of Standard, custom object on orders
      • capture special requests
        • multiple years of data
        • APIs and integrations
      • track missing information
        • imports from current docs
        • number of licenses needed
      • verify standard config details
        • ie physical v virtual
  • Problems
    • 50+ types potentially on Opps
    • people not creating them


They want to choose on demand SOSes at any given time not only in response

CMDT is just static data stored by itself, like a one-off record

New Process

  • New requirements
    • Display master list of SOS (from Excel)
    • SMs select & create SOSes on demand
    • Maintain standard data in fields
    • Continue to prevent duplicates
    • but this system requires system controls, it can't be 100% controlled
    • but data changes frequently, how can we sustainably maintain it
    • but what about items missed during the cycle
  • Suggestion: just clone - won't work
    • field level security, product managers quote custom products
    • sales ops has to confirm
    • changing data, changes with staff
    • validation procedures change
    • no duplicate management
  • New Idea - record template - CMDT
    • 46 "Standard" SOSes
      • same basic info each each time (ie you want to virtualize servers, here are the requirements)
    • Create from Template
      • ensures complete and accurate info
    • Easy as a Custom Object
      • CMDT fields map to SOS record fields

To design:

Doing dupe check with flag on originating record
CMDT supports text, number, picklist

Call Capture example:

Flow:

  • looks up opportunity
  • checked for SOS
  • check type of Opp
    • looks up SOSes that are compatible to show user
    • Allows them to pick 1+ SOS
      • make sure again that they don't exist
      • Pull all valid ones CMDT and use to populate records
      • sets data and creates SOS per each
        • can use the Opp data fields to name/populate SOS fields

Other Ideas

  • Country/Region Mapping
    • ex one record
      • Country: USA
      • Region: North American
  • Record Type ID to Record Type Name mapping
    • just generally use a CMDT to get dynamic IDs, like JStartImportantIdsc.AdminUserIdc
  • pricing structures
    • product sku map etc
  • automation on/off switches
    • have a check for the CMDT with a checkbox on can_automate
  • Project Estimates
    • Object for requests, so use CMDT to profile tasks and their values

NED19: Lighting Components

https://unofficialsf.com/flow-screen-components/

Actual LWCs:

Confetti, but actually though

NED19: Lighting Ex

Simplicity > Consistency > Ease of Use

Simplicity

  • Use navigation tabs to show only what is relevant
  • Use Apps to define what tabs they see, have focused Apps
  • Use Set Component Visibility to hide irrelevant components
    • look into Rich Text Field for links etfc (like the child data links)
    • you can use parts of email address or department attribute of user and permission sets
  • Use Compact Layout to highlight key info
  • Use minimal Tabs to simplify all record pages
  • Use Related List Quick Links

Consistency

  • same rough layout on each object

Ease of Use

  • Global actions - the little plus sign
  • 3-5 features, for things like logging call etc
  • Utility bar to house macros etc
    • list view in utility bar
      • you can pop the list view out and have it control the main view
  • Home page list views
  • Use Rich Text Components to show relevant hyperlinks, alerts and reminders
  • Report charts relevant for the screen

Theme and branding (we already have customized)

NED19: Admin and SFDX

get this presentation to see all the exact commands

  • every command has a help guide
  • don't need to use scratch orgs - he doesn't know anyone who does
  • big win of SFDX is multiple logins
    • open alias - opens in browser

moving metadata

  • run tests - selective or all
  • pull changesets and then push to other orgs
  • watching changeset progress possible, validation/check possible
  • deploy from directory possible - could be useful in sandbox quick checks
  • Moving Massive Metadata
    • video on how to create 500 fields
    • XML file -> copy field, use Excel to generate fields, copy back into XML file
    • did it on Page Layout too
  • using changeset to identify just particular objects etc to download that metadata

Export Data from CLI

  • Run SOQL right from sfdx, straight to csv file if necessary
    • sfdx force:data:soql:query
    • can pull json with --json
  • sfdx force:data:record:get examples
    • sfdx force:data:record:get -s Account -i 001D000000Kv3dl
    • sfdx force:data:record:get -s Account -w "Name=Acme"
    • sfdx force:data:record:get -s Account -w "Name='Universal Containers'"
    • sfdx force:data:record:get -s Account -w "Name='Universal Containers' Phone='(123) 456-7890'"
    • sfdx force:data:record:get -t -s TraceFlag -i 7tf170000009cUBAAY --perflog --json
  • sfdx force:data:bulk looks interesting!
  • sfdx report building - commandline sort multiple Object types into one combined report

NED19: Automate all the things

Know your process

People stuff first, you have to know the people and what they do and WHY

Process learning techniques

  • SABWA
    • Salesforce Admin by walking around
    • ask to see common process
    • not to check if they are "correct", but just to chech reality
    • mobile, desktop, virtual, doesn't have to be in person
      • KATIE shadowing opportunities
  • The 5 Whys
    • story of the Lincoln Memorial with bird poop and lights
    • get past obvious (Root Cause Analysis)
      • ie Finance spreadsheets
  • OpenEnded Questions
    • requires a response different than yes or no
    • ask things like "how", "why", "what"
      • how is the information collected
      • what reports do you need regulary
      • why does your data get formatted that way

Process Mapping Techniques

Low tech: whiteboards, sticky notes

  • assemble cross-functional teams
  • determine individual process setps and who does
  • identify key areas:
    • optimization hot spots
    • deliverables and outputs
    • handoffs between teams
    • "secret spreadsheets"
    • duplicate effort

Middle Tech: Cross-functional flowcharts

  • often used software Visio lucidchart
  • each "swim-lane" represnets a function
  • breezetree article flow

High Tech: Process Mapping Software

  • Elements.cloud (Ian Gotts)
  • Nintex

Choose the Right Tools

  • Quick Actions
    • simple to create
    • great for mobile and desktop
    • useful for common tasks
    • "Custom buttons" for the Lightning Era
  • Approval Processes (!!!)
    • good for simple processes
    • (interface leaves something to be desired)
    • may want to use Roles or custom user fields for hierarchies
      • can use custom fields to handle escalation
      • can have criteria set up about which record should enter the stage
    • can prevent record changes while in approval process
      • maybe for Budget Line Items
      • maybe for Volunteer Registrations
      • can use it just to make sure each step is used like an assembly process, less about "approval" per se
    • Process Builder
      • great for creating or updating records
      • Immediate or Time-delayed actions
      • Easy to understand
      • Don't go overboard
        • 1 main process per object - use sub-processes to control order of operations) (Salesforce best practice) - invocable sub-processes
    • Flow Builder
      • Enables rich user input
      • Allows record deletion
      • More complicated than PB, but still visual
      • The new hotness
    • Macros (!!!)
      • Recordable by end users
      • Great for repeated tasks
      • Editable and shareable
      • Not console only
      • Can have step logic
      • Can do emails
        • can insert at cursor, after/before/replace
        • must implement for Patrick
    • Code: Apex Triggers/Lightning Components
      • sample gallery
      • Automation Home (Beta)

The Power of Too

WIP

Balance of Power

If you have been around Salesforce for any amount of time, there is a good chance you have heard of The Power of One, a brilliant way to perform different calculations on the fly that until very recently simply weren't an option. By just adding a field that always equals 1, you can find totals, averages, and all sorts of other useful statistics in reports dynamically.

But sometimes you need to know something that just doesn't fit a mathmetical method. Sometimes you need more than 1 - you need "too".

The Problem

Actually, there are a lot of them. Sometimes you need to get information on multiple elements of a record. Sometimes you need to account for many true/false values, and you end up with a truly ugly IF() tree. The truth is, there are a lot of situations where you may want to look at many values all together, but you can run into some really unruly formulas very quickly.

I was thinking through a very ugly series of IFs and I just couldn't get it right. There were several different checkboxes, and different combinations that could all mean the same thing. I actually ran into a formula character limit just trying to accomodate all the different states! Somewhere in the back of my mind I kept thinking, "There has to be a better way!"

Finally it clicked - condition flags! The original inspriation came from how command line programmers use binary values like a list of true/false flags - more on that at the bottom. But after working out a math-y way to tackle this, it dawned on me letter flags would be more readable and easier to understand. So by mapping our conditions to a letter or symbol and then using the CASE() function, our formulas can become enormously flexible and expressive, all while staying simple and clearly written. This is the Power of Too - using CASE to see if a letter is included too (alright, it's a little weak, but I like the sound of it, and it is in honor of the binary that inspired it).

The Solution

So how can we implement a Power of Too formula in Salesforce?

If we assign a Power of Too value to our areas of concern we can find all kinds of conditions! For instance, we have an organizational history check process that needs to satisfy multiple steps, but those steps may have different requirements for different people, serving in different US states. The record type has been created with a set of checkbox fields that will be TRUE if a part of the process is missing:

Now, depening on which state the person is in, different rules may apply. Also, depending on where they are in the process, they may be able to participate in some parts of the program, but not others. So normally we would set up a series of IF statements to try to capture the History Check Status:

IF(OR(History_Check_NSOPW_Missing__c, History_Check_Service_State_Missing__c, History_Check_Residence_State_Missing__c, History_Check_FBI_Missing__c, History_Check_Acknowledgement_Missing__c),
    "Missing Steps",
    "Training and Service"
)

That would tell us if they can serve, but not where in the process they are. So let's make it a little more specific:

IF(OR(History_Check_NSOPW_Missing__c, History_Check_Service_State_Missing__c, History_Check_Residence_State_Missing__c, History_Check_FBI_Missing__c, History_Check_Acknowledgement_Missing__c),
    IF(AND(NOT(History_Check_NSOPW_Missing__c), OR(History_Check_Service_State_Missing__c, History_Check_Residence_State_Missing__c, History_Check_FBI_Missing__c, History_Check_Acknowledgement_Missing__c)),
        "NSOPW Complete",
        "Missing Steps"
    ),
    "Training and Service"
)

But that only gets us one more layer. So:

IF(OR(History_Check_NSOPW_Missing__c, History_Check_Service_State_Missing__c, History_Check_Residence_State_Missing__c, History_Check_FBI_Missing__c, History_Check_Acknowledgement_Missing__c),
    IF(AND(NOT(History_Check_NSOPW_Missing__c), OR(History_Check_Service_State_Missing__c, History_Check_Residence_State_Missing__c, History_Check_FBI_Missing__c, History_Check_Acknowledgement_Missing__c)),
        "NSOPW Complete",
        IF(AND(NOT(OR(History_Check_NSOPW_Missing__c, History_Check_FBI_Missing__c)), OR(History_Check_Service_State_Missing__c, History_Check_Residence_State_Missing__c, History_Check_Acknowledgement_Missing__c)),
            "FBI Check Complete",
            "Missing Steps"
        )
    ),
    "Training and Service"
)

Not only would going on this way take a long time, updating it would be a nightmare, and there are variations it doesn't work well with. There may be states that allow people to serve with only an FBI check, and their might be some that don't require an acknowledgement letter sent out. We would need to do an entire convoluted IF series for those scenarios as well. And though there may be better ways to structure these statements (I'm sure there is), they would still fundamentally run into issues with flexibility, readability, and maintainability longterm.

By Our Powers Combined

But what if we look at those checkboxes another way? Then something useful starts to happen:

The state of that record turns out to be something we can represent with a short string of letters - in this case 'sa'. We can check that value against our list of outcomes, we just need to make a Power of Too map, like this:

Values:
History_Check_NSOPW_Missing__c                      = n
History_Check_Service_State_Missing__c              = s
History_Check_Residence_State_Missing__c            = r
History_Check_FBI_Missing__c                        = f
History_Check_Acknowledgement_Missing__c            = a
Agreement__r.State_Policy_Accompaniment_Allowed__c  = c // we can even add flags for values from related records, and this will be counted if equal to TRUE

Outcomes:
Accompaniment                                       = nc
Non-Accompaniment (State Cleared)                   = nfa
Non-Accompaniment (State Cleared)                   = nfac
Non-Accompaniment (FBI Cleared)                     = nsrac
Non-Accompaniment (FBI Cleared)                     = nsac
Non-Accompaniment (FBI Cleared)                     = nrac
Non-Accompaniment (FBI Cleared)                     = nra
Acknowledgement                                     = a
Acknowledgement                                     = ac
Training and Service                                = BLANK
Training and Service                                = c
None                                                = Default output

Once we have a clear understanding of the map of values, then we can make our formula accordingly:

CASE(
IF(History_Check_NSOPW_Missing__c, 'n', '')
+ IF(History_Check_Service_State_Missing__c, 's', '')
+ IF(History_Check_Residence_State_Missing__c, 'r', '')
+ IF(History_Check_FBI_Missing__c, 'f', '')
+ IF(History_Check_Acknowledgement_Missing__c, 'a', '')
+ IF(Agreement__r.State_Policy_Accompaniment_Allowed__c, 'c', '')
,
    'srfac', "Accompaniment",
    'fa', "Non-Accompaniment (State Cleared)",
    'fac', "Non-Accompaniment (State Cleared)",
    'sra', "Non-Accompaniment (FBI Cleared)",
    'srac', "Non-Accompaniment (FBI Cleared)",
    'sa', "Non-Accompaniment (FBI Cleared)",
    'sac', "Non-Accompaniment (FBI Cleared)",
    'ra', "Non-Accompaniment (FBI Cleared)",
    'rac', "Non-Accompaniment (FBI Cleared)",
    'a', "Acknowledgement",
    'ac', "Acknowledgement",
    '', "Training and Service",
    'c', "Training and Service",
"None")

Note that when we check a value, in this case true/false values, we need to understand the output we want. Most of those fields are "missing" fields - they become true if information is not on a record. The Agreement field that allows us to use the Accompaniment status works the other way - if it is true, then we need to use that information. So when returning the flag value for each, we need to be sure it goes in the right order in the IF function.

Now, not only is that function more readable, but it is also more flexible. If some states made a rule that only people over 18 could volunteer, we could integrate that by simply adding other cases with just one line, rather than re-writing a complex flow of IF statements:

CASE(
IF(History_Check_NSOPW_Missing__c, 'n', '')
+ IF(History_Check_Service_State_Missing__c, 's', '')
+ IF(History_Check_Residence_State_Missing__c, 'r', '')
+ IF(History_Check_FBI_Missing__c, 'f', '')
+ IF(History_Check_Acknowledgement_Missing__c, 'a', '')
+ IF(Agreement__r.State_Policy_Accompaniment_Allowed__c, 'c', '')
+ IF(AND(History_Check_Is_Minor__c, Agreement__r.State_Policy_Minors_Disallowed__c), 'm', '')
,
    'srfac', "Accompaniment",
    'fa', "Non-Accompaniment (State Cleared)",
    'fac', "Non-Accompaniment (State Cleared)",
    'sra', "Non-Accompaniment (FBI Cleared)",
    'srac', "Non-Accompaniment (FBI Cleared)",
    'sa', "Non-Accompaniment (FBI Cleared)",
    'sac', "Non-Accompaniment (FBI Cleared)",
    'ra', "Non-Accompaniment (FBI Cleared)",
    'rac', "Non-Accompaniment (FBI Cleared)",
    'a', "Acknowledgement",
    'ac', "Acknowledgement",
    '', "Training and Service",
    'c', "Training and Service",
"None")

If the minor is blocked from serving, none of the values will match allowed status values, so they will default to none. We didn't have to rewrite any IF logic and can even implement other statuses just by matching a number to the output we want our formula to have! A word of warning, when using letters as flags, be absolutely certain that you are using unique values, or you could end up matching against the wrong flags.

Power to the People

By using the Power of Too, we can have an extremely flexible solution to complicated outcomes. It can take a little rewiring and thinking ahead about how to map the values, but once your flags are set you can easily manage a wide array of situations. Though the Power of Too is a little more involved than the Power of One, it is a handy tool to have in your belt as you build your formulas!

Adden-dumb - The 1s and 0s

The first way I went about this used a concept called binary flags, which uses powers of 2 to translate between numbers we normally use and the strings of 1s and 0s you see in cheesy computer animations. I liked the math-iness of this method and getting to talk about binary. But then it hit me, you can do this with plain old letters too, and with better readability! The original thought is worth noting though, as it can work in situations where you can't use letters.

Binary is a base 2 number system, and it reads from right to left. Each number position is either a 0 or 1, which is what makes it a great match for tracking true/false values (also known as Boolean values). Binary math looks like:

     01 =   1
    + 1 = + 1
     10 =   2
    + 1 = + 1
     11 =   3

Think about it like those elementary school counting blocks - the base is how high you can count before you have to start a new stack. No blocks = 0, and when the next block you put on would equal the base number, you start a new stack. Each unit value (the counting block) in base 10, the system we normally use, can hold a 0 through a 9 - then it rolls over to the next digit, so 1+9 = 10. Binary works the same way, but it only gets two unit values, 0 and 1. Once you go to put on that second block, you have to make a new stack!

Now that means if we take a decimal (base 10) number, we can convert that into a binary number that can represent true/false values - even if we never see a string of 0s and 1s.

Basically, all we really have to do is build our map with powers of two (20 = 1, 21 = 2, 22 = 4, 23 = 8, etc), and then check our match from there. So to rewrite the solution above:

Values:
History_Check_NSOPW_Missing__c                      = 1
History_Check_Service_State_Missing__c              = 2
History_Check_Residence_State_Missing__c            = 4
History_Check_FBI_Missing__c                        = 8
History_Check_Acknowledgement_Missing__c            = 16
Agreement__r.State_Policy_Accompaniment_Allowed__c  = 32

Outcomes:
Accompaniment                                       = 2+4+8+16+32
Non-Accompaniment (State Cleared)                   = 8+16
Non-Accompaniment (State Cleared)                   = 8+16+32
Non-Accompaniment (FBI Cleared)                     = 2+16
Non-Accompaniment (FBI Cleared)                     = 4+16
Non-Accompaniment (FBI Cleared)                     = 2+4+16
Non-Accompaniment (FBI Cleared)                     = 2+16+32
Non-Accompaniment (FBI Cleared)                     = 4+16+32
Non-Accompaniment (FBI Cleared)                     = 2+4+16+32
Acknowledgement                                     = 16
Acknowledgement                                     = 16+32
Training and Service                                = 0
Training and Service                                = 32
None                                                = Default output

Once we have a clear understanding of the map of values, then we can make our formula accordingly:

CASE(
IF(History_Check_NSOPW_Missing__c, 1, 0)
+ IF(History_Check_Service_State_Missing__c, 2, 0)
+ IF(History_Check_Residence_State_Missing__c, 4, 0)
+ IF(History_Check_FBI_Missing__c, 8, 0)
+ IF(History_Check_Acknowledgement_Missing__c, 16, 0)
+ IF(Agreement__r.State_Policy_Accompaniment_Allowed__c, 32, 0)
,
    2+4+8+16+32, "Accompaniment",
    8+16, "Non-Accompaniment (State Cleared)",
    8+16+32, "Non-Accompaniment (State Cleared)",
    2+16, "Non-Accompaniment (FBI Cleared)",
    4+16, "Non-Accompaniment (FBI Cleared)",
    2+4+16, "Non-Accompaniment (FBI Cleared)",
    2+16+32, "Non-Accompaniment (FBI Cleared)",
    4+16+32, "Non-Accompaniment (FBI Cleared)",
    2+4+16+32, "Non-Accompaniment (FBI Cleared)",
    16, "Acknowledgement",
    16+32, "Acknowledgement",
    0, "Training and Service",
    32, "Training and Service",
"None")

So, if you like math, the Power of Two is here to help - but if you like letters, you can use that Power, Too.

The Power of Two

WIP

Balance of Power

If you have been around Salesforce for any amount of time, there is a good chance you have heard of The Power of One, a brilliant way to perform different calculations on the fly that until very recently simply weren't an option. By just adding a field that always equals 1, you can find totals, averages, and all sorts of other useful statistics in reports dynamically.

But sometimes you need to know something that just doesn't fit a mathmetical method. Sometimes you need more than 1 - you need 2.

The Problem

Actually, there are a lot of them. Sometimes you need to get information on multiple elements of a record. Sometimes you need to account for many true/false values, and you end up with a truly ugly IF() tree. The truth is, there are a lot of situations where you may want to look at many values all together, but you can run into some really unruly formulas very quickly.

I was thinking through a very ugly series of IFs and I just couldn't get it right. There were several different checkboxes, and different combinations that could all mean the same thing. I actually ran into a formula character limit just trying to accomodate all the different states! Somewhere in the back of my mind I kept thinking, "There has to be a better way!"

Finally it clicked - binary flags! Binary values can work like a list of true/false flags if you can read them right. Fortunately we can work with binary numbers by using...drum roll...the Powers of Two! By adding and subtracting powers of two (22 = 4, 23 = 8, etc), and then using the CASE() function, our formulas can become enormously flexible and expressive, all while staying simple and clearly written.

The Solution

So how can we implement a Power of Two formula in Salesforce? Well first, we need to understand some basic math. Binary is a base 2 number system, and it reads from right to left. Each number position is either a 0 or 1, which is what makes it a great match for tracking true/false values (also known as Boolean values). Binary math looks like:

 01 =   1
+ 1 = + 1
 10 =   2
+ 1 = + 1
 11 =   3

Now that means if we take a decimal (base 10) number, we can convert that into a binary number that can represent true/false values - even if we never see a string of 0s and 1s. If we assign a Power of Two value to our areas of concern we can find all kinds of conditions! For instance, we have an organizational history check process that needs to satisfy multiple steps, but those steps may have different requirements for different people, serving in different US states. The record type has been created with a set of checkbox fields that will be TRUE if a part of the process is missing:

Now, depening on which state the person is in, different rules may apply. Also, depending on where they are in the process, they may be able to participate in some parts of the program, but not others. So normally we would set up a series of IF statements to try to capture the History Check Status:

IF(OR(History_Check_NSOPW_Missing__c, History_Check_Service_State_Missing__c, History_Check_Residence_State_Missing__c, History_Check_FBI_Missing__c, History_Check_Acknowledgement_Missing__c),
    "Missing Steps",
    "Training and Service"
)

That would tell us if they can serve, but not where in the process they are. So let's make it a little more specific:

IF(OR(History_Check_NSOPW_Missing__c, History_Check_Service_State_Missing__c, History_Check_Residence_State_Missing__c, History_Check_FBI_Missing__c, History_Check_Acknowledgement_Missing__c),
    IF(AND(NOT(History_Check_NSOPW_Missing__c), OR(History_Check_Service_State_Missing__c, History_Check_Residence_State_Missing__c, History_Check_FBI_Missing__c, History_Check_Acknowledgement_Missing__c)),
        "NSOPW Complete",
        "Missing Steps"
    ),
    "Training and Service"
)

But that only gets us one more layer. So:

IF(OR(History_Check_NSOPW_Missing__c, History_Check_Service_State_Missing__c, History_Check_Residence_State_Missing__c, History_Check_FBI_Missing__c, History_Check_Acknowledgement_Missing__c),
    IF(AND(NOT(History_Check_NSOPW_Missing__c), OR(History_Check_Service_State_Missing__c, History_Check_Residence_State_Missing__c, History_Check_FBI_Missing__c, History_Check_Acknowledgement_Missing__c)),
        "NSOPW Complete",
        IF(AND(NOT(OR(History_Check_NSOPW_Missing__c, History_Check_FBI_Missing__c)), OR(History_Check_Service_State_Missing__c, History_Check_Residence_State_Missing__c, History_Check_Acknowledgement_Missing__c)),
            "FBI Check Complete",
            "Missing Steps"
        )
    ),
    "Training and Service"
)

Not only would going on this way take a long time, updating it would be a nightmare, and there are variations it doesn't work well with. There may be states that allow people to serve with only an FBI check, and their might be some that don't require an acknowledgement letter sent out. We would need to do an entire convoluted IF series for those scenarios as well. And though there may be better ways to structure these statements (I'm sure there is), they would still fundamentally run into issues with flexibility, readability, and maintainability longterm.

By Our Powers Combined

But what if we look at those checkboxes another way? Then something useful starts to happen:

The state of that record turns out to be something we can represent with a single binary (base 2) number - in this case 01001, or 9 in decimal. We can check that base 10 value against our list of outcomes, we just need to make a Power of Two map, like this:

Values:
History_Check_NSOPW_Missing__c                      = 1 // in this case the Missing fields are counted if they equal FALSE
History_Check_Service_State_Missing__c              = 2
History_Check_Residence_State_Missing__c            = 4
History_Check_FBI_Missing__c                        = 8
History_Check_Acknowledgement_Missing__c            = 16
Agreement__r.State_Policy_Accompaniment_Allowed__c  = 32 // we can even add flags for values from related records, and this will be counted if equal to TRUE

Outcomes:
Accompaniment                                       = 33 (1+32)
Non-Accompaniment (State Cleared)                   = 57 (1+8+16+32)
Non-Accompaniment (FBI Cleared)                     = 51 (1+2+16+32)
Non-Accompaniment (FBI Cleared)                     = 53 (1+4+16+32)
Non-Accompaniment (FBI Cleared)                     = 55 (1+2+4+16+32)
Acknowledgement                                     = 15 (1+2+4+8)
Acknowledgement                                     = 47 (1+2+4+8+32)
Training and Service                                = 31 (1+2+4+8+16)
Training and Service                                = 63 (1+2+4+8+16+32)
None                                                = Default output

Once we have a clear understanding of the map of values, then we can make our formula accordingly:

CASE(
IF(History_Check_NSOPW_Missing__c, 0, 1)
+ IF(History_Check_Service_State_Missing__c, 0, 2)
+ IF(History_Check_Residence_State_Missing__c, 0, 4)
+ IF(History_Check_FBI_Missing__c, 0, 8)
+ IF(History_Check_Acknowledgement_Missing__c, 0, 16)
+ IF(Agreement__r.State_Policy_Accompaniment_Allowed__c, 32, 0)
,
    33, "Accompaniment",
    57, "Non-Accompaniment (State Cleared)",
    51, "Non-Accompaniment (FBI Cleared)",
    53, "Non-Accompaniment (FBI Cleared)",
    55, "Non-Accompaniment (FBI Cleared)",
    15, "Acknowledgement",
    47, "Acknowledgement",
    31, "Training and Service",
    63, "Training and Service",
"None")

Note that when we check a value, in this case true/false values, we need to understand the output we want. Most of those fields are "missing" fields - they become true if information is not on a record. The Agreement field that allows us to use the Accompaniment status works the other way - if it is true, then we need to use that information. So when returning the flag value for each, we need to be sure it goes in the right order in the IF function.

Now, not only is that function more readable, but it is also more flexible. If some states made a rule that only people over 18 could volunteer, we could integrate that by simply adding other cases with just one line, rather than re-writing a complex flow of IF statements:

CASE(
IF(History_Check_NSOPW_Missing__c, 0, 1)
+ IF(History_Check_Service_State_Missing__c, 0, 2)
+ IF(History_Check_Residence_State_Missing__c, 0, 4)
+ IF(History_Check_FBI_Missing__c, 0, 8)
+ IF(History_Check_Acknowledgement_Missing__c, 0, 16)
+ IF(Agreement__r.State_Policy_Accompaniment_Allowed__c, 32, 0)
+ IF(AND(History_Check_Is_Minor__c, Agreement__r.State_Policy_Minors_Disallowed__c), 0, 64)
,
    33 + 64, "Accompaniment",
    57 + 64, "Non-Accompaniment (State Cleared)",
    51 + 64, "Non-Accompaniment (FBI Cleared)",
    53 + 64, "Non-Accompaniment (FBI Cleared)",
    55 + 64, "Non-Accompaniment (FBI Cleared)",
    15 + 64, "Acknowledgement",
    47 + 64, "Acknowledgement",
    31 + 64, "Training and Service",
    63 + 64, "Training and Service",
"None")

If the minor is blocked from serving, none of the values will match allowed status values, so they will default to none. We didn't have to rewrite any IF logic and can even implement other statuses just by matching a number to the output we want our formula to have!

Power to the People

By using the Power of Twos, we can have an extremely flexible solution to complicated outcomes. It can take a little rewiring and thinking ahead about how to map the values, but once your flags are set you can easily manage a wide array of situations. Though the Power of Two is a little more involved than the Power of One, it is a handy tool to have in your belt as you build your formulas!

Adden-dumb

The more I thought about this way of doing things, the more I liked it. I like the math-iness and getting to talk about binary. But then it hit me, you can do this with plain old letters too, and with better readability! A word of warning, the reason binary works so well is that it has a clearly defined difference of values - when we go to use letters, we'll have to do the same! Basically, all we really have to do is build our map with single letters (which might be the first letter of the test case or some other more meaningful signifier), and then check our match from there. So to rewrite the formula above:

CASE(
IF(History_Check_NSOPW_Missing__c, '', 'n')
+ IF(History_Check_Service_State_Missing__c, '', 's')
+ IF(History_Check_Residence_State_Missing__c, '', 'r')
+ IF(History_Check_FBI_Missing__c, '', 'f')
+ IF(History_Check_Acknowledgement_Missing__c, '', 'a')
+ IF(Agreement__r.State_Policy_Accompaniment_Allowed__c, 'p', '')
+ IF(AND(History_Check_Is_Minor__c, Agreement__r.State_Policy_Minors_Disallowed__c), '', 'm')
,
    'np', "Accompaniment",
    'nsr', "Non-Accompaniment (State Cleared)",
    'nfb', "Non-Accompaniment (FBI Cleared)",
    'nfbp', "Non-Accompaniment (FBI Cleared)",
    'nfbpm', "Non-Accompaniment (FBI Cleared)",
    'nsrf', "Acknowledgement",
    'nsrfpm', "Acknowledgement",
    'nsrfa', "Training and Service",
    'nsrfapm', "Training and Service",
"None")

As long as you use each letter only once in your map, and you make sure you put them in order in your matching step, you can use letters and tell right away what a given state covers, and what it doesn't. I'll even admit, when I did this I realized there are a few more cases I need to cover, because some letters were missing.

So, if you like math, the Power of Two is here to help - but if you like letters, you can use that Power, Too.

WordCamp 2019 - Stop Guess: Diagnosing and Fixing WP Performance

https://bit.ly/wcbos19

Why performance matters

"2.7 seconds for load time" - maybe

  • conversions
  • wp admin being slow
  • customer perception
  • Google rank
  • satisfaction
  • speed == happiness
  • habituation

Measure

Rough ranges with page caching turned off:

  • very fast 50
  • fast 150
  • good 300-500 <-- and up should be
  • average 500
  • slow 700
  • very slow 1.5

page perf extension in Chrome will show a summary of TTFB etc
https://webpagetest.org

WordCamp 2019 - Purpose Driven Sites

data-focused or audience focused

user personas

  • mayor
    • goals - needs new city site
    • target audience members of the community, visitors
    • fun fact
  • CMO at SAAS
    • needs to update site
    • target audience

discovery phase - audience-focused

Current site

  • how often do you use the site
  • which device do you visit on
  • what is your main reason for visiting
  • what do you have trouble finding?
  • rate current site 1-10

Wish list

  • what must haves?
  • top 3 things accessible right away
  • other sites we can use for inspiration

doing actual user surveys, limited to most critical answers
needs good cross section
opt-in to phone interview at the end of the survey

Focus groups

have them do common tasks together
can do brainstorming too

do we have a process map that mirrors this for our projects??? #todo because we don't

discovery phase - data-focused

  • analyze current site
  • who what where when why of visitors
  • detail of when
    • lots of new users is good for leads, tracking what pulls them in
    • returners show content drive
  • session monitoring
    • actually records their actions and movements -- could be great for JStats
    • inspectlet and hotjar are two services -- https://www.hotjar.com/pricing basic/2k per day is free, do that
  • A/B testing to look at data outcomes
    • Google Analytics Experiments can do some things
    • other sources, probably some WP plugins -- how would that work with static sites? data driven cycle v the above audience driven cycle

Output

Audience:

Features:

quotes from feedback

WordCamp 2019 - WP Headless and Gatsby

http://rebrand.ly/wp-gatsby-guide

Why headless

  • Security
    • static files
    • no data
    • separate concerns
  • Performance
    • cdn integration
    • scalability
    • market is improving
  • Cost
    • lean infrastructure
    • free is possible
    • flexible

pre-req

Chrome, NodeJS, NPM, GIT, (?NVM)

WordPress

using Gutenberg

GatsbyJS

Gatsby is a React-based, GraphQL powered, static site generator

Cmds

for Vue instead

https://dev.to/mittalyashu/gatsby-for-react-and-gridsome-for-vuejs-281c
or https://gridsome.org/ for Vue
Nuxt is the thing -- https://nuxtjs.org/

GraphQL

handled by Gatsby from REST API

ReactJS

Build

gatsby build will save down all content to rendered files in /public
good question about previewing content since it will be distinct front end - gutenberg helps a lot with that, basically the same as how we are using VisualCustom whatever

npm website scraper to grab static
serverless js - https://serverless.com/framework/
https://github.com/jdub233/PAGE-CAPTURE-S3 LAMBDA TO CAPTURE

WordCamp 2019 - Accessibility Made Easy pt2, Auditing

people with disabilities for over $2 trillion in income globally
WP runs over 1/3 of the internet, so we can make a difference

  • Use a code validator, simple process
  • use automated testing tools
  • put mouse away and try it

  • Axe - will highlight in the view, you can click through it

  • Lighthouse - scores, offers 11 step process for manual testing, but lookout https://www.matuzo.at/blog/building-the-most-inaccessible-site-possible-with-a-perfect-lighthouse-score/

    • also gives you manual check items to follow up on
  • Wave primarily for Headers

    • screen readers really truly use headers, so having h2 after h3 causes it to go down then go back up
  • tota11y Audit plugin - from Khan

    • WAVE can also be a WP plugin, and they can be bundled together - #todo install these on Dev
    • not only highlights element issues, but also makes recommendations about everything, including color
    • has a screenreader wand and show what SR would actually say
  • Site Approve

Use built-in features of mobile devices to test
https://bit.ly/iosa11ytesting
https://bit.ly/androida11yscanner

Scott O'Hara SVG and Accessibility -- inline SVG use title to equivalent to alt-text, regular SVG in IMG tag just use alt-text

WordCamp 2019 - Accessibility Made Easy pt1

from St. Pete

  • we've attended the National For Blind the last few years and we were very moved at how difficult it is for low-vision people to use software
  • section 508

facts about 508 WCAG
11 easy steps
free developer tools
how to do basic site audit

Facts

  • 1 billion people worldwide have some disability
  • 253 million people have vision impairment
  • 19 million children are visually impaired
  • you will be disabled at some point in your life
  • 814 s508 lawsuits in 2017
  • 2258 s508 lawsuits in 2018
    • 4000 at state level in CA
  • less than 50 plaintiffs
    • majority filed under Title III of ADA

WCAG is the worldwide guidelines
s508 is the US law, and nonprofits are accountable to it

WCAG's Main Ideas

  • Perceivable
    • text alternative, media, adaptable, distinguishable
  • Operable
    • time limits, seizures, navigable without mouse
  • Understandable
    • readable, predictable
  • Robust
    • wide compatibility

(blank screen - this is why)

11 Steps to Accessibility

  1. Provide alt-text -- you can even set as decorative
  2. Label your form elements -- can use aria-label
  3. Add closed caption for video, audio description for animations -- training materials need the close captioning https://vimeo.zendesk.com/hc/en-us/articles/224968828-Captions-and-subtitles -- Amazoon has tools
  4. Be sure your contrast ratio meets minimum thresholds -- 4.5:1 for text, 3:1 for others
  5. Make all link text descriptive and underline them -- "click here" or "read more" VS 'folow this link to "Wikipedia"'
  6. The color of any given content cannot be the only indication of meaning -- "required fields are in red" VS required are in red and marked with * --
  7. Make sure your font size is at least 16px and is still properly spaced when enlarged to 200% -- plugin to see https://www.stpetedesign.com/ada-section-508-compliant/
  8. Error handling - 'Errors' vs 'Title is Required' and 'Last Name Required'
  9. Tab navigation - entire site should be keyboard navigable -- take your mouse and put it in the drawer majority of suits brought because couldn't interact with site functions -- make sure focus state CSS is clear
  10. You must offer flexible time limits (if any) associated with the website or software -- think about the time it takes for a screen reader to finish page
  11. provide skip navigation links so that users can skip repetitive content such as nav menus, widgets etc -- Mega Menus work terrible for screen readers -- primary content link only shows when tabbed into -- Add Skiplinks with wp-accessibility, skiplinks don't have to always be visible

Tools

  • Axe Chrome - less false positives
  • Wave.webaim.org - don't think it can do logged in views
  • Google Lighthouse - performance and accessibility
  • Contrast Ratio Checker - https://contrast-ratio.com
  • WP Accessibility Tools & Alt Text Finder - This plugin has a missing Alt text finder, the tota11y testing software from Khan University, a contrast checker and a WCAG 2.1 checklist
    • WP Media library won't update alt-text after file has been embedded in page
  • Userway.org - adds a widget on the frontend of your WP site that allows users to set Accessibility settings like contrast text size etc !!! get this #todo
  • Accessibility Statement Generator - from W3C, asks you questions to create your statement so that people having a problem know who to contact -- also good for legal coverage
  • NVDA - nvaccess.org, Windows only, made by blind people
  • ChromeVox - Classic Chrome extension
  • WP Guidelines - https://make.wordpress.org/accessibility
  • Actual Law - https://goo.gl/QWMzuo

No grace period for violations
Brick and mortar in CA - instant $4k fine even before lawsuit, now instant $4k for website too, then the lawsuit