Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PubCommonId - Add support for localStorage #3661

Merged
merged 2 commits into from
Apr 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 170 additions & 22 deletions modules/pubCommonId.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,126 @@
import * as utils from '../src/utils'
import { config } from '../src/config';

const COOKIE_NAME = '_pubcid';
const DEFAULT_EXPIRES = 2628000; // 5-year worth of minutes
const ID_NAME = '_pubcid';
const OPTOUT_NAME = '_pubcid_optout';
const DEFAULT_EXPIRES = 525600; // 1-year worth of minutes
const PUB_COMMON = 'PublisherCommonId';
const EXP_SUFFIX = '_exp';
const COOKIE = 'cookie';
const LOCAL_STORAGE = 'html5';

var pubcidEnabled = true;
var interval = DEFAULT_EXPIRES;
let pubcidConfig = {
enabled: true,
interval: DEFAULT_EXPIRES,
typeEnabled: LOCAL_STORAGE,
readOnly: false
};

export function isPubcidEnabled() { return pubcidEnabled; }
export function getExpInterval() { return interval; }
/**
* Set an item in the storage with expiry time.
* @param {string} key Key of the item to be stored
* @param {string} val Value of the item to be stored
* @param {number} expires Expiry time in minutes
*/

export function setStorageItem(key, val, expires) {
try {
if (expires !== undefined && expires != null) {
const expStr = (new Date(Date.now() + (expires * 60 * 1000))).toUTCString();
localStorage.setItem(key + EXP_SUFFIX, expStr);
}

localStorage.setItem(key, val);
} catch (e) {
utils.logMessage(e);
}
}

/**
* Retrieve an item from storage if it exists and hasn't expired.
* @param {string} key Key of the item.
* @returns {string|null} Value of the item.
*/
export function getStorageItem(key) {
let val = null;

try {
const expVal = localStorage.getItem(key + EXP_SUFFIX);

if (!expVal) {
// If there is no expiry time, then just return the item
val = localStorage.getItem(key);
} else {
// Only return the item if it hasn't expired yet.
// Otherwise delete the item.
const expDate = new Date(expVal);
const isValid = (expDate.getTime() - Date.now()) > 0;
if (isValid) {
val = localStorage.getItem(key);
} else {
removeStorageItem(key);
}
}
} catch (e) {
utils.logMessage(e);
}

return val;
}

/**
* Remove an item from storage
* @param {string} key Key of the item to be removed
*/
export function removeStorageItem(key) {
try {
localStorage.removeItem(key + EXP_SUFFIX);
localStorage.removeItem(key);
} catch (e) {
utils.logMessage(e);
}
}

/**
* Read a value either from cookie or local storage
* @param {string} name Name of the item
* @returns {string|null} a string if item exists
*/
function readValue(name) {
let value;
if (pubcidConfig.typeEnabled === COOKIE) {
value = getCookie(name);
} else if (pubcidConfig.typeEnabled === LOCAL_STORAGE) {
value = getStorageItem(name);
if (!value) {
value = getCookie(name);
}
}

if (value === 'undefined' || value === 'null') { return null; }

return value;
}

/**
* Write a value to either cookies or local storage
* @param {string} name Name of the item
* @param {string} value Value to be stored
* @param {number} expInterval Expiry time in minutes
*/
function writeValue(name, value, expInterval) {
if (name && value) {
if (pubcidConfig.typeEnabled === COOKIE) {
setCookie(name, value, expInterval);
} else if (pubcidConfig.typeEnabled === LOCAL_STORAGE) {
setStorageItem(name, value, expInterval);
}
}
}

export function isPubcidEnabled() { return pubcidConfig.enabled; }
export function getExpInterval() { return pubcidConfig.interval; }
export function getPubcidConfig() { return pubcidConfig; }

