Initial checkin v13.0

This commit is contained in:
Thilina Hasantha
2015-10-10 20:18:50 +05:30
parent 5fdd19b2c5
commit eb3439b29d
1396 changed files with 318492 additions and 0 deletions

556
src/api/AdapterBase.js Normal file
View File

@@ -0,0 +1,556 @@
/*
This file is part of Ice Framework.
Ice Framework is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Ice Framework is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Ice Framework. If not, see <http://www.gnu.org/licenses/>.
------------------------------------------------------------------
Original work Copyright (c) 2012 [Gamonoid Media Pvt. Ltd]
Developer: Thilina Hasantha (thilina.hasantha[at]gmail.com / facebook.com/thilinah)
*/
function AdapterBase(endPoint) {
}
this.moduleRelativeURL = null;
this.tableData = new Array();
this.sourceData = new Array();
this.filter = null;
this.origFilter = null;
this.orderBy = null;
this.currentElement = null;
AdapterBase.inherits(IceHRMBase);
AdapterBase.method('initAdapter' , function(endPoint,tab,filter,orderBy) {
this.moduleRelativeURL = baseUrl;
this.table = endPoint;
if(tab == undefined || tab == null){
this.tab = endPoint;
}else{
this.tab = tab;
}
if(filter == undefined || filter == null){
this.filter = null;
}else{
this.filter = filter;
}
this.origFilter = this.filter;
if(orderBy == undefined || orderBy == null){
this.orderBy = null;
}else{
this.orderBy = orderBy;
}
this.trackEvent("initAdapter",tab);
this.requestCache = new RequestCache();
});
AdapterBase.method('setFilter', function(filter) {
this.filter = filter;
});
AdapterBase.method('getFilter', function() {
return this.filter;
});
AdapterBase.method('setOrderBy', function(orderBy) {
this.orderBy = orderBy;
});
AdapterBase.method('getOrderBy', function() {
return this.orderBy;
});
/**
* @method add
* @param object {Array} object data to be added to database
* @param getFunctionCallBackData {Array} once a success is returned call get() function for this module with these parameters
* @param callGetFunction {Boolean} if false the get function of the module will not be called (default: true)
* @param successCallback {Function} this will get called after success response
*/
AdapterBase.method('add', function(object,getFunctionCallBackData,callGetFunction,successCallback) {
var that = this;
if(callGetFunction == undefined || callGetFunction == null){
callGetFunction = true;
}
$(object).attr('a','add');
$(object).attr('t',this.table);
$.post(this.moduleRelativeURL, object, function(data) {
if(data.status == "SUCCESS"){
that.addSuccessCallBack(getFunctionCallBackData,data.object, callGetFunction, successCallback, that);
}else{
that.addFailCallBack(getFunctionCallBackData,data.object);
}
},"json");
this.trackEvent("add",this.tab,this.table);
});
AdapterBase.method('addSuccessCallBack', function(callBackData,serverData, callGetFunction, successCallback, thisObject) {
if(callGetFunction){
this.get(callBackData);
}
this.initFieldMasterData();
if(successCallback != undefined && successCallback != null){
successCallback.apply(thisObject,[serverData]);
}
this.trackEvent("addSuccess",this.tab,this.table);
});
AdapterBase.method('addFailCallBack', function(callBackData,serverData) {
this.showMessage("Error saving",serverData);
this.trackEvent("addFailed",this.tab,this.table);
});
AdapterBase.method('deleteObj', function(id,callBackData) {
var that = this;
$.post(this.moduleRelativeURL, {'t':this.table,'a':'delete','id':id}, function(data) {
if(data.status == "SUCCESS"){
that.deleteSuccessCallBack(callBackData,data.object);
}else{
that.deleteFailCallBack(callBackData,data.object);
}
},"json");
this.trackEvent("delete",this.tab,this.table);
});
AdapterBase.method('deleteSuccessCallBack', function(callBackData,serverData) {
this.get(callBackData);
this.clearDeleteParams();
});
AdapterBase.method('deleteFailCallBack', function(callBackData,serverData) {
this.clearDeleteParams();
this.showMessage("Error Occurred while Deleting Item",serverData);
});
AdapterBase.method('get', function(callBackData) {
var that = this;
if(this.getRemoteTable()){
this.createTableServer(this.getTableName());
$("#"+this.getTableName()+'Form').hide();
$("#"+this.getTableName()).show();
return;
}
var sourceMappingJson = JSON.stringify(this.getSourceMapping());
var filterJson = "";
if(this.getFilter() != null){
filterJson = JSON.stringify(this.getFilter());
}
var orderBy = "";
if(this.getOrderBy() != null){
orderBy = this.getOrderBy();
}
sourceMappingJson = this.fixJSON(sourceMappingJson);
filterJson = this.fixJSON(filterJson);
$.post(this.moduleRelativeURL, {'t':this.table,'a':'get','sm':sourceMappingJson,'ft':filterJson,'ob':orderBy}, function(data) {
if(data.status == "SUCCESS"){
that.getSuccessCallBack(callBackData,data.object);
}else{
that.getFailCallBack(callBackData,data.object);
}
},"json");
that.initFieldMasterData();
this.trackEvent("get",this.tab,this.table);
//var url = this.getDataUrl();
//console.log(url);
});
AdapterBase.method('getDataUrl', function(columns) {
var that = this;
var sourceMappingJson = JSON.stringify(this.getSourceMapping());
var columns = JSON.stringify(columns);
var filterJson = "";
if(this.getFilter() != null){
filterJson = JSON.stringify(this.getFilter());
}
var orderBy = "";
if(this.getOrderBy() != null){
orderBy = this.getOrderBy();
}
var url = this.moduleRelativeURL.replace("service.php","data.php");
url = url+"?"+"t="+this.table;
url = url+"&"+"sm="+this.fixJSON(sourceMappingJson);
url = url+"&"+"cl="+this.fixJSON(columns);
url = url+"&"+"ft="+this.fixJSON(filterJson);
url = url+"&"+"ob="+orderBy;
if(this.isSubProfileTable()){
url = url+"&"+"type=sub";
}
if(this.remoteTableSkipProfileRestriction()){
url = url+"&"+"skip=1";
}
return url;
});
AdapterBase.method('isSubProfileTable', function() {
return false;
});
AdapterBase.method('remoteTableSkipProfileRestriction', function() {
return false;
});
AdapterBase.method('preProcessTableData', function(row) {
return row;
});
AdapterBase.method('getSuccessCallBack', function(callBackData,serverData) {
var data = [];
var mapping = this.getDataMapping();
for(var i=0;i<serverData.length;i++){
var row = [];
for(var j=0;j<mapping.length;j++){
row[j] = serverData[i][mapping[j]];
}
data.push(this.preProcessTableData(row));
}
this.sourceData = serverData;
if(callBackData['callBack']!= undefined && callBackData['callBack'] != null){
if(callBackData['callBackData'] == undefined || callBackData['callBackData'] == null){
callBackData['callBackData'] = new Array();
}
callBackData['callBackData'].push(serverData);
callBackData['callBackData'].push(data);
this.callFunction(callBackData['callBack'],callBackData['callBackData']);
}
this.tableData = data;
if(callBackData['noRender']!= undefined && callBackData['noRender'] != null && callBackData['noRender'] == true){
}else{
this.createTable(this.getTableName());
$("#"+this.getTableName()+'Form').hide();
$("#"+this.getTableName()).show();
}
});
AdapterBase.method('getFailCallBack', function(callBackData,serverData) {
});
AdapterBase.method('getElement', function(id,callBackData) {
var that = this;
var sourceMappingJson = JSON.stringify(this.getSourceMapping());
sourceMappingJson = this.fixJSON(sourceMappingJson);
$.post(this.moduleRelativeURL, {'t':this.table,'a':'getElement','id':id,'sm':sourceMappingJson}, function(data) {
if(data.status == "SUCCESS"){
that.getElementSuccessCallBack.apply(that,[callBackData,data.object]);
}else{
that.getElementFailCallBack.apply(that,[callBackData,data.object]);
}
},"json");
this.trackEvent("getElement",this.tab,this.table);
});
AdapterBase.method('getElementSuccessCallBack', function(callBackData,serverData) {
if(callBackData['callBack']!= undefined && callBackData['callBack'] != null){
if(callBackData['callBackData'] == undefined || callBackData['callBackData'] == null){
callBackData['callBackData'] = new Array();
}
callBackData['callBackData'].push(serverData);
this.callFunction(callBackData['callBack'],callBackData['callBackData'],this);
}
this.currentElement = serverData;
if(callBackData['noRender']!= undefined && callBackData['noRender'] != null && callBackData['noRender'] == true){
}else{
this.renderForm(serverData);
}
});
AdapterBase.method('getElementFailCallBack', function(callBackData,serverData) {
});
AdapterBase.method('getTableData', function() {
return this.tableData;
});
AdapterBase.method('getTableName', function() {
return this.tab;
});
AdapterBase.method('getFieldValues', function(fieldMaster,callBackData) {
var that = this;
var method = "";
var methodParams = "";
if(fieldMaster[3] != undefined && fieldMaster[3] != null){
method = fieldMaster[3];
}
if(fieldMaster[4] != undefined && fieldMaster[4] != null){
methodParams = JSON.stringify(fieldMaster[4]);
}
var key = this.requestCache.getKey(this.moduleRelativeURL,{'t':fieldMaster[0],'key':fieldMaster[1],'value':fieldMaster[2],'method':method,'methodParams':methodParams,'a':'getFieldValues'});
var cacheData = this.requestCache.getData(key);
if(cacheData != null){
if(cacheData.status == "SUCCESS"){
callBackData['callBackData'].push(cacheData.data);
if(callBackData['callBackSuccess'] != null && callBackData['callBackSuccess'] != undefined){
callBackData['callBackData'].push(callBackData['callBackSuccess']);
}
that.callFunction(callBackData['callBack'],callBackData['callBackData']);
}
}
var callbackWraper = function(data) {
if(data.status == "SUCCESS"){
that.requestCache.setData(this.success.key, data);
callBackData['callBackData'].push(data.data);
if(callBackData['callBackSuccess'] != null && callBackData['callBackSuccess'] != undefined){
callBackData['callBackData'].push(callBackData['callBackSuccess']);
}
that.callFunction(callBackData['callBack'],callBackData['callBackData']);
}
};
callbackWraper.key = key;
$.post(this.moduleRelativeURL, {'t':fieldMaster[0],'key':fieldMaster[1],'value':fieldMaster[2],'method':method,'methodParams':methodParams,'a':'getFieldValues'}, callbackWraper,"json");
});
AdapterBase.method('setAdminProfile', function(empId) {
var that = this;
$.post(this.moduleRelativeURL, {'a':'setAdminEmp','empid':empId}, function(data) {
top.location.href = clientUrl;
},"json");
});
AdapterBase.method('customAction', function(subAction,module,request,callBackData) {
var that = this;
request = this.fixJSON(request);
$.getJSON(this.moduleRelativeURL, {'t':this.table,'a':'ca','sa':subAction,'mod':module,'req':request}, function(data) {
if(data.status == "SUCCESS"){
callBackData['callBackData'].push(data.data);
that.callFunction(callBackData['callBackSuccess'],callBackData['callBackData']);
}else{
callBackData['callBackData'].push(data.data);
that.callFunction(callBackData['callBackFail'],callBackData['callBackData']);
}
});
});
AdapterBase.method('sendCustomRequest', function(action,params,successCallback,failCallback) {
var that = this;
params['a'] = action;
$.post(this.moduleRelativeURL, params, function(data) {
if(data.status == "SUCCESS"){
successCallback(data['data']);
}else{
failCallback(data['data']);
}
},"json");
});
AdapterBase.method('getCustomActionUrl', function(action,params) {
params['a'] = action;
var str = "";
for(var key in params){
if(params.hasOwnProperty(key)){
if(str != ""){
str += "&";
}
str += key + "=" + params[key];
}
}
return this.moduleRelativeURL+"?"+str;
});
AdapterBase.method('getClientDataUrl', function() {
return this.moduleRelativeURL.replace("service.php","")+"data/";
});
AdapterBase.method('getCustomUrl', function(str) {
return this.moduleRelativeURL.replace("service.php",str);
});
/**
* @class SubAdapterBase
* @param endPoint
* @param tab
* @param filter
* @param orderBy
* @returns
*/
function SubAdapterBase(endPoint,tab,filter,orderBy) {
this.initAdapter(endPoint,tab,filter,orderBy);
}
SubAdapterBase.inherits(AdapterBase);
SubAdapterBase.method('deleteRow', function(id) {
this.deleteParams['id'] = id;
this.confirmDelete();
});
SubAdapterBase.method('createTable', function(elementId) {
var item, itemHtml,itemDelete,itemEdit;
var data = this.getTableData();
var deleteButton = '<button id="#_id_#_delete" onclick="modJs.subModJsList[\'tab'+elementId+'\'].deleteRow(\'_id_\');return false;" type="button" style="position: absolute;bottom: 5px;right: 5px;font-size: 13px;" tooltip="Delete"><li class="fa fa-times"></li></button>';
var editButton = '<button id="#_id_#_edit" onclick="modJs.subModJsList[\'tab'+elementId+'\'].edit(\'_id_\');return false;" type="button" style="position: absolute;bottom: 5px;right: 35px;font-size: 13px;" tooltip="Edit"><li class="fa fa-edit"></li></button>';
var table = $('<div class="list-group"></div>');
//add Header
var header = this.getSubHeader();
table.append(header);
if(data.length == 0){
table.append('<a href="#" class="list-group-item">'+this.getNoDataMessage()+'</a>');
}else{
for(var i=0;i<data.length;i++){
item = data[i];
itemDelete = deleteButton.replace(/_id_/g,item[0]);
itemEdit = editButton.replace(/_id_/g,item[0]);
itemHtml = this.getSubItemHtml(item,itemDelete,itemEdit);
table.append(itemHtml);
}
}
$("#"+elementId).html("");
$("#"+elementId).append(table);
$('#plainMessageModel').modal('hide');
});
SubAdapterBase.method('getNoDataMessage', function() {
return "No data found";
});
SubAdapterBase.method('getSubHeader', function() {
var header = $('<a href="#" onclick="return false;" class="list-group-item" style="background:#eee;"><h4 class="list-group-item-heading">'+this.getSubHeaderTitle()+'</h4></a>');
return header;
});
/**
* IdNameAdapter
*/
function IdNameAdapter(endPoint) {
this.initAdapter(endPoint);
}
IdNameAdapter.inherits(AdapterBase);
IdNameAdapter.method('getDataMapping', function() {
return [
"id",
"name"
];
});
IdNameAdapter.method('getHeaders', function() {
return [
{ "sTitle": "ID" ,"bVisible":false},
{ "sTitle": "Name"}
];
});
IdNameAdapter.method('getFormFields', function() {
return [
[ "id", {"label":"ID","type":"hidden"}],
[ "name", {"label":"Name","type":"text","validation":""}]
];
});
/**
* RequestCache
*/
function RequestCache() {
}
RequestCache.method('getKey', function(url,params) {
var key = url+"|";
for(index in params){
key += index+"="+params[index]+"|";
}
return key;
});
RequestCache.method('getData', function(key) {
var data;
if (typeof(Storage) == "undefined") {
return null;
}
var strData = localStorage.getItem(key);
if(strData != undefined && strData != null && strData != ""){
return JSON.parse(strData);
}
return null;
});
RequestCache.method('setData', function(key, data) {
if (typeof(Storage) == "undefined") {
return null;
}
var strData = JSON.stringify(data);
var strData = localStorage.setItem(key,strData);
return strData;
});

503
src/api/AesCrypt.js Normal file
View File

