PHPackages                             fivejars/openy\_traction\_rec - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. fivejars/openy\_traction\_rec

ActiveDrupal-module[API Development](/categories/api)

fivejars/openy\_traction\_rec
=============================

Provides integration with Traction Rec CRM for YMCA Website Services

1.0.4(2mo ago)12649[1 PRs](https://github.com/fivejars/openy_traction_rec/pulls)LGPL-3.0-or-laterPHPPHP ^7.4 || ^8

Since Jan 31Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/fivejars/openy_traction_rec)[ Packagist](https://packagist.org/packages/fivejars/openy_traction_rec)[ RSS](/packages/fivejars-openy-traction-rec/feed)WikiDiscussions main Synced 3d ago

READMEChangelog (10)Dependencies (16)Versions (24)Used By (0)

YMCA Website Services Traction Rec integration
==============================================

[](#ymca-website-services-traction-rec-integration)

This module provides YMCA Website Services integration with the [Traction Rec CRM](https://www.tractionrec.com).

- JWT OAuth flow is used for the integration: [OAuth 2.0 JWT Bearer Flow for Server-to-Server Integration](https://help.salesforce.com/articleView?id=remoteaccess_oauth_jwt_flow.htm&type=5)
- The [Drupal Key module](https://www.drupal.org/project/key) assists with key management for authentication.

Installation
------------

[](#installation)

Require this module:

```
composer require ycloudyusa/openy_traction_rec
```

Then enable the necessary modules and submodules:

```
drush en openy_traction_rec openy_traction_rec_import openy_tr_activity_finder
```

Usage
-----

[](#usage)

The main module itself provides only API that helps fetch data from TractionRec. More specific functionality is provided in submodules:

- `YMCA Website Services Traction Rec: Activity Finder` extends YMCA Website Services Activity Finder with the new fields and logic.
- `YMCA Website Services Traction Rec: PEF import` provides PEF migrations. See [modules/openy\_traction\_rec\_import/README.md](modules/openy_traction_rec_import/README.md) for details on how to import content once configuration is complete.
- `YMCA Website Services Traction Rec: SSO` provides the possibility to login user using SSO and manipulate menu items according to Traction Rec login status. See [modules/openy\_traction\_rec\_sso/README.md](modules/openy_traction_rec_sso/README.md)

Configuration
-------------

[](#configuration)

### Create a Connected App in Salesforce

[](#create-a-connected-app-in-salesforce)

1. Create a new private key and X509 certificate, customizing the `subj` options in the command to suit your organization. (See [the manual for openssl-req](https://www.openssl.org/docs/manmaster/man1/openssl-req.html) to understand the options here.) ```
    openssl req -x509 -noenc -sha256 -days 365 \
     -keyout traction_rec.key \
     -out traction_rec.crt \
     -subj "/C=US/ST=Illinois/L=Chicago/O=My YMCA/OU=Org/emailAddress=youremail@example.com"
    ```

    - The email address in the certificate does not need to match the email on the Connected App.
    - The certificate **must be renewed yearly** (or after the set number of `--days`). We recommend you set a reminder in order to prevent unwanted failures.
2. In **Salesforce** &gt; **Setup** &gt; **App Manager**, create a **New Connected App**.
    - Set a **Name** and **Email**.
        - The **Contact Email** is not used for authentication.
    - Check **Enable OAuth Settings**
        - Set the callback url as the base URL of your site
        - Check **Use digital signatures** and upload the X509 certificate (`.crt`) created above.
        - Ensure the app has the following **Selected OAuth Scopes**
            - Full access (full)
            - Manage user data via APIs (api)
            - Manage user data via Web browsers (web)
            - Perform requests at any time (refresh\_token, offline\_access)
        - Check these options:
            - **Require Proof Key for Code Exchange (PKCE) Extension for Supported Authorization Flows**
            - **Issue JSON Web Token (JWT)-based access tokens for named users**
        - Uncheck all other options in the **OAuth** section.
    - **Save** the Connected App
3. Once the app is saved, you will need to get the **Consumer Details**:
    - In the "My Connected App" screen that appears once you save (or via **Setup** &gt; **App Manager**), click **Manage Consumer Details**.
    - Save the **Consumer Key** and **Consumer Secret** for the next step.
4. Create a **Profile** OR **Permission Set** to assign permissions to your app. We recommend using a **Permission Set** as those are the option recommended by Salesforce.
    1. Your Traction Rec support team should be able to deploy the `Traction Rec Activity Finder Permission Set` from their `dev1` instance. If this Permission Set is deployed, proceed straight to the **User** creation step. To create a **Permission Set** from scratch:

    - **Setup** &gt; **Users** &gt; **Permission Sets** &gt; **New**
    - Fill in the **Label** as you wish, and leave **License** as `--None--`
    - In the new **Permission Set**, open **Object Settings**.
    - In the very long list of **Object Settings**, do the following for each of the 10 [Objects listed below](#salesforce-permissions):
        - Find the object and click to open it. In the configuration screen for each Object:
            - Under **Object Permissions**, mark **Read** as **Enabled**.
            - Under **Field Permissions**, mark **Read Access** on the header field to provide access to all fields.
        - Save the **Object Settings** and search for the next one.
    - Finally, review the summary of access permissions and ensure **Read** access is provided for each of the necessary objects.

    2. Create a **Profile**:
        - You **must** do this **before** creating a user.
        - **Setup** &gt; **Users** &gt; **Profiles** &gt; **New**
        - When asked what **Existing Profile** to clone from, select **Standard User** or **Standard Platform User**. Be sure to note the **User License** connected to the target profile.
        - In the *very large* configuration screen, click **Edit**, then:
            - Under **Connected App Access**, add access to the Connected App you created above.
            - Search for and enable the [System permissions listed below](#salesforce-permissions).
            - Under **Custom Object Permissions**, add **Read** access to the [Objects listed below](#salesforce-permissions).
        - Save those changes.
5. Create a new **User** with the new Profile or Permission Set:

- **Setup** &gt; **Users** &gt; **New User**
    - **User License** - The option under which you created the Profile in the previous step, or **Salesforce**.
    - **Email** - A working email that you will use to receive login verifications.
    - **Username** - This is *not* your email and *must* be unique across *all Salesforce Organizations*. **This is the name that will be used in the Drupal connection below.** If you enter a preexisting username, you will receive this error: > Error: Duplicate Username. The username already exists in this or another Salesforce organization. Usernames must be unique across all Salesforce organizations. To resolve, use a different username (it doesn't need to match the user's email address).
    - Assign the User to the **Profile** you created above, or a **Permission Set** that has the [necessary permissions](#salesforce-permissions).
        - Under **Permission Set Assignments**, click **Edit Assignments**
        - Find the **Permission Set** you created in the prior step, select it, click **Add**, then **Save**.

6. Confirm your **Connected App**, **Profile**, and **User** are connected:
    - Go to **Setup** &gt; **Apps** &gt; **Connected Apps** &gt; **Manage Connected Apps** and choose your new app. Assign the **Profile** or **Permission Set** that contains your new user if it does not already show under the relevant section.
        - Click **Manage Profiles** or **Manage Permission Sets**
        - Search for your Profile or Permission Set and Save.
    - In the Connect App Detail, click **Edit Policies**:
        - Under **OAuth Policies** &gt; **Permitted Users** choose **Admin approved users are pre-authorized**.
        - Check **Issue JSON Web Token (JWT)-based access tokens**.
        - **Save** the Connected App details.

> When the process is complete, you should have the following relationships between the **User**, **Permission Set** OR **Profile**, and **Connected App**:
>
> - the API **User** should be assigned the **Permission Set** OR **Profile**.
> - the **Connected App** should be assigned the same **Permission Set** OR **Profile**.

Review all of these steps carefully. Missing any of them can result in an inability to query the API.

#### Salesforce permissions

[](#salesforce-permissions)

The Salesforce integration **Permission Set** OR **Profile** should have read access to all fields in the following objects:

- Course Options
- Courses
- Course Session Options
- Course Sessions
- Locations
- Products and Discounts
- Program Categories
- Program Category Tags
- Programs
- Sessions

If using a **Profile**, it should also have the following Systems Permissions:

- Apex REST Services
- View Restriction and Scoping Rules
- Update Consent Preferences Using REST API

### Configure the connection in Drupal

[](#configure-the-connection-in-drupal)

1. Go to **Admin** &gt; **Configuration** &gt; **System** &gt; **Keys** (`/admin/config/system/keys`) and create a new key to store the private key created above.
    - **Add key**
    - Add a **Key name** and **Description**
    - Choose **Key Type**: "TractionRec JWT Private Key"
    - Choose the **Key provider** depending on your configuration. See [Managing Keys](https://www.drupal.org/project/key#:~:text=the%20encrypt%20module-,Managing%20keys,-Key%20provides%20an) for details.
    - Configure the chosen provider then **Save** the key.
2. Go to **Admin** &gt; **YMCA Website Services** &gt; **Integrations** &gt; **Traction Rec** &gt; **Auth settings** (`/admin/openy/integrations/traction-rec/auth`) to configure the keys &amp; secrets provided by Traction Rec.
    - Add the **Consumer key** and **Consumer Secret** from **Manage Consumer Details** in Salesforce.
    - Add the **User** connected to the Connected App.
        - This is the **Username** of the **User**, not the **Contact email**.
    - Enter a **Login URL**.
        - This will most likely be `https://login.salesforce.com`
    - Set the **Services base URL** and **REST API Base URL** as per their descriptions.
        - **Ensure the REST API Base URL responds to `curl -I` with a `200` response**. Replace URLs like `*.lightning.force.com` with `*.my.salesforce.com` because the `lightning` url may result in a redirect, which will cause an authentication error, like (`[@"message":"Session expired or invalid","errorCode":"INVALID_SESSION_ID"]`).
    - Set the **Community URL** based on the publicly accessible registration links.
        - This may be something like `https://my-ymca.my.site.com`
        - The URL can be found in Salesforce under **Setup** &gt; **Digital Experiences** &gt; **All Sites**.
    - Choose the key as configured above.

Mapping
-------

[](#mapping)

The TractionRec importer pulls data from [many Traction Rec Objects](#salesforce-permissions) (see [TractionRec.php for the full queries](https://github.com/YCloudYUSA/openy_traction_rec/blob/main/src/TractionRec.php)):

### Object Mapping

[](#object-mapping)

The fetcher outputs these files:

- `classes.json` - from Courses
    - Maps to both Activities and Classes. Since TREC does not have this distinction, information in the resulting Activities and Classes in Drupal is duplicated.
- `locations.json` - from Locations
    - This file is unused, but Locations map to Location via the Session import.
- `program_categories.json` - from Program Category Tags
    - Maps to Program.
- `programs.json` - from Programs
    - Maps to Program Subcategory.
- `sessions.json` - from Course Options
    - Maps to Session.

**Note:** Traction Rec's labels for "Programs" and their child groupings are different:

- Traction Rec: "Program Category" is the parent of "Program".
- Drupal: "Program" is the parent of "Program Subcategory".

### Mapping to Drupal fields

[](#mapping-to-drupal-fields)

Those files are then imported into Drupal content via [importers](https://github.com/YCloudYUSA/openy_traction_rec/tree/main/modules/openy_traction_rec_import/config/install) (in config items that start with `migrate_plus.`). The import goes as follows:

> - Drupal Content Type (bundle)
>     - `Salesforce/TractionRec source field` → `Drupal destination field`

- **Program** - from `programs.json` / TREC Program Categories
    - Id → id
    - Name → Title
    - Available → Published (`status`)
- **Program Subcategory** - from `program_categories.json`/ TREC Programs
    - Id → id
    - Name → Title
    - Program → Program (`field_category_program`) via a lookup to the Programs import
    - Available → Published (`status`)
- **Activity** - from `classes.json` / TREC Courses
    - Id → id
    - Name → Title
    - Program/Id → Program Subcategory (`field_activity_category`) via a lookup to the Program Subcategory import
    - Available → Published (`status`)
- **Class** - from `classes.json` / TREC Courses
    - Id → id
        - The Class Id will also be used to set the Activity (`field_class_activity`)
    - Name → Title
    - Program/Id → *ignored*
    - Description/Rich Description → Description (`field_class_description`)
        - If a Rich Description is set, it will be used, otherwise the Description field will be used.
    - Available → Published (`status`)
- **Session** - from `sessions.json` / TREC Sessions
    - Course\_Option/Name → Title
    - Course\_Option/ID → id
        - Also used to generate the Registration link URL using the Community URL set in Traction Rec auth settings (`/admin/openy/integrations/traction-rec/auth`).
    - Course\_Session/Course/Id → Class
    - Course\_Session/Course/Name → Course
    - Course\_Session/Course/Description &amp; Rich\_Description → Description (`field_class_description`)
        - If a Rich Description is set, it will be used, otherwise the Description field will be used.
    - Course\_Option/Start\_Date → Session Time &gt; Start date
    - Course\_Option/Start\_Time → Session Time &gt; Start time
    - Course\_Option/End\_Date → Session Time &gt; End date
    - Course\_Option/End\_Time → Session Time &gt; End time
    - Course\_Option/Day\_of\_Week → Session Time &gt; Days
    - Course\_Option/Age\_Min → Min Age (`field_session_min_age`) converted to months
    - Course\_Option/Age\_Max → Max Age (`field_session_max_age`) converted to months
    - Course\_Option/Location/Name → Location (`field_session_location`)
        - Location Name is used as a backup in case the Location Mapping does not match.
    - Course\_Option/Location/Id → Location (`field_session_location`)
        - Location ID is used to attempt to match a location in the Location mapping in the Traction Rec importer settings (`/admin/openy/integrations/traction-rec/importer`)
    - Course\_Option/Instructor → Instructor (`field_session_instructor`) trimmed to 255 characters
    - Course\_Option/Available\_Online → Online registration (`field_session_online`)
    - Course\_Option/Available → Published (`status`)
    - Course\_Option/Register\_Online\_From\_Date → not used
    - Course\_Option/Register\_Online\_To\_Date → not used
    - Course\_Option/Capacity → Initial Availability (`field_availability`)
    - Course\_Option/Total\_Capacity\_Available → Initial Availability (`field_availability`)
    - Course\_Option/Unlimited\_Capacity → if set, overrides Capacity and sets Initial Availability (`field_availability`) to 100
    - Course\_Option/Unlimited\_Waitlist\_Capacity → Wait list Unlimited Capacity (`waitlist_unlimited_capacity`)
    - Course\_Option/Waitlist\_Total → Wait list capacity (`waitlist_capacity`)
    - Course\_Option/Product/Price\_Description → Price description (`field_price_description`)
    - Course\_Session/Id → Class (`field_session_class`) via a lookup to the Class import

Data Model
----------

[](#data-model)

This module assumes a Traction Rec "standard" data model in its queries. Any deviations from this model will require overriding the queries in `src/TractionRec.php`.

This model contains a subset of the fields in Traction Rec that are relevant to our usage. All entities have more fields than listed.

Field types are taken from Salesforce's **Setup** &gt; **Object Manager** &gt; **{Entity}** &gt; **Fields &amp; Relationships**.

- Number field options are: `number(length_decimal places)`

 ```
erDiagram
  Program_Category__c {
    id Id
    text(80) Name
  }
  Program__c {
    id Id
    text(80) Name
    checkbox Available__c
    textArea(255) Description__c
  }
  Program_Category_Tag__c {
    id Id
    autoNumber Name
    lookup(Program) Program__c
    lookup(Program_Category) Program_Category_c
  }
  Course__c {
    id Id
    text(80) Name
    checkbox Available__c
    text(128) Code__c
    longTextArea(640) Description__c
    lookup(Program) Program__c
    richTextArea Rich_Description__c
  }
  Course_Session__c {
    id Id
    text(80) Name
    checkbox Available__C
    text(128) Code__c
    lookup(Course) Course__c
    longTextArea(640) Description__c
    number(18_0) Num_Option_Entitlements__c
    lookup(ProductAndDiscount) Product__C
    richTextArea Rich_Description__c
    sum Total_Option_Capacity__c
    formula(number) Total_Option_Capacity_Remaining__C
    sum Total_Option_Registrants__c
    count Total_Options_Available__c
  }
  Course_Option__c {
    id Id
    text(80) Name
    number(3_1) Age_Max__c
    number(3_1) Age_Min__c
    checkbox Available__c
    number(18_0) Capacity__c
    picklist(multiSelect) Day_of_Week__c
    date End_Date__c
    text(8) End_Time__c
    text(128) Instructor__c
    lookup(ProductAndDiscount) Product__c
    number(18_0) Registration_Total_c
    longTextArea(3500) Setup_Notes__c
    number(3_0) Setup_Time_Required___c
    date Start_Date__c
    text(8) Start_Time__c
    longTextArea(3500) Tear_Down_Notes__c
    number(3_0) Tear_Down_Time_Required__C
  }
  Course_Session_Option__c {
    id Id
    autoNumber Name
    lookup(CourseOption) Course_Option__c
    masterDetail(CourseSession) Course_Session__c
    checkbox Option_Available__c
    number(18_0) Option_Capacity__c
    number(18_0) Option_Registration_Total__c
  }
  Program_Category__c ||--|{ Program_Category_Tag__c : ""
  Program__c ||--|{ Program_Category_Tag__c : ""
  Program__c ||--|{ Course__c : ""
  Course__c ||--|{ Course_Session__c : ""
  Course_Session__c ||--|{ Course_Session_Option__c : ""
  Course_Option__c ||--|{ Course_Session_Option__c : ""
```

      Loading Using Google Tag Manager (GTM)
------------------------------

[](#using-google-tag-manager-gtm)

By [integrating Google Tag Manager (GTM) with your Salesforce Community](https://developer.salesforce.com/blogs/2019/04/google-tag-manager-for-community-cloud), you can enable your marketing team to manage the deployment of marketing tags and tracking pixels, without relying on a developer to modify any code.

You may also want to [configure cross-domain tracking](https://ds-docs.y.org/docs/howto/track-users/#cross-domain-tracking) on your Drupal site.

###  Health Score

48

—

FairBetter than 94% of packages

Maintenance87

Actively maintained with recent releases

Popularity19

Limited adoption so far

Community18

Small or concentrated contributor base

Maturity59

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 55.4% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~166 days

Recently: every ~79 days

Total

10

Last Release

65d ago

PHP version history (2 changes)1.0.0-alpha.2PHP ^7.4 || ^8.0

1.0.0-beta.1PHP ^7.4 || ^8

### Community

Maintainers

![](https://www.gravatar.com/avatar/faaae86b867666363d1191e5c83c9e89a01cddab139b26fd9b843efe4bab07b6?d=identicon)[fivejars](/maintainers/fivejars)

![](https://www.gravatar.com/avatar/043af45323fb742da420c4e2853b6b36970a2678a7d38b3975516c5b622a0f5a?d=identicon)[AndreyMaximov](/maintainers/AndreyMaximov)

---

Top Contributors

[![dchubar-fj](https://avatars.githubusercontent.com/u/58946680?v=4)](https://github.com/dchubar-fj "dchubar-fj (149 commits)")[![manachynskyi](https://avatars.githubusercontent.com/u/4816431?v=4)](https://github.com/manachynskyi "manachynskyi (36 commits)")[![froboy](https://avatars.githubusercontent.com/u/238201?v=4)](https://github.com/froboy "froboy (31 commits)")[![OleksandrRiumshyn](https://avatars.githubusercontent.com/u/152503916?v=4)](https://github.com/OleksandrRiumshyn "OleksandrRiumshyn (26 commits)")[![podarok](https://avatars.githubusercontent.com/u/563412?v=4)](https://github.com/podarok "podarok (12 commits)")[![anpolimus](https://avatars.githubusercontent.com/u/3023950?v=4)](https://github.com/anpolimus "anpolimus (8 commits)")[![aleevas](https://avatars.githubusercontent.com/u/744406?v=4)](https://github.com/aleevas "aleevas (7 commits)")

### Embed Badge

![Health badge](/badges/fivejars-openy-traction-rec/health.svg)

```
[![Health](https://phpackages.com/badges/fivejars-openy-traction-rec/health.svg)](https://phpackages.com/packages/fivejars-openy-traction-rec)
```

###  Alternatives

[docusign/esign-client

The Docusign PHP library makes integrating Docusign into your apps and websites a super fast and painless process. The library is open sourced on GitHub, look for the docusign-esign-php-client repository. Join the eSign revolution!

2087.4M13](/packages/docusign-esign-client)[farmos/farmos

A web-based farm record keeping application.

1.2k6.7k1](/packages/farmos-farmos)[get-stream/stream

A PHP client for Stream (https://getstream.io)

1451.3M8](/packages/get-stream-stream)[agence104/livekit-server-sdk

Server-side SDK for LiveKit.

79189.9k1](/packages/agence104-livekit-server-sdk)[packbackbooks/lti-1p3-tool

A library used for building IMS-certified LTI 1.3 tool providers in PHP.

51438.3k2](/packages/packbackbooks-lti-1p3-tool)[celtic/lti

PHP class library for building LTI integrations

57279.3k7](/packages/celtic-lti)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
