Importing DataSets via the API

If you are using Data Sets in your editable templates, you can now allow Data Sets manipulation through our API.
You can retrieve, update, create and delete any Data Set or any of its elements, called Data Points.

What are datasets?

DataSets are used in Editable Product templates. They are used to group sets of values (DataPoints) for selection by users during the ordering process. Each DataSet belongs to a specific company and can have one or more DataPoints which users can select from a dropdown when they fill in the template fields. Each DataPoint contains up to 9 separate columns that can store strings that would appear in the final product.

Retrieving data from API is relatively easy. All you have to do is to navigate to the desired endpoint and you get a list of all objects currently present in the system.

Request:

curl "https://api-domain.printjob.com/v2/dataSets" -u "sk_test_key":

Response:

{
  "object": "list",
  "count": 2,
  "data": [
    {
      "object": "data_set",
      "attributes": {
        "company": "company_YEkjyNa0q6GXMlR83Bm9",
        "name": "Office Addresses"
    },
    "id": "data_set_8lwpXy6JRgdDLYmBaKO9",
    "test_environment": true
    },
    {...}
  ],
  "links": []
}

Once you get your list you may want to update one or more of the objects. This is where new functionality comes in. PrintJob API is built around RESTful approach, so to affect a change in the contents of the object you will need to switch to a ‘PUT’ verb in your HTTP request.

Let us assume that first object from the truncated list above is the one that interests us. We want to change its name to ‘Company Addresses’. Let us try that.

Request:

curl -X PUT -d "attributes[company]=company_YEkjyNa0q6GXMlR83Bm9&attributes[name]=Company Addresses" "https://api-domain.printjob.com/v2/dataSets/data_set_8lwpXy6JRgdDLYmBaKO9" -u "sk_test_key":

Response:

{
  "object": "data_set",
  "attributes": {
    "company": "company_YEkjyNa0q6GXMlR83Bm9",
    "name": "Company Addresses"
  },
  "id": "data_set_8lwpXy6JRgdDLYmBaKO9",
  "test_environment": true
}

Let’s walk through the example above.

-X PUT flag sets our request verb to PUT, which tells API that we want to modify an existing record
-d “…” contains a list of all attributes of the model with the values we chose to change.
NOTE: Remember that you need to send an entire object, with all of its attributes you don’t want to change prefilled with current values as any missing attributes will be set to empty.
URL part contains an URL to dataSets endpoint and an ID of an object we want to modify (data_set_8lwpXy6JRgdDLYmBaKO9 in this case)
-u is the authentication part where username is (the part before the colon) is your API key, and password (the part after the colon) is empty.

Request returns an updated object which you can immediately use, modify again or even delete. As you can see it’s ID remained the same while values of desired attributes have changed.

If you would like to add a new DataSet then you need to use POST verb and send the data in a very same way as in case of the PUT request.  Check out the example below:

Request:

curl -X POST -d "attributes[company]=company_YEkjyNa0q6GXMlR83Bm9&attributes[name]=Home Addresses" "https://api-domain.printjob.com/v2/dataSets" -u "sk_test_key":

Response:

{
  "object": "data_set",
  "attributes": {
    "company": "company_YEkjyNa0q6GXMlR83Bm9",
    "name": "Company Addresses"
  },
  "id": "data_set_q21ZlAJGR27Dzd6Pxpnr",
  "test_environment": true
}

Please notice the differences from the previous example.
In the request, we do not supply the object ID as it does not yet exist. We send our data directly to DataSet endpoint and let it do its magic. The ID of a newly created DataSet will be included in the response.

In the case of the DataSet, there are two attributes – company and name. Company is a reference to a company present in your system and takes the form of that company’s ID. Name is just a string that will describe the DataSet. Both are required, and if you omitted any of them your request would fail and return an error describing the problem. Please refer to the documentation to see all the required attributes of objects you are working with.

If you then wanted to remove one of your DataSets you could use DELETE verb in your request. Example follows:

Request:

curl -X DELETE "https://api-domain.printjob.com/v2/dataSets/data_set_q21ZlAJGR27Dzd6Pxpnr" -u "sk_test_key":

Response:

{
  "code": 200,
  "message": "Success"
}

As you can see this one is pretty straightforward. All you need is an ID of the DataSet you want to remove and a DELETE verb. Use it with care as removed items can not be restored.