@@ -0,0 +1,503 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* AES implementation in JavaScript (c) Chris Veness 2005-2014 / MIT Licence */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* jshint node:true *//* global define */
'use strict';
/**
* AES (Rijndael cipher) encryption routines,
*
* Reference implementation of FIPS-197 http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf.
*
* @namespace
*/
var Aes = {};
/**
* AES Cipher function: encrypt 'input' state with Rijndael algorithm [§5.1];
* applies Nr rounds (10/12/14) using key schedule w for 'add round key' stage.
*
* @param {number[]} input - 16-byte (128-bit) input state array.
* @param {number[][]} w - Key schedule as 2D byte-array (Nr+1 x Nb bytes).
* @returns {number[]} Encrypted output state array.
*/
Aes.cipher = function(input, w) {
var Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES)
var Nr = w.length/Nb - 1; // no of rounds: 10/12/14 for 128/192/256-bit keys
var state = [[],[],[],[]]; // initialise 4xNb byte-array 'state' with input [§3.4]
for (var i=0; i<4*Nb; i++) state[i%4][Math.floor(i/4)] = input[i];
state = Aes.addRoundKey(state, w, 0, Nb);
for (var round=1; round<Nr; round++) {
state = Aes.subBytes(state, Nb);
state = Aes.shiftRows(state, Nb);
state = Aes.mixColumns(state, Nb);
state = Aes.addRoundKey(state, w, round, Nb);
}
state = Aes.subBytes(state, Nb);
state = Aes.shiftRows(state, Nb);
state = Aes.addRoundKey(state, w, Nr, Nb);
var output = new Array(4*Nb); // convert state to 1-d array before returning [§3.4]
for (var i=0; i<4*Nb; i++) output[i] = state[i%4][Math.floor(i/4)];
return output;
};
/**
* Perform key expansion to generate a key schedule from a cipher key [§5.2].
*
* @param {number[]} key - Cipher key as 16/24/32-byte array.
* @returns {number[][]} Expanded key schedule as 2D byte-array (Nr+1 x Nb bytes).
*/
Aes.keyExpansion = function(key) {
var Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES)
var Nk = key.length/4; // key length (in words): 4/6/8 for 128/192/256-bit keys
var Nr = Nk + 6; // no of rounds: 10/12/14 for 128/192/256-bit keys
var w = new Array(Nb*(Nr+1));
var temp = new Array(4);
// initialise first Nk words of expanded key with cipher key
for (var i=0; i<Nk; i++) {
var r = [key[4*i], key[4*i+1], key[4*i+2], key[4*i+3]];
w[i] = r;
}
// expand the key into the remainder of the schedule
for (var i=Nk; i<(Nb*(Nr+1)); i++) {
w[i] = new Array(4);
for (var t=0; t<4; t++) temp[t] = w[i-1][t];
// each Nk'th word has extra transformation
if (i % Nk == 0) {
temp = Aes.subWord(Aes.rotWord(temp));
for (var t=0; t<4; t++) temp[t] ^= Aes.rCon[i/Nk][t];
}
// 256-bit key has subWord applied every 4th word
else if (Nk > 6 && i%Nk == 4) {
temp = Aes.subWord(temp);
}
// xor w[i] with w[i-1] and w[i-Nk]
for (var t=0; t<4; t++) w[i][t] = w[i-Nk][t] ^ temp[t];
}
return w;
};
/**
* Apply SBox to state S [§5.1.1]
* @private
*/
Aes.subBytes = function(s, Nb) {
for (var r=0; r<4; r++) {
for (var c=0; c<Nb; c++) s[r][c] = Aes.sBox[s[r][c]];
}
return s;
};
/**
* Shift row r of state S left by r bytes [§5.1.2]
* @private
*/
Aes.shiftRows = function(s, Nb) {
var t = new Array(4);
for (var r=1; r<4; r++) {
for (var c=0; c<4; c++) t[c] = s[r][(c+r)%Nb]; // shift into temp copy
for (var c=0; c<4; c++) s[r][c] = t[c]; // and copy back
} // note that this will work for Nb=4,5,6, but not 7,8 (always 4 for AES):
return s; // see asmaes.sourceforge.net/rijndael/rijndaelImplementation.pdf
};
/**
* Combine bytes of each col of state S [§5.1.3]
* @private
*/
Aes.mixColumns = function(s, Nb) {
for (var c=0; c<4; c++) {
var a = new Array(4); // 'a' is a copy of the current column from 's'
var b = new Array(4); // 'b' is a•{02} in GF(2^8)
for (var i=0; i<4; i++) {
a[i] = s[i][c];
b[i] = s[i][c]&0x80 ? s[i][c]<<1 ^ 0x011b : s[i][c]<<1;
}
// a[n] ^ b[n] is a•{03} in GF(2^8)
s[0][c] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3]; // {02}•a0 + {03}•a1 + a2 + a3
s[1][c] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3]; // a0 • {02}•a1 + {03}•a2 + a3
s[2][c] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3]; // a0 + a1 + {02}•a2 + {03}•a3
s[3][c] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3]; // {03}•a0 + a1 + a2 + {02}•a3
}
return s;
};
/**
* Xor Round Key into state S [§5.1.4]
* @private
*/
Aes.addRoundKey = function(state, w, rnd, Nb) {
for (var r=0; r<4; r++) {
for (var c=0; c<Nb; c++) state[r][c] ^= w[rnd*4+c][r];
}
return state;
};
/**
* Apply SBox to 4-byte word w
* @private
*/
Aes.subWord = function(w) {
for (var i=0; i<4; i++) w[i] = Aes.sBox[w[i]];
return w;
};
/**
* Rotate 4-byte word w left by one byte
* @private
*/
Aes.rotWord = function(w) {
var tmp = w[0];
for (var i=0; i<3; i++) w[i] = w[i+1];
w[3] = tmp;
return w;
};
// sBox is pre-computed multiplicative inverse in GF(2^8) used in subBytes and keyExpansion [§5.1.1]
Aes.sBox = [0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16];
// rCon is Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)] [§5.2]
Aes.rCon = [ [0x00, 0x00, 0x00, 0x00],
[0x01, 0x00, 0x00, 0x00],
[0x02, 0x00, 0x00, 0x00],
[0x04, 0x00, 0x00, 0x00],
[0x08, 0x00, 0x00, 0x00],
[0x10, 0x00, 0x00, 0x00],
[0x20, 0x00, 0x00, 0x00],
[0x40, 0x00, 0x00, 0x00],
[0x80, 0x00, 0x00, 0x00],
[0x1b, 0x00, 0x00, 0x00],
[0x36, 0x00, 0x00, 0x00] ];
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
if (typeof module != 'undefined' && module.exports) module.exports = Aes; // CommonJs export
if (typeof define == 'function' && define.amd) define([], function() { return Aes; }); // AMD
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* AES Counter-mode implementation in JavaScript (c) Chris Veness 2005-2014 / MIT Licence */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* jshint node:true *//* global define, escape, unescape, btoa, atob */
'use strict';
if (typeof module!='undefined' && module.exports) var Aes = require('./aes'); // CommonJS (Node.js)
/**
* Aes.Ctr: Counter-mode (CTR) wrapper for AES.
*
* This encrypts a Unicode string to produces a base64 ciphertext using 128/192/256-bit AES,
* and the converse to decrypt an encrypted ciphertext.
*
* See http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
*
* @augments Aes
*/
Aes.Ctr = {};
/**
* Encrypt a text using AES encryption in Counter mode of operation.
*
* Unicode multi-byte character safe
*
* @param {string} plaintext - Source text to be encrypted.
* @param {string} password - The password to use to generate a key.
* @param {number} nBits - Number of bits to be used in the key; 128 / 192 / 256.
* @returns {string} Encrypted text.
*
* @example
* var encr = Aes.Ctr.encrypt('big secret', 'pāşšŵōřđ', 256); // encr: 'lwGl66VVwVObKIr6of8HVqJr'
*/
Aes.Ctr.encrypt = function(plaintext, password, nBits) {
var blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES
if (!(nBits==128 || nBits==192 || nBits==256)) return ''; // standard allows 128/192/256 bit keys
plaintext = String(plaintext).utf8Encode();
password = String(password).utf8Encode();
// use AES itself to encrypt password to get cipher key (using plain password as source for key
// expansion) - gives us well encrypted key (though hashed key might be preferred for prod'n use)
var nBytes = nBits/8; // no bytes in key (16/24/32)
var pwBytes = new Array(nBytes);
for (var i=0; i<nBytes; i++) { // use 1st 16/24/32 chars of password for key
pwBytes[i] = isNaN(password.charCodeAt(i)) ? 0 : password.charCodeAt(i);
}
var key = Aes.cipher(pwBytes, Aes.keyExpansion(pwBytes)); // gives us 16-byte key
key = key.concat(key.slice(0, nBytes-16)); // expand key to 16/24/32 bytes long
// initialise 1st 8 bytes of counter block with nonce (NIST SP800-38A §B.2): [0-1] = millisec,
// [2-3] = random, [4-7] = seconds, together giving full sub-millisec uniqueness up to Feb 2106
var counterBlock = new Array(blockSize);
var nonce = (new Date()).getTime(); // timestamp: milliseconds since 1-Jan-1970
var nonceMs = nonce%1000;
var nonceSec = Math.floor(nonce/1000);
var nonceRnd = Math.floor(Math.random()*0xffff);
// for debugging: nonce = nonceMs = nonceSec = nonceRnd = 0;
for (var i=0; i<2; i++) counterBlock[i] = (nonceMs >>> i*8) & 0xff;
for (var i=0; i<2; i++) counterBlock[i+2] = (nonceRnd >>> i*8) & 0xff;
for (var i=0; i<4; i++) counterBlock[i+4] = (nonceSec >>> i*8) & 0xff;
// and convert it to a string to go on the front of the ciphertext
var ctrTxt = '';
for (var i=0; i<8; i++) ctrTxt += String.fromCharCode(counterBlock[i]);
// generate key schedule - an expansion of the key into distinct Key Rounds for each round
var keySchedule = Aes.keyExpansion(key);
var blockCount = Math.ceil(plaintext.length/blockSize);
var ciphertxt = new Array(blockCount); // ciphertext as array of strings
for (var b=0; b<blockCount; b++) {
// set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
// done in two stages for 32-bit ops: using two words allows us to go past 2^32 blocks (68GB)
for (var c=0; c<4; c++) counterBlock[15-c] = (b >>> c*8) & 0xff;
for (var c=0; c<4; c++) counterBlock[15-c-4] = (b/0x100000000 >>> c*8);
var cipherCntr = Aes.cipher(counterBlock, keySchedule); // -- encrypt counter block --
// block size is reduced on final block
var blockLength = b<blockCount-1 ? blockSize : (plaintext.length-1)%blockSize+1;
var cipherChar = new Array(blockLength);
for (var i=0; i<blockLength; i++) { // -- xor plaintext with ciphered counter char-by-char --
cipherChar[i] = cipherCntr[i] ^ plaintext.charCodeAt(b*blockSize+i);
cipherChar[i] = String.fromCharCode(cipherChar[i]);
}
ciphertxt[b] = cipherChar.join('');
}
// use Array.join() for better performance than repeated string appends
var ciphertext = ctrTxt + ciphertxt.join('');
ciphertext = ciphertext.base64Encode();
return ciphertext;
};
/**
* Decrypt a text encrypted by AES in counter mode of operation
*
* @param {string} ciphertext - Source text to be encrypted.
* @param {string} password - Password to use to generate a key.
* @param {number} nBits - Number of bits to be used in the key; 128 / 192 / 256.
* @returns {string} Decrypted text
*
* @example
* var decr = Aes.Ctr.encrypt('lwGl66VVwVObKIr6of8HVqJr', 'pāşšŵōřđ', 256); // decr: 'big secret'
*/
Aes.Ctr.decrypt = function(ciphertext, password, nBits) {
var blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES
if (!(nBits==128 || nBits==192 || nBits==256)) return ''; // standard allows 128/192/256 bit keys
ciphertext = String(ciphertext).base64Decode();
password = String(password).utf8Encode();
// use AES to encrypt password (mirroring encrypt routine)
var nBytes = nBits/8; // no bytes in key
var pwBytes = new Array(nBytes);
for (var i=0; i<nBytes; i++) {
pwBytes[i] = isNaN(password.charCodeAt(i)) ? 0 : password.charCodeAt(i);
}
var key = Aes.cipher(pwBytes, Aes.keyExpansion(pwBytes));
key = key.concat(key.slice(0, nBytes-16)); // expand key to 16/24/32 bytes long
// recover nonce from 1st 8 bytes of ciphertext
var counterBlock = new Array(8);
var ctrTxt = ciphertext.slice(0, 8);
for (var i=0; i<8; i++) counterBlock[i] = ctrTxt.charCodeAt(i);
// generate key schedule
var keySchedule = Aes.keyExpansion(key);
// separate ciphertext into blocks (skipping past initial 8 bytes)
var nBlocks = Math.ceil((ciphertext.length-8) / blockSize);
var ct = new Array(nBlocks);
for (var b=0; b<nBlocks; b++) ct[b] = ciphertext.slice(8+b*blockSize, 8+b*blockSize+blockSize);
ciphertext = ct; // ciphertext is now array of block-length strings
// plaintext will get generated block-by-block into array of block-length strings
var plaintxt = new Array(ciphertext.length);
for (var b=0; b<nBlocks; b++) {
// set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
for (var c=0; c<4; c++) counterBlock[15-c] = ((b) >>> c*8) & 0xff;
for (var c=0; c<4; c++) counterBlock[15-c-4] = (((b+1)/0x100000000-1) >>> c*8) & 0xff;
var cipherCntr = Aes.cipher(counterBlock, keySchedule); // encrypt counter block
var plaintxtByte = new Array(ciphertext[b].length);
for (var i=0; i<ciphertext[b].length; i++) {
// -- xor plaintxt with ciphered counter byte-by-byte --
plaintxtByte[i] = cipherCntr[i] ^ ciphertext[b].charCodeAt(i);
plaintxtByte[i] = String.fromCharCode(plaintxtByte[i]);
}
plaintxt[b] = plaintxtByte.join('');
}
// join array of blocks into single plaintext string
var plaintext = plaintxt.join('');
plaintext = plaintext.utf8Decode(); // decode from UTF8 back to Unicode multi-byte chars
return plaintext;
};
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/** Extend String object with method to encode multi-byte string to utf8
* - monsur.hossa.in/2012/07/20/utf-8-in-javascript.html */
if (typeof String.prototype.utf8Encode == 'undefined') {
String.prototype.utf8Encode = function() {
return unescape( encodeURIComponent( this ) );
};
}
/** Extend String object with method to decode utf8 string to multi-byte */
if (typeof String.prototype.utf8Decode == 'undefined') {
String.prototype.utf8Decode = function() {
try {
return decodeURIComponent( escape( this ) );
} catch (e) {
return this; // invalid UTF-8? return as-is
}
};
}
/** Extend String object with method to encode base64
* - developer.mozilla.org/en-US/docs/Web/API/window.btoa, nodejs.org/api/buffer.html
* note: if btoa()/atob() are not available (eg IE9-), try github.com/davidchambers/Base64.js */
if (typeof String.prototype.base64Encode == 'undefined') {
String.prototype.base64Encode = function() {
if (typeof btoa != 'undefined') return btoa(this); // browser
if (typeof Buffer != 'undefined') return new Buffer(this, 'utf8').toString('base64'); // Node.js
throw new Error('No Base64 Encode');
};
}
/** Extend String object with method to decode base64 */
if (typeof String.prototype.base64Decode == 'undefined') {
String.prototype.base64Decode = function() {
if (typeof atob != 'undefined') return atob(this); // browser
if (typeof Buffer != 'undefined') return new Buffer(this, 'base64').toString('utf8'); // Node.js
throw new Error('No Base64 Decode');
};
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
if (typeof module != 'undefined' && module.exports) module.exports = Aes.Ctr; // CommonJs export
if (typeof define == 'function' && define.amd) define(['Aes'], function() { return Aes.Ctr; }); // AMD
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Encrypt/decrypt files */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function encryptFile(file) {
// use FileReader.readAsArrayBuffer to handle binary files
var reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = function(evt) {
$('body').css({'cursor':'wait'});
// Aes.Ctr.encrypt expects a string, but converting binary file directly to string could
// give invalid Unicode sequences, so convert bytestream ArrayBuffer to single-byte chars
var contentBytes = new Uint8Array(reader.result); // ≡ evt.target.result
var contentStr = '';
for (var i=0; i<contentBytes.length; i++) {
contentStr += String.fromCharCode(contentBytes[i]);
}
var password = $('#password-file').val();
var t1 = new Date();
var ciphertext = Aes.Ctr.encrypt(contentStr, password, 256);
var t2 = new Date();
// use Blob to save encrypted file
var blob = new Blob([ciphertext], { type: 'text/plain' });
var filename = file.name+'.encrypted';
saveAs(blob, filename);
$('#encrypt-file-time').html(((t2 - t1)/1000)+'s'); // display time taken
$('body').css({'cursor':'default'});
}
}
function decryptFile(file) {
// use FileReader.ReadAsText to read (base64-encoded) ciphertext file
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(evt) {
$('body').css({'cursor':'wait'});
var content = reader.result; // ≡ evt.target.result
var password = $('#password-file').val();
var t1 = new Date();
var plaintext = Aes.Ctr.decrypt(content, password, 256);
var t2 = new Date();
// convert single-byte character stream to ArrayBuffer bytestream
var contentBytes = new Uint8Array(plaintext.length);
for (var i=0; i<plaintext.length; i++) {
contentBytes[i] = plaintext.charCodeAt(i);
}
// use Blob to save decrypted file
var blob = new Blob([contentBytes], { type: 'application/octet-stream' });
var filename = file.name.replace(/\.encrypted$/,'')+'.decrypted';
saveAs(blob, filename);
$('#decrypt-file-time').html(((t2 - t1)/1000)+'s'); // display time taken
$('body').css({'cursor':'default'});
}
}

2052
src/api/Base.js Normal file

File diff suppressed because it is too large Load Diff

270
src/api/FormValidation.js Normal file
View File

@@ -0,0 +1,270 @@
/*
This file is part of Ice Framework.
Ice Framework is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Ice Framework is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Ice Framework. If not, see <http://www.gnu.org/licenses/>.
------------------------------------------------------------------
Original work Copyright (c) 2012 [Gamonoid Media Pvt. Ltd]
Developer: Thilina Hasantha (thilina.hasantha[at]gmail.com / facebook.com/thilinah)
*/
function FormValidation(formId,validateAll,options) {
this.tempOptions = {};
this.formId = formId;
this.formError = false;
this.formObject = null;
this.errorMessages = "";
this.popupDialog = null;
this.validateAll = validateAll;
this.errorMap = new Array();
this.settings = {"thirdPartyPopup":null,"LabelErrorClass":false, "ShowPopup":true};
this.settings = jQuery.extend(this.settings,options);
this.inputTypes = new Array( "text", "radio", "checkbox", "file", "password", "select-one","select-multi", "textarea","fileupload");
this.validator = {
float: function (str) {
var floatstr = /^[-+]?[0-9]+(\.[0-9]+)?$/;
if (str != null && str.match(floatstr)) {
return true;
} else {
return false;
}
},
number: function (str) {
var numstr = /^[0-9]+$/;
if (str != null && str.match(numstr)) {
return true;
} else {
return false;
}
},
numberOrEmpty: function (str) {
if(str == ""){
return true;
}
var numstr = /^[0-9]+$/;
if (str != null && str.match(numstr)) {
return true;
} else {
return false;
}
},
email: function (str) {
var emailPattern = /^\s*[\w\-\+_]+(\.[\w\-\+_]+)*\@[\w\-\+_]+\.[\w\-\+_]+(\.[\w\-\+_]+)*\s*$/;
return str != null && emailPattern.test(str);
},
emailOrEmpty: function (str) {
if(str == ""){
return true;
}
var emailPattern = /^\s*[\w\-\+_]+(\.[\w\-\+_]+)*\@[\w\-\+_]+\.[\w\-\+_]+(\.[\w\-\+_]+)*\s*$/;
return str != null && emailPattern.test(str);
},
username: function (str) {
var username = /^[a-zA-Z0-9]+$/;
return str != null && username.test(str);
},
input: function (str) {
if (str != null && str.length > 0) {
return true;
} else {
return false;
}
}
};
}
FormValidation.method('clearError' , function(formInput, overrideMessage) {
var id = formInput.attr("id");
$('#'+ this.formId +' #field_'+id).removeClass('error');
$('#'+ this.formId +' #help_'+id).html('');
});
FormValidation.method('addError' , function(formInput, overrideMessage) {
this.formError = true;
if(formInput.attr("message") != null) {
this.errorMessages += (formInput.attr("message") + "\n");
this.errorMap[formInput.attr("name")] = formInput.attr("message");
}else{
this.errorMap[formInput.attr("name")] = "";
}
var id = formInput.attr("id");
var validation = formInput.attr("validation");
var message = formInput.attr("validation");
$('#'+ this.formId +' #field_'+id).addClass('error');
if(message == undefined || message == null || message == ""){
$('#'+ this.formId +' #help_'+id).html(message);
}else{
if(validation == undefined || validation == null || validation == ""){
$('#'+ this.formId +' #help_'+id).html("Required");
}else{
if(validation == "float" || validation == "number"){
$('#'+ this.formId +' #help_'+id).html("Number required");
}else if(validation == "email"){
$('#'+ this.formId +' #help_'+id).html("Email required");
}else{
$('#'+ this.formId +' #help_'+id).html("Required");
}
}
}
});
FormValidation.method('showErrors' , function() {
if(this.formError) {
if(this.settings['thirdPartyPopup'] != undefined && this.settings['thirdPartyPopup'] != null){
this.settings['thirdPartyPopup'].alert();
}else{
if(this.settings['ShowPopup'] == true){
if(this.tempOptions['popupTop'] != undefined && this.tempOptions['popupTop'] != null){
this.alert("Errors Found",this.errorMessages,this.tempOptions['popupTop']);
}else{
this.alert("Errors Found",this.errorMessages,-1);
}
}
}
}
});
FormValidation.method('checkValues' , function(options) {
this.tempOptions = options;
var that = this;
this.formError = false;
this.errorMessages = "";
this.formObject = new Object();
var validate = function (inputObject) {
if(that.settings['LabelErrorClass'] != false){
$("label[for='" + name + "']").removeClass(that.settings['LabelErrorClass']);
}
var id = inputObject.attr("id");
var name = inputObject.attr("name");
var type = inputObject.attr("type");
if(inputObject.hasClass('select2-focusser') || inputObject.hasClass('select2-input')){
return true;
}
if(jQuery.inArray(type, that.inputTypes ) >= 0) {
if(inputObject.hasClass('uploadInput')){
inputValue = inputObject.attr("val");
//}else if(inputObject.hasClass('datetimeInput')){
//inputValue = inputObject.getDate()+":00";
}else{
//inputValue = (type == "radio" || type == "checkbox")?$("input[name='" + name + "']:checked").val():inputObject.val();
inputValue = null;
if(type == "radio" || type == "checkbox"){
inputValue = $("input[name='" + name + "']:checked").val();
}else if(inputObject.hasClass('select2Field')){
if($('#'+id).select2('data') != null && $('#'+id).select2('data') != undefined){
inputValue = $('#'+id).select2('data').id;
}else{
inputValue = "";
}
}else if(inputObject.hasClass('select2Multi')){
if($('#'+id).select2('data') != null && $('#'+id).select2('data') != undefined){
inputValueObjects = $('#'+id).select2('data');
inputValue = [];
for(var i=0;i<inputValueObjects.length;i++){
inputValue.push(inputValueObjects[i].id);
}
inputValue = JSON.stringify(inputValue);
}else{
inputValue = "";
}
}else{
inputValue = inputObject.val();
}
}
var validation = inputObject.attr('validation');
var valid = false;
if(validation != undefined && validation != null && that.validator[validation] != undefined && that.validator[validation] != null){
valid = that.validator[validation](inputValue);
}else{
if(that.validateAll){
if(validation != undefined && validation != null && validation == "none"){
valid = true;
}else{
valid = that.validator['input'](inputValue);
}
}else{
valid = true;
}
$(that.formObject).attr(id,inputValue);
}
if(!valid) {
that.addError(inputObject, null);
}else{
that.clearError(inputObject, null);
$(that.formObject).attr(id,inputValue);
}
}
};
var inputs = $('#'+ this.formId + " :input");
inputs.each(function() {
var that = $(this);
validate(that);
});
inputs = $('#'+ this.formId + " .uploadInput");
inputs.each(function() {
var that = $(this);
validate(that);
});
this.showErrors();
this.tempOptions = {};
return !this.formError;
});
FormValidation.method('getFormParameters' , function() {
return this.formObject;
});
FormValidation.method('alert', function (title,text,top) {
alert(text);
});

138
src/api/Notifications.js Normal file
View File

@@ -0,0 +1,138 @@
/*
This file is part of Ice Framework.
Ice Framework is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Ice Framework is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Ice Framework. If not, see <http://www.gnu.org/licenses/>.
------------------------------------------------------------------
Original work Copyright (c) 2012 [Gamonoid Media Pvt. Ltd]
Developer: Thilina Hasantha (thilina.hasantha[at]gmail.com / facebook.com/thilinah)
*/
function NotificationManager() {
this.baseUrl = "";
this.templates = {};
}
NotificationManager.method('setBaseUrl' , function(url) {
this.baseUrl = url;
});
NotificationManager.method('setTemplates' , function(data) {
this.templates = data;
});
NotificationManager.method('setTimeUtils' , function(timeUtils) {
this.timeUtils = timeUtils;
});
NotificationManager.method('getNotifications' , function(name, data) {
var that = this;
$.getJSON(this.baseUrl, {'a':'getNotifications'}, function(data) {
if(data.status == "SUCCESS"){
that.renderNotifications(data.data[1],data.data[0]);
}
});
});
NotificationManager.method('clearPendingNotifications' , function(name, data) {
var that = this;
$.getJSON(this.baseUrl, {'a':'clearNotifications'}, function(data) {
});
});
NotificationManager.method('renderNotifications' , function(notifications, unreadCount) {
if(notifications.length == 0){
return;
}
var t = this.templates['notifications'];
if(unreadCount > 0){
t = t.replace('#_count_#',unreadCount);
if(unreadCount > 1){
t = t.replace('#_header_#',"You have "+unreadCount+" new notifications");
}else{
t = t.replace('#_header_#',"You have "+unreadCount+" new notification");
}
}else{
t = t.replace('#_count_#',"");
t = t.replace('#_header_#',"You have no new notifications");
}
var notificationStr = "";
for (index in notifications){
notificationStr += this.renderNotification(notifications[index]);
}
t = t.replace('#_notifications_#',notificationStr);
$obj = $(t);
if(unreadCount == 0){
$obj.find('.label-danger').remove();
}
$obj.attr("id","notifications");
var k = $("#notifications");
k.replaceWith($obj);
$(".navbar .menu").slimscroll({
height: "320px",
alwaysVisible: false,
size: "3px"
}).css("width", "100%");
this.timeUtils.convertToRelativeTime($(".notificationTime"));
});
NotificationManager.method('renderNotification' , function(notification) {
var t = this.templates['notification'];
t = t.replace('#_image_#',notification.image);
try{
var json = JSON.parse(notification.action);
t = t.replace('#_url_#',this.baseUrl.replace('service.php','?')+json['url']);
}catch(e){
t = t.replace('#_url_#',"");
}
t = t.replace('#_time_#',notification.time);
t = t.replace('#_fromName_#',notification.type);
t = t.replace('#_message_#',this.getLineBreakString(notification.message,27));
return t;
});
NotificationManager.method('getLineBreakString' , function(str, len) {
var t = "";
try{
var arr = str.split(" ");
var count = 0;
for(var i=0;i<arr.length;i++){
count += arr[i].length + 1;
if(count > len){
t += arr[i] + "<br/>";
count = 0;
}else{
t += arr[i] + " ";
}
}
}catch(e){}
return t;
});

47
src/api/SocialShare.js Normal file
View File

@@ -0,0 +1,47 @@
function SocialShare(){
};
SocialShare.facebook = function(url) {
var w = 700;
var h = 500;
var left = (screen.width/2)-(w/2);
var top = (screen.height/2)-(h/2);
var url = "https://www.facebook.com/sharer/sharer.php?u="+encodeURIComponent(url);
window.open(url, "Share on Facebook", "width="+w+",height="+h+",left="+left+",top="+top);
return false;
};
SocialShare.google = function(url) {
var w = 500;
var h = 500;
var left = (screen.width/2)-(w/2);
var top = (screen.height/2)-(h/2);
var url = "https://plus.google.com/share?url="+encodeURIComponent(url);
window.open(url, "Share on Google", "width="+w+",height="+h+",left="+left+",top="+top);
return false;
};
SocialShare.linkedin = function(url) {
var w = 500;
var h = 500;
var left = (screen.width/2)-(w/2);
var top = (screen.height/2)-(h/2);
var url = "https://www.linkedin.com/cws/share?url="+encodeURIComponent(url);
window.open(url, "Share on Linked in", "width="+w+",height="+h+",left="+left+",top="+top);
return false;
};
SocialShare.twitter = function(url, msg) {
window.open('http://twitter.com/share?text='+escape(msg) + '&url=' + escape(url),'popup','width=550,height=260,scrollbars=yes,resizable=yes,toolbar=no,directories=no,location=no,menubar=no,status=no,left=200,top=200');
return false;
};

152
src/api/TimeUtils.js Normal file
View File

@@ -0,0 +1,152 @@
/*
This file is part of Ice Framework.
Ice Framework is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Ice Framework is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Ice Framework. If not, see <http://www.gnu.org/licenses/>.
------------------------------------------------------------------
Original work Copyright (c) 2012 [Gamonoid Media Pvt. Ltd]
Developer: Thilina Hasantha (thilina.hasantha[at]gmail.com / facebook.com/thilinah)
*/
function TimeUtils() {
}
TimeUtils.method('setServerGMToffset' , function(serverGMToffset) {
this.serverGMToffset = serverGMToffset;
});
TimeUtils.method('convertToRelativeTime',function(selector) {
var that = this;
var getAmPmTime = function(curHour, curMin) {
var amPm = "am";
var amPmHour = curHour;
if (amPmHour >= 12) {
amPm = "pm";
if (amPmHour > 12) {
amPmHour = amPmHour - 12;
}
}
var prefixCurMin = "";
if (curMin < 10) {
prefixCurMin = "0";
}
var prefixCurHour = "";
if (curHour == 0) {
prefixCurHour = "0";
}
return " at " + prefixCurHour + amPmHour + ":" + prefixCurMin + curMin + amPm;
};
var getBrowserTimeZone = function() {
var current_date = new Date();
var gmt_offset = current_date.getTimezoneOffset() / 60;
return -gmt_offset;
};
var curDate = new Date();
var months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
var timezoneDiff = this.serverGMToffset - getBrowserTimeZone();
var timezoneTimeDiff = timezoneDiff*60*60*1000;
selector.each(function () {
try{
var thisValue = $(this).html();
// Split value into date and time
var thisValueArray = thisValue.split(" ");
var thisValueDate = thisValueArray[0];
var thisValueTime = thisValueArray[1];
// Split date into components
var thisValueDateArray = thisValueDate.split("-");
var curYear = thisValueDateArray[0];
var curMonth = thisValueDateArray[1]-1;
var curDay = thisValueDateArray[2];
// Split time into components
var thisValueTimeArray = thisValueTime.split(":");
var curHour = thisValueTimeArray[0];
var curMin = thisValueTimeArray[1];
var curSec = thisValueTimeArray[2];
// Create this date
var thisDate = new Date(curYear, curMonth, curDay, curHour, curMin, curSec);
var thisTime = thisDate.getTime();
var tzDate = new Date(thisTime - timezoneTimeDiff);
//var tzDay = tzDate.getDay();//getDay will return the day of the week not the month
//var tzDay = tzDate.getUTCDate(); //getUTCDate will return the day of the month
var tzDay = tzDate.toString('d'); //
var tzYear = tzDate.getFullYear();
var tzHour = tzDate.getHours();
var tzMin = tzDate.getMinutes();
// Create the full date
//var fullDate = days[tzDate.getDay()] + ", " + months[tzDate.getMonth()] + " " + tzDay + ", " + tzYear + getAmPmTime(tzHour, tzMin);
var fullDate = days[tzDate.getDay()] + ", " + months[tzDate.getMonth()] + " " + tzDay + ", " + tzYear + getAmPmTime(tzHour, tzMin);
// Get the time different
var timeDiff = (curDate.getTime() - tzDate.getTime())/1000;
var minDiff = Math.abs(timeDiff/60);
var hourDiff = Math.abs(timeDiff/(60*60));
var dayDiff = Math.abs(timeDiff/(60*60*24));
var yearDiff = Math.abs(timeDiff/(60*60*24*365));
// If more than a day old, display the month, day and time (and year, if applicable)
var fbDate = '';
if (dayDiff > 1) {
//fbDate = curDay + " " + months[tzDate.getMonth()].substring(0,3);
fbDate = tzDay + " " + months[tzDate.getMonth()].substring(0,3);
// Add the year, if applicable
if (yearDiff > 1) {
fbDate = fbDate + " "+ curYear;
}
// Add the time
fbDate = fbDate + getAmPmTime(tzHour, tzMin);
}
// Less than a day old, and more than an hour old
else if (hourDiff >= 1) {
var roundedHour = Math.round(hourDiff);
if (roundedHour == 1)
fbDate = "about an hour ago";
else
fbDate = roundedHour + " hours ago";
}
// Less than an hour, and more than a minute
else if (minDiff >= 1) {
var roundedMin = Math.round(minDiff);
if (roundedMin == 1)
fbDate = "about a minute ago";
else
fbDate = roundedMin + " minutes ago";
}
// Less than a minute
else if (minDiff < 1) {
fbDate = "less than a minute ago";
}
// Update this element
$(this).html(fbDate);
$(this).attr('title', fullDate);
}catch(e){}
});
});

26
src/app/config.sample.php Normal file
View File

@@ -0,0 +1,26 @@
<?php
ini_set('error_log', '_LOG_');
define('APP_NAME', 'Ice Framework');
define('FB_URL', 'Ice Framework');
define('TWITTER_URL', 'Ice Framework');
define('CLIENT_NAME', '_CLIENT_');
define('APP_BASE_PATH', '_APP_BASE_PATH_');
define('CLIENT_BASE_PATH', '_CLIENT_BASE_PATH_');
define('BASE_URL','_BASE_URL_');
define('CLIENT_BASE_URL','_CLIENTBASE_URL_');
define('APP_DB', '_APP_DB_');
define('APP_USERNAME', '_APP_USERNAME_');
define('APP_PASSWORD', '_APP_PASSWORD_');
define('APP_HOST', '_APP_HOST_');
define('APP_CON_STR', 'mysql://'.APP_USERNAME.':'.APP_PASSWORD.'@'.APP_HOST.'/'.APP_DB);
//file upload
define('FILE_TYPES', 'jpg,png,jpeg');
define('MAX_FILE_SIZE_KB', 10 * 1024);
//Home Links
define('HOME_LINK_ADMIN', CLIENT_BASE_URL."?g=admin&n=dashboard&m=admin_Admin");
define('HOME_LINK_OTHERS', CLIENT_BASE_URL."?g=modules&n=dashboard&m=module_My_Account");

3
src/app/data.php Normal file
View File

@@ -0,0 +1,3 @@
<?php
include ('config.php');
include (APP_BASE_PATH.'data.php');

View File

@@ -0,0 +1 @@
http://icehrm.com

3
src/app/fileupload.php Normal file
View File

@@ -0,0 +1,3 @@
<?php
include ('config.php');
include (APP_BASE_PATH.'fileupload.php');

View File

@@ -0,0 +1,3 @@
<?php
include ('config.php');
include (APP_BASE_PATH.'fileupload_page.php');

3
src/app/header.php Normal file
View File

@@ -0,0 +1,3 @@
<?php
include (APP_BASE_PATH.'header.php');

22
src/app/index.php Normal file
View File

@@ -0,0 +1,22 @@
<?php
if(!file_exists('config.php')){
header("Location:install/");
exit();
}
include ('config.php');
if(!isset($_REQUEST['g']) || !isset($_REQUEST['n'])){
header("Location:".CLIENT_BASE_URL."login.php");
exit();
}
$group = $_REQUEST['g'];
$name= $_REQUEST['n'];
$groups = array('admin','modules');
if($group == 'admin' || $group == 'modules'){
$name = str_replace("..","",$name);
$name = str_replace("/","",$name);
include APP_BASE_PATH.'/'.$group.'/'.$name.'/index.php';
}else{
exit();
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

2027
src/app/install/bootstrap/js/bootstrap.js vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
<?php
error_reporting(E_ERROR);
ini_set("error_log", "/tmp/icehrm_install.log");
define('CURRENT_PATH',dirname(__FILE__));
define('CLIENT_APP_PATH',realpath(dirname(__FILE__)."/..")."/");
define('APP_PATH',realpath(dirname(__FILE__)."/../..")."/");
define('APP_NAME',"Ice Framework");
define('APP_ID',"ice_framework");

256
src/app/install/index.php Normal file
View File

@@ -0,0 +1,256 @@
<?php
require dirname(__FILE__)."/config.php";
$isConfigFileExists = file_exists(CLIENT_APP_PATH."config.php");
$errorMap = array();
if($isConfigFileExists){
$data = file_get_contents(CLIENT_APP_PATH."config.php");
if($data != ""){
$errorMap[] = array("important","A configuration file exists","Application is already installed. If you want to reinstall, please delete the config file, clear data folder and use a new database during the installation.");
}
}else{
$file = fopen(CLIENT_APP_PATH."config.php","w");
fwrite($file,"");
fclose($file);
}
$isConfigFileWriteable = is_writable(CLIENT_APP_PATH."config.php");
error_log("Config writable ".$isConfigFileWriteable);
error_log("Config exists ".file_exists(CLIENT_APP_PATH."config.php"));
if(!$isConfigFileWriteable){
$errorMap[] = array("important","Configuration file [".CLIENT_APP_PATH."config.php] is not writable","Make this file writable",array("sudo touch ".CLIENT_APP_PATH."config.php","sudo chmod 777 ".CLIENT_APP_PATH."config.php"));
}
$isConfigSampleFileExists = file_exists(CLIENT_APP_PATH."config.sample.php");
if(!$isConfigSampleFileExists){
$errorMap[] = array("important","Sample configuration file doesn't exists","Please check :".CLIENT_APP_PATH."config.sample.php");
}
$isDataFolderExists = is_dir(CLIENT_APP_PATH."data");
$isDataFolderWritable = false;
if(!$isDataFolderExists){
$errorMap[] = array("important","Data directory does not exists","Please create directory :".CLIENT_APP_PATH."data",array("sudo mkdir ".CLIENT_APP_PATH."data"));
}else{
$file = fopen(CLIENT_APP_PATH."data/test.txt","w");
if($file){
fwrite($file,"Test file write");
fclose($file);
$data = file_get_contents(CLIENT_APP_PATH."data/test.txt");
if($data == "Test file write"){
$isDataFolderWritable = true;
}
unlink(CLIENT_APP_PATH."data/test.txt");
}
if(!$isDataFolderWritable){
$errorMap[] = array("important","Data folder is not writable","Provide wirte permission to the web server user to ".CLIENT_APP_PATH."data",array("sudo chmod 777 ".CLIENT_APP_PATH."data"));
}
}
?><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>IceHRM</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<!-- Le styles -->
<link href="bootstrap/css/bootstrap.css" rel="stylesheet">
<script type="text/javascript" src="../../js/jquery.js"></script>
<script src="bootstrap/js/bootstrap.js"></script>
<link href="bootstrap/css/bootstrap-responsive.css" rel="stylesheet">
<link href="styles.css?v=2" rel="stylesheet">
<script type="text/javascript" src="../../js/date.js"></script>
<script type="text/javascript" src="../../js/json2.js"></script>
<script type="text/javascript" src="../../js/CrockfordInheritance.v0.1.js"></script>
<!-- Le fav and touch icons -->
<link rel="shortcut icon" href="bootstrap/ico/favicon.ico">
<!-- IE Fix for HTML5 Tags -->
<!--[if lt IE 9]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<script type="text/javascript">
$(document).ready(function() {
var url = top.location.href;
url = url.substring(0,url.lastIndexOf('/app'));
$("#BASE_URL").val(url);
});
function testDB(){
var request = {};
request["APP_DB"] = $("#APP_DB").val();
request["APP_USERNAME"] = $("#APP_USERNAME").val();
request["APP_PASSWORD"] = $("#APP_PASSWORD").val();
request["APP_HOST"] = $("#APP_HOST").val();
request["action"] = "TEST_DB";
$.post("submit.php",request , function(data) {
if(data.status == "SUCCESS"){
alert(data.msg);
$("#installBtn").removeAttr('disabled');
}else{
alert(data.msg);
}
},"json");
}
function install(){
var request = {};
request["APP_DB"] = $("#APP_DB").val();
request["APP_USERNAME"] = $("#APP_USERNAME").val();
request["APP_PASSWORD"] = $("#APP_PASSWORD").val();
request["APP_HOST"] = $("#APP_HOST").val();
request["action"] = "INS";
request["LOG"] = $("#LOG").val();
request["BASE_URL"] = $("#BASE_URL").val();
if(request["BASE_URL"] == undefined || request["BASE_URL"] == null
|| request["BASE_URL"] == ""){
alert("Invalid Base URL");
return;
}
if(request["BASE_URL"].indexOf("http://") == 0 || request["BASE_URL"].indexOf("https://")){
}else{
alert("Invalid Base URL");
return;
}
if(!endsWith(request["BASE_URL"],"/")){
request["BASE_URL"] = request["BASE_URL"] + "/";
}
$("#installBtn").attr('disabled','disabled');
$.post("submit.php",request , function(data) {
if(data.status == "SUCCESS"){
alert(data.msg);
top.location.href = request["BASE_URL"]+"app/";
}else{
alert(data.msg);
$("#installBtn").removeAttr('disabled');
}
},"json");
}
function endsWith(str,pattern) {
var d = str.length - pattern.length;
return d >= 0 && str.lastIndexOf(pattern) === d;
};
</script>
<div class="container-fluid bgbody" style="max-width:800px;padding-top:10px;margin:auto">
<h1>IceHRM Installation</h1>
<p class="p1">
Please do not install this application if you have already installed (this could currupt existing instalation)
</p>
<?php if(count($errorMap)>0){?>
<?php foreach($errorMap as $error){?>
<p class="p2">
<!--
<span style="" class="label label-<?=$error[0]?>"><?=$error[1]?></span><br/>
-->
<span style="font-size:14px;color:red;font-weight: bold;"><?=$error[1]?></span><br/>
<?=$error[2]?><br/>
<?php if(!empty($error[3]) && is_array($error[3])){?>
<?php foreach($error[3] as $command){?>
<span class="label label-inverse">
<?=$command?></span><br/>
<?php }?>
<?php }?>
</p>
<hr/>
<?php }?>
Once above errors are corrected, please reload the page<br/><br/>
<button onclick="location.reload();;return false;" class="btn">Reload</button>
<?php }else{?>
<form class="form-horizontal" id="install_step1">
<div class="control-group">
<div class="controls">
<span class="label label-warning" id="install_step1_error" style="display:none;"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="LOG">Log file path</label>
<div class="controls">
<input class="input-xxlarge" type="text" id="LOG" name="LOG" value="data/icehrm.log"/>
<span class="help-inline p1">Keep this empty if you want logs to be in web server's default logs</span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="BASE_URL">App Url</label>
<div class="controls">
<input class="input-xxlarge" type="text" id="BASE_URL" name="BASE_URL" value=""/>
<span class="help-inline p1">This is the web path to folder that you copy icehrm sources (e.g http://yourdomain.com/icehrm/)</span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="APP_DB">MySql Database Name</label>
<div class="controls">
<input class="input-xxlarge" type="text" id="APP_DB" name="APP_DB" value="icehrmdb"/>
<span class="help-inline p1">Application DB Name</span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="APP_USERNAME">Database User</label>
<div class="controls">
<input class="input-xxlarge" type="text" id="APP_USERNAME" name="APP_USERNAME" value="icehrmuser"/>
<span class="help-inline p1">Database username</span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="APP_PASSWORD">Database User Password</label>
<div class="controls">
<input class="input-xxlarge" type="password" id="APP_PASSWORD" name="APP_PASSWORD" value=""/>
<span class="help-inline p1">Database user's password</span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="APP_HOST">Database Host</label>
<div class="controls">
<input class="input-xxlarge" type="text" id="APP_HOST" name="APP_HOST" value="localhost"/>
<span class="help-inline p1">MySql DB Host</span>
</div>
</div>
<div class="control-group">
<div class="controls">
<button id="testBtn" onclick="testDB();return false;" class="btn">Test Database Connectivity</button>
<button id="installBtn" onclick="install();return false;" class="btn" disabled="disabled">Install Application</button>
</div>
</div>
</form>
<?php }?>
</div>
<div class="row-fluid" style="height:10px;">
<div class="span12" style="padding:5px;">
<p style="text-align:center;font-size: 10px;">
<?=APP_NAME?> All rights reserved.
</p>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,9 @@
@CHARSET "ISO-8859-1";
.p1{
font-size:11px;
}
.p2{
font-size:12px;
}

137
src/app/install/submit.php Normal file
View File

@@ -0,0 +1,137 @@
<?php
include dirname(__FILE__).'/config.php';
include(CLIENT_APP_PATH.'../adodb512/adodb.inc.php');
$isConfigFileExists = file_exists(CLIENT_APP_PATH."config.php");
$configData = file_get_contents(CLIENT_APP_PATH."config.php");
error_log("isConfigFileExists $isConfigFileExists");
error_log("configData $configData");
$ret = array();
if(!$isConfigFileExists || $configData != ""){
$ret["status"] = "ERROR";
$ret["msg"] = "You are trying to install icehrm on an existing installation.";
echo json_encode($ret);
exit();
}
$action = $_REQUEST['action'];
if($action == "TEST_DB"){
$db = NewADOConnection('mysql');
$res = $db->Connect($_REQUEST["APP_HOST"], $_REQUEST["APP_USERNAME"], $_REQUEST["APP_PASSWORD"], $_REQUEST["APP_DB"]);
if (!$res){
error_log('Could not connect: ' . mysql_error());
$ret["status"] = "ERROR";
$ret["msg"] = "Incorrect credentials or incorrect DB host";
echo json_encode($ret);
exit();
}
$result = $db->Execute("Show tables");
error_log(print_r("Number of tables:".$result->RecordCount(),true));
$num_rows = $result->RecordCount();
if($num_rows != 0){
error_log('Database is not empty: ' . mysql_error());
$ret["status"] = "ERROR";
$ret["msg"] = "Database is not empty";
echo json_encode($ret);
exit();
}
$ret["status"] = "SUCCESS";
$ret["msg"] = "Successfully connected to the database";
echo json_encode($ret);
}else if($action == "INS"){
$config = file_get_contents(CLIENT_APP_PATH."config.sample.php");
if(empty($config)){
error_log('Sample config file is empty');
$ret["status"] = "ERROR";
$ret["msg"] = "Sample config file not found";
echo json_encode($ret);
exit();
}
$config = str_replace("_LOG_", $_REQUEST['LOG'], $config);
$config = str_replace("_APP_BASE_PATH_", APP_PATH, $config);
$config = str_replace("_CLIENT_BASE_PATH_", CLIENT_APP_PATH, $config);
$config = str_replace("_BASE_URL_", $_REQUEST['BASE_URL'], $config);
$config = str_replace("_CLIENTBASE_URL_", $_REQUEST['BASE_URL']."app/", $config);
$config = str_replace("_APP_DB_", $_REQUEST['APP_DB'], $config);
$config = str_replace("_APP_USERNAME_", $_REQUEST['APP_USERNAME'], $config);
$config = str_replace("_APP_PASSWORD_", $_REQUEST['APP_PASSWORD'], $config);
$config = str_replace("_APP_HOST_", $_REQUEST['APP_HOST'], $config);
$config = str_replace("_CLIENT_", 'app', $config);
$con = mysql_connect($_REQUEST["APP_HOST"],$_REQUEST["APP_USERNAME"],$_REQUEST["APP_PASSWORD"]);
$db = NewADOConnection('mysql');
$res = $db->Connect($_REQUEST["APP_HOST"], $_REQUEST["APP_USERNAME"], $_REQUEST["APP_PASSWORD"], $_REQUEST["APP_DB"]);
if (!$res){
error_log('Could not connect: ' . mysql_error());
$ret["status"] = "ERROR";
$ret["msg"] = "Incorrect credentials or incorrect DB host";
echo json_encode($ret);
exit();
}
$result = $db->Execute("Show tables");
error_log(print_r("Number of tables:".$result->RecordCount(),true));
$num_rows = $result->RecordCount();
if($num_rows != 0){
error_log('Database is not empty: ' . mysql_error());
$ret["status"] = "ERROR";
$ret["msg"] = "Database is not empty";
echo json_encode($ret);
exit();
}
//Run create table script
$insql = file_get_contents(CLIENT_APP_PATH."../scripts/".APP_ID."db.sql");
$sql_list = preg_split('/;/',$insql);
foreach($sql_list as $sql){
if (preg_match('/^\s+$/', $sql) || $sql == '') { # skip empty lines
continue;
}
$db->Execute($sql);
}
//Run create table script
$insql = file_get_contents(CLIENT_APP_PATH."../scripts/".APP_ID."_master_data.sql");
$sql_list = preg_split('/;/',$insql);
foreach($sql_list as $sql){
if (preg_match('/^\s+$/', $sql) || $sql == '') { # skip empty lines
continue;
}
$db->Execute($sql);
}
//Write config file
$file = fopen(CLIENT_APP_PATH."config.php","w");
if($file){
fwrite($file,$config);
fclose($file);
}else{
error_log('Unable to write configurations to file');
$ret["status"] = "ERROR";
$ret["msg"] = "Unable to write configurations to file";
echo json_encode($ret);
exit();
}
$ret["status"] = "SUCCESS";
$ret["msg"] = "Successfully installed. Please rename or delete install folder";
echo json_encode($ret);
}

3
src/app/login.php Normal file
View File

@@ -0,0 +1,3 @@
<?php
include ('config.php');
include (APP_BASE_PATH.'login.php');

3
src/app/logout.php Normal file
View File

@@ -0,0 +1,3 @@
<?php
include ('config.php');
include (APP_BASE_PATH.'logout.php');

3
src/app/rest.php Normal file
View File

@@ -0,0 +1,3 @@
<?php
include ('config.php');
include (APP_BASE_PATH.'rest.php');

3
src/app/service.php Normal file
View File

@@ -0,0 +1,3 @@
<?php
include ('config.php');
include (APP_BASE_PATH.'service.php');

1058
src/bootstrap/css/bootstrap-responsive.css vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

5774
src/bootstrap/css/bootstrap.css vendored Normal file

File diff suppressed because it is too large Load Diff

9
src/bootstrap/css/bootstrap.min.css vendored Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

2027
src/bootstrap/js/bootstrap.js vendored Normal file

File diff suppressed because it is too large Load Diff

6
src/bootstrap/js/bootstrap.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,35 @@
<?php
/*
This file is part of Ice Framework.
Ice Framework is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Ice Framework is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Ice Framework. If not, see <http://www.gnu.org/licenses/>.
------------------------------------------------------------------
Original work Copyright (c) 2012 [Gamonoid Media Pvt. Ltd]
Developer: Thilina Hasantha (thilina.hasantha[at]gmail.com / facebook.com/thilinah)
*/
abstract class AbstractInitialize{
var $baseService = null;
public function setBaseService($baseService){
$this->baseService = $baseService;
}
public function getCurrentProfileId(){
return $this->baseService->getCurrentProfileId();
}
public abstract function init();
}

View File

@@ -0,0 +1,115 @@
<?php
/**
*The base class for module manager classes. ModuleManager classes which extend this class provide core backend functionality
*to each module such as defining models, error handliing and other configuration details
*@class AbstractModuleManager
*/
abstract class AbstractModuleManager{
private $fileFieldMappings = array();
private $userClasses = array();
private $errorMappings = array();
private $modelClasses = array();
/**
* Override this method in module manager class to define user classes.
* A user class is a class that is mapped to a table having a field named profile. The profile field is mapped to the id of a Profile element.
* When a user is saving this type of an object in db, profile field will be set to the id of the Profile of currently logged in or switched user.
* When a user is retriving this type of records, only the records having profile field set to currently logged in users profile id will be released.
* @method initializeUserClasses
* @example
public function initializeUserClasses(){
$this->addUserClass("EmployeeDocument");
}
*
*/
public abstract function initializeUserClasses();
/**
* Override this method in module manager class to define file field mappings. If you have a table field that stores a name of a file which need to be
* deleted from the disk when the record is deleted a file field mapping should be added.
* @method initializeFieldMappings
* @example
public function initializeFieldMappings(){
$this->addFileFieldMapping('EmployeeDocument', 'attachment', 'name');
}
*/
public abstract function initializeFieldMappings();
/**
* Override this method in module manager class to define DB error mappings. Some actions to your model classes trigger database errors.
* These errors need to be translated to user friendly texts using DB error mappings
* @method initializeDatabaseErrorMappings
* @example
public function initializeDatabaseErrorMappings(){
$this->addDatabaseErrorMapping('CONSTRAINT `Fk_User_Employee` FOREIGN KEY',"Can not delete Employee, please delete the User for this employee first.");
$this->addDatabaseErrorMapping("Duplicate entry|for key 'employee'","A duplicate entry found");
}
*/
public abstract function initializeDatabaseErrorMappings();
/**
* Override this method in module manager class to add model classes to this module. All the model classes defind for the module should be added here
* @method setupModuleClassDefinitions
* @example
public function setupModuleClassDefinitions(){
$this->addModelClass('Employee');
$this->addModelClass('EmploymentStatus');
}
*/
public abstract function setupModuleClassDefinitions();
public function setupRestEndPoints(){
}
public function setupFileFieldMappings(&$fileFields){
foreach ($this->fileFieldMappings as $mapping){
if(empty($fileFields[$mapping[0]])){
$fileFields[$mapping[0]] = array();
}
$fileFields[$mapping[0]][$mapping[1]] = $mapping[2];
}
}
public function setupUserClasses(&$userTables){
foreach($this->userClasses as $className){
if(!in_array($className, $userTables)){
$userTables[] = $className;
}
}
}
public function setupErrorMappings(&$mysqlErrors){
foreach($this->errorMappings as $name=>$desc){
$mysqlErrors[$name] = $desc;
}
}
public function getModelClasses(){
return $this->modelClasses;
}
protected function addFileFieldMapping($className, $fieldName, $fileTableFieldName){
$this->fileFieldMappings[] = array($className, $fieldName, $fileTableFieldName);
}
protected function addUserClass($className){
$this->userClasses[] = $className;
}
protected function addDatabaseErrorMapping($error, $description){
$this->errorMappings[$error] = $description;
}
protected function addModelClass($className){
$this->modelClasses[] = $className;
}
}

1119
src/classes/BaseService.php Normal file

File diff suppressed because it is too large Load Diff

33
src/classes/CronUtils.php Normal file
View File

@@ -0,0 +1,33 @@
<?php
class CronUtils{
var $clientBasePath;
var $cronFile;
private static $me = null;
private function __construct($clientBasePath, $cronFile){
$this->clientBasePath = $clientBasePath;
$this->cronFile = $cronFile;
}
public static function getInstance($clientBasePath, $cronFile){
if(empty(self::$me)){
self::$me = new CronUtils($clientBasePath, $cronFile);
}
return self::$me;
}
public function run(){
$ams = scandir($this->clientBasePath);
foreach($ams as $am){
if(is_dir($this->clientBasePath.$am) && $am != '.' && $am != '..'){
$command = "php ".$this->cronFile." -c".$this->clientBasePath.$am;
echo "Run:".$command."\r\n";
passthru($command, $res);
echo "Result :".$res."\r\n";
}
}
}
}

260
src/classes/EmailSender.php Normal file
View File

@@ -0,0 +1,260 @@
<?php
/*
* This file is part of Ice Framework.
* Copyright Thilina Hasantha (thilina.hasantha[at]gmail.com | http://facebook.com/thilinah | https://twitter.com/thilina84)
* Licensed under MIT (https://github.com/thilinah/ice-framework/master/LICENSE)
*/
use Aws\Ses\SesClient;
abstract class EmailSender{
var $settings = null;
public function __construct($settings){
$this->settings = $settings;
}
public function sendEmail($subject, $toEmail, $template, $params, $ccList = array(), $bccList = array()){
$body = $template;
foreach($params as $k=>$v){
$body = str_replace("#_".$k."_#", $v, $body);
}
$fromEmail = APP_NAME." <".$this->settings->getSetting("Email: Email From").">";
//Convert to an html email
$emailBody = file_get_contents(APP_BASE_PATH.'/templates/email/emailBody.html');
$emailBody = str_replace("#_emailBody_#", $body, $emailBody);
$emailBody = str_replace("#_logourl_#",
BASE_URL."images/logo.png"
, $emailBody);
$user = new User();
$user->load("username = ?",array('admin'));
if(empty($user->id)){
$users = $user->Find("user_level = ?",array('Admin'));
$user = $users[0];
}
$emailBody = str_replace("#_adminEmail_#", $user->email, $emailBody);
$emailBody = str_replace("#_url_#", CLIENT_BASE_URL, $emailBody);
foreach($params as $k=>$v){
$emailBody = str_replace("#_".$k."_#", $v, $emailBody);
}
$this->sendMail($subject, $emailBody, $toEmail, $fromEmail, $user->email, $ccList, $bccList);
}
public function sendEmailWithoutWrap($subject, $toEmail, $template, $params, $ccList = array(), $bccList = array()){
$body = $template;
foreach($params as $k=>$v){
$body = str_replace("#_".$k."_#", $v, $body);
}
$fromEmail = APP_NAME." <".$this->settings->getSetting("Email: Email From").">";
//Convert to an html email
$emailBody = $body;
$emailBody = str_replace("#_logourl_#",
BASE_URL."images/logo.png"
, $emailBody);
$user = new User();
$user->load("username = ?",array('admin'));
if(empty($user->id)){
$users = $user->Find("user_level = ?",array('Admin'));
$user = $users[0];
}
$emailBody = str_replace("#_adminEmail_#", $user->email, $emailBody);
$emailBody = str_replace("#_url_#", CLIENT_BASE_URL, $emailBody);
foreach($params as $k=>$v){
$emailBody = str_replace("#_".$k."_#", $v, $emailBody);
}
$this->sendMail($subject, $emailBody, $toEmail, $fromEmail, $user->email, $ccList, $bccList);
}
protected abstract function sendMail($subject, $body, $toEmail, $fromEmail, $replyToEmail = null, $ccList = array(), $bccList = array());
public function sendResetPasswordEmail($emailOrUserId){
$user = new User();
$user->Load("email = ?",array($emailOrUserId));
if(empty($user->id)){
$user = new User();
$user->Load("username = ?",array($emailOrUserId));
if(empty($user->id)){
return false;
}
}
$params = array();
//$params['user'] = $user->first_name." ".$user->last_name;
$params['url'] = CLIENT_BASE_URL;
$newPassHash = array();
$newPassHash["CLIENT_NAME"] = CLIENT_NAME;
$newPassHash["oldpass"] = $user->password;
$newPassHash["email"] = $user->email;
$newPassHash["time"] = time();
$json = json_encode($newPassHash);
$encJson = AesCtr::encrypt($json, $user->password, 256);
$encJson = urlencode($user->id."-".$encJson);
$params['passurl'] = CLIENT_BASE_URL."service.php?a=rsp&key=".$encJson;
$emailBody = file_get_contents(APP_BASE_PATH.'/templates/email/passwordReset.html');
$this->sendEmail("[".APP_NAME."] Password Change Request", $user->email, $emailBody, $params);
return true;
}
}
class SNSEmailSender extends EmailSender{
var $ses = null;
public function __construct($settings){
parent::__construct($settings);
$arr = array(
'key' => $this->settings->getSetting('Email: Amazon Access Key ID'),
'secret' => $this->settings->getSetting('Email: Amazon Secret Access Key'),
'region' => AWS_REGION
);
//$this->ses = new AmazonSES($arr);
$this->ses = SesClient::factory($arr);
}
protected function sendMail($subject, $body, $toEmail, $fromEmail, $replyToEmail = null, $ccList = array(), $bccList = array()) {
if(empty($replyToEmail)){
$replyToEmail = $fromEmail;
}
LogManager::getInstance()->info("Sending email to: ".$toEmail."/ from: ".$fromEmail);
$toArray = array('ToAddresses' => array($toEmail),
'CcAddresses' => $ccList,
'BccAddresses' => $bccList);
$message = array(
'Subject' => array(
'Data' => $subject,
'Charset' => 'UTF-8'
),
'Body' => array(
'Html' => array(
'Data' => $body,
'Charset' => 'UTF-8'
)
)
);
//$response = $this->ses->sendEmail($fromEmail, $toArray, $message);
$response = $this->ses->sendEmail(
array(
'Source'=>$fromEmail,
'Destination'=>$toArray,
'Message'=>$message,
'ReplyToAddresses' => array($replyToEmail),
'ReturnPath' => $fromEmail
)
);
LogManager::getInstance()->info("SES Response:".print_r($response,true));
return $response;
}
}
class SMTPEmailSender extends EmailSender{
public function __construct($settings){
parent::__construct($settings);
}
protected function sendMail($subject, $body, $toEmail, $fromEmail, $replyToEmail = null, $ccList = array(), $bccList = array()) {
if(empty($replyToEmail)){
$replyToEmail = $fromEmail;
}
LogManager::getInstance()->info("Sending email to: ".$toEmail."/ from: ".$fromEmail);
$host = $this->settings->getSetting("Email: SMTP Host");
$username = $this->settings->getSetting("Email: SMTP User");
$password = $this->settings->getSetting("Email: SMTP Password");
$port = $this->settings->getSetting("Email: SMTP Port");
if(empty($port)){
$port = '25';
}
if($this->settings->getSetting("Email: SMTP Authentication Required") == "0"){
$auth = array ('host' => $host,
'auth' => false);
}else{
$auth = array ('host' => $host,
'auth' => true,
'username' => $username,
'port' => $port,
'password' => $password);
}
$smtp = Mail::factory('smtp',$auth);
$headers = array ('MIME-Version' => '1.0',
'Content-type' => 'text/html',
'charset' => 'iso-8859-1',
'From' => $fromEmail,
'To' => $toEmail,
'Reply-To' => $replyToEmail,
'Subject' => $subject);
$mail = $smtp->send($toEmail, $headers, $body);
return true;
}
}
class PHPMailer extends EmailSender{
public function __construct($settings){
parent::__construct($settings);
}
protected function sendMail($subject, $body, $toEmail, $fromEmail, $replyToEmail = null, $ccList = array(), $bccList = array()) {
if(empty($replyToEmail)){
$replyToEmail = $fromEmail;
}
LogManager::getInstance()->info("Sending email to: ".$toEmail."/ from: ".$fromEmail);
$headers = 'MIME-Version: 1.0' . "\r\n";
$headers .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n";
$headers .= 'From: '.$fromEmail. "\r\n";
$headers .= 'ReplyTo: '.$replyToEmail. "\r\n";
$headers .= 'Ice-Mailer: PHP/' . phpversion();
// Mail it
$res = mail($toEmail, $subject, $body, $headers);
LogManager::getInstance()->info("PHP mailer result : ".$res);
return true;
}
}

View File

@@ -0,0 +1,4 @@
<?php
class ErrorCodes{
}

307
src/classes/FileService.php Normal file
View File

@@ -0,0 +1,307 @@
<?php
/*
This file is part of Ice Framework.
Ice Framework is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Ice Framework is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Ice Framework. If not, see <http://www.gnu.org/licenses/>.
------------------------------------------------------------------
Original work Copyright (c) 2012 [Gamonoid Media Pvt. Ltd]
Developer: Thilina Hasantha (thilina.hasantha[at]gmail.com / facebook.com/thilinah)
*/
class FileService{
private static $me = null;
private $memcache;
private function __construct(){
}
public static function getInstance(){
if(empty(self::$me)){
self::$me = new FileService();
}
return self::$me;
}
public function getFromCache($key){
try{
if(empty($this->memcache)){
$this->memcache = new Memcached();
$this->memcache->addServer("127.0.0.1", 11211);
}
$data = $this->memcache->get($key);
if(!empty($data)){
return $data;
}
return null;
}catch(Exception $e){
return null;
}
}
public function saveInCache($key, $data, $expire){
try{
if(empty($this->memcache)){
$this->memcache = new Memcached();
$this->memcache->addServer("127.0.0.1", 11211);
}
$this->memcache->set($key,$data, $expire);
}catch(Exception $e){
}
}
public function checkAddSmallProfileImage($profileImage){
$file = new File();
$file->Load('name = ?',array($profileImage->name."_small"));
if(empty($file->id)){
LogManager::getInstance()->info("Small profile image ".$profileImage->name."_small not found");
$largeFileUrl = $this->getFileUrl($profileImage->name);
$file->name = $profileImage->name."_small";
$signInMappingField = SIGN_IN_ELEMENT_MAPPING_FIELD_NAME;
$file->$signInMappingField = $profileImage->$signInMappingField;
$file->filename = $file->name.str_replace($profileImage->name,"",$profileImage->filename);
$file->file_group = $profileImage->file_group;
file_put_contents("/tmp/".$file->filename."_orig", file_get_contents($largeFileUrl));
if(file_exists("/tmp/".$file->filename."_orig")){
//Resize image to 100
$img = new abeautifulsite\SimpleImage("/tmp/".$file->filename."_orig");
$img->fit_to_width(100);
$img->save("/tmp/".$file->filename);
$uploadFilesToS3Key = SettingsManager::getInstance()->getSetting("Files: Amazon S3 Key for File Upload");
$uploadFilesToS3Secret = SettingsManager::getInstance()->getSetting("Files: Amazone S3 Secret for File Upload");
$s3Bucket = SettingsManager::getInstance()->getSetting("Files: S3 Bucket");
$uploadname = CLIENT_NAME."/".$file->filename;
$localFile = "/tmp/".$file->filename;
$s3FileSys = new S3FileSystem($uploadFilesToS3Key, $uploadFilesToS3Secret);
$result = $s3FileSys->putObject($s3Bucket, $uploadname, $localFile, 'authenticated-read');
unlink("/tmp/".$file->filename);
unlink("/tmp/".$file->filename."_orig");
LogManager::getInstance()->info("Upload Result:".print_r($result,true));
if(!empty($result)){
$ok = $file->Save();
}
return $file;
}
return null;
}
return $file;
}
public function updateSmallProfileImage($profile){
$file = new File();
$file->Load('name = ?',array('profile_image_'.$profile->id));
if($file->name == 'profile_image_'.$profile->id){
$uploadFilesToS3 = SettingsManager::getInstance()->getSetting("Files: Upload Files to S3");
if($uploadFilesToS3 == "1"){
try{
$fileNew = $this->checkAddSmallProfileImage($file);
if(!empty($fileNew)){
$file = $fileNew;
}
$uploadFilesToS3Key = SettingsManager::getInstance()->getSetting("Files: Amazon S3 Key for File Upload");
$uploadFilesToS3Secret = SettingsManager::getInstance()->getSetting("Files: Amazone S3 Secret for File Upload");
$s3FileSys = new S3FileSystem($uploadFilesToS3Key, $uploadFilesToS3Secret);
$s3WebUrl = SettingsManager::getInstance()->getSetting("Files: S3 Web Url");
$fileUrl = $s3WebUrl.CLIENT_NAME."/".$file->filename;
$expireUrl = $this->getFromCache($fileUrl);
if(empty($expireUrl)){
$expireUrl = $s3FileSys->generateExpiringURL($fileUrl, 600);
$this->saveInCache($fileUrl, $expireUrl, 500);
}
$profile->image = $expireUrl;
}catch (Exception $e){
LogManager::getInstance()->error("Error generating profile image: ".$e->getMessage());
if($profile->gender == 'Female'){
$profile->image = BASE_URL."images/user_female.png";
}else{
$profile->image = BASE_URL."images/user_male.png";
}
}
}else{
$profile->image = CLIENT_BASE_URL.'data/'.$file->filename;
}
}else{
if($profile->gender == 'Female'){
$profile->image = BASE_URL."images/user_female.png";
}else{
$profile->image = BASE_URL."images/user_male.png";
}
}
return $profile;
}
public function updateProfileImage($profile){
$file = new File();
$file->Load('name = ?',array('profile_image_'.$profile->id));
if($file->name == 'profile_image_'.$profile->id){
$uploadFilesToS3 = SettingsManager::getInstance()->getSetting("Files: Upload Files to S3");
if($uploadFilesToS3 == "1"){
$uploadFilesToS3Key = SettingsManager::getInstance()->getSetting("Files: Amazon S3 Key for File Upload");
$uploadFilesToS3Secret = SettingsManager::getInstance()->getSetting("Files: Amazone S3 Secret for File Upload");
$s3FileSys = new S3FileSystem($uploadFilesToS3Key, $uploadFilesToS3Secret);
$s3WebUrl = SettingsManager::getInstance()->getSetting("Files: S3 Web Url");
$fileUrl = $s3WebUrl.CLIENT_NAME."/".$file->filename;
$expireUrl = $this->getFromCache($fileUrl);
if(empty($expireUrl)){
$expireUrl = $s3FileSys->generateExpiringURL($fileUrl, 600);
$this->saveInCache($fileUrl, $expireUrl, 500);
}
$profile->image = $expireUrl;
}else{
$profile->image = CLIENT_BASE_URL.'data/'.$file->filename;
}
}else{
if($profile->gender == 'Female'){
$profile->image = BASE_URL."images/user_female.png";
}else{
$profile->image = BASE_URL."images/user_male.png";
}
}
return $profile;
}
public function getFileUrl($fileName){
$file = new File();
$file->Load('name = ?',array($fileName));
$uploadFilesToS3 = SettingsManager::getInstance()->getSetting("Files: Upload Files to S3");
if($uploadFilesToS3 == "1"){
$uploadFilesToS3Key = SettingsManager::getInstance()->getSetting("Files: Amazon S3 Key for File Upload");
$uploadFilesToS3Secret = SettingsManager::getInstance()->getSetting("Files: Amazone S3 Secret for File Upload");
$s3FileSys = new S3FileSystem($uploadFilesToS3Key, $uploadFilesToS3Secret);
$s3WebUrl = SettingsManager::getInstance()->getSetting("Files: S3 Web Url");
$fileUrl = $s3WebUrl.CLIENT_NAME."/".$file->filename;
$expireUrl = $this->getFromCache($fileUrl);
if(empty($expireUrl)){
$expireUrl = $s3FileSys->generateExpiringURL($fileUrl, 600);
$this->saveInCache($fileUrl, $expireUrl, 500);
}
return $expireUrl;
}else{
return CLIENT_BASE_URL.'data/'.$file->filename;
}
}
public function deleteProfileImage($profileId){
$file = new File();
$file->Load('name = ?',array('profile_image_'.$profileId));
if($file->name == 'profile_image_'.$profileId){
$ok = $file->Delete();
if($ok){
LogManager::getInstance()->info("Delete File:".CLIENT_BASE_PATH.$file->filename);
unlink(CLIENT_BASE_PATH.'data/'.$file->filename);
}else{
return false;
}
}
$file = new File();
$file->Load('name = ?',array('profile_image_'.$profileId."_small"));
if($file->name == 'profile_image_'.$profileId."_small"){
$ok = $file->Delete();
if($ok){
LogManager::getInstance()->info("Delete File:".CLIENT_BASE_PATH.$file->filename);
unlink(CLIENT_BASE_PATH.'data/'.$file->filename);
}else{
return false;
}
}
return true;
}
public function deleteFileByField($value, $field){
LogManager::getInstance()->info("Delete file by field: $field / value: $value");
$file = new File();
$file->Load("$field = ?",array($value));
if($file->$field == $value){
$ok = $file->Delete();
if($ok){
$uploadFilesToS3 = SettingsManager::getInstance()->getSetting("Files: Upload Files to S3");
if($uploadFilesToS3 == "1"){
$uploadFilesToS3Key = SettingsManager::getInstance()->getSetting("Files: Amazon S3 Key for File Upload");
$uploadFilesToS3Secret = SettingsManager::getInstance()->getSetting("Files: Amazone S3 Secret for File Upload");
$s3Bucket = SettingsManager::getInstance()->getSetting("Files: S3 Bucket");
$uploadname = CLIENT_NAME."/".$file->filename;
LogManager::getInstance()->info("Delete from S3:".$uploadname);
$s3FileSys = new S3FileSystem($uploadFilesToS3Key, $uploadFilesToS3Secret);
$res = $s3FileSys->deleteObject($s3Bucket, $uploadname);
}else{
LogManager::getInstance()->info("Delete:".CLIENT_BASE_PATH.'data/'.$file->filename);
unlink(CLIENT_BASE_PATH.'data/'.$file->filename);
}
}else{
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,156 @@
<?php
class ModuleBuilder {
var $modules = array();
public function addModuleOrGroup($module){
$this->modules[] = $module;
}
public function getTabHeadersHTML(){
$html = "";
foreach($this->modules as $module){
$html .= $module->getHTML()."\r\n";
}
return $html;
}
public function getTabPagesHTML(){
$html = "";
foreach($this->modules as $module){
if(get_class($module) == "ModuleTab"){
$html .= $module->getPageHTML()."\r\n";
}else{
foreach($module->modules as $mod){
$html .= $mod->getPageHTML()."\r\n";
}
}
}
return $html;
}
public function getModJsHTML(){
$html = "var modJsList = new Array();\r\n";
$activeModule = "";
foreach($this->modules as $module){
if(get_class($module) == "ModuleTab"){
$html .= $module->getJSObjectCode()."\r\n";
if($module->isActive){
$activeModule = $module->name;
}
}else{
foreach($module->modules as $mod){
if($module->isActive && $activeModule == ""){
$activeModule = $mod->name;
}
$html .= $mod->getJSObjectCode()."\r\n";
}
}
}
$html .= "var modJs = modJsList['tab".$activeModule."'];\r\n";
return $html;
}
}
class ModuleTab{
public $name;
var $class;
var $label;
var $adapterName;
var $filter;
var $orderBy;
public $isActive = false;
public $isInsideGroup = false;
var $options = array();
public function __construct($name, $class, $label, $adapterName, $filter, $orderBy, $isActive = false, $options = array()){
$this->name = $name;
$this->class = $class;
$this->label = $label;
$this->adapterName = $adapterName;
$this->filter = $filter;
$this->orderBy = $orderBy;
$this->isActive = $isActive;
$this->options = $options;
}
public function getHTML(){
$active = ($this->isActive)?"active":"";
if(!$this->isInsideGroup) {
return '<li class="' . $active . '"><a id="tab' . $this->name . '" href="#tabPage' . $this->name . '">' . $this->label . '</a></li>';
}else{
return '<li class="' . $active . '"><a id="tab' . $this->name . '" href="#tabPage' . $this->name . '">' . $this->label . '</a></li>';
}
}
public function getPageHTML(){
$active = ($this->isActive)?" active":"";
$html = '<div class="tab-pane'.$active.'" id="tabPage'.$this->name.'">'.
'<div id="'.$this->name.'" class="reviewBlock" data-content="List" style="padding-left:5px;"></div>'.
'<div id="'.$this->name.'Form" class="reviewBlock" data-content="Form" style="padding-left:5px;display:none;"></div>'.
'</div>';
return $html;
}
public function getJSObjectCode()
{
$js = '';
if (empty($this->filter)) {
$js.= "modJsList['tab" . $this->name . "'] = new " . $this->adapterName . "('" . $this->class . "','" . $this->name . "','','".$this->orderBy."');";
} else {
$js.= "modJsList['tab" . $this->name . "'] = new " . $this->adapterName . "('" . $this->class . "','" . $this->name . "'," . $this->filter . ",'".$this->orderBy."');";
}
foreach($this->options as $key => $val){
$js.= "modJsList['tab" . $this->name . "'].".$key."(".$val.");";
}
return $js;
}
}
class ModuleTabGroup{
var $name;
var $label;
var $isActive = false;
public $modules = array();
public function __construct($name, $label){
$this->name = $name;
$this->label = $label;
}
public function addModuleTab($moduleTab){
if($moduleTab->isActive){
$this->isActive = true;
$moduleTab->isActive = false;
}
$moduleTab->isInsideGroup = true;
$this->modules[] = $moduleTab;
}
public function getHTML(){
$html = "";
$active = ($this->isActive)?" active":"";
$html.= '<li class="dropdown'.$active.'">'."\r\n".
'<a href="#" id="'.$this->name.'" class="dropdown-toggle" data-toggle="dropdown" aria-controls="'.$this->name.'-contents">'.$this->label.' <span class="caret"></span></a>'."\r\n".
'<ul class="dropdown-menu" role="menu" aria-labelledby="'.$this->name.'" id="'.$this->name.'-contents">';
foreach($this->modules as $module){
$html.= $module->getHTML();
}
$html .= "</ul></li>";
return $html;
}
}

View File

@@ -0,0 +1,78 @@
<?php
class NotificationManager{
var $baseService;
public function setBaseService($baseService){
$this->baseService = $baseService;
}
public function addNotification($toUser, $message, $action, $type){
$profileVar = SIGN_IN_ELEMENT_MAPPING_FIELD_NAME;
$profileClass = ucfirst(SIGN_IN_ELEMENT_MAPPING_FIELD_NAME);
$userEmp = new User();
$userEmp->load("profile = ?",array($toUser));
if(!empty($userEmp->$profileVar) && $userEmp->$profileVar == $toUser){
$toUser = $userEmp->id;
}else{
return;
}
$noti = new Notification();
$user = $this->baseService->getCurrentUser();
$noti->fromUser = $user->id;
$noti->fromProfile = $user->$profileVar;
$noti->toUser = $toUser;
$noti->message = $message;
if(!empty($noti->fromProfile)){
$profile = $this->baseService->getElement($profileClass,$noti->fromProfile,null,true);
if(!empty($profile)){
$fs = new FileService();
$profile = $fs->updateProfileImage($profile);
$noti->image = $profile->image;
}
}
if(empty($noti->image)){
$noti->image = BASE_URL."images/user_male.png";
}
$noti->action = $action;
$noti->type = $type;
$noti->time = date('Y-m-d H:i:s');
$noti->status = 'Unread';
$ok = $noti->Save();
if(!$ok){
LogManager::getInstance()->info("Error adding notification: ".$noti->ErrorMsg());
}
}
public function clearNotifications($userId){
$notification = new Notification();
$listUnread = $notification->Find("toUser = ? and status = ?",array($userId,'Unread'));
foreach($listUnread as $not){
$not->status = "Read";
$not->Save();
}
}
public function getLatestNotificationsAndCounts($userId){
$notification = new Notification();
$listUnread = $notification->Find("toUser = ? and status = ?",array($userId,'Unread'));
$unreadCount = count($listUnread);
$limit = ($unreadCount < 20)?20:$unreadCount;
$list = $notification->Find("toUser = ? order by time desc limit ?",array($userId,$limit));
return array($unreadCount, $list);
}
}

View File

@@ -0,0 +1,143 @@
<?php
class ReportHandler{
public function handleReport($request){
if(!empty($request['id'])){
$report = new Report();
$report->Load("id = ?",array($request['id']));
if($report->id."" == $request['id']){
if($report->type == 'Query'){
$where = $this->buildQueryOmmit(json_decode($report->paramOrder,true), $request);
$query = str_replace("_where_", $where[0], $report->query);
return $this->executeReport($report,$query,$where[1]);
}else if($report->type == 'Class'){
$className = $report->query;
include MODULE_PATH.'/reportClasses/ReportBuilder.php';
include MODULE_PATH.'/reportClasses/'.$className.".php";
$cls = new $className();
$data = $cls->getData($report,$request);
return $this->generateReport($report,$data);
}
}else{
return array("ERROR","Report id not found");
}
}
}
private function executeReport($report,$query,$parameters){
$report->DB()->SetFetchMode(ADODB_FETCH_ASSOC);
$rs = $report->DB()->Execute($query,$parameters);
if(!$rs){
LogManager::getInstance()->info($report->DB()->ErrorMsg());
return array("ERROR","Error generating report");
}
$reportNamesFilled = false;
$columnNames = array();
$reportData = array();
foreach ($rs as $rowId => $row) {
$reportData[] = array();
if(!$reportNamesFilled){
foreach ($row as $name=> $value){
$columnNames[] = $name;
$reportData[count($reportData)-1][] = $value;
}
$reportNamesFilled = true;
}else{
foreach ($row as $name=> $value){
$reportData[count($reportData)-1][] = $value;
}
}
}
array_unshift($reportData,$columnNames);
return $this->generateReport($report, $reportData);
}
private function generateReport($report, $data){
$fileFirst = "Report_".str_replace(" ", "_", $report->name)."-".date("Y-m-d_H-i-s");
$file = $fileFirst.".csv";
$fileName = CLIENT_BASE_PATH.'data/'.$file;
$fp = fopen($fileName, 'w');
foreach ($data as $fields) {
fputcsv($fp, $fields);
}
fclose($fp);
$uploadedToS3 = false;
$uploadFilesToS3 = SettingsManager::getInstance()->getSetting("Files: Upload Files to S3");
$uploadFilesToS3Key = SettingsManager::getInstance()->getSetting("Files: Amazon S3 Key for File Upload");
$uploadFilesToS3Secret = SettingsManager::getInstance()->getSetting("Files: Amazone S3 Secret for File Upload");
$s3Bucket = SettingsManager::getInstance()->getSetting("Files: S3 Bucket");
$s3WebUrl = SettingsManager::getInstance()->getSetting("Files: S3 Web Url");
if($uploadFilesToS3.'' == '1' && !empty($uploadFilesToS3Key)
&& !empty($uploadFilesToS3Secret) && !empty($s3Bucket) && !empty($s3WebUrl)){
$uploadname = CLIENT_NAME."/".$file;
$s3FileSys = new S3FileSystem($uploadFilesToS3Key, $uploadFilesToS3Secret);
$res = $s3FileSys->putObject($s3Bucket, $uploadname, $fileName, 'authenticated-read');
if(empty($res)){
return array("ERROR",$file);
}
unlink($fileName);
$file_url = $s3WebUrl.$uploadname;
$file_url = $s3FileSys->generateExpiringURL($file_url);
$uploadedToS3 = true;
}
$fileObj = new File();
$fileObj->name = $fileFirst;
$fileObj->filename = $file;
$fileObj->file_group = "Report";
$ok = $fileObj->Save();
if(!$ok){
LogManager::getInstance()->info($fileObj->ErrorMsg());
return array("ERROR","Error generating report");
}
$headers = array_shift($data);
if($uploadedToS3){
return array("SUCCESS",array($file_url,$headers,$data));
}else{
return array("SUCCESS",array($file,$headers,$data));
}
}
private function buildQueryOmmit($names, $params){
$parameters = array();
$query = "";
foreach($names as $name){
if($params[$name] != "NULL"){
if($query != ""){
$query.=" AND ";
}
$query.=$name." = ?";
$parameters[] = $params[$name];
}
}
if($query != ""){
$query = "where ".$query;
}
return array($query, $parameters);
}
}

View File

@@ -0,0 +1,151 @@
<?php
class RestApiManager{
private static $me = NULL;
var $endPoints = array();
private function __construct(){
}
public static function getInstance(){
if(empty(self::$me)){
self::$me = new RestApiManager();
}
return self::$me;
}
public function generateUserAccessToken($user){
$data = array();
$data['userId'] = $user->id;
$data['expires'] = strtotime('now') + 60*60;
$accessTokenTemp = AesCtr::encrypt(json_encode($data), $user->password, 256);
$accessTokenTemp = $user->id."|".$accessTokenTemp;
$accessToken = AesCtr::encrypt($accessTokenTemp, APP_SEC, 256);
return new IceResponse(IceResponse::SUCCESS, $accessToken);
}
public function getAccessTokenForUser($user){
$accessTokenObj = new RestAccessToken();
$accessTokenObj->Load("userId = ?",array($user->id));
$generateAccessToken = false;
$accessToken = $accessTokenObj->token;
if(!empty($accessToken)){
$resp = $this->validateAccessTokenInner($accessToken);
if($resp->getStatus() != IceResponse::SUCCESS){
$generateAccessToken = true;
}
}else{
$generateAccessToken = true;
}
if($generateAccessToken){
$accessToken = $this->generateUserAccessToken($user)->getData();
if(!empty($accessTokenObj->id)){
$accessTokenObj->token = $accessToken;
$accessTokenObj->hash = base64_encode(CLIENT_BASE_URL).":".md5($accessTokenObj->token);
$accessTokenObj->updated = date("Y-m-d H:i:s");
$accessTokenObj->Save();
}else{
$accessTokenObj = new RestAccessToken();
$accessTokenObj->userId = $user->id;
$accessTokenObj->token = $accessToken;
$accessTokenObj->hash = base64_encode(CLIENT_BASE_URL).":".md5($accessTokenObj->token);
$accessTokenObj->updated = date("Y-m-d H:i:s");
$accessTokenObj->created = date("Y-m-d H:i:s");
$accessTokenObj->Save();
}
}
return new IceResponse(IceResponse::SUCCESS, $accessTokenObj->hash);
}
public function validateAccessToken($hash){
$accessTokenObj = new RestAccessToken();
$accessTokenObj->Load("hash = ?",array($hash));
if(!empty($accessTokenObj->id) && $accessTokenObj->hash == $hash){
return $this->validateAccessTokenInner($accessTokenObj->token);
}
return new IceResponse(IceResponse::ERROR, "Acess Token not found");
}
private function validateAccessTokenInner($accessToken){
$accessTokenTemp = AesCtr::decrypt($accessToken, APP_SEC, 256);
$parts = explode("|", $accessTokenTemp);
$user = new User();
$user->Load("id = ?",array($parts[0]));
if(empty($user->id) || $user->id != $parts[0] || empty($parts[0])){
return new IceResponse(IceResponse::ERROR, -1);
}
$accessToken = AesCtr::decrypt($parts[1], $user->password, 256);
$data = json_decode($accessToken, true);
if($data['userId'] == $user->id){
return new IceResponse(IceResponse::SUCCESS, true);
}
return new IceResponse(IceResponse::ERROR, false);
}
public function addEndPoint($endPoint){
$url = $endPoint->getUrl();
LogManager::getInstance()->info("Adding REST end point for - ".$url);
$this->endPoints[$url] = $endPoint;
}
public function process($type, $url, $parameters){
$accessTokenValidation = $this->validateAccessToken($parameters['access_token']);
if($accessTokenValidation->getStatus() == IceResponse::ERROR){
return $accessTokenValidation;
}
if(isset($this->endPoints[$url])){
return $this->endPoints[$url]->$type($parameters);
}
return new IceResponse(IceResponse::ERROR, "End Point ".$url." - Not Found");
}
}
class RestEndPoint{
var $url;
public function setUrl($url){
$this->url = $url;
}
public function getUrl(){
return $this->url;
}
public function get($parameters){
return new IceResponse(IceResponse::ERROR, false);
}
public function post($parameters){
return new IceResponse(IceResponse::ERROR, false);
}
public function put($parameters){
return new IceResponse(IceResponse::ERROR, false);
}
public function delete($parameters){
return new IceResponse(IceResponse::ERROR, false);
}
}

View File

@@ -0,0 +1,107 @@
<?php
use Aws\S3\S3Client;
class S3FileSystem{
var $s3;
var $key;
var $secret;
public function __construct($key, $secret){
$this->key = $key;
$this->secret = $secret;
$arr = array(
'key' => $key,
'secret' => $secret,
'region' => AWS_REGION
);
$this->s3 = S3Client::factory($arr);
}
public function putObject($bucket, $key, $sourceFile, $acl){
$res = null;
try{
$res = $this->s3->putObject(array(
'Bucket' => $bucket,
'Key' => $key,
'SourceFile' => $sourceFile,
'ACL' => $acl
/*'ContentType' => 'image/jpeg'*/
));
}catch(Exception $e){
LogManager::getInstance()->info($e->getMessage());
return NULL;
}
LogManager::getInstance()->info("Response from s3:".print_r($res,true));
$result = $res->get('RequestId');
if(!empty($result)){
return $result;
}
return NULL;
}
public function deleteObject($bucket, $key){
$res = null;
try{
$res = $this->s3->deleteObject(array(
'Bucket' => $bucket,
'Key' => $key
));
}catch(Exception $e){
LogManager::getInstance()->info($e->getMessage());
return NULL;
}
LogManager::getInstance()->info("Response from s3:".print_r($res,true));
$result = $res->get('RequestId');
if(!empty($result)){
return $result;
}
return NULL;
}
public function generateExpiringURL($url, $expiresIn = 600) {
// Calculate expiry time
$expiresTimestamp = time() + intval($expiresIn);
$path = parse_url($url, PHP_URL_PATH);
$path = str_replace('%2F', '/', rawurlencode($path = ltrim($path, '/')));
$host = parse_url($url, PHP_URL_HOST);
$bucket = str_replace(".s3.amazonaws.com", "", $host);
// Path for signature starts with the bucket
$signpath = '/'. $bucket .'/'. $path;
// S3 friendly string to sign
$signsz = implode("\n", $pieces = array('GET', null, null, $expiresTimestamp, $signpath));
// Calculate the hash
$signature = $this->el_crypto_hmacSHA1($this->secret, $signsz);
// ... to the query string ...
$qs = http_build_query($pieces = array(
'AWSAccessKeyId' => $this->key,
'Expires' => $expiresTimestamp,
'Signature' => $signature,
));
// ... and return the URL!
return $url.'?'.$qs;
}
private function el_crypto_hmacSHA1($key, $data, $blocksize = 64) {
if (strlen($key) > $blocksize) $key = pack('H*', sha1($key));
$key = str_pad($key, $blocksize, chr(0x00));
$ipad = str_repeat(chr(0x36), $blocksize);
$opad = str_repeat(chr(0x5c), $blocksize);
$hmac = pack( 'H*', sha1(
($key ^ $opad) . pack( 'H*', sha1(
($key ^ $ipad) . $data
))
));
return base64_encode($hmac);
}
}

View File

@@ -0,0 +1,44 @@
<?php
class SettingsManager{
private static $me = null;
private function __construct(){
}
public static function getInstance(){
if(empty(self::$me)){
self::$me = new SettingsManager();
}
return self::$me;
}
public function getSetting($name){
if(class_exists("ProVersion")){
$pro = new ProVersion();
$val = $pro->getSetting($name);
if(!empty($val)){
return $val;
}
}
$setting = new Setting();
$setting->Load("name = ?",array($name));
if($setting->name == $name){
return $setting->value;
}
return null;
}
public function setSetting($name, $value){
$setting = new Setting();
$setting->Load("name = ?",array($name));
if($setting->name == $name){
$setting->value = $value;
$setting->Save();
}
}
}

1287
src/classes/SimpleImage.php Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,104 @@
<?php
/*
This file is part of Ice Framework.
Ice Framework is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Ice Framework is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Ice Framework. If not, see <http://www.gnu.org/licenses/>.
------------------------------------------------------------------
Original work Copyright (c) 2012 [Gamonoid Media Pvt. Ltd]
Developer: Thilina Hasantha (thilina.hasantha[at]gmail.com / facebook.com/thilinah)
*/
class IceResponse{
const SUCCESS = "SUCCESS";
const ERROR = "ERROR";
var $status;
var $data;
public function __construct($status,$data = null){
$this->status = $status;
$this->data = $data;
}
public function getStatus(){
return $this->status;
}
public function getData(){
return $this->data;
}
public function getObject(){
return $this->data;
}
public function getJsonArray(){
return array("status"=>$this->status,"data"=>$this->data);
}
}
abstract class SubActionManager{
var $user = null;
protected $baseService = null;
var $emailTemplates = null;
var $emailSender = null;
public function setUser($user){
$this->user = $user;
}
public function setBaseService($baseService){
$this->baseService = $baseService;
}
public function getCurrentProfileId(){
return $this->baseService->getCurrentProfileId();
}
public function setEmailTemplates($emailTemplates){
$this->emailTemplates = $emailTemplates;
}
public function getEmailTemplate($name){
//Read module email templates
if($this->emailTemplates == null){
$this->emailTemplates = array();
if(is_dir(MODULE_PATH.'/emailTemplates/')){
$ams = scandir(MODULE_PATH.'/emailTemplates/');
foreach($ams as $am){
if(!is_dir(MODULE_PATH.'/emailTemplates/'.$am) && $am != '.' && $am != '..'){
$this->emailTemplates[$am] = file_get_contents(MODULE_PATH.'/emailTemplates/'.$am);
}
}
}
}
return $this->emailTemplates[$name];
}
public function setEmailSender($emailSender){
$this->emailSender = $emailSender;
}
public function getUserFromProfileId($profileId){
return $this->baseService->getUserFromProfileId($profileId);
}
}

228
src/classes/UIManager.php Normal file
View File

@@ -0,0 +1,228 @@
<?php
class UIManager{
private static $me = null;
var $user;
var $currentProfile;
var $switchedProfile;
var $currentProfileBlock = null;
var $switchedProfileBlock = null;
var $tempates = array();
var $quickAccessMenuItems = array();
private function __construct(){
}
public static function getInstance(){
if(empty(self::$me)){
self::$me = new UIManager();
}
return self::$me;
}
private function getTemplate($name, $type){
if(isset($this->tempates[$name])){
return $this->tempates[$name];
}
$this->tempates[$name] = file_get_contents(APP_BASE_PATH."templates/".$type."/".$name.".html");
return $this->tempates[$name];
}
public function populateTemplate($name, $type, $params){
$template= $this->getTemplate($name, $type);
foreach($params as $key=>$value){
$template = str_replace("#_".$key."_#", $value, $template);
}
return $template;
}
public function setCurrentUser($user){
$this->user = $user;
}
public function setHomeLink($homeLink){
$this->homeLink = $homeLink;
}
public function setProfiles($profileCurrent, $profileSwitched){
$this->currentProfile = $profileCurrent;
$this->switchedProfile = $profileSwitched;
if(!empty($profileCurrent) && !empty($profileSwitched)){
$this->currentProfileBlock = array(
"profileImage"=>$profileCurrent->image,
"firstName"=>$profileCurrent->first_name,
"lastName"=>$profileCurrent->last_name
);
$this->switchedProfileBlock = array(
"profileImage"=>$profileSwitched->image,
"firstName"=>$profileSwitched->first_name,
"lastName"=>$profileSwitched->last_name
);
} else if(!empty($profileCurrent)){
$this->currentProfileBlock = array(
"profileImage"=>$profileCurrent->image,
"firstName"=>$profileCurrent->first_name,
"lastName"=>$profileCurrent->last_name
);
} else if(!empty($profileSwitched)){
$this->currentProfileBlock = array(
"profileImage"=>BASE_URL."images/user_male.png",
"firstName"=>$this->user->username,
"lastName"=>""
);
$this->switchedProfileBlock = array(
"profileImage"=>$profileSwitched->image,
"firstName"=>$profileSwitched->first_name,
"lastName"=>$profileSwitched->last_name
);
}else{
$this->currentProfileBlock = array(
"profileImage"=>BASE_URL."images/user_male.png",
"firstName"=>$this->user->username,
"lastName"=>""
);
}
}
public function getProfileBlocks(){
$tempateProfileBlock = "";
$tempateProfileBlock = $this->populateTemplate('profile_info', 'app', $this->currentProfileBlock);
if(!empty($this->switchedProfileBlock)){
$tempateProfileBlock .= $this->populateTemplate('switched_profile_info', 'app', $this->switchedProfileBlock);
}
return $tempateProfileBlock;
}
public function getMenuBlocks(){
$manuItems = array();
if(!empty($this->quickAccessMenuItems)){
$itemsHtml = $this->getQuickAccessMenuItemsHTML();
if(!empty($itemsHtml)){
$manuItems[] = new MenuItemTemplate('menuButtonQuick', array("ITEMS"=>$itemsHtml));
}
}
$manuItems[] = new MenuItemTemplate('menuButtonNotification', array());
if($this->user->user_level == "Admin"){
$manuItems[] = new MenuItemTemplate('menuButtonSwitchProfile', array());
}
if(!empty($this->currentProfile)){
$manuItems[] = new MenuItemTemplate('menuButtonProfile', array(
"profileImage"=>$this->currentProfile->image,
"firstName"=>$this->currentProfile->first_name,
"lastName"=>$this->currentProfile->last_name,
"homeLink"=>$this->homeLink,
"CLIENT_BASE_URL"=>CLIENT_BASE_URL
));
}else{
$manuItems[] = new MenuItemTemplate('menuButtonProfile', array(
"profileImage"=>BASE_URL."images/user_male.png",
"firstName"=>$this->user->username,
"lastName"=>"",
"homeLink"=>$this->homeLink,
"CLIENT_BASE_URL"=>CLIENT_BASE_URL
));
}
if($this->user->user_level == "Admin"){
$manuItems[] = new MenuItemTemplate('menuButtonHelp', array(
"APP_NAME"=>APP_NAME,
"VERSION"=>VERSION,
"VERSION_DATE"=>VERSION_DATE
));
}
return $manuItems;
}
public function getMenuItemsHTML(){
$menuItems = $this->getMenuBlocks();
$menuHtml = "";
foreach($menuItems as $item){
$menuHtml.=$item->getHtml();
}
return $menuHtml;
}
public function addQuickAccessMenuItem($name, $icon, $link, $userLevels = array()){
$this->quickAccessMenuItems[] = array($name, $icon, $link, $userLevels);
}
public function getQuickAccessMenuItemsHTML(){
$html = "";
$user = BaseService::getInstance()->getCurrentUser();
foreach($this->quickAccessMenuItems as $item){
if(empty($item[3]) || in_array($user->user_level,$item[3])){
$html .= '<a href="'.$item[2].'"><i class="fa '.$item[1].'"></i> '.$item[0].'</a>';
}
}
return $html;
}
public function renderModule($moduleBuilder){
$str = '<div class="span9"><ul class="nav nav-tabs" id="modTab" style="margin-bottom:0px;margin-left:5px;border-bottom: none;">__tabHeaders__</ul><div class="tab-content">__tabPages__</div></div><script>__tabJs__</script>';
$str = str_replace("__tabHeaders__",$moduleBuilder->getTabHeadersHTML(), $str);
$str = str_replace("__tabPages__",$moduleBuilder->getTabPagesHTML(), $str);
$str = str_replace("__tabJs__",$moduleBuilder->getModJsHTML(), $str);
return $str;
}
}
//Menu Items
class MenuItemTemplate{
public $templateName;
public $params;
public function __construct($templateName, $params){
$this->templateName = $templateName;
$this->params = $params;
}
public function getHtml(){
return UIManager::getInstance()->populateTemplate($this->templateName, 'menu', $this->params);
}
}

View File

@@ -0,0 +1,28 @@
<?php
/*
This file is part of Ice Framework.
Ice Framework is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Ice Framework is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Ice Framework. If not, see <http://www.gnu.org/licenses/>.
------------------------------------------------------------------
Original work Copyright (c) 2012 [Gamonoid Media Pvt. Ltd]
Developer: Thilina Hasantha (thilina.hasantha[at]gmail.com / facebook.com/thilinah)
*/
class UserService{
public function getAuthUser($username, $password){
}
}

165
src/classes/crypt/Aes.php Normal file
View File

@@ -0,0 +1,165 @@
<?php
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* AES implementation in PHP (c) Chris Veness 2005-2011. Right of free use is granted for all */
/* commercial or non-commercial use under CC-BY licence. No warranty of any form is offered. */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
class Aes {
/**
* AES Cipher function: encrypt 'input' with Rijndael algorithm
*
* @param input message as byte-array (16 bytes)
* @param w key schedule as 2D byte-array (Nr+1 x Nb bytes) -
* generated from the cipher key by keyExpansion()
* @return ciphertext as byte-array (16 bytes)
*/
public static function cipher($input, $w) { // main cipher function [§5.1]
$Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES)
$Nr = count($w)/$Nb - 1; // no of rounds: 10/12/14 for 128/192/256-bit keys
$state = array(); // initialise 4xNb byte-array 'state' with input [§3.4]
for ($i=0; $i<4*$Nb; $i++) $state[$i%4][floor($i/4)] = $input[$i];
$state = self::addRoundKey($state, $w, 0, $Nb);
for ($round=1; $round<$Nr; $round++) { // apply Nr rounds
$state = self::subBytes($state, $Nb);
$state = self::shiftRows($state, $Nb);
$state = self::mixColumns($state, $Nb);
$state = self::addRoundKey($state, $w, $round, $Nb);
}
$state = self::subBytes($state, $Nb);
$state = self::shiftRows($state, $Nb);
$state = self::addRoundKey($state, $w, $Nr, $Nb);
$output = array(4*$Nb); // convert state to 1-d array before returning [§3.4]
for ($i=0; $i<4*$Nb; $i++) $output[$i] = $state[$i%4][floor($i/4)];
return $output;
}
private static function addRoundKey($state, $w, $rnd, $Nb) { // xor Round Key into state S [§5.1.4]
for ($r=0; $r<4; $r++) {
for ($c=0; $c<$Nb; $c++) $state[$r][$c] ^= $w[$rnd*4+$c][$r];
}
return $state;
}
private static function subBytes($s, $Nb) { // apply SBox to state S [§5.1.1]
for ($r=0; $r<4; $r++) {
for ($c=0; $c<$Nb; $c++) $s[$r][$c] = self::$sBox[$s[$r][$c]];
}
return $s;
}
private static function shiftRows($s, $Nb) { // shift row r of state S left by r bytes [§5.1.2]
$t = array(4);
for ($r=1; $r<4; $r++) {
for ($c=0; $c<4; $c++) $t[$c] = $s[$r][($c+$r)%$Nb]; // shift into temp copy
for ($c=0; $c<4; $c++) $s[$r][$c] = $t[$c]; // and copy back
} // note that this will work for Nb=4,5,6, but not 7,8 (always 4 for AES):
return $s; // see fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.311.pdf
}
private static function mixColumns($s, $Nb) { // combine bytes of each col of state S [§5.1.3]
for ($c=0; $c<4; $c++) {
$a = array(4); // 'a' is a copy of the current column from 's'
$b = array(4); // 'b' is a•{02} in GF(2^8)
for ($i=0; $i<4; $i++) {
$a[$i] = $s[$i][$c];
$b[$i] = $s[$i][$c]&0x80 ? $s[$i][$c]<<1 ^ 0x011b : $s[$i][$c]<<1;
}
// a[n] ^ b[n] is a•{03} in GF(2^8)
$s[0][$c] = $b[0] ^ $a[1] ^ $b[1] ^ $a[2] ^ $a[3]; // 2*a0 + 3*a1 + a2 + a3
$s[1][$c] = $a[0] ^ $b[1] ^ $a[2] ^ $b[2] ^ $a[3]; // a0 * 2*a1 + 3*a2 + a3
$s[2][$c] = $a[0] ^ $a[1] ^ $b[2] ^ $a[3] ^ $b[3]; // a0 + a1 + 2*a2 + 3*a3
$s[3][$c] = $a[0] ^ $b[0] ^ $a[1] ^ $a[2] ^ $b[3]; // 3*a0 + a1 + a2 + 2*a3
}
return $s;
}
/**
* Key expansion for Rijndael cipher(): performs key expansion on cipher key
* to generate a key schedule
*
* @param key cipher key byte-array (16 bytes)
* @return key schedule as 2D byte-array (Nr+1 x Nb bytes)
*/
public static function keyExpansion($key) { // generate Key Schedule from Cipher Key [§5.2]
$Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES)
$Nk = count($key)/4; // key length (in words): 4/6/8 for 128/192/256-bit keys
$Nr = $Nk + 6; // no of rounds: 10/12/14 for 128/192/256-bit keys
$w = array();
$temp = array();
for ($i=0; $i<$Nk; $i++) {
$r = array($key[4*$i], $key[4*$i+1], $key[4*$i+2], $key[4*$i+3]);
$w[$i] = $r;
}
for ($i=$Nk; $i<($Nb*($Nr+1)); $i++) {
$w[$i] = array();
for ($t=0; $t<4; $t++) $temp[$t] = $w[$i-1][$t];
if ($i % $Nk == 0) {
$temp = self::subWord(self::rotWord($temp));
for ($t=0; $t<4; $t++) $temp[$t] ^= self::$rCon[$i/$Nk][$t];
} else if ($Nk > 6 && $i%$Nk == 4) {
$temp = self::subWord($temp);
}
for ($t=0; $t<4; $t++) $w[$i][$t] = $w[$i-$Nk][$t] ^ $temp[$t];
}
return $w;
}
private static function subWord($w) { // apply SBox to 4-byte word w
for ($i=0; $i<4; $i++) $w[$i] = self::$sBox[$w[$i]];
return $w;
}
private static function rotWord($w) { // rotate 4-byte word w left by one byte
$tmp = $w[0];
for ($i=0; $i<3; $i++) $w[$i] = $w[$i+1];
$w[3] = $tmp;
return $w;
}
// sBox is pre-computed multiplicative inverse in GF(2^8) used in subBytes and keyExpansion [§5.1.1]
private static $sBox = array(
0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16);
// rCon is Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)] [§5.2]
private static $rCon = array(
array(0x00, 0x00, 0x00, 0x00),
array(0x01, 0x00, 0x00, 0x00),
array(0x02, 0x00, 0x00, 0x00),
array(0x04, 0x00, 0x00, 0x00),
array(0x08, 0x00, 0x00, 0x00),
array(0x10, 0x00, 0x00, 0x00),
array(0x20, 0x00, 0x00, 0x00),
array(0x40, 0x00, 0x00, 0x00),
array(0x80, 0x00, 0x00, 0x00),
array(0x1b, 0x00, 0x00, 0x00),
array(0x36, 0x00, 0x00, 0x00) );
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
?>

View File

@@ -0,0 +1,164 @@
<?php
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* AES counter (CTR) mode implementation in PHP (c) Chris Veness 2005-2011. Right of free use is */
/* granted for all commercial or non-commercial use under CC-BY licence. No warranty of any */
/* form is offered. */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
class AesCtr extends Aes {
/**
* Encrypt a text using AES encryption in Counter mode of operation
* - see http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
*
* Unicode multi-byte character safe
*
* @param plaintext source text to be encrypted
* @param password the password to use to generate a key
* @param nBits number of bits to be used in the key (128, 192, or 256)
* @return encrypted text
*/
public static function encrypt($plaintext, $password, $nBits) {
$blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES
if (!($nBits==128 || $nBits==192 || $nBits==256)) return ''; // standard allows 128/192/256 bit keys
// note PHP (5) gives us plaintext and password in UTF8 encoding!
// use AES itself to encrypt password to get cipher key (using plain password as source for
// key expansion) - gives us well encrypted key
$nBytes = $nBits/8; // no bytes in key
$pwBytes = array();
for ($i=0; $i<$nBytes; $i++) $pwBytes[$i] = ord(substr($password,$i,1)) & 0xff;
$key = Aes::cipher($pwBytes, Aes::keyExpansion($pwBytes));
$key = array_merge($key, array_slice($key, 0, $nBytes-16)); // expand key to 16/24/32 bytes long
// initialise 1st 8 bytes of counter block with nonce (NIST SP800-38A §B.2): [0-1] = millisec,
// [2-3] = random, [4-7] = seconds, giving guaranteed sub-ms uniqueness up to Feb 2106
$counterBlock = array();
$nonce = floor(microtime(true)*1000); // timestamp: milliseconds since 1-Jan-1970
$nonceMs = $nonce%1000;
$nonceSec = floor($nonce/1000);
$nonceRnd = floor(rand(0, 0xffff));
for ($i=0; $i<2; $i++) $counterBlock[$i] = self::urs($nonceMs, $i*8) & 0xff;
for ($i=0; $i<2; $i++) $counterBlock[$i+2] = self::urs($nonceRnd, $i*8) & 0xff;
for ($i=0; $i<4; $i++) $counterBlock[$i+4] = self::urs($nonceSec, $i*8) & 0xff;
// and convert it to a string to go on the front of the ciphertext
$ctrTxt = '';
for ($i=0; $i<8; $i++) $ctrTxt .= chr($counterBlock[$i]);
// generate key schedule - an expansion of the key into distinct Key Rounds for each round
$keySchedule = Aes::keyExpansion($key);
//print_r($keySchedule);
$blockCount = ceil(strlen($plaintext)/$blockSize);
$ciphertxt = array(); // ciphertext as array of strings
for ($b=0; $b<$blockCount; $b++) {
// set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
// done in two stages for 32-bit ops: using two words allows us to go past 2^32 blocks (68GB)
for ($c=0; $c<4; $c++) $counterBlock[15-$c] = self::urs($b, $c*8) & 0xff;
for ($c=0; $c<4; $c++) $counterBlock[15-$c-4] = self::urs($b/0x100000000, $c*8);
$cipherCntr = Aes::cipher($counterBlock, $keySchedule); // -- encrypt counter block --
// block size is reduced on final block
$blockLength = $b<$blockCount-1 ? $blockSize : (strlen($plaintext)-1)%$blockSize+1;
$cipherByte = array();
for ($i=0; $i<$blockLength; $i++) { // -- xor plaintext with ciphered counter byte-by-byte --
$cipherByte[$i] = $cipherCntr[$i] ^ ord(substr($plaintext, $b*$blockSize+$i, 1));
$cipherByte[$i] = chr($cipherByte[$i]);
}
$ciphertxt[$b] = implode('', $cipherByte); // escape troublesome characters in ciphertext
}
// implode is more efficient than repeated string concatenation
$ciphertext = $ctrTxt . implode('', $ciphertxt);
$ciphertext = base64_encode($ciphertext);
return $ciphertext;
}
/**
* Decrypt a text encrypted by AES in counter mode of operation
*
* @param ciphertext source text to be decrypted
* @param password the password to use to generate a key
* @param nBits number of bits to be used in the key (128, 192, or 256)
* @return decrypted text
*/
public static function decrypt($ciphertext, $password, $nBits) {
$blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES
if (!($nBits==128 || $nBits==192 || $nBits==256)) return ''; // standard allows 128/192/256 bit keys
$ciphertext = base64_decode($ciphertext);
// use AES to encrypt password (mirroring encrypt routine)
$nBytes = $nBits/8; // no bytes in key
$pwBytes = array();
for ($i=0; $i<$nBytes; $i++) $pwBytes[$i] = ord(substr($password,$i,1)) & 0xff;
$key = Aes::cipher($pwBytes, Aes::keyExpansion($pwBytes));
$key = array_merge($key, array_slice($key, 0, $nBytes-16)); // expand key to 16/24/32 bytes long
// recover nonce from 1st element of ciphertext
$counterBlock = array();
$ctrTxt = substr($ciphertext, 0, 8);
for ($i=0; $i<8; $i++) $counterBlock[$i] = ord(substr($ctrTxt,$i,1));
// generate key schedule
$keySchedule = Aes::keyExpansion($key);
// separate ciphertext into blocks (skipping past initial 8 bytes)
$nBlocks = ceil((strlen($ciphertext)-8) / $blockSize);
$ct = array();
for ($b=0; $b<$nBlocks; $b++) $ct[$b] = substr($ciphertext, 8+$b*$blockSize, 16);
$ciphertext = $ct; // ciphertext is now array of block-length strings
// plaintext will get generated block-by-block into array of block-length strings
$plaintxt = array();
for ($b=0; $b<$nBlocks; $b++) {
// set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
for ($c=0; $c<4; $c++) $counterBlock[15-$c] = self::urs($b, $c*8) & 0xff;
for ($c=0; $c<4; $c++) $counterBlock[15-$c-4] = self::urs(($b+1)/0x100000000-1, $c*8) & 0xff;
$cipherCntr = Aes::cipher($counterBlock, $keySchedule); // encrypt counter block
$plaintxtByte = array();
for ($i=0; $i<strlen($ciphertext[$b]); $i++) {
// -- xor plaintext with ciphered counter byte-by-byte --
$plaintxtByte[$i] = $cipherCntr[$i] ^ ord(substr($ciphertext[$b],$i,1));
$plaintxtByte[$i] = chr($plaintxtByte[$i]);
}
$plaintxt[$b] = implode('', $plaintxtByte);
}
// join array of blocks into single plaintext string
$plaintext = implode('',$plaintxt);
return $plaintext;
}
/*
* Unsigned right shift function, since PHP has neither >>> operator nor unsigned ints
*
* @param a number to be shifted (32-bit integer)
* @param b number of bits to shift a to the right (0..31)
* @return a right-shifted and zero-filled by b bits
*/
private static function urs($a, $b) {
$a &= 0xffffffff; $b &= 0x1f; // (bounds check)
if ($a&0x80000000 && $b>0) { // if left-most bit set
$a = ($a>>1) & 0x7fffffff; // right-shift one bit & clear left-most bit
$a = $a >> ($b-1); // remaining right-shifts
} else { // otherwise
$a = ($a>>$b); // use normal right-shift
}
return $a;
}
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
?>

View File

@@ -0,0 +1,8 @@
{
"require": {
"guzzle/guzzle": "~3.7"
},
"require-dev": {
"phpunit/phpunit": "4.5.*"
}
}

129
src/composer/composer.lock generated Normal file
View File

@@ -0,0 +1,129 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "c6263cc0d124a7e40ee7c52782f80cff",
"packages": [
{
"name": "monolog/monolog",
"version": "1.13.1",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
"reference": "c31a2c4e8db5da8b46c74cf275d7f109c0f249ac"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/c31a2c4e8db5da8b46c74cf275d7f109c0f249ac",
"reference": "c31a2c4e8db5da8b46c74cf275d7f109c0f249ac",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"psr/log": "~1.0"
},
"provide": {
"psr/log-implementation": "1.0.0"
},
"require-dev": {
"aws/aws-sdk-php": "~2.4, >2.4.8",
"doctrine/couchdb": "~1.0@dev",
"graylog2/gelf-php": "~1.0",
"phpunit/phpunit": "~4.0",
"raven/raven": "~0.5",
"ruflin/elastica": "0.90.*",
"swiftmailer/swiftmailer": "~5.3",
"videlalvaro/php-amqplib": "~2.4"
},
"suggest": {
"aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
"doctrine/couchdb": "Allow sending log messages to a CouchDB server",
"ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
"ext-mongo": "Allow sending log messages to a MongoDB server",
"graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
"raven/raven": "Allow sending log messages to a Sentry server",
"rollbar/rollbar": "Allow sending log messages to Rollbar",
"ruflin/elastica": "Allow sending log messages to an Elastic Search server",
"videlalvaro/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.13.x-dev"
}
},
"autoload": {
"psr-4": {
"Monolog\\": "src/Monolog"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
}
],
"description": "Sends your logs to files, sockets, inboxes, databases and various web services",
"homepage": "http://github.com/Seldaek/monolog",
"keywords": [
"log",
"logging",
"psr-3"
],
"time": "2015-03-09 09:58:04"
},
{
"name": "psr/log",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "fe0936ee26643249e916849d48e3a51d5f5e278b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b",
"reference": "fe0936ee26643249e916849d48e3a51d5f5e278b",
"shasum": ""
},
"type": "library",
"autoload": {
"psr-0": {
"Psr\\Log\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"keywords": [
"log",
"psr",
"psr-3"
],
"time": "2012-12-21 11:40:51"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": []
}

BIN
src/composer/composer.phar Normal file

Binary file not shown.

7
src/composer/vendor/autoload.php vendored Normal file
View File

@@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderInit91d733469d809ee1828b45ab2da48a10::getLoader();

View File

@@ -0,0 +1,413 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0 class loader
*
* See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-0 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
if ('\\' == $class[0]) {
$class = substr($class, 1);
}
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative) {
return false;
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if ($file === null && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if ($file === null) {
// Remember that this class does not exist.
return $this->classMap[$class] = false;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
if (0 === strpos($class, $prefix)) {
foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}

View File

@@ -0,0 +1,9 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View File

@@ -0,0 +1,10 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Psr\\Log\\' => array($vendorDir . '/psr/log'),
);

View File

@@ -0,0 +1,10 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
);

View File

@@ -0,0 +1,50 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit91d733469d809ee1828b45ab2da48a10
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit91d733469d809ee1828b45ab2da48a10', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit91d733469d809ee1828b45ab2da48a10', 'loadClassLoader'));
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
$loader->register(true);
return $loader;
}
}
function composerRequire91d733469d809ee1828b45ab2da48a10($file)
{
require $file;
}

View File

@@ -0,0 +1,117 @@
[
{
"name": "psr/log",
"version": "1.0.0",
"version_normalized": "1.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "fe0936ee26643249e916849d48e3a51d5f5e278b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b",
"reference": "fe0936ee26643249e916849d48e3a51d5f5e278b",
"shasum": ""
},
"time": "2012-12-21 11:40:51",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"Psr\\Log\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"keywords": [
"log",
"psr",
"psr-3"
]
},
{
"name": "monolog/monolog",
"version": "1.13.1",
"version_normalized": "1.13.1.0",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
"reference": "c31a2c4e8db5da8b46c74cf275d7f109c0f249ac"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/c31a2c4e8db5da8b46c74cf275d7f109c0f249ac",
"reference": "c31a2c4e8db5da8b46c74cf275d7f109c0f249ac",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"psr/log": "~1.0"
},
"provide": {
"psr/log-implementation": "1.0.0"
},
"require-dev": {
"aws/aws-sdk-php": "~2.4, >2.4.8",
"doctrine/couchdb": "~1.0@dev",
"graylog2/gelf-php": "~1.0",
"phpunit/phpunit": "~4.0",
"raven/raven": "~0.5",
"ruflin/elastica": "0.90.*",
"swiftmailer/swiftmailer": "~5.3",
"videlalvaro/php-amqplib": "~2.4"
},
"suggest": {
"aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
"doctrine/couchdb": "Allow sending log messages to a CouchDB server",
"ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
"ext-mongo": "Allow sending log messages to a MongoDB server",
"graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
"raven/raven": "Allow sending log messages to a Sentry server",
"rollbar/rollbar": "Allow sending log messages to Rollbar",
"ruflin/elastica": "Allow sending log messages to an Elastic Search server",
"videlalvaro/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib"
},
"time": "2015-03-09 09:58:04",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.13.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Monolog\\": "src/Monolog"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
}
],
"description": "Sends your logs to files, sockets, inboxes, databases and various web services",
"homepage": "http://github.com/Seldaek/monolog",
"keywords": [
"log",
"logging",
"psr-3"
]
}
]