/**
* Decorate ad units with pubcid. This hook function is called before the
Expand All @@ -29,7 +140,7 @@ export function requestBidHook(next, config) {
let pubcid = null;

// Pass control to the next function if not enabled
if (!pubcidEnabled) {
if (!pubcidConfig.enabled || !pubcidConfig.typeEnabled) {
return next.call(this, config);
}

Expand All @@ -38,11 +149,22 @@ export function requestBidHook(next, config) {
pubcid = window[PUB_COMMON].getId();
utils.logMessage(PUB_COMMON + ': pubcid = ' + pubcid);
} else {
// Otherwise get the existing cookie or create a new id
pubcid = getCookie(COOKIE_NAME) || utils.generateUUID();
// Otherwise get the existing cookie
pubcid = readValue(ID_NAME);

if (!pubcidConfig.readOnly) {
if (!pubcid) {
pubcid = utils.generateUUID();
// Update the cookie/storage with the latest expiration date
writeValue(ID_NAME, pubcid, pubcidConfig.interval);
// Only return pubcid if it is saved successfully
pubcid = readValue(ID_NAME);
} else {
// Update the cookie/storage with the latest expiration date
writeValue(ID_NAME, pubcid, pubcidConfig.interval);
}
}

// Update the cookie with the latest expiration date
setCookie(COOKIE_NAME, pubcid, interval);
utils.logMessage('pbjs: pubcid = ' + pubcid);
}

Expand All @@ -68,21 +190,49 @@ export function setCookie(name, value, expires) {

// Helper to read a cookie
export function getCookie(name) {
let m = window.document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]*)\\s*(;|$)');
return m ? decodeURIComponent(m[2]) : null;
if (name && window.document.cookie) {
let m = window.document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]*)\\s*(;|$)');
return m ? decodeURIComponent(m[2]) : null;
}
return null;
}

/**
* Configuration function
* @param {boolean} enable Enable or disable pubcid. By default the module is enabled.
* @param {number} expInterval Expiration interval of the cookie in minutes.
* @param {string} type Type of storage to use
* @param {boolean} readOnly Read but not update id
*/

export function setConfig({ enable = true, expInterval = DEFAULT_EXPIRES } = {}) {
pubcidEnabled = enable;
interval = parseInt(expInterval, 10);
if (isNaN(interval)) {
interval = DEFAULT_EXPIRES;
export function setConfig({ enable = true, expInterval = DEFAULT_EXPIRES, type = 'html5,cookie', readOnly = false } = {}) {
pubcidConfig.enabled = enable;
pubcidConfig.interval = parseInt(expInterval, 10);
if (isNaN(pubcidConfig.interval)) {
pubcidConfig.interval = DEFAULT_EXPIRES;
}

pubcidConfig.readOnly = readOnly;

// Default is to use local storage. Fall back to
// cookie only if local storage is not supported.

pubcidConfig.typeEnabled = null;

const typeArray = type.split(',');
for (let i = 0; i < typeArray.length; ++i) {
const name = typeArray[i].trim();
if (name === COOKIE) {
if (utils.cookiesAreEnabled()) {
pubcidConfig.typeEnabled = COOKIE;
break;
}
} else if (name === LOCAL_STORAGE) {
if (utils.hasLocalStorage()) {
pubcidConfig.typeEnabled = LOCAL_STORAGE;
break;
}
}
}
}

Expand All @@ -92,10 +242,8 @@ export function setConfig({ enable = true, expInterval = DEFAULT_EXPIRES } = {})
export function initPubcid() {
config.getConfig('pubcid', config => setConfig(config.pubcid));

if (utils.cookiesAreEnabled()) {
if (!getCookie('_pubcid_optout')) {
$$PREBID_GLOBAL$$.requestBids.before(requestBidHook);
}
if (!readValue(OPTOUT_NAME)) {
$$PREBID_GLOBAL$$.requestBids.before(requestBidHook);
}
}

Expand Down
37 changes: 37 additions & 0 deletions modules/pubCommonId.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
## Publisher Common ID Example Configuration

When the module is included, it's automatically enabled and saves an id to both cookie and local storage with an expiration time of 1 year.

Example of disabling publisher common id.

```
pbjs.setConfig(
pubcid: {
enable: false
}
);
```

Example of setting expiration interval to 30 days. The interval is expressed in minutes.

```
pbjs.setConfig(
pubcid: {
expInterval: 43200
}
);
```

Example of using local storage only and setting expiration interval to 30 days.

```
pbjs.setConfig(
pubcid: {
expInterval: 43200,
type: 'html5'
}
);
```



Loading