NOTE: Removing DataSet with associated DataPoints will remove those DataPoints as well without any additional checks or questions. Always ensure that you are deleting correct objects and don’t try to guess the IDs of objects to delete!

DataPoints

Each DataSet contains one or (usually) more DataPoints that allow users to choose values they want to appear on their products. To retrieve and manipulate DataPoints we use the same techniques as described for the DataSets above.

Just like with DataSets belonging to a Company, DataPoints belong to a specific DataSet. To create a new DataPoint for our DataSet we would follow a similar path as we did with DataSets.

Request:

curl -X POST -d "attributes[name]=Main Office&attributes[sort_order]=1&attributes[value_1]=10 Some Street&attributes[value_3]=Anytown&attributes[value_4]=AN12YT&attributes[data_set]=data_set_8lwpXy6JRgdDLYmBaKO9" "https://api-domain.printjob.com/v2/dataPoints" -u "sk_test_test":

Response:

{
  "object": "data_point",
  "attributes": {
    "data_set": "data_set_8lwpXy6JRgdDLYmBaKO9",
    "name": "Main Office",
    "sort_order": "1",
    "value_1": "10 Some Street",
    "value_2": "",
    "value_3": "Anytown",
    "value_4": "AN12YT",
    "value_5": "",
    "value_6": "",
    "value_7": "",
    "value_8": "",
    "value_9": ""
  },
  "id": "data_point_wJq6G5l4xR0XzovWNeL1",
  "test_environment": true
}

As you can see, we sent only the attributes we wanted to hold a value. Other attributes were set to empty. DataSet is a required field and needs to hold an ID of an existing DataSet.

Modifying and deleting DataPonts is very similar to DataSets and if in doubt you can consult our documentation for specific examples.

Email SPF Records can be confusing.

The most important thing to know is that there can only be one SPF record per domain so if you have more than one you need to join them.

Here is what you should do if you are merging 2 SPF records (ie adding PrintJob to Microsoft outlooks SFP)

Change the Host: as outlined in red and add another include:spf also marked in red.

Host: @

TXT Value:  v=spf1 include:spf.protection.outlook.com include:spf-s1.printjob.com ~all

 

Information about what SPF data needs to be added can be found when you click Authenticate Email. These instructions vary depending on where you are based in the world but overall they look something like this. screenshot-admin-demo.printjob.com-2019.09.16-13_02_02

SPF records take time to propagate so be a little patient.

If Host: is giving you trouble you can replace your domain with @ and it should work.

 

 

Adding SPF, DKIM and DMARC records

SPF, DKIM and DMARC are important for verifying the sender address used in emails. When the three methods are used together, the likelihood that the email will be seen by the recipient increases, because these are internationally agreed standards used by most anti spam filters.

This is what you need to achieve before your email will be used as the sender. 4 green ticks.

screenshot-admin-demo.printjob.com-2019.09.16-12_55_12

 

 

 

The method for adding all three is almost identical – here is an example of adding a DMARC record to a domain name held in a Godaddy Account. The first image shows some instructions provided in the PrintJob System:

 

Setting up DMARC

Login to Godaddy – navigate to manage domains and click on Manage DNS (here you can add you SPF, DKIM and DMARC records all at once)

screenshot-dcc.godaddy.com-2019.09.16-10_22_02

Then add a new record

screenshot-dcc.godaddy.com-2019.09.16-09_54_47

Then select TXT record

screenshot-dcc.godaddy.com-2019.09.16-09_55_49

Enter values provided by PrintJob

screenshot-dcc.godaddy.com-2019.09.16-09_57_26 (1)

Click save and you are done.

Normally, it will start working after only a short wait, but in some cases you may have to wait up to 24hrs for the change to propagate.

Xero

Orders created in PrintJob can be automatically created as invoices in your Xero accounting software.

Here is how to set it up.

Xero1

Then activate Xero and click Setup.

There are three pages of setup.

On the first page follow the instructions. You will be asked to open a link to Xero and the link will take you to a page like this. (if you need to verify your email with Xero then do so and re-click the link on the PrintJob page)

screenshot-developer.xero.com-2019.03.05-16-29-34

Click the ‘New App’

You will see a popup like this in Xero.

