Create a Things To-Do from MS Outlook with a Link Back to the Email

Posted 06/27/2019 10:30 AM by Corey Klass

I'm a big fan of using Things by Cultured Code as my To-Do manager, as it's very easy to use and allows for the quick creation and management of my To-Dos.

Since I work almost exclusively off my email in Microsoft Outlook at work, I've created an AppleScript that I've assigned to a hotkey in BetterTouchTool that will automatically generate a new Things To-Do from the selected email.

Once my To-Do is complete, however, it's annoying to go back into MS Outlook to find the email that I was referencing, so that I can respond to it.

To solve this problem, I've added a link to each of these To Do's with a custom URL handler that directs me back to the MS Outlook email that I created the To Do from. The email link that is created in the To Do looks like this

outlookemailopener://2019-05-13%2003:07:17%20+0000/My%20Email%20Subject/ck@malcontentboffin.com

I've written a separate AppleScript, that I've saved as an App and modified the info.plist file to register the outlookemailopener handler as the opener for this AppleScript.

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>Outlook Email Opener</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>outlookemailopener</string>
        </array>
    </dict>
</array>

The outlookemailopener AppleScript uses a Spotlight query to search for the email file on your Mac and open it in MS Outlook. Because it uses a combination of the email date, subject, and sender, rather than a randomly-generated internal identifier, this script will work across all of your Macs (because each copy of MS Outlook assigns an email a randomly generated identifier).

set searchQuery to "(com_microsoft_outlook_messageSent == $time.iso(" & theDate & ")) && (com_microsoft_outlook_author_email_addresses == \"*" & theEmail & "*\") && (kMDItemTitle == \"" & theSubject & "*\")"

set searchCmd to "mdfind '" & searchQuery & "'"

You can find the link to two AppleScripts here.

Bind "Create Things To Do.scpt" to a hotkey that you can press while in MS Outlook to create a Things To-Do.

"Open Outlook Email.app" should be picked up by LaunchServices and register the outlookemailopener URL handler.

TypeScript Function Validates a Phone Number

Posted 05/04/2019 10:02 AM by Corey Klass

Here is a TypeScript function that you can use to validate telephone numbers in Angular:

// validates a phone number
static phoneValidate(textToValidate: string, maxDigits?: number): boolean {
  if (!maxDigits) {
    maxDigits = 20;
  }

  let validFlag = true;


  // test for invalid characters
  if (validFlag) {
    const validCharacters = /[0-9\(\)\ \-\.]/g;
    const invalidText = textToValidate.replace(validCharacters, '');

    if (invalidText !== '') {
      validFlag = false;
    }
  }


  // test for a valid phone number
  if (validFlag) {
    const nonNumericCharacters = /[^0-9]]/g;
    let rawText = textToValidate.replace(nonNumericCharacters, '');

    // if the first character is a 1
    if (rawText.substr(0, 1) === '1') {
      rawText = rawText.substr(1, rawText.length - 1);
    }

    if (rawText.length < 10) {
      validFlag = false;

    } else if (rawText.length > maxDigits) {
      validFlag = false;
    }
  }


  return validFlag;
}

Below is a description of what's going on.

