Merging development branch into master
This commit is contained in:
commit
23f57d36fe
65 changed files with 1366 additions and 234 deletions
173
documentation/api/GetActivities.markdown
Normal file
173
documentation/api/GetActivities.markdown
Normal file
|
@ -0,0 +1,173 @@
|
|||
Get Activities
|
||||
=======================
|
||||
|
||||
|
||||
----------------------------------------
|
||||
|
||||
1. [Purpose][purpose]
|
||||
1. [Endpoint][endpoint]
|
||||
1. [Parameters][parameters]
|
||||
1. [Examples][examples]
|
||||
* [Command line][example-cli]
|
||||
* [PHP][example-php]
|
||||
1. [Response][response]
|
||||
* [Sample][sample]
|
||||
|
||||
----------------------------------------
|
||||
|
||||
<a name="purpose"></a>
|
||||
### Purpose of the Get Activities API
|
||||
|
||||
Use this API to get a user's activity feed.
|
||||
|
||||
----------------------------------------
|
||||
|
||||
<a name="endpoint"></a>
|
||||
### Endpoint
|
||||
|
||||
_Authentication: optional_
|
||||
|
||||
GET /activities/list.json
|
||||
|
||||
<a name="parameters"></a>
|
||||
### Parameters
|
||||
|
||||
1. groupBy (optional), Time period to group activities by
|
||||
|
||||
----------------------------------------
|
||||
|
||||
<a name="examples"></a>
|
||||
### Examples
|
||||
|
||||
<a name="example-cli"></a>
|
||||
#### Command Line (using [openphoto-php][openphoto-php])
|
||||
|
||||
./openphoto -p -h current.openphoto.me -e /activities/list.json
|
||||
|
||||
<a name="example-php"></a>
|
||||
#### PHP (using [openphoto-php][openphoto-php])
|
||||
|
||||
$client = new OpenPhotoOAuth($host, $consumerKey, $consumerSecret, $oauthToken, $oauthTokenSecret);
|
||||
$response = $client->get("/tags/activities.json");
|
||||
|
||||
----------------------------------------
|
||||
|
||||
<a name="response"></a>
|
||||
### Response
|
||||
|
||||
The response is in a standard [response envelope](http://theopenphotoproject.org/documentation/api/Envelope).
|
||||
|
||||
* _message_, A string describing the result. Don't use this for anything but reading.
|
||||
* _code_, _200_ on success
|
||||
* _result_, An array of activities
|
||||
|
||||
<a name="sample"></a>
|
||||
#### Sample without `groupBy`
|
||||
|
||||
{
|
||||
"message" : "User's list of activities",
|
||||
"code" : 200,
|
||||
"result" : [
|
||||
{ // Photo object
|
||||
"id" : "l", // activity id, not photo id. See photo object for photo id
|
||||
"owner" : "jaisen+test@jmathai.com",
|
||||
"appId" : "openphoto-frontend",
|
||||
"type" : "photo-upload",
|
||||
"data" : {
|
||||
// Photo object
|
||||
}
|
||||
},
|
||||
{ // comment
|
||||
"id" : "p", // activity id, not photo id. See photo object for photo id
|
||||
"owner" : "jaisen+test@jmathai.com",
|
||||
"appId" : "openphoto-frontend",
|
||||
"type" : "action-create",
|
||||
"data" : {
|
||||
"targetType" : "photo",
|
||||
"target" : {
|
||||
// Photo object
|
||||
},
|
||||
"action" : {
|
||||
// Action object
|
||||
}
|
||||
},
|
||||
"permission" : "1",
|
||||
"dateCreated" : "1328851975"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
#### Sample with `groupBy`
|
||||
|
||||
{
|
||||
"message" : "User's list of activities",
|
||||
"code" : 200,
|
||||
"result" : {
|
||||
"2012020921-photo-upload" : [ // photo uploads
|
||||
{
|
||||
"id" : "l", // activity id, not photo id. See photo object for photo id
|
||||
"type" : "photo-upload",
|
||||
"data" : {
|
||||
// Photo object
|
||||
},
|
||||
"permission" : "1",
|
||||
"dateCreated" : "1328851361"
|
||||
},
|
||||
{
|
||||
"id" : "m", // activity id, not photo id. See photo object for photo id
|
||||
"type" : "photo-upload",
|
||||
"data" : {
|
||||
// Photo object
|
||||
},
|
||||
"permission" : "1",
|
||||
"dateCreated" : "1328851363"
|
||||
}
|
||||
],
|
||||
"2012020921-action-create" : [
|
||||
{
|
||||
"id" : "p", // activity id, not photo id. See photo object for photo id
|
||||
"type" : "action-create",
|
||||
"data" : {
|
||||
"targetType" : "photo",
|
||||
"target" : {
|
||||
// Photo object
|
||||
},
|
||||
"action" : {
|
||||
// Action object
|
||||
}
|
||||
},
|
||||
"permission" : "1",
|
||||
"dateCreated" : "1328851975"
|
||||
},
|
||||
{
|
||||
"id" : "q", // activity id, not photo id. See photo object for photo id
|
||||
"owner" : "jaisen+test@jmathai.com",
|
||||
"appId" : "openphoto-frontend",
|
||||
"type" : "action-create",
|
||||
"data" : {
|
||||
"targetType" : "photo",
|
||||
"target" : {
|
||||
// Photo object
|
||||
},
|
||||
"action" : {
|
||||
// Action object
|
||||
}
|
||||
},
|
||||
"permission" : "1",
|
||||
"dateCreated" : "1328852131"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[purpose]: #purpose
|
||||
[endpoint]: #endpoint
|
||||
[parameters]: #parameters
|
||||
[examples]: #examples
|
||||
[example-cli]: #example-cli
|
||||
[example-php]: #example-php
|
||||
[response]: #response
|
||||
[sample]: #sample
|
||||
[openphoto-php]: https://github.com/openphoto/openphoto-php
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
OpenPhoto / Installation for Ubuntu
|
||||
OpenPhoto / Installation for Ubuntu + Apache
|
||||
=======================
|
||||
#### OpenPhoto, a photo service for the masses
|
||||
|
||||
|
@ -26,7 +26,7 @@ Once you've confirmed that your cloud account is setup you can get started on yo
|
|||
|
||||
apt-get update
|
||||
apt-get upgrade
|
||||
apt-get install apache2 php5 libapache2-mod-php5 php5-curl php5-gd php5-mcrypt php-apc
|
||||
apt-get install apache2 php5 libapache2-mod-php5 php5-curl php5-gd php5-mcrypt php-apc build-essential libpcre3-dev
|
||||
a2enmod rewrite
|
||||
|
||||
There are also a few optional but recommended packages and modules.
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
OpenPhoto / Installation for Ubuntu
|
||||
OpenPhoto / Installation for Ubuntu + Cherokee
|
||||
=======================
|
||||
#### OpenPhoto, a photo service for the masses
|
||||
|
||||
## OS: Linux Ubuntu Server 10.04+
|
||||
|
||||
This guide instructs you on how to install OpenPhoto on Cherokee Web Server on an Ubuntu server.
|
||||
To have a recent version of Cherokee, I advice to add the ppa maintained with latest version. You can add it with the command
|
||||
To have a recent version of Cherokee, I advice to add the ppa maintained with latest version. You can add it with the command below.
|
||||
|
||||
add-apt-repository ppa:cherokee-webserver/ppa
|
||||
|
||||
----------------------------------------
|
||||
|
@ -28,7 +29,7 @@ Once you've confirmed that your cloud account is setup you can get started on yo
|
|||
|
||||
apt-get update
|
||||
apt-get upgrade
|
||||
apt-get install cherokee php5-fpm php5-curl php5-mcrypt php-apc
|
||||
apt-get install cherokee php5-fpm php5-curl php5-mcrypt php-apc build-essential libpcre3-dev
|
||||
|
||||
There are also a few optional but recommended packages and modules.
|
||||
|
||||
|
|
|
@ -5,7 +5,9 @@ currentCodeVersion=1.3.2
|
|||
|
||||
[site]
|
||||
mode=prod
|
||||
allowOriginalDownload=0
|
||||
|
||||
[epi]
|
||||
cache=EpiCache_File
|
||||
session=EpiSession_Php
|
||||
config=EpiConfig_File
|
||||
|
|
|
@ -1,11 +1,44 @@
|
|||
<?php
|
||||
$status = true;
|
||||
|
||||
$sql = <<<SQL
|
||||
CREATE TABLE IF NOT EXISTS `{$this->mySqlTablePrefix}activity` (
|
||||
`id` varchar(6) NOT NULL,
|
||||
`owner` varchar(255) NOT NULL,
|
||||
`appId` varchar(255) NOT NULL,
|
||||
`type` varchar(32) NOT NULL,
|
||||
`data` text NOT NULL,
|
||||
`permission` int(11) DEFAULT NULL,
|
||||
`dateCreated` int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (`id`,`owner`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
SQL;
|
||||
$status = $status && mysql_1_4_0($sql);
|
||||
|
||||
$sql = <<<SQL
|
||||
ALTER TABLE `{$this->mySqlTablePrefix}elementTag` ADD INDEX ( `element` )
|
||||
SQL;
|
||||
$status = $status && mysql_1_4_0($sql);
|
||||
|
||||
$sql = <<<SQL
|
||||
ALTER TABLE `{$this->mySqlTablePrefix}photo` ADD `filenameOriginal` VARCHAR( 255 ) NULL AFTER `dateUploadedYear`
|
||||
SQL;
|
||||
$status = $status && mysql_1_4_0($sql);
|
||||
|
||||
$sql = <<<SQL
|
||||
SELECT `id`, `owner`, `pathOriginal` from `{$this->mySqlTablePrefix}photo`
|
||||
SQL;
|
||||
$photos = getDatabase()->all($sql);
|
||||
foreach($photos as $photo)
|
||||
{
|
||||
$filename = basename($photo['pathOriginal']);
|
||||
$filenameOriginal = substr($filename, strpos($filename, '-')+1);
|
||||
$sql = <<<SQL
|
||||
UPDATE `{$this->mySqlTablePrefix}photo` SET `filenameOriginal`=:filenameOriginal WHERE `owner`=:owner AND `id`=:id
|
||||
SQL;
|
||||
getDatabase()->execute($sql, array(':filenameOriginal' => $filenameOriginal, ':owner' => $photo['owner'], ':id' => $photo['id']));
|
||||
}
|
||||
|
||||
$sql = <<<SQL
|
||||
UPDATE `{$this->mySqlTablePrefix}admin` SET `value`=:version WHERE `key`=:key
|
||||
SQL;
|
||||
|
|
|
@ -3,7 +3,7 @@ $domains = $this->db->get_domain_list("/^{$this->domainPhoto}(Action|Credential|
|
|||
if(count($domains) == 7)
|
||||
return true;
|
||||
|
||||
$domainsToCreate = array($this->domainAction, $this->domainCredential, $this->domainGroup,
|
||||
$domainsToCreate = array($this->domainAction, $this->domainActivity, $this->domainCredential, $this->domainGroup,
|
||||
$this->domainPhoto, $this->domainTag, $this->domainUser, $this->domainWebhook);
|
||||
|
||||
$queue = new CFBatchRequest();
|
||||
|
|
|
@ -23,6 +23,7 @@ javascript="jquery"
|
|||
|
||||
[frontApis]
|
||||
photos="GET /photos/list.json?pageSize=20&returnSizes=800x450xCR"
|
||||
#activities="GET /activities/list.json?pageSize=10&groupBy=hour"
|
||||
|
||||
[pagination]
|
||||
pagesToDisplay=10
|
||||
|
|
|
@ -42,31 +42,32 @@ this.isShown=false;f.call(this);this.$element.trigger("hide").removeClass("in");
|
|||
b(document).ready(function(){b("body").delegate("[data-controls-modal]","click",function(a){a.preventDefault();a=b(this).data("show",true);b("#"+a.attr("data-controls-modal")).modal(a.data())})})})(window.jQuery||window.ender);
|
||||
var opTheme=function(){var b=null,h=function(a){console!==void 0&&console.log!==void 0&&console.log(a)},l=function(a){var b="";arguments.length>1&&(arguments[1]=="error"?b="error":arguments[1]=="confirm"&&(b="success"));return'<div class="alert-message block-message '+b+'"><a class="modal-close-click close" href="#">x</a>'+a+"</div>"},a=function(a){var b=$(document.activeElement).val(),a=a.result,e=[];for(i in a)a.hasOwnProperty(i)&&e.push({id:a[i].id,name:a[i].id+" ("+a[i].count+")"});e.push({id:b,
|
||||
name:b});return e},g=function(a){return"<li>"+a.name+"</li>"},f=void 0;return{callback:{keyboard:function(){h("keyboard!!!!")},actionDelete:function(a){a.preventDefault();var a=$(a.target),b=a.attr("href")+".json",e=a.attr("data-id");OP.Util.makeRequest(b,a.parent().serializeArray(),function(a){a.code===200?$(".action-container-"+e).hide("medium",function(){$(this).remove()}):opTheme.message.error("Could not delete the photo.")});return false},batchAdd:function(a){$(".id-"+a.id).removeClass("unpinned").addClass("pinned");
|
||||
opTheme.ui.batchMessage();h("Adding photo "+a.id)},batchClear:function(){var a=$("#batch-message").parent();$(".pinned").removeClass("pinned").addClass("unpinned").children().filter(".pin").fadeOut();a.slideUp("fast",function(){$(this).remove()})},batchField:function(a){var a=$(a.target).val(),b=$("form#batch-edit .form-fields");switch(a){case "permission":b.html(opTheme.ui.batchFormFields.permission());break;case "tagsAdd":case "tagsRemove":b.html(opTheme.ui.batchFormFields.tags()),OP.Util.fire("callback:tags-autocomplete")}},
|
||||
batchModal:function(){var a=$("#modal"),b='<form id="batch-edit"> <div class="clearfix"> <label>Property</label> <div class="input"> <select id="batch-key" class="batch-field-change" name="property"> <option value="tagsAdd">Add Tags</option> <option value="tagsRemove">Remove Tags</option> <option value="permission">Permission</option> </select> </div> </div> <div class="form-fields">'+opTheme.ui.batchFormFields.tags()+"</div></form>";a.html('<div class="modal-header"> <a href="#" class="close">×</a> <h3>Batch edit your pinned photos</h3></div><div class="modal-body"> <p>'+
|
||||
b+'</p></div><div class="modal-footer"><a href="#" class="btn photo-update-batch-click">Update</a></div>');OP.Util.fire("callback:tags-autocomplete")},batchRemove:function(a){$(".id-"+a).addClass("unpinned").removeClass("pinned");opTheme.ui.batchMessage();h("Removing photo "+a)},commentJump:function(a){a.preventDefault();$.scrollTo($("div.comment-form"),200);return false},credentailDelete:function(a){a.preventDefault();var b=$(a.target),a=b.attr("href")+".json";OP.Util.makeRequest(a,{},function(a){a.code===
|
||||
200?(b.parent().remove(),opTheme.message.confirm("Credential successfully deleted.")):opTheme.message.error("Could not delete credential.")});return false},groupCheckbox:function(a){a=$(a.target);a.hasClass("none")&&a.is(":checked")?$("input.group-checkbox:not(.none)").removeAttr("checked"):a.is(":checked")&&$("input.group-checkbox.none").removeAttr("checked")},groupPost:function(a){a.preventDefault();var a=$(a.target).parent().parent(),b=a.attr("action")+".json",e=b.search("create")>-1;OP.Util.makeRequest(b,
|
||||
a.serializeArray(),function(a){a.code===200?e?location.href=location.href:opTheme.message.confirm("Group updated successfully."):opTheme.message.error("Could not update group.")});return false},login:function(a){a=$(a.target);a.hasClass("browserid")?navigator.id.getVerifiedEmail(function(a){a?opTheme.user.browserid.loginSuccess(a):opTheme.user.browserid.loginFailure(a)}):a.hasClass("facebook")&&FB.login(function(a){a.authResponse?(h("User logged in, posting to openphoto host."),OP.Util.makeRequest("/user/facebook/login.json",
|
||||
opTheme.user.base.loginProcessed)):h("User cancelled login or did not fully authorize.")},{scope:"email"})},modalClose:function(a){a.preventDefault();$(a.target).parent().slideUp("fast",function(){$(this).remove()})},photoDelete:function(a){a.preventDefault();var b=$(a.target),a=b.parent().attr("action")+".json";OP.Util.makeRequest(a,b.parent().serializeArray(),function(a){a.code===200?(b.html("This photo has been deleted"),opTheme.message.confirm("This photo has been deleted.")):opTheme.message.error("Could not delete the photo.")});
|
||||
return false},photoEdit:function(a){a.preventDefault();a=$(a.target).attr("href")+".json";$("div.owner-edit").length==1?$.scrollTo($("div.owner-edit"),200):OP.Util.makeRequest(a,{},function(a){a.code===200?($("#main").append(a.result.markup),$.scrollTo($("div.owner-edit"),200),OP.Util.fire("callback:tags-autocomplete")):opTheme.message.error("Could not load the form to edit this photo.")},"json","get");return false},photoUpdateBatch:function(a){a.preventDefault();$(a.target);var a=$("#batch-key").val(),
|
||||
j=$("form#batch-edit").find("*[name='value']"),j=j.length==1?j.val():$("form#batch-edit").find("*[name='value']:checked").val();params={crumb:b};params[a]=j;params.ids=OP.Batch.collection.getIds().join(",");h(params);OP.Util.makeRequest("/photos/update.json",params,opTheme.callback.photoUpdateBatchCb,"json","post")},photoUpdateBatchCb:function(a){a.code==200?opTheme.message.append(l("Your photos were successfully updated.","confirm")):opTheme.message.append(l("There was a problem updating your photos.",
|
||||
"error"));$("#modal").modal("hide")},pinClick:function(a){var a=$(a.target),b=a.attr("data-id");a.parent().hasClass("unpinned")?OP.Batch.add(b):OP.Batch.remove(b)},pinClearClick:function(a){a.preventDefault();OP.Batch.clear()},pinOver:function(a){$(a.target).parent().prev().fadeIn("fast")},pinOut:function(a){$(a.target).filter('[class~="unpinned"]').children().filter(".pin").filter(":visible").fadeOut("fast")},pluginStatus:function(a){a.preventDefault();a=$(a.target).attr("href")+".json";OP.Util.makeRequest(a,
|
||||
{},function(a){a.code===200?window.location.reload():opTheme.message.error("Could not update the status of this plugin.")},"json","post");return false},pluginUpdate:function(a){a.preventDefault();var a=$(a.target).parent(),b=a.attr("action")+".json";OP.Util.makeRequest(b,a.serializeArray(),function(a){a.code===200?opTheme.message.confirm("Your plugin was successfully updated."):opTheme.message.error("Could not update the status of this plugin.")},"json","post");return false},searchByTags:function(a){a.preventDefault();
|
||||
var b=$(a.target).parent(),a=$(b.find("input[name=tags]")[0]).val(),b=$(b).attr("action");location.href=a.length>0?b.replace("/list","")+"/tags-"+a+"/list":b;return false},settings:function(){$("ul#settingsbar").slideToggle("medium");$("li#nav-signin").toggleClass("active");return false},keyBrowseNext:function(){var a;if(a=$(".image-pagination .next a").attr("href"))location.href=a},keyBrowsePrevious:function(){var a;if(a=$(".image-pagination .previous a").attr("href"))location.href=a},webhookDelete:function(a){a.preventDefault();
|
||||
var b=$(a.target),a=b.attr("href")+".json";OP.Util.makeRequest(a,{},function(a){a.code===200?(b.parent().remove(),opTheme.message.confirm("Credential successfully deleted.")):opTheme.message.error("Could not delete credential.")});return false}},formHandlers:{hasErrors:function(a,b){var e=[];a.children("input, textarea").each(function(){var c=$(this);c.prev().removeClass("error");var f=c.attr(b);if(f!=void 0)for(var f=f.split(" "),g=0;g<f.length;g++){if(f[g]=="date"&&!opTheme.formHandlers.passesDate(c)){var h=
|
||||
c.prev().html()+" is not a valid date";e.push([c,h])}f[g]=="email"&&!opTheme.formHandlers.passesEmail(c)&&(h=c.prev().html()+" is not a valid email address",e.push([c,h]));f[g]=="ifexists"&&c.val()!=""&&c.val()!=void 0&&$.merge(e,opTheme.formHandlers.hasErrors(a,"data-ifexists"));f[g]=="integer"&&!opTheme.formHandlers.passesInteger(c)&&(h=c.prev().html()+" is not a number",e.push([c,h]));f[g]=="match"&&(h=c.attr("data-match"),opTheme.formHandlers.passesMatch(c,h)||(h=c.prev().html()+" does not match "+
|
||||
$("#"+h).prev().html(),e.push([c,h])));f[g]=="required"&&!opTheme.formHandlers.passesRequired(c)&&(h=c.prev().html()+" is required",e.push([c,h]));f[g]=="alphanumeric"&&!opTheme.formHandlers.passesAlphaNumeric(c)&&(h=c.prev().html()+" can only contain alpha-numeric characters",e.push([c,h]))}});return e},init:function(){$(this).submit(opTheme.submitHandlers.siteForm);opTheme.formHandlers.showPlaceholders();$("input[data-placeholder]").live("focus",opTheme.formHandlers.placeholderFocus);$("input[data-placeholder]").live("blur",
|
||||
opTheme.formHandlers.placeholderBlur)},passesAlphaNumeric:function(a){return/^[a-zA-Z0-9]+$/.test(a.val())},passesDate:function(a){return/^\d{1,2}\/\d{1,2}\/\d{4}$/.test(a.val())},passesEmail:function(a){return/^([\w-\.+]+@([\w-]+\.)+[\w-]{2,4})?$/.test(a.val())},passesInteger:function(a){return/^\d+$/.test(a.val())},passesMatch:function(a,b){return a.val()==$("#"+b).val()},passesRequired:function(a){return a.is("textarea")||a.is("input")&&(a.attr("type")=="text"||a.attr("type")=="password")?a.val()!=
|
||||
""&&a.val()!=void 0:a.is("checkbox")?a.is(":checked"):true},placeholderBlur:function(){var a=$(this);a.val()==""&&(a.val(a.attr("data-placeholder")),a.addClass("placeholder"))},placeholderFocus:function(){var a=$(this);a.val()==a.attr("data-placeholder")&&(a.val(""),a.removeClass("placeholder"))},removePlaceholders:function(){$("input[data-placeholder]").each(function(){var a=$(this);a.val()==a.attr("data-placeholder")&&(a.val(""),a.removeClass("placeholder"))})},showPlaceholders:function(){$("input[data-placeholder]").each(function(){var a=
|
||||
$(this);a.val()==""&&(a.val(a.attr("data-placeholder")),a.addClass("placeholder"))})}},front:{init:function(a){a.length>0&&a.cycle({fx:"fade"}).find("img").click(function(a){location.href=$(a.target).attr("data-origin")})}},init:{load:function(a){b=a;$("section#slideshow").length>0&&$(window).load(function(){$(".flexslider").flexslider({animation:"slide",controlsContainer:".flex-container",controlNav:true,pausePlay:false,directionNav:true,nextText:"<span title='Next'>Next</span>",prevText:"<span title='Previous'>Previous</span>"})});
|
||||
$("#modal").modal({keyboard:true})},attach:function(){OP.Util.on("click:action-delete",opTheme.callback.actionDelete);OP.Util.on("click:action-jump",opTheme.callback.commentJump);OP.Util.on("click:batch-modal",opTheme.callback.batchModal);OP.Util.on("click:credential-delete",opTheme.callback.credentailDelete);OP.Util.on("click:group-checkbox",opTheme.callback.groupCheckbox);OP.Util.on("click:group-update",opTheme.callback.groupPost);OP.Util.on("click:login",opTheme.callback.login);OP.Util.on("click:modal-close",
|
||||
opTheme.callback.modalClose);OP.Util.on("click:photo-delete",opTheme.callback.photoDelete);OP.Util.on("click:photo-edit",opTheme.callback.photoEdit);OP.Util.on("click:photo-update-batch",opTheme.callback.photoUpdateBatch);OP.Util.on("click:plugin-status",opTheme.callback.pluginStatus);OP.Util.on("click:plugin-update",opTheme.callback.pluginUpdate);OP.Util.on("click:nav-item",opTheme.callback.searchBarToggle);OP.Util.on("click:search",opTheme.callback.searchByTags);OP.Util.on("click:settings",opTheme.callback.settings);
|
||||
OP.Util.on("click:webhook-delete",opTheme.callback.webhookDelete);OP.Util.on("click:pin",opTheme.callback.pinClick);OP.Util.on("click:pin-clear",opTheme.callback.pinClearClick);OP.Util.on("keydown:browse-next",opTheme.callback.keyBrowseNext);OP.Util.on("keydown:browse-previous",opTheme.callback.keyBrowsePrevious);OP.Util.on("mouseover:pin",opTheme.callback.pinOver);OP.Util.on("mouseout:pin",opTheme.callback.pinOut);OP.Util.on("change:batch-field",opTheme.callback.batchField);OP.Util.on("callback:tags-autocomplete",
|
||||
opTheme.init.tags.autocomplete);OP.Util.on("callback:batch-add",opTheme.callback.batchAdd);OP.Util.on("callback:batch-remove",opTheme.callback.batchRemove);OP.Util.on("callback:batch-clear",opTheme.callback.batchClear);OP.Util.fire("callback:tags-autocomplete");typeof OPU==="object"&&OPU.init();$("form.validate").each(opTheme.formHandlers.init)},photos:function(){var a=OP.Batch.collection.getAll(),b=OP.Batch.collection.getLength(),e=$(".unpinned"),c,f;b>0&&opTheme.ui.batchMessage();e.each(function(b,
|
||||
e){e=$(e);c=e.attr("class");f=c.match(/ id-([a-z0-9]+)/);f.length==2&&a[f[1]]!==void 0&&e.removeClass("unpinned").addClass("pinned")})},tags:{autocomplete:function(){var b={queryParam:"search",propertyToSearch:"id",preventDuplicates:true};b.onResult=a;b.resultsFormatter=g;$("input[class~='tags-autocomplete']").each(function(a,e){var e=$(e),c=e.attr("value");if(e.css("display")!="none"){if(c!=""){for(var c=c.split(","),f=[],a=0;a<c.length;a++)f.push({id:c[a],name:c[a]});b.prePopulate=f}$(e).tokenInput("/tags/list.json",
|
||||
b)}})}}},message:{append:function(a){$("#message").append(a).slideDown()},close:function(){f!=void 0&&(clearTimeout(f),f=void 0,$("#message-box").animate({height:"toggle"},500,function(){$("#message-box").remove()}))},confirm:function(a){opTheme.message.show(a,"confirm")},error:function(a){opTheme.message.show(a,"error")},show:function(a,b){var e=b=="error"?"confirm":"error";f!=void 0?(clearTimeout(f),f=void 0,$("#message-box").removeClass(e).addClass(b).html('<div><a class="message-close">close</a>'+
|
||||
a+"</div>"),f=setTimeout(function(){$("#message-box").animate({height:"toggle"},500,function(){$("#message-box").remove();f=void 0})},7E3)):($("html").append('<section id="message-box" style="display:none;"><div><a class="message-close">close</a>'+a+"</div></section>"),$("#message-box").removeClass(e).addClass(b).animate({height:"toggle"},500,function(){f=setTimeout(function(){$("#message-box").animate({height:"toggle"},500,function(){$("#message-box").remove();f=void 0})},7E3)}));$("a.message-close").click(opTheme.message.close)}},
|
||||
submitHandlers:{siteForm:function(a){var b=$(this);a.preventDefault();opTheme.formHandlers.removePlaceholders();a=opTheme.formHandlers.hasErrors(b,"data-validation");opTheme.formHandlers.showPlaceholders();if(a.length==0)this.submit();else{for(var b="<ul>",e=0;e<a.length;e++)a[e][0].prev().addClass("error"),b+="<li>"+a[e][1]+"</li>";b+="</ul>";$("html").animate({scrollTop:a[0][0].offset().top-30},500);a[0][0].focus();opTheme.message.error(b)}}},ui:{batchFormFields:{permission:function(){return' <div class="clearfix"> <label>Value</label> <div class="input"> <ul class="inputs-list"> <li> <label> <input type="radio" name="value" value="1" checked="checked"> <span>Public</span> </label> </li> <li> <label> <input type="radio" name="value" value="0"> <span>Private</span> </label> </li> </div> </div>'},
|
||||
opTheme.ui.batchMessage();h("Adding photo "+a.id)},batchClear:function(){var a=$("#batch-message").parent();$(".pinned").removeClass("pinned").addClass("unpinned").children().filter(".pin").fadeOut();a.slideUp("fast",function(){$(this).remove()})},batchField:function(a){var a=$(a.target).val(),b=$("form#batch-edit .form-fields");switch(a){case "delete":b.html(opTheme.ui.batchFormFields.empty());break;case "permission":b.html(opTheme.ui.batchFormFields.permission());break;case "tagsAdd":case "tagsRemove":b.html(opTheme.ui.batchFormFields.tags()),
|
||||
OP.Util.fire("callback:tags-autocomplete")}},batchModal:function(){var a=$("#modal"),b='<form id="batch-edit"> <div class="clearfix"> <label>Property</label> <div class="input"> <select id="batch-key" class="batch-field-change" name="property"> <option value="tagsAdd">Add Tags</option> <option value="tagsRemove">Remove Tags</option> <option value="permission">Permission</option> <option value="delete">Delete</option> </select> </div> </div> <div class="form-fields">'+
|
||||
opTheme.ui.batchFormFields.tags()+"</div></form>";a.html('<div class="modal-header"> <a href="#" class="close">×</a> <h3>Batch edit your pinned photos</h3></div><div class="modal-body"> <p>'+b+'</p></div><div class="modal-footer"><a href="#" class="btn photo-update-batch-click">Submit</a></div>');OP.Util.fire("callback:tags-autocomplete")},batchRemove:function(a){$(".id-"+a).addClass("unpinned").removeClass("pinned");opTheme.ui.batchMessage();h("Removing photo "+a)},commentJump:function(a){a.preventDefault();
|
||||
$.scrollTo($("div.comment-form"),200);return false},credentailDelete:function(a){a.preventDefault();var b=$(a.target),a=b.attr("href")+".json";OP.Util.makeRequest(a,{},function(a){a.code===200?(b.parent().remove(),opTheme.message.confirm("Credential successfully deleted.")):opTheme.message.error("Could not delete credential.")});return false},groupCheckbox:function(a){a=$(a.target);a.hasClass("none")&&a.is(":checked")?$("input.group-checkbox:not(.none)").removeAttr("checked"):a.is(":checked")&&$("input.group-checkbox.none").removeAttr("checked")},
|
||||
groupPost:function(a){a.preventDefault();var a=$(a.target).parent().parent(),b=a.attr("action")+".json",e=b.search("create")>-1;OP.Util.makeRequest(b,a.serializeArray(),function(a){a.code===200?e?location.href=location.href:opTheme.message.confirm("Group updated successfully."):opTheme.message.error("Could not update group.")});return false},login:function(a){a=$(a.target);a.hasClass("browserid")?navigator.id.getVerifiedEmail(function(a){a?opTheme.user.browserid.loginSuccess(a):opTheme.user.browserid.loginFailure(a)}):
|
||||
a.hasClass("facebook")&&FB.login(function(a){a.authResponse?(h("User logged in, posting to openphoto host."),OP.Util.makeRequest("/user/facebook/login.json",opTheme.user.base.loginProcessed)):h("User cancelled login or did not fully authorize.")},{scope:"email"})},modalClose:function(a){a.preventDefault();$(a.target).parent().slideUp("fast",function(){$(this).remove()})},photoDelete:function(a){a.preventDefault();var b=$(a.target),a=b.parent().attr("action")+".json";OP.Util.makeRequest(a,b.parent().serializeArray(),
|
||||
function(a){a.code===200?(b.html("This photo has been deleted"),opTheme.message.confirm("This photo has been deleted.")):opTheme.message.error("Could not delete the photo.")});return false},photoEdit:function(a){a.preventDefault();a=$(a.target).attr("href")+".json";$("div.owner-edit").length==1?$.scrollTo($("div.owner-edit"),200):OP.Util.makeRequest(a,{},function(a){a.code===200?($("#main").append(a.result.markup),$.scrollTo($("div.owner-edit"),200),OP.Util.fire("callback:tags-autocomplete")):opTheme.message.error("Could not load the form to edit this photo.")},
|
||||
"json","get");return false},photoUpdateBatch:function(a){a.preventDefault();$(a.target);var a=$("#batch-key").val(),j=$("form#batch-edit").find("*[name='value']"),j=j.length==1?j.val():$("form#batch-edit").find("*[name='value']:checked").val();params={crumb:b};params[a]=j;params.ids=OP.Batch.collection.getIds().join(",");a!=="delete"?OP.Util.makeRequest("/photos/update.json",params,opTheme.callback.photoUpdateBatchCb,"json","post"):OP.Util.makeRequest("/photos/delete.json",params,opTheme.callback.photoUpdateBatchCb,
|
||||
"json","post")},photoUpdateBatchCb:function(a){a.code==200?opTheme.message.append(l("Your photos were successfully updated.","confirm")):a.code==204?(OP.Batch.clear(),opTheme.message.append(l("Your photos were successfully deleted.","confirm"))):opTheme.message.append(l("There was a problem updating your photos.","error"));$("#modal").modal("hide")},pinClick:function(a){var a=$(a.target),b=a.attr("data-id");a.parent().hasClass("unpinned")?OP.Batch.add(b):OP.Batch.remove(b)},pinClearClick:function(a){a.preventDefault();
|
||||
OP.Batch.clear()},pinOver:function(a){$(a.target).parent().prev().fadeIn("fast")},pinOut:function(a){$(a.target).filter('[class~="unpinned"]').children().filter(".pin").filter(":visible").fadeOut("fast")},pluginStatus:function(a){a.preventDefault();a=$(a.target).attr("href")+".json";OP.Util.makeRequest(a,{},function(a){a.code===200?window.location.reload():opTheme.message.error("Could not update the status of this plugin.")},"json","post");return false},pluginUpdate:function(a){a.preventDefault();
|
||||
var a=$(a.target).parent(),b=a.attr("action")+".json";OP.Util.makeRequest(b,a.serializeArray(),function(a){a.code===200?opTheme.message.confirm("Your plugin was successfully updated."):opTheme.message.error("Could not update the status of this plugin.")},"json","post");return false},searchByTags:function(a){a.preventDefault();var b=$(a.target).parent(),a=$(b.find("input[name=tags]")[0]).val(),b=$(b).attr("action");location.href=a.length>0?b.replace("/list","")+"/tags-"+a+"/list":b;return false},settings:function(){$("ul#settingsbar").slideToggle("medium");
|
||||
$("li#nav-signin").toggleClass("active");return false},keyBrowseNext:function(){var a;if(a=$(".image-pagination .next a").attr("href"))location.href=a},keyBrowsePrevious:function(){var a;if(a=$(".image-pagination .previous a").attr("href"))location.href=a},webhookDelete:function(a){a.preventDefault();var b=$(a.target),a=b.attr("href")+".json";OP.Util.makeRequest(a,{},function(a){a.code===200?(b.parent().remove(),opTheme.message.confirm("Credential successfully deleted.")):opTheme.message.error("Could not delete credential.")});
|
||||
return false}},formHandlers:{hasErrors:function(a,b){var e=[];a.children("input, textarea").each(function(){var c=$(this);c.prev().removeClass("error");var f=c.attr(b);if(f!=void 0)for(var f=f.split(" "),g=0;g<f.length;g++){if(f[g]=="date"&&!opTheme.formHandlers.passesDate(c)){var h=c.prev().html()+" is not a valid date";e.push([c,h])}f[g]=="email"&&!opTheme.formHandlers.passesEmail(c)&&(h=c.prev().html()+" is not a valid email address",e.push([c,h]));f[g]=="ifexists"&&c.val()!=""&&c.val()!=void 0&&
|
||||
$.merge(e,opTheme.formHandlers.hasErrors(a,"data-ifexists"));f[g]=="integer"&&!opTheme.formHandlers.passesInteger(c)&&(h=c.prev().html()+" is not a number",e.push([c,h]));f[g]=="match"&&(h=c.attr("data-match"),opTheme.formHandlers.passesMatch(c,h)||(h=c.prev().html()+" does not match "+$("#"+h).prev().html(),e.push([c,h])));f[g]=="required"&&!opTheme.formHandlers.passesRequired(c)&&(h=c.prev().html()+" is required",e.push([c,h]));f[g]=="alphanumeric"&&!opTheme.formHandlers.passesAlphaNumeric(c)&&
|
||||
(h=c.prev().html()+" can only contain alpha-numeric characters",e.push([c,h]))}});return e},init:function(){$(this).submit(opTheme.submitHandlers.siteForm);opTheme.formHandlers.showPlaceholders();$("input[data-placeholder]").live("focus",opTheme.formHandlers.placeholderFocus);$("input[data-placeholder]").live("blur",opTheme.formHandlers.placeholderBlur)},passesAlphaNumeric:function(a){return/^[a-zA-Z0-9]+$/.test(a.val())},passesDate:function(a){return/^\d{1,2}\/\d{1,2}\/\d{4}$/.test(a.val())},passesEmail:function(a){return/^([\w-\.+]+@([\w-]+\.)+[\w-]{2,4})?$/.test(a.val())},
|
||||
passesInteger:function(a){return/^\d+$/.test(a.val())},passesMatch:function(a,b){return a.val()==$("#"+b).val()},passesRequired:function(a){return a.is("textarea")||a.is("input")&&(a.attr("type")=="text"||a.attr("type")=="password")?a.val()!=""&&a.val()!=void 0:a.is("checkbox")?a.is(":checked"):true},placeholderBlur:function(){var a=$(this);a.val()==""&&(a.val(a.attr("data-placeholder")),a.addClass("placeholder"))},placeholderFocus:function(){var a=$(this);a.val()==a.attr("data-placeholder")&&(a.val(""),
|
||||
a.removeClass("placeholder"))},removePlaceholders:function(){$("input[data-placeholder]").each(function(){var a=$(this);a.val()==a.attr("data-placeholder")&&(a.val(""),a.removeClass("placeholder"))})},showPlaceholders:function(){$("input[data-placeholder]").each(function(){var a=$(this);a.val()==""&&(a.val(a.attr("data-placeholder")),a.addClass("placeholder"))})}},front:{init:function(a){a.length>0&&a.cycle({fx:"fade"}).find("img").click(function(a){location.href=$(a.target).attr("data-origin")})}},
|
||||
init:{load:function(a){b=a;$("section#slideshow").length>0&&$(window).load(function(){$(".flexslider").flexslider({animation:"slide",controlsContainer:".flex-container",controlNav:true,pausePlay:false,directionNav:true,nextText:"<span title='Next'>Next</span>",prevText:"<span title='Previous'>Previous</span>"})});$("#modal").modal({keyboard:true})},attach:function(){OP.Util.on("click:action-delete",opTheme.callback.actionDelete);OP.Util.on("click:action-jump",opTheme.callback.commentJump);OP.Util.on("click:batch-modal",
|
||||
opTheme.callback.batchModal);OP.Util.on("click:credential-delete",opTheme.callback.credentailDelete);OP.Util.on("click:group-checkbox",opTheme.callback.groupCheckbox);OP.Util.on("click:group-update",opTheme.callback.groupPost);OP.Util.on("click:login",opTheme.callback.login);OP.Util.on("click:modal-close",opTheme.callback.modalClose);OP.Util.on("click:photo-delete",opTheme.callback.photoDelete);OP.Util.on("click:photo-edit",opTheme.callback.photoEdit);OP.Util.on("click:photo-update-batch",opTheme.callback.photoUpdateBatch);
|
||||
OP.Util.on("click:plugin-status",opTheme.callback.pluginStatus);OP.Util.on("click:plugin-update",opTheme.callback.pluginUpdate);OP.Util.on("click:nav-item",opTheme.callback.searchBarToggle);OP.Util.on("click:search",opTheme.callback.searchByTags);OP.Util.on("click:settings",opTheme.callback.settings);OP.Util.on("click:webhook-delete",opTheme.callback.webhookDelete);OP.Util.on("click:pin",opTheme.callback.pinClick);OP.Util.on("click:pin-clear",opTheme.callback.pinClearClick);OP.Util.on("keydown:browse-next",
|
||||
opTheme.callback.keyBrowseNext);OP.Util.on("keydown:browse-previous",opTheme.callback.keyBrowsePrevious);OP.Util.on("mouseover:pin",opTheme.callback.pinOver);OP.Util.on("mouseout:pin",opTheme.callback.pinOut);OP.Util.on("change:batch-field",opTheme.callback.batchField);OP.Util.on("callback:tags-autocomplete",opTheme.init.tags.autocomplete);OP.Util.on("callback:batch-add",opTheme.callback.batchAdd);OP.Util.on("callback:batch-remove",opTheme.callback.batchRemove);OP.Util.on("callback:batch-clear",opTheme.callback.batchClear);
|
||||
OP.Util.fire("callback:tags-autocomplete");typeof OPU==="object"&&OPU.init();$("form.validate").each(opTheme.formHandlers.init)},photos:function(){var a=OP.Batch.collection.getAll(),b=OP.Batch.collection.getLength(),e=$(".unpinned"),c,f;b>0&&opTheme.ui.batchMessage();e.each(function(b,e){e=$(e);c=e.attr("class");f=c.match(/ id-([a-z0-9]+)/);f.length==2&&a[f[1]]!==void 0&&e.removeClass("unpinned").addClass("pinned")})},tags:{autocomplete:function(){var b={queryParam:"search",propertyToSearch:"id",
|
||||
preventDuplicates:true};b.onResult=a;b.resultsFormatter=g;$("input[class~='tags-autocomplete']").each(function(a,e){var e=$(e),c=e.attr("value");if(e.css("display")!="none"){if(c!=""){for(var c=c.split(","),f=[],a=0;a<c.length;a++)f.push({id:c[a],name:c[a]});b.prePopulate=f}$(e).tokenInput("/tags/list.json",b)}})}}},message:{append:function(a){$("#message").append(a).slideDown()},close:function(){f!=void 0&&(clearTimeout(f),f=void 0,$("#message-box").animate({height:"toggle"},500,function(){$("#message-box").remove()}))},
|
||||
confirm:function(a){opTheme.message.show(a,"confirm")},error:function(a){opTheme.message.show(a,"error")},show:function(a,b){var e=b=="error"?"confirm":"error";f!=void 0?(clearTimeout(f),f=void 0,$("#message-box").removeClass(e).addClass(b).html('<div><a class="message-close">close</a>'+a+"</div>"),f=setTimeout(function(){$("#message-box").animate({height:"toggle"},500,function(){$("#message-box").remove();f=void 0})},7E3)):($("html").append('<section id="message-box" style="display:none;"><div><a class="message-close">close</a>'+
|
||||
a+"</div></section>"),$("#message-box").removeClass(e).addClass(b).animate({height:"toggle"},500,function(){f=setTimeout(function(){$("#message-box").animate({height:"toggle"},500,function(){$("#message-box").remove();f=void 0})},7E3)}));$("a.message-close").click(opTheme.message.close)}},submitHandlers:{siteForm:function(a){var b=$(this);a.preventDefault();opTheme.formHandlers.removePlaceholders();a=opTheme.formHandlers.hasErrors(b,"data-validation");opTheme.formHandlers.showPlaceholders();if(a.length==
|
||||
0)this.submit();else{for(var b="<ul>",e=0;e<a.length;e++)a[e][0].prev().addClass("error"),b+="<li>"+a[e][1]+"</li>";b+="</ul>";$("html").animate({scrollTop:a[0][0].offset().top-30},500);a[0][0].focus();opTheme.message.error(b)}}},ui:{batchFormFields:{empty:function(){return""},permission:function(){return' <div class="clearfix"> <label>Value</label> <div class="input"> <ul class="inputs-list"> <li> <label> <input type="radio" name="value" value="1" checked="checked"> <span>Public</span> </label> </li> <li> <label> <input type="radio" name="value" value="0"> <span>Private</span> </label> </li> </div> </div>'},
|
||||
tags:function(){return' <div class="clearfix"> <label>Tags</label> <div class="input"> <input type="text" name="value" class="tags-autocomplete" placeholder="A comma separated list of tags" value=""> </div> </div>'}},batchMessage:function(){var a=OP.Batch.collection.getLength();$("#batch-message").length>0&&a>0?$("#batch-count").html(a):a==0?$("#batch-message").parent().slideUp("fast",function(){$(this).remove()}):opTheme.message.append(l(' <a id="batch-message"></a>You have <span id="batch-count">'+
|
||||
a+'</span> photos pinned. <div class="alert-actions"><a class="btn small info batch-modal-click" data-controls-modal="modal" data-backdrop="static">Batch edit</a><a href="#" class="btn small pin-clear-click">Or clear pins</a></div>'))}},user:{base:{loginProcessed:function(a){a.code!=200?h("processing of login failed"):(h("login processing succeeded"),window.location.reload())}},browserid:{loginFailure:function(){h("login failed")},loginSuccess:function(a){OP.Util.makeRequest("/user/browserid/login.json",
|
||||
{assertion:a},opTheme.user.base.loginProcessed)}}}}}();
|
||||
|
|
|
@ -73,7 +73,7 @@ var opTheme = (function() {
|
|||
opTheme.ui.batchMessage();
|
||||
log("Adding photo " + photo.id);
|
||||
},
|
||||
batchClear: function(photo) {
|
||||
batchClear: function() {
|
||||
var el = $("#batch-message").parent();
|
||||
$(".pinned").removeClass("pinned").addClass("unpinned").children().filter(".pin").fadeOut();
|
||||
el.slideUp('fast', function(){ $(this).remove(); });
|
||||
|
@ -83,6 +83,9 @@ var opTheme = (function() {
|
|||
val = el.val(),
|
||||
tgt = $("form#batch-edit .form-fields");
|
||||
switch(val) {
|
||||
case 'delete':
|
||||
tgt.html(opTheme.ui.batchFormFields.empty());
|
||||
break;
|
||||
case 'permission':
|
||||
tgt.html(opTheme.ui.batchFormFields.permission());
|
||||
break;
|
||||
|
@ -106,12 +109,13 @@ var opTheme = (function() {
|
|||
' <option value="tagsAdd">Add Tags</option>' +
|
||||
' <option value="tagsRemove">Remove Tags</option>' +
|
||||
' <option value="permission">Permission</option>' +
|
||||
' <option value="delete">Delete</option>' +
|
||||
' </select>' +
|
||||
' </div>' +
|
||||
' </div>' +
|
||||
' <div class="form-fields">'+opTheme.ui.batchFormFields.tags()+'</div>' +
|
||||
'</form>',
|
||||
'<a href="#" class="btn photo-update-batch-click">Update</a>'
|
||||
'<a href="#" class="btn photo-update-batch-click">Submit</a>'
|
||||
);
|
||||
el.html(html);
|
||||
OP.Util.fire('callback:tags-autocomplete');
|
||||
|
@ -246,12 +250,18 @@ var opTheme = (function() {
|
|||
params = {'crumb':crumb};
|
||||
params[key] = value;
|
||||
params['ids'] = OP.Batch.collection.getIds().join(',');
|
||||
log(params);
|
||||
if(key !== 'delete') {
|
||||
OP.Util.makeRequest('/photos/update.json', params, opTheme.callback.photoUpdateBatchCb, 'json', 'post');
|
||||
} else {
|
||||
OP.Util.makeRequest('/photos/delete.json', params, opTheme.callback.photoUpdateBatchCb, 'json', 'post');
|
||||
}
|
||||
},
|
||||
photoUpdateBatchCb: function(response) {
|
||||
if(response.code == 200) {
|
||||
opTheme.message.append(messageMarkup('Your photos were successfully updated.', 'confirm'));
|
||||
} else if(response.code == 204) {
|
||||
OP.Batch.clear();
|
||||
opTheme.message.append(messageMarkup('Your photos were successfully deleted.', 'confirm'));
|
||||
} else {
|
||||
opTheme.message.append(messageMarkup('There was a problem updating your photos.', 'error'));
|
||||
}
|
||||
|
@ -687,6 +697,9 @@ var opTheme = (function() {
|
|||
},
|
||||
ui: {
|
||||
batchFormFields: {
|
||||
empty: function() {
|
||||
return '';
|
||||
},
|
||||
permission: function() {
|
||||
return ' <div class="clearfix">' +
|
||||
' <label>Value</label>' +
|
||||
|
|
|
@ -1609,7 +1609,7 @@ aside.sidebar .meta li span {
|
|||
top:2px;
|
||||
left:0;
|
||||
position:absolute;
|
||||
width:15px;
|
||||
width:20px;
|
||||
}
|
||||
|
||||
aside.sidebar .meta li.date span {
|
||||
|
@ -1631,6 +1631,9 @@ aside.sidebar .meta li.license span {
|
|||
aside.sidebar .meta li.location span {
|
||||
background:transparent url("../images/icons.png") 0 -626px no-repeat;
|
||||
}
|
||||
aside.sidebar .meta li.original span {
|
||||
background:transparent url("../images/icons.png") 0px 0px no-repeat;
|
||||
}
|
||||
|
||||
aside.sidebar .meta li.location img {
|
||||
-webkit-border-radius: 2px;
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
<?php /*
|
||||
<?php if(count($activities) > 0) { ?>
|
||||
<ul>
|
||||
<?php foreach($activities as $key => $activityGrp) { ?>
|
||||
<li>
|
||||
<?php if(preg_match('/photo-upload|photo-update|action-create/', $key)) { ?>
|
||||
<?php $this->theme->display(sprintf('partials/feed-%s.php', $activityGrp[0]['type']), array('activities' => $activityGrp)); ?>
|
||||
<?php } ?>
|
||||
</li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
<?php } ?>
|
||||
<?php */ ?>
|
||||
|
||||
<?php if($photos[0]['totalRows'] == 0) { ?>
|
||||
<?php if($this->user->isOwner()) { ?>
|
||||
<a href="<?php $this->url->photoUpload(); ?>" class="link" title="Start uploading now!"><img src="<?php $this->theme->asset('image', 'front.png'); ?>" class="front" /></a>
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<?php foreach($activities as $activity) { ?>
|
||||
<?php if($activity['data']['action']['type'] == 'comment') {?>
|
||||
<?php $this->utility->safe($activity['data']['action']['value']); ?>
|
||||
<img src="<?php $this->url->photoUrl($activity['data']['target'], '100x100xCR'); ?>" width="50" height="50">
|
||||
<?php } ?>
|
||||
<?php } ?>
|
|
@ -0,0 +1,4 @@
|
|||
The following photos were updated.
|
||||
<?php foreach($activities as $activity) { ?>
|
||||
<img src="<?php $this->url->photoUrl($activity['data'], '100x100xCR'); ?>" width="50" height="50">
|
||||
<?php } ?>
|
|
@ -0,0 +1,4 @@
|
|||
The following photos were uploaded.
|
||||
<?php foreach($activities as $activity) { ?>
|
||||
<a href="<?php $this->url->photoView($activity['data']['id']); ?>"><img src="<?php $this->url->photoUrl($activity['data'], '100x100xCR'); ?>" width="50" height="50"></a>
|
||||
<?php } ?>
|
|
@ -132,10 +132,12 @@
|
|||
<?php if(!empty($photo[$key])) { ?>
|
||||
<li><?php printf($value, $this->utility->safe($photo[$key], false)); ?></li>
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php } ?>
|
||||
<?php if(isset($photo['pathOriginal'])) { ?>
|
||||
<li class="original"><span></span><a href="<?php $this->utility->safe($photo['pathOriginal']); ?>">Download original</a></li>
|
||||
<?php } ?>
|
||||
<?php if($this->user->isOwner()) { ?>
|
||||
<li class="edit">
|
||||
<span></span>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<link rel="stylesheet" href="<?php echo getAssetPipeline(true)->addCss($this->theme->asset('stylesheet', 'bootstrap.min.css', false))->
|
||||
addCss("/assets/stylesheets/upload.css")->
|
||||
addCss($this->theme->asset('stylesheet', 'main.css', false))->
|
||||
getUrl(AssetPipeline::css, 'd'); ?>">
|
||||
getUrl(AssetPipeline::css, 'e'); ?>">
|
||||
<?php } ?>
|
||||
|
||||
<?php $this->plugin->invoke('onHead', array('page' => $page)); ?>
|
||||
|
@ -130,7 +130,7 @@
|
|||
<?php } else { ?>
|
||||
'<?php echo getAssetPipeline(true)->addJs('/assets/javascripts/openphoto-batch.min.js')->
|
||||
addJs($this->theme->asset('javascript', 'openphoto-theme-full-min.js', false))->
|
||||
getUrl(AssetPipeline::js, 'd'); ?>'
|
||||
getUrl(AssetPipeline::js, 'e'); ?>'
|
||||
<?php } ?>
|
||||
],
|
||||
onComplete: function(){
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
|
@ -4,21 +4,41 @@ var opTheme = (function() {
|
|||
console.log(msg);
|
||||
};
|
||||
return{
|
||||
init: {
|
||||
attach: function(PhotoSwipe) {
|
||||
$('div.gallery-page').live('pageshow', function(e){
|
||||
var
|
||||
currentPage = $(e.target),
|
||||
photoSwipeInstanceId = parseInt(Math.random()*10000),
|
||||
photoSwipeInstance = PhotoSwipe.getInstance(photoSwipeInstanceId)
|
||||
options = {};
|
||||
|
||||
if ($("ul.gallery a").length > 0 && (typeof photoSwipeInstance === "undefined" || photoSwipeInstance === null)) {
|
||||
photoSwipeInstance = $("ul.gallery a", e.target).photoSwipe(options, photoSwipeInstanceId);
|
||||
callback: {
|
||||
login: function(el) {
|
||||
navigator.id.getVerifiedEmail(function(assertion) {
|
||||
if (assertion) {
|
||||
opTheme.user.loginSuccess(assertion);
|
||||
} else {
|
||||
opTheme.user.loginFailure(assertion);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
},
|
||||
init: {
|
||||
attach: function() {
|
||||
OP.Util.on('click:login', opTheme.callback.login);
|
||||
}
|
||||
},
|
||||
user: {
|
||||
loginFailure: function(assertion) {
|
||||
log('login failed');
|
||||
// TODO something here to handle failed login
|
||||
},
|
||||
loginProcessed: function(response) {
|
||||
if(response.code != 200) {
|
||||
log('processing of login failed');
|
||||
// TODO do something here to handle failed login
|
||||
return;
|
||||
}
|
||||
|
||||
log('login processing succeeded');
|
||||
window.location.reload();
|
||||
},
|
||||
loginSuccess: function(assertion) {
|
||||
var params = {assertion: assertion};
|
||||
OP.Util.makeRequest('/user/browserid/login.json', params, opTheme.user.loginProcessed, 'json');
|
||||
}
|
||||
}
|
||||
};
|
||||
}());
|
||||
|
|
|
@ -472,6 +472,6 @@ var opTheme = (function() {
|
|||
var params = {assertion: assertion};
|
||||
OP.Util.makeRequest('/user/browserid/login.json', params, opTheme.user.loginProcessed, 'json');
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
}());
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -116,6 +116,10 @@
|
|||
background: url(../images/icon-globe.png) left 2px no-repeat;
|
||||
}
|
||||
|
||||
.photo-details .original {
|
||||
background: url(../images/header-navigation-photos.png) center left no-repeat;
|
||||
}
|
||||
|
||||
.photo-details .location img {
|
||||
display: block;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,16 @@
|
|||
</div>
|
||||
</form>
|
||||
<ul data-role="listview" data-inset="true">
|
||||
<?php if($this->user->isLoggedIn()) { ?>
|
||||
<li data-icon="delete"><a href="/user/logout" rel="external"><img
|
||||
src="<?php echo $this->user->getAvatarFromEmail(16, $this->user->getEmailAddress()); ?>"
|
||||
class="ui-li-icon ui-corner-none"><?php $this->utility->safe($this->user->getEmailAddress()); ?></a></li>
|
||||
<li></li>
|
||||
<?php } else { ?>
|
||||
<li><a href="#" class="login-click"><img
|
||||
src="<?php $this->theme->asset('image', 'header-navigation-login.png'); ?>" alt="my photos"
|
||||
class="ui-li-icon">Login</a></li>
|
||||
<?php } ?>
|
||||
<li><a href="/photos/list"><img
|
||||
src="<?php $this->theme->asset('image', 'header-navigation-photos.png'); ?>" alt="my photos"
|
||||
class="ui-li-icon">Photos</a></li>
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
<li class="date"><?php $this->utility->dateLong($photo['dateTaken']); ?></li>
|
||||
<li class="heart"><?php echo count($photo['actions']); ?> favorites & comments - <a href="#comments" class="action-jump-click">see all</a></li>
|
||||
<li class="tags"><?php $this->url->tagsAsLinks($photo['tags']); ?></li>
|
||||
<?php if(isset($photo['pathOriginal'])) { ?>
|
||||
<li class="original"><span></span><a href="<?php $this->utility->safe($photo['pathOriginal']); ?>">Download original</a></li>
|
||||
<?php } ?>
|
||||
<?php if($this->utility->licenseLink($photo['license'], false)) { ?>
|
||||
<a rel="license" href="<?php $this->utility->licenseLink($photo['license']); ?>">
|
||||
<?php $this->utility->licenseLong($photo['license']); ?>
|
||||
|
|
|
@ -17,25 +17,55 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="shortcut icon" href="<?php $this->theme->asset('image', 'favicon.ico'); ?>">
|
||||
<link rel="apple-touch-icon" href="<?php $this->theme->asset('image', 'apple-touch-icon.png'); ?>">
|
||||
<?php if($this->config->site->mode === 'dev') { ?>
|
||||
<link rel="stylesheet" href="<?php $this->theme->asset('stylesheet', 'jquery.mobile.css'); ?>">
|
||||
<link rel="stylesheet" href="<?php $this->theme->asset('stylesheet', 'photoswipe.css'); ?>">
|
||||
<link rel="stylesheet" href="<?php $this->theme->asset('stylesheet', 'pagination.css'); ?>">
|
||||
<link rel="stylesheet" href="<?php $this->theme->asset('stylesheet', 'mobile.css'); ?>">
|
||||
<?php } else { ?>
|
||||
<link rel="stylesheet" href="<?php echo getAssetPipeline(true)->addCss($this->theme->asset('stylesheet', 'jquery.mobile.css', false))->
|
||||
addCss($this->theme->asset('stylesheet', 'photoswipe.css', false))->
|
||||
addCss($this->theme->asset('stylesheet', 'pagination.css', false))->
|
||||
addCss($this->theme->asset('stylesheet', 'mobile.css', false))->
|
||||
getUrl(AssetPipeline::css, 'a'); ?>">
|
||||
<?php } ?>
|
||||
|
||||
|
||||
<?php $this->plugin->invoke('onHead', array('page' => $page)); ?>
|
||||
</head>
|
||||
<body class="<?php echo $page; ?>">
|
||||
<?php $this->plugin->invoke('onBodyBegin', array('page' => $page)); ?>
|
||||
<?php echo $body; ?>
|
||||
<?php if($this->config->site->mode === 'dev') { ?>
|
||||
<script type="text/javascript" src="<?php $this->theme->asset($this->config->dependencies->javascript); ?>"></script>
|
||||
<script type="text/javascript" src="<?php $this->theme->asset('util'); ?>"></script>
|
||||
<?php } else { ?>
|
||||
<script type="text/javascript" src="<?php echo getAssetPipeline(true)->setMode(AssetPipeline::combined)->
|
||||
addJs($this->theme->asset('util', null, false))->
|
||||
addJs($this->theme->asset($this->config->dependencies->javascript, null, false))->
|
||||
getUrl(AssetPipeline::js, 'e'); ?>"></script>
|
||||
<?php } ?>
|
||||
<script>
|
||||
OP.Util.init(jQuery, {
|
||||
eventMap: {
|
||||
click: {
|
||||
'login-click':'click:login'
|
||||
}
|
||||
},
|
||||
js: {
|
||||
assets: [
|
||||
<?php if($this->config->site->mode === 'dev') { ?>
|
||||
'<?php $this->theme->asset('javascript', 'jquery.mobile.js'); ?>',
|
||||
'<?php $this->theme->asset('javascript', 'openphoto-theme-mobile.js'); ?>'
|
||||
]
|
||||
}
|
||||
<?php } else { ?>
|
||||
'<?php echo getAssetPipeline(true)->setMode(AssetPipeline::combined)->
|
||||
addJs($this->theme->asset('javascript', 'jquery.mobile.js', false))->
|
||||
addJs($this->theme->asset('javascript', 'openphoto-theme-mobile.js', false))->
|
||||
getUrl(AssetPipeline::js, 'b'); ?>'
|
||||
<?php } ?>
|
||||
],
|
||||
onComplete: function(){ opTheme.init.attach(); }
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<?php $this->plugin->invoke('onBodyEnd', array('page' => $page)); ?>
|
||||
|
|
|
@ -20,6 +20,8 @@ interface DatabaseInterface
|
|||
public function deleteWebhook($id);
|
||||
// get methods read
|
||||
public function getAction($id);
|
||||
public function getActivities();
|
||||
public function getActivity($id);
|
||||
public function getCredential($id);
|
||||
public function getCredentialByUserToken($userToken);
|
||||
public function getCredentials();
|
||||
|
@ -48,6 +50,7 @@ interface DatabaseInterface
|
|||
// put methods create but do not update
|
||||
public function putGroup($id, $params);
|
||||
public function putAction($id, $params);
|
||||
public function putActivity($id, $params);
|
||||
public function putCredential($id, $params);
|
||||
public function putPhoto($id, $params);
|
||||
public function putUser($params);
|
||||
|
|
|
@ -163,6 +163,41 @@ class DatabaseMySql implements DatabaseInterface
|
|||
return include $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves activity
|
||||
*
|
||||
* @return mixed Array on success, FALSE on failure
|
||||
*/
|
||||
public function getActivity($id)
|
||||
{
|
||||
$activity = $this->db->all("SELECT * FROM `{$this->mySqlTablePrefix}activity` WHERE `id`=:id AND `owner`=:owner",
|
||||
array(':id' => $id, ':owner' => $this->owner));
|
||||
if($activity === false)
|
||||
return false;
|
||||
|
||||
$activity = $this->normalizeActivity($activity);
|
||||
|
||||
return $activity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves activities
|
||||
*
|
||||
* @return mixed Array on success, FALSE on failure
|
||||
*/
|
||||
public function getActivities()
|
||||
{
|
||||
$activities = $this->db->all("SELECT * FROM `{$this->mySqlTablePrefix}activity` WHERE `owner`=:owner",
|
||||
array(':owner' => $this->owner));
|
||||
if($activities === false)
|
||||
return false;
|
||||
|
||||
foreach($activities as $key => $activity)
|
||||
$activities[$key] = $this->normalizeActivity($activity);
|
||||
|
||||
return $activities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an action with $id
|
||||
*
|
||||
|
@ -174,10 +209,9 @@ class DatabaseMySql implements DatabaseInterface
|
|||
$action = $this->db->one("SELECT * FROM `{$this->mySqlTablePrefix}action` WHERE `id`=:id AND owner=:owner",
|
||||
array(':id' => $id, ':owner' => $this->owner));
|
||||
if(empty($action))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return $this->normalizeCredential($action);
|
||||
|
||||
return $this->normalizeAction($action);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -364,7 +398,7 @@ class DatabaseMySql implements DatabaseInterface
|
|||
if(!empty($actions))
|
||||
{
|
||||
foreach($actions as $action)
|
||||
$photo['actions'][] = $action;
|
||||
$photo['actions'][] = $this->normalizeAction($action);
|
||||
}
|
||||
}
|
||||
return $photo;
|
||||
|
@ -773,6 +807,21 @@ class DatabaseMySql implements DatabaseInterface
|
|||
return ($res == 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new activity to the database
|
||||
* This method does not overwrite existing values present in $params - hence "new action".
|
||||
*
|
||||
* @param string $id ID of the action to update which is always 1.
|
||||
* @param array $params Attributes to update.
|
||||
* @return boolean
|
||||
*/
|
||||
public function putActivity($id, $params)
|
||||
{
|
||||
$stmt = $this->sqlInsertExplode($this->prepareActivity($params));
|
||||
$result = $this->db->execute("INSERT INTO `{$this->mySqlTablePrefix}activity` (id,{$stmt['cols']}) VALUES (:id,{$stmt['vals']})", array(':id' => $id));
|
||||
return ($result !== false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new action to the database
|
||||
* This method does not overwrite existing values present in $params - hence "new action".
|
||||
|
@ -1149,6 +1198,18 @@ class DatabaseMySql implements DatabaseInterface
|
|||
return $versions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes data from MySql into schema definition
|
||||
*
|
||||
* @param SimpleXMLObject $raw An action from SimpleDb in SimpleXML.
|
||||
* @return array
|
||||
*/
|
||||
private function normalizeActivity($raw)
|
||||
{
|
||||
$raw['data'] = json_decode($raw['data'], 1);
|
||||
return $raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes data from MySql into schema definition
|
||||
*
|
||||
|
@ -1157,7 +1218,7 @@ class DatabaseMySql implements DatabaseInterface
|
|||
*/
|
||||
private function normalizeAction($raw)
|
||||
{
|
||||
// TODO shouldn't we require and use this method?
|
||||
return $raw;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1249,6 +1310,16 @@ class DatabaseMySql implements DatabaseInterface
|
|||
return $raw;
|
||||
}
|
||||
|
||||
/** Prepare activity to store in the database
|
||||
*/
|
||||
private function prepareActivity($params)
|
||||
{
|
||||
if(isset($params['data']))
|
||||
$params['data'] = json_encode($params['data']);
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/** Prepare credential to store in the database
|
||||
*/
|
||||
private function prepareCredential($params)
|
||||
|
|
|
@ -11,7 +11,7 @@ class DatabaseSimpleDb implements DatabaseInterface
|
|||
* Member variables holding the names to the SimpleDb domains needed and the database object itself.
|
||||
* @access private
|
||||
*/
|
||||
private $config, $db, $domainAction, $domainCredential, $domainPhoto,
|
||||
private $config, $db, $domainAction, $domainActivity, $domainCredential, $domainPhoto,
|
||||
$domainTag, $domainUser, $domainWebhook, $errors = array(), $owner;
|
||||
|
||||
/**
|
||||
|
@ -32,6 +32,7 @@ class DatabaseSimpleDb implements DatabaseInterface
|
|||
|
||||
$this->domainPhoto = $this->config->aws->simpleDbDomain;
|
||||
$this->domainAction = $this->config->aws->simpleDbDomain.'Action';
|
||||
$this->domainActivity = $this->config->aws->simpleDbDomain.'Activity';
|
||||
$this->domainCredential = $this->config->aws->simpleDbDomain.'Credential';
|
||||
$this->domainGroup = $this->config->aws->simpleDbDomain.'Group';
|
||||
$this->domainUser = $this->config->aws->simpleDbDomain.'User';
|
||||
|
@ -200,6 +201,36 @@ class DatabaseSimpleDb implements DatabaseInterface
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves activities
|
||||
*
|
||||
* @return mixed Array on success, FALSE on failure
|
||||
*/
|
||||
public function getActivities()
|
||||
{
|
||||
$res = $this->db->select("SELECT * FROM `{$this->domainActivities}`", array('ConsistentRead' => 'true'));
|
||||
$this->logErrors($res);
|
||||
if(isset($res->body->SelectResult->Item))
|
||||
return self::normalizeActivity($res->body->SelectResult->Item);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves activity
|
||||
*
|
||||
* @return mixed Array on success, FALSE on failure
|
||||
*/
|
||||
public function getActivity($id)
|
||||
{
|
||||
$res = $this->db->select("SELECT * FROM `{$this->domainActivities}` WHERE itemName()='{$id}'", array('ConsistentRead' => 'true'));
|
||||
$this->logErrors($res);
|
||||
if(isset($res->body->SelectResult->Item))
|
||||
return self::normalizeActivity($res->body->SelectResult->Item);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a credential with $id
|
||||
*
|
||||
|
@ -709,6 +740,20 @@ class DatabaseSimpleDb implements DatabaseInterface
|
|||
$this->logErrors($res);
|
||||
return $res->isOK();
|
||||
}
|
||||
/**
|
||||
* Add a new activity to the database
|
||||
* This method does not overwrite existing values present in $params - hence "new action".
|
||||
*
|
||||
* @param string $id ID of the action to update which is always 1.
|
||||
* @param array $params Attributes to update.
|
||||
* @return boolean
|
||||
*/
|
||||
public function putActivity($id, $params)
|
||||
{
|
||||
$res = $this->db->put_attributes($this->domainActivity, $id, $params);
|
||||
$this->logErrors($res);
|
||||
return $res->isOK();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new credential to the database
|
||||
|
@ -948,6 +993,26 @@ class DatabaseSimpleDb implements DatabaseInterface
|
|||
return $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes data from simpleDb into schema definition
|
||||
*
|
||||
* @param SimpleXMLObject $raw An action from SimpleDb in SimpleXML.
|
||||
* @return array
|
||||
*/
|
||||
private function normalizeActivity($raw)
|
||||
{
|
||||
$activity = array();
|
||||
$activity['id'] = strval($raw->Name);
|
||||
$activity['appId'] = $this->config->application->appId;
|
||||
foreach($raw->Attribute as $item)
|
||||
{
|
||||
$name = (string)$item->Name;
|
||||
$value = (string)$item->Value;
|
||||
$activity[$name] = $value;
|
||||
}
|
||||
return $activity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes data from simpleDb into schema definition
|
||||
*
|
||||
|
|
28
src/libraries/compatability.php
Normal file
28
src/libraries/compatability.php
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
// parse_ini_string is >= 5.3
|
||||
if(!function_exists('parse_ini_string')){
|
||||
function parse_ini_string($str, $ProcessSections=false){
|
||||
$lines = explode("\n", $str);
|
||||
$return = Array();
|
||||
$inSect = false;
|
||||
foreach($lines as $line){
|
||||
$line = trim($line);
|
||||
if(!$line || $line[0] == "#" || $line[0] == ";")
|
||||
continue;
|
||||
if($line[0] == "[" && $endIdx = strpos($line, "]")){
|
||||
$inSect = substr($line, 1, $endIdx-1);
|
||||
continue;
|
||||
}
|
||||
if(!strpos($line, '=')) // (We don't use "=== false" because value 0 is not valid as well)
|
||||
continue;
|
||||
|
||||
$tmp = explode("=", $line, 2);
|
||||
$tmp[1] = preg_replace(array('/^"/', '/"$/'), '', $tmp[1]);
|
||||
if($ProcessSections && $inSect)
|
||||
$return[$inSect][trim($tmp[0])] = ltrim($tmp[1]);
|
||||
else
|
||||
$return[trim($tmp[0])] = ltrim($tmp[1]);
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@ class ActionController extends BaseController
|
|||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->authentication = getAuthentication();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,8 +27,9 @@ class ActionController extends BaseController
|
|||
*/
|
||||
public function create($targetId, $targetType)
|
||||
{
|
||||
getAuthentication()->requireAuthentication(false);
|
||||
getAuthentication()->requireCrumb($_POST['crumb']);
|
||||
// does not need to be owner, anyone can comment
|
||||
$this->authentication->requireAuthentication(false);
|
||||
$this->authentication->requireCrumb($_POST['crumb']);
|
||||
$res = $this->api->invoke(sprintf('%s.json', $this->url->actionCreate($targetId, $targetType, false)), EpiRoute::httpPost);
|
||||
$result = $res ? '1' : '0';
|
||||
// TODO: standardize messaging parameter
|
||||
|
|
|
@ -40,6 +40,11 @@ class ApiActionController extends ApiBaseController
|
|||
{
|
||||
$action = $this->action->view($id);
|
||||
getPlugin()->invoke('onAction', $action);
|
||||
// get the target element for the action
|
||||
$apiResp = $this->api->invoke("/{$targetType}/{$targetId}/view.json", EpiRoute::httpGet, array('_GET' => array('returnSizes' => '100x100xCR')));
|
||||
$target = $apiResp['result'];
|
||||
$activityParams = array('type' => 'action-create', 'data' => array('targetType' => $targetType, 'target' => $target, 'action' => $action), 'permission' => $target['permission']);
|
||||
$this->api->invoke('/activity/create.json', EpiRoute::httpPost, array('_POST' => $activityParams));
|
||||
return $this->success("Action {$id} created on {$targetType} {$targetId}", $action);
|
||||
}
|
||||
|
||||
|
|
74
src/libraries/controllers/ApiActivityController.php
Normal file
74
src/libraries/controllers/ApiActivityController.php
Normal file
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
/**
|
||||
* Activity controller for API endpoints
|
||||
*
|
||||
* This controller handles all activity endpoints
|
||||
* @author Jaisen Mathai <jaisen@jmathai.com>
|
||||
*/
|
||||
class ApiActivityController extends ApiBaseController
|
||||
{
|
||||
/**
|
||||
* Call the parent constructor
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->activity = new Activity;
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
$status = $this->activity->create($_POST);
|
||||
if($status !== false)
|
||||
return $this->success('Created activity for user', true);
|
||||
else
|
||||
return $this->error('Could not create activities', false);
|
||||
}
|
||||
|
||||
public function list_()
|
||||
{
|
||||
$activities = $this->activity->list_();
|
||||
if(isset($_GET['groupBy']))
|
||||
$activities = $this->groupActivities($activities, $_GET['groupBy']);
|
||||
|
||||
if($activities !== false)
|
||||
return $this->success("User's list of activities", $activities);
|
||||
else
|
||||
return $this->error('Could not get activities', false);
|
||||
}
|
||||
|
||||
public function view($id)
|
||||
{
|
||||
$activity = $this->activity->view($id);
|
||||
if($activity !== false)
|
||||
return $this->success("Activity {$id}", $activity);
|
||||
else
|
||||
return $this->error('Could not get activity', false);
|
||||
|
||||
}
|
||||
|
||||
protected function groupActivities($activities, $groupBy)
|
||||
{
|
||||
switch($groupBy)
|
||||
{
|
||||
case 'hour':
|
||||
$fmt = 'YmdH';
|
||||
break;
|
||||
case 'day':
|
||||
default:
|
||||
$fmt = 'Ymd';
|
||||
break;
|
||||
}
|
||||
|
||||
$return = array();
|
||||
foreach($activities as $activity)
|
||||
{
|
||||
$grp = sprintf('%s-%s', date($fmt, $activity['dateCreated']), $activity['type']);
|
||||
$return[$grp][] = $activity;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ class ApiBaseController
|
|||
const statusError = 500;
|
||||
const statusSuccess = 200;
|
||||
const statusCreated = 201;
|
||||
const statusNoContent = 204;
|
||||
const statusForbidden = 403;
|
||||
const statusNotFound = 404;
|
||||
|
||||
|
@ -28,7 +29,7 @@ class ApiBaseController
|
|||
}
|
||||
|
||||
/**
|
||||
* Created, HTTP 202
|
||||
* Created, HTTP 201
|
||||
*
|
||||
* @param string $message A friendly message to describe the operation
|
||||
* @param mixed $result The result with values needed by the caller to take action.
|
||||
|
@ -36,7 +37,19 @@ class ApiBaseController
|
|||
*/
|
||||
public function created($message, $result = null)
|
||||
{
|
||||
return self::json($message, self::statusCreated, $result);
|
||||
return $this->json($message, self::statusCreated, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* No content, HTTP 204
|
||||
*
|
||||
* @param string $message A friendly message to describe the operation
|
||||
* @param mixed $result The result with values needed by the caller to take action.
|
||||
* @return string Standard JSON envelope
|
||||
*/
|
||||
public function noContent($message, $result = null)
|
||||
{
|
||||
return $this->json($message, self::statusNoContent, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -48,7 +61,7 @@ class ApiBaseController
|
|||
*/
|
||||
public function error($message, $result = null)
|
||||
{
|
||||
return self::json($message, self::statusError, $result);
|
||||
return $this->json($message, self::statusError, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,7 +73,7 @@ class ApiBaseController
|
|||
*/
|
||||
public function success($message, $result = null)
|
||||
{
|
||||
return self::json($message, self::statusSuccess, $result);
|
||||
return $this->json($message, self::statusSuccess, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -72,7 +85,7 @@ class ApiBaseController
|
|||
*/
|
||||
public function forbidden($message, $result = null)
|
||||
{
|
||||
return self::json($message, self::statusForbidden, $result);
|
||||
return $this->json($message, self::statusForbidden, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,7 +97,7 @@ class ApiBaseController
|
|||
*/
|
||||
public function notFound($message, $result = null)
|
||||
{
|
||||
return self::json($message, self::statusNotFound, $result);
|
||||
return $this->json($message, self::statusNotFound, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,7 +21,7 @@ class ApiOAuthController extends ApiBaseController
|
|||
getAuthentication()->requireAuthentication();
|
||||
$res = getDb()->deleteCredential($id);
|
||||
if($res)
|
||||
return $this->success('Oauth credential deleted', true);
|
||||
return $this->noContent('Oauth credential deleted', true);
|
||||
else
|
||||
return $this->error('Could not delete credential', false);
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ class ApiPhotoController extends ApiBaseController
|
|||
if($status)
|
||||
{
|
||||
$this->tag->updateTagCounts($res['result']['tags'], array(), 1, 1);
|
||||
return $this->success('Photo deleted successfully', true);
|
||||
return $this->noContent('Photo deleted successfully', true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -43,6 +43,35 @@ class ApiPhotoController extends ApiBaseController
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete multiple photos
|
||||
*
|
||||
* @return string Standard JSON envelope
|
||||
*/
|
||||
public function deleteBatch()
|
||||
{
|
||||
getAuthentication()->requireAuthentication();
|
||||
getAuthentication()->requireCrumb();
|
||||
if(!isset($_POST['ids']) || empty($_POST['ids']))
|
||||
return $this->error('This API requires an ids parameter.', false);
|
||||
|
||||
$ids = (array)explode(',', $_POST['ids']);
|
||||
$params = $_POST;
|
||||
unset($params['ids']);
|
||||
|
||||
$retval = true;
|
||||
foreach($ids as $id)
|
||||
{
|
||||
$response = $this->api->invoke("/photo/{$id}/delete.json", EpiRoute::httpPost, array('_POST' => $params));
|
||||
$retval = $retval && $response['result'] !== false;
|
||||
}
|
||||
|
||||
if($retval)
|
||||
return $this->noContent(sprintf('%d photos deleted', count($ids)), true);
|
||||
else
|
||||
return $this->error('Error deleting one or more photos', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a form to edit a photo specified by the ID.
|
||||
*
|
||||
|
@ -260,35 +289,62 @@ class ApiPhotoController extends ApiBaseController
|
|||
}
|
||||
elseif(isset($_POST['photo']))
|
||||
{
|
||||
unset($attributes['photo']);
|
||||
// if a filename is passed in we use it else it's the random temp name
|
||||
$localFile = tempnam($this->config->paths->temp, 'opme');
|
||||
if(isset($_POST['filename']))
|
||||
$name = $_POST['filename'];
|
||||
else
|
||||
$name = basename($localFile).'.jpg';
|
||||
|
||||
// if we have a path to a photo we download it
|
||||
// else we base64_decode it
|
||||
if(preg_match('#https?://#', $_POST['photo']))
|
||||
{
|
||||
unset($attributes['photo']);
|
||||
$fp = fopen($localFile, 'w');
|
||||
$ch = curl_init($_POST['photo']);
|
||||
curl_setopt($ch, CURLOPT_FILE, $fp);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
||||
$data = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
fclose($fp);
|
||||
$photoId = $this->photo->upload($localFile, $name, $attributes);
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($attributes['photo']);
|
||||
file_put_contents($localFile, base64_decode($_POST['photo']));
|
||||
$photoId = $this->photo->upload($localFile, $name, $attributes);
|
||||
}
|
||||
}
|
||||
|
||||
if($photoId)
|
||||
{
|
||||
if(isset($returnSizes))
|
||||
{
|
||||
$sizes = (array)explode(',', $returnSizes);
|
||||
if(!in_array('100x100xCR', $sizes))
|
||||
$sizes[] = '100x100xCR';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sizes = array('100x100xCR');
|
||||
}
|
||||
|
||||
foreach($sizes as $size)
|
||||
{
|
||||
$options = $this->photo->generateFragmentReverse($size);
|
||||
$hash = $this->photo->generateHash($photoId, $options['width'], $options['height'], $options['options']);
|
||||
$this->photo->generate($photoId, $hash, $options['width'], $options['height'], $options['options']);
|
||||
}
|
||||
}
|
||||
|
||||
$params = array();
|
||||
if(isset($returnSizes))
|
||||
$params = array('returnSizes' => $returnSizes);
|
||||
$photo = $this->api->invoke("/photo/{$photoId}/view.json", EpiRoute::httpGet, array('_GET' => $params));
|
||||
$apiResp = $this->api->invoke("/photo/{$photoId}/view.json", EpiRoute::httpGet, array('_GET' => array('returnSizes' => implode(',', $sizes))));
|
||||
$photo = $apiResp['result'];
|
||||
|
||||
$webhookApi = $this->api->invoke('/webhooks/photo.upload/list.json', EpiRoute::httpGet);
|
||||
if(!empty($webhookApi['result']) && is_array($webhookApi['result']))
|
||||
{
|
||||
$photoAsArgs = $photo['result'];
|
||||
$photoAsArgs = $photo;
|
||||
$photoAsArgs['tags'] = implode(',', $photoAsArgs['tags']);
|
||||
foreach($webhookApi['result'] as $key => $hook)
|
||||
{
|
||||
|
@ -296,7 +352,14 @@ class ApiPhotoController extends ApiBaseController
|
|||
$this->logger->info(sprintf('Webhook callback executing for photo.upload: %s', $hook['callback']));
|
||||
}
|
||||
}
|
||||
return $this->created("Photo {$photoId} uploaded successfully", $photo['result']);
|
||||
|
||||
$permission = isset($attributes['permission']) ? $attributes['permission'] : 0;
|
||||
$this->api->invoke(
|
||||
'/activity/create.json',
|
||||
EpiRoute::httpPost,
|
||||
array('_POST' => array('type' => 'photo-upload', 'data' => $photo, 'permission' => $permission))
|
||||
);
|
||||
return $this->created("Photo {$photoId} uploaded successfully", $photo);
|
||||
}
|
||||
|
||||
return $this->error('File upload failure', false);
|
||||
|
@ -358,8 +421,10 @@ class ApiPhotoController extends ApiBaseController
|
|||
|
||||
if($photoUpdatedId)
|
||||
{
|
||||
$photo = $this->api->invoke("/photo/{$id}/view.json", EpiRoute::httpGet);
|
||||
return $this->success("photo {$id} updated", $photo['result']);
|
||||
$apiResp = $this->api->invoke("/photo/{$id}/view.json", EpiRoute::httpGet, array('_GET' => array('returnSizes' => '100x100xCR', 'generate' => 'true')));
|
||||
$photo = $apiResp['result'];
|
||||
$this->api->invoke('/activity/create.json', EpiRoute::httpPost, array('_POST' => array('type' => 'photo-update', 'data' => $photo, 'permission' => $params['permission'])));
|
||||
return $this->success("photo {$id} updated", $photo);
|
||||
}
|
||||
|
||||
return $this->error("photo {$id} could not be updated", false);
|
||||
|
@ -370,7 +435,6 @@ class ApiPhotoController extends ApiBaseController
|
|||
* Parameters to be updated are in _POST
|
||||
* This method also manages updating tag counts
|
||||
*
|
||||
* @param string $id ID of the photo to be updated.
|
||||
* @return string Standard JSON envelope
|
||||
*/
|
||||
public function updateBatch()
|
||||
|
|
|
@ -30,7 +30,18 @@ class PhotoController extends BaseController
|
|||
*/
|
||||
public function create($id, $hash, $width, $height, $options = null)
|
||||
{
|
||||
$args = func_get_args();
|
||||
$fragment = $this->photo->generateFragment($width, $height, $options);
|
||||
$apiResp = $this->api->invoke("/photo/{$id}/view.json", EpiRoute::httpGet, array('_GET' => array('returnSizes' => $fragment)));
|
||||
if($apiResp['code'] === 200);
|
||||
{
|
||||
// check if this size exists
|
||||
if(stristr($apiResp['result']["path{$fragment}"], "/{$hash}/") === false)
|
||||
{
|
||||
$this->route->redirect($apiResp['result']["path{$fragment}"], 301, true);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO, this should call a method in the API
|
||||
$photo = $this->photo->generate($id, $hash, $width, $height, $options);
|
||||
// TODO return 404 graphic
|
||||
|
@ -41,6 +52,8 @@ class PhotoController extends BaseController
|
|||
unlink($photo);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->route->run('/error/500');
|
||||
}
|
||||
|
||||
|
|
|
@ -792,7 +792,7 @@ class SetupController extends BaseController
|
|||
file_get_contents("{$configDir}/template.ini")
|
||||
);
|
||||
|
||||
$iniWritten = file_put_contents(sprintf("%s/userdata/configs/%s.ini", $baseDir, getenv('HTTP_HOST')), $generatedIni);
|
||||
$iniWritten = getConfig()->write(sprintf("%s/userdata/configs/%s.ini", $baseDir, getenv('HTTP_HOST')), $generatedIni);
|
||||
if(!$iniWritten)
|
||||
return false;
|
||||
|
||||
|
|
|
@ -40,20 +40,21 @@ class UpgradeController extends BaseController
|
|||
{
|
||||
getAuthentication()->requireAuthentication();
|
||||
getUpgrade()->performUpgrade();
|
||||
$configObj = getConfig();
|
||||
// Backwards compatibility
|
||||
// TODO remove in 2.0
|
||||
$basePath = dirname(Epi::getPath('config'));
|
||||
$configFile = sprintf('%s/userdata/configs/%s.ini', $basePath, getenv('HTTP_HOST'));
|
||||
if(!file_exists($configFile))
|
||||
$configFile = sprintf('%s/generated/%s.ini', Epi::getPath('config'), getenv('HTTP_HOST'));
|
||||
$config = file_get_contents($configFile);
|
||||
$config = $configObj->getString($configFile);
|
||||
// Backwards compatibility
|
||||
// TODO remove in 2.0
|
||||
if(strstr($config, 'lastCodeVersion="') !== false)
|
||||
$config = preg_replace('/lastCodeVersion="\d+\.\d+\.\d+"/', sprintf('lastCodeVersion="%s"', getUpgrade()->getCurrentVersion()), $config);
|
||||
else // Before the upgrade code the lastCodeVersion was not in the config template
|
||||
$config = sprintf("[site]\nlastCodeVersion=\"%s\"\n\n", getUpgrade()->getCurrentVersion()) . $config;
|
||||
file_put_contents($configFile, $config);
|
||||
$configObj->write($configFile, $config);
|
||||
$this->route->redirect('/');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ require $pathsObj->controllers . '/ApiController.php';
|
|||
require $pathsObj->controllers . '/GeneralController.php';
|
||||
require $pathsObj->controllers . '/AssetsController.php';
|
||||
require $pathsObj->controllers . '/ApiActionController.php';
|
||||
require $pathsObj->controllers . '/ApiActivityController.php';
|
||||
require $pathsObj->controllers . '/ActionController.php';
|
||||
require $pathsObj->controllers . '/ApiGroupController.php';
|
||||
require $pathsObj->controllers . '/GroupController.php';
|
||||
|
@ -64,6 +65,7 @@ require $pathsObj->models . '/Url.php';
|
|||
require $pathsObj->models . '/Authentication.php';
|
||||
require $pathsObj->models . '/Credential.php';
|
||||
require $pathsObj->models . '/Action.php';
|
||||
require $pathsObj->models . '/Activity.php';
|
||||
require $pathsObj->models . '/Group.php';
|
||||
require $pathsObj->models . '/Photo.php';
|
||||
require $pathsObj->models . '/Tag.php';
|
||||
|
|
|
@ -5,13 +5,14 @@ class OPException extends Exception
|
|||
{
|
||||
getLogger()->warn($exception->getMessage());
|
||||
$class = get_class($exception);
|
||||
$baseController = new BaseController;
|
||||
switch($class)
|
||||
{
|
||||
case 'OPAuthorizationException':
|
||||
case 'OPAuthorizationSessionException':
|
||||
if(isset($_GET['__route__']) && substr($_GET['__route__'], -5) == '.json')
|
||||
{
|
||||
echo json_encode(BaseController::forbidden('You do not have sufficient permissions to access this page.'));
|
||||
echo json_encode($baseController->forbidden('You do not have sufficient permissions to access this page.'));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -20,7 +21,7 @@ class OPException extends Exception
|
|||
die();
|
||||
break;
|
||||
case 'OPAuthorizationOAuthException':
|
||||
echo json_encode(BaseController::forbidden($exception->getMessage()));
|
||||
echo json_encode($baseController->forbidden($exception->getMessage()));
|
||||
die();
|
||||
break;
|
||||
default:
|
||||
|
@ -38,11 +39,12 @@ class OPInvalidImageException extends OPException{}
|
|||
function op_exception_handler($exception)
|
||||
{
|
||||
static $handled;
|
||||
$baseController = new BaseController;
|
||||
if(!$handled)
|
||||
{
|
||||
getLogger()->warn(sprintf('Uncaught exception (%s:%s): %s', $exception->getFile(), $exception->getLine(), $exception->getMessage()));
|
||||
if(isset($_GET['__route__']) && substr($_GET['__route__'], -5) == '.json')
|
||||
echo json_encode(BaseController::error('An unknown error occurred.'));
|
||||
echo json_encode($baseController->error('An unknown error occurred.'));
|
||||
else
|
||||
getRoute()->run('/error/500', EpiRoute::httpGet);
|
||||
|
||||
|
|
4
src/libraries/external/epi/Epi.php
vendored
4
src/libraries/external/epi/Epi.php
vendored
|
@ -21,7 +21,9 @@ class Epi
|
|||
'cache-apc' => array('base', 'EpiCache.php', 'EpiCache_Apc.php'),
|
||||
'cache-file' => array('base', 'EpiCache.php', 'EpiCache_File.php'),
|
||||
'cache-memcached' => array('base', 'EpiCache.php', 'EpiCache_Memcached.php'),
|
||||
'config' => array('base', 'EpiConfig.php'),
|
||||
'config' => array('base', 'EpiConfig.php', 'config-file', 'config-mysql'),
|
||||
'config-file' => array('base', 'EpiConfig.php', 'EpiConfig_File.php'),
|
||||
'config-mysql' => array('base', 'database', 'EpiConfig.php', 'EpiConfig_MySql.php'),
|
||||
'form' => array('EpiForm.php'),
|
||||
'logger' => array('EpiLogger.php'),
|
||||
'session' => array('base', 'EpiSession.php', 'session-php', 'session-apc', 'session-memcached'),
|
||||
|
|
2
src/libraries/external/epi/EpiApi.php
vendored
2
src/libraries/external/epi/EpiApi.php
vendored
|
@ -96,7 +96,7 @@ class EpiApi
|
|||
EpiException::raise(new EpiException('Could not call ' . json_encode($def) . " for route {$regex}"));
|
||||
}
|
||||
}
|
||||
EpiException::raise(new EpiException("Could not find route {$this->route} from {$_SERVER['REQUEST_URI']}"));
|
||||
EpiException::raise(new EpiException("Could not find route ({$route}) from ({$_SERVER['REQUEST_URI']})"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
3
src/libraries/external/epi/EpiCache.php
vendored
3
src/libraries/external/epi/EpiCache.php
vendored
|
@ -21,9 +21,8 @@ class EpiCache
|
|||
|
||||
$type = array_shift($params);
|
||||
if(!file_exists($file = dirname(__FILE__) . "/{$type}.php"))
|
||||
EpiException::raise(EpiCacheTypeDoesNotExistException("EpiCache type does not exist: ({$type}). Tried loading {$file}", 404));
|
||||
EpiException::raise(new EpiCacheTypeDoesNotExistException("EpiCache type does not exist: ({$type}). Tried loading {$file}", 404));
|
||||
|
||||
require_once $file;
|
||||
self::$instances[$hash] = new $type($params);
|
||||
self::$instances[$hash]->hash = $hash;
|
||||
return self::$instances[$hash];
|
||||
|
|
102
src/libraries/external/epi/EpiConfig.php
vendored
102
src/libraries/external/epi/EpiConfig.php
vendored
|
@ -1,46 +1,16 @@
|
|||
<?php
|
||||
class EpiConfig
|
||||
{
|
||||
private static $instance;
|
||||
private $config;
|
||||
const FILE = 'EpiConfig_File';
|
||||
const MYSQL = 'EpiConfig_MySql';
|
||||
private static $employ;
|
||||
protected static $instances;
|
||||
protected $config;
|
||||
public function __construct()
|
||||
{
|
||||
$this->config = new stdClass;
|
||||
}
|
||||
|
||||
public function load(/*$file, $file, $file, $file...*/)
|
||||
{
|
||||
$args = func_get_args();
|
||||
foreach($args as $file)
|
||||
{
|
||||
// Prepend config directory if the path doesn't start with . or /
|
||||
if($file[0] != '.' && $file[0] != '/')
|
||||
$file = Epi::getPath('config') . "/{$file}";
|
||||
|
||||
if(!file_exists($file))
|
||||
{
|
||||
EpiException::raise(new EpiConfigException("Config file ({$file}) does not exist"));
|
||||
break; // need to simulate same behavior if exceptions are turned off
|
||||
}
|
||||
|
||||
$parsed_array = parse_ini_file($file, true);
|
||||
foreach($parsed_array as $key => $value)
|
||||
{
|
||||
if(!is_array($value))
|
||||
{
|
||||
$this->config->$key = $value;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!isset($this->config->$key))
|
||||
$this->config->$key = new stdClass;
|
||||
foreach($value as $innerKey => $innerValue)
|
||||
$this->config->$key->$innerKey = $innerValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function get($key = null)
|
||||
{
|
||||
if(!empty($key))
|
||||
|
@ -57,22 +27,74 @@ class EpiConfig
|
|||
$this->config->$key = $val;
|
||||
}
|
||||
|
||||
/*
|
||||
* @param $const
|
||||
* @params optional
|
||||
*/
|
||||
public static function employ()
|
||||
{
|
||||
if(func_num_args() === 1)
|
||||
self::$employ = func_get_arg(0);
|
||||
|
||||
return self::$employ;
|
||||
}
|
||||
|
||||
public function loadString($iniAsString)
|
||||
{
|
||||
$config = parse_ini_string($iniAsString, true);
|
||||
$this->mergeConfig($config);
|
||||
}
|
||||
|
||||
protected function mergeConfig($config)
|
||||
{
|
||||
foreach($config as $key => $value)
|
||||
{
|
||||
if(!is_array($value))
|
||||
{
|
||||
$this->config->$key = $value;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!isset($this->config->$key))
|
||||
$this->config->$key = new stdClass;
|
||||
foreach($value as $innerKey => $innerValue)
|
||||
$this->config->$key->$innerKey = $innerValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* EpiConfig::getInstance
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if(self::$instance)
|
||||
return self::$instance;
|
||||
$params = func_get_args();
|
||||
$hash = md5(json_encode($params));
|
||||
if(isset(self::$instances[$hash]))
|
||||
return self::$instances[$hash];
|
||||
|
||||
self::$instance = new EpiConfig;
|
||||
return self::$instance;
|
||||
$type = $params[0];
|
||||
if(!file_exists($file = dirname(__FILE__) . "/{$type}.php"))
|
||||
EpiException::raise(new EpiConfigTypeDoesNotExistException("EpiConfig type does not exist: ({$type}). Tried loading {$file}", 404));
|
||||
|
||||
self::$instances[$hash] = new $type($params[1]);
|
||||
self::$instances[$hash]->hash = $hash;
|
||||
return self::$instances[$hash];
|
||||
}
|
||||
}
|
||||
|
||||
function getConfig()
|
||||
{
|
||||
return EpiConfig::getInstance();
|
||||
$employ = EpiConfig::employ();
|
||||
$class = array_shift($employ);
|
||||
if($class && class_exists($class))
|
||||
return EpiConfig::getInstance($class, $employ);
|
||||
elseif(class_exists(EpiConfig::FILE))
|
||||
return EpiConfig::getInstance(EpiConfig::FILE, $employ);
|
||||
elseif(class_exists(EpiConfig::MYSQL))
|
||||
return EpiConfig::getInstance(EpiConfig::MYSQL, $employ);
|
||||
else
|
||||
EpiException::raise(new EpiConfigTypeDoesNotExistException('Could not determine which cache handler to load', 404));
|
||||
}
|
||||
|
||||
class EpiConfigException extends EpiException {}
|
||||
|
|
63
src/libraries/external/epi/EpiConfig_File.php
vendored
Normal file
63
src/libraries/external/epi/EpiConfig_File.php
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
class EpiConfig_File extends EpiConfig
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function getString($file)
|
||||
{
|
||||
$file = $this->getFilePath($file);
|
||||
|
||||
if(!file_exists($file))
|
||||
{
|
||||
EpiException::raise(new EpiConfigException("Config file ({$file}) does not exist"));
|
||||
return; // need to simulate same behavior if exceptions are turned off
|
||||
}
|
||||
return file_get_contents($file);
|
||||
}
|
||||
|
||||
public function exists($file)
|
||||
{
|
||||
$file = $this->getFilePath($file);
|
||||
return file_exists($file);
|
||||
}
|
||||
|
||||
public function load(/*$file, $file, $file, $file...*/)
|
||||
{
|
||||
$args = func_get_args();
|
||||
foreach($args as $file)
|
||||
{
|
||||
$confAsIni = $this->getString($file);
|
||||
$config = parse_ini_string($confAsIni, true);
|
||||
$this->mergeConfig($config);
|
||||
}
|
||||
}
|
||||
|
||||
public function write($file, $string)
|
||||
{
|
||||
$this->createDirectoryIfNotExists(dirname($file));
|
||||
$created = @file_put_contents($file, $string);
|
||||
return $created != false;
|
||||
}
|
||||
|
||||
private function createDirectoryIfNotExists($dir)
|
||||
{
|
||||
// if directory exists and it's writable return true
|
||||
if(is_dir($dir) && is_writable($dir))
|
||||
return true;
|
||||
|
||||
// try to do a recursive write
|
||||
return mkdir($dir, 0600, true);
|
||||
}
|
||||
|
||||
private function getFilePath($file)
|
||||
{
|
||||
// Prepend config directory if the path doesn't start with . or /
|
||||
if($file[0] != '.' && $file[0] != '/')
|
||||
$file = Epi::getPath('config') . "/{$file}";
|
||||
|
||||
return $file;
|
||||
}
|
||||
}
|
102
src/libraries/external/epi/EpiConfig_MySql.php
vendored
Normal file
102
src/libraries/external/epi/EpiConfig_MySql.php
vendored
Normal file
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
class EpiConfig_MySql extends EpiConfig
|
||||
{
|
||||
private $db, $table;
|
||||
public function __construct($params)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->db = EpiDatabase::getInstance('mysql', $params['database'], $params['host'], $params['username'], $params['password']);
|
||||
$this->table = $params['table'];
|
||||
}
|
||||
|
||||
public function getRecord($file)
|
||||
{
|
||||
$file = $this->getFilePath($file);
|
||||
$res = $this->db->one("SELECT * FROM `{$this->table}` WHERE `id`=:file OR `aliasOf`=:aliasOf", array(':file' => $file, ':aliasOf' => $file));
|
||||
if(!$res)
|
||||
{
|
||||
EpiException::raise(new EpiConfigException("Config file ({$file}) does not exist in db"));
|
||||
return; // need to simulate same behavior if exceptions are turned off
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function getString($file)
|
||||
{
|
||||
$res = $this->getRecord($file);
|
||||
return $res['value'];
|
||||
}
|
||||
|
||||
public function exists($file)
|
||||
{
|
||||
$file = $this->getFilePath($file);
|
||||
$res = $this->db->one("SELECT * FROM `{$this->table}` WHERE `id`=:file OR `aliasOf`=:aliasOf", array(':file' => $file, ':aliasOf' => $file));
|
||||
return $res !== false;
|
||||
}
|
||||
|
||||
public function load(/*$file, $file, $file, $file...*/)
|
||||
{
|
||||
$args = func_get_args();
|
||||
foreach($args as $file)
|
||||
{
|
||||
$confAsIni = $this->getString($file);
|
||||
$config = parse_ini_string($confAsIni, true);
|
||||
$this->mergeConfig($config);
|
||||
}
|
||||
}
|
||||
|
||||
public function search($term, $field = null)
|
||||
{
|
||||
$res = $this->db->all($sql = "SELECT * FROM `{$this->table}` WHERE `value` LIKE :term", array(':term' => "%{$term}%"));
|
||||
foreach($res as $r)
|
||||
{
|
||||
$cfg = parse_ini_string($r['value'], true);
|
||||
$cfg['__id__'] = $r['id'];
|
||||
if($field !== null)
|
||||
{
|
||||
if(is_array($field))
|
||||
{
|
||||
list($k, $v) = each($field);
|
||||
if(isset($cfg[$k][$v]) && $cfg[$k][$v] == $term)
|
||||
return $cfg;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(isset($cfg[$field]))
|
||||
return $cfg;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function write($file, $string, $aliasOf = null)
|
||||
{
|
||||
$exists = $this->exists($file);
|
||||
$file = $this->getFilePath($file);
|
||||
if($exists)
|
||||
{
|
||||
$params = array(':value' => $string);
|
||||
$sql = "UPDATE `{$this->table}` SET `value`=:value ";
|
||||
if($aliasOf !== null)
|
||||
{
|
||||
$sql .= ", `aliasOf`=:aliasOf ";
|
||||
$params[':aliasOf'] = $this->getFilePath($aliasOf);
|
||||
}
|
||||
$params[':file'] = $file;
|
||||
$sql .= " WHERE `id`=:file";
|
||||
$res = $this->db->execute($sql, $params);
|
||||
}
|
||||
else
|
||||
{
|
||||
$res = $this->db->execute("INSERT INTO `{$this->table}` (`id`, `value`, `aliasOf`) VALUES(:file, :value, :aliasOf)", array(':file' => $file, ':value' => $string, ':aliasOf' => $aliasOf));
|
||||
}
|
||||
return $res !== false;
|
||||
}
|
||||
|
||||
private function getFilePath($file)
|
||||
{
|
||||
return basename($file);
|
||||
}
|
||||
}
|
17
src/libraries/external/epi/EpiSession.php
vendored
17
src/libraries/external/epi/EpiSession.php
vendored
|
@ -17,12 +17,14 @@ class EpiSession
|
|||
public static function getInstance()
|
||||
{
|
||||
$params = func_get_args();
|
||||
$hash = md5(implode('.', $params));
|
||||
$hash = md5(json_encode($params));
|
||||
if(isset(self::$instances[$hash]))
|
||||
return self::$instances[$hash];
|
||||
|
||||
$type = array_shift($params);
|
||||
self::$instances[$hash] = new $type($params);
|
||||
$type = $params[0];
|
||||
if(!isset($params[1]))
|
||||
$params[1] = array();
|
||||
self::$instances[$hash] = new $type($params[1]);
|
||||
self::$instances[$hash]->hash = $hash;
|
||||
return self::$instances[$hash];
|
||||
}
|
||||
|
@ -34,9 +36,9 @@ class EpiSession
|
|||
public static function employ(/*$const*/)
|
||||
{
|
||||
if(func_num_args() === 1)
|
||||
{
|
||||
self::$employ = func_get_arg(0);
|
||||
}
|
||||
elseif(func_num_args() > 1)
|
||||
self::$employ = func_get_args();
|
||||
|
||||
return self::$employ;
|
||||
}
|
||||
|
@ -53,8 +55,9 @@ if(!function_exists('getSession'))
|
|||
function getSession()
|
||||
{
|
||||
$employ = EpiSession::employ();
|
||||
if($employ && class_exists($employ))
|
||||
return EpiSession::getInstance($employ);
|
||||
$class = array_shift($employ);
|
||||
if($employ && class_exists($class))
|
||||
return EpiSession::getInstance($class, $employ);
|
||||
elseif(class_exists(EpiSession::PHP))
|
||||
return EpiSession::getInstance(EpiSession::PHP);
|
||||
elseif(class_exists(EpiSession::APC))
|
||||
|
|
|
@ -8,6 +8,7 @@ if(isset($_GET['__route__']) && strstr($_GET['__route__'], '.json'))
|
|||
$basePath = dirname(dirname(__FILE__));
|
||||
$epiPath = "{$basePath}/libraries/external/epi";
|
||||
require "{$epiPath}/Epi.php";
|
||||
require "{$basePath}/libraries/compatability.php";
|
||||
require "{$basePath}/libraries/models/UserConfig.php";
|
||||
|
||||
Epi::setSetting('exceptions', true);
|
||||
|
@ -20,10 +21,18 @@ Epi::init('api','cache','config','curl','form','logger','route','session','templ
|
|||
$userConfigObj = new UserConfig;
|
||||
$hasConfig = $userConfigObj->load();
|
||||
|
||||
EpiCache::employ(getConfig()->get('epi')->cache);
|
||||
EpiSession::employ(getConfig()->get('epi')->session);
|
||||
$configObj = getConfig();
|
||||
EpiCache::employ($configObj->get('epi')->cache);
|
||||
$sessionParams = array($configObj->get('epi')->session);
|
||||
if($configObj->get('epiSessionParams'))
|
||||
$sessionParams = array_merge($sessionParams, (array)$configObj->get('epiSessionParams'));
|
||||
EpiSession::employ($sessionParams);
|
||||
getSession();
|
||||
|
||||
// load theme after everything is initialized
|
||||
// this initializes user which extends BaseModel and gets session, config and cache objects
|
||||
$userConfigObj->loadTheme();
|
||||
|
||||
// determine if this is a login endpoint
|
||||
$loginEndpoint = $assetEndpoint = false;
|
||||
if(isset($_GET['__route__']) && preg_match('#/user/(.*)(login|logout)#', $_GET['__route__']))
|
||||
|
@ -45,7 +54,7 @@ if($hasConfig && !$runSetup)
|
|||
$runUpgrade = false;
|
||||
if(!getUpgrade()->isCurrent())
|
||||
$runUpgrade = true;
|
||||
require getConfig()->get('paths')->libraries . '/routes.php';
|
||||
require $configObj->get('paths')->libraries . '/routes.php';
|
||||
|
||||
// initializes plugins
|
||||
getPlugin()->load();
|
||||
|
@ -68,15 +77,15 @@ else
|
|||
$paths->models = "{$baseDir}/libraries/models";
|
||||
$paths->templates = "{$baseDir}/templates";
|
||||
$paths->themes = "{$baseDir}/html/assets/themes";
|
||||
getConfig()->set('paths', $paths);
|
||||
$configObj->set('paths', $paths);
|
||||
|
||||
if(!$hasConfig)
|
||||
require getConfig()->get('paths')->libraries . '/dependencies.php';
|
||||
require $configObj->get('paths')->libraries . '/dependencies.php';
|
||||
|
||||
require getConfig()->get('paths')->libraries . '/routes-setup.php';
|
||||
require getConfig()->get('paths')->libraries . '/routes-error.php';
|
||||
require getConfig()->get('paths')->controllers . '/SetupController.php';
|
||||
getConfig()->load(sprintf('%s/html/assets/themes/%s/config/settings.ini', dirname(dirname(__FILE__)), getTheme()->getThemeName()));
|
||||
require $configObj->get('paths')->libraries . '/routes-setup.php';
|
||||
require $configObj->get('paths')->libraries . '/routes-error.php';
|
||||
require $configObj->get('paths')->controllers . '/SetupController.php';
|
||||
$configObj->loadString(file_get_contents(sprintf('%s/html/assets/themes/%s/config/settings.ini', dirname(dirname(__FILE__)), getTheme()->getThemeName())));
|
||||
|
||||
// Before we run the setup in edit mode, we need to validate ownership
|
||||
$userObj = new User;
|
||||
|
|
89
src/libraries/models/Activity.php
Normal file
89
src/libraries/models/Activity.php
Normal file
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
/**
|
||||
* Activity model.
|
||||
*
|
||||
* This handles adding and retrieving activity
|
||||
* @author Jaisen Mathai <jaisen@jmathai.com>
|
||||
*/
|
||||
class Activity extends BaseModel
|
||||
{
|
||||
/*
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct($params = null)
|
||||
{
|
||||
parent::__construct();
|
||||
if(isset($params['user']))
|
||||
$this->user = $params['user'];
|
||||
else
|
||||
$this->user = new User;
|
||||
}
|
||||
|
||||
public function create($attributes)
|
||||
{
|
||||
getAuthentication()->requireAuthentication();
|
||||
|
||||
$attributes = array_merge($this->getDefaultAttributes(), $attributes);
|
||||
$attributes = $this->whitelistParams($attributes);
|
||||
if(!$this->validateParams($attributes))
|
||||
{
|
||||
$this->logger->warn('Not all required paramaters were passed to create an activity');
|
||||
return false;
|
||||
}
|
||||
|
||||
$id = $this->user->getNextId('activity');
|
||||
if($id === false)
|
||||
{
|
||||
$this->logger->warn('Could not fetch the next activity id');
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->db->putActivity($id, $attributes);
|
||||
}
|
||||
|
||||
public function list_()
|
||||
{
|
||||
return $this->db->getActivities();
|
||||
}
|
||||
|
||||
public function view($id)
|
||||
{
|
||||
return $this->db->getActivity($id);
|
||||
}
|
||||
|
||||
private function getDefaultAttributes()
|
||||
{
|
||||
return array(
|
||||
'appId' => $this->config->application->appId,
|
||||
'owner' => $this->config->user->email,
|
||||
'dateCreated' => time()
|
||||
);
|
||||
}
|
||||
|
||||
private function validateParams($attributes)
|
||||
{
|
||||
if(!isset($attributes['owner']) || !isset($attributes['type']) || !isset($attributes['permission']))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function whitelistParams($attributes)
|
||||
{
|
||||
$returnAttrs = array();
|
||||
$matches = array('id' => 1, 'owner' => 1, 'appId' => 1, 'type' => 1, 'data' => 1, 'permission' => 1, 'dateCreated' => 1);
|
||||
|
||||
foreach($attributes as $key => $val)
|
||||
{
|
||||
if(isset($matches[$key]))
|
||||
{
|
||||
$returnAttrs[$key] = $val;
|
||||
continue;
|
||||
}
|
||||
|
||||
$returnAttrs[$key] = $val;
|
||||
}
|
||||
|
||||
return $returnAttrs;
|
||||
}
|
||||
}
|
|
@ -84,6 +84,12 @@ class AssetPipeline
|
|||
return $url;
|
||||
}
|
||||
|
||||
public function setMode($mode)
|
||||
{
|
||||
$this->mode = $mode;
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function addAsset($src, $type)
|
||||
{
|
||||
// verify this file exists
|
||||
|
|
|
@ -9,8 +9,8 @@ class Credential extends BaseModel
|
|||
const statusActive = '1';
|
||||
|
||||
const nonceCacheKey = 'oauthTimestamps';
|
||||
public $consumer, $oauthException, $oauthParams, $provider, $sendHeadersOnError = true;
|
||||
private static $requestStatus;
|
||||
public $oauthException, $oauthParams, $provider, $sendHeadersOnError = true;
|
||||
private static $consumer = null, $requestStatus = null;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -84,7 +84,7 @@ class Credential extends BaseModel
|
|||
|
||||
public function checkRequest()
|
||||
{
|
||||
if(isset(self::$requestStatus))
|
||||
if(self::$requestStatus !== null)
|
||||
return self::$requestStatus;
|
||||
|
||||
if(!class_exists('OAuthProvider'))
|
||||
|
@ -206,18 +206,18 @@ class Credential extends BaseModel
|
|||
|
||||
public function getConsumer($consumerKey)
|
||||
{
|
||||
if(!$this->consumer)
|
||||
$this->consumer = $this->db->getCredential($consumerKey);
|
||||
if(!self::$consumer)
|
||||
self::$consumer = $this->db->getCredential($consumerKey);
|
||||
|
||||
return $this->consumer;
|
||||
return self::$consumer;
|
||||
}
|
||||
|
||||
public function getEmailFromOAuth()
|
||||
{
|
||||
if(!$this->consumer)
|
||||
if(!self::$consumer)
|
||||
return false;
|
||||
|
||||
return $this->consumer['owner'];
|
||||
return self::$consumer['owner'];
|
||||
}
|
||||
|
||||
public function getErrorAsString()
|
||||
|
@ -267,6 +267,12 @@ class Credential extends BaseModel
|
|||
// oauth_token and oauth_callback can be passed in for authenticated endpoints to obtain a credential
|
||||
return (count($params) > 2);
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
self::$consumer = null;
|
||||
self::$requestStatus = null;
|
||||
}
|
||||
}
|
||||
|
||||
if(!function_exists('getCredential'))
|
||||
|
|
|
@ -30,6 +30,9 @@ class Photo extends BaseModel
|
|||
$this->user = $params['user'];
|
||||
else
|
||||
$this->user = new User;
|
||||
|
||||
if(isset($params['config']))
|
||||
$this->config = $params['config'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -64,11 +67,15 @@ class Photo extends BaseModel
|
|||
}
|
||||
}
|
||||
|
||||
// we never need the Base
|
||||
// we never need the base
|
||||
if(isset($photo['pathBase']))
|
||||
unset($photo['pathBase']);
|
||||
|
||||
// the original needs to be conditionally included
|
||||
if($this->config->site->allowOriginalDownload == 1 || $this->user->isOwner())
|
||||
$photo['pathOriginal'] = $this->generateUrlOriginal($photo);
|
||||
elseif(isset($photo['pathOriginal']))
|
||||
unset($photo['pathOriginal']);
|
||||
|
||||
$photo['url'] = $this->getPhotoViewUrl($photo);
|
||||
return $photo;
|
||||
|
@ -453,6 +460,7 @@ class Photo extends BaseModel
|
|||
}
|
||||
$tagObj = new Tag;
|
||||
$attributes = $this->whitelistParams($attributes);
|
||||
$filenameOriginal = $name;
|
||||
$paths = $this->generatePaths($name);
|
||||
$exiftran = $this->config->modules->exiftran;
|
||||
if(is_executable($exiftran))
|
||||
|
@ -536,6 +544,7 @@ class Photo extends BaseModel
|
|||
'exifExposureTime' => @$exif['exposureTime'],
|
||||
'exifISOSpeed' => @$exif['ISO'],
|
||||
'exifFocalLength' => @$exif['focalLength'],
|
||||
'filenameOriginal' => $filenameOriginal,
|
||||
'width' => @$exif['width'],
|
||||
'height' => @$exif['height'],
|
||||
'dateTaken' => $dateTaken,
|
||||
|
@ -712,7 +721,7 @@ class Photo extends BaseModel
|
|||
$returnAttrs = array();
|
||||
$matches = array('id' => 1,'host' => 1,'appId' => 1,'title' => 1,'description' => 1,'key' => 1,'hash' => 1,'tags' => 1,'size' => 1,'width' => 1,'photo'=>1,
|
||||
'height' => 1,'altitude' => 1, 'latitude' => 1,'longitude' => 1,'views' => 1,'status' => 1,'permission' => 1,'groups' => 1,'license' => 1,
|
||||
'dateTaken' => 1, 'dateUploaded' => 1);
|
||||
'dateTaken' => 1, 'dateUploaded' => 1, 'filenameOriginal' => 1 /* TODO remove in 1.5.0, only used for upgrade */);
|
||||
$patterns = array('exif.*' => 1,'date.*' => 1,'path.*' => 1);
|
||||
foreach($attributes as $key => $val)
|
||||
{
|
||||
|
|
|
@ -59,6 +59,11 @@ class Plugin extends BaseModel
|
|||
return $plugins;
|
||||
}
|
||||
|
||||
public function getConfigObj()
|
||||
{
|
||||
return getConfig();
|
||||
}
|
||||
|
||||
public function invoke($action, $params = null)
|
||||
{
|
||||
$output = '';
|
||||
|
@ -97,7 +102,8 @@ class Plugin extends BaseModel
|
|||
$conf = $inst->defineConf();
|
||||
if(file_exists($confPath = sprintf('%s/plugins/%s.%s.ini', $this->config->paths->userdata, $_SERVER['HTTP_HOST'], $plugin)))
|
||||
{
|
||||
$parsedConf = parse_ini_file($confPath);
|
||||
$configObj = $this->getConfigObj();
|
||||
$parsedConf = parse_ini_string($configObj->getString($confPath));
|
||||
foreach($conf as $name => $tmp)
|
||||
{
|
||||
if(isset($parsedConf[$name]))
|
||||
|
@ -110,16 +116,13 @@ class Plugin extends BaseModel
|
|||
|
||||
public function writeConf($plugin, $string)
|
||||
{
|
||||
$configObj = $this->getConfigObj();
|
||||
$pluginDir = sprintf('%s/plugins', $this->config->paths->userdata);
|
||||
if(!is_dir($pluginDir))
|
||||
{
|
||||
if(!@mkdir($pluginDir))
|
||||
$this->logger->warn(sprintf('Could not create directory at %s', $pluginDir));
|
||||
}
|
||||
|
||||
if($string !== false)
|
||||
{
|
||||
$fileCreated = @file_put_contents($pluginConfFile = sprintf('%s/%s.%s.ini', $pluginDir, $_SERVER['HTTP_HOST'], $plugin), $string) !== false;
|
||||
$pluginConfFile = sprintf('%s/%s.%s.ini', $pluginDir, $_SERVER['HTTP_HOST'], $plugin);
|
||||
$fileCreated = $configObj->write($pluginConfFile, $string) !== false;
|
||||
if(!$fileCreated)
|
||||
$this->logger->warn(sprintf('Could not create file at %s', $pluginConfFile));
|
||||
|
||||
|
|
|
@ -6,11 +6,15 @@
|
|||
*/
|
||||
class PluginBase extends BaseModel
|
||||
{
|
||||
private $pluginName, $pluginConf = null;
|
||||
public function __construct()
|
||||
private $plugin, $pluginName, $pluginConf = null;
|
||||
public function __construct($params = null)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->pluginName = preg_replace('/Plugin$/', '', get_class($this));
|
||||
if(isset($params['plugin']))
|
||||
$this->plugin = $params['plugin'];
|
||||
else
|
||||
$this->plugin = getPlugin();
|
||||
}
|
||||
|
||||
public function defineConf()
|
||||
|
@ -24,7 +28,7 @@ class PluginBase extends BaseModel
|
|||
return $this->pluginConf;
|
||||
|
||||
$this->pluginConf = new stdClass;
|
||||
$conf = getPlugin()->loadConf($this->pluginName);
|
||||
$conf = $this->plugin->loadConf($this->pluginName);
|
||||
foreach($conf as $name => $value)
|
||||
$this->pluginConf->$name = $value;
|
||||
|
||||
|
@ -33,31 +37,25 @@ class PluginBase extends BaseModel
|
|||
|
||||
public function onAction($params)
|
||||
{
|
||||
$this->logger->info('Plugin onAction called');
|
||||
}
|
||||
|
||||
public function onBodyBegin($params = null)
|
||||
{
|
||||
$this->logger->info('Plugin onBodyBegin called');
|
||||
}
|
||||
|
||||
public function onBodyEnd($params = null)
|
||||
{
|
||||
$this->logger->info('Plugin onBodyEnd called');
|
||||
}
|
||||
|
||||
public function onHead($params = null)
|
||||
{
|
||||
$this->logger->info('Plugin onHead called');
|
||||
}
|
||||
|
||||
public function onLoad($params = null)
|
||||
{
|
||||
$this->logger->info('Plugin onLoad called');
|
||||
}
|
||||
|
||||
public function onView($params)
|
||||
{
|
||||
$this->logger->info('Plugin onView called');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ class Theme
|
|||
{
|
||||
$this->theme = self::themeDefault;
|
||||
if(file_exists($mobileSettings = sprintf('%s/%s/config/settings-mobile.ini', getConfig()->get('paths')->themes, $this->getThemeName())))
|
||||
getConfig()->load($mobileSettings);
|
||||
getConfig()->loadString(file_get_contents($mobileSettings));
|
||||
}
|
||||
elseif($themeConfig !== null)
|
||||
{
|
||||
|
|
|
@ -243,7 +243,7 @@ class Upgrade extends BaseModel
|
|||
{
|
||||
foreach($versions as $file)
|
||||
{
|
||||
getLogger()->info(sprintf('Calling executeScript on base file %s', $file));
|
||||
$this->logger->info(sprintf('Calling executeScript on base file %s', $file));
|
||||
$this->executeScript($file);
|
||||
}
|
||||
}
|
||||
|
@ -257,8 +257,8 @@ class Upgrade extends BaseModel
|
|||
{
|
||||
foreach($version as $file)
|
||||
{
|
||||
getLogger()->info(sprintf('Calling executeScript on %s file %s', $database, $file));
|
||||
getDb()->executeScript($file, $database);
|
||||
$this->logger->info(sprintf('Calling executeScript on %s file %s', $database, $file));
|
||||
$this->db->executeScript($file, $database);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -272,8 +272,8 @@ class Upgrade extends BaseModel
|
|||
{
|
||||
foreach($version as $file)
|
||||
{
|
||||
getLogger()->info(sprintf('Calling executeScript on %s file %s', $filesystem, $file));
|
||||
getFs()->executeScript($file, $filesystem);
|
||||
$this->logger->info(sprintf('Calling executeScript on %s file %s', $filesystem, $file));
|
||||
$this->fs->executeScript($file, $filesystem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,9 +6,38 @@ class UserConfig
|
|||
public function __construct($params = null)
|
||||
{
|
||||
if(isset($params['config']))
|
||||
{
|
||||
$this->config = $params['config'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$path = dirname(dirname(dirname(__FILE__)));
|
||||
$params = parse_ini_file(sprintf('%s/configs/defaults.ini', $path), true);
|
||||
if(file_exists($overrideIni = sprintf('%s/configs/override.ini', $path)))
|
||||
{
|
||||
$override = parse_ini_file($overrideIni, true);
|
||||
foreach($override as $key => $value)
|
||||
{
|
||||
if(array_key_exists($key, $params))
|
||||
{
|
||||
if(is_array($value))
|
||||
$params[$key] = array_merge((array)$params[$key], $value);
|
||||
else
|
||||
$params[$key] = $value;
|
||||
}
|
||||
else
|
||||
{
|
||||
$params[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$configParams = array($params['epi']['config']);
|
||||
if(isset($params['epiConfigParams']))
|
||||
$configParams = array_merge($configParams, $params['epiConfigParams']);
|
||||
EpiConfig::employ($configParams);
|
||||
$this->config = getConfig();
|
||||
}
|
||||
|
||||
if(isset($params['utility']))
|
||||
$this->utility = $params['utility'];
|
||||
|
@ -22,7 +51,8 @@ class UserConfig
|
|||
$configFile = $this->getConfigFile();
|
||||
if(!$configFile)
|
||||
return false;
|
||||
return parse_ini_file($configFile, true);
|
||||
$iniString = $this->config->getString($configFile);
|
||||
return parse_ini_string($iniString, true);
|
||||
}
|
||||
|
||||
public function writeSiteSettings($settings)
|
||||
|
@ -41,7 +71,8 @@ class UserConfig
|
|||
|
||||
public function load()
|
||||
{
|
||||
$this->config->load('defaults.ini');
|
||||
$path = dirname(dirname(dirname(__FILE__)));
|
||||
$this->config->loadString(file_get_contents(sprintf('%s/configs/defaults.ini', $path)));
|
||||
$configFile = $this->getConfigFile();
|
||||
|
||||
// backwards compatibility for 1.2.1 -> 1.2.2 upgrade
|
||||
|
@ -55,17 +86,22 @@ class UserConfig
|
|||
|
||||
// we need to load the deps to get the theme modules
|
||||
require $this->config->get('paths')->libraries . '/dependencies.php';
|
||||
|
||||
$this->config->load(sprintf('%s/html/assets/themes/%s/config/settings.ini', dirname(dirname(dirname(__FILE__))), getTheme()->getThemeName()));
|
||||
$utilityObj = $this->getUtility();
|
||||
if($utilityObj->isMobile() && file_exists($mobileSettings = sprintf('%s/html/assets/themes/%s/config/settings-mobile.ini', dirname(dirname(dirname(__FILE__))), getTheme(false)->getThemeName())))
|
||||
$this->config->load($mobileSettings);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function loadTheme()
|
||||
{
|
||||
if(!$this->getConfigFile())
|
||||
return;
|
||||
|
||||
$this->config->loadString(file_get_contents(sprintf('%s/html/assets/themes/%s/config/settings.ini', dirname(dirname(dirname(__FILE__))), getTheme()->getThemeName())));
|
||||
$utilityObj = $this->getUtility();
|
||||
if($utilityObj->isMobile() && file_exists($mobileSettings = sprintf('%s/html/assets/themes/%s/config/settings-mobile.ini', dirname(dirname(dirname(__FILE__))), getTheme(false)->getThemeName())))
|
||||
$this->config->loadString(file_get_contents($mobileSettings));
|
||||
}
|
||||
|
||||
protected function getUtility()
|
||||
{
|
||||
if(isset($this->utility))
|
||||
|
@ -78,7 +114,7 @@ class UserConfig
|
|||
private function getConfigFile()
|
||||
{
|
||||
$configFile = sprintf('%s/userdata/configs/%s.ini', $this->basePath, $this->host);
|
||||
if(!file_exists($configFile))
|
||||
if(!$this->config->exists($configFile))
|
||||
return false;
|
||||
return $configFile;
|
||||
}
|
||||
|
|
|
@ -246,7 +246,7 @@ class Utility
|
|||
{
|
||||
$licenses = $this->getLicenses();
|
||||
$link = '';
|
||||
if(isset($licenses[$key]))
|
||||
if(isset($licenses[$key]) && isset($licenses[$key]['link']))
|
||||
$link = $licenses[$key]['link'];
|
||||
return $this->returnValue($link, $write);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ class FacebookConnectPlugin extends PluginBase
|
|||
return array('id' => null, 'secret' => null);
|
||||
}
|
||||
|
||||
public function onBodyEnd()
|
||||
public function onBodyEnd($params = null)
|
||||
{
|
||||
parent::onBodyEnd();
|
||||
$conf = $this->getConf();
|
||||
|
|
|
@ -17,7 +17,7 @@ class GoogleAnalyticsPlugin extends PluginBase
|
|||
return array('id' => null);
|
||||
}
|
||||
|
||||
public function onHead()
|
||||
public function onHead($params = null)
|
||||
{
|
||||
parent::onHead();
|
||||
$conf = $this->getConf();
|
||||
|
|
|
@ -11,12 +11,12 @@ class HelloWorldPlugin extends PluginBase
|
|||
parent::__construct();
|
||||
}
|
||||
|
||||
public function onBody()
|
||||
public function onBodyBegin($params = null)
|
||||
{
|
||||
parent::onBody();
|
||||
parent::onBodyBegin();
|
||||
}
|
||||
|
||||
public function onView()
|
||||
public function onView($params = null)
|
||||
{
|
||||
parent::onView();
|
||||
}
|
||||
|
|
|
@ -15,6 +15,16 @@ getApi()->get('/action/([a-zA-Z0-9]+)/view.json', array('ApiActionController', '
|
|||
getApi()->post('/action/([a-zA-Z0-9]+)/delete.json', array('ApiActionController', 'delete'), EpiApi::external); // delete an action (/action/{id}/delete.json)
|
||||
getApi()->post('/action/([a-zA-Z0-9]+)/(photo)/create.json', array('ApiActionController', 'create'), EpiApi::external); // post an action (/action/{id}/{type}/create.json)
|
||||
|
||||
/*
|
||||
* Activity endpoints
|
||||
* All activity endpoints follow the same convention.
|
||||
* Everything in []'s are optional
|
||||
* /activit(y|ies)/{action}.json
|
||||
*/
|
||||
getApi()->get('/activities/list.json', array('ApiActivityController', 'list_'), EpiApi::external); // retrieve activities (/activities/list.json)
|
||||
getApi()->get('/activity/([a-zA-Z0-9]+)/view.json', array('ApiActivityController', 'view'), EpiApi::external); // retrieve activity (/activity/:id/view.json)
|
||||
getApi()->post('/activity/create.json', array('ApiActivityController', 'create'), EpiApi::internal); // post an action (/action/{id}/{type}/create.json)
|
||||
|
||||
/*
|
||||
* Photo endpoints
|
||||
* All photo endpoints follow the same convention.
|
||||
|
@ -26,7 +36,8 @@ getApi()->get('/photo/([a-zA-Z0-9]+)/edit.json', array('ApiPhotoController', 'ed
|
|||
getApi()->post('/photo/([a-zA-Z0-9]+)/update.json', array('ApiPhotoController', 'update'), EpiApi::external); // update a photo (/photo/{id}/update.json)
|
||||
getApi()->get('/photo/([a-zA-Z0-9]+)/view.json', array('ApiPhotoController', 'view'), EpiApi::external); // get a photo's information (/photo/view/{id}.json)
|
||||
getApi()->get('/photos/?(.+)?/list.json', array('ApiPhotoController', 'list_'), EpiApi::external); // get all photos / optionally filter (/photos[/{options}]/view.json)
|
||||
getApi()->post('/photos/update.json', array('ApiPhotoController', 'updateBatch'), EpiApi::external); // get all photos / optionally filter (/photos[/{options}]/view.json)
|
||||
getApi()->post('/photos/update.json', array('ApiPhotoController', 'updateBatch'), EpiApi::external); // update multiple photos (/photos/update.json)
|
||||
getApi()->post('/photos/delete.json', array('ApiPhotoController', 'deleteBatch'), EpiApi::external); // delete multiple photos (/photos/delete.json)
|
||||
getApi()->post('/photo/upload.json', array('ApiPhotoController', 'upload'), EpiApi::external); // upload a photo
|
||||
getApi()->get('/photo/([a-zA-Z0-9]+)/url/(\d+)x(\d+)x?([A-Zx]*)?.json', array('ApiPhotoController', 'dynamicUrl'), EpiApi::external); // generate a dynamic photo url (/photo/{id}/url/{options}.json) TODO, make internal for now
|
||||
getApi()->get('/photo/([a-zA-Z0-9]+)/nextprevious/?(.+)?.json', array('ApiPhotoController', 'nextPrevious'), EpiApi::external); // get a photo's next/previous (/photo/{id}/nextprevious[/{options}].json)
|
||||
|
|
|
@ -24,6 +24,7 @@ class CredentialTest extends PHPUnit_Framework_TestCase
|
|||
|
||||
$this->credential = new Credential(array('utility' => $utility));
|
||||
$this->credential->sendHeadersOnError = false;
|
||||
$this->credential->reset();
|
||||
$this->token = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ class PhotoTest extends PHPUnit_Framework_TestCase
|
|||
$params = array('user' => new FauxObject, 'utility' => new stdClass, 'url' => new stdClass, 'image' => new FauxObject);
|
||||
$this->photo = new Photo($params);;
|
||||
$config = new stdClass;
|
||||
$config->site = new stdClass;
|
||||
$config->site->allowOriginalDownload = 1;
|
||||
$secrets = new stdClass;
|
||||
$secrets->secret = 'secret';
|
||||
$config->secrets = $secrets;
|
||||
|
@ -58,6 +60,81 @@ class PhotoTest extends PHPUnit_Framework_TestCase
|
|||
$this->assertEquals(10, $res['photo10x10xCR'][2], 'The height is not correct in the photo array');
|
||||
}
|
||||
|
||||
public function testAddApiUrlsOriginalAsOwner()
|
||||
{
|
||||
$user = $this->getMock('User', array('isOwner'));
|
||||
$user->expects($this->any())
|
||||
->method('isOwner')
|
||||
->will($this->returnValue(true));
|
||||
$url = $this->getMock('Url', array('photoView'));
|
||||
$url->expects($this->any())
|
||||
->method('photoView')
|
||||
->will($this->returnValue('/url'));
|
||||
$utility = $this->getMock('Utility', array('getProtocol'));
|
||||
$utility->expects($this->any())
|
||||
->method('getProtocol')
|
||||
->will($this->returnValue('http'));
|
||||
$config = $this->photo->config;
|
||||
$config->site->allowOriginalDownload = 0;
|
||||
$this->photo->inject('user', $user);
|
||||
$this->photo->inject('url', $url);
|
||||
$this->photo->inject('utility', $utility);
|
||||
$this->photo->inject('config', $config);
|
||||
|
||||
$res = $this->photo->addApiUrls($this->photoData, array('10x10'));
|
||||
$this->assertTrue(isset($res['pathOriginal']));
|
||||
}
|
||||
|
||||
public function testAddApiUrlsOriginalAsNonOwner()
|
||||
{
|
||||
$user = $this->getMock('User', array('isOwner'));
|
||||
$user->expects($this->any())
|
||||
->method('isOwner')
|
||||
->will($this->returnValue(false));
|
||||
$url = $this->getMock('Url', array('photoView'));
|
||||
$url->expects($this->any())
|
||||
->method('photoView')
|
||||
->will($this->returnValue('/url'));
|
||||
$utility = $this->getMock('Utility', array('getProtocol'));
|
||||
$utility->expects($this->any())
|
||||
->method('getProtocol')
|
||||
->will($this->returnValue('http'));
|
||||
$config = $this->photo->config;
|
||||
$config->site->allowOriginalDownload = 1;
|
||||
$this->photo->inject('user', $user);
|
||||
$this->photo->inject('url', $url);
|
||||
$this->photo->inject('utility', $utility);
|
||||
$this->photo->inject('config', $config);
|
||||
|
||||
$res = $this->photo->addApiUrls($this->photoData, array('10x10'));
|
||||
$this->assertTrue(isset($res['pathOriginal']));
|
||||
}
|
||||
|
||||
public function testAddApiUrlsOriginalNotAllowed()
|
||||
{
|
||||
$user = $this->getMock('User', array('isOwner'));
|
||||
$user->expects($this->any())
|
||||
->method('isOwner')
|
||||
->will($this->returnValue(false));
|
||||
$url = $this->getMock('Url', array('photoView'));
|
||||
$url->expects($this->any())
|
||||
->method('photoView')
|
||||
->will($this->returnValue('/url'));
|
||||
$utility = $this->getMock('Utility', array('getProtocol'));
|
||||
$utility->expects($this->any())
|
||||
->method('getProtocol')
|
||||
->will($this->returnValue('http'));
|
||||
$config = $this->photo->config;
|
||||
$config->site->allowOriginalDownload = 0;
|
||||
$this->photo->inject('user', $user);
|
||||
$this->photo->inject('url', $url);
|
||||
$this->photo->inject('utility', $utility);
|
||||
$this->photo->inject('config', $config);
|
||||
|
||||
$res = $this->photo->addApiUrls($this->photoData, array('10x10'));
|
||||
$this->assertFalse(isset($res['pathOriginal']));
|
||||
}
|
||||
|
||||
public function testDeleteCouldNotGetPhoto()
|
||||
{
|
||||
$db = $this->getMock('db', array('getPhoto'));
|
||||
|
@ -280,10 +357,11 @@ class PhotoTest extends PHPUnit_Framework_TestCase
|
|||
{
|
||||
// This *should* work
|
||||
$now = time();
|
||||
$ym = date('Ym');
|
||||
$res = $this->photo->generatePaths('foobar');
|
||||
$this->assertNotEquals("/original/201201/{$now}-foobar", $res['pathOriginal'], 'original path not correct, if it is a timestamp mismatch - ignore');
|
||||
$this->assertTrue(preg_match('#/original/201201/[a-z0-9]{6}-foobar#', $res['pathOriginal']) == 1, 'original path not correct, if it is a timestamp mismatch - ignore');
|
||||
$this->assertEquals("/base/201201/{$now}-foobar", $res['pathBase'], 'base path not correct, if it is a timestamp mismatch - ignore');
|
||||
$this->assertNotEquals("/original/{$ym}/{$now}-foobar", $res['pathOriginal'], 'original path not correct, if it is a timestamp mismatch - ignore');
|
||||
$this->assertTrue(preg_match("#/original/{$ym}/[a-z0-9]{6}-foobar#", $res['pathOriginal']) == 1, 'original path not correct, if it is a timestamp mismatch - ignore');
|
||||
$this->assertEquals("/base/{$ym}/{$now}-foobar", $res['pathBase'], 'base path not correct, if it is a timestamp mismatch - ignore');
|
||||
}
|
||||
|
||||
public function testGenerateUrlOriginal()
|
||||
|
|
|
@ -8,7 +8,7 @@ class PluginBaseTest extends PHPUnit_Framework_TestCase
|
|||
{
|
||||
public function setUp()
|
||||
{
|
||||
$this->pluginBase = new PluginBase;
|
||||
$this->pluginBase = new PluginBase(array('plugin' => new FauxObject));
|
||||
}
|
||||
|
||||
public function testDefineConf()
|
||||
|
|
|
@ -35,7 +35,7 @@ class UserConfigTest extends PHPUnit_Framework_TestCase
|
|||
mkdir("{$this->userConfigDir}/userdata/configs");
|
||||
}
|
||||
|
||||
$params = array('utility' => new FauxObject);
|
||||
$params = array('utility' => new FauxObject, 'config' => new FauxObject);
|
||||
$_SERVER['HTTP_HOST'] = 'example.com';
|
||||
$this->userConfig = new UserConfigWrapper($params);
|
||||
$this->userConfig->inject('basePath', $this->userConfigDir);
|
||||
|
@ -54,7 +54,15 @@ class UserConfigTest extends PHPUnit_Framework_TestCase
|
|||
|
||||
public function testGetSiteSettingsWithoutSections()
|
||||
{
|
||||
file_put_contents("{$this->userConfigDir}/userdata/configs/example.com.ini", "foo=bar");
|
||||
$config = $this->getMock('config', array('getString','exists'));
|
||||
$config->expects($this->any())
|
||||
->method('getString')
|
||||
->will($this->returnValue('foo=bar'));
|
||||
$config->expects($this->any())
|
||||
->method('exists')
|
||||
->will($this->returnValue(true));
|
||||
$this->userConfig->inject('config', $config);
|
||||
|
||||
$res = $this->userConfig->getSiteSettings();
|
||||
$expected = array('foo' => 'bar');
|
||||
$this->assertEquals($expected, $res);
|
||||
|
@ -62,7 +70,15 @@ class UserConfigTest extends PHPUnit_Framework_TestCase
|
|||
|
||||
public function testGetSiteSettingsWithSections()
|
||||
{
|
||||
file_put_contents("{$this->userConfigDir}/userdata/configs/example.com.ini", "[stuff]\nfoo=bar");
|
||||
$config = $this->getMock('config', array('getString','exists'));
|
||||
$config->expects($this->any())
|
||||
->method('getString')
|
||||
->will($this->returnValue("[stuff]\nfoo=bar"));
|
||||
$config->expects($this->any())
|
||||
->method('exists')
|
||||
->will($this->returnValue(true));
|
||||
$this->userConfig->inject('config', $config);
|
||||
|
||||
$res = $this->userConfig->getSiteSettings();
|
||||
$expected = array('stuff' => array('foo' => 'bar'));
|
||||
$this->assertEquals($expected, $res);
|
||||
|
@ -70,12 +86,24 @@ class UserConfigTest extends PHPUnit_Framework_TestCase
|
|||
|
||||
public function testGetSiteSettingsDNE()
|
||||
{
|
||||
$config = $this->getMock('config', array('exists'));
|
||||
$config->expects($this->any())
|
||||
->method('exists')
|
||||
->will($this->returnValue(false));
|
||||
$this->userConfig->inject('config', $config);
|
||||
|
||||
$res = $this->userConfig->getSiteSettings();
|
||||
$this->assertFalse($res);
|
||||
}
|
||||
|
||||
public function testWriteSiteSettingsDNE()
|
||||
{
|
||||
$config = $this->getMock('config', array('exists'));
|
||||
$config->expects($this->any())
|
||||
->method('exists')
|
||||
->will($this->returnValue(false));
|
||||
$this->userConfig->inject('config', $config);
|
||||
|
||||
$res = $this->userConfig->writeSiteSettings(array('foo'));
|
||||
$this->assertFalse($res);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue