Skip to content
Permalink
master
Switch branches/tags
Go to file
Latest commit c48e64a Dec 3, 2018 History
Originally developed in private: https://github.com/eventespresso/event-espresso-private/pull/2

<!-- Thanks for your pull request.  Comments within these type of tags will be hidden and not show up when you submit your pull request -->
<!-- Please answer the following questions in order to expediate acceptance -->

## Problem this Pull Request solves ##

The outward effects we want are:
* events, venues and attendees with a password set are considered password-protected, meaning for REST API requests their excerpt and body are, by default, replaced with empty strings (when requesting using the default "caps context", which is "read", front-end reads, but not when using "admin_read" admin-end reads, which is what our mobile apps use)
* similarly, there are other fields that are password-protected (see each model's `EE_Password_Field` for the list). This means their true values get replaced with defaults (more notes on this later)
* similarly, by default, you cannot "read" data related to password-protected posts over the REST API unless you provide the password for the model they're related to (eg the following will exclude datetimes for password-proteted posts: `/wp-json/ee/v4.8.36/datetimes`, `wp-json/ee/v4.8.36/events/1/datetimes` and `wp-json/ee/v4.8.36/events?include=Datetime`)
* all REST API requests allow you to pass a `password` query parameter. If this does not match the password that protects that data (eg on events that's the password field, or datetimes its their event's password field, on tickets it's their datetime's event's password field), an error response is returned. If it does, you are permitted to see the password-protected data. Note, the following are valid requests: `wp-json/ee/v4.8.36/events?password=mypassword`, `/wp-json/ee/v4.8.36/datetimes?password=mypassword`, `wp-json/ee/v4.8.36/events/1/datetimes?password=mypassword` and `wp-json/ee/v4.8.36/events?include=Datetime&password=mypassword`. BUT if that password isn't correct any single item returned, the entire REST response will be replaced with an error messsage. So `wp-json/ee/v4.8.36/events?password=mypassword` will return an error if that is NOT the password for any of the events retrieved. This makes brute force attacks more difficult.
* full admin can use the `password` when querying, but no one else can. (Because otherwise you could sneakily do `wp-json/ee/v4.8.36/events?where[password]=foo`, which would return all events whose password was "foo", which I'm sure hackers would like to know.)
* REST API responses will only contain entities' passwords if the correct password was provided while querying, or when querying using a caps context other than `read` (front-end reads)
* REST API responses for entities with passwords (eg events, venues, attendees) have an extra property `_protected`. Here is its schema:
```
"_protected": {
                "description": "Array of property names whose values were replaced with their default (because they are related to a password-protected entity.)",
                "type": "array",
                "items": {
                    "description": "Each name corresponds to a property that is protected by password for this entity and has its default value returned in the response.",
                    "type": "string",
                    "readonly": true
                },
                "readonly": true
            },
```

and here is an example of it in a request for events:

```
"_protected": [
            "password",
            "EVT_desc",
            "EVT_short_desc",
            "EVT_display_desc",
            "EVT_display_ticket_selector",
            "EVT_visible_on",
            "EVT_additional_limit",
            "EVT_default_registration_status",
            "EVT_member_only",
            "EVT_phone",
            "EVT_allow_overflow",
            "EVT_timezone_string",
            "EVT_external_URL",
            "EVT_donations"
        ]
    },
```

That indicates each of those fields, which can be password protected, were replaced with their default value on this particular event. So the `EVT_desc` in this event entity is NOT its real value; whereas if it weren't mentioned in `_protected` it would be its real value.

* most calculated fields aren't accessible on password-protected entities. Eg, if event 123 has a password, doing `GET .../wp-json/ee/v4.8.36/events/123?calculate=optimum_sales_at_start, image_medium`, the property `optimum_sales_at_start` will be replaced with 0 (instead of its true value, which should be kept hidden for a protected event), but  `image_medium` will be visible as usual (WP core allows you to see featured media for password-protected posts). The `_calculated_fields` property also has a `_protected` property which will list which calculated fields were "protected" on the current request. Also, we indicate which calculated fields can be password-protected in the schema, by adding `protected: true` to the property. 


### Model-Level Changes
* add new class `EE_Password_Field`, which stores a list of the fields on the model to which it protects access
* this field is defined on events, venues and attendees. Other EE CPT models could define it too.
* you can now pass in a new boolean query parameter to models, `exclude_protected`. If set to true, model objects relating to models with a password set will be excluded. Eg `EEM_Datetime::instance()->get_all(array('exclude_protected' => true));` will return all datetimes for events with no password.
* in order to support that new query parameter, the property `EEM_Base::model_chain_to_password` was introduced. This indicates which related model, if any, has a password that protects access to this model's items. Eg on `EEM_Datetime` its just `Event`, meaning the event holds a password field, so that queries for datetimes that set `'exclude_protected' => true` should be excluded if their related event has a password set. On `EEM_Ticket` its `Datetime.Event`. This property doesn't need to be set for models which aren't publicly queryable (eg `EEM_Registration` isn't publicly queryable, so it doesn't need that property.)
* note: the REST API makes use of `exclude_protected` in order to hide datetimes and tickets for events with passwords

## Up for Debate or Improvement
* the WP API allows you to see a password-protected post's author and terms. Following that logic, in EE REST API, some entities for password-protected entities should be accessible. Eg you should be able to see a password protected event's WP user, and terms and taxonomies. However, each of those models are from WP core models, which we hoped to deprecate in the EE REST API anyway. 

 Do we have a preference? Should we do both approaches in both cases?

## How has this been tested
FYI This has been pretty extensively unit tested.
<!-- Please describe in detail how you tested your changes and how testing can be reproduced -->
<!-- Include details of your testing environment, and tests ran to see how your changes affect other areas of code -->
<!-- Include any notes about automated tests you've written for this pull request.  Pull requests with automated tests are preferred. -->
* [x] Create a password-protected event. Request it via the REST API (eg `/wp-json/ee/v4.8.36/events/{id}`). Its password, description, and excerpt should be blank. Also, the `_protected` property on that event should list all the fields which were replaced with their default values.
* [x] Try including related data (eg `/wp-json/ee/v4.8.36/events/{id}?include=Datetime`) the entity's `datetimes` property should be replaced with null, and `datetimes` should be in the `_protected` property, indicating it was protected.
* [x] Try fetching its related data (eg `/wp-json/ee/v4.8.36/events/{id}/datetimes`). You should get a 403
* [x] Use the mobile apps to check a registrant into an event. It should work exactly as before.

## Checklist

* [x] I have read the documentation relating to systems affected by this pull request, see https://github.com/eventespresso/event-espresso-core/tree/master/docs
* [x] User input is adequately validated and sanitized
* [x] all publicly displayed strings are internationalized (usually using `esc_html__()`, see https://codex.wordpress.org/I18n_for_WordPress_Developers)
* [x] My code is tested.
* [x] My code follows the Event Espresso code style.
* [x] My code has proper inline documentation.
* [x] My code accounts for when the site is Maintenance Mode (MM2 especially disallows usage of models) -> the EE REST API doesn't handle this super great but that's a long-standing issue
* [x] Maybe add some more unit tests
* [ ] Add documentation
3 contributors

Users who have contributed to this file

@sethshoultes @nerrad @joshfeck

EE4 REST API: An Introduction

The Event Espresso 4 (EE4) REST API in Core is intended to allow client-side applications, and apps on different servers, to be able to interact with the WordPress Plugin Event Espresso. It is included in Event Espresso since version 4.8.29, and is built on the WP REST API included in WordPress since version 4.4.

Other WordPress plugins that intend to use Event Espresso 4 data server-side (in the PHP code) generally do not need to use the API, and can instead use Event Espresso 4's database models, config, and other modules directly.

Example applications of the EE4 REST API could include:

  • JavaScript and HTML snippets that could be pasted onto non-WordPress sites that could list events from Event Espresso
  • mobile applications for signing attendees into events in Event Espresso
  • WordPress plugin that shows (and eventually controls) event data entirely client-side using javascript

If you'd like a more hands-on tutorial, checkout Building an EE4 Addon that uses Angular.js and the EE4 REST API.

Development

While developing with the EE4 REST API, it is recommended you define the following inside WordPress's wp-config.php file:

define('EE_REST_API_DEBUG_MODE',true);

This will help make sure you are made aware of any warnings or errors that happen, and it will also ensure that if you add any new endpoints, they will be made available immediately (otherwise we cache our registered routes, and only refresh the cache on updates or reactivations of EE).

Live sites, on the other hand, should set this to false, to improve speed, and avoid showing error messages to site visitors.

Authentication

Some of the EE4 REST API data is public and requires no authentication, but much of it is protected and requires authentication. Because the EE4 REST API is built on the WP REST API, the authentication process is identical: once you authenticate with the WP REST API, you are also authenticated with the EE4 REST API. So we suggest you read their documentation on authentication.

Currently, Event Espresso also comes bundled with a customized version of WP API Basic Auth, primarily used in our mobile apps. However, we do not guarantee we will continue to bundle it with Event Espresso, and so recommend 3rd party applications instead use other authentication methods.

We also have a tutorial on setting up a demo REST API client application to work with OAuth.

If your application needs to write or delete EE4 data, or if it needs to read data that's normally not publicly-available, you'll need to authenticate (read our google doc on permissions for more info).

Endpoint Discovery

Again, because the EE4 REST API is built on the WP REST API, discovering what URLs (endpoints) are available for sending requests to is quite simple. Please [read their documentation](http://v2.wp-api.org/guide/discovery/ on endpoint discovery.

Want to see what the current EE4 REST API looks like? Go ahead and send a request to https://demoee.org/wp-json and see for yourself.

Note: throughout the rest of this article you will see URIs to a particular server that's setup to use the EE4 REST API: demoee.org, where the WP REST API works at demoee.org/wp-json. This is just for ease of learning about the EE4 REST API, obviously your application will want to use data from your server. So for example, if your site's url is mygreatthing.com, the WP REST API would work at mygreatthing.com/wp-json.

Event Espresso Data in the WP API Index

The WP API index page contains all the EE4 routes, as well some meta information in the "ee" property. Currently it contains:

  • version: the version of the Event Espresso 4 plugin installed on this site
  • documentation_url: where to learn more about how to use the API
  • addons: a JSON object with properties for each EE4 addon currently active. Each addon contains
    • name: the system name for identifying the addon
    • version: the current version of the addon on this site
  • maintenance_mode: indicates whether Event Espresso is currently in maintenance mode. "0" means it is not in maintenance mode (so it can be used as normal), "1" indicates frontend-only maintenance mode (so the site should only be usable by site administrators), "2" indicates full maintenance mode (so the site should not be used, probably because the database needs to be migrated)
  • served_core_versions: indicates which EE4 API namespaces are available for use. Please see the section below on [#versioning-and-backwards-compatibility] for more info

Here is an example:


"ee": {
    "version": "4.9.0.rc.025",
    "documentation_url": "https://github.com/eventespresso/event-espresso-core/tree/master/docs/C--REST-API",
    "addons": {
      "Promotions": {
        "name": "Promotions",
        "version": "1.0.7.rc.000"
      },
      "Barcode_Scanner": {
        "name": "Barcode_Scanner",
        "version": "1.0.7.rc.000"
      }
    },
    "maintenance_mode": "0",
    "served_core_versions": [
      "4.8.29",
      "4.8.33",
      "4.8.34",
      "4.8.36"
    ]
  },
  

Site Info

This is an extra endpoint with site meta information. Currently it contains the following properties:

  • default_timezone: an object describing the site's default timezone, with 3 sub-properties:
    • pretty: a string which can be used for displaying the timezone to users. This is translated into the site's language. If the site is in a specific city's timezone then it will display the city; if the site is in a simple UTC offset timezone, then that offset will be shown. The format of this property may change without notice, so your code should not depend on this being in a specific format
    • string: this is the exact timezone string set in WordPress. It will be one of those listed on [http://php.net/manual/en/timezones.php], or it will be an empty string (eg if the site is set to a specific UTC offset)
    • offset: the number of seconds to add onto times returned by the EE4 REST API to get them into the site's default timezone. Note this value can be negative, and it may change depending on whether daylight savings time is being observed
  • default_currency: an object describing the site's default currency, with 8 sub-properties:
    • code: the currency code used for money amounts
    • name: a pretty, but not translated, name for the currency, in its singular form
    • name_plural: same as above, but plural
    • sign: the character representing the currency
    • sign_b4: 1 indicates the currency sign should be placed BEFORE the amount, 0 indicates it should be placed afterwards
    • dec_plc: the number of digits to appear after the decimal place when displaying to users
    • dec_mrk: the character to be used for the decimal mark when displaying to users
    • thsnds: the character to be used for separating thousands For example:
{
  "default_timezone": {
    "pretty": "North Dakota - Beulah",
    "string": "America/North_Dakota/Beulah",
    "offset": -18000
  },
  "default_currency": {
    "code": "USD",
    "name": "Dollar",
    "plural": "Dollars",
    "sign": "$",
    "sign_b4": 1,
    "dec_plc": 2,
    "dec_mrk": ".",
    "thsnds": ","
  }
}

This route is expected to contain more information in the future, as required.

Resources (Models)

The "resources" in the Event Espresso REST API are built around the EE4 models. In the REST APIs we talk about "resources", but in PHP code we talk about models, but they represent the same thing. Each resource/model has fields and relations to other resources.

Resources are the protoypes, and entities are the specific instances (just like models are the prototype, and model objects specific instances). Eg, "events" is a resource queryable on https://demoee.org/wp-json/ee/v4.8.29/events which returns specific "event" entities.

To see what entities exist, send a request to https://demoee.org/wp-json/ee/v4.8.29/resources, or just click this link to see the resources from one of our servers.

Here is an excerpt:

"Event": {
 "fields": {
 "EVT_ID": {
 "name": "EVT_ID",
 "nicename": "Post ID for Event",
 "has_rendered_format": false,
 "has_pretty_format": false,
 "type": "Primary_Key_Int_Field",
 "datatype": "Number",
 "nullable": false,
 "default": 0,
 "table_alias": "Event_CPT",
 "table_column": "ID"
 },
 "EVT_name": {
 "name": "EVT_name",
 "nicename": "Event Name",
 "has_rendered_format": false,
 "has_pretty_format": false,
 "type": "Plain_Text_Field",
 "datatype": "String",
 "nullable": false,
 "default": "",
 "table_alias": "Event_CPT",
 "table_column": "post_title"
 },
 "EVT_desc": {
 "name": "EVT_desc",
 "nicename": "Event Description",
 "has_rendered_format": true,
 "has_pretty_format": false,
 "type": "Post_Content_Field",
 "datatype": "String",
 "nullable": false,
 "default": "",
 "table_alias": "Event_CPT",
 "table_column": "post_content"
 },
//...many more entries in actual response

"relations": {
 "Registration": {
 "name": "Registration",
 "type": "Has_Many_Relation",
 "single": false
 },
 "Datetime": {
 "name": "Datetime",
 "type": "Has_Many_Relation",
 "single": false
 },
 "Question_Group": {
 "name": "Question_Group",
 "type": "HABTM_Relation",
 "single": false
 },
 "Venue": {
 "name": "Venue",
 "type": "HABTM_Relation",
 "single": false
 },
 "Term_Taxonomy": {
 "name": "Term_Taxonomy",
 "type": "HABTM_Relation",
 "single": false
 },
 "Message_Template_Group": {
 "name": "Message_Template_Group",
 "type": "HABTM_Relation",
 "single": false
 },
 "Attendee": {
 "name": "Attendee",
 "type": "HABTM_Relation",
 "single": false
 },
 "WP_User": {
 "name": "WP_User",
 "type": "Belongs_To_Relation",
 "single": true
 },

...many more entries in actual response

Notice the collection of "fields". Here we see there is a field named "EVT_ID" which is a number, "EVT_name" which is a string and "EVT_desc." We also notice that events "have many" datetimes (among other relations).

Versioning and Backwards Compatibility

Due to feedback from WordPress REST API developer Daniel Bachhuber, we have changed our versioning policy. (You can look at previous versions of this file in github to know what it was previously).

New features and backwards-compatible changes will be added to the latest namespaced version of the EE REST API (eg at the time of writing, the latest versioned namespace is v4.8.36). A new versioned namespace will be released when a non-backwards-compatible change is added.

An exception to this is bugfixes however, especially security fixes: those can be added to the API at any time, and it's possible that they might break backwards compatibility. Of course we will aim to have no bugs and no such changes.

For example, at the time of writing this article, the latest release of Event Espresso is 4.8.40.p, but the latest versioned namespace for the EE4 REST API is v4.8.36 (ie, to request the event list, you would send a request to mysite.com/wp-json/ee/v4.8.36/events, regardless of what the current version of Event Espresso is). However, if a backwards-incompatible change is going to be introduced, which isn't a bugfix, (eg we decide all dates should instead be unix timestamps instead of rfc3339 format, say in version 4.8.255), we would add a new versioned namespace (eg v4.8.255, so you could also send requests to mysite.com/wp-json/ee/4.8.255/events) which would include the latest change, and if possible, we would continue to support requests to the previous versioned namespaces (eg so you could continue to use v4.8.36 if you have code you'd rather not update right away to use the new endpoints).

Also note, only the most current endpoints are listed in the index. Eg, if you query a server running EE4.8.40, only wp-json/ee/v4.8.36/events will appear in the index, NOT wp-json/ee/v4.8.29/events or wp-json/ee/4.8.34/events, although those will continue to work. If you need specific endpoints from an older EE namespace to appear, append "force_show_ee_namespace={namespace}" to show it.

For example, if you need to always see the ee/v4.8.36 endpoints in the WP index, send a request to wp-json/?force_show_ee_namespace=ee/v4.8.29.

Note if you building an API client that might be used with installations of Event Espresso that you won't control: some users might try to use your application on older installations of Event Espresso, which might not have the versioned namespace your API client uses. Eg, they might only have Event Espresso 4.8.34.p installed, and so the v4.8.36 versioned namespace isn't available. Also, because the EE4 REST API is modifyable by server-side code. So be aware that the particular installation of Event Espresso you are interacting with could have removed, added, or changed nearly any endpoint, resource, or behaviour. Your API client will need to be ready to handle unexpected behaviour like this.

Please keep up to date on our developer.eventespresso.com blog for updates to the EE4 REST API. You can subscribe to its RSS feed, or follow us on twitter.

Related Articles

If you have a feature request or bug to report, please let us know on our Github repo.