screenshot-developer.xero.com-2019.03.05-17-22-53

 

Fill out the form and click create app.

You will then see the following page in Xero, copy and paste the keys into PrintJob

screenshot-developer.xero.com-2019.03.05-17-32-30

Then you are ready to move to the next page on PrintJob. Select what kind of invoice you want created and all the relevant accounts you wish to setup. Then on the last page you marry up the companies in Xero with those in PrintJob. After this all new orders will be created into invoices automatically.

Coming soon. All new companies created in PrintJob will be created automatically in Xero.

 

Aligning text items

You align text objects by assigning their XY co-ords to a point on the texts ‘virtual box’. If you imagine that the text is held in an invisible box, much like in Illustrator or Photoshop, you can determine how the box is laid onto the artwork by saying which bit sits on the XY.

Image 005

Image 006

List vs Range Pricing

You can offer a product at certain quantities (QTYs) or let users type in the number they want. Both allow QTY Price breaks, but both work slightly differently when it comes to calculating the price.

Acme Widgets might come in boxes/batches of 12. You would then offer 12, 24, 36 etc (List Price). For each QTY you would simply type in the price. So for 12 Acme Widgets it might be $56.

If these Acme Widgets are sold individually you can let the user type a QTY between 1 and say 500 (Range Price).  In this case you put into the system a UNIT PRICE. ie: each Acme Widget is $1.50. You only need to put the unit price in.

range_price

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

So to recap:

Range Pricing – where the user chooses any QTY number eg: 347, and the system calculates a price ie: 347 x unit price (0.1) = $34.7

List Pricing – where the user chooses one of the set QTYs and the price in the system is shown (ie: there is no UNIT PRICE and no calculations are made)

Google Analytics + Ecommerce Tracking

If you are interested in tracking activity on your system you can do it with Google Analytics. Its free and gives you a plethora of info to look at, such as where your users are, how long they spend on your site, which page they look at the most etc

Add your Google Analytics ID on the System Settings >  Appearance page.

screenshot-admin-demo.printjob.com-2018.04.13-10-35-09

Google Analytics works on all pages of the client side. You can also enable Ecommerce Tracking (in Google Analytics), allowing you to see all your web data in conjunction with data about orders (product, price, tax, shipping costs etc)

ecommerce setup

Some screenshots of the Google Analytics dashboard with Ecommerce enabled:

 desk ana

 

breakdown ana desk 2

GDPR – EU initiative for greater data transparency

The EU General Data Protection Regulation (GDPR) is an EU Directive (Law) which affects all companies (even outside the EU) holding data about people who live in the Europe. The basic idea is that if you hold such data you must state what you do with it and how you store it and relay it so it is easily understood by all and not buried in legal jargon. Such companies must also delete such data on request.

As part of meeting these requirements at PrintJob, we have added cookie acceptance on all systems and updated our Privacy PolicyCookie Policy and Terms & Conditions.

Just to reaffirm –  PrintJob will never share or sell data or information about our clients to anyone EVER.

Any data relating to our Clients (and their clients) that is held on our Systems is deemed to be the property and responsible of our Clients. PrintJob is not responsible for this content, and clients should be aware of their own GDPR obligations and limitations. For example, system (event triggered) emails are considered normal business, but if clients use the data they hold to send marketing or sales offers via email, then under GDPR stipulations EXPLICIT approval is required from each recipient to whom such offers are sent.

Datasets

Datasets are like mini databases which allow you to load data into editable text fields on templates. Normally you would load either User data, Department data or Address data, but where you want to load other data you can use Datasets.

dataset1

An example of this is where you want to allow users to choose a label (Tel, Fax or Email) by a drop down. You can make a little Dataset with those three values.

dataset2

Then add a dataloader which uses this dataset

dataset3

Make sure that your text object is set to load this data

dataset4

Finally – heres what it can look like for the End User.

dataset5

Dynamic Headers

PrintJob allows you to create a custom header that will reflect the look and feel of your website, via the HTML area of the Appearance>Header section (either in the Storefront Settings or in the Settings for a particular account).

However, if you uncheck the option on the appearance page ‘Show dynamic menu’. you will need to make your header DYNAMIC. That means, you can have the header show dynamic content, such as ‘Login’ or ‘Logout’ or a count icon over the basket. Also, the PJ system is ‘role based’ which means that different users get different page links.