static phoneValidate(textToValidate: string, maxDigits?: number): boolean {
  if (!maxDigits) {
    maxDigits = 20;
  }

The default maximum number of digits has been set to 20 here, but you can supply an alternate number of digits for specific scenarios.

  let validFlag = true;

Default that the phone number is true

    const validCharacters = /[0-9\(\)\ \-\.]/g;
    const invalidText = textToValidate.replace(validCharacters, '');

    if (invalidText !== '') {
      validFlag = false;
    }

The validCharacters regular expression defines the following characters as valid: The digits 0-9, an open parenthesis "(", a closed parenthesis ")", a space, a dash, and a period. It then replaces the valid characters with empty strings, leaving only potentially invalid characters. If the invalid character string is anything other than empty, the phone number string is not valid.

  // test for a valid phone number
  if (validFlag) {
    const nonNumericCharacters = /[^0-9]/g;
    let rawText = textToValidate.replace(nonNumericCharacters, '');

If the phone number is valid up to this point, remove any non-numeric characters from the input string.

    // if the first character is a 1
    if (rawText.substr(0, 1) === '1') {
      rawText = rawText.substr(1, rawText.length - 1);
    }

In the US, a "1" at the beginning of the phone number is a special digit, so remove it. You may need to customize this based on your country.

    if (rawText.length < 10) {
      validFlag = false;

In the US, telephone numbers are at least 10 digits long. If the numeric string is less than 10 characters, it is not valid.

    } else if (rawText.length > maxDigits) {
      validFlag = false;
    }

If the numeric string is longer than the maximum digits allowed, the telephone number is not valid.

TypeScript Function Validates an Email Address

Posted 05/03/2019 3:26 PM by Corey Klass

Validating email addresses on the client side in Angular can be done using a regular expression (though complicated as it may be). Below you can find a TypeScript function that will validate the input as an email address.

// validates an email address
static emailValidate(textToValidate: string): boolean {
  const email = textToValidate.toLowerCase();
  const regex = /(?:[a-z0-9!#\u0024%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#\u0024%&'*+\/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/;

  const emailIndex = email.search(regex);

  const validFlag = (emailIndex === 0);

  return validFlag;
}

Auto-Refreshing Angular JSON Web Tokens (JWT) w/ HttpClient

Posted 05/02/2019 7:52 AM by Corey Klass

The applications that I've implemented in Angular 4+ with HttpClient from @angular/common/http utilize JSON Web Tokens (JWT) for authentication from our server, which are then stored in the Angular app and passed in an Authentication header to the web server for any API calls.

This works well until the JWT expires, in my case after two hours, at which point any HTTP requests start failing because of the expired token.

Rather than having to implement JWT refresh code in every HTTP call, I've instead written a drop-in replacement service for HttpClient that performs the refresh authentication automatically. You call the .get() and .post() methods as you would normally for HttpClient.

The full code can be found here, with a breakdown of what's going on below:

import {Injectable} from '@angular/core';
import {HttpClient, HttpErrorResponse} from '@angular/common/http';
import {Observable, Subscriber} from 'rxjs';


@Injectable()

export class CkHttpClientService {

  // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  // Populate your own Refresh token value here
  // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  refreshTokenValue: string = null;



  // constructor
  constructor(private httpClient: HttpClient) { }



  // is the specified error an auth error?
  private _isAuthError(error: HttpErrorResponse): boolean {
    // default that it's not?
    let authError = false;

    // if an error was included
    if (error) {
      // check the statuses for an auth error
      authError = authError || (error.status === 401);
      authError = authError || (error.status === 403);
    }

    return authError;
  }




  // refreshes the login token
  private _refreshToken(): Observable<boolean> {
    const observable = Observable.create((subscriber: Subscriber<boolean>) => {

      // if there is no refresh token value
      if ((this.refreshTokenValue || '') === '') {
        subscriber.next(false);
        subscriber.complete();

      // if there is a refresh token value
      } else {
        // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        // Your Login Token refresh code goes here
        // - Make sure to store the new refresh token for later use
        // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        this.refreshTokenValue = '';

        subscriber.next(true);
        subscriber.complete();
      }
    });

    return observable;
  }






  // performs a GET operation
  get(url: string, options?: any): Observable<any> {
    return this._http('get', url, null, options);
  }



  // performs a POST operation
  post(url: string, payload: any, options?: any): Observable<any> {
    return this._http('post', url, payload, options);
  }


  // returns the appropriate HttpClient Observable
  private _httpClient(method: string, url: string, payload: any, options: any): Observable<any> {
    // where we're storing our HTTP Client observable
    let httpClientObservable: Observable<any> = null;

    // if this is a POST
    if (method.toLowerCase() === 'post') {
      httpClientObservable = this.httpClient.post(url, payload, options);

      // default to a GET
    } else {
      httpClientObservable = this.httpClient.get(url, options);
    }

    return httpClientObservable;
  }



  // generic HTTP caller
  private _http(method: string, url: string, payload: any, options: any): Observable<any> {
    const observable = Observable.create((subscriber: Subscriber<any>) => {

      // execute the HTTP observable
      this._httpClient(method, url, payload, options).subscribe((result: any) => {
        // on success
        subscriber.next(result);
        subscriber.complete();

      // on error
      }, (error: HttpErrorResponse) => {
        // is this an auth error?
        if (this._isAuthError(error)) {

          // call the refresh token function
          this._refreshToken().subscribe((refreshSuccessFlag: boolean) => {

            // if the refresh was successful
            if (refreshSuccessFlag) {

              // re-attempt the observable
              this._httpClient(method, url, payload, options).subscribe((retryResult: any) => {
                // on success, send the result
                subscriber.next(retryResult);
                subscriber.complete();

              // on error
              }, (retryError: HttpErrorResponse) => {
                subscriber.error(retryError);
                subscriber.complete();
              });

            // if the refresh failed
            } else {
              subscriber.error(error);
              subscriber.complete();
            }
          });

        // if this is not an auth error
        } else {
          subscriber.error(error);
          subscriber.complete();
        }
      });
    });

    return observable;
  }



}

Here is an explanation of what's going on.

refreshTokenValue: string = null;

This is a string variable that stores the login refresh token, and will be used to refresh the token when it expires. You should populate this when the user initially logs in, or you can replace it with a function or getter that refers to another service.

constructor(private httpClient: HttpClient) { }

Here we're injecting httpClient from @angular/common/http.

private _isAuthError(error: HttpErrorResponse): boolean {
  // default that it's not?
  let authError = false;

  // if an error was included
  if (error) {
    // check the statuses for an auth error
    authError = authError || (error.status === 401);
    authError = authError || (error.status === 403);
  }

  return authError;
}

This function determines if the HTTP error is an authentication error. We're using the HTTP responses statuses of 401 (Unauthorized) and 403 (Forbidden). We first check that error is a defined object.

private _refreshToken(): Observable<boolean> {
  const observable = Observable.create((subscriber: Subscriber<boolean>) => {

    // if there is no refresh token value
    if ((this.refreshTokenValue || '') === '') {
      subscriber.next(false);
      subscriber.complete();

    // if there is a refresh token value
    } else {
      // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      // Your Login Token refresh code goes here
      // - Make sure to store the new refresh token for later use
      // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      this.refreshTokenValue = '';

      subscriber.next(true);
      subscriber.complete();
    }
  });

  return observable;
}

If there is no refresh token stored, the function returns false by default. You'll need to swap out the dummy refresh token code here with your own call to refresh the login token.

get(url: string, options?: any): Observable<any> {
  return this._http('get', url, null, options);
}

post(url: string, payload: any, options?: any): Observable<any> {
  return this._http('post', url, payload, options);
}

These functions mimic HttpClient from @angular/common/http, but internally refer to a common, private _http function, since the code is mostly identical. I haven't implemented OPTIONS or PUT or any other methods, but you should be able to yourself.

private _httpClient(method: string, url: string, payload: any, options: any): Observable<any> {
  // where we're storing our HTTP Client observable
  let httpClientObservable: Observable<any> = null;

  // if this is a POST
  if (method.toLowerCase() === 'post') {
    httpClientObservable = this.httpClient.post(url, payload, options);

  // default to a GET
  } else {
    httpClientObservable = this.httpClient.get(url, options);
  }

  return httpClientObservable;
}

This function returns the appropriate GET or POST Observable, based on the method value passed to it. We'll be using this function multiple times in _http().

private _http(method: string, url: string, payload: any, options: any): Observable<any> {
  const observable = Observable.create((subscriber: Subscriber<any>) => {

The bulk of the heavy lifting is done in this function, performing the initial HTTP request, refreshing the token if we receive an authentication error, and performing the subsequent HTTP request after the token is refreshed.

    this._httpClient(method, url, payload, options).subscribe((result: any) => {
      // on success
      subscriber.next(result);
      subscriber.complete();

    // on error
    }, (error: HttpErrorResponse) => {
      // is this an auth error?
      if (this._isAuthError(error)) {

We first try performing the initial HTTP request. If it was successful, we pass through the result. Otherwise if it errors, we test to see if it was an authentication error.

        // call the refresh token function
        this._refreshToken().subscribe((refreshSuccessFlag: boolean) => {

If it was an authentication error, we call the _refreshToken() function to perform the refresh and return a success indicator.

          // if the refresh was successful
          if (refreshSuccessFlag) {

            // re-attempt the observable
            this._httpClient(method, url, payload, options).subscribe((retryResult: any) => {
              // on success, send the result
              subscriber.next(retryResult);
              subscriber.complete();

If the refresh was successful, we re-attempt the HTTP request that we performed earlier, passing through the result if it was successful.

            // on error
            }, (retryError: HttpErrorResponse) => {
              subscriber.error(retryError);
              subscriber.complete();
            });

If the HTTP request was unsuccessful, we pass through the error.

          // if the refresh failed
          } else {
            subscriber.error(error);
            subscriber.complete();
          }
        });

If the token refresh was not successful, pass through that error.

      // if this is not an auth error
      } else {
        subscriber.error(error);
        subscriber.complete();
      }

If the initial HTTP error wasn't an authentication error, we pass that error through.

Access the Angular-CLI Internal Web Server From Another Computer

Posted 10/29/2018 8:19 PM by Corey Klass

By default, the Angular-CLI internal web server only listens on the localhost network interface (127.0.0.1). If you want to access your development web application from another computer, you can start the internal web server with the following command:

ng serve --host 0.0.0.0 --disableHostCheck true

Groovy Deep Copy Objects

Posted 10/26/2018 2:33 PM by Corey Klass

There is no built-in functionality to perform a deep-copy of a Groovy object. Below is a convenience class that will perform a deep copy of an object.

import org.grails.web.json.JSONArray
import org.grails.web.json.JSONObject

class ObjectUtility {



    /**
     * Duplicates an object
     * @param object
     * @return
     */
    static Object duplicate(Object object) {
        def duplicateObject = null

        if (object instanceof JSONObject) {
            duplicateObject = ObjectUtility.duplicateJSONObject(object)

        } else if (object instanceof JSONArray) {
            duplicateObject = ObjectUtility.duplicateJSONArray(object)

        } else if (object instanceof Map) {
            duplicateObject = ObjectUtility.duplicateMap(object as Map<Object, Object>)

        } else if (object instanceof List) {
            duplicateObject = ObjectUtility.duplicateList(object as List<Object>)

        } else if (object instanceof Collection) {
            duplicateObject = ObjectUtility.duplicateCollection(object as Collection<Object>)

        } else if (object instanceof Set) {
            duplicateObject = ObjectUtility.duplicateSet(object as Set<Object>)

        } else {
            duplicateObject = object.getClass().newInstance(object)
        }

        return duplicateObject
    }



    /**
     * Duplicates a Map
     * @param map
     */
    private static Map<Object, Object> duplicateMap(Map<Object, Object> map) {
        def duplicateMap = (map.getClass().newInstance() as Map)

        map.each { Object key, Object value ->
            duplicateMap[key] = ObjectUtility.duplicate(value)
        }

        return duplicateMap
    }




    /**
     * Duplicates a JSONObject
     * @param map
     */
    private static JSONObject duplicateJSONObject(JSONObject jsonObject) {
        def duplicateObject = (jsonObject.getClass().newInstance() as JSONObject)

        jsonObject.each { Object key, Object value ->
            duplicateObject[key] = ObjectUtility.duplicate(value)
        }

        return duplicateObject
    }



    /**
     * Duplicates a List
     * @param list
     * @return
     */
    private static List<Object> duplicateList(List<Object> list) {
        def duplicateList = (list.getClass().newInstance() as List)

        list.each { Object indexItem ->
            duplicateList.push(ObjectUtility.duplicate(indexItem))
        }

        return duplicateList
    }




    /**
     * Duplicates a JSONArray
     * @param list
     * @return
     */
    private static JSONArray duplicateJSONArray(JSONArray array) {
        def duplicateArray = (array.getClass().newInstance() as JSONArray)

        array.each {Object indexItem ->
            duplicateArray.push(ObjectUtility.duplicate(indexItem))
        }

        return duplicateArray
    }




    /**
     * Duplicates a Collection
     * @param collection
     * @return
     */
    private static Collection<Object> duplicateCollection(Collection<Object> collection) {
        def duplicateCollection = (collection.getClass().newInstance() as Collection)

        collection.each { Object indexItem ->
            duplicateCollection.add(ObjectUtility.duplicate(indexItem))
        }

        return duplicateCollection
    }



    /**
     * Duplicates a Set
     * @param set
     * @return
     */
    private static Set<Object> duplicateSet(Set<Object> set) {
        def duplicateSet = (set.getClass().newInstance() as Set)

        set.each { Object indexItem ->
            duplicateSet.add(ObjectUtility.duplicate(set))
        }

        return duplicateSet
    }



}

Run the Angular-CLI Internal Web Server on an Alternate Port

Posted 10/20/2018 10:24 AM by Corey Klass

The Angular-CLI internal web server runs on port 4200 by default. To change the port number, launch it with the following command:

ng serve --port 8888

Path-less SASS Imports in your Angular App

Posted 10/14/2018 3:34 PM by Corey Klass

One of the nice features of SASS in your Angular-CLI app is that you can include an @import "variables"; command to include the SASS variable definitions from an external file. You can easily make your SASS import file globally available for @import statements, so that you only refer to it by filename, by modifying your /angular.json file in the following hierarchy: Root -> "projects" -> "my-project-name" -> "architect" -> "build" -> "options"

Under "options", create a new map entry called "stylePreprocessorOptions" if it does not exist. Within that, create an array entry called "includePaths". Within "includePaths", you can specify the relative path to your SASS file from the root of your project. For example, it may be located at "src/assets/sass".

Global CSS/SASS Includes in Angular-CLI

Posted 10/09/2018 1:34 PM by Corey Klass

You can globally include additional CSS/SASS style sheets into your Angular-CLI application by modifying your /angular.json file in the following hierarchy: Root -> "projects" -> "my-project-name" -> "architect" -> "build" -> "options"

Under "options", create a new array entry called "styles" if it does not exist. Within that, you can specify the relative path to your CSS/SASS file from the root of your project. For example, it may be located at "src/assets/sass".

NPM: Unexpected value 'undefined' exported by the module 'Module1'

Posted 10/02/2018 1:12 PM by Corey Klass

Barrels

It's considered good practice to separate Angular functionality into NPM modules for ease of re-use. The easiest way of importing modules is to import from the root of the module, and you accomplish this by creating a barrel.

In each of your module folders, create an index.ts file that looks like the following:

export * from './file1';
export * from './file2';
export * from './file3';

Then in the root of your module, add export statements for each of the index.ts files that you created above.

export * from './folder1/index';
export * from './folder2/index';

You are then able to import any file definition in your Angular application with a basic module reference:

import {File1} from '@coreyklass/module1';

Errors

Barrels are very useful, but they can result in compilation errors when building your Angular application for production.

The most common of these will be:

ERROR in : Unexpected value 'undefined' exported by the module 'Module1'

Solution

When building your module definition TS file, ensure that all of the import statements reference the actual file that the code is contained in, not the barrel file that you created above. You may not have noticed, but your IDE may have used the barrel for the automatic import statement.

Instead of this in your module definition file: import {File1} from './folder1';

Use this: import {File1} from './folder1/file1';

This should solve your compilation problem.