Last active
March 7, 2017 16:08
-
-
Save mogilka/56a97958a6062dbb767e30261314e3d3 to your computer and use it in GitHub Desktop.
PHP: How to make Web widget with limited (authorized) access
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Client widget model | |
* @todo for Client and Widget entities you should create appropriate models | |
*/ | |
class ClientWidget { | |
public static function getTable() { | |
return 'client_widget'; | |
} | |
/** | |
* Find active customer widget | |
* @param string $apikey customer ID | |
* @param string $action | |
* @return self|NULL | |
*/ | |
public static function getActive($apikey, $action) { | |
$key = __CLASS__."getActive".$apikey."_".$action; | |
$model = $cache->get($key); | |
if (false === $model) { | |
$w = Widget::getTable(); | |
$cw = self::getTable(); | |
$sql = "select * from $cw | |
inner join $w on $cw.widgetid = $w.id | |
where $cw.apikey = :apikey | |
and $w.action = :action | |
and $cw.initdate <= :today | |
and $cw.finaldate >= :today | |
limit 1"; | |
$model = self::find($sql, [ | |
":apikey"=>$apikey, | |
":action"=>$action, | |
":today"=>date("Y-m-d")]); | |
$cache->set($key, $model); | |
} | |
return $model; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CREATE TABLE `client` ( | |
`id` bigint(20) NOT NULL AUTO_INCREMENT, | |
`name` varchar(255) NOT NULL, | |
`domain` varchar(32) NOT NULL, | |
`email` varchar(128) NOT NULL, | |
`initdate` date DEFAULT NULL, | |
`finaldate` date DEFAULT NULL, | |
`apikey` varchar(16) NOT NULL, | |
`document` varchar(45) DEFAULT NULL, | |
`created_at` varchar(45) NOT NULL, | |
PRIMARY KEY (`id`), | |
KEY `apikey` (`apikey`), | |
) ENGINE=InnoDB DEFAULT CHARSET=utf8; | |
INSERT INTO `client` VALUES (1,'Customer','https://secret-santa.gift https://www.secret-santa.gift','info@customer.mail','2017-01-10','2017-12-31','123456789','Contract 01/17','2017-01-10 16:48'); | |
CREATE TABLE `widget` ( | |
`id` bigint(20) NOT NULL AUTO_INCREMENT, | |
`name` varchar(255) NOT NULL, | |
`description` text NOT NULL, | |
`template` varchar(255) NOT NULL DEFAULT '0', | |
`action` varchar(32) NOT NULL, | |
`created_at` varchar(45) NOT NULL, | |
PRIMARY KEY (`id`) | |
) ENGINE=InnoDB DEFAULT CHARSET=utf8; | |
INSERT INTO `widget` VALUES (1,'Moon phase','','https://zvezdochet.guru/widget/moon?key=123456789&date=2017-01-10&zone=6','moon','2017-01-10 14:37'); | |
CREATE TABLE `client_widget` ( | |
`id` bigint(20) NOT NULL AUTO_INCREMENT, | |
`widgetid` bigint(20) NOT NULL, | |
`initdate` date NOT NULL, | |
`finaldate` date NOT NULL, | |
`apikey` varchar(16) NOT NULL, | |
`created_at` varchar(45) NOT NULL, | |
PRIMARY KEY (`id`), | |
KEY `widgetid` (`widgetid`), | |
KEY `apikey` (`apikey`), | |
CONSTRAINT `client_widget` FOREIGN KEY (`widgetid`) REFERENCES `widget` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, | |
CONSTRAINT `widget_client` FOREIGN KEY (`apikey`) REFERENCES `client` (`apikey`) ON DELETE CASCADE ON UPDATE CASCADE | |
) ENGINE=InnoDB DEFAULT CHARSET=utf8; | |
INSERT INTO `client_widget` VALUES (1,1,'2017-01-20','2017-12-31','123456789','2017-01-10 14:37'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Renders action content to be placed within iframe | |
* @todo you should create appropriate CSS style | |
*/ | |
* @var $date | |
* @var $zone | |
* @var $brand true|false | |
* @var $style | |
*/ | |
//if widget has links, it must open them out of iframe | |
$target = ""; | |
if ($brand) { | |
$target = 'target="_top"'; ?> | |
<link href="https://zvezdochet.guru/css/widget-<?=$style?>.css" rel="stylesheet"> | |
<?php } ?> | |
<div class="vegetable"> | |
<?php #some HTML content ?> | |
<?php if ($brand): ?> | |
<div class="watermark-bottom"> | |
<a href="https://zvezdochet.guru" <?=$target?>>Stargazer</a> | |
</div> | |
<?php endif; ?> | |
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Get widget content to be placed within iframe. | |
* May be used for different widgets | |
*/ | |
(function() { | |
try { | |
var obj = JSON.parse(decodeURIComponent(document.uriparams)); | |
var params = Object.keys(obj).map(function(key) { | |
return key + '=' + obj[key]; | |
}).join('&'); | |
var url = "https://zvezdochet.guru/widget/" + document.uriaction + "?" + params; | |
var req = new XMLHttpRequest(); | |
req.onreadystatechange = function() { | |
if (XMLHttpRequest.DONE == req.readyState) { | |
if (200 == req.status) | |
document.write(req.responseText); | |
} | |
}; | |
req.open("GET", url, true); | |
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); | |
req.send(); | |
} catch(e) { | |
console.log(e.message); | |
} | |
})() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- | |
customer website page | |
parameter 'type' specifies widget controller action | |
--> | |
<script src="https://zvezdochet.guru/widget.js"></script> | |
<div id="widget-content"> | |
<script type="text/javascript"> | |
Stargazer.widget.init({ | |
"containerid":"widget-content", | |
"style":"default", | |
"fontsize":"small", | |
"width":300, | |
"height":500, | |
"id":"moon-widget", | |
"type":"moon", | |
"key":"123456789", | |
"date":"2017-02-26", | |
"zone":6 | |
});</script> | |
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Create iframe and dynamically fill it with external JS content. | |
* This approach ensures asynchronous data loading on customer website. | |
* May be used for different widgets | |
*/ | |
function compose(d, iparams) { | |
var p = "https:" === window.location.protocol ? "https:" : "http:", | |
src = p + "//zvezdochet.guru/request.js"; | |
var iframe = d.createElement('iframe'); | |
iframe.id = iparams.id; | |
iframe.width = iparams.width; | |
iframe.height = iparams.height; | |
iframe.scrolling = "auto"; | |
iframe.frameborder = "0"; | |
iframe.style="border:0"; | |
d.getElementById(iparams.containerid).appendChild(iframe); | |
var html = '<body onload="var d=document;' + | |
'd.getElementsByTagName(\'head\')[0].appendChild(d.createElement(\'script\')).src=\'' + src + '\'">'; | |
var doc = iframe.contentWindow.document; | |
doc.uriaction = iparams.type; | |
doc.uriparams = iparams.uri; | |
doc.open().write(html); | |
doc.close(); | |
} | |
window.Stargazer = window.Stargazer || {}; | |
window.Stargazer.widget = { | |
init: function(params) { | |
params = params || {}; | |
compose(document, { | |
width: params.width, | |
height: params.height, | |
type: params.type, | |
id: params.id, | |
containerid: params.containerid, | |
uri: encodeURIComponent(JSON.stringify(params)) | |
}); | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @todo you may want to manage created models, so you'll need appropriate controllers and views which I ain't cover in my gist | |
*/ | |
class WidgetController { | |
/** | |
* Moon phase widget | |
* @param string $key customer ID | |
* @param string $date date | |
* @param number $zone timezone | |
* @param string $style widget style | |
* @param string $fontsize font size small|medium|large | |
*/ | |
public function actionMoon($key, $date = null, $zone = 0, $style = "default", $fontsize = "medium") { | |
if (empty($key)) | |
return "invalid key"; | |
$widget = ClientWidget::getActive($key, "moon"); | |
if (empty($widget)) | |
return ""; | |
else { | |
$this->initHeaders($widget); | |
if (empty($date)) | |
$date = date("Y-m-d"); | |
$cachekey = __CLASS__."actionMoon".$date."_".$zone."_".$style; | |
$partial = $cache->get($cachekey); | |
if (false === $partial) { | |
$partial = $this->render('moon', [ | |
"date"=>$date, | |
"zone"=>$zone, | |
"brand"=>true, | |
"style"=>$style | |
]); | |
$cache->set($cachekey, $partial, 42300); | |
} | |
return $partial; | |
} | |
} | |
/** | |
* Initialize headers for customer response. | |
* Noone but your customer can access widget content due to CORS. | |
* The customer can access widget from all domains specified in its profile | |
* @param Widget $widget | |
*/ | |
private function initHeaders($widget) { | |
$headers = $response->headers; | |
$headers->add('Access-Control-Allow-Methods', 'GET,OPTION'); | |
$headers->add('Access-Control-Allow-Headers', 'Origin, Content-Type, X-Auth-Token, X-Requested-With'); | |
$http_origin = $_SERVER['HTTP_ORIGIN']; | |
$domains = explode(" ", $widget->client->domain); | |
foreach ($domains as $domain) { | |
if ($http_origin == $domain) { | |
$headers->add('Access-Control-Allow-Origin', $http_origin); | |
break; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment