<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: ExpressionEngine</title>
    <description>The latest articles on Forem by ExpressionEngine (@eecms).</description>
    <link>https://forem.com/eecms</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F3602%2Ffa556d8c-0425-4270-8865-11a4b89eda9e.png</url>
      <title>Forem: ExpressionEngine</title>
      <link>https://forem.com/eecms</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/eecms"/>
    <language>en</language>
    <item>
      <title>ExpressionEngine and Headless</title>
      <dc:creator>ExpressionEngine</dc:creator>
      <pubDate>Mon, 27 Jun 2022 14:11:00 +0000</pubDate>
      <link>https://forem.com/eecms/expressionengine-and-headless-4bp</link>
      <guid>https://forem.com/eecms/expressionengine-and-headless-4bp</guid>
      <description>&lt;p&gt;Headless is all the rage, and for good reason. People and devices consume content in a variety of ways that have needed a different approach to the CMS as a single platform to handle both the managing of content and displaying the content. The idea of a headless CMS focuses the responsibility of the CMS to only administrating content, while the presentation of content is left to a different stack of tools. The presentation can also benefit from static CDN caching for scaling, and increased security. We are not talking about just blog posts, but also data like the current temperature outside, rocket launch times, and infection rates.&lt;/p&gt;

&lt;p&gt;In the world of headless, ExpressionEngine is considered a hybrid CMS. It is able to deliver on many headless concepts, so the final answer depends on which part of the definition is the most useful for your case. Let’s first cover what ExpressionEngine does well around the scope of headless out of the box.&lt;/p&gt;

&lt;h2&gt;
  
  
  What ExpressionEngine Can Do
&lt;/h2&gt;

&lt;p&gt;ExpressionEngine excels at content management through user-defined custom fields that are grouped into channels of content. Said another way, content can be broken into highly structured fields that fit the needs of the site. It has robust roles &amp;amp; permission management for users, and a template engine able to output text in most formats, from JSON to HTML and XML. ExpressionEngine can also be wildly extended by add-on modules. All of this is managed by an intuitive control panel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4kZvLNbG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://docs.expressionengine.com/latest/_images/cp-create.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4kZvLNbG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://docs.expressionengine.com/latest/_images/cp-create.png" alt="ee entry editing screen" width="880" height="842"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What ExpressionEngine is Not
&lt;/h2&gt;

&lt;p&gt;ExpressionEngine is not offered as a SaaS, or built with microservices, and is not currently offered as a fully maintained cloud option. ExpressionEngine is a traditional monolithic PHP application that must be downloaded and installed on a LAMP-like stack. Like most CMS’s in this category, the core can be auto-updated with a single click. It also does not have GraphQL, and editing entries through the API takes a little bit of extra work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Out of the Box
&lt;/h2&gt;

&lt;p&gt;Most APIs for a headless CMS are typically going to return JSON, and this is absolutely possible in ExpressionEngine! To create a JSON-formatted template, simply create a new template, select the type of javascript, and create a tag loop to output entry data. Since templates are independent of each other, it is also possible to run both a standard HTML website alongside various API endpoints in the same ExpressionEngine install.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yihDw5P4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://u.expressionengine.com/asset/article-images/json-feed.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yihDw5P4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://u.expressionengine.com/asset/article-images/json-feed.png" alt="ee template editor" width="880" height="597"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  No rate limits
&lt;/h3&gt;

&lt;p&gt;Since these behave like normal web pages, there is not a way to limit page requests per API key.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lack of access control with something like tokens
&lt;/h3&gt;

&lt;p&gt;API keys and tokens in the header of a request to control access or enforce limits are not supported.&lt;/p&gt;

&lt;h3&gt;
  
  
  GET is the only request type
&lt;/h3&gt;

&lt;p&gt;Requests that modify data such as &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PATCH&lt;/code&gt;, and &lt;code&gt;DELETE&lt;/code&gt; would not be supported out of the box. See further down the article for more information.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;GET&lt;/code&gt; Example
&lt;/h2&gt;

&lt;p&gt;To display a list of upcoming events at, say, a conference center, first create a channel to hold the event information such as the title, event date…etc. Conference rooms in the facility would have their own channel and contain information like hall capacity, name, and photos and then be related to the event. This structure can then be outputted in templates:&lt;/p&gt;

&lt;h3&gt;
  
  
  Example of an HTML template at &lt;code&gt;/events/item/my-event&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{exp:channel:entries channel="events"}

  &amp;lt;!-- Event Info --&amp;gt;
  &amp;lt;h1&amp;gt;{title}&amp;lt;/h1&amp;gt;
  &amp;lt;time&amp;gt;{entry_date format="%m/%d/%Y"}&amp;lt;/time&amp;gt;
  &amp;lt;p&amp;gt;{description_custom_field}&amp;lt;/p&amp;gt;

  &amp;lt;!--- Room Relationship --&amp;gt;
  {conference_room}
    &amp;lt;h3&amp;gt;Room: {conference_room:title}&amp;lt;/h3&amp;gt;
    &amp;lt;p&amp;gt;Capacity: {conference_room:size}&amp;lt;/p&amp;gt;
  {conference_room}

{/exp:channel:entries}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example of the same data in JSON at &lt;code&gt;/api/events/my-event&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{exp:http_header content_type="application/json"}
[
  {exp:channel:entries channel="events" backspace=’1’}
  {
    // Event Info:
    ‘title’ : ‘{title}’,
    ‘entry_id’ : {entry_id},
    ‘description’ : ‘{description_custom_field}’,

    // Room Relationship:
    ‘conference_room’ : [
      {conference_room backspace='1'}
      {
        'room_title' : ‘{conference_room:title}’,
        ‘size’ : ‘{conference_room:size}’
      },{/conference_room}
    ]

  },{/exp:channel:entries}
]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating Better Queries
&lt;/h2&gt;

&lt;p&gt;The vanilla channel entries loop is a bit cumbersome to fine-tune queries using the URL like a typical REST API. To get more control, URL query parameters can be used through the first-party add-on Low Search. They can be any of the standard fields such as date, status, or title plus all custom fields, and relationships.&lt;/p&gt;

&lt;p&gt;The most common use for this approach is updating a page with search results using asynchronous javascript (AJAX). Andy McCormick wrote a &lt;a href="https://u.expressionengine.com/article/advanced-searching-with-low-search-and-ajax"&gt;series on this&lt;/a&gt;. While in Andy’s series the templates returned HTML, it can be easily changed to JSON:&lt;/p&gt;

&lt;p&gt;Entries Template &lt;code&gt;/group/entries&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{exp:http_header content_type="application/json"}
[
  {exp:low_search:results query="{segment_3}" backspace="1"}
    {
      ‘title’ : ‘{title}’,
      ‘entry_id’ : {entry_id},
      ‘custom_field_1’ : ‘{custom_field_1}’,

    },{/exp:low_search:results}
]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Static HTML File:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const request_url = '/group/entries?status=open&amp;amp;channel=events&amp;amp;limit=10';

const entries = await fetch(request_url).then(response =&amp;gt; response.json());

entries.forEach( entry =&amp;gt; {

  console.log(entry.title);
  console.log(entry.entry_id);
  console.log(entry.custom_field_1);

});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Extending with Add-ons
&lt;/h2&gt;

&lt;p&gt;ExpressionEngine can be extended with Add-ons written by 3rd party developers through the ExpressionEngine Store, or can be custom-written per-project. Here are just a few to check out:&lt;/p&gt;

&lt;h3&gt;
  
  
  Bones by TripleNERDScore
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://expressionengine.com/add-ons/bones"&gt;https://expressionengine.com/add-ons/bones&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bones combines both the Low Search query URL feature, plus gives a full output of entry data in a predictable format. This skips a lot of custom template authoring. Data can be returned as JSON through an action URL (see below), or template. Bones also provides the ability to require an API key in order to access data, adding to security. Like the Low Search approach, data queries are made through the URL parameter. Bones also helps with counting objects, and better error handling than a standard template.&lt;/p&gt;

&lt;h3&gt;
  
  
  Webservice by Reinos
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://expressionengine.com/add-ons/reinos-webservice"&gt;https://expressionengine.com/add-ons/reinos-webservice&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This add-on provides a highly customized web service for ExpressionEngine that includes GET and POST processes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom Add-ons
&lt;/h2&gt;

&lt;p&gt;If none of these options suit your needs, you can create your own custom add-on. Add-ons can tap into control panel events such as when entries are updated, or someone logs in, to have their own custom logic and endpoints for data.&lt;/p&gt;

&lt;p&gt;When building an ExpressionEngine module there is a service called Action ID, or “ACT”. A module method can be run when a specific URL is requested. This can be the start of your API and can accept GET as well as POST and other requests to update ExpressionEngine. For example, by visiting the URL below, the function &lt;code&gt;act_action&lt;/code&gt; would be called.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://example.com/index.php?ACT=23&amp;amp;action=get_entries&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// On install, ExpressionEngine can
// create a new ACT ID to the method name of your choice.
function act_action()
{

  if (ee()-&amp;gt;input-&amp;gt;post('action') === 'get_entries')
  {

    $entries = ee('Model')-&amp;gt;get('ChannelEntry')-&amp;gt;all();

    return json_encode($entries);

  }

}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Modifying Entries
&lt;/h2&gt;

&lt;p&gt;Creating and updating entries in ExpressionEngine will take a little bit of work, and require building a Module add-on.&lt;/p&gt;

&lt;p&gt;When installing an add-on’s ACT action, setting the &lt;code&gt;csrf_exempt&lt;/code&gt; to &lt;code&gt;1&lt;/code&gt; will disable the token check as shown below. At this point any data can be submitted to your method, so be careful how this is used.&lt;/p&gt;

&lt;p&gt;Also to note, updating Grid and Fluid fields take additional steps to be updated and are beyond the scope of this writeup.&lt;/p&gt;