View File

@@ -0,0 +1,15 @@
<?php
$finder = Symfony\CS\Finder\DefaultFinder::create()
->files()
->name('*.php')
->in(__DIR__.'/src')
->in(__DIR__.'/tests')
;
return Symfony\CS\Config\Config::create()
->fixers(array(
'psr0', 'encoding', 'short_tag', 'braces', 'elseif', 'eof_ending', 'function_declaration', 'indentation', 'line_after_namespace', 'linefeed', 'lowercase_constants', 'lowercase_keywords', 'multiple_use', 'php_closing_tag', 'trailing_spaces', 'visibility', 'duplicate_semicolon', 'extra_empty_lines', 'include', 'namespace_no_leading_whitespace', 'object_operator', 'operators_spaces', 'phpdoc_params', 'return', 'single_array_no_trailing_comma', 'spaces_cast', 'standardize_not_equal', 'ternary_spaces', 'unused_use', 'whitespacy_lines',
))
->finder($finder)
;

View File

@@ -0,0 +1,217 @@
### 1.13.1 (2015-03-09)
* Fixed regression in HipChat requiring a new token to be created
### 1.13.0 (2015-03-05)
* Added Registry::hasLogger to check for the presence of a logger instance
* Added context.user support to RavenHandler
* Added HipChat API v2 support in the HipChatHandler
* Added NativeMailerHandler::addParameter to pass params to the mail() process
* Added context data to SlackHandler when $includeContextAndExtra is true
* Added ability to customize the Swift_Message per-email in SwiftMailerHandler
* Fixed SwiftMailerHandler to lazily create message instances if a callback is provided
* Fixed serialization of INF and NaN values in Normalizer and LineFormatter
### 1.12.0 (2014-12-29)
* Break: HandlerInterface::isHandling now receives a partial record containing only a level key. This was always the intent and does not break any Monolog handler but is strictly speaking a BC break and you should check if you relied on any other field in your own handlers.
* Added PsrHandler to forward records to another PSR-3 logger
* Added SamplingHandler to wrap around a handler and include only every Nth record
* Added MongoDBFormatter to support better storage with MongoDBHandler (it must be enabled manually for now)
* Added exception codes in the output of most formatters
* Added LineFormatter::includeStacktraces to enable exception stack traces in logs (uses more than one line)
* Added $useShortAttachment to SlackHandler to minify attachment size and $includeExtra to append extra data
* Added $host to HipChatHandler for users of private instances
* Added $transactionName to NewRelicHandler and support for a transaction_name context value
* Fixed MandrillHandler to avoid outputing API call responses
* Fixed some non-standard behaviors in SyslogUdpHandler
### 1.11.0 (2014-09-30)
* Break: The NewRelicHandler extra and context data are now prefixed with extra_ and context_ to avoid clashes. Watch out if you have scripts reading those from the API and rely on names
* Added WhatFailureGroupHandler to suppress any exception coming from the wrapped handlers and avoid chain failures if a logging service fails
* Added MandrillHandler to send emails via the Mandrillapp.com API
* Added SlackHandler to log records to a Slack.com account
* Added FleepHookHandler to log records to a Fleep.io account
* Added LogglyHandler::addTag to allow adding tags to an existing handler
* Added $ignoreEmptyContextAndExtra to LineFormatter to avoid empty [] at the end
* Added $useLocking to StreamHandler and RotatingFileHandler to enable flock() while writing
* Added support for PhpAmqpLib in the AmqpHandler
* Added FingersCrossedHandler::clear and BufferHandler::clear to reset them between batches in long running jobs
* Added support for adding extra fields from $_SERVER in the WebProcessor
* Fixed support for non-string values in PrsLogMessageProcessor
* Fixed SwiftMailer messages being sent with the wrong date in long running scripts
* Fixed minor PHP 5.6 compatibility issues
* Fixed BufferHandler::close being called twice
### 1.10.0 (2014-06-04)
* Added Logger::getHandlers() and Logger::getProcessors() methods
* Added $passthruLevel argument to FingersCrossedHandler to let it always pass some records through even if the trigger level is not reached
* Added support for extra data in NewRelicHandler
* Added $expandNewlines flag to the ErrorLogHandler to create multiple log entries when a message has multiple lines
### 1.9.1 (2014-04-24)
* Fixed regression in RotatingFileHandler file permissions
* Fixed initialization of the BufferHandler to make sure it gets flushed after receiving records
* Fixed ChromePHPHandler and FirePHPHandler's activation strategies to be more conservative
### 1.9.0 (2014-04-20)
* Added LogEntriesHandler to send logs to a LogEntries account
* Added $filePermissions to tweak file mode on StreamHandler and RotatingFileHandler
* Added $useFormatting flag to MemoryProcessor to make it send raw data in bytes
* Added support for table formatting in FirePHPHandler via the table context key
* Added a TagProcessor to add tags to records, and support for tags in RavenHandler
* Added $appendNewline flag to the JsonFormatter to enable using it when logging to files
* Added sound support to the PushoverHandler
* Fixed multi-threading support in StreamHandler
* Fixed empty headers issue when ChromePHPHandler received no records
* Fixed default format of the ErrorLogHandler
### 1.8.0 (2014-03-23)
* Break: the LineFormatter now strips newlines by default because this was a bug, set $allowInlineLineBreaks to true if you need them
* Added BrowserConsoleHandler to send logs to any browser's console via console.log() injection in the output
* Added FilterHandler to filter records and only allow those of a given list of levels through to the wrapped handler
* Added FlowdockHandler to send logs to a Flowdock account
* Added RollbarHandler to send logs to a Rollbar account
* Added HtmlFormatter to send prettier log emails with colors for each log level
* Added GitProcessor to add the current branch/commit to extra record data
* Added a Monolog\Registry class to allow easier global access to pre-configured loggers
* Added support for the new official graylog2/gelf-php lib for GelfHandler, upgrade if you can by replacing the mlehner/gelf-php requirement
* Added support for HHVM
* Added support for Loggly batch uploads
* Added support for tweaking the content type and encoding in NativeMailerHandler
* Added $skipClassesPartials to tweak the ignored classes in the IntrospectionProcessor
* Fixed batch request support in GelfHandler
### 1.7.0 (2013-11-14)
* Added ElasticSearchHandler to send logs to an Elastic Search server
* Added DynamoDbHandler and ScalarFormatter to send logs to Amazon's Dynamo DB
* Added SyslogUdpHandler to send logs to a remote syslogd server
* Added LogglyHandler to send logs to a Loggly account
* Added $level to IntrospectionProcessor so it only adds backtraces when needed
* Added $version to LogstashFormatter to allow using the new v1 Logstash format
* Added $appName to NewRelicHandler
* Added configuration of Pushover notification retries/expiry
* Added $maxColumnWidth to NativeMailerHandler to change the 70 chars default
* Added chainability to most setters for all handlers
* Fixed RavenHandler batch processing so it takes the message from the record with highest priority
* Fixed HipChatHandler batch processing so it sends all messages at once
* Fixed issues with eAccelerator
* Fixed and improved many small things
### 1.6.0 (2013-07-29)
* Added HipChatHandler to send logs to a HipChat chat room
* Added ErrorLogHandler to send logs to PHP's error_log function
* Added NewRelicHandler to send logs to NewRelic's service
* Added Monolog\ErrorHandler helper class to register a Logger as exception/error/fatal handler
* Added ChannelLevelActivationStrategy for the FingersCrossedHandler to customize levels by channel
* Added stack traces output when normalizing exceptions (json output & co)
* Added Monolog\Logger::API constant (currently 1)
* Added support for ChromePHP's v4.0 extension
* Added support for message priorities in PushoverHandler, see $highPriorityLevel and $emergencyLevel
* Added support for sending messages to multiple users at once with the PushoverHandler
* Fixed RavenHandler's support for batch sending of messages (when behind a Buffer or FingersCrossedHandler)
* Fixed normalization of Traversables with very large data sets, only the first 1000 items are shown now
* Fixed issue in RotatingFileHandler when an open_basedir restriction is active
* Fixed minor issues in RavenHandler and bumped the API to Raven 0.5.0
* Fixed SyslogHandler issue when many were used concurrently with different facilities
### 1.5.0 (2013-04-23)
* Added ProcessIdProcessor to inject the PID in log records
* Added UidProcessor to inject a unique identifier to all log records of one request/run
* Added support for previous exceptions in the LineFormatter exception serialization
* Added Monolog\Logger::getLevels() to get all available levels
* Fixed ChromePHPHandler so it avoids sending headers larger than Chrome can handle
### 1.4.1 (2013-04-01)
* Fixed exception formatting in the LineFormatter to be more minimalistic
* Fixed RavenHandler's handling of context/extra data, requires Raven client >0.1.0
* Fixed log rotation in RotatingFileHandler to work with long running scripts spanning multiple days
* Fixed WebProcessor array access so it checks for data presence
* Fixed Buffer, Group and FingersCrossed handlers to make use of their processors
### 1.4.0 (2013-02-13)
* Added RedisHandler to log to Redis via the Predis library or the phpredis extension
* Added ZendMonitorHandler to log to the Zend Server monitor
* Added the possibility to pass arrays of handlers and processors directly in the Logger constructor
* Added `$useSSL` option to the PushoverHandler which is enabled by default
* Fixed ChromePHPHandler and FirePHPHandler issue when multiple instances are used simultaneously
* Fixed header injection capability in the NativeMailHandler
### 1.3.1 (2013-01-11)
* Fixed LogstashFormatter to be usable with stream handlers
* Fixed GelfMessageFormatter levels on Windows
### 1.3.0 (2013-01-08)
* Added PSR-3 compliance, the `Monolog\Logger` class is now an instance of `Psr\Log\LoggerInterface`
* Added PsrLogMessageProcessor that you can selectively enable for full PSR-3 compliance
* Added LogstashFormatter (combine with SocketHandler or StreamHandler to send logs to Logstash)
* Added PushoverHandler to send mobile notifications
* Added CouchDBHandler and DoctrineCouchDBHandler
* Added RavenHandler to send data to Sentry servers
* Added support for the new MongoClient class in MongoDBHandler
* Added microsecond precision to log records' timestamps
* Added `$flushOnOverflow` param to BufferHandler to flush by batches instead of losing
the oldest entries
* Fixed normalization of objects with cyclic references
### 1.2.1 (2012-08-29)
* Added new $logopts arg to SyslogHandler to provide custom openlog options
* Fixed fatal error in SyslogHandler
### 1.2.0 (2012-08-18)
* Added AmqpHandler (for use with AMQP servers)
* Added CubeHandler
* Added NativeMailerHandler::addHeader() to send custom headers in mails
* Added the possibility to specify more than one recipient in NativeMailerHandler
* Added the possibility to specify float timeouts in SocketHandler
* Added NOTICE and EMERGENCY levels to conform with RFC 5424
* Fixed the log records to use the php default timezone instead of UTC
* Fixed BufferHandler not being flushed properly on PHP fatal errors
* Fixed normalization of exotic resource types
* Fixed the default format of the SyslogHandler to avoid duplicating datetimes in syslog
### 1.1.0 (2012-04-23)
* Added Monolog\Logger::isHandling() to check if a handler will
handle the given log level
* Added ChromePHPHandler
* Added MongoDBHandler
* Added GelfHandler (for use with Graylog2 servers)
* Added SocketHandler (for use with syslog-ng for example)
* Added NormalizerFormatter
* Added the possibility to change the activation strategy of the FingersCrossedHandler
* Added possibility to show microseconds in logs
* Added `server` and `referer` to WebProcessor output
### 1.0.2 (2011-10-24)
* Fixed bug in IE with large response headers and FirePHPHandler
### 1.0.1 (2011-08-25)
* Added MemoryPeakUsageProcessor and MemoryUsageProcessor
* Added Monolog\Logger::getName() to get a logger's channel name
### 1.0.0 (2011-07-06)
* Added IntrospectionProcessor to get info from where the logger was called
* Fixed WebProcessor in CLI
### 1.0.0-RC1 (2011-07-01)
* Initial release