header_explain2


To utilize these dynamic properties you will need to use javascript in your header that will manipulate your header content according to the data that the PrintJob System provides. Right before your header starts, and within the .header-wrapper div, a javascript object is being injected – it contains all information necessary for dynamic links that depend on the user privileges, their status and contents of their interface.

Here is a complete example of a dynamic header.

<style type="text/css">
    .header-wrapper .contact-bar {
        background-color: #66A931;
        transition: background-color 0.4s ease 0s, transform 0.4s ease 0s, opacity 0.4s ease-in-out 0s;
        line-height: 1em;
        color: #fff;
        font-size: 12px;
        line-height: 13px;
        overflow: auto;
        padding: 10px 0;
    }
    .header-wrapper .logo_container {
        height: 80px;
        text-align: center;
        width: 100%;
        
    }
    .header-wrapper .logo_container img {
        height: 40px;
    }

    .header-wrapper .navbar li {
        display: none;  
    }
    .header-wrapper sup {
        font-size: 50%;
        line-height: 133%;
        position: relative;
        top: -10px;
    }
    .header-wrapper .navbar {
        background-color: navy;
    }
    .header-wrapper .navbar a {
        color: lightblue;
    }
    .header-wrapper .navbar a:hover {
        color: lightcyan;
    }

    .navbar .navbar-toggler div
    {
        display: block;
        width: 33px;
        height: 5px;
        margin-bottom: 5px;
        position: relative;
        background: lightblue;
        border-radius: 3px;
        z-index: 1;
        transform-origin: 4px 0px;
        transition: transform 0.5s cubic-bezier(0.77,0.2,0.05,1.0), background 0.5s cubic-bezier(0.77,0.2,0.05,1.0), opacity 0.55s ease;
    }
    .navbar .navbar-toggler:hover div {
        background: lightcyan;
    }
    .navbar .navbar-toggler div:first-of-type {
        margin-top: 5px;
    }
</style>

<div class="row contact-bar">
    <div class="container">
        <div class="row">
            <div class="col">
                <span><strong>T: </strong>+44 20 8144 2778</span> <span><strong>E: </strong>info@printjob.com</span>            
            </div>
            <div class="col text-right">
                <span id="header-wrapper-login-link">
                    <a class='badge badge-pill badge-secondary' id="login-link" href=""><span class="my-icon"></span> <span class='text'>Login</span></a>
                </span>
            </div>
        </div>
    </div>
</div>
<div class="row">
    <div class="logo_container">
        <span class="logo_helper"></span>
        <br>
        <img src="https://printjob.com/wp-content/uploads/2016/01/printjob-logo-dark.png">
    </div>
</div>
<div class="row" id="header-wrapper-menu">
    <div class="container">
        <nav class="navbar navbar-expand-md">
            <button class="navbar-toggler collapsed" type="button" data-toggle="collapse" data-target="#custom-navbar" aria-controls="custom-navbar" aria-expanded="false" aria-label="Toggle navigation">
                <div></div>
                <div></div>
                <div></div>
            </button>
            <div class="collapse navbar-collapse" id="custom-navbar">
                <ul class="navbar-nav">
                    <li  id="header-wrapper-products-link" class="nav-item">
                        <a  class="nav-link" href="/product/catalogue">Product Catalogue</a>           
                    </li>
                    <li id="header-wrapper-rfq-link" class="nav-item">
                        <a  class="nav-link" href="/Rfq/Manager/create">RFQ</a>                
                    </li>
                </ul>
                <ul class="navbar-nav ml-auto">
                    <li id="header-wrapper-orders-link" class="nav-item">
                        <a class="nav-link"><span class="my-icon"></span></a>                
                    </li>
                    <li id="header-wrapper-quotes-link" class="nav-item">
                        <a title="Quotes" class="nav-link" href="/Rfq/manager/user"><span class="my-icon"></span></a>                    
                    </li>
                    <li id="header-wrapper-users-link" class="nav-item">
                        <a title="Users" class="nav-link" href="/manager/users"><span class="icon-users"></span> Users</a>                    
                    </li>
                    <li id="header-wrapper-addresses-link" class="nav-item">
                        <a title="Addresses" class="nav-link" href="/user/addresses"><span class="icon-addresses"></span> Addresses</a>                    
                    </li>
                    <li id="header-wrapper-designs-link" class="nav-item">
                        <a class="nav-link"><span class="my-icon"></span></a>
                    </li>
                    <li id="header-wrapper-stock-link" class="nav-item">
                        <a class="nav-link"><span class="my-icon"></span></a>
                    </li>
                    <li id="header-wrapper-budget-info" class="nav-item">
                         <a class="nav-link disabled" disabled><span class="my-icon"></span></a>
                    </li>
                    <li id="header-wrapper-profile-link" class="nav-item">
                        <a class="nav-link"><span class="icon-user"></span> Profile</a>                
                    </li>
                    <li id="header-wrapper-basket-link" class="nav-item">
                        <a class="nav-link" title="Shopping Basket" href="/basket/basket"><span class="icon-shopping-cart"></span></a>            
                    </li>
                </ul>
            </div>
        </nav>
    </div>
</div>
<script type="text/javascript">
    //Links
    $('#header-wrapper-rfq-link a').prop('href', pj_header_data.rfq.link);
    $('#header-wrapper-orders-link a').prop('href', pj_header_data.orders.link);
    $('#header-wrapper-quotes-link a').prop('href', pj_header_data.quotes.link);
    $('#header-wrapper-designs-link a').prop('href', pj_header_data.saved_designs.link);
    $('#header-wrapper-profile-link a').prop('href', pj_header_data.profile.link);
    $('#header-wrapper-stock-link a').prop('href', pj_header_data.replenish_stock.link);
    $('#header-wrapper-basket-link a').prop('href', pj_header_data.basket.link);
    $('#header-wrapper-users-link a').prop('href', pj_header_data.users.link);
    $('#header-wrapper-addresses-link a').prop('href', pj_header_data.addresses.link);
    $('#header-wrapper-login-link a').prop('href', pj_header_data.login.link);

    //Logout button functionality
    if (pj_header_data.login.state == 1) {
        $("#header-wrapper-login-link a .text").text('Logout');
    }

    //Show links
    if (pj_header_data.products.show == 1) {
        $('#header-wrapper-products-link').css({'display': 'inline-block'});
    }
    if (pj_header_data.rfq.show == 1) {
        $('#header-wrapper-rfq-link').css({'display': 'inline-block'});
    }
    if (pj_header_data.basket.show == 1) {
        $('#header-wrapper-basket-link').css({'display': 'inline-block'});
    }
    if (pj_header_data.orders.show == 1) {
        $('#header-wrapper-orders-link').css({'display': 'inline-block'});
    }
    if (pj_header_data.quotes.show == 1) {
        $('#header-wrapper-quotes-link').css({'display': 'inline-block'});
    }
    if (pj_header_data.saved_designs.show == 1) {
        $('#header-wrapper-designs-link').css({'display': 'inline-block'});
    }
    if (pj_header_data.profile.show == 1) {
        $('#header-wrapper-profile-link').css({'display': 'inline-block'});
    }
    if (pj_header_data.replenish_stock.show == 1) {
        $('#header-wrapper-stock-link').css({'display': 'inline-block'});
    }
    if (pj_header_data.users.show == 1) {
        $('#header-wrapper-users-link').css({'display': 'inline-block'});
    }
    if (pj_header_data.addresses.show == 1) {
        $('#header-wrapper-addresses-link').css({'display': 'inline-block'});
    }

    //Budget functionality
    if (pj_header_data.budgets.show == 1) {

        function objToString(obj) {
            var str = '';
            for (var p in obj) {
                if (obj.hasOwnProperty(p)) {
                    str += p + ': ' + obj[p] + '\n';
                }
            }
            return str;
        }

        $('#header-wrapper-budget-info').css({'display': 'inline-block'});
        $('#header-wrapper-budget-info a').append(pj_header_data.budgets.total_value);
        $('#header-wrapper-budget-info').attr('title', objToString(pj_header_data.budgets.budgets));
    }

    //Activate links
    if (pj_header_data.products.active == 1) {
        $('#header-wrapper-products-link').addClass('active');
    }
    if (pj_header_data.rfq.active == 1) {
        $('#header-wrapper-rfq-link').addClass('active');
    }
    if (pj_header_data.orders.active == 1) {
        $('#header-wrapper-orders-link').addClass('active');
    }
    if (pj_header_data.quotes.active == 1) {
        $('#header-wrapper-quotes-link').addClass('active');
    }
    if (pj_header_data.saved_designs.active == 1) {
        $('#header-wrapper-designs-link').addClass('active');
    }
    if (pj_header_data.basket.active == 1) {
        $('#header-wrapper-basket-link').addClass('active');
    }
    if (pj_header_data.profile.active == 1) {
        $('#header-wrapper-profile-link').addClass('active');
    }
    if (pj_header_data.replenish_stock.active == 1) {
        $('#header-wrapper-stock-link').addClass('active');
    }
    if (pj_header_data.users.active == 1) {
        $('#header-wrapper-users-link').addClass('active');
    }
    if (pj_header_data.addresses.active == 1) {
        $('#header-wrapper-addresses-link').addClass('active');
    }

    //Counters
    $('#header-wrapper-basket-link a').append('<sup class="badge badge-info badge-pill">' + pj_header_data.basket.count + '</sup>');
    if (pj_header_data.orders.count > 0) {
        $('#header-wrapper-orders-link a').append('<sup class="badge badge-info badge-pill">' + pj_header_data.orders.count + '</sup>');
    }
    if (pj_header_data.quotes.count > 0) {
        $('#header-wrapper-quotes-link a').append('<sup class="badge badge-info badge-pill">' + pj_header_data.quotes.count + '</sup>');
    }
    if (pj_header_data.saved_designs.count > 0) {
        $('#header-wrapper-designs-link a').append('<sup class="badge badge-info badge-pill">' + pj_header_data.saved_designs.count + '</sup>');
    }

    //Icons 
    $('#header-wrapper-orders-link a').prepend('<span class="' + pj_header_data.orders.icon + '"></span>');
    $('#header-wrapper-quotes-link .my-icon').addClass(pj_header_data.quotes.icon);
    $('#header-wrapper-designs-link .my-icon').addClass(pj_header_data.saved_designs.icon);
    $('#header-wrapper-stock-info .my-icon').addClass(pj_header_data.replenish_stock.icon);
    $('#header-wrapper-budget-info .my-icon').addClass(pj_header_data.budgets.icon);
    $('#header-wrapper-profile-link .my-icon').addClass(pj_header_data.profile.icon);
    $('#header-wrapper-basket-link .my-icon').addClass(pj_header_data.basket.icon);
    $('#login-link .my-icon').addClass(pj_header_data.login.icon);