&lt;p&gt;Within the add-ons’s &lt;code&gt;upd.myaddon.php&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$data = array(
    'class' =&amp;gt; 'MyClass' ,
    'method' =&amp;gt; 'my_act_method',
    'csrf_exempt' =&amp;gt; 1 // Important, use with care.
);
ee()-&amp;gt;db-&amp;gt;insert('actions', $data);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From here, the &lt;code&gt;my_method&lt;/code&gt; function can alter data in ExpressionEngine. For example, to update the title of an entry we could do something like the following:&lt;/p&gt;

&lt;p&gt;POST Request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -d 'title=NewTitle!&amp;amp;entry_id=5' -X POST https://example.com/index.php?ACT=23

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;mod.myaddon.php&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function my_act_method()
{

    // Get fields from POST using sanitized methods:
    $entry_id = ee()-&amp;gt;input-&amp;gt;post('entry_id');
    $new_title = ee()-&amp;gt;input-&amp;gt;post('title');

    // Use ExpressionEngine models to get an entry.
    $entry = ee('Model')-&amp;gt;get('ChannelEntry', $entry_id)-&amp;gt;first(); 
    $entry-&amp;gt;title = $new_title; // Change the title.
    $entry-&amp;gt;save(); // Save changes.

}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Using ExpressionEngine to power a headless application is absolutely possible, with some limitations. ExpressionEngine excels at content management while providing the possibility for a hybrid of standard HTML and API templates in the same open-source system. It can deliver that data in numerous ways. ExpressionEngine can also be extended with existing or custom add-ons to build more functionality where the native options are limited. Where ExpressionEngine may not fit is if you are looking for a fully-hosted and maintained CMS on a monthly or annual fee with a little extendability, or if a lot of data is being updated outside of the ExpressionEngine control panel.&lt;/p&gt;

&lt;p&gt;-- &lt;/p&gt;

&lt;p&gt;Originally published by &lt;a href="https://u.expressionengine.com/author/blair-liikala"&gt;Blair Liikala&lt;/a&gt; at &lt;a href="https://u.expressionengine.com//article/expressionengine-and-headless"&gt;u.expressionengine.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>expressionengine</category>
      <category>eecms</category>
      <category>headless</category>
    </item>
    <item>
      <title>Building an E-commerce Website With ExpressionEngine, Part 4</title>
      <dc:creator>ExpressionEngine</dc:creator>
      <pubDate>Mon, 13 Jun 2022 10:04:00 +0000</pubDate>
      <link>https://forem.com/eecms/building-an-e-commerce-website-with-expressionengine-part-4-49ca</link>
      <guid>https://forem.com/eecms/building-an-e-commerce-website-with-expressionengine-part-4-49ca</guid>
      <description>&lt;h1&gt;
  
  
  Part 4: Notifications and Order management
&lt;/h1&gt;

&lt;h2&gt;
  
  
  4a) Notifications
&lt;/h2&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/631310597" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;We have most of the puzzle pieces of a working store in place. Next we’ll quickly review Notification setup and Order Management within CartThrob. Navigate to the &lt;strong&gt;Notifications&lt;/strong&gt; tab.&lt;/p&gt;

&lt;p&gt;Starting at the top of the page with the &lt;strong&gt;Log email?&lt;/strong&gt; setting; I typically don’t log email unless it’s a troubleshooting situation. When enabled, it writes debug info to a CartThrob database table.&lt;/p&gt;

&lt;p&gt;We are going to setup two email notifications. The first notification is intended for the &lt;strong&gt;customer,&lt;/strong&gt; while the second is intended for the &lt;strong&gt;store owner&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the CartThrob settings, the first block is what we’ll use for the Customer notification. CartThrob likely created a few of these email configuration blocks by default.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dLqqc2wo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/6a-customer-notification.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dLqqc2wo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/6a-customer-notification.jpg" alt="Customer notification" width="880" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Everything is pretty self-explanatory here. The &lt;strong&gt;To email&lt;/strong&gt; address is dynamically assigned from the order itself using the ExpressionEngine variable.&lt;/p&gt;

&lt;p&gt;Next, the &lt;strong&gt;Email template&lt;/strong&gt; needs to point to a standard ExpressionEngine template, in our case a fairly basic HTML table template geared for email delivery.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Event&lt;/strong&gt; setting is used to match a CartThrob event that must be triggered to send this email. In this case, a successful transaction.&lt;/p&gt;

&lt;p&gt;We are ready to move on to the next email block for the store owner. The setup is nearly identical, only differing in subject line, recipient, and email template. The &lt;strong&gt;Event&lt;/strong&gt; setting remains as “Successful Transaction,” which means two emails are sent by CartThrob for each successful order. Marked in yellow, below, are the subtle differences in the owner email compared to customer&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4F8P1a7B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/6b-owner-notification.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4F8P1a7B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/6b-owner-notification.jpg" alt="Owner notification" width="880" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the frontend, we’re ready to submit a test order to test the notifications. I’ll add a couple of items to my cart, even specifying an order note. Here is what the email notification looks like to the customer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OlxtbIWh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/6c-email-preview.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OlxtbIWh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/6c-email-preview.jpg" alt="Email preview" width="880" height="814"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4b) Order Management
&lt;/h2&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/631310816" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Where does CartThrob store all this?&lt;/p&gt;

&lt;h3&gt;
  
  
  Order entries
&lt;/h3&gt;

&lt;p&gt;If you remember when we used that installation function within the CartThrob &lt;strong&gt;Installation&lt;/strong&gt; Tab, it created the Coupon Codes and the Orders Channels.&lt;/p&gt;

&lt;p&gt;The Orders Channel looks like a standard ExpressionEngine channel with the title being the order number, and then the statuses that we have assigned to the channel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Mo78RrPr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/7a-order-entries.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Mo78RrPr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/7a-order-entries.jpg" alt="Order Entries List" width="880" height="307"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If I look through a sample Order entry, it’s pretty standard; just a lot of custom fields. In this case, the actual line items for the order appear on the order items CartThrob field.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gCpByK62--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/7b-individual-order.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gCpByK62--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/7b-individual-order.jpg" alt="Order Entry" width="880" height="718"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The expected order and customer data is found in the entry fields. Note the layout tabs that capture the billing, shipping payment. It’s all here in the ExpressionEngine Entry!&lt;/p&gt;

&lt;h3&gt;
  
  
  Order Manager
&lt;/h3&gt;

&lt;p&gt;The CartThrob order manager is a separate Add-On; you can add both to your ExpressionEngine control panel menu manager as you see fit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tLtIHjM8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/8a-order-manager.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tLtIHjM8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/8a-order-manager.jpg" alt="Order Entry" width="880" height="227"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Within Order Manager, you’ll have basic reports around order totals by month, product reports, etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--05FKFrSf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/8b-order-reports.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--05FKFrSf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/8b-order-reports.jpg" alt="Order Reports" width="880" height="644"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Within Order Manager, the &lt;strong&gt;Orders&lt;/strong&gt; menu appears above the Reports. I’m just going to look at a single order I just made. It &lt;strong&gt;looks&lt;/strong&gt; like an ExpressionEngine channel entries interface but it is slightly different, supplementing with some CartThrob-specific functionality.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WcbGQDLF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/8c-order-list.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WcbGQDLF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/8c-order-list.jpg" alt="Order List" width="880" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on a test entry and you’ll see the contents of this order, with functions for deleting the order, re-sending the order notification, or issuing a tracking number.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WFCAVgnK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/8d-order-entry.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WFCAVgnK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/8d-order-entry.jpg" alt="Order Entry" width="880" height="918"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This concludes our tutorial of the CartThrob setup and frontend review. On to our final installment to cover Templates!  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Have questions or comments about this course?&lt;/strong&gt; Be sure to join the discussion and post in the &lt;a href="https://expressionengine.com/forums/topic/253239/building-an-e-commerce-website-with-expressionengine"&gt;ExpressionEngine Forums&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;-- &lt;/p&gt;

&lt;p&gt;Originally published by &lt;a href="https://u.expressionengine.com/author/paul-larson"&gt;Paul Larson&lt;/a&gt; at &lt;a href="https://u.expressionengine.com//article/building-an-e-commerce-website-with-expressionengine-part-4"&gt;u.expressionengine.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>expressionengine</category>
      <category>eecms</category>
      <category>ecommerce</category>
    </item>
    <item>
      <title>Building an E-commerce Website With ExpressionEngine, Part 3</title>
      <dc:creator>ExpressionEngine</dc:creator>
      <pubDate>Fri, 10 Jun 2022 10:04:00 +0000</pubDate>
      <link>https://forem.com/eecms/building-an-e-commerce-website-with-expressionengine-part-3-2gm4</link>
      <guid>https://forem.com/eecms/building-an-e-commerce-website-with-expressionengine-part-3-2gm4</guid>
      <description>&lt;h1&gt;
  
  
  Part 3: Shipping / Tax / Payments
&lt;/h1&gt;

&lt;h2&gt;
  
  
  3a) Shipping
&lt;/h2&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/631318531" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Within CartThrob, navigate to &lt;strong&gt;Shipping&lt;/strong&gt;. There’s several plugins to choose from; for this example I’m using &lt;strong&gt;By weight - Threshold&lt;/strong&gt;. The settings are self-explanatory. The shipping plugin uses a tiered system so any order weight total of up to 5 lbs will match the first row, resulting in a shipping charge of $8, and so on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ldfnF2oH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/5-a-shipping-by-weight-thresh.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ldfnF2oH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/5-a-shipping-by-weight-thresh.jpg" alt="Shipping Plugin" width="880" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the last row I have a really high threshold of 500, so anything after 15 lbs will just cost $500.&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Submit&lt;/strong&gt; to save the plugin selection and its values.&lt;/p&gt;