View File

@@ -0,0 +1,19 @@
Copyright (c) 2011-2014 Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,292 @@
Monolog - Logging for PHP 5.3+ [![Build Status](https://secure.travis-ci.org/Seldaek/monolog.png)](http://travis-ci.org/Seldaek/monolog)
==============================
[![Total Downloads](https://poser.pugx.org/monolog/monolog/downloads.png)](https://packagist.org/packages/monolog/monolog)
[![Latest Stable Version](https://poser.pugx.org/monolog/monolog/v/stable.png)](https://packagist.org/packages/monolog/monolog)
[![Reference Status](https://www.versioneye.com/php/monolog:monolog/reference_badge.svg)](https://www.versioneye.com/php/monolog:monolog/references)
Monolog sends your logs to files, sockets, inboxes, databases and various
web services. See the complete list of handlers below. Special handlers
allow you to build advanced logging strategies.
This library implements the [PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md)
interface that you can type-hint against in your own libraries to keep
a maximum of interoperability. You can also use it in your applications to
make sure you can always use another compatible logger at a later time.
As of 1.11.0 Monolog public APIs will also accept PSR-3 log levels.
Internally Monolog still uses its own level scheme since it predates PSR-3.
Usage
-----
Install the latest version with `composer require monolog/monolog`
```php
<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
// create a log channel
$log = new Logger('name');
$log->pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING));
// add records to the log
$log->addWarning('Foo');
$log->addError('Bar');
```
Core Concepts
-------------
Every `Logger` instance has a channel (name) and a stack of handlers. Whenever
you add a record to the logger, it traverses the handler stack. Each handler
decides whether it fully handled the record, and if so, the propagation of the
record ends there.
This allows for flexible logging setups, for example having a `StreamHandler` at
the bottom of the stack that will log anything to disk, and on top of that add
a `MailHandler` that will send emails only when an error message is logged.
Handlers also have a `$bubble` property which defines whether they block the
record or not if they handled it. In this example, setting the `MailHandler`'s
`$bubble` argument to false means that records handled by the `MailHandler` will
not propagate to the `StreamHandler` anymore.
You can create many `Logger`s, each defining a channel (e.g.: db, request,
router, ..) and each of them combining various handlers, which can be shared
or not. The channel is reflected in the logs and allows you to easily see or
filter records.
Each Handler also has a Formatter, a default one with settings that make sense
will be created if you don't set one. The formatters normalize and format
incoming records so that they can be used by the handlers to output useful
information.
Custom severity levels are not available. Only the eight
[RFC 5424](http://tools.ietf.org/html/rfc5424) levels (debug, info, notice,
warning, error, critical, alert, emergency) are present for basic filtering
purposes, but for sorting and other use cases that would require
flexibility, you should add Processors to the Logger that can add extra
information (tags, user ip, ..) to the records before they are handled.
Log Levels
----------
Monolog supports the logging levels described by [RFC 5424](http://tools.ietf.org/html/rfc5424).
- **DEBUG** (100): Detailed debug information.
- **INFO** (200): Interesting events. Examples: User logs in, SQL logs.
- **NOTICE** (250): Normal but significant events.
- **WARNING** (300): Exceptional occurrences that are not errors. Examples:
Use of deprecated APIs, poor use of an API, undesirable things that are not
necessarily wrong.
- **ERROR** (400): Runtime errors that do not require immediate action but
should typically be logged and monitored.
- **CRITICAL** (500): Critical conditions. Example: Application component
unavailable, unexpected exception.
- **ALERT** (550): Action must be taken immediately. Example: Entire website
down, database unavailable, etc. This should trigger the SMS alerts and wake
you up.
- **EMERGENCY** (600): Emergency: system is unusable.
Docs
====
**See the `doc` directory for more detailed documentation.
The following is only a list of all parts that come with Monolog.**
Handlers
--------
### Log to files and syslog
- _StreamHandler_: Logs records into any PHP stream, use this for log files.
- _RotatingFileHandler_: Logs records to a file and creates one logfile per day.
It will also delete files older than `$maxFiles`. You should use
[logrotate](http://linuxcommand.org/man_pages/logrotate8.html) for high profile
setups though, this is just meant as a quick and dirty solution.
- _SyslogHandler_: Logs records to the syslog.
- _ErrorLogHandler_: Logs records to PHP's
[`error_log()`](http://docs.php.net/manual/en/function.error-log.php) function.
### Send alerts and emails
- _NativeMailerHandler_: Sends emails using PHP's
[`mail()`](http://php.net/manual/en/function.mail.php) function.
- _SwiftMailerHandler_: Sends emails using a [`Swift_Mailer`](http://swiftmailer.org/) instance.
- _PushoverHandler_: Sends mobile notifications via the [Pushover](https://www.pushover.net/) API.
- _HipChatHandler_: Logs records to a [HipChat](http://hipchat.com) chat room using its API.
- _FlowdockHandler_: Logs records to a [Flowdock](https://www.flowdock.com/) account.
- _SlackHandler_: Logs records to a [Slack](https://www.slack.com/) account.
- _MandrillHandler_: Sends emails via the Mandrill API using a [`Swift_Message`](http://swiftmailer.org/) instance.
- _FleepHookHandler_: Logs records to a [Fleep](https://fleep.io/) conversation using Webhooks.
### Log specific servers and networked logging
- _SocketHandler_: Logs records to [sockets](http://php.net/fsockopen), use this
for UNIX and TCP sockets. See an [example](https://github.com/Seldaek/monolog/blob/master/doc/sockets.md).
- _AmqpHandler_: Logs records to an [amqp](http://www.amqp.org/) compatible
server. Requires the [php-amqp](http://pecl.php.net/package/amqp) extension (1.0+).
- _GelfHandler_: Logs records to a [Graylog2](http://www.graylog2.org) server.
- _CubeHandler_: Logs records to a [Cube](http://square.github.com/cube/) server.
- _RavenHandler_: Logs records to a [Sentry](http://getsentry.com/) server using
[raven](https://packagist.org/packages/raven/raven).
- _ZendMonitorHandler_: Logs records to the Zend Monitor present in Zend Server.
- _NewRelicHandler_: Logs records to a [NewRelic](http://newrelic.com/) application.
- _LogglyHandler_: Logs records to a [Loggly](http://www.loggly.com/) account.
- _RollbarHandler_: Logs records to a [Rollbar](https://rollbar.com/) account.
- _SyslogUdpHandler_: Logs records to a remote [Syslogd](http://www.rsyslog.com/) server.
- _LogEntriesHandler_: Logs records to a [LogEntries](http://logentries.com/) account.
### Logging in development
- _FirePHPHandler_: Handler for [FirePHP](http://www.firephp.org/), providing
inline `console` messages within [FireBug](http://getfirebug.com/).
- _ChromePHPHandler_: Handler for [ChromePHP](http://www.chromephp.com/), providing
inline `console` messages within Chrome.
- _BrowserConsoleHandler_: Handler to send logs to browser's Javascript `console` with
no browser extension required. Most browsers supporting `console` API are supported.
### Log to databases
- _RedisHandler_: Logs records to a [redis](http://redis.io) server.
- _MongoDBHandler_: Handler to write records in MongoDB via a
[Mongo](http://pecl.php.net/package/mongo) extension connection.
- _CouchDBHandler_: Logs records to a CouchDB server.
- _DoctrineCouchDBHandler_: Logs records to a CouchDB server via the Doctrine CouchDB ODM.
- _ElasticSearchHandler_: Logs records to an Elastic Search server.
- _DynamoDbHandler_: Logs records to a DynamoDB table with the [AWS SDK](https://github.com/aws/aws-sdk-php).
### Wrappers / Special Handlers
- _FingersCrossedHandler_: A very interesting wrapper. It takes a logger as
parameter and will accumulate log records of all levels until a record
exceeds the defined severity level. At which point it delivers all records,
including those of lower severity, to the handler it wraps. This means that
until an error actually happens you will not see anything in your logs, but
when it happens you will have the full information, including debug and info
records. This provides you with all the information you need, but only when
you need it.
- _WhatFailureGroupHandler_: This handler extends the _GroupHandler_ ignoring
exceptions raised by each child handler. This allows you to ignore issues
where a remote tcp connection may have died but you do not want your entire
application to crash and may wish to continue to log to other handlers.
- _BufferHandler_: This handler will buffer all the log records it receives
until `close()` is called at which point it will call `handleBatch()` on the
handler it wraps with all the log messages at once. This is very useful to
send an email with all records at once for example instead of having one mail
for every log record.
- _GroupHandler_: This handler groups other handlers. Every record received is
sent to all the handlers it is configured with.
- _FilterHandler_: This handler only lets records of the given levels through
to the wrapped handler.
- _SamplingHandler_: Wraps around another handler and lets you sample records
if you only want to store some of them.
- _NullHandler_: Any record it can handle will be thrown away. This can be used
to put on top of an existing handler stack to disable it temporarily.
- _PsrHandler_: Can be used to forward log records to an existing PSR-3 logger
- _TestHandler_: Used for testing, it records everything that is sent to it and
has accessors to read out the information.
Formatters
----------
- _LineFormatter_: Formats a log record into a one-line string.
- _HtmlFormatter_: Used to format log records into a human readable html table, mainly suitable for emails.
- _NormalizerFormatter_: Normalizes objects/resources down to strings so a record can easily be serialized/encoded.
- _ScalarFormatter_: Used to format log records into an associative array of scalar values.
- _JsonFormatter_: Encodes a log record into json.
- _WildfireFormatter_: Used to format log records into the Wildfire/FirePHP protocol, only useful for the FirePHPHandler.
- _ChromePHPFormatter_: Used to format log records into the ChromePHP format, only useful for the ChromePHPHandler.
- _GelfMessageFormatter_: Used to format log records into Gelf message instances, only useful for the GelfHandler.
- _LogstashFormatter_: Used to format log records into [logstash](http://logstash.net/) event json, useful for any handler listed under inputs [here](http://logstash.net/docs/latest).
- _ElasticaFormatter_: Used to format log records into an Elastica\Document object, only useful for the ElasticSearchHandler.
- _LogglyFormatter_: Used to format log records into Loggly messages, only useful for the LogglyHandler.
- _FlowdockFormatter_: Used to format log records into Flowdock messages, only useful for the FlowdockHandler.
- _MongoDBFormatter_: Converts \DateTime instances to \MongoDate and objects recursively to arrays, only useful with the MongoDBHandler.
Processors
----------
- _IntrospectionProcessor_: Adds the line/file/class/method from which the log call originated.
- _WebProcessor_: Adds the current request URI, request method and client IP to a log record.
- _MemoryUsageProcessor_: Adds the current memory usage to a log record.
- _MemoryPeakUsageProcessor_: Adds the peak memory usage to a log record.
- _ProcessIdProcessor_: Adds the process id to a log record.
- _UidProcessor_: Adds a unique identifier to a log record.
- _GitProcessor_: Adds the current git branch and commit to a log record.
- _TagProcessor_: Adds an array of predefined tags to a log record.
Utilities
---------
- _Registry_: The `Monolog\Registry` class lets you configure global loggers that you
can then statically access from anywhere. It is not really a best practice but can
help in some older codebases or for ease of use.
- _ErrorHandler_: The `Monolog\ErrorHandler` class allows you to easily register
a Logger instance as an exception handler, error handler or fatal error handler.
- _ErrorLevelActivationStrategy_: Activates a FingersCrossedHandler when a certain log
level is reached.
- _ChannelLevelActivationStrategy_: Activates a FingersCrossedHandler when a certain
log level is reached, depending on which channel received the log record.
Third Party Packages
--------------------
Third party handlers, formatters and processors are
[listed in the wiki](https://github.com/Seldaek/monolog/wiki/Third-Party-Packages). You
can also add your own there if you publish one.
About
=====
Requirements
------------
- Monolog works with PHP 5.3 or above, and is also tested to work with HHVM.
Submitting bugs and feature requests
------------------------------------
Bugs and feature request are tracked on [GitHub](https://github.com/Seldaek/monolog/issues)
Frameworks Integration
----------------------
- Frameworks and libraries using [PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md)
can be used very easily with Monolog since it implements the interface.
- [Symfony2](http://symfony.com) comes out of the box with Monolog.
- [Silex](http://silex.sensiolabs.org/) comes out of the box with Monolog.
- [Laravel 4 & 5](http://laravel.com/) come out of the box with Monolog.
- [PPI](http://www.ppi.io/) comes out of the box with Monolog.
- [CakePHP](http://cakephp.org/) is usable with Monolog via the [cakephp-monolog](https://github.com/jadb/cakephp-monolog) plugin.
- [Slim](http://www.slimframework.com/) is usable with Monolog via the [Slim-Monolog](https://github.com/Flynsarmy/Slim-Monolog) log writer.
- [XOOPS 2.6](http://xoops.org/) comes out of the box with Monolog.
- [Aura.Web_Project](https://github.com/auraphp/Aura.Web_Project) comes out of the box with Monolog.
- [Nette Framework](http://nette.org/en/) can be used with Monolog via [Kdyby/Monolog](https://github.com/Kdyby/Monolog) extension.
- [Proton Micro Framework](https://github.com/alexbilbie/Proton) comes out of the box with Monolog.
Author
------
Jordi Boggiano - <j.boggiano@seld.be> - <http://twitter.com/seldaek><br />
See also the list of [contributors](https://github.com/Seldaek/monolog/contributors) which participated in this project.
License
-------
Monolog is licensed under the MIT License - see the `LICENSE` file for details
Acknowledgements
----------------
This library is heavily inspired by Python's [Logbook](http://packages.python.org/Logbook/)
library, although most concepts have been adjusted to fit to the PHP world.

View File

@@ -0,0 +1,54 @@
{
"name": "monolog/monolog",
"description": "Sends your logs to files, sockets, inboxes, databases and various web services",
"keywords": ["log", "logging", "psr-3"],
"homepage": "http://github.com/Seldaek/monolog",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
}
],
"require": {
"php": ">=5.3.0",
"psr/log": "~1.0"
},
"require-dev": {
"phpunit/phpunit": "~4.0",
"graylog2/gelf-php": "~1.0",
"raven/raven": "~0.5",
"ruflin/elastica": "0.90.*",
"doctrine/couchdb": "~1.0@dev",
"aws/aws-sdk-php": "~2.4, >2.4.8",
"videlalvaro/php-amqplib": "~2.4",
"swiftmailer/swiftmailer": "~5.3"
},
"suggest": {
"graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
"raven/raven": "Allow sending log messages to a Sentry server",
"doctrine/couchdb": "Allow sending log messages to a CouchDB server",
"ruflin/elastica": "Allow sending log messages to an Elastic Search server",
"videlalvaro/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
"ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
"ext-mongo": "Allow sending log messages to a MongoDB server",
"aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
"rollbar/rollbar": "Allow sending log messages to Rollbar"
},
"autoload": {
"psr-4": {"Monolog\\": "src/Monolog"}
},
"provide": {
"psr/log-implementation": "1.0.0"
},
"extra": {
"branch-alias": {
"dev-master": "1.13.x-dev"
}
},
"scripts": {
"test": "phpunit"
}
}

View File

@@ -0,0 +1,76 @@
Extending Monolog
=================
Monolog is fully extensible, allowing you to adapt your logger to your needs.
Writing your own handler
------------------------
Monolog provides many built-in handlers. But if the one you need does not
exist, you can write it and use it in your logger. The only requirement is
to implement `Monolog\Handler\HandlerInterface`.
Let's write a PDOHandler to log records to a database. We will extend the
abstract class provided by Monolog to keep things DRY.
```php
<?php
use Monolog\Logger;
use Monolog\Handler\AbstractProcessingHandler;
class PDOHandler extends AbstractProcessingHandler
{
private $initialized = false;
private $pdo;
private $statement;
public function __construct(PDO $pdo, $level = Logger::DEBUG, $bubble = true)
{
$this->pdo = $pdo;
parent::__construct($level, $bubble);
}
protected function write(array $record)
{
if (!$this->initialized) {
$this->initialize();
}
$this->statement->execute(array(
'channel' => $record['channel'],
'level' => $record['level'],
'message' => $record['formatted'],
'time' => $record['datetime']->format('U'),
));
}
private function initialize()
{
$this->pdo->exec(
'CREATE TABLE IF NOT EXISTS monolog '
.'(channel VARCHAR(255), level INTEGER, message LONGTEXT, time INTEGER UNSIGNED)'
);
$this->statement = $this->pdo->prepare(
'INSERT INTO monolog (channel, level, message, time) VALUES (:channel, :level, :message, :time)'
);
$this->initialized = true;
}
}
```
You can now use this handler in your logger:
```php
<?php
$logger->pushHandler(new PDOHandler(new PDO('sqlite:logs.sqlite')));
// You can now use your logger
$logger->addInfo('My logger is now ready');
```
The `Monolog\Handler\AbstractProcessingHandler` class provides most of the
logic needed for the handler, including the use of processors and the formatting
of the record (which is why we use ``$record['formatted']`` instead of ``$record['message']``).

View File

@@ -0,0 +1,37 @@
Sockets Handler
===============
This handler allows you to write your logs to sockets using [fsockopen](http://php.net/fsockopen)
or [pfsockopen](http://php.net/pfsockopen).
Persistent sockets are mainly useful in web environments where you gain some performance not closing/opening
the connections between requests.
Basic Example
-------------
```php
<?php
use Monolog\Logger;
use Monolog\Handler\SocketHandler;
// Create the logger
$logger = new Logger('my_logger');
// Create the handler
$handler = new SocketHandler('unix:///var/log/httpd_app_log.socket');
$handler->setPersistent(true);
// Now add the handler
$logger->pushHandler($handler, Logger::DEBUG);
// You can now use your logger
$logger->addInfo('My logger is now ready');
```
In this example, using syslog-ng, you should see the log on the log server:
cweb1 [2012-02-26 00:12:03] my_logger.INFO: My logger is now ready [] []

View File

@@ -0,0 +1,162 @@
Using Monolog
=============
Installation
------------
Monolog is available on Packagist ([monolog/monolog](http://packagist.org/packages/monolog/monolog))
and as such installable via [Composer](http://getcomposer.org/).
```bash
php composer.phar require monolog/monolog
```
If you do not use Composer, you can grab the code from GitHub, and use any
PSR-0 compatible autoloader (e.g. the [Symfony2 ClassLoader component](https://github.com/symfony/ClassLoader))
to load Monolog classes.
Configuring a logger
--------------------
Here is a basic setup to log to a file and to firephp on the DEBUG level:
```php
<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\FirePHPHandler;
// Create the logger
$logger = new Logger('my_logger');
// Now add some handlers
$logger->pushHandler(new StreamHandler(__DIR__.'/my_app.log', Logger::DEBUG));
$logger->pushHandler(new FirePHPHandler());
// You can now use your logger
$logger->addInfo('My logger is now ready');
```
Let's explain it. The first step is to create the logger instance which will
be used in your code. The argument is a channel name, which is useful when
you use several loggers (see below for more details about it).
The logger itself does not know how to handle a record. It delegates it to
some handlers. The code above registers two handlers in the stack to allow
handling records in two different ways.
Note that the FirePHPHandler is called first as it is added on top of the
stack. This allows you to temporarily add a logger with bubbling disabled if
you want to override other configured loggers.
Adding extra data in the records
--------------------------------
Monolog provides two different ways to add extra informations along the simple
textual message.
### Using the logging context
The first way is the context, allowing to pass an array of data along the
record:
```php
<?php
$logger->addInfo('Adding a new user', array('username' => 'Seldaek'));
```
Simple handlers (like the StreamHandler for instance) will simply format
the array to a string but richer handlers can take advantage of the context
(FirePHP is able to display arrays in pretty way for instance).
### Using processors
The second way is to add extra data for all records by using a processor.
Processors can be any callable. They will get the record as parameter and
must return it after having eventually changed the `extra` part of it. Let's
write a processor adding some dummy data in the record:
```php
<?php
$logger->pushProcessor(function ($record) {
$record['extra']['dummy'] = 'Hello world!';
return $record;
});
```
Monolog provides some built-in processors that can be used in your project.
Look at the [README file](https://github.com/Seldaek/monolog/blob/master/README.mdown) for the list.
> Tip: processors can also be registered on a specific handler instead of
the logger to apply only for this handler.
Leveraging channels
-------------------
Channels are a great way to identify to which part of the application a record
is related. This is useful in big applications (and is leveraged by
MonologBundle in Symfony2).
Picture two loggers sharing a handler that writes to a single log file.
Channels would allow you to identify the logger that issued every record.
You can easily grep through the log files filtering this or that channel.
```php
<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\FirePHPHandler;
// Create some handlers
$stream = new StreamHandler(__DIR__.'/my_app.log', Logger::DEBUG);
$firephp = new FirePHPHandler();
// Create the main logger of the app
$logger = new Logger('my_logger');
$logger->pushHandler($stream);
$logger->pushHandler($firephp);
// Create a logger for the security-related stuff with a different channel
$securityLogger = new Logger('security');
$securityLogger->pushHandler($stream);
$securityLogger->pushHandler($firephp);
```
Customizing log format
----------------------
In Monolog it's easy to customize the format of the logs written into files,
sockets, mails, databases and other handlers. Most of the handlers use the
```php
$record['formatted']
```
value to be automatically put into the log device. This value depends on the
formatter settings. You can choose between predefined formatter classes or
write your own (e.g. a multiline text file for human-readable output).
To configure a predefined formatter class, just set it as the handler's field:
```php
// the default date format is "Y-m-d H:i:s"
$dateFormat = "Y n j, g:i a";
// the default output format is "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"
$output = "%datetime% > %level_name% > %message% %context% %extra%\n";
// finally, create a formatter
$formatter = new LineFormatter($output, $dateFormat);
// Create a handler
$stream = new StreamHandler(__DIR__.'/my_app.log', Logger::DEBUG);
$stream->setFormatter($formatter);
// bind it to a logger object
$securityLogger = new Logger('security');
$securityLogger->pushHandler($stream);
```
You may also reuse the same formatter between multiple handlers and share those
handlers between multiple loggers.

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="tests/bootstrap.php" colors="true">
<testsuites>
<testsuite name="Monolog Test Suite">
<directory>tests/Monolog/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">src/Monolog/</directory>
</whitelist>
</filter>
</phpunit>

View File

@@ -0,0 +1,208 @@
<?php
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
/**
* Monolog error handler
*
* A facility to enable logging of runtime errors, exceptions and fatal errors.
*
* Quick setup: <code>ErrorHandler::register($logger);</code>
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class ErrorHandler
{
private $logger;
private $previousExceptionHandler;
private $uncaughtExceptionLevel;
private $previousErrorHandler;
private $errorLevelMap;
private $fatalLevel;
private $reservedMemory;
private static $fatalErrors = array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR);
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
/**
* Registers a new ErrorHandler for a given Logger
*
* By default it will handle errors, exceptions and fatal errors
*
* @param LoggerInterface $logger
* @param array|false $errorLevelMap an array of E_* constant to LogLevel::* constant mapping, or false to disable error handling
* @param int|false $exceptionLevel a LogLevel::* constant, or false to disable exception handling
* @param int|false $fatalLevel a LogLevel::* constant, or false to disable fatal error handling
* @return ErrorHandler
*/
public static function register(LoggerInterface $logger, $errorLevelMap = array(), $exceptionLevel = null, $fatalLevel = null)
{
$handler = new static($logger);
if ($errorLevelMap !== false) {
$handler->registerErrorHandler($errorLevelMap);
}
if ($exceptionLevel !== false) {
$handler->registerExceptionHandler($exceptionLevel);
}
if ($fatalLevel !== false) {
$handler->registerFatalHandler($fatalLevel);
}
return $handler;
}
public function registerExceptionHandler($level = null, $callPrevious = true)
{
$prev = set_exception_handler(array($this, 'handleException'));
$this->uncaughtExceptionLevel = $level;
if ($callPrevious && $prev) {
$this->previousExceptionHandler = $prev;
}
}
public function registerErrorHandler(array $levelMap = array(), $callPrevious = true, $errorTypes = -1)
{
$prev = set_error_handler(array($this, 'handleError'), $errorTypes);
$this->errorLevelMap = array_replace($this->defaultErrorLevelMap(), $levelMap);
if ($callPrevious) {
$this->previousErrorHandler = $prev ?: true;
}
}
public function registerFatalHandler($level = null, $reservedMemorySize = 20)
{
register_shutdown_function(array($this, 'handleFatalError'));
$this->reservedMemory = str_repeat(' ', 1024 * $reservedMemorySize);
$this->fatalLevel = $level;
}
protected function defaultErrorLevelMap()
{
return array(
E_ERROR => LogLevel::CRITICAL,
E_WARNING => LogLevel::WARNING,
E_PARSE => LogLevel::ALERT,
E_NOTICE => LogLevel::NOTICE,
E_CORE_ERROR => LogLevel::CRITICAL,
E_CORE_WARNING => LogLevel::WARNING,
E_COMPILE_ERROR => LogLevel::ALERT,
E_COMPILE_WARNING => LogLevel::WARNING,
E_USER_ERROR => LogLevel::ERROR,
E_USER_WARNING => LogLevel::WARNING,
E_USER_NOTICE => LogLevel::NOTICE,
E_STRICT => LogLevel::NOTICE,
E_RECOVERABLE_ERROR => LogLevel::ERROR,
E_DEPRECATED => LogLevel::NOTICE,
E_USER_DEPRECATED => LogLevel::NOTICE,
);
}
/**
* @private
*/
public function handleException(\Exception $e)
{
$this->logger->log(
$this->uncaughtExceptionLevel === null ? LogLevel::ERROR : $this->uncaughtExceptionLevel,
sprintf('Uncaught Exception %s: "%s" at %s line %s', get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()),
array('exception' => $e)
);
if ($this->previousExceptionHandler) {
call_user_func($this->previousExceptionHandler, $e);
}
}
/**
* @private
*/
public function handleError($code, $message, $file = '', $line = 0, $context = array())
{
if (!(error_reporting() & $code)) {
return;
}
$level = isset($this->errorLevelMap[$code]) ? $this->errorLevelMap[$code] : LogLevel::CRITICAL;
$this->logger->log($level, self::codeToString($code).': '.$message, array('code' => $code, 'message' => $message, 'file' => $file, 'line' => $line));
if ($this->previousErrorHandler === true) {
return false;
} elseif ($this->previousErrorHandler) {
return call_user_func($this->previousErrorHandler, $code, $message, $file, $line, $context);
}
}
/**
* @private
*/
public function handleFatalError()
{
$this->reservedMemory = null;
$lastError = error_get_last();
if ($lastError && in_array($lastError['type'], self::$fatalErrors)) {
$this->logger->log(
$this->fatalLevel === null ? LogLevel::ALERT : $this->fatalLevel,
'Fatal Error ('.self::codeToString($lastError['type']).'): '.$lastError['message'],
array('code' => $lastError['type'], 'message' => $lastError['message'], 'file' => $lastError['file'], 'line' => $lastError['line'])
);
}
}
private static function codeToString($code)
{
switch ($code) {
case E_ERROR:
return 'E_ERROR';
case E_WARNING:
return 'E_WARNING';
case E_PARSE:
return 'E_PARSE';
case E_NOTICE:
return 'E_NOTICE';
case E_CORE_ERROR:
return 'E_CORE_ERROR';
case E_CORE_WARNING:
return 'E_CORE_WARNING';
case E_COMPILE_ERROR:
return 'E_COMPILE_ERROR';
case E_COMPILE_WARNING:
return 'E_COMPILE_WARNING';
case E_USER_ERROR:
return 'E_USER_ERROR';
case E_USER_WARNING:
return 'E_USER_WARNING';
case E_USER_NOTICE:
return 'E_USER_NOTICE';
case E_STRICT:
return 'E_STRICT';
case E_RECOVERABLE_ERROR:
return 'E_RECOVERABLE_ERROR';
case E_DEPRECATED:
return 'E_DEPRECATED';
case E_USER_DEPRECATED:
return 'E_USER_DEPRECATED';
}
return 'Unknown PHP error';
}
}

View File

@@ -0,0 +1,79 @@
<?php
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Formatter;
use Monolog\Logger;
/**
* Formats a log message according to the ChromePHP array format
*
* @author Christophe Coevoet <stof@notk.org>
*/
class ChromePHPFormatter implements FormatterInterface
{
/**
* Translates Monolog log levels to Wildfire levels.
*/
private $logLevels = array(
Logger::DEBUG => 'log',
Logger::INFO => 'info',
Logger::NOTICE => 'info',
Logger::WARNING => 'warn',
Logger::ERROR => 'error',
Logger::CRITICAL => 'error',
Logger::ALERT => 'error',
Logger::EMERGENCY => 'error',
);
/**
* {@inheritdoc}
*/
public function format(array $record)
{
// Retrieve the line and file if set and remove them from the formatted extra
$backtrace = 'unknown';
if (isset($record['extra']['file']) && isset($record['extra']['line'])) {
$backtrace = $record['extra']['file'].' : '.$record['extra']['line'];
unset($record['extra']['file']);
unset($record['extra']['line']);
}
$message = array('message' => $record['message']);
if ($record['context']) {
$message['context'] = $record['context'];
}
if ($record['extra']) {
$message['extra'] = $record['extra'];
}
if (count($message) === 1) {
$message = reset($message);
}
return array(
$record['channel'],
$message,
$backtrace,
$this->logLevels[$record['level']],
);
}
public function formatBatch(array $records)
{
$formatted = array();
foreach ($records as $record) {
$formatted[] = $this->format($record);
}
return $formatted;
}
}

View File

@@ -0,0 +1,87 @@
<?php
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Formatter;
use Elastica\Document;
/**
* Format a log message into an Elastica Document
*
* @author Jelle Vink <jelle.vink@gmail.com>
*/
class ElasticaFormatter extends NormalizerFormatter
{
/**
* @var string Elastic search index name
*/
protected $index;
/**
* @var string Elastic search document type
*/
protected $type;
/**
* @param string $index Elastic Search index name
* @param string $type Elastic Search document type
*/
public function __construct($index, $type)
{
parent::__construct(\DateTime::ISO8601);
$this->index = $index;
$this->type = $type;
}
/**
* {@inheritdoc}
*/
public function format(array $record)
{
$record = parent::format($record);
return $this->getDocument($record);
}
/**
* Getter index
* @return string
*/
public function getIndex()
{
return $this->index;
}
/**
* Getter type
* @return string
*/
public function getType()
{
return $this->type;
}
/**
* Convert a log message into an Elastica Document
*
* @param array $record Log message
* @return Document
*/
protected function getDocument($record)
{
$document = new Document();
$document->setData($record);
$document->setType($this->type);
$document->setIndex($this->index);
return $document;
}
}

View File

@@ -0,0 +1,104 @@
<?php
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Formatter;
/**
* formats the record to be used in the FlowdockHandler
*
* @author Dominik Liebler <liebler.dominik@gmail.com>
*/
class FlowdockFormatter implements FormatterInterface
{
/**
* @var string
*/
private $source;
/**
* @var string
*/
private $sourceEmail;
/**
* @param string $source
* @param string $sourceEmail
*/
public function __construct($source, $sourceEmail)
{
$this->source = $source;
$this->sourceEmail = $sourceEmail;
}
/**
* {@inheritdoc}
*/
public function format(array $record)
{
$tags = array(
'#logs',
'#' . strtolower($record['level_name']),
'#' . $record['channel'],
);
foreach ($record['extra'] as $value) {
$tags[] = '#' . $value;
}
$subject = sprintf(
'in %s: %s - %s',
$this->source,
$record['level_name'],
$this->getShortMessage($record['message'])
);
$record['flowdock'] = array(
'source' => $this->source,
'from_address' => $this->sourceEmail,
'subject' => $subject,
'content' => $record['message'],
'tags' => $tags,
'project' => $this->source,
);
return $record;
}
/**
* {@inheritdoc}
*/
public function formatBatch(array $records)
{
$formatted = array();
foreach ($records as $record) {
$formatted[] = $this->format($record);
}
return $formatted;
}
/**
* @param string $message
*
* @return string
*/
public function getShortMessage($message)
{
$maxLength = 45;
if (strlen($message) > $maxLength) {
$message = substr($message, 0, $maxLength - 4) . ' ...';
}
return $message;
}
}

View File

@@ -0,0 +1,36 @@
<?php
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Formatter;
/**
* Interface for formatters
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
interface FormatterInterface
{
/**
* Formats a log record.
*
* @param array $record A record to format
* @return mixed The formatted record
*/
public function format(array $record);
/**
* Formats a set of log records.
*
* @param array $records A set of records to format
* @return mixed The formatted set of records
*/
public function formatBatch(array $records);
}

View File

@@ -0,0 +1,111 @@
<?php
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Formatter;
use Monolog\Logger;
use Gelf\Message;
/**
* Serializes a log message to GELF
* @see http://www.graylog2.org/about/gelf
*
* @author Matt Lehner <mlehner@gmail.com>
*/
class GelfMessageFormatter extends NormalizerFormatter
{
/**
* @var string the name of the system for the Gelf log message
*/
protected $systemName;
/**
* @var string a prefix for 'extra' fields from the Monolog record (optional)
*/
protected $extraPrefix;
/**
* @var string a prefix for 'context' fields from the Monolog record (optional)
*/
protected $contextPrefix;
/**
* Translates Monolog log levels to Graylog2 log priorities.
*/
private $logLevels = array(
Logger::DEBUG => 7,
Logger::INFO => 6,
Logger::NOTICE => 5,
Logger::WARNING => 4,
Logger::ERROR => 3,
Logger::CRITICAL => 2,
Logger::ALERT => 1,
Logger::EMERGENCY => 0,
);
public function __construct($systemName = null, $extraPrefix = null, $contextPrefix = 'ctxt_')
{
parent::__construct('U.u');
$this->systemName = $systemName ?: gethostname();
$this->extraPrefix = $extraPrefix;
$this->contextPrefix = $contextPrefix;
}
/**
* {@inheritdoc}
*/
public function format(array $record)
{
$record = parent::format($record);
if (!isset($record['datetime'], $record['message'], $record['level'])) {
throw new \InvalidArgumentException('The record should at least contain datetime, message and level keys, '.var_export($record, true).' given');
}
$message = new Message();
$message
->setTimestamp($record['datetime'])
->setShortMessage((string) $record['message'])
->setHost($this->systemName)
->setLevel($this->logLevels[$record['level']]);
if (isset($record['channel'])) {
$message->setFacility($record['channel']);
}
if (isset($record['extra']['line'])) {
$message->setLine($record['extra']['line']);
unset($record['extra']['line']);
}
if (isset($record['extra']['file'])) {
$message->setFile($record['extra']['file']);
unset($record['extra']['file']);
}
foreach ($record['extra'] as $key => $val) {
$message->setAdditional($this->extraPrefix . $key, is_scalar($val) ? $val : $this->toJson($val));
}
foreach ($record['context'] as $key => $val) {
$message->setAdditional($this->contextPrefix . $key, is_scalar($val) ? $val : $this->toJson($val));
}
if (null === $message->getFile() && isset($record['context']['exception']['file'])) {
if (preg_match("/^(.+):([0-9]+)$/", $record['context']['exception']['file'], $matches)) {
$message->setFile($matches[1]);
$message->setLine($matches[2]);
}
}
return $message;
}
}

View File

@@ -0,0 +1,140 @@
<?php
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Formatter;
use Monolog\Logger;
/**
* Formats incoming records into an HTML table
*
* This is especially useful for html email logging
*
* @author Tiago Brito <tlfbrito@gmail.com>
*/
class HtmlFormatter extends NormalizerFormatter
{
/**
* Translates Monolog log levels to html color priorities.
*/
private $logLevels = array(
Logger::DEBUG => '#cccccc',
Logger::INFO => '#468847',
Logger::NOTICE => '#3a87ad',
Logger::WARNING => '#c09853',
Logger::ERROR => '#f0ad4e',
Logger::CRITICAL => '#FF7708',
Logger::ALERT => '#C12A19',
Logger::EMERGENCY => '#000000',
);
/**
* @param string $dateFormat The format of the timestamp: one supported by DateTime::format
*/
public function __construct($dateFormat = null)
{
parent::__construct($dateFormat);
}
/**
* Creates an HTML table row
*
* @param string $th Row header content
* @param string $td Row standard cell content
* @param bool $escapeTd false if td content must not be html escaped
* @return string
*/
private function addRow($th, $td = ' ', $escapeTd = true)
{
$th = htmlspecialchars($th, ENT_NOQUOTES, 'UTF-8');
if ($escapeTd) {
$td = '<pre>'.htmlspecialchars($td, ENT_NOQUOTES, 'UTF-8').'</pre>';
}
return "<tr style=\"padding: 4px;spacing: 0;text-align: left;\">\n<th style=\"background: #cccccc\" width=\"100px\">$th:</th>\n<td style=\"padding: 4px;spacing: 0;text-align: left;background: #eeeeee\">".$td."</td>\n</tr>";
}
/**
* Create a HTML h1 tag
*
* @param string $title Text to be in the h1
* @param integer $level Error level
* @return string
*/
private function addTitle($title, $level)
{
$title = htmlspecialchars($title, ENT_NOQUOTES, 'UTF-8');
return '<h1 style="background: '.$this->logLevels[$level].';color: #ffffff;padding: 5px;" class="monolog-output">'.$title.'</h1>';
}
/**
* Formats a log record.
*
* @param array $record A record to format
* @return mixed The formatted record
*/
public function format(array $record)
{
$output = $this->addTitle($record['level_name'], $record['level']);
$output .= '<table cellspacing="1" width="100%" class="monolog-output">';
$output .= $this->addRow('Message', (string) $record['message']);
$output .= $this->addRow('Time', $record['datetime']->format($this->dateFormat));
$output .= $this->addRow('Channel', $record['channel']);
if ($record['context']) {
$embeddedTable = '<table cellspacing="1" width="100%">';
foreach ($record['context'] as $key => $value) {
$embeddedTable .= $this->addRow($key, $this->convertToString($value));
}
$embeddedTable .= '</table>';
$output .= $this->addRow('Context', $embeddedTable, false);
}
if ($record['extra']) {
$embeddedTable = '<table cellspacing="1" width="100%">';
foreach ($record['extra'] as $key => $value) {
$embeddedTable .= $this->addRow($key, $this->convertToString($value));
}
$embeddedTable .= '</table>';
$output .= $this->addRow('Extra', $embeddedTable, false);
}
return $output.'</table>';
}
/**
* Formats a set of log records.
*
* @param array $records A set of records to format
* @return mixed The formatted set of records
*/
public function formatBatch(array $records)
{
$message = '';
foreach ($records as $record) {
$message .= $this->format($record);
}
return $message;
}
protected function convertToString($data)
{
if (null === $data || is_scalar($data)) {
return (string) $data;
}
$data = $this->normalize($data);
if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
return str_replace('\\/', '/', json_encode($data));
}
}

View File

@@ -0,0 +1,116 @@
<?php
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Formatter;
/**
* Encodes whatever record data is passed to it as json
*
* This can be useful to log to databases or remote APIs
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class JsonFormatter implements FormatterInterface
{
const BATCH_MODE_JSON = 1;
const BATCH_MODE_NEWLINES = 2;
protected $batchMode;
protected $appendNewline;
/**
* @param int $batchMode
*/
public function __construct($batchMode = self::BATCH_MODE_JSON, $appendNewline = true)
{
$this->batchMode = $batchMode;
$this->appendNewline = $appendNewline;
}
/**
* The batch mode option configures the formatting style for
* multiple records. By default, multiple records will be
* formatted as a JSON-encoded array. However, for
* compatibility with some API endpoints, alternative styles
* are available.
*
* @return int
*/
public function getBatchMode()
{
return $this->batchMode;
}
/**
* True if newlines are appended to every formatted record
*
* @return bool
*/
public function isAppendingNewlines()
{
return $this->appendNewline;
}
/**
* {@inheritdoc}
*/
public function format(array $record)
{
return json_encode($record) . ($this->appendNewline ? "\n" : '');
}
/**
* {@inheritdoc}
*/
public function formatBatch(array $records)
{
switch ($this->batchMode) {
case static::BATCH_MODE_NEWLINES:
return $this->formatBatchNewlines($records);
case static::BATCH_MODE_JSON:
default:
return $this->formatBatchJson($records);
}
}
/**
* Return a JSON-encoded array of records.
*
* @param array $records
* @return string
*/
protected function formatBatchJson(array $records)
{
return json_encode($records);
}
/**
* Use new lines to separate records instead of a
* JSON-encoded array.
*
* @param array $records
* @return string
*/
protected function formatBatchNewlines(array $records)
{
$instance = $this;
$oldNewline = $this->appendNewline;
$this->appendNewline = false;
array_walk($records, function (&$value, $key) use ($instance) {
$value = $instance->format($value);
});
$this->appendNewline = $oldNewline;
return implode("\n", $records);
}
}

View File

@@ -0,0 +1,159 @@
<?php
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Formatter;
use Exception;
/**
* Formats incoming records into a one-line string
*
* This is especially useful for logging to files
*
* @author Jordi Boggiano <j.boggiano@seld.be>
* @author Christophe Coevoet <stof@notk.org>
*/
class LineFormatter extends NormalizerFormatter
{
const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n";
protected $format;
protected $allowInlineLineBreaks;
protected $ignoreEmptyContextAndExtra;
protected $includeStacktraces;
/**
* @param string $format The format of the message
* @param string $dateFormat The format of the timestamp: one supported by DateTime::format
* @param bool $allowInlineLineBreaks Whether to allow inline line breaks in log entries
* @param bool $ignoreEmptyContextAndExtra
*/
public function __construct($format = null, $dateFormat = null, $allowInlineLineBreaks = false, $ignoreEmptyContextAndExtra = false)
{
$this->format = $format ?: static::SIMPLE_FORMAT;
$this->allowInlineLineBreaks = $allowInlineLineBreaks;
$this->ignoreEmptyContextAndExtra = $ignoreEmptyContextAndExtra;
parent::__construct($dateFormat);
}
public function includeStacktraces($include = true)
{
$this->includeStacktraces = $include;
if ($this->includeStacktraces) {
$this->allowInlineLineBreaks = true;
}
}
public function allowInlineLineBreaks($allow = true)
{
$this->allowInlineLineBreaks = $allow;
}
public function ignoreEmptyContextAndExtra($ignore = true)
{
$this->ignoreEmptyContextAndExtra = $ignore;
}
/**
* {@inheritdoc}
*/
public function format(array $record)
{
$vars = parent::format($record);
$output = $this->format;
foreach ($vars['extra'] as $var => $val) {
if (false !== strpos($output, '%extra.'.$var.'%')) {
$output = str_replace('%extra.'.$var.'%', $this->stringify($val), $output);
unset($vars['extra'][$var]);
}
}
if ($this->ignoreEmptyContextAndExtra) {
if (empty($vars['context'])) {
unset($vars['context']);
$output = str_replace('%context%', '', $output);
}
if (empty($vars['extra'])) {
unset($vars['extra']);
$output = str_replace('%extra%', '', $output);
}
}
foreach ($vars as $var => $val) {
if (false !== strpos($output, '%'.$var.'%')) {
$output = str_replace('%'.$var.'%', $this->stringify($val), $output);
}
}
return $output;
}
public function formatBatch(array $records)
{
$message = '';
foreach ($records as $record) {
$message .= $this->format($record);
}
return $message;
}
public function stringify($value)
{
return $this->replaceNewlines($this->convertToString($value));
}
protected function normalizeException(Exception $e)
{
$previousText = '';
if ($previous = $e->getPrevious()) {
do {
$previousText .= ', '.get_class($previous).'(code: '.$previous->getCode().'): '.$previous->getMessage().' at '.$previous->getFile().':'.$previous->getLine();
} while ($previous = $previous->getPrevious());
}
$str = '[object] ('.get_class($e).'(code: '.$e->getCode().'): '.$e->getMessage().' at '.$e->getFile().':'.$e->getLine().$previousText.')';
if ($this->includeStacktraces) {
$str .= "\n[stacktrace]\n".$e->getTraceAsString();
}
return $str;
}
protected function convertToString($data)
{
if (null === $data || is_bool($data)) {
return var_export($data, true);
}
if (is_scalar($data)) {
return (string) $data;
}
if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
return $this->toJson($data, true);
}
return str_replace('\\/', '/', @json_encode($data));
}
protected function replaceNewlines($str)
{
if ($this->allowInlineLineBreaks) {
return $str;
}
return strtr($str, array("\r\n" => ' ', "\r" => ' ', "\n" => ' '));
}
}

View File

@@ -0,0 +1,47 @@
<?php
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Formatter;
/**
* Encodes message information into JSON in a format compatible with Loggly.
*
* @author Adam Pancutt <adam@pancutt.com>
*/
class LogglyFormatter extends JsonFormatter
{
/**
* Overrides the default batch mode to new lines for compatibility with the
* Loggly bulk API.
*
* @param integer $batchMode
*/
public function __construct($batchMode = self::BATCH_MODE_NEWLINES, $appendNewline = false)
{
parent::__construct($batchMode, $appendNewline);
}
/**
* Appends the 'timestamp' parameter for indexing by Loggly.
*
* @see https://www.loggly.com/docs/automated-parsing/#json
* @see \Monolog\Formatter\JsonFormatter::format()
*/
public function format(array $record)
{
if (isset($record["datetime"]) && ($record["datetime"] instanceof \DateTime)) {
$record["timestamp"] = $record["datetime"]->format("Y-m-d\TH:i:s.uO");
// TODO 2.0 unset the 'datetime' parameter, retained for BC
}
return parent::format($record);
}
}

View File

@@ -0,0 +1,165 @@
<?php
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Formatter;
/**
* Serializes a log message to Logstash Event Format
*
* @see http://logstash.net/
* @see https://github.com/logstash/logstash/blob/master/lib/logstash/event.rb
*
* @author Tim Mower <timothy.mower@gmail.com>
*/
class LogstashFormatter extends NormalizerFormatter
{
const V0 = 0;
const V1 = 1;
/**
* @var string the name of the system for the Logstash log message, used to fill the @source field
*/
protected $systemName;
/**
* @var string an application name for the Logstash log message, used to fill the @type field
*/
protected $applicationName;
/**
* @var string a prefix for 'extra' fields from the Monolog record (optional)
*/
protected $extraPrefix;
/**
* @var string a prefix for 'context' fields from the Monolog record (optional)
*/
protected $contextPrefix;
/**
* @var integer logstash format version to use
*/
protected $version;
/**
* @param string $applicationName the application that sends the data, used as the "type" field of logstash
* @param string $systemName the system/machine name, used as the "source" field of logstash, defaults to the hostname of the machine
* @param string $extraPrefix prefix for extra keys inside logstash "fields"
* @param string $contextPrefix prefix for context keys inside logstash "fields", defaults to ctxt_
*/
public function __construct($applicationName, $systemName = null, $extraPrefix = null, $contextPrefix = 'ctxt_', $version = self::V0)
{
// logstash requires a ISO 8601 format date with optional millisecond precision.
parent::__construct('Y-m-d\TH:i:s.uP');
$this->systemName = $systemName ?: gethostname();
$this->applicationName = $applicationName;
$this->extraPrefix = $extraPrefix;
$this->contextPrefix = $contextPrefix;
$this->version = $version;
}
/**
* {@inheritdoc}
*/
public function format(array $record)
{
$record = parent::format($record);
if ($this->version === self::V1) {
$message = $this->formatV1($record);
} else {
$message = $this->formatV0($record);
}
return $this->toJson($message) . "\n";
}
protected function formatV0(array $record)
{
if (empty($record['datetime'])) {
$record['datetime'] = gmdate('c');
}
$message = array(
'@timestamp' => $record['datetime'],
'@source' => $this->systemName,
'@fields' => array()
);
if (isset($record['message'])) {
$message['@message'] = $record['message'];
}
if (isset($record['channel'])) {
$message['@tags'] = array($record['channel']);
$message['@fields']['channel'] = $record['channel'];
}
if (isset($record['level'])) {
$message['@fields']['level'] = $record['level'];
}
if ($this->applicationName) {
$message['@type'] = $this->applicationName;
}
if (isset($record['extra']['server'])) {
$message['@source_host'] = $record['extra']['server'];
}
if (isset($record['extra']['url'])) {
$message['@source_path'] = $record['extra']['url'];
}
if (!empty($record['extra'])) {
foreach ($record['extra'] as $key => $val) {
$message['@fields'][$this->extraPrefix . $key] = $val;
}
}
if (!empty($record['context'])) {
foreach ($record['context'] as $key => $val) {
$message['@fields'][$this->contextPrefix . $key] = $val;
}
}
return $message;
}
protected function formatV1(array $record)
{
if (empty($record['datetime'])) {
$record['datetime'] = gmdate('c');
}
$message = array(
'@timestamp' => $record['datetime'],
'@version' => 1,
'host' => $this->systemName,
);
if (isset($record['message'])) {
$message['message'] = $record['message'];
}
if (isset($record['channel'])) {
$message['type'] = $record['channel'];
$message['channel'] = $record['channel'];
}
if (isset($record['level_name'])) {
$message['level'] = $record['level_name'];
}
if ($this->applicationName) {
$message['type'] = $this->applicationName;
}
if (!empty($record['extra'])) {
foreach ($record['extra'] as $key => $val) {
$message[$this->extraPrefix . $key] = $val;
}
}
if (!empty($record['context'])) {
foreach ($record['context'] as $key => $val) {
$message[$this->contextPrefix . $key] = $val;
}
}
return $message;
}
}

View File

@@ -0,0 +1,105 @@
<?php
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Formatter;
/**
* Formats a record for use with the MongoDBHandler.
*
* @author Florian Plattner <me@florianplattner.de>
*/
class MongoDBFormatter implements FormatterInterface
{
private $exceptionTraceAsString;
private $maxNestingLevel;
/**
* @param int $maxNestingLevel 0 means infinite nesting, the $record itself is level 1, $record['context'] is 2
* @param bool $exceptionTraceAsString set to false to log exception traces as a sub documents instead of strings
*/
public function __construct($maxNestingLevel = 3, $exceptionTraceAsString = true)
{
$this->maxNestingLevel = max($maxNestingLevel, 0);
$this->exceptionTraceAsString = (bool) $exceptionTraceAsString;
}
/**
* {@inheritDoc}
*/
public function format(array $record)
{
return $this->formatArray($record);
}
/**
* {@inheritDoc}
*/
public function formatBatch(array $records)
{
foreach ($records as $key => $record) {
$records[$key] = $this->format($record);
}
return $records;
}
protected function formatArray(array $record, $nestingLevel = 0)
{
if ($this->maxNestingLevel == 0 || $nestingLevel <= $this->maxNestingLevel) {
foreach ($record as $name => $value) {
if ($value instanceof \DateTime) {
$record[$name] = $this->formatDate($value, $nestingLevel + 1);
} elseif ($value instanceof \Exception) {
$record[$name] = $this->formatException($value, $nestingLevel + 1);
} elseif (is_array($value)) {
$record[$name] = $this->formatArray($value, $nestingLevel + 1);
} elseif (is_object($value)) {
$record[$name] = $this->formatObject($value, $nestingLevel + 1);
}
}
} else {
$record = '[...]';
}
return $record;
}
protected function formatObject($value, $nestingLevel)
{
$objectVars = get_object_vars($value);
$objectVars['class'] = get_class($value);
return $this->formatArray($objectVars, $nestingLevel);
}
protected function formatException(\Exception $exception, $nestingLevel)
{
$formattedException = array(
'class' => get_class($exception),
'message' => $exception->getMessage(),
'code' => $exception->getCode(),
'file' => $exception->getFile() . ':' . $exception->getLine(),
);
if ($this->exceptionTraceAsString === true) {
$formattedException['trace'] = $exception->getTraceAsString();
} else {
$formattedException['trace'] = $exception->getTrace();
}
return $this->formatArray($formattedException, $nestingLevel);
}
protected function formatDate(\DateTime $value, $nestingLevel)
{
return new \MongoDate($value->getTimestamp());
}
}

View File

@@ -0,0 +1,150 @@
<?php
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Formatter;
use Exception;
/**
* Normalizes incoming records to remove objects/resources so it's easier to dump to various targets
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class NormalizerFormatter implements FormatterInterface
{
const SIMPLE_DATE = "Y-m-d H:i:s";
protected $dateFormat;
/**
* @param string $dateFormat The format of the timestamp: one supported by DateTime::format
*/
public function __construct($dateFormat = null)
{
$this->dateFormat = $dateFormat ?: static::SIMPLE_DATE;
if (!function_exists('json_encode')) {
throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s NormalizerFormatter');
}
}
/**
* {@inheritdoc}
*/
public function format(array $record)
{
return $this->normalize($record);
}
/**
* {@inheritdoc}
*/
public function formatBatch(array $records)
{
foreach ($records as $key => $record) {
$records[$key] = $this->format($record);
}
return $records;
}
protected function normalize($data)
{
if (null === $data || is_scalar($data)) {
if (is_float($data)) {
if (is_infinite($data)) {
return ($data > 0 ? '' : '-') . 'INF';
}
if (is_nan($data)) {
return 'NaN';
}
}
return $data;
}
if (is_array($data) || $data instanceof \Traversable) {
$normalized = array();
$count = 1;
foreach ($data as $key => $value) {
if ($count++ >= 1000) {
$normalized['...'] = 'Over 1000 items, aborting normalization';
break;
}
$normalized[$key] = $this->normalize($value);
}
return $normalized;
}
if ($data instanceof \DateTime) {
return $data->format($this->dateFormat);
}
if (is_object($data)) {
if ($data instanceof Exception) {
return $this->normalizeException($data);
}
return sprintf("[object] (%s: %s)", get_class($data), $this->toJson($data, true));
}
if (is_resource($data)) {
return '[resource]';
}
return '[unknown('.gettype($data).')]';
}
protected function normalizeException(Exception $e)
{
$data = array(
'class' => get_class($e),
'message' => $e->getMessage(),
'code' => $e->getCode(),
'file' => $e->getFile().':'.$e->getLine(),
);
$trace = $e->getTrace();
foreach ($trace as $frame) {
if (isset($frame['file'])) {
$data['trace'][] = $frame['file'].':'.$frame['line'];
} else {
// We should again normalize the frames, because it might contain invalid items
$data['trace'][] = $this->toJson($this->normalize($frame), true);
}
}
if ($previous = $e->getPrevious()) {
$data['previous'] = $this->normalizeException($previous);
}
return $data;
}
protected function toJson($data, $ignoreErrors = false)
{
// suppress json_encode errors since it's twitchy with some inputs
if ($ignoreErrors) {
if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
return @json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
return @json_encode($data);
}
if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
return json_encode($data);
}
}

View File

@@ -0,0 +1,48 @@
<?php
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Formatter;
/**
* Formats data into an associative array of scalar values.
* Objects and arrays will be JSON encoded.
*
* @author Andrew Lawson <adlawson@gmail.com>
*/
class ScalarFormatter extends NormalizerFormatter
{
/**
* {@inheritdoc}
*/
public function format(array $record)
{
foreach ($record as $key => $value) {
$record[$key] = $this->normalizeValue($value);
}
return $record;
}
/**
* @param mixed $value
* @return mixed
*/
protected function normalizeValue($value)
{
$normalized = $this->normalize($value);
if (is_array($normalized) || is_object($normalized)) {
return $this->toJson($normalized, true);
}
return $normalized;
}
}

View File

@@ -0,0 +1,113 @@
<?php
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Formatter;
use Monolog\Logger;
/**
* Serializes a log message according to Wildfire's header requirements
*
* @author Eric Clemmons (@ericclemmons) <eric@uxdriven.com>
* @author Christophe Coevoet <stof@notk.org>
* @author Kirill chEbba Chebunin <iam@chebba.org>
*/
class WildfireFormatter extends NormalizerFormatter
{
const TABLE = 'table';
/**
* Translates Monolog log levels to Wildfire levels.
*/
private $logLevels = array(
Logger::DEBUG => 'LOG',
Logger::INFO => 'INFO',
Logger::NOTICE => 'INFO',
Logger::WARNING => 'WARN',
Logger::ERROR => 'ERROR',
Logger::CRITICAL => 'ERROR',
Logger::ALERT => 'ERROR',
Logger::EMERGENCY => 'ERROR',
);
/**
* {@inheritdoc}
*/
public function format(array $record)
{
// Retrieve the line and file if set and remove them from the formatted extra
$file = $line = '';
if (isset($record['extra']['file'])) {
$file = $record['extra']['file'];
unset($record['extra']['file']);
}
if (isset($record['extra']['line'])) {
$line = $record['extra']['line'];
unset($record['extra']['line']);
}
$record = $this->normalize($record);
$message = array('message' => $record['message']);
$handleError = false;
if ($record['context']) {
$message['context'] = $record['context'];
$handleError = true;
}
if ($record['extra']) {
$message['extra'] = $record['extra'];
$handleError = true;
}
if (count($message) === 1) {
$message = reset($message);
}
if (isset($record['context'][self::TABLE])) {
$type = 'TABLE';
$label = $record['channel'] .': '. $record['message'];
$message = $record['context'][self::TABLE];
} else {
$type = $this->logLevels[$record['level']];
$label = $record['channel'];
}
// Create JSON object describing the appearance of the message in the console
$json = $this->toJson(array(
array(
'Type' => $type,
'File' => $file,
'Line' => $line,
'Label' => $label,
),
$message,
), $handleError);
// The message itself is a serialization of the above JSON object + it's length
return sprintf(
'%s|%s|',
strlen($json),
$json
);
}
public function formatBatch(array $records)
{
throw new \BadMethodCallException('Batch formatting does not make sense for the WildfireFormatter');
}
protected function normalize($data)
{
if (is_object($data) && !$data instanceof \DateTime) {
return $data;
}
return parent::normalize($data);
}
}

View File

@@ -0,0 +1,184 @@
<?php
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Handler;
use Monolog\Logger;
use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LineFormatter;
/**
* Base Handler class providing the Handler structure
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
abstract class AbstractHandler implements HandlerInterface
{
protected $level = Logger::DEBUG;
protected $bubble = true;
/**
* @var FormatterInterface
*/
protected $formatter;
protected $processors = array();
/**
* @param integer $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
*/
public function __construct($level = Logger::DEBUG, $bubble = true)
{
$this->setLevel($level);
$this->bubble = $bubble;
}
/**
* {@inheritdoc}
*/
public function isHandling(array $record)
{
return $record['level'] >= $this->level;
}
/**
* {@inheritdoc}
*/
public function handleBatch(array $records)
{
foreach ($records as $record) {
$this->handle($record);
}
}
/**
* Closes the handler.
*
* This will be called automatically when the object is destroyed
*/
public function close()
{
}
/**
* {@inheritdoc}
*/
public function pushProcessor($callback)
{
if (!is_callable($callback)) {
throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), '.var_export($callback, true).' given');
}
array_unshift($this->processors, $callback);
return $this;
}
/**
* {@inheritdoc}
*/
public function popProcessor()
{
if (!$this->processors) {
throw new \LogicException('You tried to pop from an empty processor stack.');
}
return array_shift($this->processors);
}
/**
* {@inheritdoc}
*/
public function setFormatter(FormatterInterface $formatter)
{
$this->formatter = $formatter;
return $this;
}
/**
* {@inheritdoc}
*/
public function getFormatter()
{
if (!$this->formatter) {
$this->formatter = $this->getDefaultFormatter();
}
return $this->formatter;
}
/**
* Sets minimum logging level at which this handler will be triggered.
*
* @param integer $level
* @return self
*/
public function setLevel($level)
{
$this->level = Logger::toMonologLevel($level);
return $this;
}
/**
* Gets minimum logging level at which this handler will be triggered.
*
* @return integer
*/
public function getLevel()
{
return $this->level;
}
/**
* Sets the bubbling behavior.
*
* @param Boolean $bubble true means that this handler allows bubbling.
* false means that bubbling is not permitted.
* @return self
*/
public function setBubble($bubble)
{
$this->bubble = $bubble;
return $this;
}
/**
* Gets the bubbling behavior.
*
* @return Boolean true means that this handler allows bubbling.
* false means that bubbling is not permitted.
*/
public function getBubble()
{
return $this->bubble;
}
public function __destruct()
{
try {
$this->close();
} catch (\Exception $e) {
// do nothing
}
}
/**
* Gets the default formatter.
*
* @return FormatterInterface
*/
protected function getDefaultFormatter()
{
return new LineFormatter();
}
}

View File

@@ -0,0 +1,66 @@
<?php
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Handler;
/**
* Base Handler class providing the Handler structure
*
* Classes extending it should (in most cases) only implement write($record)
*
* @author Jordi Boggiano <j.boggiano@seld.be>
* @author Christophe Coevoet <stof@notk.org>
*/
abstract class AbstractProcessingHandler extends AbstractHandler
{
/**
* {@inheritdoc}
*/
public function handle(array $record)
{
if (!$this->isHandling($record)) {
return false;
}
$record = $this->processRecord($record);
$record['formatted'] = $this->getFormatter()->format($record);
$this->write($record);
return false === $this->bubble;
}
/**
* Writes the record down to the log of the implementing handler
*
* @param array $record
* @return void
*/
abstract protected function write(array $record);
/**
* Processes a record.
*
* @param array $record
* @return array
*/
protected function processRecord(array $record)
{
if ($this->processors) {
foreach ($this->processors as $processor) {
$record = call_user_func($processor, $record);
}
}
return $record;
}
}

View File

@@ -0,0 +1,92 @@
<?php
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Handler;
use Monolog\Logger;
use Monolog\Formatter\LineFormatter;
/**
* Common syslog functionality
*/
abstract class AbstractSyslogHandler extends AbstractProcessingHandler
{
protected $facility;
/**
* Translates Monolog log levels to syslog log priorities.
*/
protected $logLevels = array(
Logger::DEBUG => LOG_DEBUG,
Logger::INFO => LOG_INFO,
Logger::NOTICE => LOG_NOTICE,
Logger::WARNING => LOG_WARNING,
Logger::ERROR => LOG_ERR,
Logger::CRITICAL => LOG_CRIT,
Logger::ALERT => LOG_ALERT,
Logger::EMERGENCY => LOG_EMERG,
);
/**
* List of valid log facility names.
*/
protected $facilities = array(
'auth' => LOG_AUTH,
'authpriv' => LOG_AUTHPRIV,
'cron' => LOG_CRON,
'daemon' => LOG_DAEMON,
'kern' => LOG_KERN,
'lpr' => LOG_LPR,
'mail' => LOG_MAIL,
'news' => LOG_NEWS,
'syslog' => LOG_SYSLOG,
'user' => LOG_USER,
'uucp' => LOG_UUCP,
);
/**
* @param mixed $facility
* @param integer $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
*/
public function __construct($facility = LOG_USER, $level = Logger::DEBUG, $bubble = true)
{
parent::__construct($level, $bubble);
if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
$this->facilities['local0'] = LOG_LOCAL0;
$this->facilities['local1'] = LOG_LOCAL1;
$this->facilities['local2'] = LOG_LOCAL2;
$this->facilities['local3'] = LOG_LOCAL3;
$this->facilities['local4'] = LOG_LOCAL4;
$this->facilities['local5'] = LOG_LOCAL5;
$this->facilities['local6'] = LOG_LOCAL6;
$this->facilities['local7'] = LOG_LOCAL7;
}
// convert textual description of facility to syslog constant
if (array_key_exists(strtolower($facility), $this->facilities)) {
$facility = $this->facilities[strtolower($facility)];
} elseif (!in_array($facility, array_values($this->facilities), true)) {
throw new \UnexpectedValueException('Unknown facility value "'.$facility.'" given');
}
$this->facility = $facility;
}
/**
* {@inheritdoc}
*/
protected function getDefaultFormatter()
{
return new LineFormatter('%channel%.%level_name%: %message% %context% %extra%');
}
}

View File

@@ -0,0 +1,98 @@
<?php
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Handler;
use Monolog\Logger;
use Monolog\Formatter\JsonFormatter;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Channel\AMQPChannel;
use AMQPExchange;
class AmqpHandler extends AbstractProcessingHandler
{
/**
* @var AMQPExchange|AMQPChannel $exchange
*/
protected $exchange;
/**
* @var string
*/
protected $exchangeName;
/**
* @param AMQPExchange|AMQPChannel $exchange AMQPExchange (php AMQP ext) or PHP AMQP lib channel, ready for use
* @param string $exchangeName
* @param int $level
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/
public function __construct($exchange, $exchangeName = 'log', $level = Logger::DEBUG, $bubble = true)
{
if ($exchange instanceof AMQPExchange) {
$exchange->setName($exchangeName);
} elseif ($exchange instanceof AMQPChannel) {
$this->exchangeName = $exchangeName;
} else {
throw new \InvalidArgumentException('PhpAmqpLib\Channel\AMQPChannel or AMQPExchange instance required');
}
$this->exchange = $exchange;
parent::__construct($level, $bubble);
}
/**
* {@inheritDoc}
*/
protected function write(array $record)
{
$data = $record["formatted"];
$routingKey = sprintf(
'%s.%s',
// TODO 2.0 remove substr call
substr($record['level_name'], 0, 4),
$record['channel']
);
if ($this->exchange instanceof AMQPExchange) {
$this->exchange->publish(
$data,
strtolower($routingKey),
0,
array(
'delivery_mode' => 2,
'Content-type' => 'application/json'
)
);
} else {
$this->exchange->basic_publish(
new AMQPMessage(
(string) $data,
array(
'delivery_mode' => 2,
'content_type' => 'application/json'
)
),
$this->exchangeName,
strtolower($routingKey)
);
}
}
/**
* {@inheritDoc}
*/
protected function getDefaultFormatter()
{
return new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false);
}
}

View File

@@ -0,0 +1,184 @@
<?php
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Handler;
use Monolog\Formatter\LineFormatter;
/**
* Handler sending logs to browser's javascript console with no browser extension required
*
* @author Olivier Poitrey <rs@dailymotion.com>
*/
class BrowserConsoleHandler extends AbstractProcessingHandler
{
protected static $initialized = false;
protected static $records = array();
/**
* {@inheritDoc}
*
* Formatted output may contain some formatting markers to be transferred to `console.log` using the %c format.
*
* Example of formatted string:
*
* You can do [[blue text]]{color: blue} or [[green background]]{background-color: green; color: white}
*
*/
protected function getDefaultFormatter()
{
return new LineFormatter('[[%channel%]]{macro: autolabel} [[%level_name%]]{font-weight: bold} %message%');
}
/**
* {@inheritDoc}
*/
protected function write(array $record)
{
// Accumulate records
self::$records[] = $record;
// Register shutdown handler if not already done
if (PHP_SAPI !== 'cli' && !self::$initialized) {
self::$initialized = true;
register_shutdown_function(array('Monolog\Handler\BrowserConsoleHandler', 'send'));
}
}
/**
* Convert records to javascript console commands and send it to the browser.
* This method is automatically called on PHP shutdown if output is HTML.
*/
public static function send()
{
// Check content type
foreach (headers_list() as $header) {
if (stripos($header, 'content-type:') === 0) {
if (stripos($header, 'text/html') === false) {
// This handler only works with HTML outputs
return;
}
break;
}
}
if (count(self::$records)) {
echo '<script>' . self::generateScript() . '</script>';
self::reset();
}
}
/**
* Forget all logged records
*/
public static function reset()
{
self::$records = array();
}
private static function generateScript()
{
$script = array();
foreach (self::$records as $record) {
$context = self::dump('Context', $record['context']);
$extra = self::dump('Extra', $record['extra']);
if (empty($context) && empty($extra)) {
$script[] = self::call_array('log', self::handleStyles($record['formatted']));
} else {
$script = array_merge($script,
array(self::call_array('groupCollapsed', self::handleStyles($record['formatted']))),
$context,
$extra,
array(self::call('groupEnd'))
);
}
}
return "(function (c) {if (c && c.groupCollapsed) {\n" . implode("\n", $script) . "\n}})(console);";
}
private static function handleStyles($formatted)
{
$args = array(self::quote('font-weight: normal'));
$format = '%c' . $formatted;
preg_match_all('/\[\[(.*?)\]\]\{([^}]*)\}/s', $format, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
foreach (array_reverse($matches) as $match) {
$args[] = self::quote(self::handleCustomStyles($match[2][0], $match[1][0]));
$args[] = '"font-weight: normal"';
$pos = $match[0][1];
$format = substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . substr($format, $pos + strlen($match[0][0]));
}
array_unshift($args, self::quote($format));
return $args;
}
private static function handleCustomStyles($style, $string)
{
static $colors = array('blue', 'green', 'red', 'magenta', 'orange', 'black', 'grey');
static $labels = array();
return preg_replace_callback('/macro\s*:(.*?)(?:;|$)/', function ($m) use ($string, &$colors, &$labels) {
if (trim($m[1]) === 'autolabel') {
// Format the string as a label with consistent auto assigned background color
if (!isset($labels[$string])) {
$labels[$string] = $colors[count($labels) % count($colors)];
}
$color = $labels[$string];
return "background-color: $color; color: white; border-radius: 3px; padding: 0 2px 0 2px";
}
return $m[1];
}, $style);
}
private static function dump($title, array $dict)
{
$script = array();
$dict = array_filter($dict);
if (empty($dict)) {
return $script;
}
$script[] = self::call('log', self::quote('%c%s'), self::quote('font-weight: bold'), self::quote($title));
foreach ($dict as $key => $value) {
$value = json_encode($value);
if (empty($value)) {
$value = self::quote('');
}
$script[] = self::call('log', self::quote('%s: %o'), self::quote($key), $value);
}
return $script;
}
private static function quote($arg)
{
return '"' . addcslashes($arg, "\"\n") . '"';
}
private static function call()
{
$args = func_get_args();
$method = array_shift($args);
return self::call_array($method, $args);
}
private static function call_array($method, array $args)
{
return 'c.' . $method . '(' . implode(', ', $args) . ');';
}
}

View File

@@ -0,0 +1,117 @@
<?php
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Handler;
use Monolog\Logger;
/**
* Buffers all records until closing the handler and then pass them as batch.
*
* This is useful for a MailHandler to send only one mail per request instead of
* sending one per log message.
*
* @author Christophe Coevoet <stof@notk.org>
*/
class BufferHandler extends AbstractHandler
{
protected $handler;
protected $bufferSize = 0;
protected $bufferLimit;
protected $flushOnOverflow;
protected $buffer = array();
protected $initialized = false;
/**
* @param HandlerInterface $handler Handler.
* @param integer $bufferLimit How many entries should be buffered at most, beyond that the oldest items are removed from the buffer.
* @param integer $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
* @param Boolean $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded
*/
public function __construct(HandlerInterface $handler, $bufferLimit = 0, $level = Logger::DEBUG, $bubble = true, $flushOnOverflow = false)
{
parent::__construct($level, $bubble);
$this->handler = $handler;
$this->bufferLimit = (int) $bufferLimit;
$this->flushOnOverflow = $flushOnOverflow;
}
/**
* {@inheritdoc}
*/
public function handle(array $record)
{
if ($record['level'] < $this->level) {
return false;
}
if (!$this->initialized) {
// __destructor() doesn't get called on Fatal errors
register_shutdown_function(array($this, 'close'));
$this->initialized = true;
}
if ($this->bufferLimit > 0 && $this->bufferSize === $this->bufferLimit) {
if ($this->flushOnOverflow) {
$this->flush();
} else {
array_shift($this->buffer);
$this->bufferSize--;
}
}
if ($this->processors) {
foreach ($this->processors as $processor) {
$record = call_user_func($processor, $record);
}
}
$this->buffer[] = $record;
$this->bufferSize++;
return false === $this->bubble;
}
public function flush()
{
if ($this->bufferSize === 0) {
return;
}
$this->handler->handleBatch($this->buffer);
$this->clear();
}
public function __destruct()
{
// suppress the parent behavior since we already have register_shutdown_function()
// to call close(), and the reference contained there will prevent this from being
// GC'd until the end of the request
}
/**
* {@inheritdoc}
*/
public function close()
{
$this->flush();
}
/**
* Clears the buffer without flushing any messages down to the wrapped handler.
*/
public function clear()
{
$this->bufferSize = 0;
$this->buffer = array();
}
}

View File

@@ -0,0 +1,204 @@
<?php
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Handler;
use Monolog\Formatter\ChromePHPFormatter;
use Monolog\Logger;
/**
* Handler sending logs to the ChromePHP extension (http://www.chromephp.com/)
*
* @author Christophe Coevoet <stof@notk.org>
*/
class ChromePHPHandler extends AbstractProcessingHandler
{
/**
* Version of the extension
*/
const VERSION = '4.0';
/**
* Header name
*/
const HEADER_NAME = 'X-ChromeLogger-Data';
protected static $initialized = false;
/**
* Tracks whether we sent too much data
*
* Chrome limits the headers to 256KB, so when we sent 240KB we stop sending
*
* @var Boolean
*/
protected static $overflowed = false;
protected static $json = array(
'version' => self::VERSION,
'columns' => array('label', 'log', 'backtrace', 'type'),
'rows' => array(),
);
protected static $sendHeaders = true;
/**
* @param integer $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
*/
public function __construct($level = Logger::DEBUG, $bubble = true)
{
parent::__construct($level, $bubble);
if (!function_exists('json_encode')) {
throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s ChromePHPHandler');
}
}
/**
* {@inheritdoc}
*/
public function handleBatch(array $records)
{
$messages = array();
foreach ($records as $record) {
if ($record['level'] < $this->level) {
continue;
}
$messages[] = $this->processRecord($record);
}
if (!empty($messages)) {
$messages = $this->getFormatter()->formatBatch($messages);
self::$json['rows'] = array_merge(self::$json['rows'], $messages);
$this->send();
}
}
/**
* {@inheritDoc}
*/
protected function getDefaultFormatter()
{
return new ChromePHPFormatter();
}
/**
* Creates & sends header for a record
*
* @see sendHeader()
* @see send()
* @param array $record
*/
protected function write(array $record)
{
self::$json['rows'][] = $record['formatted'];
$this->send();
}
/**
* Sends the log header
*
* @see sendHeader()
*/
protected function send()
{
if (self::$overflowed || !self::$sendHeaders) {
return;
}
if (!self::$initialized) {
self::$initialized = true;
self::$sendHeaders = $this->headersAccepted();
if (!self::$sendHeaders) {
return;
}
self::$json['request_uri'] = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
}
$json = @json_encode(self::$json);
$data = base64_encode(utf8_encode($json));
if (strlen($data) > 240*1024) {
self::$overflowed = true;
$record = array(
'message' => 'Incomplete logs, chrome header size limit reached',
'context' => array(),
'level' => Logger::WARNING,
'level_name' => Logger::getLevelName(Logger::WARNING),
'channel' => 'monolog',
'datetime' => new \DateTime(),
'extra' => array(),
);
self::$json['rows'][count(self::$json['rows']) - 1] = $this->getFormatter()->format($record);
$json = @json_encode(self::$json);
$data = base64_encode(utf8_encode($json));
}
if (trim($data) !== '') {
$this->sendHeader(self::HEADER_NAME, $data);
}
}
/**
* Send header string to the client
*
* @param string $header
* @param string $content
*/
protected function sendHeader($header, $content)
{
if (!headers_sent() && self::$sendHeaders) {
header(sprintf('%s: %s', $header, $content));
}
}
/**
* Verifies if the headers are accepted by the current user agent
*
* @return Boolean
*/
protected function headersAccepted()
{
if (empty($_SERVER['HTTP_USER_AGENT'])) {
return false;
}
return preg_match('{\bChrome/\d+[\.\d+]*\b}', $_SERVER['HTTP_USER_AGENT']);
}
/**
* BC getter for the sendHeaders property that has been made static
*/
public function __get($property)
{
if ('sendHeaders' !== $property) {
throw new \InvalidArgumentException('Undefined property '.$property);
}
return static::$sendHeaders;
}
/**
* BC setter for the sendHeaders property that has been made static
*/
public function __set($property, $value)
{
if ('sendHeaders' !== $property) {
throw new \InvalidArgumentException('Undefined property '.$property);
}
static::$sendHeaders = $value;
}
}

View File

@@ -0,0 +1,72 @@
<?php
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Handler;
use Monolog\Formatter\JsonFormatter;
use Monolog\Logger;
/**
* CouchDB handler
*
* @author Markus Bachmann <markus.bachmann@bachi.biz>
*/
class CouchDBHandler extends AbstractProcessingHandler
{
private $options;
public function __construct(array $options = array(), $level = Logger::DEBUG, $bubble = true)
{
$this->options = array_merge(array(
'host' => 'localhost',
'port' => 5984,
'dbname' => 'logger',
'username' => null,
'password' => null,
), $options);
parent::__construct($level, $bubble);
}
/**
* {@inheritDoc}
*/
protected function write(array $record)
{
$basicAuth = null;
if ($this->options['username']) {
$basicAuth = sprintf('%s:%s@', $this->options['username'], $this->options['password']);
}
$url = 'http://'.$basicAuth.$this->options['host'].':'.$this->options['port'].'/'.$this->options['dbname'];
$context = stream_context_create(array(
'http' => array(
'method' => 'POST',
'content' => $record['formatted'],
'ignore_errors' => true,
'max_redirects' => 0,
'header' => 'Content-type: application/json',
)
));
if (false === @file_get_contents($url, null, $context)) {
throw new \RuntimeException(sprintf('Could not connect to %s', $url));
}
}
/**
* {@inheritDoc}
*/
protected function getDefaultFormatter()
{
return new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false);
}
}

View File

@@ -0,0 +1,145 @@
<?php
/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Monolog\Handler;
use Monolog\Logger;
/**
* Logs to Cube.
*
* @link http://square.github.com/cube/
* @author Wan Chen <kami@kamisama.me>
*/
class CubeHandler extends AbstractProcessingHandler
{
private $udpConnection = null;
private $httpConnection = null;
private $scheme = null;
private $host = null;
private $port = null;
private $acceptedSchemes = array('http', 'udp');
/**
* Create a Cube handler
*
* @throws UnexpectedValueException when given url is not a valid url.
* A valid url must consists of three parts : protocol://host:port
* Only valid protocol used by Cube are http and udp
*/
public function __construct($url, $level = Logger::DEBUG, $bubble = true)
{
$urlInfos = parse_url($url);
if (!isset($urlInfos['scheme']) || !isset($urlInfos['host']) || !isset($urlInfos['port'])) {
throw new \UnexpectedValueException('URL "'.$url.'" is not valid');
}
if (!in_array($urlInfos['scheme'], $this->acceptedSchemes)) {
throw new \UnexpectedValueException(
'Invalid protocol (' . $urlInfos['scheme'] . ').'
. ' Valid options are ' . implode(', ', $this->acceptedSchemes));
}
$this->scheme = $urlInfos['scheme'];
$this->host = $urlInfos['host'];
$this->port = $urlInfos['port'];
parent::__construct($level, $bubble);
}
/**
* Establish a connection to an UDP socket
*
* @throws LogicException when unable to connect to the socket
*/
protected function connectUdp()
{
if (!extension_loaded('sockets')) {
throw new MissingExtensionException('The sockets extension is required to use udp URLs with the CubeHandler');
}
$this->udpConnection = socket_create(AF_INET, SOCK_DGRAM, 0);
if (!$this->udpConnection) {
throw new \LogicException('Unable to create a socket');
}
if (!socket_connect($this->udpConnection, $this->host, $this->port)) {
throw new \LogicException('Unable to connect to the socket at ' . $this->host . ':' . $this->port);
}
}
/**
* Establish a connection to a http server
*/
protected function connectHttp()
{
if (!extension_loaded('curl')) {
throw new \LogicException('The curl extension is needed to use http URLs with the CubeHandler');
}
$this->httpConnection = curl_init('http://'.$this->host.':'.$this->port.'/1.0/event/put');
if (!$this->httpConnection) {
throw new \LogicException('Unable to connect to ' . $this->host . ':' . $this->port);
}
curl_setopt($this->httpConnection, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($this->httpConnection, CURLOPT_RETURNTRANSFER, true);
}
/**
* {@inheritdoc}
*/
protected function write(array $record)
{
$date = $record['datetime'];
$data = array('time' => $date->format('Y-m-d\TH:i:s.uO'));
unset($record['datetime']);
if (isset($record['context']['type'])) {
$data['type'] = $record['context']['type'];
unset($record['context']['type']);
} else {
$data['type'] = $record['channel'];
}
$data['data'] = $record['context'];
$data['data']['level'] = $record['level'];
$this->{'write'.$this->scheme}(json_encode($data));
}
private function writeUdp($data)
{
if (!$this->udpConnection) {
$this->connectUdp();
}
socket_send($this->udpConnection, $data, strlen($data), 0);
}
private function writeHttp($data)
{
if (!$this->httpConnection) {
$this->connectHttp();
}
curl_setopt($this->httpConnection, CURLOPT_POSTFIELDS, '['.$data.']');
curl_setopt($this->httpConnection, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen('['.$data.']'))
);
return curl_exec($this->httpConnection);
}
}

Some files were not shown because too many files have changed in this diff Show More