</script>

 

Learning from this example

The following section explains some of how this code is put together.

 

Its at this point that your IT expert should step in.

 

PrintJob Header Data Object

It’s important you are aware of these objects – they can be found by looking at the “source” of a storefront page when the default <system> header is enabled. You can refer to these objects in your custom header code.

Here is an example of such an object:

var pj_header_data = {
  products: {
    "show":1,
    "active":1,
    "link":"/product/catalogue",
    "icon":"icon-products"
  },
  rfq: {
    "show":0,
    "active":0,
    "link":"/Rfq/manager/create",
    "icon":"icon-quotes"
  },
  basket: {
    "show":1,
    "active":0,
    "count":2,
    "link":"/basket/basket",
    "icon":"icon-shopping-cart"
  },
  orders: {
    "show":1,
    "active":0,
    "count":0,
    "link":"/Order/manager/user",
    "icon":"icon-orders"
  },
  quotes: {
    "show":1,
    "active":0,
    "count":0,
    "link":"/Rfq/manager/user",
    "icon":"icon-quotes"
  },
  saved_designs: {
    "show":1,
    "active":0,
    "count":1,
    "link":"/user/savedForLater",
    "icon":"fas fa-save"
  },
  users: {
    "show":1,
    "active":0,
    "link":"/manager/users",
    "icon":"icon-users"
  },
  addresses: {
    "show":1,
    "active":0,
    "link":"/user/addresses",
    "icon":"icon-addresses"
  },
  replenish_stock: {
    "show":0,
    "active":0,
    "link":"/product/replenishment",
    "icon":"icon-"
  },
  profile: {
    "show":1,
    "active":0,
    "link":"/user/myProfile",
    "icon":"icon-user"
  },
  budgets: {
    "show":0,
    "active":0,
    "total_value":"£552.50",
    "icon":"icon-budget",
    "budgets": {
      "Purchase Budget":"£432.50",
      "Special":"120.00"
    }
  },
  login: {
    "link":"/site/logout",
    "state":1,
    "icon":"icon-logout"
  },
  language: {
    "language_code":"en_gb",
  }
}