&lt;p&gt;On the frontend we’ll test the Yoda Bowling Ball. The cart tallies shipping as $13, because the &lt;strong&gt;By Weight - Threshold&lt;/strong&gt; plugin matches the order total of 10 lbs. (Totals up to and matching 10 lbs will match the $13 calculation.)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kCwbOg8x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/5-b-shipping-by-weight-frontend.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kCwbOg8x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/5-b-shipping-by-weight-frontend.jpg" alt="Shipping frontend test" width="880" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3b) Taxes
&lt;/h2&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/631318859" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Within CartThrob, navigate to &lt;strong&gt;Taxes&lt;/strong&gt;. I’ll leave “Calculate taxes using shipping address” as &lt;strong&gt;No&lt;/strong&gt; , since I want to calculate by billing address.&lt;/p&gt;

&lt;p&gt;For the Tax Plugin, we’ll use “Tax by Location - Percentage”. Each row represents a tax rate, region, and zip. CartThrob will use these values to match to the customer’s billing address to tally the proper tax rate. For this example, I’ll use 7.7%.&lt;/p&gt;

&lt;p&gt;Like shipping, I added a second row with a high value to catch all other situations. Anything that doesn’t match the first row would be match by this global rule which is 20%. We could put in more rows and more zip codes and regions, but we’ll leave this simple for now!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JrOKNe0a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/5-c-tax-by-location.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JrOKNe0a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/5-c-tax-by-location.jpg" alt="Tax plugin" width="880" height="285"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Submit&lt;/strong&gt;. Taxes are done! Add an item to your cart on the frontend and give it a try!&lt;/p&gt;

&lt;h2&gt;
  
  
  3c) Payments
&lt;/h2&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/631319089" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Now that our shipping and tax are behaving, I’ll look at the payment gateways. Within CartThrob, navigate to &lt;strong&gt;Payments&lt;/strong&gt; , and focus on the first menu, &lt;strong&gt;Choose your primary payment gateway&lt;/strong&gt;. I’m using the Stripe plugin developer mode. (Stripe and Authorize are the gateways I use the most. I prefer Stripe because it’s just easier to test compared to Authorize or Paypal)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2UmTxxgc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/5-d-gateway-select.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2UmTxxgc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/5-d-gateway-select.jpg" alt="Payment Gateway selection" width="880" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can choose the various gateways that CartThrob has available. The first select menu is showing you what gateway is chosen, the second is giving you the chance to edit that gateway’s settings. (the settings will appear below, without a page reload, upon making this selection)&lt;/p&gt;

&lt;p&gt;Below is where I have my test modes selected, and I have my &lt;strong&gt;Test Mode API key (secret)&lt;/strong&gt;, and &lt;strong&gt;Test Mode API key (publishable)&lt;/strong&gt; that I configured within my Stripe account.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6RFxj5Lg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/5-e-gateway-config.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6RFxj5Lg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/5-e-gateway-config.jpg" alt="Payment Gateway config" width="880" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Everything else I can leave as-is. Click &lt;strong&gt;Submit&lt;/strong&gt; to save the changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  3d) Submit an order on the frontend
&lt;/h2&gt;

&lt;p&gt;Now we can actually complete an order! At the bottom of my checkout page, I’m using the default Stripe test number, 4242 4242 4242 4242, providing any CVV code and a valid expiration date.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ftoGrOoG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/5-f-test-order.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ftoGrOoG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/5-f-test-order.jpg" alt="Submit test payment" width="880" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our test order was hopefully successful! Head over to Stripe (or your gateway), and view the appropriate Dashboard for test orders to confirm.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Have questions or comments about this course?&lt;/strong&gt; Be sure to join the discussion and post in the &lt;a href="https://expressionengine.com/forums/topic/253239/building-an-e-commerce-website-with-expressionengine"&gt;ExpressionEngine Forums&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;-- &lt;/p&gt;

&lt;p&gt;Originally published by &lt;a href="https://u.expressionengine.com/author/paul-larson"&gt;Paul Larson&lt;/a&gt; at &lt;a href="https://u.expressionengine.com//article/building-an-e-commerce-website-with-expressionengine-part-3"&gt;u.expressionengine.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>expressionengine</category>
      <category>eecms</category>
      <category>ecommerce</category>
    </item>
    <item>
      <title>Building an E-commerce Website With ExpressionEngine, Part 2</title>
      <dc:creator>ExpressionEngine</dc:creator>
      <pubDate>Wed, 08 Jun 2022 10:04:00 +0000</pubDate>
      <link>https://forem.com/eecms/building-an-e-commerce-website-with-expressionengine-part-2-4om1</link>
      <guid>https://forem.com/eecms/building-an-e-commerce-website-with-expressionengine-part-2-4om1</guid>
      <description>&lt;h1&gt;
  
  
  Part 2: Channel installation and Discounts
&lt;/h1&gt;

&lt;h2&gt;
  
  
  2a) Orders/Discount Channel Installation
&lt;/h2&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/631309736" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Back in the ExpressionEngine backend, go to the CartThrob add-on, and then the “Installation” tab. (We’re not going to Orders Tab which would be the logical next step)&lt;/p&gt;

&lt;p&gt;Of these available installation options, we actually skip most of them because we’re starting with pretty basic templates that we’ve created. Our store is not really complicated enough for Packages or Discounts channels. Instead, we will only choose &lt;strong&gt;Store - Orders&lt;/strong&gt; and then &lt;strong&gt;Store - Coupon Codes&lt;/strong&gt; channels. (Remember, our FakeBowling.shop ExpressionEngine site already had a Products channel) If you’re curious, &lt;strong&gt;Store - Purchased&lt;/strong&gt; items would be used to if you want to show “Customers Also Bought..” on a product page, which is a cool feature.&lt;/p&gt;

&lt;p&gt;We are not installing any templates, as we already have those completed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OtZ0Vcnc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/4-c-CT-channel-install.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OtZ0Vcnc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/4-c-CT-channel-install.jpg" alt="CartThrob test channels install" width="880" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Install Templates &amp;amp; Channels&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Navigate to the Channels menu, and you’ll hopefully see that the two new channels were created.&lt;/p&gt;

&lt;p&gt;Now, head back to CartThrob, navigate to the &lt;strong&gt;Orders&lt;/strong&gt; tab, and this one’s sort of settle. You have to first click your order channel, &lt;strong&gt;Store - Orders&lt;/strong&gt; , and then be sure to select “Yes” for “Save completed orders to a channel?”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BQ53sbuP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/4-d-CT-channel-map.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BQ53sbuP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/4-d-CT-channel-map.jpg" alt="CartThrob channel mapping" width="880" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Upon submitting these changes, we should see statuses that need to be assigned. Scroll down to the &lt;strong&gt;Order Status&lt;/strong&gt; section and map each status accordingly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0klQQnAx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/4-e-CT-status-map.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0klQQnAx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/4-e-CT-status-map.jpg" alt="CartThrob status mapping" width="880" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’ll follow suit with &lt;strong&gt;Order Data Fields&lt;/strong&gt;. (this one takes a while!)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Er7H1MKc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/4-e-CT-field-map.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Er7H1MKc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/4-e-CT-field-map.jpg" alt="CartThrob field mapping" width="880" height="669"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now CartThrob knows about our Orders channel and its Status and Data fields.&lt;/p&gt;

&lt;h2&gt;
  
  
  2b) Coupon Code (Discounts)
&lt;/h2&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/631310236" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Next, we’re going to make some fields to store coupon codes. In the Fields menu, there’s now a field group called &lt;strong&gt;Coupon Codes&lt;/strong&gt; with a default field &lt;code&gt;{coupon_code_type}&lt;/code&gt;. We’re going to supplement this with two more fields of our own.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1mdt1o__--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/4-f-CT-coupon-field.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1mdt1o__--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/4-f-CT-coupon-field.jpg" alt="Coupon Code field" width="880" height="563"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add a text input field, we’ll call it just “Coupon Code”, shortname &lt;code&gt;{coupon_code}&lt;/code&gt;. This will be the field that we put the actual code the customer would type in on the cart page, e.g. “Black Friday” coupon.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MbY2qRtx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/4-g-CT-coupon-code.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MbY2qRtx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/4-g-CT-coupon-code.jpg" alt="Coupon Code field" width="880" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Follow up by creating another text field as the &lt;code&gt;{coupon_description}&lt;/code&gt;. This field would be for a frontend display, such as, “You now have a 10% off your order” to display in the cart.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2owoQwJj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/4-g-CT-coupon-desc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2owoQwJj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/4-g-CT-coupon-desc.jpg" alt="Coupon Description Field" width="880" height="638"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the discount fields created, it’s back CartThrob settings and then the &lt;strong&gt;Discounts&lt;/strong&gt; tab. Our fields haven’t yet been assigned to the new channel, so we’ll add that setting and click &lt;strong&gt;Submit&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VVeD2WiB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/4-h-CT-map-desc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VVeD2WiB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/4-h-CT-map-desc.jpg" alt="Coupon Mapping" width="880" height="759"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we are ready to make a coupon code entry within the &lt;strong&gt;Store - Coupon Codes&lt;/strong&gt; channel. Our title is “Ten Percent Off EE CONF Special,” a Type of “Percentage Off,” and &lt;strong&gt;Percentage Off&lt;/strong&gt; value of 10. And finally, a &lt;strong&gt;Coupon Code&lt;/strong&gt; of “abc123” and a &lt;strong&gt;Coupon Description&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ygb3K1uH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/4-i-CT-coupon-entry.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ygb3K1uH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/4-i-CT-coupon-entry.jpg" alt="Coupon entry creation" width="880" height="738"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’re done! These coupon codes can be tested on the frontend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Have questions or comments about this course?&lt;/strong&gt; Be sure to join the discussion and post in the &lt;a href="https://expressionengine.com/forums/topic/253239/building-an-e-commerce-website-with-expressionengine"&gt;ExpressionEngine Forums&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;-- &lt;/p&gt;