WARNING: The above is only an example for you to study. Do NOT include the above in your header as it is automatically generated, based on who is logged in and what privileges they have. If you include the above you will override real values with static ones!

Description of the attributes:

link – appears in every object and describes a URI component (Uniform Resource Identifier) that should be used to reach a required page within the system.
show – available on every object, except for login and basket (those appear always). If its value is 0 it means that link should not be visible i.e. that user will not be able to use it due to their privileges or not being logged in etc.
active – appears on every object except for login. If the value is 1 it means that user is on a page that corresponds to the link. This value may be used to highlight a current page in your item.
count – present on several objects, namely basket, quotes, orders and saved_designs. If its present it will show how many items a corresponding entity contains i.e. 4 items in the basket or 2 unseen quotes.
login – this object is special, it has a unique property `state` which translates to 1 – the user is logged in, 0 – the user is not logged in.

We also have a special object called ‘language‘. It has only one value – ‘language_code’ which shows which language your user is using in the system. This will be helpful in providing localized content if your system runs multiple languages.

Now… about the example code:

Styles

The first section is responsible for all the styles that manage the aesthetic look of the header.

<style type="text/css">
  .header-wrapper #top-header #et-secondary-menu {
    float: right;
  } 
  .header-wrapper #top-header #et-info-phone::before {
    content: "T:";
    margin-right: 4px;
  }
  [...]
</style>

Please note, that every style starts with

.header-wrapper

class, which ensures that whatever names of classes or selectors you are going to use you are not going interfere with the rest of the site. A notable recurring example is using `p` selector to change the text colour in the header. E.g.:

p {color: white; } // BAD: this will make text white in the whole of the page, not only the header, and some text inside PrintJob will become white on white, while
.header-wrapper p { color: white} // GOOD: will not affect anything outside of the header

HTML

<div id="top-header">
  [...]
</div>

Its up to you what you are going to put in here. All naming conventions are allowed. The entirety of the HTML you going to enter here will be wrapped by a div with `header-wrapper` class and as long as your styles are correctly named, you should be able to achieve your desired aesthetic result.

One thing to bear in mind is that if you want to use dynamic header functionalities (i.e. your own menu) then you have to be giving specific elements (like links) unique ids or classes so you can call them from the javascript section and enable automation.

In our examples we chose to use list elements as menu defining elements and gave them all unique IDs, like in the example below:

<li id="header-wrapper-products-link"><a href='/product/catalogue'>Product Catalogue</a></li>
[...]
<li id="header-wrapper-orders-link"><a href='/Order/manager/user' title="Orders"><span class="my-icon icon-orders"></span></a></li>
<li id="header-wrapper-quotes-link"><a><span class="my-icon icon-quotes"></span></a></li>
[...]
<li id="header-wrapper-budget-info"><span class="my-icon"></span></li>
<li id="header-wrapper-basket-link"><a><span class="my-icon"></span> Basket</a></li>

The above code shows several different approaches to building your links.

As you might have noticed, some link selectors `a` do not have the `href` property (which is normally required), and some do. This is to illustrate two possible approaches. One is to simply enter values manually (hardcode) and another is to allow adding of this property dynamically with javascript. Hardcoding is easier and less resource intensive. Dynamic attribution is more complex but more resilient i.e. if links were ever to change (that will not happen often if at all) then your header would still work without intervention.

You can include PrintJob supplied icons by hardcoding them into a ‘span’ element or use javascript (as described in a separate section below) to apply icons in relevant places.

Another thing to notice is that we opted to use a text inside the ‘basket’ link and a title in ‘orders’ link.

It is up to you how you build your header – our example aims to show you some of the different options you have.

Javascript

<script type="text/javascript">
[...]
</script>

This section is optional – if you are using the system supplied dynamic header you wont need it.