&lt;p&gt;Originally published by &lt;a href="https://u.expressionengine.com/author/paul-larson"&gt;Paul Larson&lt;/a&gt; at &lt;a href="https://u.expressionengine.com//article/building-an-e-commerce-website-with-expressionengine-part-2"&gt;u.expressionengine.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>expressionengine</category>
      <category>eecms</category>
      <category>ecommerce</category>
    </item>
    <item>
      <title>Building an E-commerce Website With ExpressionEngine, Part 1</title>
      <dc:creator>ExpressionEngine</dc:creator>
      <pubDate>Tue, 07 Jun 2022 10:04:00 +0000</pubDate>
      <link>https://forem.com/eecms/building-an-e-commerce-website-with-expressionengine-part-1-4o8b</link>
      <guid>https://forem.com/eecms/building-an-e-commerce-website-with-expressionengine-part-1-4o8b</guid>
      <description>&lt;p&gt;This is a five-part series on how to build an ecommerce website in ExpressionEngine from scratch using the CartThrob add-on.  &lt;/p&gt;

&lt;p&gt;This walkthrough will be in 5 parts, covering the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Part 1: Setting the stage: product field creation&lt;/li&gt;
&lt;li&gt;Part 2: Channel installation and Discounts&lt;/li&gt;
&lt;li&gt;Part 3: Shipping / Tax / Payments&lt;/li&gt;
&lt;li&gt;Part 4: Notifications and Order management&lt;/li&gt;
&lt;li&gt;Part 5: Template setup and tag overview
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Throughout this series I'm joined on video by Greg Crane, a developer at Creative Arc, for each key segment of the walkthrough. Greg created all the templates, too, which are available for download.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Have questions or comments about this course?&lt;/strong&gt; Be sure to join the discussion and post in the &lt;a href="https://expressionengine.com/forums/topic/253239/building-an-e-commerce-website-with-expressionengine"&gt;ExpressionEngine Forums&lt;/a&gt;!&lt;/p&gt;

&lt;h1&gt;
  
  
  Part 1: Setting the stage
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://cartthrob.com"&gt;CartThrob&lt;/a&gt; is an ecommerce add-on we’ve used for many years, building sophisticated stores that integrate with ERPs, event ticketing sites, or even basic invoice payment forms. This series provides a foundation for building a CartThrob commerce website from scratch, covering field creation, channel configuration and assignment, payments, shipping, taxes and order notifications. A high-level overview of template tags is found in Part 5 of the walkthrough, along with a zip archive of the full example templates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; This is &lt;em&gt;a way&lt;/em&gt; to build a CartThrob store, not &lt;strong&gt;the&lt;/strong&gt; way. The power of CartThrob is in how it extends ExpressionEngine. You can bring your ExpressionEngine skills to bear and simply extend into commerce.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Acknowledgements&lt;/strong&gt; : These example templates, and the general configuration covered in this walkthrough are developed by &lt;strong&gt;Greg Crane&lt;/strong&gt; from store builds for our clients.&lt;/p&gt;

&lt;p&gt;Introducing our demo store, FakeBowling.shop!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ld1I4psE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/fakebowling-home.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ld1I4psE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/fakebowling-home.png" alt="FakeBowling.shop" width="880" height="847"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you might have guessed, our chosen demonstration store is one that sells bowling accessories. Such a store would likely have &lt;strong&gt;Simple Products&lt;/strong&gt; such as a bowling ball, or &lt;strong&gt;Optioned Products&lt;/strong&gt; such as a shirt with size options, or &lt;strong&gt;Virtual Products&lt;/strong&gt; such as League dues where shipping and weight don’t apply.&lt;/p&gt;

&lt;p&gt;This is a speed run starting from an existing ExpressionEngine website; our journey begins just after the CartThrob add-on has been installed but none of the configuration has been completed. Two basic channels exist in our ExpressionEngine site for this example: &lt;code&gt;{simple_products}&lt;/code&gt; and &lt;code&gt;{optioned_products}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let’s get started!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1a) Simple Products: field creation and entry configuration
&lt;/h2&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/631308664" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;We’re going to create our &lt;strong&gt;simple product fields&lt;/strong&gt; and then review how those appear on the front end, then configure CartThrob so it knows about that simple product channel. Within the &lt;strong&gt;Fields&lt;/strong&gt; menu, you’ll see we already created field groups for our Simple and Optioned products, but for now we’re going to focus on the Simple Product Field group.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QJgHeYcr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/1-a-fields.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QJgHeYcr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/1-a-fields.jpg" alt="Simple Product Field group" width="880" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first field we need to add is the price. Create a new field within this field group; use the field type “CartThrob - Simple”. I’ll call this &lt;strong&gt;Simple Product Price&lt;/strong&gt; , &lt;code&gt;{simple_product_price}&lt;/code&gt;, making it required.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5Wkcz6dZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/1-b-price-field.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5Wkcz6dZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/1-b-price-field.jpg" alt="Simple Product Price field" width="880" height="949"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next field is the Quantity, or the Inventory Tracking field, and this can just be text input with “Integer” as the &lt;strong&gt;Allowed Content&lt;/strong&gt; setting. In our case we used &lt;code&gt;{simple_product_inventory}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VVnn9qTe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/1-c-inventory-field.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VVnn9qTe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/1-c-inventory-field.jpg" alt="Simple Product Inventory field" width="880" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The third field to create is a Text input field, named &lt;code&gt;{simple_product_weight}&lt;/code&gt; with &lt;strong&gt;Number&lt;/strong&gt; set for Allowed Content.&lt;/p&gt;

&lt;p&gt;We have three demo products in our store (two bowling balls and one accessory). Note that Products within CartThrob are simply ExpressionEngine channel entries: everything you’re accustomed to from a standard ExpressionEngine website applies; we are simply introducing CartThrob field types.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IuTutRFD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/1-d-entry-list.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IuTutRFD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/1-d-entry-list.jpg" alt="Simple Product Entry List" width="880" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s look at one test product entry as an example (8-pound Hello Kitty ball). We already have the photo and the body and the SKU in there, but I’ll just give this a price of $53 and a quantity of 98 in stock.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Jebm656s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/1-e-hello-kitty-entry.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Jebm656s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/1-e-hello-kitty-entry.jpg" alt="Hello Kitty, example item" width="880" height="1375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Following suit, the Yoda ball is $103 with a Inventory of 5, followed by the Bag Roller product, priced at $80 and a quantity of 60.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A quick peek at one of these items on the frontend:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3n4xKbUk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/1-f-hello-kitty-frontend.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3n4xKbUk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/1-f-hello-kitty-frontend.jpg" alt="Hello Kitty, frontend" width="880" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we can go to the CartThrob add-on within the ExpressionEngine backend, which at the moment doesn’t know this channel exists. From the Products tab, we’ll assign the channel &lt;strong&gt;Simple Products&lt;/strong&gt;. Our Price Field name is the &lt;strong&gt;Simple Product Price&lt;/strong&gt; , the weight field name is &lt;strong&gt;Simple Product Weight&lt;/strong&gt; and the quantity field is &lt;strong&gt;Simple Product Inventory&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HDHM8EGk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/1-g-cartthrob-assignment.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HDHM8EGk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/1-g-cartthrob-assignment.jpg" alt="Simple Product Channel Assignments" width="880" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So now CartThrob knows that products are coming from that channel, and it knows which fields to use for the pricing and for the shipping/weight calculations. On to Optioned Products!&lt;/p&gt;

&lt;h2&gt;
  
  
  1b) Optioned Products
&lt;/h2&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/631309045" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;So we have the basics of the simple product configured and populated. Now we have our more complicated Optioned products that are in a different channel, and just as we did for the Simple products, we have to make some fields, populate the products, and then configure CartThrob to use the appropriate channel and fields. As a reminder, Optioned products refer to T-shirt with sizes of small, medium, and large, for example.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The CartThrob price modifier field&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Returning to the Fields menu, navigate to the Optioned Product Field Group. Create a new field of type &lt;strong&gt;CartThrob price modifier&lt;/strong&gt;. The name of my field is simply “Options,” with a Short name of &lt;code&gt;{optioned_product_options}&lt;/code&gt;. Note: On the frontend, the name of the field (Options) is what’s going to display next to the dropdown with the options, shirt sizes and colors, for example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wZqFCO62--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/2-a-options-field.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wZqFCO62--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/2-a-options-field.jpg" alt="Optioned Product Channel Assignments" width="880" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we’ll make a basic Text input field &lt;code&gt;{optioned_product_weight}&lt;/code&gt; with Number as the Allowed Content setting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add Product Options to Entry&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Go to “Edit” menu and bring up our Optioned Product channel. We’ll use our Shirt product (The Andy) as an example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eCd4SsFs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/2-b-optioned-entries.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eCd4SsFs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/2-b-optioned-entries.jpg" alt="Optioned Product Channel Entries" width="880" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As we scroll down that product entry page, we’ll see our blank CartThrob Price Modifier field with default columns of &lt;strong&gt;Option Short Name,&lt;/strong&gt;  &lt;strong&gt;Option Label,&lt;/strong&gt; and &lt;strong&gt;Price&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Next, a detail that is easy to miss the first time you do it: we can add additional columns; in our case, &lt;strong&gt;Inventory&lt;/strong&gt; and &lt;strong&gt;SKU&lt;/strong&gt;. (We’re making the assumption that the option prices here are what the customer will see on the frontend. Another option is to have a separate base price for which the price modifiers would add or subtract the prices from that base price). For this example, though, there is no base price, and the Option price is what the customer will see in their cart.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G8w7WfUE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/2-b-options-entry.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G8w7WfUE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/2-b-options-entry.jpg" alt="Optioned Product Entry" width="880" height="948"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sizes (Options) have been added: Small, Medium, and Large with their associated price, inventory, and SKU.&lt;/p&gt;