This section is responsible for six functionalities:

  1. Setting link locations on your buttons
  2. Modifying text on the login/logout button, depending on whether the user is logged in or not
  3. Showing or hiding buttons depending on users privileges
  4. Budget functionality
  5. Adding active class to links that are active (user is on a page that link is pointing to) (optional)
  6. Displaying counters on relevant links (optional)
  7. Setting icons for links (optional)

All the information required to perform those tasks is available as a javascript object called ‘pj_header_data’ on every PrintJob page as described in a section above.

Here is an explanation of specific parts of the javascript.

1. Links association:
  $('#header-wrapper-rfq-link a').prop('href', pj_header_data.rfq.link);

This and similar lines are populating `href` attributes of link elements with values taken from the PrintJob system. You could hardcode those into your header but using dynamic allocation ensures that menu in your header will stay operational even if links will change for some reason, however unlikely that may be. If you prefer to hard-code the links then this whole section is optional.

2. Login button functionality

if (pj_header_data.login.state == 1) {
$(“#header-wrapper-login-link a .text”).text(‘Logout’);
}

If user is logged in (login.stat == 1) we change text to Logout.

3. Hiding and showing specific links:
  if (pj_header_data.orders.show == 1) {
    $('#header-wrapper-orders-link').css({'display':'inline-block'});   
  }

Each link has a property ‘show’ which determines if it should be shown so in our example we are using an ‘if’ conditional to determine if we want to make it visible (in our example all links that are transitional are hidden by default).

Two notable exceptions are Basket and Products link – although it has a ‘show’ flag, that flag is always 1, because both guests and users can see basket and product catalogue.

4. Budget functionality
  if (pj_header_data.budgets.show == 1) {
  
    function objToString (obj) {
      var str = '';
      for (var p in obj) {
          if (obj.hasOwnProperty(p)) {
              str += p + ': ' + obj[p] + '\n';
          }
      }
      return str;
    }
  
    $('#header-wrapper-budget-info').css({'display':'inline-block'});
    $('#header-wrapper-budget-info').append(pj_header_data.budgets.total_value);
    $('#header-wrapper-budget-info').attr('title', objToString(pj_header_data.budgets.budgets));
  }

This section of javascript is responsible for displaying and formatting the budgets information icon.

This icon is not a link, there is no page where detailed budget information is displayed for a user. Instead, all information is packed into this icon and its title attribute. Firstly, we only do all the formatting if the icon is supposed to be showing (i.e. if admin has activate the budgets feature for this user). If so, we make it visible and then we add the value of ‘total_value’ parameter after the icon (append line). Then we create a ‘title’ attribute for our budget icon with the stringified content of ‘budgets’ model (title line and objToString function).

Of course, you can choose to show it differently, we are illustrating how to achieve the same look as on the original PrintJob menu.

5. Activating links:
  if (pj_header_data.products.active == 1) {
    $('#header-wrapper-products-link').addClass('active');
  }

Each link has a property ‘active’ which take on value 1 when a user is on the page associated with the link. We use the same style ‘if’ conditional to determine if we should add ‘active’ class to the link. You can name the class however you wish and style it in ‘styles’ section of your header.

6. Displaying counters:
  $('#header-wrapper-basket-link a').append(' <span class="header-wrapper-counts">' + pj_header_data.basket.count + '</span>');
  if (pj_header_data.orders.count > 0) {
    $('#header-wrapper-orders-link a').append(' <span class="header-wrapper-counts">' + pj_header_data.orders.count + '</span>');
  }

Because in our example we always show the basket link, we also chose to show basket counter all the time, that is why there is no conditional in our example. In our code, we are appending a ‘span’ to basket link that is styled by ‘header-wrapper-counts’ class. The result is a coloured circle with a white number inside.

In case of the Orders counter, we only show it when there is at least one unseen order (just like in default PrintJob menu). That is why there is a conditional encapsulating append code.

7. Adding icons:
  $('#header-wrapper-basket-link .my-icon').addClass(pj_header_data.basket.icon);
  $('#login-link .my-icon').addClass(pj_header_data.login.icon);

You can add your own icons in HTML without using any of the PrintJob icons. In such case feel free to ignore this section. Otherwise, if you wish to use PrintJob supplied icons, every link has an ‘icon’ property which specifies a name of the class that needs to be added to a span element to render an icon corresponding with the link. You can either hard code it or use the javascript similar to the above to accomplish the same goal.