&lt;p&gt;Note: Within the Options (CartThrob Price Modifier field), the “Save” action is for the &lt;em&gt;Preset&lt;/em&gt;, you’ll need to save the &lt;em&gt;Entry&lt;/em&gt; afterwards. The preset makes data entry easier as you add more products.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setup Optioned Product Channel within CartThrob&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;CartThrob doesn’t know this “Optioned Products” channel exists yet, so we’ll head back to the CartThrob add-on, “Products” tab. You’ll see our simple products are still there but we’re going to add another channel (click “ &lt;strong&gt;Add Another Channel&lt;/strong&gt; ”). We’ll choose Optioned Products with the settings below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JGXPG-vI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/2-c-cartthrob-assignment.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JGXPG-vI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/2-c-cartthrob-assignment.jpg" alt="Optioned Product CartThrob Assignment" width="880" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So far so good! Now we have to save by clicking &lt;strong&gt;Submit&lt;/strong&gt; at the bottom of the page. We should be ready to move on.&lt;/p&gt;

&lt;h2&gt;
  
  
  1c) Frontend Add to Cart Demo
&lt;/h2&gt;

&lt;p&gt;We now have our products created, both the simple and the optioned products. Upon adding the Yoda bowling ball to the cart, it’s good to see it has the proper line-item cost and then a subtotal cost. Updating the cart to a quantity of 2, for example, properly adjusts the subtotal and listed quantity.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fQCPJUHL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/4-a-simple-add-test.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fQCPJUHL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/4-a-simple-add-test.jpg" alt="Simple product Add to cart test on frontend" width="880" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we add the Andy product, size Medium, to the cart.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gDoNA16R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/4-b-optioned-add-test.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gDoNA16R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://creativearc.com/images/uploads/blog/4-b-optioned-add-test.jpg" alt="Optioned product Add to cart test on frontend" width="880" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Both simple and optioned products are together in the same cart with a proper subtotal, all responding to quantity changes by clicking “Update Cart.”&lt;/p&gt;

&lt;p&gt;We have the basics of the cart working in terms of the product information and calculations. Next, we’ll continue and configure CartThrob to handle the orders.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Have questions or comments about this course?&lt;/strong&gt; Be sure to join the discussion and post in the &lt;a href="https://expressionengine.com/forums/topic/253239/building-an-e-commerce-website-with-expressionengine"&gt;ExpressionEngine Forums&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;-- &lt;/p&gt;

&lt;p&gt;Originally published by &lt;a href="https://u.expressionengine.com/author/paul-larson"&gt;Paul Larson&lt;/a&gt; at &lt;a href="https://u.expressionengine.com//article/building-an-e-commerce-website-with-expressionengine-part-1"&gt;u.expressionengine.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>expressionengine</category>
      <category>eecms</category>
      <category>ecommerce</category>
    </item>
    <item>
      <title>Headless ExpressionEngine: Using Bones to Create a Static Site</title>
      <dc:creator>ExpressionEngine</dc:creator>
      <pubDate>Mon, 06 Jun 2022 10:34:00 +0000</pubDate>
      <link>https://forem.com/eecms/headless-expressionengine-using-bones-to-create-a-static-site-366a</link>
      <guid>https://forem.com/eecms/headless-expressionengine-using-bones-to-create-a-static-site-366a</guid>
      <description>&lt;p&gt;ExpressionEngine is so powerful under the hood when it comes to managing content. In this example, we’ll use &lt;a href="https://expressionengine.com/add-ons/bones"&gt;Bones&lt;/a&gt; to use the data from our EE site to build out a static site, using a static site generator called &lt;a href="https://jigsaw.tighten.co/"&gt;Jigsaw&lt;/a&gt;. We’ll explore a bit of API safety, as well as managing data coming from EE.&lt;/p&gt;

&lt;p&gt;If you haven’t, watch the first part of our series on Bones here: &lt;a href="https://u.expressionengine.com/course/headless-expressionengine/using-expressionengine-to-power-your-javascript-frontend-using-bones"&gt;Using ExpressionEngine To Power Your JavaScript Frontend Using Bones&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;YouTube: &lt;iframe width="710" height="399" src="https://www.youtube.com/embed/h5OdDCKr9po"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Bones is a super simple way to utilize ExpressionEngine as a headless content management system for use in javascript frontends, static site builders, and more!&lt;/p&gt;

&lt;p&gt;Bones Hello World: &lt;a href="https://github.com/dougblackjr/bones-hello-world"&gt;https://github.com/dougblackjr/bones-hello-world&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;-- &lt;/p&gt;

&lt;p&gt;Originally published by &lt;a href="https://u.expressionengine.com/author/doug-black"&gt;Doug Black&lt;/a&gt; at &lt;a href="https://u.expressionengine.com//article/headless-expressionengine-using-bones-to-create-a-static-site"&gt;u.expressionengine.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>expressionengine</category>
      <category>eecms</category>
      <category>headless</category>
      <category>jigsaw</category>
    </item>
    <item>
      <title>Using ExpressionEngine To Power Your JavaScript Frontend Using Bones</title>
      <dc:creator>ExpressionEngine</dc:creator>
      <pubDate>Mon, 17 Jan 2022 14:26:00 +0000</pubDate>
      <link>https://forem.com/eecms/using-expressionengine-to-power-your-javascript-frontend-using-bones-4j1m</link>
      <guid>https://forem.com/eecms/using-expressionengine-to-power-your-javascript-frontend-using-bones-4j1m</guid>
      <description>&lt;p&gt;Let’s take a look at building a Vue.js frontend powered by headless ExpressionEngine using the &lt;a href="https://expressionengine.com/add-ons/bones"&gt;Bones&lt;/a&gt; add-on.&lt;/p&gt;

&lt;p&gt;Bones is a super simple way to utilize ExpressionEngine as a headless content management system for use in javascript frontends, static site builders, and more!&lt;/p&gt;

&lt;p&gt;YouTube: &lt;iframe width="710" height="399" src="https://www.youtube.com/embed/rOOziVmUvS8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Find more information here:&lt;/p&gt;

&lt;p&gt;Bones Hello World: &lt;a href="https://github.com/dougblackjr/bones-hello-world"&gt;https://github.com/dougblackjr/bones-hello-world&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;-- &lt;/p&gt;

&lt;p&gt;Originally published by &lt;a href="https://u.expressionengine.com/author/doug-black"&gt;Doug Black&lt;/a&gt; at &lt;a href="https://u.expressionengine.com//article/using-expressionengine-to-power-your-javascript-frontend-using-bones"&gt;u.expressionengine.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>expressionengine</category>
      <category>eecms</category>
      <category>headless</category>
      <category>vue</category>
    </item>
    <item>
      <title>A Primer On Simple Conditionals</title>
      <dc:creator>ExpressionEngine</dc:creator>
      <pubDate>Tue, 11 May 2021 14:55:00 +0000</pubDate>
      <link>https://forem.com/eecms/a-primer-on-simple-conditionals-58jo</link>
      <guid>https://forem.com/eecms/a-primer-on-simple-conditionals-58jo</guid>
      <description>&lt;p&gt;This article is intended as a gentle introduction to ExpressionEngine conditionals to give the reader an idea of how they work using real-world examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a “conditional”?
&lt;/h2&gt;

&lt;p&gt;They allow you to compare or evaluate one piece of information against another and optionally show a response based on the result.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Conditionals generally work on the result being true or false - in essence, you’re asking a question to get an answer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What can you compare/evaluate?
&lt;/h2&gt;

&lt;p&gt;You can compare any variable or value available in ExpressionEngine, including global variables, channel entry fields, member fields, and your own custom values.&lt;/p&gt;

&lt;p&gt;Here’s a non-exhaustive list of things you can evaluate either singly or one versus the other:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;numbers&lt;/li&gt;
&lt;li&gt;dates&lt;/li&gt;
&lt;li&gt;URL segments&lt;/li&gt;
&lt;li&gt;entry fields, title, custom fields&lt;/li&gt;
&lt;li&gt;files, filename, extension&lt;/li&gt;
&lt;li&gt;statuses&lt;/li&gt;
&lt;li&gt;trigger words (like yes/no, on/off)&lt;/li&gt;
&lt;li&gt;logged in status, name, email, etc.&lt;/li&gt;
&lt;li&gt;member group, name, ID, etc.&lt;/li&gt;
&lt;li&gt;your own values, hard coded or generated programmatically&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When to use a conditional
&lt;/h2&gt;

&lt;p&gt;There are near infinite uses, but some common ones are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;checking if a visitor is logged in or not&lt;/li&gt;
&lt;li&gt;display alternative content based on the value of other content&lt;/li&gt;
&lt;li&gt;display different content based on part(s) of a URL&lt;/li&gt;
&lt;li&gt;changing HTML or other code based on the value of something&lt;/li&gt;
&lt;li&gt;adding opening and closing HTML tags like &lt;code&gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;checking if something exists or not&lt;/li&gt;
&lt;li&gt;checking the value of something&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conditional tag syntax
&lt;/h2&gt;

&lt;p&gt;Conditional tags are always wrapped in &lt;code&gt;{if}{/if}&lt;/code&gt; tags - just like regular HTML tags. You add values you want to evaluate and provide the answer based on the result.&lt;/p&gt;

&lt;p&gt;Here are a few very &lt;em&gt;simplistic&lt;/em&gt; examples showing you how evaluation logic works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{if sky}Yes the sky exists!{/if}
{if sky is blue}It's day time{/if}
{if sky is black}It's night time{/if}
{if sky is NOT blue}It's night time{/if}
{if sky is NOT black}It's day time{/if}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See how we’re comparing the sky and its color then providing an answer for each variation? Note how there’s more than one way to provide the same answer (“is” and “is NOT”). The actual answer can be anything you want, even no answer at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s start with a basic example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{if logged_in}
  Hello customer
{/if}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this does is check if the visitor has logged in to your site.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if logged in, they will see the “Hello customer” message&lt;/li&gt;
&lt;li&gt;if not logged in, they will see nothing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s expand that a bit more…&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{if logged_in}
  Hello customer
{if:else}
  Please login
{/if}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we’re now doing is getting two possible values.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if logged in, they will see the “Hello customer” message&lt;/li&gt;
&lt;li&gt;if not logged in, they will see the “Please login” message&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;So “if” the first value is true do that, “else” do the other.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Evaluating entry content
&lt;/h2&gt;

&lt;p&gt;Your entries are made up of several custom and core fields such as Title, Entry date, Status, and so on. You can evaluate any of these in a conditional, some are a little more complicated than others, but the logic is the same.&lt;/p&gt;

&lt;p&gt;Let’s say you have a field for a blog excerpt contained in a &lt;code&gt;div&lt;/code&gt; element. Your template code may look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="row"&amp;gt;
  {blog_excerpt}
&amp;lt;/div&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only problem here; if the excerpt field is blank, the page will have an empty &lt;code&gt;div&lt;/code&gt; element in it; you won’t want that!&lt;/p&gt;

&lt;p&gt;To solve this, you can use a conditional to &lt;em&gt;only&lt;/em&gt; show the &lt;code&gt;div&lt;/code&gt; &lt;em&gt;if&lt;/em&gt; the excerpt exists:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{if blog_excerpt}
&amp;lt;div class="row"&amp;gt;
  {blog_excerpt}
&amp;lt;/div&amp;gt;
{/if}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;So “if” the excerpt exists, then show it, otherwise do nothing. In other words, the &lt;code&gt;div&lt;/code&gt; elements will only show if the excerpt exists.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s expand that by providing a fallback message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{if blog_excerpt}
  &amp;lt;div class="row"&amp;gt;
    {blog_excerpt}
  &amp;lt;/div&amp;gt;
{if:else}
  The author hasn't written an excerpt
{/if}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;So “if” the excerpt exists, show it, “else” show the message.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Comparing a date
&lt;/h2&gt;

&lt;p&gt;Date variables in ExpressionEngine allow you to format a display date however you need.&lt;/p&gt;

&lt;p&gt;In this example, I’m using the 3 letter code for day of the week, Mon, Tue, Wed, etc. referenced by the &lt;code&gt;%D&lt;/code&gt; format value. The &lt;code&gt;current_time&lt;/code&gt; tag outputs, you can probably guess, the current time, as in &lt;em&gt;right now&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The idea here is to show a message on a specific day, in this case, a Sunday:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{if "{current_time format='%D'}" == "Sun"}
 Sorry we're closed today
{/if}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this does is compare the current day of the week &lt;code&gt;{current_time format="%D"}&lt;/code&gt; to a target day value of “Sun”(day). If the answer is true (if today is Sunday) show the message, otherwise do nothing. The &lt;code&gt;==&lt;/code&gt; value is shorthand for “is equal to.”&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note that here we have to wrap the &lt;code&gt;current_time&lt;/code&gt; tag in double quotes because it’s a tag inside another tag.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing against a URL segment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Conditionally do something based on part of a URL
&lt;/h3&gt;

&lt;p&gt;Say you have a URL like &lt;a href="https://example.com/blog/post/my-blog-post"&gt;https://example.com/blog/post/my-blog-post&lt;/a&gt;, then “blog” is segment 1, “post” is segment 2, and “my-blog-post” is segment 3 (See the docs for more on &lt;a href="https://docs.expressionengine.com/latest/templates/globals/url-segments.html#url-segment-variables"&gt;URL Segments&lt;/a&gt; ).&lt;/p&gt;

&lt;p&gt;Here’s a couple of examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{if segment_1 == "blog"}This is the blog.{/if}


{if segment_2 == "post"}You're reading a blog post.{/if}


{if segment_1 == "about-us"}Why not read our blog?{/if}


{if segment_1 == ""}This must the home page.{/if}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using a conditional to show a confirmation message
&lt;/h3&gt;

&lt;p&gt;Say you have a contact form page at &lt;a href="https://example.com/contact"&gt;https://example.com/contact&lt;/a&gt; and your success page is at &lt;a href="https://example.com/contact/thanks"&gt;https://example.com/contact/thanks&lt;/a&gt; - you can run a conditional on segment_2 (thanks) to show a success message in the same template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{if segment_2 == "thanks"}
Thanks for getting in touch!
{/if}

&amp;lt;form&amp;gt;
...contact form fields
&amp;lt;/form&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using a conditional to insert a CSS class name
&lt;/h3&gt;

&lt;p&gt;Let’s say you wanted to add a class to an element but only in the /blog section. You could do something like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;body{if segment_1 == "blog"} class="blog"{/if}&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Would output as this (if the segment matched):&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;body class="blog"&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You could also append a new class name to an existing one like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;body class="body{if segment_1 == 'blog'} blog{/if}"&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The result:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;body class="body blog"&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Counting first and last
&lt;/h2&gt;

&lt;p&gt;You can use conditionals to do simple calculations based on numbers. ExpressionEngine entries make available variables for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;count&lt;/code&gt; (a running count of the entries being displayed, so the first entry will be “1”, the third entry would be “3”)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;total_results&lt;/code&gt; (the total number of entries being displayed)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You want to mark the first or second item:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{if count == 1}
This is the first one
{/if}

{if count == 2}
This is the second one
{/if}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You want to mark the last item:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{if count == total_results}
This is the last one
{/if}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can combine these when you need to output some HTML before and after your content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{if count == 1}

&amp;lt;div class="row"&amp;gt;
{/if}

  Content...

{if count == total_results}
&amp;lt;/div&amp;gt;

{/if}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using operators
&lt;/h2&gt;

&lt;p&gt;These allow you to compare things but in different ways. You can test for things that exist or don’t exist, less than or more than, and even look for specific words or phrases.&lt;/p&gt;

&lt;p&gt;So far I’ve only used the &lt;code&gt;==&lt;/code&gt; operator (equal to) in my examples. Here are a few more ways to compare values:&lt;/p&gt;

&lt;h3&gt;
  
  
  Check if something is not empty/blank
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;{if blog_exerpt != ""}The excerpt is not empty{/if}&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Where != “” is asking if the field is not empty - &lt;em&gt;not equals blank&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Check if something is empty/blank
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;{if blog_exerpt == ""}The excerpt is empty{/if}&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Where == “” is asking if the field is empty - &lt;em&gt;equals blank&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Check for a word or phrase in text
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;{if blog_exerpt *= "Tom"}The excerpt mentions Tom{/if}&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Where *= is asking if the text contains the word “Tom.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Check if something is less than x
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;{if product_stock &amp;lt; 10}Low stock{/if}&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Where &amp;lt; is asking if the value is &lt;em&gt;less&lt;/em&gt; than 10.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Check if something is more than x
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;{if product_stock &amp;gt; 10}We have loads in stock{/if}&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Where &amp;gt; is asking if the value is more than 10.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are many operators available for you to use. A full list is available in the &lt;a href="https://docs.expressionengine.com/latest/templates/conditionals.html"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting conditionals
&lt;/h2&gt;

&lt;p&gt;If your conditional tag doesn’t work, here are a few things to check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;is the syntax correct?&lt;/li&gt;
&lt;li&gt;have you missed the trailing &lt;code&gt;{/if}&lt;/code&gt; ?&lt;/li&gt;
&lt;li&gt;check spelling, missing or extra spaces&lt;/li&gt;
&lt;li&gt;is your tag in the right place in the template?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you still have problems, it’s always worth posting on the &lt;a href="https://expressionengine.com/blog/join-us-in-slack#join-us-on-slack"&gt;EECMS Slack channel&lt;/a&gt; or the &lt;a href="https://expressionengine.com/forums"&gt;ExpressionEngine forums&lt;/a&gt;. An experienced eye will usually help you solve it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summing up
&lt;/h2&gt;

&lt;p&gt;Conditionals can be hard to get your head around at first but stick with it. As soon as it clicks, you’ll find they can be incredibly powerful tools to help you create dynamic site content.&lt;/p&gt;

&lt;p&gt;-- &lt;/p&gt;

&lt;p&gt;Originally published by &lt;a href="https://u.expressionengine.com/author/rob-allen"&gt;Rob Allen&lt;/a&gt; at &lt;a href="https://u.expressionengine.com//article/a-primer-on-simple-conditionals"&gt;u.expressionengine.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>expressionengine</category>
      <category>eecms</category>
      <category>conditionals</category>
    </item>
    <item>
      <title>Contributing to ExpressionEngine Documentation</title>
      <dc:creator>ExpressionEngine</dc:creator>
      <pubDate>Fri, 16 Apr 2021 15:24:00 +0000</pubDate>
      <link>https://forem.com/eecms/contributing-to-expressionengine-documentation-o9a</link>
      <guid>https://forem.com/eecms/contributing-to-expressionengine-documentation-o9a</guid>
      <description>&lt;h1&gt;
  
  
  Contributing to the ExpressionEngine Documentation
&lt;/h1&gt;

&lt;p&gt;Good documentation is the foundation of good software and your contributions can help make good documentation great.&lt;/p&gt;

&lt;p&gt;If you’re an ExpressionEngine user, then you have something to add. There is always room for more examples, clarifications, corrections, and updates. Jump in and make a pull request or give us a heads up by reporting an issue. Since the docs are all in markdown formatting and updates should be pretty straightforward.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Workflow.
&lt;/h2&gt;

&lt;p&gt;Contributing to the docs requires a basic understanding of Git. Here’s an overview of the workflow&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fork the GitHub repository: &lt;a href="https://github.com/ExpressionEngine/ExpressionEngine-User-Guide"&gt;https://github.com/ExpressionEngine/ExpressionEngine-User-Guide&lt;/a&gt; . Read more about creating a fork of and working with a public repository here: &lt;a href="https://docs.github.com/en/github/getting-started-with-github/fork-a-repo#fork-an-example-repository"&gt;https://docs.github.com/en/github/getting-started-with-github/fork-a-repo#fork-an-example-repository&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Create a branch (see Branches below).&lt;/li&gt;
&lt;li&gt;Update your branch&lt;/li&gt;
&lt;li&gt;Submit a &lt;a href="https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests"&gt;Pull Request&lt;/a&gt;, requesting merging your branch into the &lt;code&gt;6.dev&lt;/code&gt; branch of the ExpressionEngine public repository.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Contributing to the Documentation
&lt;/h2&gt;

&lt;p&gt;All of the source files exist under &lt;code&gt;docs/&lt;/code&gt; and is where you will add new documentation or modify existing documentation. We recommend working from feature branches and making pull requests to the &lt;code&gt;#.dev&lt;/code&gt; branch of this repo (See Branches below).&lt;/p&gt;

&lt;p&gt;Suggesting a change is easy and there is no way you’ll mess up the public repo.&lt;/p&gt;

&lt;p&gt;Start by &lt;a href="https://help.github.com/articles/fork-a-repo"&gt;forking the repository&lt;/a&gt;, the new branch you create is the one you’ll do your work in.&lt;/p&gt;

&lt;p&gt;Once you have your new branch, confirm you’re actually in it! If you haven’t already, make sure you’ve read though the style guide. Then make your changes &lt;a href="https://help.github.com/articles/fork-a-repo"&gt;inside of your feature branch&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Push your changes to your fork of the repository, and when you’re done, &lt;a href="https://help.github.com/articles/using-pull-requests"&gt;send us a pull request&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We’ll take a look at your pull request, make sure everything looks alright, ask for any needed changes, and then merge it into the main code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Branches
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| Branch | Purpose |
| ------ | ------- |
| #.x | Currently released and published version.
| #.dev | Updates for the next version of ExpressionEngine (current version is the default branch). Does not exist for previous versions. |
** replace `#` with the current version of ExpressionEngine or version you wish to target.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Recommended branch names are namespaced and unique, e.g.:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;feature/my-feature-slug&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bug/bug-description-slug&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building and Reviewing the Docs Locally
&lt;/h2&gt;

&lt;p&gt;While some updates are easy and can just be done in markdown without much review, others require a review of what the updates will actually look like when published to the docs. To review changes, you’ll need to build the docs locally. This requires a little bit of setup but is not overly complicated if you’re already familiar with front-end build tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;Building the docs requires Node and npm.&lt;/p&gt;

&lt;p&gt;In the root of the repository, install all the dependencies: &lt;code&gt;npm install&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Building the Docs
&lt;/h3&gt;

&lt;p&gt;To build the docs: &lt;code&gt;npm run build&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To dynamically rebuild on any file changes: &lt;code&gt;npm run watch&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Building theme assets
&lt;/h3&gt;

&lt;p&gt;The documentation css and js files are located under &lt;code&gt;theme/assets-src&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To build the theme assets, run &lt;code&gt;npm run buildAssets&lt;/code&gt;. You can also dynamically rebuild the assets when a file changes: &lt;code&gt;npm run watchAssets&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As with anything else, if you have any trouble building the docs let us know and we’d be happy to help.&lt;/p&gt;

&lt;h2&gt;
  
  
  Style Guide
&lt;/h2&gt;

&lt;p&gt;Please read the &lt;a href="https://docs.expressionengine.com/latest/style-guide.html"&gt;style guide&lt;/a&gt; for samples and convention standards used in the ExpressionEngine user guide.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reporting Issues
&lt;/h2&gt;

&lt;p&gt;Want to help but a little daunted by the idea of making a pull request? We still want to hear from you. You can report errors in the documentation as &lt;a href="https://github.com/ExpressionEngine/ExpressionEngine-User-Guide/issues"&gt;issues in the github repo&lt;/a&gt;. Just remember, the more detailed the report, the more likely someone will be able to make improvements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks!
&lt;/h2&gt;

&lt;p&gt;Most importantly, THANK YOU! Thank you for caring about the community and wanting to help. ExpressionEngine is open source and has always been successful because of our community. Let us know if you have any problems with the docs or anything else and we’ll be happy to help.&lt;/p&gt;

&lt;p&gt;-- &lt;/p&gt;

&lt;p&gt;Originally published by &lt;a href="https://u.expressionengine.com/author/andy-mccormick"&gt;Andy McCormick&lt;/a&gt; at &lt;a href="https://u.expressionengine.com//article/contributing-to-expressionengine-documentation"&gt;u.expressionengine.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>expressionengine</category>
      <category>quicktips</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Using Mailgun to Send Emails from your Website</title>
      <dc:creator>ExpressionEngine</dc:creator>
      <pubDate>Tue, 13 Apr 2021 22:26:00 +0000</pubDate>
      <link>https://forem.com/eecms/using-mailgun-to-send-emails-from-your-website-3ka6</link>
      <guid>https://forem.com/eecms/using-mailgun-to-send-emails-from-your-website-3ka6</guid>
      <description>&lt;p&gt;&lt;a href="https://www.mailgun.com"&gt;Mailgun’s&lt;/a&gt; &lt;a href="https://www.mailgun.com/smtp/"&gt;SMTP Relay service&lt;/a&gt; is a quick an easy way to have ExpressionEngine transactional emails handled through a dedicated email provider. There’s just one tiny glitch you need to be aware of. The line ending setting needs to be changed from the default \n to \r\n. That’s it. Everything else is self-evident. But in case it’s not…&lt;/p&gt;

&lt;p&gt;In your Mailgun account, go to the Overview for your domain. In my case, I just have their default sandbox domain. You’re going to select ‘SMTP’ as your mail option and the required settings, including username and password, will be revealed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G8YxFs6b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://u.expressionengine.com/asset/article-images/mailgun-settings.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G8YxFs6b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://u.expressionengine.com/asset/article-images/mailgun-settings.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go to your ‘&lt;a href="https://docs.expressionengine.com/latest/control-panel/settings/email.html#settings"&gt;Settings- Outgoing Email&lt;/a&gt;’ settings in your ExpressionEngine control panel and enter your Mailgun settings&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Protocal&lt;/td&gt;
&lt;td&gt;SMTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Newline Character&lt;/td&gt;
&lt;td&gt;\r\n&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server Address&lt;/td&gt;
&lt;td&gt;smtp.mailgun.org&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server Port&lt;/td&gt;
&lt;td&gt;587&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connection Type&lt;/td&gt;
&lt;td&gt;STARTTLS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Save your settings and send a few test emails from the &lt;a href="https://docs.expressionengine.com/latest/control-panel/utilities.html#communicate"&gt;Communicate page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;-- &lt;/p&gt;

&lt;p&gt;Originally published by &lt;a href="https://u.expressionengine.com/author/robin-sowell"&gt;Robin Sowell&lt;/a&gt; at &lt;a href="https://u.expressionengine.com//article/using-mailgun-to-send-emails-from-your-website"&gt;u.expressionengine.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>quicktips</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Add commenting to your articles</title>
      <dc:creator>ExpressionEngine</dc:creator>
      <pubDate>Thu, 28 Jan 2021 22:10:00 +0000</pubDate>
      <link>https://forem.com/eecms/add-commenting-to-your-articles-2inl</link>
      <guid>https://forem.com/eecms/add-commenting-to-your-articles-2inl</guid>
      <description>&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Adding a comment section to your articles and posts gives your reader a chance to add value to what you’ve written – or draw your attention to a necessary correction or update. If you’ve struggled with integrating comments to your site content, we’re here to help!&lt;/p&gt;

&lt;p&gt;This tutorial assumes that you already have a basic site set up, with both single-entry article pages and some sort of index page that lists all of your articles. If you run into trouble with this tutorial, the Expression Engine documentation is likely a great resource.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enable the Comments Module
&lt;/h2&gt;

&lt;p&gt;The first thing you need to do is enable the Comments module; it’s not with the other add-ons where you think it might be.&lt;/p&gt;

&lt;p&gt;Head to the Expression Engine system settings page, and click on Comment Settings, at the following URL: /admin.php/cp/settings/comments&lt;/p&gt;

&lt;p&gt;Flip the “Enable comment module?” toggle to on and hit save.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tdAufO4B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://u.expressionengine.com/asset/article-images/hop_comments_image1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tdAufO4B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://u.expressionengine.com/asset/article-images/hop_comments_image1.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Enable Commenting on the Channel
&lt;/h2&gt;

&lt;p&gt;Next, you’ll need to enable commenting for the specific Channel you want to have comments on. Head to the Channel Manager. /admin.php?/cp/channels Inside the Channel Manager, click on the Settings tab for the Channel where you wish to allow commenting, and look for the Commenting section shown in the screenshot below. You’ll find it way down near the bottom of the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tauRnZHv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://u.expressionengine.com/asset/article-images/hop_comments_image2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tauRnZHv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://u.expressionengine.com/asset/article-images/hop_comments_image2.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Flip “Allow Comments?” to the “On” position, and keep in mind that you’ll need to do this for every Channel for which you want comments enabled.&lt;/p&gt;

&lt;p&gt;There are a few other helpful settings here which we’ll go over, but if you just want the basics you can skip to the next section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other Channel Commenting Settings
&lt;/h3&gt;

&lt;p&gt;Allow comments default - Each individual entry can have comments turned on or off. This option determines if new entries in this channel will have commenting turned on. Require Email - This does what it sounds like. If enabled, anyone submitting a comment must be either logged in or provide an email address. Important note: If this is turned on, you must have an email field in your comment submission form! Require Membership - A stricter version of Require Email. With this option, users must be registered members of the site to comment. Render URLs and Email addresses as links? - When members submit comments with URLs or email addresses, this setting controls if those appear as clickable links. You may wish to disable this, depending on your particular usability requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enable Commenting by Member Roles
&lt;/h2&gt;

&lt;p&gt;Next up, check to ensure you’ve given appropriate member roles permission to comment. Super Admins automatically have all permissions, so testing is sometimes confusing. It’s a good idea to create a test user for yourself in a standard Member or Editor role to verify your permissions are working correctly.&lt;/p&gt;

&lt;p&gt;Member Roles can be found by clicking on the Members menu item and the Member Groups submenu, or at this URL: /admin.php?/cp/members/roles&lt;/p&gt;

&lt;h3&gt;
  
  
  EE5 Note:
&lt;/h3&gt;

&lt;p&gt;If you’re following this guide for Expression Engine v5, you will follow essentially the same steps, but the permission is found under the Member Groups settings, rather than Member Roles. The control panel URL you will want for EE5 is: /admin.php?/cp/members/groups&lt;/p&gt;

&lt;p&gt;For each Member Role that you want commenting enabled, look for the Commenting section under the Website Access tab. Flip the toggle on for “Submit Comments.”&lt;/p&gt;

&lt;p&gt;If you want Guests (users who are not logged in) to be able to comment, be sure to enable this for the Guest member role – that’s the default anyway.&lt;/p&gt;

&lt;p&gt;If you have a special Member Role who should be immune to comment moderation, you can give them the “Bypass moderation” permission. This is also where you can allow specific Member Roles to edit or delete their own comments, or the comments of others if you wish.&lt;/p&gt;

&lt;p&gt;Important note: Enabling the “Edit comments by others” setting doesn’t automatically make the interface to allow this appear on the front end – this is just granting permissions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eBP76i24--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://u.expressionengine.com/asset/article-images/hop_comments_image3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eBP76i24--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://u.expressionengine.com/asset/article-images/hop_comments_image3.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  CAPTCHA or no CAPTCHA?
&lt;/h2&gt;

&lt;p&gt;Another comment-related setting you can optionally enable is CAPTCHA, the test to try and make sure someone is a human and not a spambot.&lt;/p&gt;

&lt;p&gt;The settings for CAPTCHA can be found on the System Settings page, under the CAPTCHA submenu at this URL: /admin.php?/cp/settings/captcha&lt;/p&gt;

&lt;p&gt;Keep in mind that enabling CAPTCHA here will enable it for all front-end forms, not just for comments. I like to enable it, but then I disable “Require CAPTCHA while logged in.” We assume that registered members can be trusted at least at this level, and this way only Guests will face the CAPTCHA.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--s0lA2YxC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://u.expressionengine.com/asset/article-images/hop_comments_image4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--s0lA2YxC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://u.expressionengine.com/asset/article-images/hop_comments_image4.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Display Comments with an Article
&lt;/h2&gt;

&lt;p&gt;That’s it for the set up work! Stretch your fingers, grab a tea, and now we can move on to the template work!&lt;/p&gt;

&lt;p&gt;In the template for your single-entry article page, you’ll need to enter template code to display comments. This tag pair is dynamic by nature, and by default will fetch the correct entry automatically based on the current URL.&lt;/p&gt;

&lt;p&gt;Important note: This code must be placed OUTSIDE of any exp:channel:entries loop, because it uses some of the same variables, including {count}. If you do need to put it inside the loop, you must use an embed.&lt;/p&gt;

&lt;p&gt;At its most basic, the comment entries loop looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{exp:comment:entries sort="asc" limit="20"}
  {comment}
  &amp;lt;p&amp;gt;By {author} on {comment_date format="%Y %m %d"}&amp;lt;/p&amp;gt;
{/exp:comment:entries}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We won’t go into a full description of the ways you can output and format comments – it’s quite robust.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Comment Submission Form
&lt;/h2&gt;

&lt;p&gt;The most important piece of the puzzle is the submission form. After all, there’s no point in displaying comments if no one can submit them.&lt;/p&gt;

&lt;p&gt;Place the following code directly beneath the comment:entries loop we posted above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{exp:comment:form channel="news"}

  {if logged_out}
    &amp;lt;label for="name"&amp;gt;Name:&amp;lt;/label&amp;gt; &amp;lt;input type="text" name="name" value="{name}" size="50" /&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;label for="email"&amp;gt;Email:&amp;lt;/label&amp;gt; &amp;lt;input type="text" name="email" value="{email}" size="50" /&amp;gt;&amp;lt;br /&amp;gt;
  {/if}

  &amp;lt;label for="comment"&amp;gt;Comment:&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;
  &amp;lt;textarea name="comment" cols="70" rows="10"&amp;gt;{comment}&amp;lt;/textarea&amp;gt;

  {if captcha}
    &amp;lt;label for="captcha"&amp;gt;Please enter the word you see in the image below:&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;p&amp;gt;{captcha}&amp;lt;br /&amp;gt;
    &amp;lt;input type="text" name="captcha" value="{captcha_word}" maxlength="20" /&amp;gt;&amp;lt;/p&amp;gt;
  {/if}

  &amp;lt;input type="submit" name="submit" value="Submit" /&amp;gt;

{/exp:comment:form}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are a few specific things to watch for in the comment submission code.&lt;/p&gt;

&lt;p&gt;First, be sure to change the channel parameter to the name of your Channel.&lt;/p&gt;

&lt;p&gt;Second, notice that we are asking for an Email address if the user is not logged in, but skipping it if they are. This is linked back to the earlier point about the “Require Email” and “Require Membership” options found under Channel Settings.&lt;/p&gt;

&lt;p&gt;The third thing to notice here is the {if captcha} line. If CAPTCHA is turned on, and the user is not exempt from CAPTCHA, a standard CAPTCHA will appear in this area of the form. You can find out more about how to customize CAPTCHA in the Expression Engine documentation.&lt;/p&gt;

&lt;p&gt;Once this is in place, you should see your comment form on the front end, and be able to submit new comments. Hurray!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n1nZQcNE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://u.expressionengine.com/asset/article-images/hop_comments_image5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n1nZQcNE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://u.expressionengine.com/asset/article-images/hop_comments_image5.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lastly, if for any reason the person viewing the page does not have commenting permission (either because of member, entry, or channel settings), the form will simply not display. You can use {if comments_disabled} to display alternate instructions if there will be some entries in each status.&lt;/p&gt;

&lt;p&gt;The display of existing comments is possible whether comments are enabled or not – once an entry has comments, disabling comments will only disable the form, not the comments you’ve already collected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Editing Comments
&lt;/h2&gt;

&lt;p&gt;ExpressionEngine does a decent job of trying to let you edit existing comments via Javascript. We recommend following the instructions in the documentation. However, because each site is unique, it may require some tweaking to their JavaScript. The important thing to note is that before you spend too much time digging into the javascript and templates, save yourself some time and check to make sure your permissions are set properly.&lt;/p&gt;

&lt;p&gt;--&lt;br&gt;
Originally published by &lt;a href="https://u.expressionengine.com/author/michael-kant"&gt;Michael Kant&lt;/a&gt; at &lt;a href="https://u.expressionengine.com//article/add-commenting-to-your-articles"&gt;u.expressionengine.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>expressionengine</category>
      <category>eecms</category>
    </item>
    <item>
      <title>Custom Global Variables Using the Config File</title>
      <dc:creator>ExpressionEngine</dc:creator>
      <pubDate>Mon, 03 Jun 2019 15:13:00 +0000</pubDate>
      <link>https://forem.com/eecms/custom-global-variables-using-the-config-file-56bg</link>
      <guid>https://forem.com/eecms/custom-global-variables-using-the-config-file-56bg</guid>
      <description>&lt;p&gt;One thing I like to do is take advantage of &lt;code&gt;global_vars&lt;/code&gt; in template tags. I’m a big fan of keeping configuration in the site’s &lt;code&gt;config.php&lt;/code&gt; file. That way, values can be changed depending on the environment either by using something like Ansible to provision and configure your site, or by using PHP’s &lt;code&gt;getenv()&lt;/code&gt; functions. For additional reading on environment based configuration check out &lt;a href="https://12factor.net/config"&gt;The Twelve-Factor App&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is an example of what is included in nearly all of my ExpressionEngine site’s &lt;code&gt;config.php&lt;/code&gt; files. Each variable is prefixed with &lt;code&gt;global:&lt;/code&gt;, similar to how we prefix the &lt;code&gt;pre:&lt;/code&gt; variables &lt;a href="https://u.expressionengine.com/article/preload-replace-variables"&gt;in my previous article&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$default_global_vars = [
    'global:disabled_params' =&amp;gt; 'disable="trackbacks|pagination|member_data"',
    'global:disabled_params_all' =&amp;gt; 'disable="trackbacks|pagination|member_data|category_fields|categories|custom_fields"',
    'global:disabled_params_strict' =&amp;gt; 'disable="trackbacks|pagination|member_data|category_fields|categories"',
    'global:blog_category_groups' =&amp;gt; '2|3|5|6',
    'global:some_api_key' =&amp;gt; '98ufahaskdfnasd312',
];

if (!isset($assign_to_config['global_vars'])) {
    $assign_to_config['global_vars'] = [];
}

$assign_to_config['global_vars'] = array_merge(
    $default_global_vars,
    $assign_to_config['global_vars']
);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now in my templates I can shorten my tags, and globally update every entries tag on the site with a single change in the &lt;code&gt;config.php&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{exp:channel:entries {global:disabled_params}}
    ...
{/exp:channel:entries}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some people like to prefix everything. I’ve seen custom fields prefixed with &lt;code&gt;cf_&lt;/code&gt;, thus their templates are filled with &lt;code&gt;{cf_body}&lt;/code&gt;, &lt;code&gt;{cf_summary}&lt;/code&gt;, or &lt;code&gt;{cf_related_entries}&lt;/code&gt;. I’ve personally never been a fan of prefixing field variables unless it is a prefix which helps indicate what channel it belongs to, such as &lt;code&gt;{person_first_name}&lt;/code&gt;. &lt;code&gt;{cf_person_first_name}&lt;/code&gt; feels redundant to me, but again, that is personal preference.&lt;/p&gt;

&lt;p&gt;Prefixing or no prefixing, find what works for you, just pick a pattern and be consistent 😊&lt;/p&gt;

&lt;p&gt;--&lt;br&gt;
Originally published by &lt;a href="https://u.expressionengine.com/author/brian-litzinger"&gt;Brian Litzinger &lt;/a&gt; at &lt;a href="https://u.expressionengine.com//article/custom-global-variables-using-the-config-file"&gt;u.expressionengine.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>expressionengine</category>
      <category>quicktips</category>
      <category>intermediate</category>
    </item>
  </channel>
</rss>
