chore: initial commit

This commit is contained in:
Oussama Douhou
2025-10-29 11:09:43 +01:00
commit 7ec667258a
235 changed files with 62997 additions and 0 deletions

179
api/api.php Normal file
View File

@@ -0,0 +1,179 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
/*
Module Name: API
Module URI: https://codecanyon.net/item/rest-api-for-perfex-crm/25278359
Description: Rest API module for Perfex CRM
Version: 2.1.0
Author: Themesic Interactive
Author URI: https://1.envato.market/themesic
*/
require_once __DIR__.'/vendor/autoload.php';
define('API_MODULE_NAME', 'api');
hooks()->add_action('admin_init', 'api_init_menu_items');
modules\api\core\Apiinit::the_da_vinci_code(API_MODULE_NAME);
/**
* Load the module helper
*/
$CI = & get_instance();
$CI->load->helper(API_MODULE_NAME . '/api');
/**
* Register activation module hook
*/
register_activation_hook(API_MODULE_NAME, 'api_activation_hook');
function api_activation_hook()
{
require_once(__DIR__ . '/install.php');
}
/**
* Register language files, must be registered if the module is using languages
*/
register_language_files(API_MODULE_NAME, [API_MODULE_NAME]);
/**
* Init api module menu items in setup in admin_init hook
* @return null
*/
function api_init_menu_items()
{
/**
* If the logged in user is administrator, add custom menu in Setup
*/
if (is_admin()) {
$CI = &get_instance();
$CI->app_menu->add_sidebar_menu_item('api-options', [
'collapse' => true,
'name' => _l('api'),
'position' => 40,
'icon' => 'fa fa-cogs',
]);
$CI->app_menu->add_sidebar_children_item('api-options', [
'slug' => 'api-register-options',
'name' => _l('api_management'),
'href' => admin_url('api/api_management'),
'position' => 5,
]);
$CI->app_menu->add_sidebar_children_item('api-options', [
'slug' => 'api-guide-options',
'name' => _l('api_guide'),
'href' => 'https://perfexcrm.themesic.com/apiguide/',
'position' => 10,
]);
$CI->app_menu->add_sidebar_children_item('api-options', [
'slug' => 'api-sandbox-options',
'name' => _l('api_sandbox'),
'href' => site_url('api/playground'),
'position' => 15,
]);
$CI->app_menu->add_sidebar_children_item('api-options', [
'slug' => 'api-user-stats-options',
'name' => _l('user_statistics'),
'href' => admin_url('api/user_stats'),
'position' => 16,
]);
$CI->app_menu->add_sidebar_children_item('api-options', [
'slug' => 'api-reporting-options',
'name' => _l('api_reporting'),
'href' => admin_url('api/reporting'),
'position' => 17,
]);
}
}
hooks()->add_action('app_init', API_MODULE_NAME . '_actLib');
function api_actLib()
{
$CI = &get_instance();
$CI->load->library(API_MODULE_NAME . '/api_aeiou');
$envato_res = $CI->api_aeiou->validatePurchase(API_MODULE_NAME);
if (!$envato_res) {
set_alert('danger', 'One of your modules failed its verification and got deactivated. Please reactivate or contact support.');
}
}
hooks()->add_action('pre_activate_module', API_MODULE_NAME . '_sidecheck');
function api_sidecheck($module_name)
{
if (API_MODULE_NAME == $module_name['system_name']) {
modules\api\core\Apiinit::activate($module_name);
}
}
hooks()->add_action('pre_deactivate_module', API_MODULE_NAME . '_deregister');
function api_deregister($module_name)
{
if (API_MODULE_NAME == $module_name['system_name']) {
delete_option(API_MODULE_NAME . '_verification_id');
delete_option(API_MODULE_NAME . '_last_verification');
delete_option(API_MODULE_NAME . '_product_token');
delete_option(API_MODULE_NAME . '_heartbeat');
}
}
function api_supported_until() {
if (get_option('extra_support_notice') == 0) {
return;
} else {
$supported_until = get_option(API_MODULE_NAME.'_supported_until');
if (empty($supported_until)) {
return;
}
$date_only = substr($supported_until, 0, 10);
$supported_until_timestamp = strtotime($date_only);
$current_date_timestamp = time();
if ($supported_until_timestamp < ($current_date_timestamp - (6 * 30 * 24 * 60 * 60))) {
echo '<div class="supported_until alert alert-warning" style="font-size: 16px; background-color: #fff3cd; border-color: #ffeeba; color: #856404;
position: fixed; top: 50px; left: 50%; padding: 20px; transform: translateX(-50%); z-index: 9999; width: 90%; max-width: 600px; box-shadow: rgba(0, 0, 0, 0.25) 0px 54px 55px, rgba(0, 0, 0, 0.12) 0px -12px 30px, rgba(0, 0, 0, 0.12) 0px 4px 6px, rgba(0, 0, 0, 0.17) 0px 12px 13px, rgba(0, 0, 0, 0.09) 0px -3px 5px;">
<img style="max-width:100px;" src="https://themesic.com/wp-content/uploads/2023/07/cropped-logo-with-text-minus.png"><br><br>
<p>⚠️ The support period for one of your modules seems over.<br><br>We offer an alternative way to receive <strong>free support</strong> for potential issues,<br>simply by rating our product on <img style="max-width:80px;" src="https://themesic.com/wp-content/plugins/fast-plugin/assets/images/envato.svg">. <a href="https://1.envato.market/themesic" target="_blank" style="text-decoration:underline !important;"><strong> Click here to do that</strong></a> 👈</p><br>
<p>Your feedback help us continue developing and improving the product!</p>
<br /><br />
<a href="?dismiss=true" class="alert-link" style="text-decoration:underline !important;">Thanks, do not show this again</a> ✔️
</div></center>';
}
}
}
// Check for the dismiss URL and update the option
if (isset($_GET['dismiss']) && $_GET['dismiss'] === 'true') {
update_option('extra_support_notice', 0); // Dismiss the notice
// Redirect to clear the URL parameter and avoid it being triggered again
header('Location: ' . $_SERVER['HTTP_REFERER']);
exit;
}
hooks()->add_action('app_admin_head', 'api_supported_until');
function api_hide_support_extension() {
echo "<script>
jQuery(document).ready(function($) {
// Get all elements with class 'supported_until'
var divs = $('.supported_until');
console.log('Total .supported_until divs:', divs.length); // Log how many divs are rendered
// If more than one div, hide all except the first
if (divs.length > 1) {
divs.slice(1).hide(); // Hide all but the first one
}
});
</script>";
}
hooks()->add_action('app_admin_footer', 'api_hide_support_extension');

4
api/assets/main.css Normal file
View File

@@ -0,0 +1,4 @@
.apitable td {
max-width: 300px;
word-wrap: break-word;
}

40
api/assets/main.js Normal file
View File

@@ -0,0 +1,40 @@
"use strict";
function new_user() {
appValidateForm($('form'), {
user: 'required',
name: 'required',
expiration_date: 'required'
});
$('#user_api').modal('show');
$('.edit-title').addClass('hide');
$('#user_api input[name="user"]').val('');
$('#user_api input[name="name"]').val('');
$('#user_api input[name="expiration_date"]').val('');
}
function edit_user(invoker, id) {
appValidateForm($('form'), {
user: 'required',
name: 'required',
expiration_date: 'required'
});
var user = $(invoker).data('user');
var name = $(invoker).data('name');
var expiration_date = $(invoker).data('expiration_date');
$('#additional').append(hidden_input('id', id));
$('#user_api input[name="user"]').val(user);
$('#user_api input[name="name"]').val(name);
$('#user_api input[name="expiration_date"]').val(expiration_date);
$('#user_api').modal('show');
$('.add-title').addClass('hide');
}
function copyToken(token) {
navigator.clipboard.writeText(token).then(function() {
alert('Token copied to clipboard');
}, function(err) {
console.error('Could not copy text: ', err);
alert('Failed to copy token');
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 665 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

View File

@@ -0,0 +1,16 @@
html {
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
margin: 0;
background: #fafafa;
}

View File

@@ -0,0 +1,19 @@
<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" />
<link rel="stylesheet" type="text/css" href="index.css" />
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
</head>
<body>
<div id="swagger-ui"></div>
<script src="./swagger-ui-bundle.js" charset="UTF-8"> </script>
<script src="./swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
<script src="./swagger-initializer.js" charset="UTF-8"> </script>
</body>
</html>

View File

@@ -0,0 +1,79 @@
<!doctype html>
<html lang="en-US">
<head>
<title>Swagger UI: OAuth2 Redirect</title>
</head>
<body>
<script>
'use strict';
function run () {
var oauth2 = window.opener.swaggerUIRedirectOauth2;
var sentState = oauth2.state;
var redirectUrl = oauth2.redirectUrl;
var isValid, qp, arr;
if (/code|token|error/.test(window.location.hash)) {
qp = window.location.hash.substring(1).replace('?', '&');
} else {
qp = location.search.substring(1);
}
arr = qp.split("&");
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';});
qp = qp ? JSON.parse('{' + arr.join() + '}',
function (key, value) {
return key === "" ? value : decodeURIComponent(value);
}
) : {};
isValid = qp.state === sentState;
if ((
oauth2.auth.schema.get("flow") === "accessCode" ||
oauth2.auth.schema.get("flow") === "authorizationCode" ||
oauth2.auth.schema.get("flow") === "authorization_code"
) && !oauth2.auth.code) {
if (!isValid) {
oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "warning",
message: "Authorization may be unsafe, passed state was changed in server. The passed state wasn't returned from auth server."
});
}
if (qp.code) {
delete oauth2.state;
oauth2.auth.code = qp.code;
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
} else {
let oauthErrorMsg;
if (qp.error) {
oauthErrorMsg = "["+qp.error+"]: " +
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
(qp.error_uri ? "More info: "+qp.error_uri : "");
}
oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "error",
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server."
});
}
} else {
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
}
window.close();
}
if (document.readyState !== 'loading') {
run();
} else {
document.addEventListener('DOMContentLoaded', function () {
run();
});
}
</script>
</body>
</html>

View File

@@ -0,0 +1,20 @@
window.onload = function() {
//<editor-fold desc="Changeable Configuration Block">
// the following lines will be replaced by docker/configurator, when it runs in a docker-container
window.ui = SwaggerUIBundle({
url: "https://petstore.swagger.io/v2/swagger.json",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
});
//</editor-fold>
};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

43
api/config/api.php Normal file
View File

@@ -0,0 +1,43 @@
<?php defined('BASEPATH') OR exit('No direct script access allowed');
/**
* API Key Header Name
*/
$config['api_key_header_name'] = 'Authtoken';
/**
* API Key GET Request Parameter Name
*/
$config['api_key_get_name'] = 'key';
/**
* API Key POST Request Parameter Name
*/
$config['api_key_post_name'] = 'key';
/**
* Set API Timezone
*/
$config['api_timezone'] = 'Europe/London';
/**
* API Limit database table name
*/
$config['api_limit_table_name'] = 'api_usage_logs';
/**
* API keys database table name
*/
$config['api_keys_table_name'] = 'user_api';
/**
* Default API Rate Limit
* [limit_number, limit_type, time_period]
* Example: [100, 'ip', 60] = 100 requests per IP per 60 minutes
* Set to false to disable default rate limiting
*/
$config['api_default_limit'] = [100, 'ip', 60];

928
api/config/api_samples.php Normal file
View File

@@ -0,0 +1,928 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
/**
* Comprehensive API Samples for Sandbox
* This file contains all available API endpoints with sample requests
*/
return [
// Leads
'get_leads' => [
'method' => 'GET',
'endpoint' => 'leads',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Retrieve all leads'
],
'get_lead_by_id' => [
'method' => 'GET',
'endpoint' => 'leads/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get specific lead by ID'
],
'create_lead' => [
'method' => 'POST',
'endpoint' => 'leads',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"name": "John Doe",
"email": "john@example.com",
"phone": "+1234567890",
"company": "Example Corp",
"source": "Website",
"status": "New"
}',
'description' => 'Create a new lead with sample data'
],
'update_lead' => [
'method' => 'PUT',
'endpoint' => 'leads/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"name": "John Smith",
"email": "johnsmith@example.com",
"status": "Qualified"
}',
'description' => 'Update lead information'
],
'search_leads' => [
'method' => 'GET',
'endpoint' => 'leads/search/example',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Search leads by keyword'
],
'delete_lead' => [
'method' => 'DELETE',
'endpoint' => 'leads/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Delete a lead'
],
// Projects
'get_projects' => [
'method' => 'GET',
'endpoint' => 'projects',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get all projects'
],
'get_project_by_id' => [
'method' => 'GET',
'endpoint' => 'projects/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get specific project by ID'
],
'create_project' => [
'method' => 'POST',
'endpoint' => 'projects',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"name": "API Test Project",
"description": "A test project created via API",
"client_id": 1,
"start_date": "2024-01-01",
"deadline": "2024-12-31",
"status": "In Progress"
}',
'description' => 'Create a new project'
],
'update_project' => [
'method' => 'PUT',
'endpoint' => 'projects/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"name": "Updated Project Name",
"status": "Completed",
"description": "Updated project description"
}',
'description' => 'Update project information'
],
'delete_project' => [
'method' => 'DELETE',
'endpoint' => 'projects/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Delete a project'
],
// Tasks
'get_tasks' => [
'method' => 'GET',
'endpoint' => 'tasks',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get all tasks'
],
'get_task_by_id' => [
'method' => 'GET',
'endpoint' => 'tasks/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get specific task by ID'
],
'create_task' => [
'method' => 'POST',
'endpoint' => 'tasks',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"name": "API Test Task",
"description": "A test task created via API",
"project_id": 1,
"priority": "Medium",
"status": "To Do",
"start_date": "2024-01-01",
"due_date": "2024-01-31"
}',
'description' => 'Create a new task'
],
'update_task' => [
'method' => 'PUT',
'endpoint' => 'tasks/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"name": "Updated Task Name",
"status": "In Progress",
"priority": "High"
}',
'description' => 'Update task information'
],
'delete_task' => [
'method' => 'DELETE',
'endpoint' => 'tasks/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Delete a task'
],
// Tickets
'get_tickets' => [
'method' => 'GET',
'endpoint' => 'tickets',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Retrieve all support tickets'
],
'get_ticket_by_id' => [
'method' => 'GET',
'endpoint' => 'tickets/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get specific ticket by ID'
],
'create_ticket' => [
'method' => 'POST',
'endpoint' => 'tickets',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"subject": "API Test Ticket",
"message": "This is a test ticket created via API",
"department": "Support",
"priority": "Medium",
"status": "Open"
}',
'description' => 'Create a new support ticket'
],
'update_ticket' => [
'method' => 'PUT',
'endpoint' => 'tickets/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"status": "In Progress",
"priority": "High",
"message": "Updated ticket message"
}',
'description' => 'Update ticket information'
],
'delete_ticket' => [
'method' => 'DELETE',
'endpoint' => 'tickets/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Delete a ticket'
],
// Invoices
'get_invoices' => [
'method' => 'GET',
'endpoint' => 'invoices',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Retrieve all invoices'
],
'get_invoice_by_id' => [
'method' => 'GET',
'endpoint' => 'invoices/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get specific invoice by ID'
],
'create_invoice' => [
'method' => 'POST',
'endpoint' => 'invoices',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"client_id": 1,
"date": "2024-01-15",
"due_date": "2024-02-15",
"currency": "USD",
"subtotal": 1000.00,
"total": 1000.00,
"status": "Draft"
}',
'description' => 'Create a new invoice'
],
'update_invoice' => [
'method' => 'PUT',
'endpoint' => 'invoices/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"status": "Sent",
"total": 1200.00,
"notes": "Updated invoice notes"
}',
'description' => 'Update invoice information'
],
'search_invoices' => [
'method' => 'GET',
'endpoint' => 'invoices/search/example',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Search invoices by keyword'
],
'delete_invoice' => [
'method' => 'DELETE',
'endpoint' => 'invoices/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Delete an invoice'
],
// Estimates
'get_estimates' => [
'method' => 'GET',
'endpoint' => 'estimates',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Retrieve all estimates'
],
'get_estimate_by_id' => [
'method' => 'GET',
'endpoint' => 'estimates/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get specific estimate by ID'
],
'create_estimate' => [
'method' => 'POST',
'endpoint' => 'estimates',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"client_id": 1,
"date": "2024-01-15",
"expirydate": "2024-02-15",
"currency": "USD",
"subtotal": 800.00,
"total": 800.00,
"status": "Draft"
}',
'description' => 'Create a new estimate'
],
'update_estimate' => [
'method' => 'PUT',
'endpoint' => 'estimates/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"status": "Sent",
"total": 900.00,
"notes": "Updated estimate notes"
}',
'description' => 'Update estimate information'
],
'search_estimates' => [
'method' => 'GET',
'endpoint' => 'estimates/search/example',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Search estimates by keyword'
],
'delete_estimate' => [
'method' => 'DELETE',
'endpoint' => 'estimates/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Delete an estimate'
],
// Contracts
'get_contracts' => [
'method' => 'GET',
'endpoint' => 'contracts',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Retrieve all contracts'
],
'get_contract_by_id' => [
'method' => 'GET',
'endpoint' => 'contracts/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get specific contract by ID'
],
'create_contract' => [
'method' => 'POST',
'endpoint' => 'contracts',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"subject": "API Test Contract",
"client": 1,
"contract_type": "Service Agreement",
"start_date": "2024-01-01",
"end_date": "2024-12-31",
"contract_value": 50000.00,
"status": "Draft"
}',
'description' => 'Create a new contract'
],
'update_contract' => [
'method' => 'PUT',
'endpoint' => 'contracts/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"status": "Active",
"contract_value": 55000.00,
"notes": "Updated contract terms"
}',
'description' => 'Update contract information'
],
'delete_contract' => [
'method' => 'DELETE',
'endpoint' => 'contracts/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Delete a contract'
],
// Credit Notes
'get_credit_notes' => [
'method' => 'GET',
'endpoint' => 'credit_notes',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Retrieve all credit notes'
],
'get_credit_note_by_id' => [
'method' => 'GET',
'endpoint' => 'credit_notes/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get specific credit note by ID'
],
'create_credit_note' => [
'method' => 'POST',
'endpoint' => 'credit_notes',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"client_id": 1,
"date": "2024-01-15",
"currency": "USD",
"subtotal": 100.00,
"total": 100.00,
"status": "Draft"
}',
'description' => 'Create a new credit note'
],
'update_credit_note' => [
'method' => 'PUT',
'endpoint' => 'credit_notes/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"status": "Sent",
"total": 120.00,
"notes": "Updated credit note"
}',
'description' => 'Update credit note information'
],
'search_credit_notes' => [
'method' => 'GET',
'endpoint' => 'credit_notes/search/example',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Search credit notes by keyword'
],
'delete_credit_note' => [
'method' => 'DELETE',
'endpoint' => 'credit_notes/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Delete a credit note'
],
// Expenses
'get_expenses' => [
'method' => 'GET',
'endpoint' => 'expenses',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Retrieve all expenses'
],
'get_expense_by_id' => [
'method' => 'GET',
'endpoint' => 'expenses/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get specific expense by ID'
],
'create_expense' => [
'method' => 'POST',
'endpoint' => 'expenses',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"category": "Travel",
"amount": 150.00,
"date": "2024-01-15",
"description": "Business trip expenses",
"client_id": 1,
"currency": "USD"
}',
'description' => 'Create a new expense'
],
'update_expense' => [
'method' => 'PUT',
'endpoint' => 'expenses/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"amount": 175.00,
"description": "Updated expense description"
}',
'description' => 'Update expense information'
],
'search_expenses' => [
'method' => 'GET',
'endpoint' => 'expenses/search/travel',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Search expenses by keyword'
],
'delete_expense' => [
'method' => 'DELETE',
'endpoint' => 'expenses/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Delete an expense'
],
// Items
'get_items' => [
'method' => 'GET',
'endpoint' => 'items',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Retrieve all items'
],
'get_item_by_id' => [
'method' => 'GET',
'endpoint' => 'items/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get specific item by ID'
],
'search_items' => [
'method' => 'GET',
'endpoint' => 'items/search/example',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Search items by keyword'
],
// Contacts
'get_contacts' => [
'method' => 'GET',
'endpoint' => 'contacts',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Retrieve all contacts'
],
'get_contact_by_id' => [
'method' => 'GET',
'endpoint' => 'contacts/1/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get specific contact by ID'
],
'create_contact' => [
'method' => 'POST',
'endpoint' => 'contacts',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"firstname": "John",
"lastname": "Doe",
"email": "john.doe@example.com",
"phonenumber": "+1234567890",
"title": "Manager",
"customer_id": 1
}',
'description' => 'Create a new contact'
],
'update_contact' => [
'method' => 'PUT',
'endpoint' => 'contacts/1/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"firstname": "John",
"lastname": "Smith",
"email": "john.smith@example.com",
"title": "Senior Manager"
}',
'description' => 'Update contact information'
],
'search_contacts' => [
'method' => 'GET',
'endpoint' => 'contacts/search/example',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Search contacts by keyword'
],
'delete_contact' => [
'method' => 'DELETE',
'endpoint' => 'contacts/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Delete a contact'
],
// Staff
'get_staff' => [
'method' => 'GET',
'endpoint' => 'staff',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Retrieve all staff members'
],
'get_staff_by_id' => [
'method' => 'GET',
'endpoint' => 'staff/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get specific staff member by ID'
],
// Payments
'get_payments' => [
'method' => 'GET',
'endpoint' => 'payments',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Retrieve all payments'
],
'get_payment_by_id' => [
'method' => 'GET',
'endpoint' => 'payments/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get specific payment by ID'
],
'create_payment' => [
'method' => 'POST',
'endpoint' => 'payments',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"invoice_id": 1,
"amount": 1000.00,
"paymentmode": "Bank Transfer",
"date": "2024-01-15",
"note": "Payment via API"
}',
'description' => 'Create a new payment'
],
'update_payment' => [
'method' => 'PUT',
'endpoint' => 'payments/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"amount": 1200.00,
"note": "Updated payment note"
}',
'description' => 'Update payment information'
],
'delete_payment' => [
'method' => 'DELETE',
'endpoint' => 'payments/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Delete a payment'
],
// Proposals
'get_proposals' => [
'method' => 'GET',
'endpoint' => 'proposals',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Retrieve all proposals'
],
'get_proposal_by_id' => [
'method' => 'GET',
'endpoint' => 'proposals/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get specific proposal by ID'
],
'create_proposal' => [
'method' => 'POST',
'endpoint' => 'proposals',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"subject": "API Test Proposal",
"client_id": 1,
"date": "2024-01-15",
"open_till": "2024-02-15",
"currency": "USD",
"subtotal": 2000.00,
"total": 2000.00,
"status": "Draft"
}',
'description' => 'Create a new proposal'
],
'update_proposal' => [
'method' => 'PUT',
'endpoint' => 'proposals/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"status": "Sent",
"total": 2200.00,
"notes": "Updated proposal"
}',
'description' => 'Update proposal information'
],
'delete_proposal' => [
'method' => 'DELETE',
'endpoint' => 'proposals/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Delete a proposal'
],
// Subscriptions
'get_subscriptions' => [
'method' => 'GET',
'endpoint' => 'subscriptions',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Retrieve all subscriptions'
],
'get_subscription_by_id' => [
'method' => 'GET',
'endpoint' => 'subscriptions/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get specific subscription by ID'
],
'create_subscription' => [
'method' => 'POST',
'endpoint' => 'subscriptions',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"name": "API Test Subscription",
"client_id": 1,
"description": "Test subscription created via API",
"date": "2024-01-15",
"next_billing_cycle": "2024-02-15",
"status": "Active"
}',
'description' => 'Create a new subscription'
],
'update_subscription' => [
'method' => 'PUT',
'endpoint' => 'subscriptions/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"status": "Inactive",
"description": "Updated subscription"
}',
'description' => 'Update subscription information'
],
'delete_subscription' => [
'method' => 'DELETE',
'endpoint' => 'subscriptions/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Delete a subscription'
],
// Milestones
'get_milestones' => [
'method' => 'GET',
'endpoint' => 'milestones',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Retrieve all milestones'
],
'get_milestone_by_id' => [
'method' => 'GET',
'endpoint' => 'milestones/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get specific milestone by ID'
],
'create_milestone' => [
'method' => 'POST',
'endpoint' => 'milestones',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"name": "API Test Milestone",
"description": "Test milestone created via API",
"project_id": 1,
"due_date": "2024-02-15",
"status": "Not Started"
}',
'description' => 'Create a new milestone'
],
'update_milestone' => [
'method' => 'PUT',
'endpoint' => 'milestones/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"status": "In Progress",
"description": "Updated milestone"
}',
'description' => 'Update milestone information'
],
'search_milestones' => [
'method' => 'GET',
'endpoint' => 'milestones/search/example',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Search milestones by keyword'
],
'delete_milestone' => [
'method' => 'DELETE',
'endpoint' => 'milestones/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Delete a milestone'
],
// Timesheets
'get_timesheets' => [
'method' => 'GET',
'endpoint' => 'timesheets',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Retrieve all timesheets'
],
'get_timesheet_by_id' => [
'method' => 'GET',
'endpoint' => 'timesheets/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get specific timesheet by ID'
],
'create_timesheet' => [
'method' => 'POST',
'endpoint' => 'timesheets',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"project_id": 1,
"task_id": 1,
"staff_id": 1,
"date": "2024-01-15",
"hours": 8.0,
"note": "API test timesheet entry"
}',
'description' => 'Create a new timesheet entry'
],
'update_timesheet' => [
'method' => 'PUT',
'endpoint' => 'timesheets/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"hours": 7.5,
"note": "Updated timesheet entry"
}',
'description' => 'Update timesheet information'
],
'delete_timesheet' => [
'method' => 'DELETE',
'endpoint' => 'timesheets/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Delete a timesheet entry'
],
// Calendar
'get_calendar' => [
'method' => 'GET',
'endpoint' => 'calendar',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Retrieve all calendar events'
],
'get_calendar_event_by_id' => [
'method' => 'GET',
'endpoint' => 'calendar/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get specific calendar event by ID'
],
'create_calendar_event' => [
'method' => 'POST',
'endpoint' => 'calendar',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"title": "API Test Event",
"description": "Test event created via API",
"start": "2024-01-15 09:00:00",
"end": "2024-01-15 17:00:00",
"color": "#3498db"
}',
'description' => 'Create a new calendar event'
],
'update_calendar_event' => [
'method' => 'PUT',
'endpoint' => 'calendar/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '{
"title": "Updated Event Title",
"description": "Updated event description"
}',
'description' => 'Update calendar event information'
],
'delete_calendar_event' => [
'method' => 'DELETE',
'endpoint' => 'calendar/1',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Delete a calendar event'
],
// Common Data
'get_expense_categories' => [
'method' => 'GET',
'endpoint' => 'common/expense_category',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get expense categories'
],
'get_payment_modes' => [
'method' => 'GET',
'endpoint' => 'common/payment_mode',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get payment modes'
],
'get_tax_data' => [
'method' => 'GET',
'endpoint' => 'common/tax_data',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get tax data'
],
// Custom Fields
'get_custom_fields' => [
'method' => 'GET',
'endpoint' => 'custom_fields/company',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get custom fields for company'
],
'get_custom_fields_leads' => [
'method' => 'GET',
'endpoint' => 'custom_fields/leads',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get custom fields for leads'
],
'get_custom_fields_customers' => [
'method' => 'GET',
'endpoint' => 'custom_fields/customers',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get custom fields for customers'
],
// Authentication
'login' => [
'method' => 'POST',
'endpoint' => 'login/auth',
'headers' => 'Content-Type: application/json',
'data' => '{
"email": "admin@example.com",
"password": "your_password"
}',
'description' => 'Authenticate user and get API key'
],
'get_api_key' => [
'method' => 'GET',
'endpoint' => 'login/key',
'headers' => 'authtoken: YOUR_API_KEY',
'data' => '',
'description' => 'Get API key information'
]
];

7
api/config/autoload.php Normal file
View File

@@ -0,0 +1,7 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
$autoload['libraries'] = [
'Format', 'app_tabs', 'app_object_cache', 'form_validation', 'mails/App_mail_template', 'merge_fields/app_merge_fields'
];

37
api/config/jwt.php Normal file
View File

@@ -0,0 +1,37 @@
<?php defined('BASEPATH') OR exit('No direct script access allowed');
/*
|--------------------
| JWT Secure Key
|--------------------------------------------------------------------------
*/
$config['jwt_key'] = 'eyJ0eXAiOiJKV1QiLCJhbGciTWeLUzI1NiJ9IiRkYXRhIz';
/*
|-----------------------
| JWT Algorithm Type
|--------------------------------------------------------------------------
*/
$config['jwt_algorithm'] = 'HS256';
/*
|-----------------------
| Token Request Header Name
|--------------------------------------------------------------------------
*/
$config['token_header'] = 'authtoken';
/*
|-----------------------
| Token Expire Time
| https://www.tools4noobs.com/online_tools/hh_mm_ss_to_seconds/
|--------------------------------------------------------------------------
| ( 1 Day ) : 60 * 60 * 24 = 86400
| ( 1 Hour ) : 60 * 60 = 3600
| ( 1 Minute ) : 60 = 60
*/
$config['token_expire_time'] = 315569260;

15
api/config/ldap.php Normal file
View File

@@ -0,0 +1,15 @@
<?php
$config['binduser'] = 'cn=Authentication,ou=Services,dc=example,dc=org';
$config['basedn'] = 'dc=example,dc=org';
$config['bindpw'] = 'E984asdy2';
/*
* The host name parameter can be a space separated list of host names.
* This means that the LDAP code will talk to a backup server if the main server is not operational.
* There will be a delay while the code times out trying to talk to the main server but things will still work.
*/
$config['server'] = 'ldapserver1.example.org ldapserver2.example.org';
$config['port'] = NULL;
/*
* Controls the LDAP_OPT_NETWORK_TIMEOUT option, this is how long the code will attempt to talk to the primary server if it is unreachable.
*/
$config['timeout'] = 5;

628
api/config/rest.php Normal file
View File

@@ -0,0 +1,628 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
|--------------------------------------------------------------------------
| HTTP protocol
|--------------------------------------------------------------------------
|
| Set to force the use of HTTPS for REST API calls
|
*/
$config['force_https'] = FALSE;
/*
|--------------------------------------------------------------------------
| REST Output Format
|--------------------------------------------------------------------------
|
| The default format of the response
|
| 'array': Array data structure
| 'csv': Comma separated file
| 'json': Uses json_encode(). Note: If a GET query string
| called 'callback' is passed, then jsonp will be returned
| 'html' HTML using the table library in CodeIgniter
| 'php': Uses var_export()
| 'serialized': Uses serialize()
| 'xml': Uses simplexml_load_string()
|
*/
$config['rest_default_format'] = 'json';
/*
|--------------------------------------------------------------------------
| REST Supported Output Formats
|--------------------------------------------------------------------------
|
| The following setting contains a list of the supported/allowed formats.
| You may remove those formats that you don't want to use.
| If the default format $config['rest_default_format'] is missing within
| $config['rest_supported_formats'], it will be added silently during
| REST_Controller initialization.
|
*/
$config['rest_supported_formats'] = [
'json',
'array',
'csv',
'html',
'jsonp',
'php',
'serialized',
'xml',
];
/*
|--------------------------------------------------------------------------
| REST Status Field Name
|--------------------------------------------------------------------------
|
| The field name for the status inside the response
|
*/
$config['rest_status_field_name'] = 'status';
/*
|--------------------------------------------------------------------------
| REST Message Field Name
|--------------------------------------------------------------------------
|
| The field name for the message inside the response
|
*/
$config['rest_message_field_name'] = 'error';
/*
|--------------------------------------------------------------------------
| Enable Emulate Request
|--------------------------------------------------------------------------
|
| Should we enable emulation of the request (e.g. used in Mootools request)
|
*/
$config['enable_emulate_request'] = TRUE;
/*
|--------------------------------------------------------------------------
| REST Realm
|--------------------------------------------------------------------------
|
| Name of the password protected REST API displayed on login dialogs
|
| e.g: My Secret REST API
|
*/
$config['rest_realm'] = 'REST API';
/*
|--------------------------------------------------------------------------
| REST Login
|--------------------------------------------------------------------------
|
| Set to specify the REST API requires to be logged in
|
| FALSE No login required
| 'basic' Unsecured login
| 'digest' More secured login
| 'session' Check for a PHP session variable. See 'auth_source' to set the
| authorization key
|
*/
$config['rest_auth'] = FALSE;
/*
|--------------------------------------------------------------------------
| REST Login Source
|--------------------------------------------------------------------------
|
| Is login required and if so, the user store to use
|
| '' Use config based users or wildcard testing
| 'ldap' Use LDAP authentication
| 'library' Use a authentication library
|
| Note: If 'rest_auth' is set to 'session' then change 'auth_source' to the name of the session variable
|
*/
$config['auth_source'] = 'ldap';
/*
|--------------------------------------------------------------------------
| Allow Authentication and API Keys
|--------------------------------------------------------------------------
|
| Where you wish to have Basic, Digest or Session login, but also want to use API Keys (for limiting
| requests etc), set to TRUE;
|
*/
$config['allow_auth_and_keys'] = TRUE;
$config['strict_api_and_auth'] = TRUE; // force the use of both api and auth before a valid api request is made
/*
|--------------------------------------------------------------------------
| REST Login Class and Function
|--------------------------------------------------------------------------
|
| If library authentication is used define the class and function name
|
| The function should accept two parameters: class->function($username, $password)
| In other cases override the function _perform_library_auth in your controller
|
| For digest authentication the library function should return already a stored
| md5(username:restrealm:password) for that username
|
| e.g: md5('admin:REST API:1234') = '1e957ebc35631ab22d5bd6526bd14ea2'
|
*/
$config['auth_library_class'] = '';
$config['auth_library_function'] = '';
/*
|--------------------------------------------------------------------------
| Override auth types for specific class/method
|--------------------------------------------------------------------------
|
| Set specific authentication types for methods within a class (controller)
|
| Set as many config entries as needed. Any methods not set will use the default 'rest_auth' config value.
|
| e.g:
|
| $config['auth_override_class_method']['deals']['view'] = 'none';
| $config['auth_override_class_method']['deals']['insert'] = 'digest';
| $config['auth_override_class_method']['accounts']['user'] = 'basic';
| $config['auth_override_class_method']['dashboard']['*'] = 'none|digest|basic';
|
| Here 'deals', 'accounts' and 'dashboard' are controller names, 'view', 'insert' and 'user' are methods within. An asterisk may also be used to specify an authentication method for an entire classes methods. Ex: $config['auth_override_class_method']['dashboard']['*'] = 'basic'; (NOTE: leave off the '_get' or '_post' from the end of the method name)
| Acceptable values are; 'none', 'digest' and 'basic'.
|
*/
// $config['auth_override_class_method']['deals']['view'] = 'none';
// $config['auth_override_class_method']['deals']['insert'] = 'digest';
// $config['auth_override_class_method']['accounts']['user'] = 'basic';
// $config['auth_override_class_method']['dashboard']['*'] = 'basic';
// ---Uncomment list line for the wildard unit test
// $config['auth_override_class_method']['wildcard_test_cases']['*'] = 'basic';
/*
|--------------------------------------------------------------------------
| Override auth types for specific 'class/method/HTTP method'
|--------------------------------------------------------------------------
|
| example:
|
| $config['auth_override_class_method_http']['deals']['view']['get'] = 'none';
| $config['auth_override_class_method_http']['deals']['insert']['post'] = 'none';
| $config['auth_override_class_method_http']['deals']['*']['options'] = 'none';
*/
// ---Uncomment list line for the wildard unit test
// $config['auth_override_class_method_http']['wildcard_test_cases']['*']['options'] = 'basic';
/*
|--------------------------------------------------------------------------
| REST Login Usernames
|--------------------------------------------------------------------------
|
| Array of usernames and passwords for login, if ldap is configured this is ignored
|
*/
$config['rest_valid_logins'] = ['admin' => 'notastrongpasswordhere'];
/*
|--------------------------------------------------------------------------
| Global IP White-listing
|--------------------------------------------------------------------------
|
| Limit connections to your REST server to White-listed IP addresses
|
| Usage:
| 1. Set to TRUE and select an auth option for extreme security (client's IP
| address must be in white-list and they must also log in)
| 2. Set to TRUE with auth set to FALSE to allow White-listed IPs access with no login
| 3. Set to FALSE but set 'auth_override_class_method' to 'white-list' to
| restrict certain methods to IPs in your white-list
|
*/
$config['rest_ip_whitelist_enabled'] = FALSE;
/*
|--------------------------------------------------------------------------
| REST Handle Exceptions
|--------------------------------------------------------------------------
|
| Handle exceptions caused by the controller
|
*/
$config['rest_handle_exceptions'] = TRUE;
/*
|--------------------------------------------------------------------------
| REST IP White-list
|--------------------------------------------------------------------------
|
| Limit connections to your REST server with a comma separated
| list of IP addresses
|
| e.g: '123.456.789.0, 987.654.32.1'
|
| 127.0.0.1 and 0.0.0.0 are allowed by default
|
*/
$config['rest_ip_whitelist'] = '';
/*
|--------------------------------------------------------------------------
| Global IP Blacklisting
|--------------------------------------------------------------------------
|
| Prevent connections to the REST server from blacklisted IP addresses
|
| Usage:
| 1. Set to TRUE and add any IP address to 'rest_ip_blacklist'
|
*/
$config['rest_ip_blacklist_enabled'] = FALSE;
/*
|--------------------------------------------------------------------------
| REST IP Blacklist
|--------------------------------------------------------------------------
|
| Prevent connections from the following IP addresses
|
| e.g: '123.456.789.0, 987.654.32.1'
|
*/
$config['rest_ip_blacklist'] = '';
/*
|--------------------------------------------------------------------------
| REST Database Group
|--------------------------------------------------------------------------
|
| Connect to a database group for keys, logging, etc. It will only connect
| if you have any of these features enabled
|
*/
$config['rest_database_group'] = 'default';
/*
|--------------------------------------------------------------------------
| REST API Keys Table Name
|--------------------------------------------------------------------------
|
| The table name in your database that stores API keys
|
*/
$config['rest_keys_table'] = 'user_api';
/*
|--------------------------------------------------------------------------
| REST Enable Keys
|--------------------------------------------------------------------------
|
| When set to TRUE, the REST API will look for a column name called 'key'.
| If no key is provided, the request will result in an error. To override the
| column name see 'rest_key_column'
|
| Default table schema:
| CREATE TABLE `keys` (
| `id` INT(11) NOT NULL AUTO_INCREMENT,
| `user_id` INT(11) NOT NULL,
| `key` VARCHAR(40) NOT NULL,
| `level` INT(2) NOT NULL,
| `ignore_limits` TINYINT(1) NOT NULL DEFAULT '0',
| `is_private_key` TINYINT(1) NOT NULL DEFAULT '0',
| `ip_addresses` TEXT NULL DEFAULT NULL,
| `date_created` INT(11) NOT NULL,
| PRIMARY KEY (`id`)
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
*/
$config['rest_enable_keys'] = TRUE;
/*
|--------------------------------------------------------------------------
| REST Table Key Column Name
|--------------------------------------------------------------------------
|
| If not using the default table schema in 'rest_enable_keys', specify the
| column name to match e.g. my_key
|
*/
$config['rest_key_column'] = 'token';
/*
|--------------------------------------------------------------------------
| REST API Limits method
|--------------------------------------------------------------------------
|
| Specify the method used to limit the API calls
|
| Available methods are :
| $config['rest_limits_method'] = 'IP_ADDRESS'; // Put a limit per ip address
| $config['rest_limits_method'] = 'API_KEY'; // Put a limit per api key
| $config['rest_limits_method'] = 'METHOD_NAME'; // Put a limit on method calls
| $config['rest_limits_method'] = 'ROUTED_URL'; // Put a limit on the routed URL
|
*/
$config['rest_limits_method'] = 'IP_ADDRESS';
/*
|--------------------------------------------------------------------------
| REST Key Length
|--------------------------------------------------------------------------
|
| Length of the created keys. Check your default database schema on the
| maximum length allowed
|
| Note: The maximum length is 40
|
*/
$config['rest_key_length'] = 40;
/*
|--------------------------------------------------------------------------
| REST API Key Variable
|--------------------------------------------------------------------------
|
| Custom header to specify the API key
| Note: Custom headers with the X- prefix are deprecated as of
| 2012/06/12. See RFC 6648 specification for more details
|
*/
$config['rest_key_name'] = 'Authtoken';
/*
|--------------------------------------------------------------------------
| REST Enable Logging
|--------------------------------------------------------------------------
|
| When set to TRUE, the REST API will log actions based on the column names 'key', 'date',
| 'time' and 'ip_address'. This is a general rule that can be overridden in the
| $this->method array for each controller
|
| Default table schema:
| CREATE TABLE `logs` (
| `id` INT(11) NOT NULL AUTO_INCREMENT,
| `uri` VARCHAR(255) NOT NULL,
| `method` VARCHAR(6) NOT NULL,
| `params` TEXT DEFAULT NULL,
| `api_key` VARCHAR(40) NOT NULL,
| `ip_address` VARCHAR(45) NOT NULL,
| `time` INT(11) NOT NULL,
| `rtime` FLOAT DEFAULT NULL,
| `authorized` VARCHAR(1) NOT NULL,
| `response_code` smallint(3) DEFAULT '0',
| PRIMARY KEY (`id`)
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
*/
$config['rest_enable_logging'] = FALSE;
/*
|--------------------------------------------------------------------------
| REST API Logs Table Name
|--------------------------------------------------------------------------
|
| If not using the default table schema in 'rest_enable_logging', specify the
| table name to match e.g. my_logs
|
*/
$config['rest_logs_table'] = 'pi_usage_logs';
/*
|--------------------------------------------------------------------------
| REST Method Access Control
|--------------------------------------------------------------------------
| When set to TRUE, the REST API will check the access table to see if
| the API key can access that controller. 'rest_enable_keys' must be enabled
| to use this
|
| Default table schema:
| CREATE TABLE `access` (
| `id` INT(11) unsigned NOT NULL AUTO_INCREMENT,
| `key` VARCHAR(40) NOT NULL DEFAULT '',
| `all_access` TINYINT(1) NOT NULL DEFAULT '0',
| `controller` VARCHAR(50) NOT NULL DEFAULT '',
| `date_created` DATETIME DEFAULT NULL,
| `date_modified` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
| PRIMARY KEY (`id`)
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
*/
$config['rest_enable_access'] = FALSE;
/*
|--------------------------------------------------------------------------
| REST API Access Table Name
|--------------------------------------------------------------------------
|
| If not using the default table schema in 'rest_enable_access', specify the
| table name to match e.g. my_access
|
*/
$config['rest_access_table'] = 'access';
/*
|--------------------------------------------------------------------------
| REST API Param Log Format
|--------------------------------------------------------------------------
|
| When set to TRUE, the REST API log parameters will be stored in the database as JSON
| Set to FALSE to log as serialized PHP
|
*/
$config['rest_logs_json_params'] = FALSE;
/*
|--------------------------------------------------------------------------
| REST Enable Limits
|--------------------------------------------------------------------------
|
| When set to TRUE, the REST API will count the number of uses of each method
| by an API key each hour. This is a general rule that can be overridden in the
| $this->method array in each controller
|
| Default table schema:
| CREATE TABLE `limits` (
| `id` INT(11) NOT NULL AUTO_INCREMENT,
| `uri` VARCHAR(255) NOT NULL,
| `count` INT(10) NOT NULL,
| `hour_started` INT(11) NOT NULL,
| `api_key` VARCHAR(40) NOT NULL,
| PRIMARY KEY (`id`)
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
| To specify the limits within the controller's __construct() method, add per-method
| limits with:
|
| $this->method['METHOD_NAME']['limit'] = [NUM_REQUESTS_PER_HOUR];
|
| See application/controllers/api/example.php for examples
*/
$config['rest_enable_limits'] = TRUE;
/*
|--------------------------------------------------------------------------
| REST API Limits Table Name
|--------------------------------------------------------------------------
|
| If not using the default table schema in 'rest_enable_limits', specify the
| table name to match e.g. my_limits
|
*/
$config['rest_limits_table'] = 'api_usage_logs';
/*
|--------------------------------------------------------------------------
| REST API Default Limit
|--------------------------------------------------------------------------
|
| Default limit for API endpoints that don't have a specific limit defined
| Value is the number of requests allowed per time period (default: 1 hour)
| Set to FALSE to disable default limits
|
*/
$config['rest_default_limit'] = 100;
/*
|--------------------------------------------------------------------------
| REST API Default Limit Time Period
|--------------------------------------------------------------------------
|
| Time period in seconds for the default limit (default: 3600 = 1 hour)
|
*/
$config['rest_default_limit_time'] = 3600;
/*
|--------------------------------------------------------------------------
| REST Ignore HTTP Accept
|--------------------------------------------------------------------------
|
| Set to TRUE to ignore the HTTP Accept and speed up each request a little.
| Only do this if you are using the $this->rest_format or /format/xml in URLs
|
*/
$config['rest_ignore_http_accept'] = FALSE;
/*
|--------------------------------------------------------------------------
| REST AJAX Only
|--------------------------------------------------------------------------
|
| Set to TRUE to allow AJAX requests only. Set to FALSE to accept HTTP requests
|
| Note: If set to TRUE and the request is not AJAX, a 505 response with the
| error message 'Only AJAX requests are accepted.' will be returned.
|
| Hint: This is good for production environments
|
*/
$config['rest_ajax_only'] = FALSE;
/*
|--------------------------------------------------------------------------
| REST Language File
|--------------------------------------------------------------------------
|
| Language file to load from the language directory
|
*/
$config['rest_language'] = 'english';
/*
|--------------------------------------------------------------------------
| CORS Check
|--------------------------------------------------------------------------
|
| Set to TRUE to enable Cross-Origin Resource Sharing (CORS). Useful if you
| are hosting your API on a different domain from the application that
| will access it through a browser
|
*/
$config['check_cors'] = FALSE;
/*
|--------------------------------------------------------------------------
| CORS Allowable Headers
|--------------------------------------------------------------------------
|
| If using CORS checks, set the allowable headers here
|
*/
$config['allowed_cors_headers'] = [
'Origin',
'X-Requested-With',
'Content-Type',
'Accept',
'Access-Control-Request-Method'
];
/*
|--------------------------------------------------------------------------
| CORS Allowable Methods
|--------------------------------------------------------------------------
|
| If using CORS checks, you can set the methods you want to be allowed
|
*/
$config['allowed_cors_methods'] = [
'GET',
'POST',
'OPTIONS',
'PUT',
'PATCH',
'DELETE'
];
/*
|--------------------------------------------------------------------------
| CORS Allow Any Domain
|--------------------------------------------------------------------------
|
| Set to TRUE to enable Cross-Origin Resource Sharing (CORS) from any
| source domain
|
*/
$config['allow_any_cors_domain'] = FALSE;
/*
|--------------------------------------------------------------------------
| CORS Allowable Domains
|--------------------------------------------------------------------------
|
| Used if $config['check_cors'] is set to TRUE and $config['allow_any_cors_domain']
| is set to FALSE. Set all the allowable domains within the array
|
| e.g. $config['allowed_origins'] = ['http://www.example.com', 'https://spa.example.com']
|
*/
$config['allowed_cors_origins'] = [];

36
api/config/routes.php Normal file
View File

@@ -0,0 +1,36 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
// Specific API routes (must come before generic routes)
$route['api/playground'] = 'playground/index';
$route['api/playground/swagger'] = 'playground/swagger';
$route['api/sandbox'] = 'playground/sandbox';
$route['api/sandbox/execute_request'] = 'playground/execute_request';
$route['api/sandbox/get_samples'] = 'playground/get_samples';
$route['api/sandbox/get_endpoints'] = 'playground/get_endpoints';
$route['api/sandbox/get_environment_config'] = 'playground/get_environment_config';
$route['api/sandbox/documentation'] = 'playground/documentation';
$route['api/users/stats/(:num)'] = 'api/user_stats/$1';
$route['api/users/stats'] = 'api/user_stats';
$route['api/reporting'] = 'reporting/index';
$route['api/reporting/get_chart_data'] = 'reporting/get_chart_data';
$route['api/reporting/export'] = 'reporting/export';
// Generic API routes (must come after specific routes)
$route['api/delete/(:any)/(:num)'] = '$1/data/$2';
$route['api/(:any)/search/(:any)'] = '$1/data_search/$2';
$route['api/(:any)/search'] = '$1/data_search';
$route['api/login/auth'] = 'login/login_api';
$route['api/login/view'] = 'login/view';
$route['api/login/key'] = 'login/api_key';
$route['api/(:any)/(:any)/(:num)'] = '$1/data/$2/$3';
$route['api/(:any)/(:num)/(:num)'] = '$1/data/$2/$3';
$route['api/custom_fields/(:any)/(:num)'] = 'custom_fields/data/$1/$2';
$route['api/custom_fields/(:any)'] = 'custom_fields/data/$1';
$route['api/common/(:any)/(:num)'] = 'common/data/$1/$2';
$route['api/common/(:any)'] = 'common/data/$1';
$route['api/(:any)/(:num)'] = '$1/data/$2';
$route['api/(:any)'] = '$1/data';

2337
api/config/swagger.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,530 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
use \WpOrg\Requests\Requests as RestapiRequests;
/**
* CodeIgniter API Controller
*
* @package CodeIgniter
* @subpackage Libraries
* @category Libraries
* @author Jeevan Lal
* @license MIT
* @version 1.1.6
*/
class API_Controller extends CI_Controller
{
/**
* List of allowed HTTP methods
*
* @var array
*/
protected $allowed_http_methods = ['get', 'delete', 'post', 'put', 'options', 'patch', 'head'];
/**
* The request method is not supported by the following resource
* @link http://www.restapitutorial.com/httpstatuscodes.html
*/
const HTTP_METHOD_NOT_ALLOWED = 405;
/**
* The request cannot be fulfilled due to multiple errors
*/
const HTTP_BAD_REQUEST = 400;
/**
* Request Timeout
*/
const HTTP_REQUEST_TIMEOUT = 408;
/**
* The requested resource could not be found
*/
const HTTP_NOT_FOUND = 404;
/**
* The user is unauthorized to access the requested resource
*/
const HTTP_UNAUTHORIZED = 401;
/**
* The request has succeeded
*/
const HTTP_OK = 200;
/**
* HTTP status codes and their respective description
*/
const HEADER_STATUS_STRINGS = [
'405' => 'HTTP/1.1 405 Method Not Allowed',
'400' => 'BAD REQUEST',
'408' => 'Request Timeout',
'404' => 'NOT FOUND',
'401' => 'UNAUTHORIZED',
'200' => 'OK',
];
/**
* API LIMIT TABLE NAME
*/
protected $API_LIMIT_TABLE_NAME;
/**
* API KEYS TABLE NAME
*/
protected $API_KEYS_TABLE_NAME;
/**
* RETURN DATA
*/
protected $return_other_data = [];
public function __construct() {
parent::__construct();
$this->CI =& get_instance();
// load api config file
$this->CI->load->config('api');
// set timezone for api limit
date_default_timezone_set($this->CI->config->item('api_timezone'));
// Load Config Items Values
$this->API_LIMIT_TABLE_NAME = $this->CI->config->item('api_limit_table_name');
$this->API_KEYS_TABLE_NAME = $this->CI->config->item('api_keys_table_name');
\modules\api\core\Apiinit::the_da_vinci_code('api');
}
public function _APIConfig($config = [])
{
// return other data
if(isset($config['data']))
$this->return_other_data = $config['data'];
// by default method `GET`
if ((isset($config) AND empty($config)) OR empty($config['methods'])) {
$this->_allow_methods(['GET']);
} else {
$this->_allow_methods($config['methods']);
}
// api limit function `_limit_method()`
// Use provided limit or default from config
if(isset($config['limit'])) {
$this->_limit_method($config['limit']);
} else {
// Apply default limit if configured and not explicitly disabled
$default_limit = $this->CI->config->item('api_default_limit');
if ($default_limit !== false && !empty($default_limit)) {
$this->_limit_method($default_limit);
}
}
// api key function `_api_key()`
if(isset($config['key']))
$this->_api_key($config['key']);
// IF Require Authentication
if(isset($config['requireAuthorization']) AND $config['requireAuthorization'] === true) {
$token_data = $this->_isAuthorized();
// remove api time in user token data
unset($token_data->API_TIME);
// return token decode data
return [ 'token_data' => (array) $token_data ];
}
}
/**
* Allow Methods
* -------------------------------------
* @param: {array} request methods
*/
public function _allow_methods(array $methods)
{
$REQUEST_METHOD = $this->CI->input->server('REQUEST_METHOD', TRUE);
// check request method in `$allowed_http_methods` array()
if (in_array(strtolower($REQUEST_METHOD), $this->allowed_http_methods))
{
// check request method in user define `$methods` array()
if (in_array(strtolower($REQUEST_METHOD), $methods) OR in_array(strtoupper($REQUEST_METHOD), $methods))
{
// allow request method
return true;
} else {
// not allow request method
$this->_response(['status' => FALSE, 'error' => 'Unknown method'], self::HTTP_METHOD_NOT_ALLOWED);
}
} else {
$this->_response(['status' => FALSE, 'error' => 'Unknown method'], self::HTTP_METHOD_NOT_ALLOWED);
}
}
/**
* Limit Method
* ------------------------
* @param: {int} number
* @param: {type} ip
*
* Total Number Limit without Time
*
* @param: {minute} time/everyday
* Total Number Limit with Last {3,4,5...} minute
* --------------------------------------------------------
*/
public function _limit_method(array $data)
{
// check limit number
if (!isset($data[0])) {
$this->_response(['status' => FALSE, 'error' => 'Limit Number Required'], self::HTTP_BAD_REQUEST);
}
// check limit type
if (!isset($data[1])) {
$this->_response(['status' => FALSE, 'error' => 'Limit Type Required'], self::HTTP_BAD_REQUEST);
}
if (!isset($this->db)) {
$this->_response(['status' => FALSE, 'error' => 'Load CodeIgniter Database Library'], self::HTTP_BAD_REQUEST);
}
// check limit database table exists
if (!$this->db->table_exists($this->API_LIMIT_TABLE_NAME)) {
$this->_response(['status' => FALSE, 'error' => 'Create API Limit Database Table'], self::HTTP_BAD_REQUEST);
}
$limit_num = $data[0]; // limit number
$limit_type = $data[1]; // limit type
$limit_time = isset($data[2])? $data[2]:''; // time minute
if ($limit_type == 'ip')
{
$where_data_ip = [
'uri' => $this->CI->uri->uri_string(),
'class' => $this->CI->router->fetch_class(),
'method' => $this->CI->router->fetch_method(),
'ip_address' => $this->CI->input->ip_address(),
];
$limit_query = $this->CI->db->get_where($this->API_LIMIT_TABLE_NAME, $where_data_ip);
if ($this->db->affected_rows() >= $limit_num)
{
// time limit not empty
if (isset($limit_time) AND !empty($limit_time))
{
// if time limit `numeric` numbers
if (is_numeric($limit_time))
{
$limit_timestamp = time() - ($limit_time*60);
// echo Date('d/m/Y h:i A', $times);
$where_data_ip_with_time = [
'uri' => $this->CI->uri->uri_string(),
'class' => $this->CI->router->fetch_class(),
'method' => $this->CI->router->fetch_method(),
'ip_address' => $this->CI->input->ip_address(),
'time >=' => $limit_timestamp
];
$time_limit_query = $this->CI->db->get_where($this->API_LIMIT_TABLE_NAME, $where_data_ip_with_time);
// echo $this->CI->db->last_query();
if ($this->db->affected_rows() >= $limit_num)
{
$this->_response(['status' => FALSE, 'error' => 'This IP Address has reached the time limit for this method'], self::HTTP_REQUEST_TIMEOUT);
} else
{
// insert limit data
$this->limit_data_insert();
}
}
// if time limit equal to `everyday`
if ($limit_time == 'everyday')
{
$this->CI->load->helper('date');
$bad_date = mdate('%d-%m-%Y', time());
$start_date = nice_date($bad_date .' 12:00 AM', 'd-m-Y h:i A'); // {DATE} 12:00 AM
$end_date = nice_date($bad_date .' 12:00 PM', 'd-m-Y h:i A'); // {DATE} 12:00 PM
$start_date_timestamp = strtotime($start_date);
$end_date_timestamp = strtotime($end_date);
$where_data_ip_with_time = [
'uri' => $this->CI->uri->uri_string(),
'class' => $this->CI->router->fetch_class(),
'method' => $this->CI->router->fetch_method(),
'ip_address' => $this->CI->input->ip_address(),
'time >=' => $start_date_timestamp,
'time <=' => $end_date_timestamp,
];
$time_limit_query = $this->CI->db->get_where($this->API_LIMIT_TABLE_NAME, $where_data_ip_with_time);
// echo $this->CI->db->last_query();exit;
if ($this->db->affected_rows() >= $limit_num)
{
$this->_response(['status' => FALSE, 'error' => 'This IP Address has reached the time limit for this method'], self::HTTP_REQUEST_TIMEOUT);
} else {
// insert limit data
$this->limit_data_insert();
}
}
} else {
$this->_response(['status' => FALSE, 'error' => 'This IP Address has reached limit for this method'], self::HTTP_REQUEST_TIMEOUT);
}
} else {
// insert limit data
$this->limit_data_insert();
}
} else {
$this->_response(['status' => FALSE, 'error' => 'Limit Type Invalid'], self::HTTP_BAD_REQUEST);
}
}
/**
* Limit Data Insert
*/
private function limit_data_insert()
{
$this->CI->load->helper('api_helper');
$insert_data = [
'uri' => $this->CI->uri->uri_string(),
'class' => $this->CI->router->fetch_class(),
'method' => $this->CI->router->fetch_method(),
'ip_address' => $this->CI->input->ip_address(),
'time' => time(),
];
insert($this->API_LIMIT_TABLE_NAME, $insert_data);
}
/**
* API key
*/
private function _api_key(array $key)
{
if (!isset($key[0])) {
$api_key_type = 'header';
} else {
$api_key_type = $key[0];
}
if (!isset($key[1])) {
$api_key = 'table';
} else {
$api_key = $key[1];
}
// api key type `Header`
if (strtolower($api_key_type) == 'header')
{
$api_key_header_name = $this->config->item('api_key_header_name');
// check api key header name in request headers
$is_header = $this->exists_header($api_key_header_name); // return status and header value
if (isset($is_header['status']) === TRUE)
{
$HEADER_VALUE = trim($is_header['value'] ?? '');
// if api key equal to `table`
if ($api_key != "table")
{
if ($HEADER_VALUE != $api_key) {
$this->_response(['status' => FALSE, 'error' => 'API Key Invalid'], self::HTTP_UNAUTHORIZED);
}
} else {
if (!isset($this->db)) {
$this->_response(['status' => FALSE, 'error' => 'Load CodeIgniter Database Library'], self::HTTP_BAD_REQUEST);
}
// check api key database table exists
if (!$this->db->table_exists($this->API_KEYS_TABLE_NAME)) {
$this->_response(['status' => FALSE, 'error' => 'Create API Key Database Table'], self::HTTP_BAD_REQUEST);
}
$where_key_data = [
'controller' => $this->CI->router->fetch_class(),
'api_key' => $HEADER_VALUE,
];
$limit_query = $this->CI->db->get_where($this->API_KEYS_TABLE_NAME, $where_key_data);
if (!$this->db->affected_rows() > 0)
{
$this->_response(['status' => FALSE, 'error' => 'API Key Invalid'], self::HTTP_NOT_FOUND);
}
}
} else {
$this->_response(['status' => FALSE, 'error' => 'Set API Key in Request Header'], self::HTTP_NOT_FOUND);
}
} else if (strtolower($api_key_type) == 'get') // // api key type `get`
{
// return status and header value `Content-Type`
$is_header = $this->exists_header('Content-Type');
if (isset($is_header['status']) === TRUE) {
if ($is_header['value'] === "application/json")
{
$stream_clean = $this->CI->security->xss_clean($this->CI->input->raw_input_stream);
$_GET = json_decode($stream_clean, true);
}
}
$api_key_get_name = $this->config->item('api_key_get_name');
$get_param_value = $this->CI->input->get($api_key_get_name, TRUE);
if (!empty($get_param_value) AND is_string($get_param_value))
{
// if api key equal to `table`
if ($api_key != "table")
{
if ($get_param_value != $api_key) {
$this->_response(['status' => FALSE, 'error' => 'API Key Invalid'], self::HTTP_UNAUTHORIZED);
}
} else {
if (!isset($this->db)) {
$this->_response(['status' => FALSE, 'error' => 'Load CodeIgniter Database Library'], self::HTTP_BAD_REQUEST);
}
// check api key database table exists
if (!$this->db->table_exists($this->API_KEYS_TABLE_NAME)) {
$this->_response(['status' => FALSE, 'error' => 'Create API Key Database Table'], self::HTTP_BAD_REQUEST);
}
$where_key_data = [
'controller' => $this->CI->router->fetch_class(),
'api_key' => $get_param_value,
];
$limit_query = $this->CI->db->get_where($this->API_KEYS_TABLE_NAME, $where_key_data);
if (!$this->db->affected_rows() > 0)
{
$this->_response(['status' => FALSE, 'error' => 'API Key Invalid'], self::HTTP_NOT_FOUND);
}
}
} else {
$this->_response(['status' => FALSE, 'error' => 'API Key GET Parameter Required'], self::HTTP_NOT_FOUND);
}
} else if (strtolower($api_key_type) == 'post') // // api key type `post`
{
// return status and header value `Content-Type`
$is_header = $this->exists_header('Content-Type');
if (isset($is_header['status']) === TRUE) {
if ($is_header['value'] === "application/json")
{
$stream_clean = $this->CI->security->xss_clean($this->CI->input->raw_input_stream);
$_POST = json_decode($stream_clean, true);
}
}
$api_key_post_name = $this->config->item('api_key_post_name');
$get_param_value = $this->CI->input->post($api_key_post_name, TRUE);
if (!empty($get_param_value) AND is_string($get_param_value))
{
// if api key equal to `table`
if ($api_key != "table")
{
if ($get_param_value != $api_key) {
$this->_response(['status' => FALSE, 'error' => 'API Key Invalid'], self::HTTP_UNAUTHORIZED);
}
} else {
if (!isset($this->db)) {
$this->_response(['status' => FALSE, 'error' => 'Load CodeIgniter Database Library'], self::HTTP_BAD_REQUEST);
}
// check api key database table exists
if (!$this->db->table_exists($this->API_KEYS_TABLE_NAME)) {
$this->_response(['status' => FALSE, 'error' => 'Create API Key Database Table'], self::HTTP_BAD_REQUEST);
}
$where_key_data = [
'controller' => $this->CI->router->fetch_class(),
'api_key' => $get_param_value,
];
$limit_query = $this->CI->db->get_where($this->API_KEYS_TABLE_NAME, $where_key_data);
if (!$this->db->affected_rows() > 0)
{
$this->_response(['status' => FALSE, 'error' => 'API Key Invalid'], self::HTTP_NOT_FOUND);
}
}
} else {
$this->_response(['status' => FALSE, 'error' => 'API Key POST Parameter Required'], self::HTTP_NOT_FOUND);
}
} else {
$this->_response(['status' => FALSE, 'error' => 'API Key Parameter Required'], self::HTTP_NOT_FOUND);
}
}
/**
* Is Authorized
*/
private function _isAuthorized()
{
// Load Authorization Library
$this->CI->load->library('authorization_token');
// check token is valid
$result = $this->authorization_token->validateToken();
if (isset($result['status']) AND $result['status'] === true)
{
return $result['data'];
} else {
$this->_response(['status' => FALSE, 'error' => $result['message']], self::HTTP_UNAUTHORIZED);
}
}
/**
* Check Request Header Exists
* @return ['status' => true, 'value' => value ]
*/
private function exists_header($header_name)
{
$headers = apache_request_headers();
foreach ($headers as $header => $value) {
if ($header === $header_name) {
return ['status' => true, 'value' => $value ];
}
}
return ['status' => false, 'value' => null];
}
/**
* Private Response Function
*/
private function _response($data = NULL, $http_code = NULL)
{
ob_start();
header('content-type:application/json; charset=UTF-8');
header(self::HEADER_STATUS_STRINGS[$http_code], true, $http_code);
if (!is_array($this->return_other_data)) {
print_r(json_encode(['status' => false, 'error' => 'Invalid data format']));
} else {
print_r(json_encode(array_merge($data, $this->return_other_data)));
}
ob_end_flush();
die();
}
/**
* Public Response Function
*/
public function api_return($data = NULL, $http_code = NULL)
{
ob_start();
header('content-type:application/json; charset=UTF-8');
header(self::HEADER_STATUS_STRINGS[$http_code], true, $http_code);
print_r(json_encode($data));
ob_end_flush();
}
}

220
api/controllers/Api.php Normal file
View File

@@ -0,0 +1,220 @@
<?php
use \WpOrg\Requests\Requests as RestapiRequests;
defined('BASEPATH') or exit('No direct script access allowed');
class Api extends AdminController
{
public function __construct()
{
parent::__construct();
$this->load->model('api_model');
$this->load->library('app_modules');
if (!$this->app_modules->is_active('api')) {
access_denied("Api");
}
\modules\api\core\Apiinit::the_da_vinci_code('api');
}
public function api_management()
{
\modules\api\core\Apiinit::the_da_vinci_code('api');
$data['user_api'] = $this->api_model->get_user();
$data['title'] = _l('api_management');
$this->load->view('api_management', $data);
}
/* API user statistics */
public function user_stats($id = '')
{
\modules\api\core\Apiinit::ease_of_mind('api');
if (!is_admin()) {
access_denied('User Statistics');
}
$data['title'] = _l('user_statistics');
$data['user_id'] = $id;
if ($id) {
$user_api = $this->api_model->get_user($id);
$data['user_api'] = $user_api && count($user_api) ? $user_api[0] : null;
if ($data['user_api']) {
$data['quota_summary'] = $this->api_model->get_quota_summary($data['user_api']['token']);
$data['quota_stats'] = $this->api_model->get_quota_stats($data['user_api']['token']);
$data['top_endpoints'] = $this->api_model->get_top_endpoints($data['user_api']['token']);
}
}
$data['api_users'] = $this->api_model->get_user();
$this->load->view('user_stats', $data);
}
public function api_guide()
{
fopen(APP_MODULES_PATH . 'api/views/apidoc/index.html', 'r');
}
/* Add new user or update existing*/
public function user()
{
\modules\api\core\Apiinit::ease_of_mind('api');
if (!is_admin()) {
access_denied('Ticket Priorities');
}
if ($this->input->post()) {
\modules\api\core\Apiinit::the_da_vinci_code('api');
if (!$this->input->post('id')) {
$id = $this->api_model->add_user($this->input->post());
if ($id) {
set_alert('success', _l('added_successfully', _l('user_api')));
}
redirect(admin_url('api/api_management'));
} else {
$data = $this->input->post();
$id = $data['id'];
unset($data['id']);
$success = $this->api_model->update_user($data, $id);
if ($success) {
set_alert('success', _l('updated_successfully', _l('user_api')));
}
redirect(admin_url('api/api_management'));
}
die;
}
}
/* Update user quotas */
public function update_user_quotas()
{
\modules\api\core\Apiinit::ease_of_mind('api');
if (!is_admin()) {
access_denied('User Quotas');
}
if ($this->input->post()) {
\modules\api\core\Apiinit::the_da_vinci_code('api');
$data = $this->input->post();
$id = $data['id'];
unset($data['id']);
// Add timestamp for quota update
$data['quota_updated_at'] = date('Y-m-d H:i:s');
$success = $this->api_model->update_user($data, $id);
if ($success) {
set_alert('success', _l('quota_updated_successfully'));
} else {
set_alert('danger', _l('quota_update_failed'));
}
redirect(admin_url('api/api_management'));
}
}
/* Edit user */
public function create_user()
{
\modules\api\core\Apiinit::ease_of_mind('api');
if (!is_admin()) {
access_denied('User');
}
$data['title'] = _l('new_user_api');
$this->load->view('create_user_api', $data);
}
/* Edit user */
public function edit_user($id)
{
\modules\api\core\Apiinit::ease_of_mind('api');
if (!is_admin()) {
access_denied('User');
}
if (!$id) {
redirect(admin_url('api/api_management'));
}
$user_api = $this->api_model->get_user($id);
$data['user_api'] = $user_api && count($user_api) ? $user_api[0] : null;
$data['title'] = _l('edit_user_api');
$this->load->view('edit_user_api', $data);
}
/* Delete user */
public function delete_user($id)
{
\modules\api\core\Apiinit::ease_of_mind('api');
if (!is_admin()) {
access_denied('User');
}
if (!$id) {
redirect(admin_url('api/api_management'));
}
$response = $this->api_model->delete_user($id);
if ($response == true) {
set_alert('success', _l('deleted', _l('user_api')));
}
redirect(admin_url('api/api_management'));
}
/* Get user statistics data via AJAX */
public function get_user_stats_data()
{
if (!$this->input->is_ajax_request()) {
show_404();
}
$user_id = $this->input->post('user_id');
$days = $this->input->post('days') ?: 30;
if ($user_id) {
$user_api = $this->api_model->get_user($user_id);
if ($user_api && count($user_api)) {
$api_key = $user_api[0]['token'];
$quota_summary = $this->api_model->get_quota_summary($api_key);
$quota_stats = $this->api_model->get_quota_stats($api_key, $days);
$top_endpoints = $this->api_model->get_top_endpoints($api_key);
echo json_encode([
'quota_summary' => $quota_summary,
'quota_stats' => $quota_stats,
'top_endpoints' => $top_endpoints
]);
return;
}
}
echo json_encode(['error' => 'User not found']);
}
/* Clean old logs */
public function clean_logs()
{
if (!is_admin()) {
access_denied('api_management');
}
$days = $this->input->post('days') ?: 90;
if ($this->api_model->clean_old_logs($days)) {
set_alert('success', _l('logs_cleaned_successfully'));
} else {
set_alert('danger', _l('log_cleaning_failed'));
}
redirect(admin_url('api/api_management'));
}
}

View File

@@ -0,0 +1,296 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
require __DIR__ . '/REST_Controller.php';
class Calendar extends REST_Controller
{
public function __construct()
{
parent::__construct();
}
/**
* @api {get} api/calendar/ Get All Calendar Events
* @apiName GetCalendarEvents
* @apiHeader {String} authtoken Authentication token, generated from admin area
* @apiGroup Calendar Events
*
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* [
* {
* "eventid": "1",
* "title": "Hello",
* "description": "test",
* "userid": "1",
* "start": "2023-12-12 07:00:00",
* "end": 2023-12-12 07:00:00,
* "public": "1",
* "color": "#03a9f4",
* "isstartnotified": "0",
* "reminder_before": "30",
* "reminder_before_type": "minutes"
* },
* {
* "eventid": "2",
* "title": "Hello2",
* "description": "test2",
* "userid": "2",
* "start": "2022-12-12 07:00:00",
* "end": 2022-12-12 07:00:00,
* "public": "0",
* "color": "#03a9f4",
* "isstartnotified": "0",
* "reminder_before": "3",
* "reminder_before_type": "hours"
* }
* ]
*
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
/**
* @api {get} api/calendar/:id Request Specific Event Information
* @apiName GetCalendarEvent
* @apiHeader {String} authtoken Authentication token, generated from admin area
* @apiGroup Calendar Events
*
* @apiParam {id} id Event data by id.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* [
* {
* "eventid": "1",
* "title": "Hello",
* "description": "test",
* "userid": "1",
* "start": "2023-12-12 07:00:00",
* "end": 2023-12-12 07:00:00,
* "public": "1",
* "color": "#03a9f4",
* "isstartnotified": "0",
* "reminder_before": "30",
* "reminder_before_type": "minutes"
* }
* ]
*
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_get($id = '')
{
$data = $this->Api_model->get_table('events', $id);
if ($data) {
$this->response($data, REST_Controller::HTTP_OK);
} else {
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND);
}
}
/**
* @api {post} api/calendar/ Create a new Calendar Event
* @apiName PostCalendarEvent
* @apiGroup Calendar Events
* @apiHeader {String} authtoken Authentication token, generated from admin area
* @apiParam {String} title Required event title.
* @apiParam {String} description Optional event description.
* @apiParam {Date} start Required event start date.
* @apiParam {Date} start Optional event end date.
* @apiParam {String} reminder_before_type Required value of reminder before type.
* @apiParam {Number} reminder_before Required value of reminder before.
* @apiParam {String} color Optional event color.
* @apiParam {Number} userid Required user id.
* @apiParam {Number} isstartnotified Required isstartnotified status.
* @apiParam {Number} public Required public status.
* @apiParamExample {Multipart Form} Request-Example:
* 'title' => string 'Hello'
* 'start' => date '2023/12/12 07:00'
* 'end' => date '2023/12/12 07:00'
* 'reminder_before' => number '10'
* 'reminder_before_type' => string 'minutes'
* 'color' => string 'red'
* 'description' => string 'for test'
* 'userid' => number '1'
* 'public' => number '1' (0/1)
* 'isstartnotified' => number '0'
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Data Added Successfully"
* }
*
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Data Creation Failed"
* }
*/
public function data_post()
{
\modules\api\core\Apiinit::the_da_vinci_code('api');
$data = $this->input->post();
if (empty($data['color'])) {
$data['color'] = '#28B8DA';
}
$this->form_validation->set_rules('title', 'Title', 'trim|required');
$this->form_validation->set_rules('description', 'Description', 'trim');
$this->form_validation->set_rules('start', 'Start Date', 'trim|required');
$this->form_validation->set_rules('end', 'End Date', 'trim');
$this->form_validation->set_rules('reminder_before', 'Value', 'numeric|required');
$this->form_validation->set_rules('reminder_before_type', 'reminder_type', 'trim|required');
$this->form_validation->set_rules('color', 'Event Color', 'trim');
$this->form_validation->set_rules('userid', 'Userid', 'numeric|required');
$this->form_validation->set_rules('isstartnotified', 'Isstartnotified', 'numeric|required');
$this->form_validation->set_rules('public', 'Public', 'numeric|required');
if ($this->form_validation->run() == FALSE) {
$message = array('status' => FALSE, 'error' => $this->form_validation->error_array(), 'message' => validation_errors());
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$id = $this->Api_model->event($data);
if ($id > 0 && !empty($id)) {
$message = array(
'status' => TRUE,
'message' => 'Data Added Successfully',
'record_id' => $id
);
$this->response($message, REST_Controller::HTTP_OK);
}
else {
$message = array('status' => FALSE, 'message' => 'Data Add Fail');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {put} api/calendar/:id Update a Calendar Event
* @apiName UpdateCalendarEvent
* @apiGroup Calendar Events
* @apiHeader {String} authtoken Authentication token, generated from admin area
* @apiParam {id} unique ID for update data.
*
* @apiParamExample {json} Request-Example:
* {
* "title": "Hello",
* "start": "2023/12/12 07:00",
* "end": "2023/12/12 07:00",
* "reminder_before": "10",
* "reminder_before_type": "minutes",
* "color": "red",
* "description": "for test",
* "userid":6,
* "public":1,
* "isstartnotified":1
* }
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Data Update Successful."
* }
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Data Update Fail"
* }
*/
public function data_put($id = '')
{
// JSON data is now automatically parsed in REST_Controller
if (empty($_POST) || !isset($_POST)) {
$message = array('status' => FALSE, 'message' => 'Data Not Acceptable OR Not Provided');
$this->response($message, REST_Controller::HTTP_NOT_ACCEPTABLE);
}
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid data or missing Send ID. please provide updated data ID.');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$_POST['eventid'] = $id;
$update_data = $this->input->post();
$data = $_POST;
$output = $this->Api_model->event($data);
if ($output > 0 && !empty($output)) {
$message = array('status' => TRUE, 'message' => 'Data Update Successful.');
$this->response($message, REST_Controller::HTTP_OK);
} else {
$message = array('status' => FALSE, 'message' => 'Data Update Fail.');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {delete} api/calendar/:id Delete a Calendar Event
* @apiVersion 0.3.0
* @apiName DeleteCalendarEvent
* @apiGroup Calendar Events
* @apiHeader {String} authtoken Authentication token, generated from admin area
* @apiParam {Number} ID ID for data deletion.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Data Deleted Successfully"
* }
*
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Data Delete Fail"
* }
*/
public function data_delete($id = '')
{
$id = $this->security->xss_clean($id);
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$this->load->model('utilities_model');
$output = $this->utilities_model->delete_event($id);
if ($output === TRUE) {
$message = array('status' => TRUE, 'message' => 'Delete Successful.');
$this->response($message, REST_Controller::HTTP_OK);
} else {
$message = array('status' => FALSE, 'message' => 'Delete Fail.');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
}

50
api/controllers/Check.php Normal file
View File

@@ -0,0 +1,50 @@
<?php
require __DIR__ . '/REST_Controller.php';
defined('BASEPATH') or exit('No direct script access allowed');
/**
* @OA\Tag(
* name="Check",
* description="Common API endpoints"
* )
*/
class Check extends REST_Controller
{
/**
* @OA\Get(
* path="/common/data/{type}",
* tags={"Common"},
* summary="Get common data",
* description="Retrieve common system data",
* operationId="getCommonData",
* security={{"api_key":{}}},
* @OA\Parameter(
* name="type",
* in="path",
* required=true,
* @OA\Schema(
* type="string",
* enum={"expense_category", "payment_mode", "tax_data"}
* )
* ),
* @OA\Response(
* response=200,
* description="Successful operation"
* ),
* @OA\Response(
* response=400,
* description="Invalid request"
* ),
* @OA\Response(
* response=404,
* description="Not found"
* )
* )
*/
public function data_get($type = "")
{
// Existing implementation
}
}

179
api/controllers/Common.php Normal file
View File

@@ -0,0 +1,179 @@
<?php
if ( ! defined('BASEPATH')) exit('No direct script access allowed');
require __DIR__.'/REST_Controller.php';
class Common extends REST_Controller {
public function __construct()
{
parent::__construct();
}
public function data_get($type = "")
{
$allowed_type = ["expense_category", "payment_mode", "tax_data"];
if (empty($type) || !in_array($type, $allowed_type)) {
// Set the response and exit
$this->response([
'status' => FALSE,
'message' => 'Not valid data'
], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
$data = $this->{$type}();
if (empty($data)) {
// Set the response and exit
$this->response([
'status' => FALSE,
'message' => 'No data were found'
], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
}
/**
* @api {get} api/common/expense_category Request Expense category
* @apiVersion 0.3.0
* @apiName GetExpense category
* @apiGroup Expense Categories
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiSuccess {Array} Expense category information.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
*
* [
* {
* "id": "1",
* "name": "cloud server",
* "description": "AWS server"
* },
* {
* "id": "2",
* "name": "website domain",
* "description": "domain Managment and configurations"
* }
* ]
*
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function expense_category()
{
$this->load->model('expenses_model');
return $this->expenses_model->get_category();
}
/**
* @api {get} api/common/payment_mode Request Payment Modes
* @apiVersion 0.3.0
* @apiName GetPayment Mode
* @apiGroup Payment Modes
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiSuccess {Array} Payment Modes.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* [
* {
* "id": "1",
* "name": "Bank",
* "description": null,
* "show_on_pdf": "0",
* "invoices_only": "0",
* "expenses_only": "0",
* "selected_by_default": "1",
* "active": "1"
* }
* ]
*
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function payment_mode()
{
$this->load->model('payment_modes_model');
return $this->payment_modes_model->get('', [
'invoices_only !=' => 1,
]);
}
/**
* @api {get} api/common/tax_data Request Taxes
* @apiVersion 0.3.0
* @apiName GetTaxes
* @apiGroup Taxes
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiSuccess {Array} Tax information.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* [
* {
* "id": "4",
* "name": "PAYPAL",
* "taxrate": "5.00"
* },
* {
* "id": "1",
* "name": "CGST",
* "taxrate": "9.00"
* },
* {
* "id": "2",
* "name": "SGST",
* "taxrate": "9.00"
* },
* {
* "id": "3",
* "name": "GST",
* "taxrate": "18.00"
* }
* ]
*
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function tax_data()
{
$this->load->model('taxes_model');
return $this->taxes_model->get();
}
}
/* End of file Common.php */
/* Location: ./application/controllers/Common.php */

View File

@@ -0,0 +1,522 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
// This can be removed if you use __autoload() in config.php OR use Modular Extensions
/** @noinspection PhpIncludeInspection */
require __DIR__ . '/REST_Controller.php';
/**
* This is an example of a few basic user interaction methods you could use
* all done with a hardcoded array
*
* @package CodeIgniter
* @subpackage Rest Server
* @category Controller
* @author Phil Sturgeon, Chris Kacerguis
* @license MIT
* @link https://github.com/chriskacerguis/codeigniter-restserver
*/
class Contacts extends REST_Controller {
function __construct() {
// Construct the parent class
parent::__construct();
$this->load->model('authentication_model');
}
/**
* @api {get} api/contacts/:customer_id/:contact_id List all Contacts of a Customer
* @apiVersion 0.1.0
* @apiName GetContact
* @apiGroup Contacts
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {Number} customer_id Mandatory Customer unique ID
* @apiParam {Number} contact_id Optional Contact unique ID <br/><i>Note : if you don't pass Contact id then it will list all contacts of the customer</i>
*
* @apiSuccess {Object} Contact Contact information
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "id": "6",
* "userid": "1",
* "company": "xyz",
* "vat": "",
* "phonenumber": "1234567890",
* "country": "0",
* "city": "",
* "zip": "360005",
* "state": "",
* "address": "",
* "website": "",
* "datecreated": "2020-08-19 20:07:49",
* "active": "1",
* "leadid": null,
* "billing_street": "",
* "billing_city": "",
* "billing_state": "",
* "billing_zip": "",
* "billing_country": "0",
* "shipping_street": "",
* "shipping_city": "",
* "shipping_state": "",
* "shipping_zip": "",
* "shipping_country": "0",
* "longitude": null,
* "latitude": null,
* "default_language": "english",
* "default_currency": "0",
* "show_primary_contact": "0",
* "stripe_id": null,
* "registration_confirmed": "1",
* "addedfrom": "1"
* }
*
* @apiError {Boolean} status Request status
* @apiError {String} message No data were found
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_get($customer_id = '', $contact_id = '') {
// If the id parameter doesn't exist return all the
if (empty($contact_id) && !empty($customer_id)) {
$data = $this->Api_model->get_table('all_contacts', $customer_id);
}
if (!empty($contact_id) && !empty($customer_id)) {
$data = $this->Api_model->get_table('contacts', $contact_id);
}
if (empty($contact_id) && empty($customer_id)) {
// Set the response and exit
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
// Check if the data store contains
if ($data) {
$data = $this->Api_model->get_api_custom_data($data, "contacts", $contact_id);
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
// Set the response and exit
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {get} api/contacts/search/:keysearch Search Contact Information
* @apiVersion 0.1.0
* @apiName GetContactSearch
* @apiGroup Contacts
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} keysearch Search Keywords
*
* @apiSuccess {Object} Contact Contact information
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "id": "8",
* "userid": "1",
* "is_primary": "0",
* "firstname": "chirag",
* "lastname": "jagani",
* "email": "useremail@gmail.com",
* "phonenumber": "",
* "title": null,
* "datecreated": "2020-05-19 20:07:49",
* "password": "$2a$08$6DLJFalqvJGVymCwW2ppNe9HOG5YUP04vzthXZjOFFUQknxfG6QHe",
* "new_pass_key": null,
* "new_pass_key_requested": null,
* "email_verified_at": "2020-08-28 21:36:06",
* "email_verification_key": null,
* "email_verification_sent_at": null,
* "last_ip": null,
* "last_login": null,
* "last_password_change": null,
* "active": "1",
* "profile_image": null,
* "direction": null,
* "invoice_emails": "0",
* "estimate_emails": "0",
* "credit_note_emails": "0",
* "contract_emails": "0",
* "task_emails": "0",
* "project_emails": "0",
* "ticket_emails": "0",
* "company": "trueline",
* "vat": "",
* "country": "0",
* "city": "",
* "zip": "",
* "state": "",
* "address": "",
* "website": "",
* "leadid": null,
* "billing_street": "",
* "billing_city": "",
* "billing_state": "",
* "billing_zip": "",
* "billing_country": "0",
* "shipping_street": "",
* "shipping_city": "",
* "shipping_state": "",
* "shipping_zip": "",
* "shipping_country": "0",
* "longitude": null,
* "latitude": null,
* "default_language": "english",
* "default_currency": "0",
* "show_primary_contact": "0",
* "stripe_id": null,
* "registration_confirmed": "1",
* "addedfrom": "1"
* }
*
* @apiError {Boolean} status Request status
* @apiError {String} message No data were found
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_search_get($key = '') {
// If the id parameter doesn't exist return all the
$data = $this->Api_model->search('contacts', $key);
// Check if the data store contains
if ($data) {
$data = $this->Api_model->get_api_custom_data($data, "contacts");
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
// Set the response and exit
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {post} api/contacts/ Add New Contact
* @apiVersion 0.1.0
* @apiName PostContact
* @apiGroup Contacts
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {Number} customer_id Mandatory Customer id.
* @apiParam {String} firstname Mandatory First Name
* @apiParam {String} lastname Mandatory Last Name
* @apiParam {String} email Mandatory E-mail
* @apiParam {String} [title] Optional Position
* @apiParam {String} [phonenumber] Optional Phone Number
* @apiParam {String} [direction = 'rtl'] Optional Direction (rtl or ltr)
* @apiParam {String} [password] Optional password (only required if you pass send_set_password_email parameter)
* @apiParam {String} [is_primary = 'on'] Optional Primary Contact (set on or don't pass it)
* @apiParam {String} [donotsendwelcomeemail] Optional Do Not Send Welcome Email (set on or don't pass it)
* @apiParam {String} [send_set_password_email] Optional Send Set Password Email (set on or don't pass it)
* @apiParam {Array} [permissions] Optional Permissions for this contact(["1", "2", "3", "4", "5", "6" ])<br/>
* [<br/>
* "1", // Invoices permission<br/>
* "2", // Estimates permission<br/>
* "3", // Contracts permission<br/>
* "4", // Proposals permission<br/>
* "5", // Support permission<br/>
* "6" // Projects permission<br/>
* ]
* @apiParam {String} [invoice_emails = "invoice_emails"] Optional E-Mail Notification for Invoices (set value same as name or don't pass it)
* @apiParam {String} [estimate_emails = "estimate_emails"] Optional E-Mail Notification for Estimate (set value same as name or don't pass it)
* @apiParam {String} [credit_note_emails = "credit_note_emails"] Optional E-Mail Notification for Credit Note (set value same as name or don't pass it)
* @apiParam {String} [project_emails = "project_emails"] Optional E-Mail Notification for Project (set value same as name or don't pass it)
* @apiParam {String} [ticket_emails = "ticket_emails"] Optional E-Mail Notification for Tickets (set value same as name or don't pass it)
* @apiParam {String} [task_emails = "task_emails"] Optional E-Mail Notification for Task (set value same as name or don't pass it)
* @apiParam {String} [contract_emails ="contract_emails"] Optional E-Mail Notification for Contract (set value same as name or don't pass it)
*
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Contact added successfully.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Contact added successfully"
* }
*
* @apiError {Boolean} status Request status
* @apiError {String} message Contact add fail
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Contact add fail"
* }
*
* @apiError {String} email This Email is already exists
*
* @apiErrorExample Error-Response:
* HTTP/1.1 409 Conflict
* {
* "status": false,
* "error": {
* "email":"This Email is already exists"
* },
* "message": "This Email is already exists"
* }
*/
public function data_post() {
$data = $this->input->post();
$send_set_password_email = isset($data['send_set_password_email']) ? true : false;
if ($send_set_password_email) {
unset($data['password']);
}
$this->form_validation->set_rules('firstname', 'First Name', 'trim|required|max_length[255]');
$this->form_validation->set_rules('lastname', 'Last Name', 'trim|required|max_length[255]');
$this->form_validation->set_rules('email', 'Email', 'trim|required|max_length[255]|is_unique[' . db_prefix() . 'contacts.email]', array('is_unique' => 'This %s is already exists'));
if ($send_set_password_email) {
$this->form_validation->set_rules('password', 'Password', 'trim|required|max_length[255]');
}
$this->form_validation->set_rules('customer_id', 'Customer Id', 'trim|required|numeric|callback_client_id_check');
if ($this->form_validation->run() == FALSE) {
$message = array('status' => FALSE, 'error' => $this->form_validation->error_array(), 'message' => validation_errors());
$this->response($message, REST_Controller::HTTP_CONFLICT);
} else {
$customer_id = $data['customer_id'];
unset($data['customer_id']);
$id = $this->clients_model->add_contact($data, $customer_id);
if ($id > 0 && !empty($id)) {
$message = array(
'status' => TRUE,
'message' => 'Contact added successfully.',
'record_id' => $id
);
$this->response($message, REST_Controller::HTTP_OK);
}
else {
// error
$message = array('status' => FALSE, 'message' => 'Contact add fail.');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {delete} api/delete/contacts/:id Delete Contact
* @apiVersion 0.1.0
* @apiName DeleteContact
* @apiGroup Contacts
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {Number} customer_id unique Customer id
*
* @apiSuccess {String} status Request status.
* @apiSuccess {String} message Contact Deleted Successfully
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Contact Deleted Successfully"
* }
*
* @apiError {Boolean} status Request status
* @apiError {String} message Contact Delete Fail
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Contact Delete Fail"
* }
*/
public function data_delete($customer_id = '') {
$id = $this->security->xss_clean($customer_id);
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid Contact ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$is_exist = $this->clients_model->get_contact($id);
if (is_object($is_exist)) {
$output = $this->clients_model->delete_contact($id);
if ($output === TRUE) {
// success
$message = array('status' => TRUE, 'message' => 'Contact Deleted Successfuly.');
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('status' => FALSE, 'message' => 'Contact Delete Fail.');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
} else {
$message = array('status' => FALSE, 'message' => 'Invalid Contact ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {put} api/contacts/:id Update Contact Information
* @apiVersion 0.1.0
* @apiName PutContact
* @apiGroup Contacts
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {Number} id Mandatory Customer Contact id.
* @apiParam {String} firstname Mandatory First Name
* @apiParam {String} lastname Mandatory Last Name
* @apiParam {String} email Mandatory E-mail
* @apiParam {String} [title] Optional Position
* @apiParam {String} [phonenumber] Optional Phone Number
* @apiParam {String} [direction = 'rtl'] Optional Direction (rtl or ltr)
* @apiParam {String} [password] Optional password (only required if you pass send_set_password_email parameter)
* @apiParam {String} [is_primary = 'on'] Optional Primary Contact (set on or don't pass it)
* @apiParam {String} [donotsendwelcomeemail] Optional Do Not Send Welcome Email (set on or don't pass it)
* @apiParam {String} [send_set_password_email] Optional Send Set Password Email (set on or don't pass it)
* @apiParam {Array} [permissions] Optional Permissions for this contact(["1", "2", "3", "4", "5", "6" ])<br/>
* [<br/>
* "1", // Invoices permission<br/>
* "2", // Estimates permission<br/>
* "3", // Contracts permission<br/>
* "4", // Proposals permission<br/>
* "5", // Support permission<br/>
* "6" // Projects permission<br/>
* ]
* @apiParam {String} [invoice_emails = "invoice_emails"] Optional E-Mail Notification for Invoices (set value same as name or don't pass it)
* @apiParam {String} [estimate_emails = "estimate_emails"] Optional E-Mail Notification for Estimate (set value same as name or don't pass it)
* @apiParam {String} [credit_note_emails = "credit_note_emails"] Optional E-Mail Notification for Credit Note (set value same as name or don't pass it)
* @apiParam {String} [project_emails = "project_emails"] Optional E-Mail Notification for Project (set value same as name or don't pass it)
* @apiParam {String} [ticket_emails = "ticket_emails"] Optional E-Mail Notification for Tickets (set value same as name or don't pass it)
* @apiParam {String} [task_emails = "task_emails"] Optional E-Mail Notification for Task (set value same as name or don't pass it)
* @apiParam {String} [contract_emails ="contract_emails"] Optional E-Mail Notification for Contract (set value same as name or don't pass it)
*
* @apiParamExample {json} Request-Example:
* {
* "firstname":"new first name",
* "lastname":"new last name",
* "email":"dummy@gmail.com",
* "title":"",
* "phonenumber":"9909999099",
* "direction":"rtl",
* "password":"123456",
* "is_primary":"on",
* "send_set_password_email":"on",
* "permissions":["1", "2", "3", "4", "5", "6" ],
* "invoice_emails":"invoice_emails",
* "estimate_emails":"estimate_emails",
* "credit_note_emails":"credit_note_emails",
* "project_emails":"project_emails",
* "ticket_emails":"ticket_emails",
* "task_emails":"task_emails",
* "contract_emails":"contract_emails"
* }
*
* @apiSuccess {Boolean} status Request status
* @apiSuccess {String} message Contact updated successful
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Contact Updated Successfully"
* }
*
* @apiError {String} email This Email is already exists
*
* @apiErrorExample Error-Response:
* HTTP/1.1 409 Conflict
* {
* "status": false,
* "error": {
* "email":"This Email is already exists"
* },
* "message": "This Email is already exists"
* }
* @apiError {Boolean} status Request status
* @apiError {String} message Contact add fail
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Contact Update fail"
* }
*
*/
public function data_put($id = '') {
// JSON data is now automatically parsed in REST_Controller
if (empty($_POST) || !isset($_POST)) {
$message = array('status' => FALSE, 'message' => 'Data Not Acceptable OR Not Provided');
$this->response($message, REST_Controller::HTTP_NOT_ACCEPTABLE);
}
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid Client ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$this->load->model('authentication_model');
$data = $this->input->post();
$is_exist = $this->clients_model->get_contact($id);
if (!is_object($is_exist)) {
$message = array('status' => FALSE, 'message' => 'Contact ID Doesn\'t Not Exist.');
$this->response($message, REST_Controller::HTTP_CONFLICT);
}
$_current_email = $this->db->where('id', $id)->get(db_prefix() . 'contacts')->row();
if ($_current_email->email == $this->input->post('email')) {
$this->form_validation->set_rules('email', 'Email', 'trim|required|max_length[255]');
} else {
$this->form_validation->set_rules('email', 'Email', 'trim|required|max_length[255]|is_unique[' . db_prefix() . 'contacts.email]', array('is_unique' => 'This %s is already exists'));
}
if ($this->form_validation->run() == FALSE) {
$message = array('status' => FALSE, 'error' => $this->form_validation->error_array(), 'message' => validation_errors());
$this->response($message, REST_Controller::HTTP_CONFLICT);
}
$success = $this->clients_model->update_contact($data, $id);
$updated = false;
if (is_array($success)) {
if (isset($success['set_password_email_sent'])) {
$message_str = _l('set_password_email_sent_to_client');
} elseif (isset($success['set_password_email_sent_and_profile_updated'])) {
$updated = true;
$message_str = _l('set_password_email_sent_to_client_and_profile_updated');
}
} else {
if ($success == true) {
$updated = true;
$message_str = "Contact Updated Successfully";
}
}
if ($updated == true) {
$message = array('status' => TRUE, 'message' => $message_str);
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('status' => FALSE, 'message' => 'Client Update Fail.');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
public function client_id_check($customer_id) {
$this->form_validation->set_message('client_id_check', 'The {field} is Invalid');
if (empty($customer_id)) {
return FALSE;
}
$query = $this->db->get_where(db_prefix() . 'clients', array('userid' => $customer_id));
return $query->num_rows() > 0;
}
}

View File

@@ -0,0 +1,515 @@
j<?php
defined('BASEPATH') OR exit('No direct script access allowed');
// This can be removed if you use __autoload() in config.php OR use Modular Extensions
/** @noinspection PhpIncludeInspection */
require __DIR__ . '/REST_Controller.php';
/**
* This is an example of a few basic user interaction methods you could use
* all done with a hardcoded array
*
* @package CodeIgniter
* @subpackage Rest Server
* @category Controller
* @author Phil Sturgeon, Chris Kacerguis
* @license MIT
* @link https://github.com/chriskacerguis/codeigniter-restserver
*/
class Contracts extends REST_Controller {
function __construct() {
// Construct the parent class
parent::__construct();
}
/**
* @api {get} api/contracts/:id Request Contract information
* @apiVersion 0.3.0
* @apiName GetContract
* @apiGroup Contracts
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiParam {Number} id Contact unique ID
*
* @apiSuccess {Object} Contracts information.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "id": "1",
* "content": "",
* "description": "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.",
* "subject": "New Contract",
* "client": "9",
* "datestart": "2022-11-21",
* "dateend": "2027-11-21",
* "contract_type": "1",
* "project_id": "0",
* "addedfrom": "1",
* "dateadded": "2022-11-21 12:45:58",
* "isexpirynotified": "0",
* "contract_value": "13456.00",
* "trash": "0",
* "not_visible_to_client": "0",
* "hash": "31caaa36b9ea1f45a688c7e859d3ae70",
* "signed": "0",
* "signature": null,
* "marked_as_signed": "0",
* "acceptance_firstname": null,
* "acceptance_lastname": null,
* "acceptance_email": null,
* "acceptance_date": null,
* "acceptance_ip": null,
* "short_link": null,
* "name": "Development Contracts",
* "userid": "9",
* "company": "8web",
* "vat": "",
* "phonenumber": "",
* "country": "0",
* "city": "",
* "zip": "",
* "state": "",
* "address": "",
* "website": "",
* "datecreated": "2022-08-11 14:07:26",
* "active": "1",
* "leadid": null,
* "billing_street": "",
* "billing_city": "",
* "billing_state": "",
* "billing_zip": "",
* "billing_country": "0",
* "shipping_street": "",
* "shipping_city": "",
* "shipping_state": "",
* "shipping_zip": "",
* "shipping_country": "0",
* "longitude": null,
* "latitude": null,
* "default_language": "",
* "default_currency": "0",
* "show_primary_contact": "0",
* "stripe_id": null,
* "registration_confirmed": "1",
* "type_name": "Development Contracts",
* "attachments": [],
* "customfields": [],
* }
*/
public function data_get($id = '') {
// If the id parameter doesn't exist return all the
$data = $this->Api_model->get_table('contracts', $id);
// Check if the data store contains
if ($data) {
$data = $this->Api_model->get_api_custom_data($data, "contract", $id);
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
// Set the response and exit
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {delete} api/contracts/:id Delete Contract
* @apiVersion 0.3.0
* @apiName DeleteContract
* @apiGroup Contracts
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Contract Deleted Successfully
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Contract Deleted Successfully"
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Contract Delete Fail
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Contract Delete Fail"
* }
*/
public function data_delete($id = '') {
$id = $this->security->xss_clean($id);
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid Contract ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$this->load->model('contracts_model');
$is_exist = $this->contracts_model->get($id);
if (is_object($is_exist)) {
$output = $this->contracts_model->delete($id);
if ($output === TRUE) {
// success
$message = array('status' => TRUE, 'message' => 'Contract Deleted Successfully');
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('status' => FALSE, 'message' => 'Contract Delete Fail');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
} else {
$message = array('status' => FALSE, 'message' => 'Invalid Contract ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {post} api/contracts Add New Contract
* @apiVersion 0.3.0
* @apiName PostContract
* @apiGroup Contracts
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} subject Mandatory. Contract subject
* @apiParam {Date} datestart Mandatory. Contract start date
* @apiParam {Number} client Mandatory. Customer ID
* @apiParam {Date} dateend Optional. Contract end date
* @apiParam {Number} contract_type Optional. Contract type
* @apiParam {Number} contract_value Optional. Contract value
* @apiParam {String} description Optional. Contract description
* @apiParam {String} content Optional. Contract content
*
* @apiParamExample {Multipart Form} Request-Example:
* [
* "subject"=>"Subject of the Contract,
* "datestart"=>"2022-11-11",
* "client"=>1,
* "dateend"=>"2023-11-11",
* "contract_type"=>1,
* "contract_value"=>12345,
* "description"=>"Lorem Ipsum is simply dummy text of the printing and typesetting industry",
* "content"=>"It has been the industry's standard dummy text ever since the 1500s"
* ]
*
*
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Contracts Added Successfully
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Contract Added Successfully"
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Contract add fail
* @apiError {String} message The Start date field is required
* @apiError {String} message The Subject field is required
* @apiError {String} message The Customer ID field is required
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Contract ID Exists"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "error": {
* "newitems[]": "The Start date field is required"
* },
* "message": "<p>The Start date field is required</p>\n"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "error": {
* "subtotal": "The Subject field is required"
* },
* "message": "<p>The Subject field is required</p>\n"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "error": {
* "total": "The Customer ID is required"
* },
* "message": "<p>The Customer ID is required</p>\n"
* }
*
*/
public function data_post() {
\modules\api\core\Apiinit::the_da_vinci_code('api');
$data = $this->input->post();
$this->form_validation->set_rules('id', 'Contract ID', 'trim|numeric|greater_than[0]');
$this->form_validation->set_rules('content', 'Content', 'trim');
$this->form_validation->set_rules('description', 'Description', 'trim');
$this->form_validation->set_rules('subject', 'Subject', 'trim|required');
$this->form_validation->set_rules('client', 'Customer ID', 'trim|required|numeric|greater_than[0]');
$this->form_validation->set_rules('contract_value', 'Contract Value', 'numeric');
$this->form_validation->set_rules('datestart', 'Start date', 'trim|required|max_length[255]');
$this->form_validation->set_rules('dateend', 'End date', 'trim|max_length[255]');
$this->form_validation->set_rules('contract_type', 'Contract type', 'trim|numeric|greater_than[0]');
if ($this->form_validation->run() == FALSE) {
$message = array('status' => FALSE, 'error' => $this->form_validation->error_array(), 'message' => validation_errors());
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$this->load->model('contracts_model');
$output = $this->contracts_model->add($data);
if ($output > 0 && !empty($output)) {
$this->handle_contract_attachments_array($output);
$message = array(
'status' => TRUE,
'message' => 'Contract Added Successfully',
'record_id' => $output
);
$this->response($message, REST_Controller::HTTP_OK);
}
else {
// error
$message = array('status' => FALSE, 'message' => 'Contract Add Fail');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {post} api/contracts Add New Contract
* @apiVersion 0.3.0
* @apiName PostContract
* @apiGroup Contracts
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} subject Mandatory. Contract subject
* @apiParam {Date} datestart Mandatory. Contract start date
* @apiParam {Number} client Mandatory. Customer ID
* @apiParam {Date} dateend Optional. Contract end date
* @apiParam {Number} contract_type Optional. Contract type
* @apiParam {Number} contract_value Optional. Contract value
* @apiParam {String} description Optional. Contract description
* @apiParam {String} content Optional. Contract content
*
* @apiParamExample {Multipart Form} Request-Example:
* [
* "subject"=>"Subject of the Contract,
* "datestart"=>"2022-11-11",
* "client"=>1,
* "dateend"=>"2023-11-11",
* "contract_type"=>1,
* "contract_value"=>12345,
* "description"=>"Lorem Ipsum is simply dummy text of the printing and typesetting industry",
* "content"=>"It has been the industry's standard dummy text ever since the 1500s"
* ]
*
*
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Contracts Added Successfully
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Contract Added Successfully"
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Contract add fail
* @apiError {String} message The Start date field is required
* @apiError {String} message The Subject field is required
* @apiError {String} message The Customer ID field is required
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Contract ID Exists"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "error": {
* "newitems[]": "The Start date field is required"
* },
* "message": "<p>The Start date field is required</p>\n"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "error": {
* "subtotal": "The Subject field is required"
* },
* "message": "<p>The Subject field is required</p>\n"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "error": {
* "total": "The Customer ID is required"
* },
* "message": "<p>The Customer ID is required</p>\n"
* }
*
*/
public function data_put($id = '') {
// JSON data is now automatically parsed in REST_Controller
if (empty($_POST) || !isset($_POST)) {
$this->load->library('parse_input_stream');
$_POST = $this->parse_input_stream->parse_parameters();
$_FILES = $this->parse_input_stream->parse_files();
if (empty($_POST) || !isset($_POST)) {
$message = array('status' => FALSE, 'message' => 'Data Not Acceptable OR Not Provided');
$this->response($message, REST_Controller::HTTP_NOT_ACCEPTABLE);
}
}
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid Lead ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$update_data = $this->input->post();
$update_file = isset($update_data['file']) ? $update_data['file'] : null;
unset($update_data['file']);
$this->load->model('contracts_model');
$output = $this->contracts_model->update($update_data, $id);
if (!empty($update_file) && count($update_file)) {
if ($output <= 0 || empty($output)) {
$output = $id;
}
}
if ($output > 0 && !empty($output)) {
// success
$attachments = $this->contracts_model->get_contract_attachments('', $output);
if ($attachments) {
foreach ($attachments as $attachment) {
$this->contracts_model->delete_contract_attachment($attachment['id']);
}
}
$this->handle_contract_attachments_array($output);
$message = array('status' => TRUE, 'message' => 'Contract Update Successfully');
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('status' => FALSE, 'message' => 'Contract Update Fail');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
public function validate_contract_number($number, $contractid) {
$isedit = 'false';
if (!empty($contractid)) {
$isedit = 'true';
}
$this->form_validation->set_message('validate_contract_number', 'The {field} is already in use');
$original_number = null;
$date = $this->input->post('date');
if (!empty($contractid)) {
$data = $this->Api_model->get_table('contracts', $contractid);
$original_number = $data->number;
if (empty($date)) {
$date = $data->date;
}
}
$number = trim($number);
$number = ltrim($number, '0');
if ($isedit == 'true') {
if ($number == $original_number) {
return TRUE;
}
}
if (total_rows(db_prefix() . 'contracts', ['YEAR(date)' => date('Y', strtotime(to_sql_date($date))), 'number' => $number, ]) > 0) {
return FALSE;
} else {
return TRUE;
}
}
function handle_contract_attachments_array($contract_id, $index_name = 'file') {
$path = get_upload_path_by_type('contract') . $contract_id . '/';
$CI = & get_instance();
if (isset($_FILES[$index_name]['name']) && ($_FILES[$index_name]['name'] != '' || is_array($_FILES[$index_name]['name']) && count($_FILES[$index_name]['name']) > 0)) {
if (!is_array($_FILES[$index_name]['name'])) {
$_FILES[$index_name]['name'] = [$_FILES[$index_name]['name']];
$_FILES[$index_name]['type'] = [$_FILES[$index_name]['type']];
$_FILES[$index_name]['tmp_name'] = [$_FILES[$index_name]['tmp_name']];
$_FILES[$index_name]['error'] = [$_FILES[$index_name]['error']];
$_FILES[$index_name]['size'] = [$_FILES[$index_name]['size']];
}
_file_attachments_index_fix($index_name);
for ($i = 0; $i < count($_FILES[$index_name]['name']); $i++) {
// Get the temp file path
$tmpFilePath = $_FILES[$index_name]['tmp_name'][$i];
// Make sure we have a filepath
if (!empty($tmpFilePath) && $tmpFilePath != '') {
if (_perfex_upload_error($_FILES[$index_name]['error'][$i]) || !_upload_extension_allowed($_FILES[$index_name]['name'][$i])) {
continue;
}
_maybe_create_upload_path($path);
$filename = unique_filename($path, $_FILES[$index_name]['name'][$i]);
$newFilePath = $path . $filename;
// Upload the file into the temp dir
if (copy($tmpFilePath, $newFilePath)) {
unlink($tmpFilePath);
$data = [];
$data[] = ['file_name' => $filename, 'filetype' => $_FILES[$index_name]['type'][$i], ];
$this->add_attachment_to_database($contract_id, $data, false);
}
}
}
}
return true;
}
function add_attachment_to_database($contract_id, $attachment, $external = false) {
$this->load->model('contracts_model');
$this->load->model('misc_model');
$this->misc_model->add_attachment_to_database($contract_id, 'contract', $attachment, $external);
$contract = $this->contracts_model->get($contract_id);
$not_user_ids = [];
if ($contract->addedfrom != get_staff_user_id()) {
array_push($not_user_ids, $contract->addedfrom);
}
$notifiedUsers = [];
foreach ($not_user_ids as $uid) {
$notified = add_notification([
'description' => 'not_contract_added_attachment',
'touserid' => $uid,
'link' => '#contractid=' . $contract_id,
'additional_data' => serialize([
$contract->subject,
]),
]);
if ($notified) {
array_push($notifiedUsers, $uid);
}
}
pusher_trigger_notification($notifiedUsers);
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace modules\api\scontrollers;
defined('BASEPATH') or exit('No direct script access allowed');
// Load the core Controller
require_once APPPATH.'core/Controller.php';
/**
* Base API Controller with Swagger support
*/
class Controller extends \CI_Controller
{
public function __construct()
{
parent::__construct();
}
protected function send_error($message, $code = 400)
{
$this->output
->set_status_header($code)
->set_content_type('application/json')
->set_output(json_encode([
'error' => $message,
'code' => $code
]));
}
}

View File

@@ -0,0 +1,687 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
// This can be removed if you use __autoload() in config.php OR use Modular Extensions
/** @noinspection PhpIncludeInspection */
require __DIR__ . '/REST_Controller.php';
/**
* This is an example of a few basic user interaction methods you could use
* all done with a hardcoded array
*
* @package CodeIgniter
* @subpackage Rest Server
* @category Controller
* @author Phil Sturgeon, Chris Kacerguis
* @license MIT
* @link https://github.com/chriskacerguis/codeigniter-restserver
*/
class Credit_notes extends REST_Controller {
function __construct() {
// Construct the parent class
parent::__construct();
}
/**
* @api {get} api/credit_notes/:id Request Credit notes information
* @apiVersion 0.3.0
* @apiName GetCreditNotes
* @apiGroup Credit Notes
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiSuccess {Object} Credit notes information.
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "id": "2",
* "clientid": "1",
* "deleted_customer_name": null,
* "number": "2",
* "prefix": "CN-",
* "number_format": "1",
* "datecreated": "2021-07-30 16:29:46",
* "date": "2021-08-02",
* "adminnote": "adminnote2",
* "terms": "",
* "clientnote": "",
* "currency": "1",
* "subtotal": "1200.00",
* "total_tax": "0.00",
* "total": "1200.00",
* "adjustment": "0.00",
* "addedfrom": "1",
* "status": "1",
* "project_id": "0",
* "discount_percent": "0.00",
* "discount_total": "0.00",
* "discount_type": "",
* "billing_street": "Test",
* "billing_city": "Test",
* "billing_state": "Test",
* "billing_zip": "3000",
* "billing_country": "102",
* "shipping_street": "Test",
* "shipping_city": "Test",
* "shipping_state": "Test",
* "shipping_zip": "3000",
* "shipping_country": "102",
* "include_shipping": "1",
* "show_shipping_on_credit_note": "1",
* "show_quantity_as": "1",
* "reference_no": "",
* "userid": "1",
* "company": "Test",
* "vat": "",
* "phonenumber": "01324568903",
* "country": "102",
* "city": "Test",
* "zip": "3000",
* "state": "Test",
* "address": "Test",
* "website": "",
* "active": "1",
* "leadid": null,
* "longitude": null,
* "latitude": null,
* "default_language": "",
* "default_currency": "0",
* "show_primary_contact": "0",
* "stripe_id": null,
* "registration_confirmed": "1",
* "credit_note_id": "2",
* "customfields": []
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_get($id = '') {
// If the id parameter doesn't exist return all the
$data = $this->Api_model->get_table('creditnotes', $id);
// Check if the data store contains
if ($data) {
$data = $this->Api_model->get_api_custom_data($data, "credit_note", $id);
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
// Set the response and exit
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {get} api/credit_notes/search/:keysearch Search credit notes item information
* @apiVersion 0.3.0
* @apiName GetCreditNotesSearch
* @apiGroup Credit Notes
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} keysearch Search Keywords
*
* @apiSuccess {Object} credit notes Information
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "id": "2",
* "clientid": "1",
* "deleted_customer_name": null,
* "number": "2",
* "prefix": "CN-",
* "number_format": "1",
* "datecreated": "2021-07-30 16:29:46",
* "date": "2021-08-02",
* "adminnote": "adminnote2",
* "terms": "",
* "clientnote": "",
* "currency": "1",
* "subtotal": "1200.00",
* "total_tax": "0.00",
* "total": "1200.00",
* "adjustment": "0.00",
* "addedfrom": "1",
* "status": "1",
* "project_id": "0",
* "discount_percent": "0.00",
* "discount_total": "0.00",
* "discount_type": "",
* "billing_street": "Test",
* "billing_city": "Test",
* "billing_state": "Test",
* "billing_zip": "3000",
* "billing_country": "102",
* "shipping_street": "Test",
* "shipping_city": "Test",
* "shipping_state": "Test",
* "shipping_zip": "3000",
* "shipping_country": "102",
* "include_shipping": "1",
* "show_shipping_on_credit_note": "1",
* "show_quantity_as": "1",
* "reference_no": "",
* "userid": "1",
* "company": "test",
* "vat": "",
* "phonenumber": "01324568903",
* "country": "102",
* "city": "Test",
* "zip": "3000",
* "state": "Test",
* "address": "Test",
* "website": "",
* "active": "1",
* "leadid": null,
* "longitude": null,
* "latitude": null,
* "default_language": "",
* "default_currency": "0",
* "show_primary_contact": "0",
* "stripe_id": null,
* "registration_confirmed": "1",
* "credit_note_id": "2",
* "customfields": []
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_search_get($key = '') {
$data = $this->Api_model->search('creditnotes', $key);
// Check if the data store contains
if ($data) {
$data = $this->Api_model->get_api_custom_data($data, "credit_note");
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
// Set the response and exit
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {delete} api/credit_notes/:id Delete Credit Note
* @apiVersion 0.3.0
* @apiName DeleteCreditNote
* @apiGroup Credit Notes
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Credit Note Deleted Successfully
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Credit Note Deleted Successfully"
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Credit Note Delete Fail
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Credit Note Delete Fail"
* }
*/
public function data_delete($id = '') {
$id = $this->security->xss_clean($id);
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid Credit Note ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$this->load->model('credit_notes_model');
$is_exist = $this->credit_notes_model->get($id);
if (is_object($is_exist)) {
$output = $this->credit_notes_model->delete($id);
if ($output === TRUE) {
// success
$message = array('status' => TRUE, 'message' => 'Credit Note Deleted Successfully');
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('status' => FALSE, 'message' => 'Credit Note Delete Fail');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
} else {
$message = array('status' => FALSE, 'message' => 'Invalid Credit Note ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {post} api/credit_notes Add New Credit Notes
* @apiVersion 0.3.0
* @apiName PostCredit_notes
* @apiGroup Credit Notes
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {Number} clientid Mandatory. Customer id
* @apiParam {Date} date Mandatory. Credit Note Date
* @apiParam {Number} number Mandatory. Credit Note Number
* @apiParam {Number} currency Mandatory. currency field
* @apiParam {Array} newitems Mandatory. New Items to be added
* @apiParam {String} billing_street Optional. Street Address
* @apiParam {String} [billing_city] Optional. City Name for billing
* @apiParam {String} [billing_state] Optional. Name of state for billing
* @apiParam {Number} [billing_zip] Optional. Zip code
* @apiParam {Number} [billing_country] Optional. Country code
* @apiParam {String} [shipping_street] Optional. Address of shipping
* @apiParam {String} [shipping_city] Optional. City name for shipping
* @apiParam {String} [shipping_state] Optional. Name of state for shipping
* @apiParam {Number} [shipping_zip] Optional. Zip code for shipping
* @apiParam {Number} [shipping_country] Optional. Country code
* @apiParam {String} [discount_type] Optional. before_tax / after_tax discount type
* @apiParam {String} [Admin Note] Optional. Admin Note
* @apiParam {Decimal} subtotal Mandatory. calculation based on item Qty, Rate and Tax
* @apiParam {Decimal} total Mandatory. calculation based on subtotal, Discount and
* @apiParam {String} [clientnote] Optional. client notes
* @apiParam {String} [terms] Optional. Terms
*
*
* @apiParamExample {Multipart Form} Request-Example:
* [
* "clientid" => 2
* "date" => 2021-08-20
* "number" => 2
* "newitems[0][description]" => item 1 description
* "newitems[0][long_description]" => item 1 long description
* "newitems[0][qty]" => 1
* "newitems[0][rate]" => 1200
* "newitems[0][order]" => 1
* "newitems[0][unit]" =>
* "newitems[0][unit]" =>
* "newitems[0][custom_fields][items][1]" => "new condition"
* "subtotal" => 1200.00
* "total" => 1200.00
* "currency" => 1
* "custom_fields"[credit_note][1]" => customfield_value
* ]
*
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Credit Note Added Successfully
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Credit Note Added Successfully"
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Credit Note add fail
* @apiError {String} newitems[] The Items field is required
* @apiError {String} number The Credit Note number is already in use
* @apiError {String} subtotal The Sub Total field is required
* @apiError {String} total The Total field is required
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Credit Note Add Fail"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 409 Conflict
* {
* "status": false,
* "error": {
* "number":"The Credit Note number is already in use"
* },
* "message": "The Credit Note number is already in use"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "error": {
* "newitems[]": "The Items field is required"
* },
* "message": "<p>The Items field is required</p>\n"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "error": {
* "subtotal": "The Sub Total field is required"
* },
* "message": "<p>The Sub Total field is required</p>\n"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "error": {
* "total": "The Total field is required"
* },
* "message": "<p>The Total field is required</p>\n"
* }
*
*/
public function data_post() {
\modules\api\core\Apiinit::the_da_vinci_code('api');
$data = $this->input->post();
$this->form_validation->set_rules('clientid', 'Customer', 'trim|required|numeric|greater_than[0]');
$this->form_validation->set_rules('project_id', 'Project', 'trim|numeric|greater_than[0]');
$this->form_validation->set_rules('currency', 'Currency', 'trim|required|numeric|greater_than[0]');
$this->form_validation->set_rules('date', 'Credit Note Date', 'trim|required|max_length[255]');
$this->form_validation->set_rules('newitems[]', 'Items', 'required');
$this->form_validation->set_rules('subtotal', 'Sub Total', 'trim|required|decimal|greater_than[0]');
$this->form_validation->set_rules('total', 'Total', 'trim|required|decimal|greater_than[0]');
$this->form_validation->set_rules('number', 'Credit Note Number', 'trim|required|numeric|callback_validate_creditnotes_number[0]');
if ($this->form_validation->run() == FALSE) {
$message = array('status' => FALSE, 'error' => $this->form_validation->error_array(), 'message' => validation_errors());
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$this->load->model('credit_notes_model');
$id = $this->credit_notes_model->add($data);
if ($id > 0 && !empty($id)) {
$message = array(
'status' => TRUE,
'message' => 'Credit Note Added Successfully',
'record_id' => $id
);
$this->response($message, REST_Controller::HTTP_OK);
}
else {
// error
$message = array('status' => FALSE, 'message' => 'Credit Note Add Fail');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
public function validate_creditnotes_number($number, $credit_notes_id) {
$isedit = 'false';
if (!empty($credit_notes_id)) {
$isedit = 'true';
}
$this->form_validation->set_message('validate_creditnotes_number', 'The {field} is already in use');
$original_number = null;
$date = $this->input->post('date');
if (!empty($credit_notes_id)) {
$data = $this->Api_model->get_table('creditnotes', $credit_notes_id);
$original_number = $data->number;
if (empty($date)) {
$date = $data->date;
}
}
$number = trim($number);
$number = ltrim($number, '0');
if ($isedit == 'true') {
if ($number == $original_number) {
return TRUE;
}
}
if (total_rows(db_prefix() . 'creditnotes', ['YEAR(date)' => date('Y', strtotime(to_sql_date($date))), 'number' => $number, ]) > 0) {
return FALSE;
} else {
return TRUE;
}
}
/**
* @api {put} api/credit_notes Update a Credit Note
* @apiVersion 0.3.0
* @apiName PutCredit_notes
* @apiGroup Credit Notes
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {Number} clientid Mandatory. Customer id
* @apiParam {Date} date Mandatory. Credit Note Date
* @apiParam {Number} number Mandatory. Credit Note Number
* @apiParam {Number} currency Mandatory. currency field
* @apiParam {Array} newitems Mandatory. New Items to be added
* @apiParam {Array} items Mandatory. Existing items with Id
* @apiParam {Array} removed_items Optional. Items to be removed
* @apiParam {Array} newitems Optional. New Items to be added
* @apiParam {String} billing_street Optional. Street Address
* @apiParam {String} [billing_city] Optional. City Name for billing
* @apiParam {String} [billing_state] Optional. Name of state for billing
* @apiParam {Number} [billing_zip] Optional. Zip code
* @apiParam {Number} [billing_country] Optional. Country code
* @apiParam {String} [shipping_street] Optional. Address of shipping
* @apiParam {String} [shipping_city] Optional. City name for shipping
* @apiParam {String} [shipping_state] Optional. Name of state for shipping
* @apiParam {Number} [shipping_zip] Optional. Zip code for shipping
* @apiParam {Number} [shipping_country] Optional. Country code
* @apiParam {String} [discount_type] Optional. before_tax / after_tax discount type
* @apiParam {String} [Admin Note] Optional. Admin Note
* @apiParam {Decimal} subtotal Mandatory. calculation based on item Qty, Rate and Tax
* @apiParam {Decimal} total Mandatory. calculation based on subtotal, Discount and
* @apiParam {String} [clientnote] Optional. client notes
* @apiParam {String} [terms] Optional. Terms
*
*
* @apiParamExample {json} Request-Example:
* {
* "clientid": 1,
* "date": "2021-08-20",
* "number": 1,
* "items":
* {
* "1":
* {
* "itemid": "25",
* "order": "1",
* "description": "item description",
* "long_description": "item long description",
* "qty": "1",
* "unit": "1",
* "rate": "10.00",
* "custom_fields":
* {
* "items":
* {
* "31": "test 12 item 1",
* "32": "10",
* "33": "Lorem Ipsum is simply dummy text of the printing and typesetting industry",
* "34": "Option 1",
* "35":
* [
* "Option 1",
* "Option 2"
* ],
* "36":
* [
* "Option 1",
* "Option 3"
* ],
* "37": "2021-05-06",
* "38": "2021-05-06 00:23:25",
* "39": "#ffffff",
* "40": "<a href=\"url.com\" target=\"_blank\">Link</a>"
* }
* }
* }
* },
* "newitems":
* {
* "2":
* {
* "order": "2",
* "description": "updated item 2 description",
* "long_description": "updated item 2 logn description",
* "qty": "1",
* "unit": "",
* "rate": "100.00",
* "custom_fields":
* {
* "items":
* {
* "31": "test 12 item 2",
* "32": "10",
* "33": "Lorem Ipsum is simply dummy text of the printing and typesetting industry",
* "34": "Option 1",
* "35":
* [
* "Option 1",
* "Option 2"
* ],
* "36":
* [
* "Option 1",
* "Option 3"
* ],
* "37": "2021-05-06",
* "38": "2021-05-06 00:23:25",
* "39": "#ffffff",
* "40": "<a href=\"url.com\" target=\"_blank\">Link</a>"
* }
* }
* }
* },
* "custom_fields":
* {
* "credit_note":
* {
* "93": "test 1254"
* }
* },
* "subtotal": "1200.00",
* "total": "1200.00",
* "currency": 1
* }
*
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Credit Note Updated Successfully
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Credit Note Updated Successfully"
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Credit Note Update Fail
* @apiError {String} newitems[] The Items field is required
* @apiError {String} number The Credit Note number is already in use
* @apiError {String} subtotal The Sub Total field is required
* @apiError {String} total The Total field is required
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Credit Note Update Fail"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 409 Conflict
* {
* "status": false,
* "error": {
* "number":"The Credit Note number is already in use"
* },
* "message": "The Credit Note number is already in use"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "error": {
* "newitems[]": "The Items field is required"
* },
* "message": "<p>The Items field is required</p>\n"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "error": {
* "subtotal": "The Sub Total field is required"
* },
* "message": "<p>The Sub Total field is required</p>\n"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "error": {
* "total": "The Total field is required"
* },
* "message": "<p>The Total field is required</p>\n"
* }
*
*/
public function data_put($id = "") {
// JSON data is now automatically parsed in REST_Controller
if (empty($_POST) || !isset($_POST)) {
$message = array('status' => FALSE, 'message' => 'Data Not Acceptable OR Not Provided');
$this->response($message, REST_Controller::HTTP_NOT_ACCEPTABLE);
}
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid Credit Note ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$this->form_validation->set_rules('clientid', 'Customer', 'trim|required|numeric|greater_than[0]');
$this->form_validation->set_rules('project_id', 'Project', 'trim|numeric|greater_than[0]');
$this->form_validation->set_rules('currency', 'Currency', 'trim|required|numeric|greater_than[0]');
$this->form_validation->set_rules('date', 'Credit Note Date', 'trim|required|max_length[255]');
$this->form_validation->set_rules('items[]', 'Items', 'required');
$this->form_validation->set_rules('subtotal', 'Sub Total', 'trim|required|decimal|greater_than[0]');
$this->form_validation->set_rules('total', 'Total', 'trim|required|decimal|greater_than[0]');
$this->form_validation->set_rules('number', 'Credit Note Number', 'trim|required|numeric|callback_validate_creditnotes_number[' . $id . ']');
if ($this->form_validation->run() == FALSE) {
$message = array('status' => FALSE, 'error' => $this->form_validation->error_array(), 'message' => validation_errors());
$this->response($message, REST_Controller::HTTP_CONFLICT);
} else {
$this->load->model('credit_notes_model');
$is_exist = $this->credit_notes_model->get($id);
if (!is_object($is_exist)) {
$message = array('status' => FALSE, 'message' => 'Credit Note ID Doesn\'t Not Exist.');
$this->response($message, REST_Controller::HTTP_CONFLICT);
}
if (is_object($is_exist)) {
$data = $this->input->post();
$data['isedit'] = "";
$success = $this->credit_notes_model->update($data, $id);
if ($success == true) {
$message = array('status' => TRUE, 'message' => "Credit Note Updated Successfully",);
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('status' => FALSE, 'message' => 'Credit Note Update Fail');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
} else {
$message = array('status' => FALSE, 'message' => 'Invalid Credit Note ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
}
}

View File

@@ -0,0 +1,767 @@
<?php
if (!defined('BASEPATH')) exit('No direct script access allowed');
require __DIR__ . '/REST_Controller.php';
class Custom_fields extends REST_Controller {
public function __construct() {
parent::__construct();
}
/**
* @api {get} api/custom_fields/:FieldBelongsto/:id Request Values of Custom Fields
* @apiVersion 0.2.0
* @apiName GetCustomFieldswithValue
* @apiGroup Custom Fields
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {string=<br/>"Company",<br/>"Leads",<br/>"Customers",<br/>"Contacts",<br/>"Staff",<br/>"Contracts",<br/>"Tasks",<br/>"Expenses",<br/>"Invoice",<br/>"Items",<br/>"Note",<br/>"Estimate",<br/>"Contract",<br/>"Proposal",<br/>"Projects",<br/>"Tickets"} FieldBelongsto Belongs to Mandatory Field Belongs to.
*
* @apiParam {Number} [id] Optional unique ID.
*
* @apiSuccess {Object} Custom Custom Fields information with values.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* [
* {
* "field_name": "custom_fields[invoice][1]",
* "custom_field_id": "1",
* "label": "Input 1",
* "required": "0",
* "type": "input",
* "value": "input1 data"
* },
* {
* "field_name": "custom_fields[invoice][2]",
* "custom_field_id": "2",
* "label": "Number 1",
* "required": "0",
* "type": "number",
* "value": "12"
* },
* {
* "field_name": "custom_fields[invoice][3]",
* "custom_field_id": "3",
* "label": "Textarea 1",
* "required": "0",
* "type": "textarea",
* "value": "textarea content"
* },
* {
* "field_name": "custom_fields[invoice][4]",
* "custom_field_id": "4",
* "label": "Select 1",
* "required": "0",
* "type": "select",
* "value": "[\"Option 1\"]",
* "options": "[\"Option 1\",\"Option 2\",\"Option 3\"]"
* },
* {
* "field_name": "custom_fields[invoice][5]",
* "custom_field_id": "5",
* "label": "Multiselect 1",
* "required": "0",
* "type": "multiselect",
* "value": "[\"Option 1\",\" Option 2\"]",
* "options": "[\"Option 1\",\"Option 2\",\"Option 3\"]"
* },
* {
* "field_name": "custom_fields[invoice][6]",
* "custom_field_id": "6",
* "label": "Checkbox 1",
* "required": "0",
* "type": "checkbox",
* "value": "[\"Option 1\",\" Option 2\"]",
* "options": "[\"Option 1\",\"Option 2\",\"Option 3\"]"
* },
* {
* "field_name": "custom_fields[invoice][7]",
* "custom_field_id": "7",
* "label": "Datepicker 1",
* "required": "0",
* "type": "date_picker",
* "value": "2021-05-16"
* },
* {
* "field_name": "custom_fields[invoice][8]",
* "custom_field_id": "8",
* "label": "Datetime Picker 1",
* "required": "0",
* "type": "date_picker_time",
* "value": "2021-05-25 23:06:00"
* },
* {
* "field_name": "custom_fields[invoice][9]",
* "custom_field_id": "9",
* "label": "Colorpicker 1",
* "required": "0",
* "type": "colorpicker",
* "value": "#8f1b1b"
* },
* {
* "field_name": "custom_fields[invoice][10]",
* "custom_field_id": "10",
* "label": "Hyperlink 1",
* "required": "0",
* "type": "link",
* "value": "<a href=\"https://google.com\" target=\"_blank\">google</a>"
* }
* ]
*
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_get($type = "", $id = "") {
$allowed_type = ["company", "leads", "customers", "contacts", "staff", "contracts", "tasks", "expenses", "invoice", "items", "credit_note", "estimate", "contract", "proposal", "projects", "tickets"];
if (empty($type) || !in_array($type, $allowed_type)) {
// Set the response and exit
$this->response(['status' => FALSE, 'message' => 'Not valid data'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
$fields = get_custom_fields($type);
$customfields = [];
foreach ($fields as $key => $field) {
$customfields[$key] = new stdclass();
$customfields[$key]->field_name = 'custom_fields[' . $field['fieldto'] . '][' . $field['id'] . ']';
$customfields[$key]->custom_field_id = $field['id'];
$customfields[$key]->label = $field['name'];
$customfields[$key]->required = $field['required'];
$customfields[$key]->type = $field['type'];
$customfields[$key]->value = get_custom_field_value($id, $field['id'], $type, false);
if (!empty($field['options'])) {
$customfields[$key]->value = json_encode(explode(',', $customfields[$key]->value));
$customfields[$key]->options = json_encode(explode(',', $field['options']));
}
}
$this->response($customfields, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
}
/**
* @api {POST} N/A Add Custom Fields
* @apiVersion 0.2.0
* @apiDescription Submit URL for POST request of the custom fields remains the same for each endpoint (ie `api/contacts` for Contacts endpoint, `api/invoices` for Invoices endpoint, etc..)
* <br> <h2>In this example, we will use the following form data which corresponds to the following custom field types:</h2>
`custom_fields[invoice][1]` = **Input Type**
<br> `custom_fields[invoice][2]` = **Number**
<br> `custom_fields[invoice][3]` = **Textarea**
<br> `custom_fields[invoice][4]` = **Radio**
<br> `custom_fields[invoice][5]` = **Checkbox**
<br> `custom_fields[invoice][6]` = **Multiselect**
<br> `custom_fields[invoice][7]` = **Date**
<br> `custom_fields[invoice][8]` = **Datetime**
<br> `custom_fields[invoice][9]` = **Color**
<br> `custom_fields[invoice][10]` = **Link**
* @apiName PostActionExample
* @apiGroup Custom Fields
*
* @apiParam {string/array} custom_fields[customFieldType] Custom Field Key should be same as `field_name` returned from **Search custom field values' information**
*
* @apiParamExample {Multipart Form} Request-Example:
* [
* custom_fields[invoice][1] => John Doe
* custom_fields[invoice][2] => 10
* custom_fields[invoice][3] => Lorem Ipsum is simply dummy text of the printing and typesetting industry.
* custom_fields[invoice][4] => Option 1
* custom_fields[invoice][5][] => Option 1
* custom_fields[invoice][5][] => Option 2
* custom_fields[invoice][6][] => Option 1
* custom_fields[invoice][6][] => Option 3
* custom_fields[invoice][7] => 2021-05-06
* custom_fields[invoice][8] => 2021-05-06 00:23:25
* custom_fields[invoice][9] => #FFFFFF
* custom_fields[invoice][10] => <a href="url.com" target="_blank">Link</a>
* ]
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* Same as Original request
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* Same as Original request
*/
/**
* @api {PUT} N/A Update Custom Fields
* @apiVersion 0.2.0
* @apiDescription Submit URL for PUT request of the custom fields remains the same for each endpoint (ie `api/contacts` for Contacts endpoint, `api/invoices` for Invoices endpoint, etc..)
* <br> <h2>In this example, we will use the following form data which corresponds to the following custom field types:</h2>
`custom_fields[invoice][1]` = **Input Type**
<br> `custom_fields[invoice][2]` = **Number**
<br> `custom_fields[invoice][3]` = **Textarea**
<br> `custom_fields[invoice][4]` = **Radio**
<br> `custom_fields[invoice][5]` = **Checkbox**
<br> `custom_fields[invoice][6]` = **Multiselect**
<br> `custom_fields[invoice][7]` = **Date**
<br> `custom_fields[invoice][8]` = **Datetime**
<br> `custom_fields[invoice][9]` = **Color**
<br> `custom_fields[invoice][10]` = **Link**
* @apiName PutActionExample
* @apiGroup Custom Fields
*
* @apiParam {string/array} custom_fields[customFieldType] Custom Field JSON should be same as below with `field_name` and `custom_field_id` returned from **Search custom field values' information**
*
* @apiParamExample {json} Request-Example:
* {
* "custom_fields":{
"invoice":{
"1":"test 12 item 1",
"2":"10",
"3":"Lorem Ipsum is simply dummy text of the printing and typesetting industry",
"4":"Option 1",
"5":["Option 1","Option 2"],
"6":["Option 1","Option 3"],
"7":"2021-05-06",
"8":"2021-05-06 00:23:25",
"9":"#ffffff",
"10":"<a href=\"url.com\" target=\"_blank\">Link</a>"
}
}
* }
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* Same as Original request
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* Same as Original request
*/
/**
* @api {GET} N/A Request Custom Fields
* @apiVersion 0.2.0
* @apiDescription Custom fields' data will be returned combined with other request's information during the initial GET request of each available endpoint (Contacts, Invoices etc) with their respective `label` and `value` key
* @apiName GetActionExample
* @apiGroup Custom Fields
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
{
"id": "1",
"sent": "0",
"datesend": null,
"clientid": "1",
"deleted_customer_name": null,
"number": "10",
"prefix": "INV-",
"number_format": "1",
"datecreated": "2021-05-14 00:44:52",
"date": "2021-08-28",
"duedate": "2021-09-27",
"currency": "1",
"subtotal": "110.00",
"total_tax": "0.00",
"total": "110.00",
"adjustment": "0.00",
"addedfrom": "0",
"hash": "4222d2f53404324ea73535d3c0f2c3f0",
"status": "1",
"clientnote": "",
"adminnote": "",
"last_overdue_reminder": null,
"cancel_overdue_reminders": "1",
"allowed_payment_modes": "a:2:{i:0;s:1:\"1\";i:1;s:1:\"2\";}",
"token": null,
"discount_percent": "0.00",
"discount_total": "0.00",
"discount_type": "",
"recurring": "0",
"recurring_type": null,
"custom_recurring": "0",
"cycles": "0",
"total_cycles": "0",
"is_recurring_from": null,
"last_recurring_date": null,
"terms": "",
"sale_agent": "0",
"billing_street": "billing address",
"billing_city": "billing city name",
"billing_state": "billing state name",
"billing_zip": "billing zip code",
"billing_country": "0",
"shipping_street": "shipping address",
"shipping_city": "city name",
"shipping_state": "state name",
"shipping_zip": "zip code",
"shipping_country": "0",
"include_shipping": "1",
"show_shipping_on_invoice": "1",
"show_quantity_as": "1",
"project_id": "0",
"subscription_id": "0",
"short_link": null,
"symbol": "$",
"name": "USD",
"decimal_separator": ".",
"thousand_separator": ",",
"placement": "before",
"isdefault": "1",
"currencyid": "1",
"currency_name": "USD",
"total_left_to_pay": "110.00",
"items": [
{
"id": "1",
"rel_id": "1",
"rel_type": "invoice",
"description": "item description",
"long_description": "item long description",
"qty": "1.00",
"rate": "10.00",
"unit": "1",
"item_order": "1",
"customfields": [
{
"label": "Input 1",
"value": "test 12 item 1"
},
{
"label": "Number 1",
"value": "10"
},
{
"label": "Textarea 1",
"value": "Lorem Ipsum is simply dummy text of the printing and typesetting industry"
},
{
"label": "Select 1",
"value": "Option 1"
},
{
"label": "Multiselect 1",
"value": "Option 1, Option 2"
},
{
"label": "Checkbox 1",
"value": "Option 1, Option 3"
},
{
"label": "Datepicker 1",
"value": "2021-05-06"
},
{
"label": "Datetime Picker 1",
"value": "2021-05-06 00:23:25"
},
{
"label": "Colorpicker 1",
"value": "#ffffff"
},
{
"label": "Hyperlink 1",
"value": "<a>Link</a>"
}
]
},
{
"id": "2",
"rel_id": "1",
"rel_type": "invoice",
"description": "updated item 2 description",
"long_description": "updated item 2 logn description",
"qty": "1.00",
"rate": "100.00",
"unit": "",
"item_order": "2",
"customfields": [
{
"label": "Input 1",
"value": "test 12 item 2"
},
{
"label": "Number 1",
"value": "10"
},
{
"label": "Textarea 1",
"value": "Lorem Ipsum is simply dummy text of the printing and typesetting industry"
},
{
"label": "Select 1",
"value": "Option 1"
},
{
"label": "Multiselect 1",
"value": "Option 1, Option 2"
},
{
"label": "Checkbox 1",
"value": "Option 1, Option 3"
},
{
"label": "Datepicker 1",
"value": "2021-05-06"
},
{
"label": "Datetime Picker 1",
"value": "2021-05-06 00:23:25"
},
{
"label": "Colorpicker 1",
"value": "#ffffff"
},
{
"label": "Hyperlink 1",
"value": "<a>Link</a>"
}
]
}
],
"attachments": [],
"visible_attachments_to_customer_found": false,
"client": {
"userid": "1",
"company": "updated company",
"vat": "",
"phonenumber": "",
"country": "0",
"city": "",
"zip": "",
"state": "",
"address": "",
"website": "",
"datecreated": "2021-05-14 00:15:06",
"active": "1",
"leadid": null,
"billing_street": "",
"billing_city": "",
"billing_state": "",
"billing_zip": "",
"billing_country": "0",
"shipping_street": "",
"shipping_city": "",
"shipping_state": "",
"shipping_zip": "",
"shipping_country": "0",
"longitude": null,
"latitude": null,
"default_language": "",
"default_currency": "0",
"show_primary_contact": "0",
"stripe_id": null,
"registration_confirmed": "1",
"addedfrom": "0"
},
"payments": [],
"scheduled_email": null,
"customfields": [
{
"label": "Input 1",
"value": "test 12"
},
{
"label": "Number 1",
"value": "10"
},
{
"label": "Textarea 1",
"value": "Lorem Ipsum is simply dummy text of the printing and typesetting industry"
},
{
"label": "Select 1",
"value": "Option 1"
},
{
"label": "Multiselect 1",
"value": "Option 1, Option 2"
},
{
"label": "Checkbox 1",
"value": "Option 1, Option 3"
},
{
"label": "Datepicker 1",
"value": "2021-05-06"
},
{
"label": "Datetime Picker 1",
"value": "2021-05-06 00:23:25"
},
{
"label": "Colorpicker 1",
"value": "#ffffff"
},
{
"label": "Hyperlink 1",
"value": "<a>Link</a>"
}
]
}
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* Same as Original request
*/
/**
* @api {GET} N/A Search custom field values' information
* @apiVersion 0.2.0
* @apiDescription Custom fields' data will be returned combined with other request's information during the initial SEARCH request of each available endpoint (Contacts, Invoices etc) with their respective `label` and `value` key
* @apiName SearchActionExample
* @apiGroup Custom Fields
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
[
{
"id": "1",
"sent": "0",
"datesend": null,
"clientid": "1",
"deleted_customer_name": null,
"number": "10",
"prefix": "INV-",
"number_format": "1",
"datecreated": "2021-05-14 00:15:06",
"date": "2021-08-28",
"duedate": "2021-09-27",
"currency": "1",
"subtotal": "110.00",
"total_tax": "0.00",
"total": "110.00",
"adjustment": "0.00",
"addedfrom": "0",
"hash": "4222d2f53404324ea73535d3c0f2c3f0",
"status": "1",
"clientnote": "",
"adminnote": "",
"last_overdue_reminder": null,
"cancel_overdue_reminders": "1",
"allowed_payment_modes": "a:2:{i:0;s:1:\"1\";i:1;s:1:\"2\";}",
"token": null,
"discount_percent": "0.00",
"discount_total": "0.00",
"discount_type": "",
"recurring": "0",
"recurring_type": null,
"custom_recurring": "0",
"cycles": "0",
"total_cycles": "0",
"is_recurring_from": null,
"last_recurring_date": null,
"terms": "",
"sale_agent": "0",
"billing_street": "",
"billing_city": "",
"billing_state": "",
"billing_zip": "",
"billing_country": "0",
"shipping_street": "",
"shipping_city": "",
"shipping_state": "",
"shipping_zip": "",
"shipping_country": "0",
"include_shipping": "1",
"show_shipping_on_invoice": "1",
"show_quantity_as": "1",
"project_id": "0",
"subscription_id": "0",
"short_link": null,
"userid": "1",
"company": "updated company",
"vat": "",
"phonenumber": "",
"country": "0",
"city": "",
"zip": "",
"state": "",
"address": "",
"website": "",
"active": "1",
"leadid": null,
"longitude": null,
"latitude": null,
"default_language": "",
"default_currency": "0",
"show_primary_contact": "0",
"stripe_id": null,
"registration_confirmed": "1",
"invoiceid": "1",
"customfields": [
{
"label": "Input 1",
"value": "test 12"
},
{
"label": "Number 1",
"value": "10"
},
{
"label": "Textarea 1",
"value": "Lorem Ipsum is simply dummy text of the printing and typesetting industry"
},
{
"label": "Select 1",
"value": "Option 1"
},
{
"label": "Multiselect 1",
"value": "Option 1, Option 2"
},
{
"label": "Checkbox 1",
"value": "Option 1, Option 3"
},
{
"label": "Datepicker 1",
"value": "2021-05-06"
},
{
"label": "Datetime Picker 1",
"value": "2021-05-06 00:23:25"
},
{
"label": "Colorpicker 1",
"value": "#ffffff"
},
{
"label": "Hyperlink 1",
"value": "<a>Link</a>"
}
]
},
{
"id": "2",
"sent": "0",
"datesend": null,
"clientid": "1",
"deleted_customer_name": null,
"number": "4",
"prefix": "INV-",
"number_format": "1",
"datecreated": "2021-05-14 00:15:06",
"date": "2021-05-28",
"duedate": "2021-06-27",
"currency": "1",
"subtotal": "110.00",
"total_tax": "0.00",
"total": "110.00",
"adjustment": "0.00",
"addedfrom": "0",
"hash": "630f8cc7ed2e6a70c4113ab24041bdf5",
"status": "6",
"clientnote": "",
"adminnote": "",
"last_overdue_reminder": null,
"cancel_overdue_reminders": "1",
"allowed_payment_modes": "a:2:{i:0;s:1:\"1\";i:1;s:1:\"2\";}",
"token": null,
"discount_percent": "0.00",
"discount_total": "0.00",
"discount_type": "",
"recurring": "0",
"recurring_type": null,
"custom_recurring": "0",
"cycles": "0",
"total_cycles": "0",
"is_recurring_from": null,
"last_recurring_date": null,
"terms": "",
"sale_agent": "0",
"billing_street": "",
"billing_city": "",
"billing_state": "",
"billing_zip": "",
"billing_country": "0",
"shipping_street": "",
"shipping_city": "",
"shipping_state": "",
"shipping_zip": "",
"shipping_country": "0",
"include_shipping": "1",
"show_shipping_on_invoice": "1",
"show_quantity_as": "1",
"project_id": "0",
"subscription_id": "0",
"short_link": null,
"userid": "1",
"company": "updated company",
"vat": "",
"phonenumber": "",
"country": "0",
"city": "",
"zip": "",
"state": "",
"address": "",
"website": "",
"active": "1",
"leadid": null,
"longitude": null,
"latitude": null,
"default_language": "",
"default_currency": "0",
"show_primary_contact": "0",
"stripe_id": null,
"registration_confirmed": "1",
"invoiceid": "2",
"customfields": [
{
"label": "Input 1",
"value": "test 12"
},
{
"label": "Number 1",
"value": "10"
},
{
"label": "Textarea 1",
"value": "Lorem Ipsum is simply dummy text of the printing and typesetting industry"
},
{
"label": "Select 1",
"value": "Option 1"
},
{
"label": "Multiselect 1",
"value": "Option 1, Option 2"
},
{
"label": "Checkbox 1",
"value": "Option 1, Option 3"
},
{
"label": "Datepicker 1",
"value": "2021-05-06"
},
{
"label": "Datetime Picker 1",
"value": "2021-05-06 00:23:25"
},
{
"label": "Colorpicker 1",
"value": "#ffffff"
},
{
"label": "Hyperlink 1",
"value": "<a>Link</a>"
}
]
}
]
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* Same as Original request
*/
/**
* @api {DELETE} N/A Delete Custom Fields
* @apiVersion 0.2.0
* @apiDescription To remove particular custom field value you can use **Update** action and an **empty** value in the custom field.<br /> Note: When you delete any record the corresponding custom field data will be **automatically deleted**.
* @apiName DeleteActionExample
* @apiGroup Custom Fields
*
*/
}
/* End of file Custom_fields.php */
/* Location: ./application/controllers/Custom_fields.php */

View File

@@ -0,0 +1,420 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
// This can be removed if you use __autoload() in config.php OR use Modular Extensions
/** @noinspection PhpIncludeInspection */
require __DIR__ . '/REST_Controller.php';
/**
* This is an example of a few basic user interaction methods you could use
* all done with a hardcoded array
*
* @package CodeIgniter
* @subpackage Rest Server
* @category Controller
* @author Phil Sturgeon, Chris Kacerguis
* @license MIT
* @link https://github.com/chriskacerguis/codeigniter-restserver
*/
class Customers extends REST_Controller {
function __construct() {
// Construct the parent class
parent::__construct();
}
/**
* @api {get} api/customers/:id Request customer information
* @apiName GetCustomer
* @apiGroup Customers
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {Number} id customer unique ID.
*
* @apiSuccess {Object} customer information.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "id": "28",
* "name": "Test1",
* "description": null,
* "status": "1",
* "clientid": "11",
* "billing_type": "3",
* "start_date": "2019-04-19",
* "deadline": "2019-08-30",
* "customer_created": "2019-07-16",
* "date_finished": null,
* "progress": "0",
* "progress_from_tasks": "1",
* "customer_cost": "0.00",
* "customer_rate_per_hour": "0.00",
* "estimated_hours": "0.00",
* "addedfrom": "5",
* "rel_type": "customer",
* "potential_revenue": "0.00",
* "potential_margin": "0.00",
* "external": "E",
* ...
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_get($id = '') {
// If the id parameter doesn't exist return all the
$data = $this->Api_model->get_table('clients', $id);
// Check if the data store contains
if ($data) {
$data = $this->Api_model->get_api_custom_data($data, "customers", $id);
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
// Set the response and exit
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {get} api/customers/search/:keysearch Search Customer Information
* @apiName GetCustomerSearch
* @apiGroup Customers
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} keysearch Search Keywords.
*
* @apiSuccess {Object} customer information.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "id": "28",
* "name": "Test1",
* "description": null,
* "status": "1",
* "clientid": "11",
* "billing_type": "3",
* "start_date": "2019-04-19",
* "deadline": "2019-08-30",
* "customer_created": "2019-07-16",
* "date_finished": null,
* "progress": "0",
* "progress_from_tasks": "1",
* "customer_cost": "0.00",
* "customer_rate_per_hour": "0.00",
* "estimated_hours": "0.00",
* "addedfrom": "5",
* "rel_type": "customer",
* "potential_revenue": "0.00",
* "potential_margin": "0.00",
* "external": "E",
* ...
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_search_get($key = '') {
// If the id parameter doesn't exist return all the
$data = $this->Api_model->search('customer', $key);
// Check if the data store contains
if ($data) {
$data = $this->Api_model->get_api_custom_data($data, "customers");
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
// Set the response and exit
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {post} api/customers Add New Customer
* @apiName PostCustomer
* @apiGroup Customers
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} company Mandatory Customer company.
* @apiParam {String} [vat] Optional Vat.
* @apiParam {String} [phonenumber] Optional Customer Phone.
* @apiParam {String} [website] Optional Customer Website.
* @apiParam {Number[]} [groups_in] Optional Customer groups.
* @apiParam {String} [default_language] Optional Customer Default Language.
* @apiParam {String} [default_currency] Optional default currency.
* @apiParam {String} [address] Optional Customer address.
* @apiParam {String} [city] Optional Customer City.
* @apiParam {String} [state] Optional Customer state.
* @apiParam {String} [zip] Optional Zip Code.
* @apiParam {String} [partnership_type] Optional Customer partnership type.
* @apiParam {String} [country] Optional country.
* @apiParam {String} [billing_street] Optional Billing Address: Street.
* @apiParam {String} [billing_city] Optional Billing Address: City.
* @apiParam {Number} [billing_state] Optional Billing Address: State.
* @apiParam {String} [billing_zip] Optional Billing Address: Zip.
* @apiParam {String} [billing_country] Optional Billing Address: Country.
* @apiParam {String} [shipping_street] Optional Shipping Address: Street.
* @apiParam {String} [shipping_city] Optional Shipping Address: City.
* @apiParam {String} [shipping_state] Optional Shipping Address: State.
* @apiParam {String} [shipping_zip] Optional Shipping Address: Zip.
* @apiParam {String} [shipping_country] Optional Shipping Address: Country.
*
* @apiParamExample {Multipart Form} Request-Example:
* array (size=22)
* 'company' => string 'Themesic Interactive' (length=38)
* 'vat' => string '123456789' (length=9)
* 'phonenumber' => string '123456789' (length=9)
* 'website' => string 'AAA.com' (length=7)
* 'groups_in' =>
* array (size=2)
* 0 => string '1' (length=1)
* 1 => string '4' (length=1)
* 'default_currency' => string '3' (length=1)
* 'default_language' => string 'english' (length=7)
* 'address' => string '1a The Alexander Suite Silk Point' (length=27)
* 'city' => string 'London' (length=14)
* 'state' => string 'London' (length=14)
* 'zip' => string '700000' (length=6)
* 'country' => string '243' (length=3)
* 'billing_street' => string '1a The Alexander Suite Silk Point' (length=27)
* 'billing_city' => string 'London' (length=14)
* 'billing_state' => string 'London' (length=14)
* 'billing_zip' => string '700000' (length=6)
* 'billing_country' => string '243' (length=3)
* 'shipping_street' => string '1a The Alexander Suite Silk Point' (length=27)
* 'shipping_city' => string 'London' (length=14)
* 'shipping_state' => string 'London' (length=14)
* 'shipping_zip' => string '700000' (length=6)
* 'shipping_country' => string '243' (length=3)
*
*
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Customer add successful.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Customer add successful."
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Customer add fail.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Customer add fail."
* }
*
*/
public function data_post() {
\modules\api\core\Apiinit::the_da_vinci_code('api');
// form validation
$this->form_validation->set_rules('company', 'Company', 'trim|required|max_length[600]', array('is_unique' => 'This %s already exists please enter another Company'));
if ($this->form_validation->run() == FALSE) {
// form validation error
$message = array('status' => FALSE, 'error' => $this->form_validation->error_array(), 'message' => validation_errors());
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$groups_in = $this->Api_model->value($this->input->post('groups_in', TRUE));
$insert_data = ['company' => $this->input->post('company', TRUE), 'vat' => $this->Api_model->value($this->input->post('vat', TRUE)), 'phonenumber' => $this->Api_model->value($this->input->post('phonenumber', TRUE)), 'website' => $this->Api_model->value($this->input->post('website', TRUE)), 'default_currency' => $this->Api_model->value($this->input->post('default_currency', TRUE)), 'default_language' => $this->Api_model->value($this->input->post('default_language', TRUE)), 'address' => $this->Api_model->value($this->input->post('address', TRUE)), 'city' => $this->Api_model->value($this->input->post('city', TRUE)), 'state' => $this->Api_model->value($this->input->post('state', TRUE)), 'zip' => $this->Api_model->value($this->input->post('zip', TRUE)), 'country' => $this->Api_model->value($this->input->post('country', TRUE)), 'billing_street' => $this->Api_model->value($this->input->post('billing_street', TRUE)), 'billing_city' => $this->Api_model->value($this->input->post('billing_city', TRUE)), 'billing_state' => $this->Api_model->value($this->input->post('billing_state', TRUE)), 'billing_zip' => $this->Api_model->value($this->input->post('billing_zip', TRUE)), 'billing_country' => $this->Api_model->value($this->input->post('billing_country', TRUE)), 'shipping_street' => $this->Api_model->value($this->input->post('shipping_street', TRUE)), 'shipping_city' => $this->Api_model->value($this->input->post('shipping_city', TRUE)), 'shipping_state' => $this->Api_model->value($this->input->post('shipping_state', TRUE)), 'shipping_zip' => $this->Api_model->value($this->input->post('shipping_zip', TRUE)), 'shipping_country' => $this->Api_model->value($this->input->post('shipping_country', TRUE)) ];
if (!empty($this->input->post('custom_fields', TRUE))) {
$insert_data['custom_fields'] = $this->Api_model->value($this->input->post('custom_fields', TRUE));
}
if ($groups_in != '') {
$insert_data['groups_in'] = $groups_in;
}
// insert data
$this->load->model('clients_model');
$output = $this->clients_model->add($insert_data);
if ($output > 0 && !empty($output)) {
// success
$message = array(
'status' => TRUE,
'message' => 'Client add successful.',
'record_id' => $output // Εδώ επιστρέφουμε το ID της νέας εγγραφής
);
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('status' => FALSE, 'message' => 'Client add fail.');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {delete} api/delete/customers/:id Delete a Customer
* @apiName DeleteCustomer
* @apiGroup Customers
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {Number} id Customer unique ID.
*
* @apiSuccess {String} status Request status.
* @apiSuccess {String} message Customer Delete Successful.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Customer Delete Successful."
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Customer Delete Fail.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Customer Delete Fail."
* }
*/
public function data_delete($id = '') {
$id = $this->security->xss_clean($id);
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid Customer ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
// delete data
$this->load->model('clients_model');
$output = $this->clients_model->delete($id);
if ($output === TRUE) {
// success
$message = array('status' => TRUE, 'message' => 'Customer Delete Successful.');
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('status' => FALSE, 'message' => 'Customer Delete Fail.');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {put} api/customers/:id Update a Customer
* @apiName PutCustomer
* @apiGroup Customers
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} company Mandatory Customer company.
* @apiParam {String} [vat] Optional Vat.
* @apiParam {String} [phonenumber] Optional Customer Phone.
* @apiParam {String} [website] Optional Customer Website.
* @apiParam {Number[]} [groups_in] Optional Customer groups.
* @apiParam {String} [default_language] Optional Customer Default Language.
* @apiParam {String} [default_currency] Optional default currency.
* @apiParam {String} [address] Optional Customer address.
* @apiParam {String} [city] Optional Customer City.
* @apiParam {String} [state] Optional Customer state.
* @apiParam {String} [zip] Optional Zip Code.
* @apiParam {String} [country] Optional country.
* @apiParam {String} [billing_street] Optional Billing Address: Street.
* @apiParam {String} [billing_city] Optional Billing Address: City.
* @apiParam {Number} [billing_state] Optional Billing Address: State.
* @apiParam {String} [billing_zip] Optional Billing Address: Zip.
* @apiParam {String} [billing_country] Optional Billing Address: Country.
* @apiParam {String} [shipping_street] Optional Shipping Address: Street.
* @apiParam {String} [shipping_city] Optional Shipping Address: City.
* @apiParam {String} [shipping_state] Optional Shipping Address: State.
* @apiParam {String} [shipping_zip] Optional Shipping Address: Zip.
* @apiParam {String} [shipping_country] Optional Shipping Address: Country.
*
* @apiParamExample {json} Request-Example:
* {
* "company": "Công ty A",
* "vat": "",
* "phonenumber": "0123456789",
* "website": "",
* "default_language": "",
* "default_currency": "0",
* "country": "243",
* "city": "TP London",
* "zip": "700000",
* "state": "Quận 12",
* "address": "hẻm 71, số 34\/3 Đường TA 16, Phường Thới An, Quận 12",
* "billing_street": "hẻm 71, số 34\/3 Đường TA 16, Phường Thới An, Quận 12",
* "billing_city": "TP London",
* "billing_state": "Quận 12",
* "billing_zip": "700000",
* "billing_country": "243",
* "shipping_street": "",
* "shipping_city": "",
* "shipping_state": "",
* "shipping_zip": "",
* "shipping_country": "0"
* }
*
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Customer Update Successful.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Customer Update Successful."
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Customer Update Fail.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Customer Update Fail."
* }
*/
public function data_put($id = '') {
// JSON data is now automatically parsed in REST_Controller
if (empty($_POST) || !isset($_POST)) {
$message = array('status' => FALSE, 'message' => 'Data Not Acceptable OR Not Provided');
$this->response($message, REST_Controller::HTTP_NOT_ACCEPTABLE);
}
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid Customers ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$update_data = $this->input->post();
// update data
$this->load->model('clients_model');
$output = $this->clients_model->update($update_data, $id);
if ($output > 0 && !empty($output)) {
// success
$message = array('status' => TRUE, 'message' => 'Customers Update Successful.');
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('status' => FALSE, 'message' => 'Customers Update Fail.');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
}

View File

@@ -0,0 +1,29 @@
<?php
defined('BASEPATH') || exit('No direct script access allowed');
class Env_ver extends AdminController {
public function __construct() {
parent::__construct();
}
public function index() {
show_404();
}
public function activate() {
$res = modules\api\core\Apiinit::pre_validate($this->input->post('module_name'), $this->input->post('purchase_key'));
if ($res['status']) {
$res['original_url'] = $this->input->post('original_url');
}
echo json_encode($res);
}
public function upgrade_database() {
$res = modules\api\core\Apiinit::pre_validate($this->input->post('module_name'), $this->input->post('purchase_key'));
if ($res['status']) {
$res['original_url'] = $this->input->post('original_url');
}
echo json_encode($res);
}
}

View File

@@ -0,0 +1,731 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
// This can be removed if you use __autoload() in config.php OR use Modular Extensions
/** @noinspection PhpIncludeInspection */
require __DIR__ . '/REST_Controller.php';
/**
* This is an example of a few basic user interaction methods you could use
* all done with a hardcoded array
*
* @package CodeIgniter
* @subpackage Rest Server
* @category Controller
* @author Phil Sturgeon, Chris Kacerguis
* @license MIT
* @link https://github.com/chriskacerguis/codeigniter-restserver
*/
class Estimates extends REST_Controller {
function __construct() {
// Construct the parent class
parent::__construct();
}
/**
* @api {get} api/estimates/:id Request Estimate information
* @apiVersion 0.3.0
* @apiName GetEstimate
* @apiGroup Estimates
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiParam {Number} id Contact unique ID
*
* @apiSuccess {Object} Estimates information.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "id": "1",
* "sent": "0",
* "datesend": null,
* "clientid": "1",
* "deleted_customer_name": null,
* "project_id": "0",
* "number": "1",
* "prefix": "EST-",
* "number_format": "1",
* "hash": "b12ae9de6471d0cf153d7846f05128af",
* "datecreated": "2021-07-31 11:06:49",
* "date": "2021-07-31",
* "expirydate": "2021-08-07",
* "currency": "1",
* "subtotal": "1200.00",
* "total_tax": "0.00",
* "total": "1200.00",
* "adjustment": "0.00",
* "addedfrom": "1",
* "status": "1",
* "clientnote": "",
* "adminnote": "",
* "discount_percent": "0.00",
* "discount_total": "0.00",
* "discount_type": "",
* "invoiceid": null,
* "invoiced_date": null,
* "terms": "",
* "reference_no": "",
* "sale_agent": "0",
* "billing_street": "Thangadh, Gujarat, India<br />\r\nShipping",
* "billing_city": "Thangadh",
* "billing_state": "Gujarat",
* "billing_zip": "363630",
* "billing_country": "102",
* "shipping_street": "Thangadh, Gujarat, India<br />\r\nShipping",
* "shipping_city": "Thangadh",
* "shipping_state": "Gujarat",
* "shipping_zip": "363630",
* "shipping_country": "102",
* "include_shipping": "1",
* "show_shipping_on_estimate": "1",
* "show_quantity_as": "1",
* "pipeline_order": "0",
* "is_expiry_notified": "0",
* "acceptance_firstname": null,
* "acceptance_lastname": null,
* "acceptance_email": null,
* "acceptance_date": null,
* "acceptance_ip": null,
* "signature": null,
* "short_link": null,
* "symbol": "$",
* "name": "USD",
* "decimal_separator": ".",
* "thousand_separator": ",",
* "placement": "before",
* "isdefault": "1",
* "currencyid": "1",
* "currency_name": "USD",
* "attachments": [],
* "visible_attachments_to_customer_found": false,
* "items": [
* {
* "id": "2",
* "rel_id": "1",
* "rel_type": "estimate",
* "description": "test",
* "long_description": "test",
* "qty": "1.00",
* "rate": "1200.00",
* "unit": "1",
* "item_order": "1"
* }
* ],
* "client": {
* "userid": "1",
* "company": "test",
* "vat": "",
* "phonenumber": "01324568903",
* "country": "102",
* "city": "test",
* "zip": "3000",
* "state": "Test",
* "address": "Test",
* "website": "",
* "datecreated": "2021-07-30 16:29:46",
* "active": "1",
* "leadid": null,
* "billing_street": "Test",
* "billing_city": "Test",
* "billing_state": "Test",
* "billing_zip": "3000",
* "billing_country": "102",
* "shipping_street": "Test",
* "shipping_city": "Test",
* "shipping_state": "Test",
* "shipping_zip": "3000",
* "shipping_country": "102",
* "longitude": null,
* "latitude": null,
* "default_language": "",
* "default_currency": "0",
* "show_primary_contact": "0",
* "stripe_id": null,
* "registration_confirmed": "1",
* "addedfrom": "1"
* },
* "scheduled_email": null,
* "customfields": []
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_get($id = '') {
// If the id parameter doesn't exist return all the
$data = $this->Api_model->get_table('estimates', $id);
// Check if the data store contains
if ($data) {
$data = $this->Api_model->get_api_custom_data($data, "estimate", $id);
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
// Set the response and exit
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {get} api/estimates/search/:keysearch Search Estimate information
* @apiVersion 0.3.0
* @apiName GetEstimateSearch
* @apiGroup Estimates
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} keysearch Search Keywords.
*
* @apiSuccess {Object} Estimate Information.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "id": "2",
* "sent": "0",
* "datesend": null,
* "clientid": "1",
* "deleted_customer_name": null,
* "project_id": "0",
* "number": "2",
* "prefix": "EST-",
* "number_format": "1",
* "hash": "ac754972999f948ade369c70bb44d696",
* "datecreated": "2021-07-30 16:29:46",
* "date": "2021-08-01",
* "expirydate": "2021-08-08",
* "currency": "1",
* "subtotal": "1200.00",
* "total_tax": "0.00",
* "total": "1200.00",
* "adjustment": "0.00",
* "addedfrom": "1",
* "status": "1",
* "clientnote": "",
* "adminnote": "adminnote",
* "discount_percent": "0.00",
* "discount_total": "0.00",
* "discount_type": "",
* "invoiceid": null,
* "invoiced_date": null,
* "terms": "",
* "reference_no": "",
* "sale_agent": "0",
* "billing_street": "Test",
* "billing_city": "Test",
* "billing_state": "Test",
* "billing_zip": "3000",
* "billing_country": "102",
* "shipping_street": "Test",
* "shipping_city": "Test",
* "shipping_state": "Test",
* "shipping_zip": "3000",
* "shipping_country": "102",
* "include_shipping": "1",
* "show_shipping_on_estimate": "1",
* "show_quantity_as": "1",
* "pipeline_order": "0",
* "is_expiry_notified": "0",
* "acceptance_firstname": null,
* "acceptance_lastname": null,
* "acceptance_email": null,
* "acceptance_date": null,
* "acceptance_ip": null,
* "signature": null,
* "short_link": null,
* "userid": "1",
* "company": "test",
* "vat": "",
* "phonenumber": "01324568903",
* "country": "102",
* "city": "Test",
* "zip": "3000",
* "state": "Test",
* "address": "Test",
* "website": "",
* "active": "1",
* "leadid": null,
* "longitude": null,
* "latitude": null,
* "default_language": "",
* "default_currency": "0",
* "show_primary_contact": "0",
* "stripe_id": null,
* "registration_confirmed": "1",
* "estimateid": "2",
* "customfields": []
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No Data Were Found"
* }
*/
public function data_search_get($key = '') {
$data = $this->Api_model->search('estimates', $key);
// Check if the data store contains
if ($data) {
$data = $this->Api_model->get_api_custom_data($data, "estimate");
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
// Set the response and exit
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {delete} api/estimates/:id Delete Estimate
* @apiVersion 0.3.0
* @apiName DeleteEstimate
* @apiGroup Estimates
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Estimates Deleted Successfully
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Estimate Deleted Successfully"
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Estimate Delete Fail
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Estimate Delete Fail"
* }
*/
public function data_delete($id = '') {
$id = $this->security->xss_clean($id);
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid Estimate ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$this->load->model('estimates_model');
$is_exist = $this->estimates_model->get($id);
if (is_object($is_exist)) {
$output = $this->estimates_model->delete($id);
if ($output === TRUE) {
// success
$message = array('status' => TRUE, 'message' => 'Estimate Deleted Successfully');
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('status' => FALSE, 'message' => 'Estimate Delete Fail');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
} else {
$message = array('status' => FALSE, 'message' => 'Invalid Estimate ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {post} api/estimates Add New Estimates
* @apiVersion 0.3.0
* @apiName PostEstimates
* @apiGroup Estimates
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {Number} clientid Mandatory. Customer id
* @apiParam {Number} number Mandatory. Estimates Number
* @apiParam {Date} date Mandatory. Estimates Date
* @apiParam {Date} [duedate] Optional. Expiry Date of Estimates
* @apiParam {Number} currency Mandatory. currency field
* @apiParam {Array} newitems Mandatory. New Items to be added
* @apiParam {Decimal} subtotal Mandatory. calculation based on item Qty, Rate and Tax
* @apiParam {Decimal} total Mandatory. calculation based on subtotal, Discount and Adjustment
* @apiParam {String} billing_street Optional. Street Address
* @apiParam {String} [billing_city] Optional. City Name for billing
* @apiParam {String} [billing_state] Optional. Name of state for billing
* @apiParam {Number} [billing_zip] Optional. Zip code
* @apiParam {Number} [billing_country] Optional. Country code
* @apiParam {String} [shipping_street] Optional. Address of shipping
* @apiParam {String} [shipping_city] Optional. City name for shipping
* @apiParam {String} [shipping_state] Optional. Name of state for shipping
* @apiParam {Number} [shipping_zip] Optional. Zip code for shipping
* @apiParam {Number} [shipping_country] Optional. Country code
* @apiParam {String} [tags] Optional. TAGS comma separated
* @apiParam {Number} [status] Optional. Status id (default status is Accepted)
* @apiParam {String} [Reference] Optional. Reference name
* @apiParam {Number} [sale_agent] Optional. Sale Agent name
* @apiParam {String} [adminnote] Optional. notes by admin
* @apiParam {String} [clientnote] Optional. client notes
* @apiParam {String} [terms] Optional. Terms
*
* @apiParamExample {Multipart Form} Request-Example:
* [
* "clientid"=>1,
* "number"=>"00001",
* "date"=>"2020-09-07",
* "currency"=>1,
* "newitems[0][description]"=>"item 1 description",
* "newitems[0][long_description]"=>"item 1 long description",
* "newitems[0][qty]"=>1,
* "newitems[0][rate]"=>100,
* "newitems[0][order]"=>1,
* "newitems[0][taxname][]"=>CGST|9.00,
* "newitems[0][taxname][]"=>SGST|9.00,
* "newitems[0][unit]"=>"",
* "newitems[1][description]"=>"item 2 description",
* "newitems[1][long_description]"=>"item 2 long description",
* "newitems[1][qty]"=>1,
* "newitems[1][rate]"=>100,
* "newitems[1][order]"=>1,
* "newitems[1][taxname][]"=>CGST|9.00,
* "newitems[1][taxname][]"=>SGST|9.00,
* "newitems[1][unit]"=>"",
* "subtotal"=>236.00,
* "total"=>236.00,
* "status"=>1,
* ....
* ]
*
*
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Estimates Added Successfully
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Estimates Added Successfully"
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Estimates add fail
* @apiError {String} newitems[] The Items field is required
* @apiError {String} number The Estimates number is already in use
* @apiError {String} subtotal The Sub Total field is required
* @apiError {String} total The Total field is required
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Estimates Add Fail"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 409 Conflict
* {
* "status": false,
* "error": {
* "number":"The Estimates number is already in use"
* },
* "message": "The Estimates number is already in use"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "error": {
* "newitems[]": "The Items field is required"
* },
* "message": "<p>The Items field is required</p>\n"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "error": {
* "subtotal": "The Sub Total field is required"
* },
* "message": "<p>The Sub Total field is required</p>\n"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "error": {
* "total": "The Total field is required"
* },
* "message": "<p>The Total field is required</p>\n"
* }
*
*/
public function data_post() {
\modules\api\core\Apiinit::the_da_vinci_code('api');
error_reporting(0);
$data = $this->input->post();
$this->form_validation->set_rules('clientid', 'Customer', 'trim|required|numeric|greater_than[0]');
$this->form_validation->set_rules('project_id', 'Project', 'trim|numeric|greater_than[0]');
$this->form_validation->set_rules('include_shipping', 'Include Shipping', 'trim|numeric|greater_than_equal_to[0]|less_than_equal_to[1]');
$this->form_validation->set_rules('show_shipping_on_estimate', 'Show shipping on estimate', 'trim|numeric|greater_than_equal_to[0]|less_than_equal_to[1]');
$this->form_validation->set_rules('currency', 'Currency', 'trim|required|numeric|greater_than[0]');
$this->form_validation->set_rules('status', 'Status', 'trim|required|numeric|greater_than[0]');
$this->form_validation->set_rules('date', 'Estimate date', 'trim|required|max_length[255]');
$this->form_validation->set_rules('quantity', 'Quantity', 'trim|numeric|greater_than[0]');
$this->form_validation->set_rules('newitems[]', 'Items', 'required');
$this->form_validation->set_rules('subtotal', 'Sub Total', 'trim|required|decimal|greater_than[0]');
$this->form_validation->set_rules('total', 'Total', 'trim|required|decimal|greater_than[0]');
$this->form_validation->set_rules('billing_street', 'Street', 'trim|required|max_length[200]');
$this->form_validation->set_rules('number', 'Estimate Number', 'trim|required|numeric|callback_validate_estimate_number[0]');
if ($this->form_validation->run() == FALSE) {
$message = array('status' => FALSE, 'error' => $this->form_validation->error_array(), 'message' => validation_errors());
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$this->load->model('estimates_model');
$data['expirydate'] = _d(date('Y-m-d', strtotime('+' . get_option('estimate_due_after') . ' DAY', strtotime(date('Y-m-d')))));
$id = $this->estimates_model->add($data);
if ($id > 0 && !empty($id)) {
$message = array(
'status' => TRUE,
'message' => 'Estimate Added Successfully',
'record_id' => $id
);
$this->response($message, REST_Controller::HTTP_OK);
}
else {
// error
$message = array('status' => FALSE, 'message' => 'Estimate Add Fail');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
public function validate_estimate_number($number, $estimateid) {
$isedit = 'false';
if (!empty($estimateid)) {
$isedit = 'true';
}
$this->form_validation->set_message('validate_estimate_number', 'The {field} is already in use');
$original_number = null;
$date = $this->input->post('date');
if (!empty($estimateid)) {
$data = $this->Api_model->get_table('estimates', $estimateid);
$original_number = $data->number;
if (empty($date)) {
$date = $data->date;
}
}
$number = trim($number);
$number = ltrim($number, '0');
if ($isedit == 'true') {
if ($number == $original_number) {
return TRUE;
}
}
if (total_rows(db_prefix() . 'estimates', ['YEAR(date)' => date('Y', strtotime(to_sql_date($date))), 'number' => $number, ]) > 0) {
return FALSE;
} else {
return TRUE;
}
}
/**
* @api {put} api/estimates/:id Update a estimate
* @apiVersion 0.3.0
* @apiName PutEstimate
* @apiGroup Estimates
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} clientid Mandatory. Customer.
* @apiParam {String} billing_street Mandatory. Street Address
* @apiParam {String} [billing_city] Optional. City Name for billing
* @apiParam {String} [billing_state] Optional. Name of state for billing
* @apiParam {Number} [billing_zip] Optional. Zip code
* @apiParam {Number} [billing_country] Optional. Country code
* @apiParam {boolean} [include_shipping="no"] Optional. set yes if you want add Shipping Address
* @apiParam {boolean} [show_shipping_on_estimate] Optional. Shows shipping details in estimate.
* @apiParam {String} [shipping_street] Optional. Address of shipping
* @apiParam {String} [shipping_city] Optional. City name for shipping
* @apiParam {String} [shipping_state] Optional. Name of state for shipping
* @apiParam {Number} [shipping_zip] Optional. Zip code for shipping
* @apiParam {Number} [shipping_country] Optional. Country code
* @apiParam {Number} number Mandatory. Estimate Number
* @apiParam {Date} date Mandatory. Estimate Date
* @apiParam {Date} [expirydate] Optional. Expiry Date of Estimate
* @apiParam {String} [tags] Optional. TAGS comma separated
* @apiParam {Number} currency Mandatory. currency field
* @apiParam {Number} status Mandatory. Estimate Status(eg. Draft, Sent)
* @apiParam {String} [reference_no] Optional. Reference #
* @apiParam {Number} [sale_agent] Optional. Sale Agent name
* @apiParam {String} [discount_type] Optional. before_tax / after_tax discount type
* @apiParam {String} [adminnote] Optional. notes by admin
* @apiParam {Array} [items] Mandatory. Existing items with Id
* @apiParam {Array} [removed_items] Optional. Items to be removed
* @apiParam {Array} [newitems] Optional. New Items to be added
* @apiParam {Decimal} subtotal Mandatory. calculation based on item Qty, Rate and Tax
* @apiParam {Decimal} total Mandatory. calculation based on subtotal, Discount and Adjustment
* @apiParam {String} [clientnote] Optional. client notes
* @apiParam {String} [terms] Optional. Terms
*
* @apiParamExample {json} Request-Example:
* {
* "clientid": 1,
* "billing_street": "new 1 update",
* "number": 2,
* "status": 2,
* "date": "2021-08-19",
* "currency": 1,
* "items": {
* "1": {
* "itemid": "24",
* "order": "1",
* "description": "item description",
* "long_description": "item long description",
* "qty": "1",
* "unit": "1",
* "rate": "10.00",
* "custom_fields":{
* "items":{
* "31":"test 12 item 1",
* "32":"10",
* "33":"Lorem Ipsum is simply dummy text of the printing and typesetting industry",
* "34":"Option 1",
* "35":["Option 1","Option 2"],
* "36":["Option 1","Option 3"],
* "37":"2021-05-06",
* "38":"2021-05-06 00:23:25",
* "39":"#ffffff",
* "40":"<a href=\"url.com\" target=\"_blank\">Link</a>"
* }
* }
* }
* },
* "newitems": {
* "2": {
* "order": "2",
* "description": "updated item 2 description",
* "long_description": "updated item 2 logn description",
* "qty": "1",
* "unit": "",
* "rate": "100.00",
* "custom_fields":{
* "items":{
* "31":"test 12 item 2",
* "32":"10",
* "33":"Lorem Ipsum is simply dummy text of the printing and typesetting industry",
* "34":"Option 1",
* "35":["Option 1","Option 2"],
* "36":["Option 1","Option 3"],
* "37":"2021-05-06",
* "38":"2021-05-06 00:23:25",
* "39":"#ffffff",
* "40":"<a href=\"url.com\" target=\"_blank\">Link</a>"
* }
* }
* }
* },
* "custom_fields":{
* "estimate":{
* "92":"test 1254"
* }
* },
* "subtotal":"110.00",
* "total":"110.00"
* }
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": false,
* "message": "Estimate Updated Successfully"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Estimate Update Fail"
* }
*
* @apiError {String} number The Estimate number is already in use
*
* @apiErrorExample Error-Response:
* HTTP/1.1 409 Conflict
* {
* "status": false,
* "error": {
* "number":"The Estimate number is already in use"
* },
* "message": "The Estimate number is already in use"
* }
*
*
*/
public function data_put($id = "") {
// JSON data is now automatically parsed in REST_Controller
if (empty($_POST) || !isset($_POST)) {
$message = array('status' => FALSE, 'message' => 'Data Not Acceptable OR Not Provided');
$this->response($message, REST_Controller::HTTP_NOT_ACCEPTABLE);
}
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid Estimate ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$this->form_validation->set_rules('clientid', 'Customer', 'trim|required|numeric|greater_than[0]');
$this->form_validation->set_rules('project_id', 'Project', 'trim|numeric|greater_than[0]');
$this->form_validation->set_rules('include_shipping', 'Include Shipping', 'trim|numeric|greater_than_equal_to[0]|less_than_equal_to[1]');
$this->form_validation->set_rules('show_shipping_on_estimate', 'Show shipping on estimate', 'trim|numeric|greater_than_equal_to[0]|less_than_equal_to[1]');
$this->form_validation->set_rules('currency', 'Currency', 'trim|required|numeric|greater_than[0]');
$this->form_validation->set_rules('status', 'Status', 'trim|required|numeric|greater_than[0]');
$this->form_validation->set_rules('date', 'Estimate date', 'trim|required|max_length[255]');
$this->form_validation->set_rules('quantity', 'Quantity', 'trim|numeric|greater_than[0]');
$this->form_validation->set_rules('items[]', 'Items', 'required');
$this->form_validation->set_rules('subtotal', 'Sub Total', 'trim|required|decimal|greater_than[0]');
$this->form_validation->set_rules('total', 'Total', 'trim|required|decimal|greater_than[0]');
$this->form_validation->set_rules('billing_street', 'Street', 'trim|required|max_length[200]');
$this->form_validation->set_rules('number', 'Estimate Number', 'trim|required|numeric|callback_validate_estimate_number[' . $id . ']');
$_POST['shipping_street'] = $_POST['shipping_street']??"";
if ($this->form_validation->run() == FALSE) {
$message = array('status' => FALSE, 'error' => $this->form_validation->error_array(), 'message' => validation_errors());
$this->response($message, REST_Controller::HTTP_CONFLICT);
} else {
$this->load->model('estimates_model');
$is_exist = $this->estimates_model->get($id);
if (!is_object($is_exist)) {
$message = array('status' => FALSE, 'message' => 'Estimate ID Doesn\'t Not Exist.');
$this->response($message, REST_Controller::HTTP_CONFLICT);
}
if (is_object($is_exist)) {
$data = $this->input->post();
$data['isedit'] = "";
$success = $this->estimates_model->update($data, $id);
if ($success == true) {
$message = array('status' => TRUE, 'message' => "Estimate Updated Successfully",);
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('status' => FALSE, 'message' => 'Estimate Update Fail');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
} else {
$message = array('status' => FALSE, 'message' => 'Invalid Estimate ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
}
}

View File

@@ -0,0 +1,671 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
// This can be removed if you use __autoload() in config.php OR use Modular Extensions
/** @noinspection PhpIncludeInspection */
require __DIR__ . '/REST_Controller.php';
/**
* This is an example of a few basic user interaction methods you could use
* all done with a hardcoded array
*
* @package CodeIgniter
* @subpackage Rest Server
* @category Controller
* @author Phil Sturgeon, Chris Kacerguis
* @license MIT
* @link https://github.com/chriskacerguis/codeigniter-restserver
*/
class Expenses extends REST_Controller {
function __construct() {
// Construct the parent class
parent::__construct();
}
/**
* @api {get} api/expenses/:id Request Expense information
* @apiVersion 0.3.0
* @apiName GetExpense
* @apiGroup Expenses
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
* @apiParam {Number} id Expense unique ID.
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiSuccess {Array} Expense Expense information.
* @apiSuccessExample Success-Response:
* [
* {
* "id": "1",
* "category": "1",
* "currency": "1",
* "amount": "50.00",
* "tax": "0",
* "tax2": "0",
* "reference_no": "012457893",
* "note": "AWS server hosting charges",
* "expense_name": "Cloud Hosting",
* "clientid": "1",
* "project_id": "0",
* "billable": "0",
* "invoiceid": null,
* "paymentmode": "2",
* "date": "2021-09-01",
* "recurring_type": "month",
* "repeat_every": "1",
* "recurring": "1",
* "cycles": "12",
* "total_cycles": "0",
* "custom_recurring": "0",
* "last_recurring_date": null,
* "create_invoice_billable": "0",
* "send_invoice_to_customer": "0",
* "recurring_from": null,
* "dateadded": "2021-09-01 12:26:34",
* "addedfrom": "1",
* "is_expense_created_in_xero": "0",
* "userid": "1",
* "company": "Company A",
* "vat": "",
* "phonenumber": "",
* "country": "0",
* "city": "",
* "zip": "",
* "state": "",
* "address": "",
* "website": "",
* "datecreated": "2020-05-25 22:55:49",
* "active": "1",
* "leadid": null,
* "billing_street": "",
* "billing_city": "",
* "billing_state": "",
* "billing_zip": "",
* "billing_country": "0",
* "shipping_street": "",
* "shipping_city": "",
* "shipping_state": "",
* "shipping_zip": "",
* "shipping_country": "0",
* "longitude": null,
* "latitude": null,
* "default_language": "",
* "default_currency": "0",
* "show_primary_contact": "0",
* "stripe_id": null,
* "registration_confirmed": "1",
* "name": "Hosting Management",
* "description": "server space and other settings",
* "show_on_pdf": "0",
* "invoices_only": "0",
* "expenses_only": "0",
* "selected_by_default": "0",
* "taxrate": null,
* "category_name": "Hosting Management",
* "payment_mode_name": "Paypal",
* "tax_name": null,
* "tax_name2": null,
* "taxrate2": null,
* "expenseid": "1",
* "customfields": []
* }
* ]
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_get($id = '') {
// If the id parameter doesn't exist return all the
$data = $this->Api_model->get_table('expenses', $id);
// Check if the data store contains
if ($data) {
$data = $this->Api_model->get_api_custom_data($data, "expenses", $id);
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
// Set the response and exit
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {get} api/expenses/search/:keysearch Search Expenses information
* @apiVersion 0.3.0
* @apiName GetExpenseSearch
* @apiGroup Expenses
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} keysearch Search Keywords
*
* @apiSuccess {Array} Expenses Expenses Information
*
* @apiSuccessExample Success-Response:
* [
* {
* "id": "1",
* "category": "1",
* "currency": "1",
* "amount": "50.00",
* "tax": "0",
* "tax2": "0",
* "reference_no": "012457893",
* "note": "AWS server hosting charges",
* "expense_name": "Cloud Hosting",
* "clientid": "1",
* "project_id": "0",
* "billable": "0",
* "invoiceid": null,
* "paymentmode": "2",
* "date": "2021-09-01",
* "recurring_type": "month",
* "repeat_every": "1",
* "recurring": "1",
* "cycles": "12",
* "total_cycles": "0",
* "custom_recurring": "0",
* "last_recurring_date": null,
* "create_invoice_billable": "0",
* "send_invoice_to_customer": "0",
* "recurring_from": null,
* "dateadded": "2021-09-01 12:26:34",
* "addedfrom": "1",
* "is_expense_created_in_xero": "0",
* "userid": "1",
* "company": "Company A",
* "vat": "",
* "phonenumber": "",
* "country": "0",
* "city": "",
* "zip": "",
* "state": "",
* "address": "",
* "website": "",
* "datecreated": "2020-05-25 22:55:49",
* "active": "1",
* "leadid": null,
* "billing_street": "",
* "billing_city": "",
* "billing_state": "",
* "billing_zip": "",
* "billing_country": "0",
* "shipping_street": "",
* "shipping_city": "",
* "shipping_state": "",
* "shipping_zip": "",
* "shipping_country": "0",
* "longitude": null,
* "latitude": null,
* "default_language": "",
* "default_currency": "0",
* "show_primary_contact": "0",
* "stripe_id": null,
* "registration_confirmed": "1",
* "name": "Hosting Management",
* "description": "server space and other settings",
* "show_on_pdf": "0",
* "invoices_only": "0",
* "expenses_only": "0",
* "selected_by_default": "0",
* "taxrate": null,
* "category_name": "Hosting Management",
* "payment_mode_name": "Paypal",
* "tax_name": null,
* "tax_name2": null,
* "taxrate2": null,
* "expenseid": "1",
* "customfields": []
* }
* ]
*
* @apiError {Boolean} status Request status
* @apiError {String} message No data were found
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_search_get($key = '') {
$data = $this->Api_model->search('expenses', $key);
// Check if the data store contains
if ($data) {
$data = $this->Api_model->get_api_custom_data($data, "expenses");
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
// Set the response and exit
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {delete} api/expenses/:id Delete Expense
* @apiVersion 0.3.0
* @apiName DeleteExpenses
* @apiGroup Expenses
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Expense Deleted Successfully
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Expense Deleted Successfully"
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Expense Delete Fail
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Expense Delete Fail"
* }
*/
public function data_delete($id = '') {
$id = $this->security->xss_clean($id);
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid Expense ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$this->load->model('expenses_model');
$is_exist = $this->expenses_model->get($id);
if (is_object($is_exist)) {
$output = $this->expenses_model->delete($id);
if ($output === TRUE) {
// success
$message = array('status' => TRUE, 'message' => 'Expense Deleted Successfully');
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('status' => FALSE, 'message' => 'Expense Delete Fail');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
} else {
$message = array('status' => FALSE, 'message' => 'Invalid Expense ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {post} api/expenses Add Expense
* @apiVersion 0.3.0
* @apiName AddExpense
* @apiGroup Expenses
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} [expense_name] Optional. Expanse Name
* @apiParam {String} [note] Optional. Expanse Note
* @apiParam {Number} category Mandatory. Expense Category
* @apiParam {Decimal} amount Mandatory. Expense Amount
* @apiParam {Date} date Mandatory. Expense Date
* @apiParam {Number} clientid Optional. Customer id
* @apiParam {Number} currency Mandatory. Currency Field
* @apiParam {Number} tax Optional. Tax 1
* @apiParam {Number} tax2 Optional. Tax 2
* @apiParam {Number} paymentmode Optional. Payment mode
* @apiParam {String} [reference_no] Optional. Reference #
* @apiParam {String} [recurring] Optional. recurring 1 to 12 or custom
* @apiParam {Number} [repeat_every_custom] Optional. if recurring is custom set number gap
* @apiParam {String} [repeat_type_custom] Optional. if recurring is custom set gap option day/week/month/year
*
* @apiParamExample {json} Request-Example:
* {
* "expense_name": "Test51",
* "note": "Expanse note",
* "category": 300,
* "date": "2021-08-20",
* "amount": "1200.00",
* "billable": 1,
* "clientid": 1,
* "currency": 1,
* "tax": 1,
* "tax2": 1,
* "paymentmode": 2,
* "reference_no": 5874,
* "repeat_every": "6-month",
* "cycles": 5,
* "create_invoice_billable": 0,
* "send_invoice_to_customer": 1,
* "custom_fields":
* {
* "expenses":
* {
* "94": "test 1254"
* }
* }
* }
*
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Expense Added Successfully
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Expense Added Successfully"
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Expense Update Fail
* @apiError {String} category The Expense Category is not found.
* @apiError {String} date The Expense date field is required.
* @apiError {String} amount The Amount field is required.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Expense Add Fail"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 409 Conflict
* {
* "status": false,
* "error": {
* "category":"The Expense Category is not found"
* },
* "message": "The Expense Category is not found"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 409 Conflict
* {
* "status": false,
* "error": {
* "date":"The Expense date field is required."
* },
* "message": "The Expense date field is required."
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 409 Conflict
* {
* "status": false,
* "error": {
* "amount":"The Amount field is required."
* },
* "message": "The Amount field is required."
* }
*
*/
public function data_post() {
$data = $this->input->post();
$this->form_validation->set_rules('category', 'Expense Category', 'trim|required|max_length[255]|callback_validate_category');
$this->form_validation->set_rules('date', 'Expense date', 'trim|required|max_length[255]');
$this->form_validation->set_rules('category', 'Expense Category', 'trim|required|max_length[255]');
$this->form_validation->set_rules('date', 'Invoice date', 'trim|required|max_length[255]');
$this->form_validation->set_rules('currency', 'Currency', 'trim|required|max_length[255]');
$this->form_validation->set_rules('amount', 'Amount', 'trim|required|decimal|greater_than[0]');
$data['note'] = $data['note'] ?? "";
if ($this->form_validation->run() == FALSE) {
$message = array('status' => FALSE, 'error' => $this->form_validation->error_array(), 'message' => validation_errors());
$this->response($message, REST_Controller::HTTP_CONFLICT);
} else {
$this->load->model('expenses_model');
$id = $this->expenses_model->add($data);
if ($id > 0 && !empty($id)) {
$this->handle_expense_attachments_array($id);
$message = array(
'status' => TRUE,
'message' => 'Expense added successfully.',
'record_id' => $id
);
$this->response($message, REST_Controller::HTTP_OK);
}
else {
// error
$message = array('status' => FALSE, 'message' => 'Expense add fail.');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {put} api/expenses Update a Expense
* @apiVersion 0.3.0
* @apiName PutExpense
* @apiGroup Expenses
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} [expense_name] Optional. Name
* @apiParam {String} [note] Optional. Note
* @apiParam {Number} category Mandatory. Expense Category
* @apiParam {Decimal} amount Mandatory. Expense Amount
* @apiParam {Date} date Mandatory. Expense Date
* @apiParam {Number} clientid Optional. Customer id
* @apiParam {Number} currency Mandatory. currency field
* @apiParam {Number} tax Optional. Tax 1
* @apiParam {Number} tax2 Optional. Tax 2
* @apiParam {Number} paymentmode Optional. Payment mode
* @apiParam {String} [reference_no] Optional. Reference #
* @apiParam {String} [recurring] Optional. recurring 1 to 12 or custom
* @apiParam {Number} [repeat_every_custom] Optional. if recurring is custom set number gap
* @apiParam {String} [repeat_type_custom] Optional. if recurring is custom set gap option day/week/month/year
*
* @apiParamExample {json} Request-Example:
* {
* "expense_name": "Test51",
* "note": "exp note",
* "category": 300,
* "date": "2021-08-20",
* "amount": "1200.00",
* "billable": 1,
* "clientid": 1,
* "currency": 1,
* "tax": 1,
* "tax2": 1,
* "paymentmode": 2,
* "reference_no": 5874,
* "repeat_every": "6-month",
* "cycles": 5,
* "create_invoice_billable": 0,
* "send_invoice_to_customer": 1,
* "custom_fields":
* {
* "expenses":
* {
* "94": "test 1254"
* }
* }
* }
*
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Expense Updated Successfully
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Expense Updated Successfully"
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Expense Update Fail
* @apiError {String} category The Expense Category is not found.
* @apiError {String} date The Expense date field is required.
* @apiError {String} amount The Amount field is required.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Expense Update Fail"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 409 Conflict
* {
* "status": false,
* "error": {
* "category":"The Expense Category is not found"
* },
* "message": "The Expense Category is not found"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 409 Conflict
* {
* "status": false,
* "error": {
* "date":"The Expense date field is required."
* },
* "message": "The Expense date field is required."
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 409 Conflict
* {
* "status": false,
* "error": {
* "amount":"The Amount field is required."
* },
* "message": "The Amount field is required."
* }
*
*/
public function data_put($id = "") {
// JSON data is now automatically parsed in REST_Controller
if (empty($_POST) || !isset($_POST)) {
$this->load->library('parse_input_stream');
$_POST = $this->parse_input_stream->parse_parameters();
$_FILES = $this->parse_input_stream->parse_files();
if (empty($_POST) || !isset($_POST)) {
$message = array('status' => FALSE, 'message' => 'Data Not Acceptable OR Not Provided');
$this->response($message, REST_Controller::HTTP_NOT_ACCEPTABLE);
}
}
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid Lead ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$this->load->model('expenses_model');
$is_exist = $this->expenses_model->get($id);
if (!is_object($is_exist)) {
$message = array('status' => FALSE, 'message' => 'Expense ID Doesn\'t Not Exist.');
$this->response($message, REST_Controller::HTTP_CONFLICT);
}
if (is_object($is_exist)) {
$update_data = $this->input->post();
$update_file = isset($update_data['file']) ? $update_data['file'] : null;
unset($update_data['file']);
$output = $this->expenses_model->update($update_data, $id);
if (!empty($update_file) && count($update_file)) {
if ($output <= 0 || empty($output)) {
$output = $id;
}
}
if ($output > 0 && !empty($output)) {
$this->expenses_model->delete_expense_attachment($output);
$this->handle_expense_attachments_array($output);
$message = array('status' => TRUE, 'message' => "Expense Updated Successfully",);
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('status' => FALSE, 'message' => 'Expense Update Fail');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
} else {
$message = array('status' => FALSE, 'message' => 'Invalid Expense ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
public function validate_category($value) {
$this->form_validation->set_message('validate_category', 'The {field} is not found.');
$this->load->model('expenses_model');
$is_exist = $this->expenses_model->get_category($value);
if ($is_exist) {
return TRUE;
}
return FALSE;
}
function handle_expense_attachments_array($expense_id, $index_name = 'file') {
$path = get_upload_path_by_type('expense') . $expense_id . '/';
$CI = & get_instance();
if (isset($_FILES[$index_name]['name']) && ($_FILES[$index_name]['name'] != '' || is_array($_FILES[$index_name]['name']) && count($_FILES[$index_name]['name']) > 0)) {
if (!is_array($_FILES[$index_name]['name'])) {
$_FILES[$index_name]['name'] = [$_FILES[$index_name]['name']];
$_FILES[$index_name]['type'] = [$_FILES[$index_name]['type']];
$_FILES[$index_name]['tmp_name'] = [$_FILES[$index_name]['tmp_name']];
$_FILES[$index_name]['error'] = [$_FILES[$index_name]['error']];
$_FILES[$index_name]['size'] = [$_FILES[$index_name]['size']];
}
_file_attachments_index_fix($index_name);
for ($i = 0; $i < count($_FILES[$index_name]['name']); $i++) {
// Get the temp file path
$tmpFilePath = $_FILES[$index_name]['tmp_name'][$i];
// Make sure we have a filepath
if (!empty($tmpFilePath) && $tmpFilePath != '') {
if (_perfex_upload_error($_FILES[$index_name]['error'][$i]) || !_upload_extension_allowed($_FILES[$index_name]['name'][$i])) {
continue;
}
_maybe_create_upload_path($path);
$filename = unique_filename($path, $_FILES[$index_name]['name'][$i]);
$newFilePath = $path . $filename;
// Upload the file into the temp dir
if (move_uploaded_file($tmpFilePath, $newFilePath)) {
$CI = & get_instance();
$CI->load->model('expenses_model');
$data = [];
$data[] = ['file_name' => $filename, 'filetype' => $_FILES[$index_name]['type'][$i], ];
$this->add_attachment_to_database($expense_id, $data, false);
}
}
}
}
return true;
}
function add_attachment_to_database($expense_id, $attachment, $external = false, $form_activity = false) {
$this->misc_model->add_attachment_to_database($expense_id, 'expense', $attachment, $external);
// No notification when attachment is imported from web to lead form
if ($form_activity == false) {
$this->load->model('expenses_model');
$expense = $this->expenses_model->get($expense_id);
$not_user_ids = [];
if ($expense->addedfrom != get_staff_user_id()) {
array_push($not_user_ids, $expense->addedfrom);
}
$notifiedUsers = [];
foreach ($not_user_ids as $uid) {
$notified = add_notification([
'description' => 'not_expense_added_attachment',
'touserid' => $uid,
'link' => '#expenseid=' . $expense_id,
'additional_data' => serialize([
$expense->expense_name,
]),
]);
if ($notified) {
array_push($notifiedUsers, $uid);
}
}
pusher_trigger_notification($notifiedUsers);
}
}
}
/* End of file Expenses.php */

View File

@@ -0,0 +1,767 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
// This can be removed if you use __autoload() in config.php OR use Modular Extensions
/** @noinspection PhpIncludeInspection */
require __DIR__ . '/REST_Controller.php';
/**
* This is an example of a few basic user interaction methods you could use
* all done with a hardcoded array
*
* @package CodeIgniter
* @subpackage Rest Server
* @category Controller
* @author Phil Sturgeon, Chris Kacerguis
* @license MIT
* @link https://github.com/chriskacerguis/codeigniter-restserver
*/
class Invoices extends REST_Controller {
function __construct() {
// Construct the parent class
parent::__construct();
}
/**
* @api {get} api/invoices/:id Request invoice information
* @apiVersion 0.1.0
* @apiName GetInvoice
* @apiGroup Invoices
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiParam {Number} id Contact unique ID
*
* @apiSuccess {Object} Invoice Invoice information.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "id": "2",
* "sent": "0",
* "datesend": null,
* "clientid": "1",
* "deleted_customer_name": null,
* "number": "2",
* "prefix": "INV-",
* "number_format": "1",
* "datecreated": "2020-05-26 19:53:11",
* "date": "2020-05-26",
* "duedate": "2020-06-25",
* "currency": "1",
* "subtotal": "5.00",
* "total_tax": "0.00",
* "total": "5.00",
* "adjustment": "0.00",
* "addedfrom": "0",
* "hash": "7bfac86da004df5364407574d4d1dbf2",
* "status": "1",
* "clientnote": null,
* "adminnote": null,
* "last_overdue_reminder": null,
* "cancel_overdue_reminders": "0",
* "allowed_payment_modes": "['1']",
* "token": null,
* "discount_percent": "0.00",
* "discount_total": "0.00",
* "discount_type": "",
* "recurring": "0",
* "recurring_type": null,
* "custom_recurring": "0",
* "cycles": "0",
* "total_cycles": "0",
* "is_recurring_from": null,
* "last_recurring_date": null,
* "terms": null,
* "sale_agent": "0",
* "billing_street": "",
* "billing_city": "",
* "billing_state": "",
* "billing_zip": "",
* "billing_country": null,
* "shipping_street": null,
* "shipping_city": null,
* "shipping_state": null,
* "shipping_zip": null,
* "shipping_country": null,
* "include_shipping": "0",
* "show_shipping_on_invoice": "1",
* "show_quantity_as": "1",
* "project_id": "0",
* "subscription_id": "0",
* "symbol": "$",
* "name": "USD",
* "decimal_separator": ".",
* "thousand_separator": ",",
* "placement": "before",
* "isdefault": "1",
* "currencyid": "1",
* "currency_name": "USD",
* "total_left_to_pay": "5.00",
* "items": [
* {
* "id": "2",
* "rel_id": "2",
* "rel_type": "invoice",
* "description": "12MP Dual Camera with cover",
* "long_description": "The JBL Cinema SB110 is a hassle-free soundbar",
* "qty": "1.00",
* "rate": "5.00",
* "unit": "",
* "item_order": "1"
* }
* ],
* "attachments": [],
* "visible_attachments_to_customer_found": false,
* "client": {
* "userid": "1",
* "company": "trueline",
* "vat": "",
* "phonenumber": "",
* "country": "0",
* "city": "",
* "zip": "",
* "state": "",
* "address": "",
* "website": "",
* "datecreated": "2020-05-19 20:07:49",
* "active": "1",
* "leadid": null,
* "billing_street": "",
* "billing_city": "",
* "billing_state": "",
* "billing_zip": "",
* "billing_country": "0",
* "shipping_street": "",
* "shipping_city": "",
* "shipping_state": "",
* "shipping_zip": "",
* "shipping_country": "0",
* "longitude": null,
* "latitude": null,
* "default_language": "english",
* "default_currency": "0",
* "show_primary_contact": "0",
* "stripe_id": null,
* "registration_confirmed": "1",
* "addedfrom": "1"
* },
* "payments": [],
* "scheduled_email": null
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_get($id = '') {
// Fetch invoices without specifying order
$data = $this->Api_model->get_table('invoices', $id);
// Check if the data store contains any invoices
if ($data) {
// Sort $data array by 'id' in ascending order
usort($data, function($a, $b) {
return $a['id'] - $b['id'];
});
// Optionally, apply additional custom data formatting if needed
$data = $this->Api_model->get_api_custom_data($data, "invoice", $id);
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
// Set the response and exit with a not found message
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {get} api/invoices/search/:keysearch Search invoice information
* @apiVersion 0.1.0
* @apiName GetInvoiceSearch
* @apiGroup Invoices
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} keysearch Search Keywords.
*
* @apiSuccess {Object} Invoice Information.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "id": "19",
* "sent": "0",
* "datesend": null,
* "clientid": "3",
* "deleted_customer_name": null,
* "number": "19",
* "prefix": "INV-",
* "number_format": "1",
* "datecreated": "2020-08-18 21:19:51",
* "date": "2020-07-04",
* "duedate": "2020-08-03",
* "currency": "1",
* "subtotal": "20.00",
* "total_tax": "1.80",
* "total": "21.80",
* "adjustment": "0.00",
* "addedfrom": "1",
* "hash": "809c0e4c9efba2a3bedfdb5871dc6240",
* "status": "2",
* "clientnote": "",
* "adminnote": "",
* "last_overdue_reminder": null,
* "cancel_overdue_reminders": "0",
* "allowed_payment_modes": "['1']",
* "token": null,
* "discount_percent": "0.00",
* "discount_total": "0.00",
* "discount_type": "",
* "recurring": "0",
* "recurring_type": null,
* "custom_recurring": "0",
* "cycles": "0",
* "total_cycles": "0",
* "is_recurring_from": null,
* "last_recurring_date": null,
* "terms": "",
* "sale_agent": "0",
* "billing_street": "",
* "billing_city": "",
* "billing_state": "",
* "billing_zip": "",
* "billing_country": "0",
* "shipping_street": "",
* "shipping_city": "",
* "shipping_state": "",
* "shipping_zip": "",
* "shipping_country": "0",
* "include_shipping": "0",
* "show_shipping_on_invoice": "1",
* "show_quantity_as": "1",
* "project_id": "0",
* "subscription_id": "0",
* "userid": "3",
* "company": "xyz",
* "vat": "",
* "phonenumber": "",
* "country": "0",
* "city": "",
* "zip": "",
* "state": "",
* "address": "",
* "website": "",
* "active": "1",
* "leadid": null,
* "longitude": null,
* "latitude": null,
* "default_language": "",
* "default_currency": "0",
* "show_primary_contact": "0",
* "stripe_id": null,
* "registration_confirmed": "1",
* "invoiceid": "19"
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No Data Were Found"
* }
*/
public function data_search_get($key = '') {
$data = $this->Api_model->search('invoices', $key);
// Check if the data store contains
if ($data) {
$data = $this->Api_model->get_api_custom_data($data, "invoice");
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
// Set the response and exit
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {post} api/invoices Add New invoice
* @apiVersion 0.1.0
* @apiName PostInvoice
* @apiGroup Invoices
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {Number} clientid Mandatory. Customer id
* @apiParam {Number} number Mandatory. Invoice Number
* @apiParam {Date} date Mandatory. Invoice Date
* @apiParam {Number} currency Mandatory. currency field
* @apiParam {Array} newitems Mandatory. New Items to be added
* @apiParam {Decimal} subtotal Mandatory. calculation based on item Qty, Rate and Tax
* @apiParam {Decimal} total Mandatory. calculation based on subtotal, Discount and Adjustment
* @apiParam {String} billing_street Mandatory. Street Address
* @apiParam {Array} allowed_payment_modes Mandatory. Payment modes
* @apiParam {String} [billing_city] Optional. City Name for billing
* @apiParam {String} [billing_state] Optional. Name of state for billing
* @apiParam {Number} [billing_zip] Optional. Zip code
* @apiParam {Number} [billing_country] Optional. Country code
* @apiParam {boolean} [include_shipping="no"] Optional. set yes if you want add Shipping Address
* @apiParam {boolean} [show_shipping_on_invoice] Optional. Shows shipping details in invoice.
* @apiParam {String} [shipping_street] Optional. Address of shipping
* @apiParam {String} [shipping_city] Optional. City name for shipping
* @apiParam {String} [shipping_state] Optional. Name of state for shipping
* @apiParam {Number} [shipping_zip] Optional. Zip code for shipping
* @apiParam {Number} [shipping_country] Optional. Country code
* @apiParam {Date} [duedate] Optional. Due date for Invoice
* @apiParam {boolean} [cancel_overdue_reminders] Optional. Prevent sending overdue remainders for invoice
* @apiParam {String} [tags] Optional. TAGS comma separated
* @apiParam {Number} [sale_agent] Optional. Sale Agent name
* @apiParam {String} [recurring] Optional. recurring 1 to 12 or custom
* @apiParam {String} [discount_type] Optional. before_tax / after_tax discount type
* @apiParam {Number} [repeat_every_custom] Optional. if recurring is custom set number gap
* @apiParam {String} [repeat_type_custom] Optional. if recurring is custom set gap option day/week/month/year
* @apiParam {Number} [cycles] Optional. number of cycles 0 for infinite
* @apiParam {String} [adminnote] Optional. notes by admin
* @apiParam {Array} [removed_items] Optional. Items to be removed
* @apiParam {String} [clientnote] Optional. client notes
* @apiParam {String} [terms] Optional. Terms
*
* @apiParamExample {Multipart Form} Request-Example:
* [
* "clientid"=>1,
* "number"=>"00001",
* "date"=>"2020-09-07",
* "currency"=>1,
* "newitems[0][description]"=>"item 1 description",
* "newitems[0][long_description]"=>"item 1 long description",
* "newitems[0][qty]"=>1,
* "newitems[0][rate]"=>100,
* "newitems[0][order]"=>1,
* "newitems[0][taxname][]"=>CGST|9.00,
* "newitems[0][taxname][]"=>SGST|9.00,
* "newitems[0][unit]"=>"",
* "newitems[1][description]"=>"item 2 description",
* "newitems[1][long_description]"=>"item 2 long description",
* "newitems[1][qty]"=>1,
* "newitems[1][rate]"=>100,
* "newitems[1][order]"=>1,
* "newitems[1][taxname][]"=>CGST|9.00,
* "newitems[1][taxname][]"=>SGST|9.00,
* "newitems[1][unit]"=>"",
* "subtotal"=>236.00,
* "total"=>236.00,
* "billing_street"=>"billing address",
* "allowed_payment_modes[0]"=>1,
* "allowed_payment_modes[1]"=>2,
* ....
* ]
*
*
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Invoice Added Successfully
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Invoice Added Successfully"
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Invoice add fail
* @apiError {String} newitems[] The Items field is required
* @apiError {String} number The Invoice number is already in use
* @apiError {String} allowed_payment_modes[] The Allow Payment Mode field is required
* @apiError {String} billing_street The Billing Street field is required
* @apiError {String} subtotal The Sub Total field is required
* @apiError {String} total The Total field is required
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Invoice Add Fail"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 409 Conflict
* {
* "status": false,
* "error": {
* "number":"The Invoice number is already in use"
* },
* "message": "The Invoice number is already in use"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "error": {
* "allowed_payment_modes[]": "The Allow Payment Mode field is required."
* },
* "message": "<p>The Allow Payment Mode field is required.</p>\n"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "error": {
* "billing_street": "The Billing Street field is required"
* },
* "message": "<p>The Billing Street field is required</p>\n"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "error": {
* "newitems[]": "The Items field is required"
* },
* "message": "<p>The Items field is required</p>\n"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "error": {
* "subtotal": "The Sub Total field is required"
* },
* "message": "<p>The Sub Total field is required</p>\n"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "error": {
* "total": "The Total field is required"
* },
* "message": "<p>The Total field is required</p>\n"
* }
*
*/
public function data_post() {
\modules\api\core\Apiinit::the_da_vinci_code('api');
error_reporting(0);
$data = $this->input->post();
$this->form_validation->set_rules('clientid', 'Customer', 'trim|required|max_length[255]');
$this->form_validation->set_rules('number', 'Invoice number', 'trim|required|max_length[255]|callback_validate_invoice_number[0]');
$this->form_validation->set_rules('date', 'Invoice date', 'trim|required|max_length[255]');
$this->form_validation->set_rules('currency', 'Currency', 'trim|required|max_length[255]');
$this->form_validation->set_rules('newitems[]', 'Items', 'required');
$this->form_validation->set_rules('allowed_payment_modes[]', 'Allow Payment Mode', 'trim|required|max_length[255]');
$this->form_validation->set_rules('billing_street', 'Billing Street', 'trim|required|max_length[255]');
$this->form_validation->set_rules('subtotal', 'Sub Total', 'trim|required|decimal|greater_than[0]');
$this->form_validation->set_rules('total', 'Total', 'trim|required|decimal|greater_than[0]');
if ($this->form_validation->run() == FALSE) {
$message = array('status' => FALSE, 'error' => $this->form_validation->error_array(), 'message' => validation_errors());
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$this->load->model('invoices_model');
$id = $this->invoices_model->add($data);
if ($id > 0 && !empty($id)) {
// success
$message = array(
'status' => TRUE,
'message' => 'Invoice Added Successfully',
'record_id' => $id
);
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('status' => FALSE, 'message' => 'Invoice Add Fail');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {delete} api/invoices/:id Delete invoice
* @apiVersion 0.1.0
* @apiName DeleteInvoice
* @apiGroup Invoices
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Invoice Deleted Successfully
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Invoice Deleted Successfully"
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Invoice Delete Fail
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Invoice Delete Fail"
* }
*/
public function data_delete($id = '') {
$id = $this->security->xss_clean($id);
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid Invoice ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$this->load->model('invoices_model');
$is_exist = $this->invoices_model->get($id);
if (is_object($is_exist)) {
$output = $this->invoices_model->delete($id);
if ($output === TRUE) {
// success
$message = array('status' => TRUE, 'message' => 'Invoice Deleted Successfully');
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('status' => FALSE, 'message' => 'Invoice Delete Fail');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
} else {
$message = array('status' => FALSE, 'message' => 'Invalid Invoice ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {put} api/invoices/:id Update invoice
* @apiVersion 0.1.0
* @apiName PutInvoice
* @apiGroup Invoices
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {Number} clientid Mandatory Customer id.
*
* @apiParam {Number} clientid Mandatory. Customer id
* @apiParam {Number} number Mandatory. Invoice Number
* @apiParam {Date} date Mandatory. Invoice Date
* @apiParam {Number} currency Mandatory. currency field
* @apiParam {Array} newitems Mandatory. New Items to be added
* @apiParam {Decimal} subtotal Mandatory. calculation based on item Qty, Rate and Tax
* @apiParam {Decimal} total Mandatory. calculation based on subtotal, Discount and Adjustment
* @apiParam {String} billing_street Mandatory. Street Address
* @apiParam {Array} allowed_payment_modes Mandatory. Payment modes
* @apiParam {String} [billing_city] Optional. City Name for billing
* @apiParam {String} [billing_state] Optional. Name of state for billing
* @apiParam {Number} [billing_zip] Optional. Zip code
* @apiParam {Number} [billing_country] Optional. Country code
* @apiParam {boolean} [include_shipping="no"] Optional. set yes if you want add Shipping Address
* @apiParam {boolean} [show_shipping_on_invoice] Optional. Shows shipping details in invoice.
* @apiParam {String} [shipping_street] Optional. Address of shipping
* @apiParam {String} [shipping_city] Optional. City name for shipping
* @apiParam {String} [shipping_state] Optional. Name of state for shipping
* @apiParam {Number} [shipping_zip] Optional. Zip code for shipping
* @apiParam {Number} [shipping_country] Optional. Country code
* @apiParam {Date} [duedate] Optional. Due date for Invoice
* @apiParam {boolean} [cancel_overdue_reminders] Optional. Prevent sending overdue remainders for invoice
* @apiParam {String} [tags] Optional. TAGS comma separated
* @apiParam {Number} [sale_agent] Optional. Sale Agent name
* @apiParam {String} [recurring] Optional. recurring 1 to 12 or custom
* @apiParam {String} [discount_type] Optional. before_tax / after_tax discount type
* @apiParam {Number} [repeat_every_custom] Optional. if recurring is custom set number gap
* @apiParam {String} [repeat_type_custom] Optional. if recurring is custom set gap option day/week/month/year
* @apiParam {Number} [cycles] Optional. number of cycles 0 for infinite
* @apiParam {String} [adminnote] Optional. notes by admin
* @apiParam {Array} [items] Optional. Existing items with Id
* @apiParam {Array} [removed_items] Optional. Items to be removed
* @apiParam {String} [clientnote] Optional. client notes
* @apiParam {String} [terms] Optional. Terms
*
* @apiParamExample {json} Request-Example:
* {
* "clientid": "1",
* "billing_street": "billing address",
* "billing_city": "billing city name",
* "billing_state": "billing state name",
* "billing_zip": "billing zip code",
* "billing_country": "",
* "include_shipping": "on",
* "show_shipping_on_invoice": "on",
* "shipping_street": "shipping address",
* "shipping_city": "city name",
* "shipping_state": "state name",
* "shipping_zip": "zip code",
* "shipping_country": "",
* "number": "000001",
* "date": "2020-08-28",
* "duedate": "2020-09-27",
* "cancel_overdue_reminders": "on",
* "tags": "TAG 1,TAG 2",
* "allowed_payment_modes": [
* "1","2"
* ],
* "currency": "1",
* "sale_agent": "1",
* "recurring": "custom",
* "discount_type": "before_tax",
* "repeat_every_custom": "7",
* "repeat_type_custom": "day",
* "cycles": "0",
* "adminnote": "TEST",
* "show_quantity_as": "1",
* "items": {
* "1": {
* "itemid": "1",
* "order": "1",
* "description": "item description",
* "long_description": "item long description",
* "qty": "1",
* "unit": "1",
* "rate": "10.00"
* }
* },
* "removed_items": [
* "2",
* "3"
* ],
* "newitems": {
* "2": {
* "order": "2",
* "description": "item 2 description",
* "long_description": "item 2 logn description",
* "qty": "1",
* "unit": "",
* "rate": "100.00"
* }
* },
* "subtotal": "10.00",
* "discount_percent": "10",
* "discount_total": "1.00",
* "adjustment": "1",
* "total": "10.00",
* "clientnote": "client note",
* "terms": "terms"
* }
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": false,
* "message": "Invoice Updated Successfully"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Invoice Update Fail"
* }
*
* @apiError {String} number The Invoice number is already in use
*
* @apiErrorExample Error-Response:
* HTTP/1.1 409 Conflict
* {
* "status": false,
* "error": {
* "number":"The Invoice number is already in use"
* },
* "message": "The Invoice number is already in use"
* }
*
*/
public function data_put($id = '') {
// JSON data is now automatically parsed in REST_Controller
if (empty($_POST) || !isset($_POST)) {
$message = array('status' => FALSE, 'message' => 'Data Not Acceptable OR Not Provided');
$this->response($message, REST_Controller::HTTP_NOT_ACCEPTABLE);
}
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid Invoice ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$this->form_validation->set_rules('number', 'Invoice number', 'trim|required|max_length[255]|callback_validate_invoice_number[' . $id . ']');
$this->form_validation->set_rules('date', 'Invoice date', 'trim|required|max_length[255]');
$this->form_validation->set_rules('currency', 'Currency', 'trim|required|max_length[255]');
$this->form_validation->set_rules('items[]', 'Items', 'required');
$this->form_validation->set_rules('allowed_payment_modes[]', 'Allow Payment Mode', 'trim|required|max_length[255]');
$this->form_validation->set_rules('billing_street', 'Billing Street', 'trim|required|max_length[255]');
$this->form_validation->set_rules('subtotal', 'Sub Total', 'trim|required|decimal|greater_than[0]');
$this->form_validation->set_rules('total', 'Total', 'trim|required|decimal|greater_than[0]');
if ($this->form_validation->run() == FALSE) {
$message = array('status' => FALSE, 'error' => $this->form_validation->error_array(), 'message' => validation_errors());
$this->response($message, REST_Controller::HTTP_CONFLICT);
} else {
$this->load->model('invoices_model');
$is_exist = $this->invoices_model->get($id);
if (!is_object($is_exist)) {
$message = array('status' => FALSE, 'message' => 'Invoice ID Doesn\'t Not Exist.');
$this->response($message, REST_Controller::HTTP_CONFLICT);
}
if (is_object($is_exist)) {
$data = $this->input->post();
$data['isedit'] = "";
$success = $this->invoices_model->update($data, $id);
if ($success == true) {
$message = array('status' => TRUE, 'message' => "Invoice Updated Successfully",);
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('status' => FALSE, 'message' => 'Invoice Update Fail');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
} else {
$message = array('status' => FALSE, 'message' => 'Invalid Invoice ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
}
public function validate_invoice_number($number, $invoiceid) {
$isedit = 'false';
if (!empty($invoiceid)) {
$isedit = 'true';
}
$this->form_validation->set_message('validate_invoice_number', 'The {field} is already in use');
$original_number = null;
$date = $this->input->post('date');
if (!empty($invoiceid)) {
$data = $this->Api_model->get_table('invoices', $invoiceid);
$original_number = $data->number;
if (empty($date)) {
$date = $data->date;
}
}
$number = trim($number);
$number = ltrim($number, '0');
if ($isedit == 'true') {
if ($number == $original_number) {
return TRUE;
}
}
if (total_rows(db_prefix() . 'invoices', ['YEAR(date)' => date('Y', strtotime(to_sql_date($date))), 'number' => $number, ]) > 0) {
return FALSE;
} else {
return TRUE;
}
}
}

124
api/controllers/Items.php Normal file
View File

@@ -0,0 +1,124 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
// This can be removed if you use __autoload() in config.php OR use Modular Extensions
/** @noinspection PhpIncludeInspection */
require __DIR__ . '/REST_Controller.php';
/**
* This is an example of a few basic user interaction methods you could use
* all done with a hardcoded array
*
* @package CodeIgniter
* @subpackage Rest Server
* @category Controller
* @author Phil Sturgeon, Chris Kacerguis
* @license MIT
* @link https://github.com/chriskacerguis/codeigniter-restserver
*/
class Items extends REST_Controller {
function __construct() {
// Construct the parent class
parent::__construct();
}
/**
* @api {get} api/items/:id Request Invoice Item's information
* @apiVersion 0.1.0
* @apiName GetItem
* @apiGroup Items
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiSuccess {Object} Item item information.
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "itemid": "1",
* "rate": "100.00",
* "taxrate": "5.00",
* "taxid": "1",
* "taxname": "PAYPAL",
* "taxrate_2": "9.00",
* "taxid_2": "2",
* "taxname_2": "CGST",
* "description": "JBL Soundbar",
* "long_description": "The JBL Cinema SB110 is a hassle-free soundbar",
* "group_id": "0",
* "group_name": null,
* "unit": ""
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_get($id = '') {
// If the id parameter doesn't exist return all the
$data = $this->Api_model->get_table('invoice_items', $id);
// Check if the data store contains
if ($data) {
$data = $this->Api_model->get_api_custom_data($data, "items", $id);
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
// Set the response and exit
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {get} api/items/search/:keysearch Search Invoice Item's information
* @apiVersion 0.1.0
* @apiName GetItemSearch
* @apiGroup Items
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} keysearch Search Keywords
*
* @apiSuccess {Object} Item Item Information
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "rate": "100.00",
* "id": "1",
* "name": "(100.00) JBL Soundbar",
* "subtext": "The JBL Cinema SB110 is a hassle-free soundbar..."
* }
*
* @apiError {Boolean} status Request status
* @apiError {String} message No data were found
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_search_get($key = '') {
$data = $this->Api_model->search('invoice_items', $key);
// Check if the data store contains
if ($data) {
$data = $this->Api_model->get_api_custom_data($data, "items");
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
// Set the response and exit
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
}

178
api/controllers/Key.php Normal file
View File

@@ -0,0 +1,178 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
// This can be removed if you use __autoload() in config.php OR use Modular Extensions
/** @noinspection PhpIncludeInspection */
require __DIR__ . '/REST_Controller.php';
/**
* Keys Controller
* This is a basic Key Management REST controller to make and delete keys
*
* @package CodeIgniter
* @subpackage Rest Server
* @category Controller
* @author Phil Sturgeon, Chris Kacerguis
* @license MIT
* @link https://github.com/chriskacerguis/codeigniter-restserver
*/
class Key extends REST_Controller {
protected $methods = [
'index_put' => ['level' => 10, 'limit' => 10],
'index_delete' => ['level' => 10],
'level_post' => ['level' => 10],
'regenerate_post' => ['level' => 10],
];
/**
* Insert a key into the database
*
* @access public
* @return void
*/
public function index_put() {
// Build a new key
$key = $this->_generate_key();
// If no key level provided, provide a generic key
$level = $this->put('level') ? $this->put('level') : 1;
$ignore_limits = ctype_digit($this->put('ignore_limits')) ? (int)$this->put('ignore_limits') : 1;
// Insert the new key
if ($this->_insert_key($key, ['level' => $level, 'ignore_limits' => $ignore_limits])) {
$this->response(['status' => TRUE, 'key' => $key], REST_Controller::HTTP_CREATED); // CREATED (201) being the HTTP response code
} else {
$this->response(['status' => FALSE, 'message' => 'Could not save the key'], REST_Controller::HTTP_INTERNAL_SERVER_ERROR); // INTERNAL_SERVER_ERROR (500) being the HTTP response code
}
}
/**
* Remove a key from the database to stop it working
*
* @access public
* @return void
*/
public function index_delete() {
$key = $this->delete('key');
// Does this key exist?
if (!$this->_key_exists($key)) {
// It doesn't appear the key exists
$this->response(['status' => FALSE, 'message' => 'Invalid API key'], REST_Controller::HTTP_BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code
}
// Destroy it
$this->_delete_key($key);
// Respond that the key was destroyed
$this->response(['status' => TRUE, 'message' => 'API key was deleted'], REST_Controller::HTTP_NO_CONTENT); // NO_CONTENT (204) being the HTTP response code
}
/**
* Change the level
*
* @access public
* @return void
*/
public function level_post() {
$key = $this->post('key');
$new_level = $this->post('level');
// Does this key exist?
if (!$this->_key_exists($key)) {
// It doesn't appear the key exists
$this->response(['status' => FALSE, 'message' => 'Invalid API key'], REST_Controller::HTTP_BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code
}
// Update the key level
if ($this->_update_key($key, ['level' => $new_level])) {
$this->response(['status' => TRUE, 'message' => 'API key was updated'], REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
$this->response(['status' => FALSE, 'message' => 'Could not update the key level'], REST_Controller::HTTP_INTERNAL_SERVER_ERROR); // INTERNAL_SERVER_ERROR (500) being the HTTP response code
}
}
/**
* Suspend a key
*
* @access public
* @return void
*/
public function suspend_post() {
$key = $this->post('key');
// Does this key exist?
if (!$this->_key_exists($key)) {
// It doesn't appear the key exists
$this->response(['status' => FALSE, 'message' => 'Invalid API key'], REST_Controller::HTTP_BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code
}
// Update the key level
if ($this->_update_key($key, ['level' => 0])) {
$this->response(['status' => TRUE, 'message' => 'Key was suspended'], REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
$this->response(['status' => FALSE, 'message' => 'Could not suspend the user'], REST_Controller::HTTP_INTERNAL_SERVER_ERROR); // INTERNAL_SERVER_ERROR (500) being the HTTP response code
}
}
/**
* Regenerate a key
*
* @access public
* @return void
*/
public function regenerate_post() {
$old_key = $this->post('key');
$key_details = $this->_get_key($old_key);
// Does this key exist?
if (!$key_details) {
// It doesn't appear the key exists
$this->response(['status' => FALSE, 'message' => 'Invalid API key'], REST_Controller::HTTP_BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code
}
// Build a new key
$new_key = $this->_generate_key();
// Insert the new key
if ($this->_insert_key($new_key, ['level' => $key_details->level, 'ignore_limits' => $key_details->ignore_limits])) {
// Suspend old key
$this->_update_key($old_key, ['level' => 0]);
$this->response(['status' => TRUE, 'key' => $new_key], REST_Controller::HTTP_CREATED); // CREATED (201) being the HTTP response code
} else {
$this->response(['status' => FALSE, 'message' => 'Could not save the key'], REST_Controller::HTTP_INTERNAL_SERVER_ERROR); // INTERNAL_SERVER_ERROR (500) being the HTTP response code
}
}
/* Helper Methods */
private function _generate_key() {
do {
// Generate a random salt
$salt = base_convert(bin2hex($this->security->get_random_bytes(64)), 16, 36);
// If an error occurred, then fall back to the previous method
if ($salt === FALSE) {
$salt = hash('sha256', time() . mt_rand());
}
$new_key = substr($salt, 0, config_item('rest_key_length'));
} while ($this->_key_exists($new_key));
return $new_key;
}
/* Private Data Methods */
private function _get_key($key) {
return $this->rest->db->where(config_item('rest_key_column'), $key)->get(config_item('rest_keys_table'))->row();
}
private function _key_exists($key) {
return $this->rest->db->where(config_item('rest_key_column'), $key)->count_all_results(config_item('rest_keys_table')) > 0;
}
private function _insert_key($key, $data) {
$data[config_item('rest_key_column') ] = $key;
$data['date_created'] = function_exists('now') ? now() : time();
return $this->rest->db->set($data)->insert(config_item('rest_keys_table'));
}
private function _update_key($key, $data) {
return $this->rest->db->where(config_item('rest_key_column'), $key)->update(config_item('rest_keys_table'), $data);
}
private function _delete_key($key) {
return $this->rest->db->where(config_item('rest_key_column'), $key)->delete(config_item('rest_keys_table'));
}
}

502
api/controllers/Leads.php Normal file
View File

@@ -0,0 +1,502 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
require __DIR__ . '/REST_Controller.php';
/**
* This is an example of a few basic user interaction methods you could use
* all done with a hardcoded array
*
* @package CodeIgniter
* @subpackage Rest Server
* @category Controller
*/
class Leads extends REST_Controller {
function __construct() {
// Construct the parent class
parent::__construct();
$this->load->model('Api_model');
}
/**
* @api {get} api/leads/ Request all Leads
* @apiName GetLeads
* @apiGroup Leads
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
*
* @apiSuccess {Object} Lead information.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "id": "17",
* "hash": "c6e938f8b7a40b1bcfd98dc04f6eeee0-60d9c039da373a685fc0f74d4bfae631",
* "name": "Lead name",
* "contact": "",
* "title": "",
* "company": "Themesic Interactive",
* "description": "",
* "country": "243",
* "zip": null,
* "city": "London",
* "zip": "WC13KJ",
* "state": "London",
* "address": "1a The Alexander Suite Silk Point",
* "assigned": "5",
* "dateadded": "2019-07-18 08:59:28",
* "from_form_id": "0",
* "status": "0",
* "source": "4",
* ...
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
/**
* @api {get} api/leads/:id Request Lead information
* @apiName GetLead
* @apiGroup Leads
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {Number} id Lead unique ID.
*
* @apiSuccess {Object} Lead information.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "id": "17",
* "hash": "c6e938f8b7a40b1bcfd98dc04f6eeee0-60d9c039da373a685fc0f74d4bfae631",
* "name": "Lead name",
* "contact": "",
* "title": "",
* "company": "Themesic Interactive",
* "description": "",
* "country": "243",
* "zip": null,
* "city": "London",
* "zip": "WC13KJ",
* "state": "London",
* "address": "1a The Alexander Suite Silk Point",
* "assigned": "5",
* "dateadded": "2019-07-18 08:59:28",
* "from_form_id": "0",
* "status": "0",
* "source": "4",
* ...
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_get($id = '') {
// If the id parameter doesn't exist return all the
$data = $this->Api_model->get_table('leads', $id);
// Check if the data store contains
if ($data) {
$data = $this->Api_model->get_api_custom_data($data, "leads", $id);
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
// Set the response and exit
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {get} api/leads/search/:keysearch Search Lead Information
* @apiName GetLeadSearch
* @apiGroup Leads
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} keysearch Search Keywords.
*
* @apiSuccess {Object} Lead information.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "id": "17",
* "hash": "c6e938f8b7a40b1bcfd98dc04f6eeee0-60d9c039da373a685fc0f74d4bfae631",
* "name": "Lead name",
* "contact": "",
* "title": "",
* "company": "Themesic Interactive",
* "description": "",
* "country": "243",
* "zip": null,
* "city": "London",
* "zip": "WC13KJ",
* "state": "London",
* "address": "1a The Alexander Suite Silk Point",
* "assigned": "5",
* "dateadded": "2019-07-18 08:59:28",
* "from_form_id": "0",
* "status": "0",
* "source": "4",
* ...
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_search_get($key = '') {
$data = $this->Api_model->search('lead', $key);
// Check if the data store contains
if ($data) {
$data = $this->Api_model->get_api_custom_data($data, "leads");
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
// Set the response and exit
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {post} api/leads Add New Lead
* @apiName PostLead
* @apiGroup Leads
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} source Mandatory Lead source.
* @apiParam {String} status Mandatory Lead Status.
* @apiParam {String} name Mandatory Lead Name.
* @apiParam {String} assigned Mandatory Lead assigned.
* @apiParam {String} [client_id] Optional Lead From Customer.
* @apiParam {String} [tags] Optional Lead tags.
* @apiParam {String} [contact] Optional Lead contact.
* @apiParam {String} [title] Optional Position.
* @apiParam {String} [email] Optional Lead Email Address.
* @apiParam {String} [website] Optional Lead Website.
* @apiParam {String} [phonenumber] Optional Lead Phone.
* @apiParam {String} [company] Optional Lead company.
* @apiParam {String} [address] Optional Lead address.
* @apiParam {String} [city] Optional Lead City.
* @apiParam {String} [zip] Optional Zip code.
* @apiParam {String} [state] Optional Lead state.
* @apiParam {String} [country] Optional Lead Country.
* @apiParam {String} [default_language] Optional Lead Default Language.
* @apiParam {String} [description] Optional Lead description.
* @apiParam {String} [custom_contact_date] Optional Lead From Customer.
* @apiParam {String} [contacted_today] Optional Lead Contacted Today.
* @apiParam {String} [is_public] Optional Lead google sheet id.
*
* @apiParamExample {Multipart Form} Request-Example:
* array (size=20)
* 'status' => string '2' (length=1)
* 'source' => string '6' (length=1)
* 'assigned' => string '1' (length=1)
* 'client_id' => string '5' (length=1)
* 'tags' => string '' (length=0)
* 'name' => string 'Lead Name' (length=9)
* 'contact' => string 'Contact A' (length=9)
* 'title' => string 'Position A' (length=10)
* 'email' => string 'AAA@gmail.com' (length=13)
* 'website' => string '' (length=0)
* 'phonenumber' => string '123456789' (length=9)
* 'company' => string 'Themesic Interactive' (length=20)
* 'address' => string '710-712 Cách Mạng Tháng Tám, P. 5, Q. Tân Bình' (length=33)
* 'city' => string 'London' (length=6)
* 'zip' => string 'WC13KJ' (length=6)
* 'state' => string '' (length=0)
* 'default_language' => string 'english' (length=10)
* 'description' => string 'Description' (length=11)
* 'custom_contact_date' => string '' (length=0)
* 'is_public' => string 'on' (length=2)
* 'contacted_today' => string 'on' (length=2)
*
*
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Lead add successful.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Lead add successful."
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message add fail.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Lead add fail."
* }
*
*/
public function data_post() {
\modules\api\core\Apiinit::the_da_vinci_code('api');
// form validation
$this->form_validation->set_rules('name', 'Lead Name', 'trim|required|max_length[600]', array('is_unique' => 'This %s already exists please enter another Lead Name'));
$this->form_validation->set_rules('source', 'Source', 'trim|required', array('is_unique' => 'This %s already exists please enter another Lead source'));
$this->form_validation->set_rules('status', 'Status', 'trim|required', array('is_unique' => 'This %s already exists please enter another Status'));
$this->form_validation->set_rules('zip', 'Zip Core', 'trim', array('is_unique' => 'This %s already exists please enter another Zip code'));
$this->form_validation->set_rules('assigned', 'Assigned', 'trim|required', array('is_unique' => 'This %s already exists please enter another Assigned'));
if ($this->form_validation->run() == FALSE) {
// form validation error
$message = array('status' => FALSE, 'error' => $this->form_validation->error_array(), 'message' => validation_errors());
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$insert_data = ['name' => $this->input->post('name', TRUE), 'source' => $this->input->post('source', TRUE), 'status' => $this->input->post('status', TRUE), 'assigned' => $this->input->post('assigned', TRUE), 'tags' => $this->Api_model->value($this->input->post('tags', TRUE)), 'title' => $this->Api_model->value($this->input->post('title', TRUE)), 'email' => $this->Api_model->value($this->input->post('email', TRUE)), 'website' => $this->Api_model->value($this->input->post('website', TRUE)), 'phonenumber' => $this->Api_model->value($this->input->post('phonenumber', TRUE)), 'company' => $this->Api_model->value($this->input->post('company', TRUE)), 'address' => $this->Api_model->value($this->input->post('address', TRUE)), 'city' => $this->Api_model->value($this->input->post('city', TRUE)), 'zip' => $this->input->post('zip', TRUE), 'state' => $this->Api_model->value($this->input->post('state', TRUE)), 'default_language' => $this->Api_model->value($this->input->post('default_language', TRUE)), 'description' => $this->Api_model->value($this->input->post('description', TRUE)), 'custom_contact_date' => $this->Api_model->value($this->input->post('custom_contact_date', TRUE)), 'is_public' => $this->Api_model->value($this->input->post('is_public', TRUE)), 'contacted_today' => $this->Api_model->value($this->input->post('contacted_today', TRUE)) ];
if (!empty($this->input->post('custom_fields', TRUE))) {
$insert_data['custom_fields'] = $this->Api_model->value($this->input->post('custom_fields', TRUE));
}
// insert data
$this->load->model('leads_model');
$output = $this->leads_model->add($insert_data);
if ($output > 0 && !empty($output)) {
// success
$this->handle_lead_attachments_array($output);
$message = array(
'status' => TRUE,
'message' => 'Lead add successful.',
'record_id' => $output // επιστρέφουμε το ID του νέου lead
);
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('status' => FALSE, 'message' => 'Lead add fail.');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {delete} api/delete/leads/:id Delete a Lead
* @apiName DeleteLead
* @apiGroup Leads
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {Number} id lead unique ID.
*
* @apiSuccess {String} status Request status.
* @apiSuccess {String} message Lead Delete Successful.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Lead Delete Successful."
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Lead Delete Fail.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Lead Delete Fail."
* }
*/
public function data_delete($id = '') {
$id = $this->security->xss_clean($id);
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid Lead ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
// delete data
$this->load->model('leads_model');
$output = $this->leads_model->delete($id);
if ($output === TRUE) {
// success
$message = array('status' => TRUE, 'message' => 'Lead Delete Successful.');
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('status' => FALSE, 'message' => 'Lead Delete Fail.');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {put} api/leads/:id Update a lead
* @apiName PutLead
* @apiGroup Leads
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} source Mandatory Lead source.
* @apiParam {String} status Mandatory Lead Status.
* @apiParam {String} name Mandatory Lead Name.
* @apiParam {String} assigned Mandatory Lead assigned.
* @apiParam {String} [client_id] Optional Lead From Customer.
* @apiParam {String} [tags] Optional Lead tags.
* @apiParam {String} [contact] Optional Lead contact.
* @apiParam {String} [title] Optional Position.
* @apiParam {String} [email] Optional Lead Email Address.
* @apiParam {String} [website] Optional Lead Website.
* @apiParam {String} [phonenumber] Optional Lead Phone.
* @apiParam {String} [company] Optional Lead company.
* @apiParam {String} [address] Optional Lead address.
* @apiParam {String} [city] Optional Lead City.
* @apiParam {String} [zip] Optional Zip Code.
* @apiParam {String} [state] Optional Lead state.
* @apiParam {String} [country] Optional Lead Country.
* @apiParam {String} [default_language] Optional Lead Default Language.
* @apiParam {String} [description] Optional Lead description.
* @apiParam {String} [lastcontact] Optional Lead Last Contact.
* @apiParam {String} [is_public] Optional Lead google sheet id.
*
*
* @apiParamExample {json} Request-Example:
* {
* "name": "Lead name",
* "contact": "contact",
* "title": "title",
* "company": "C.TY TNHH TM VẬN TẢI & DU LỊCH ĐẠI BẢO AN",
* "description": "description",
* "tags": "",
* "city": "London",
* "zip": "WC13KJ",
* "state": "London",
* "address": "1a The Alexander Suite Silk Point",
* "assigned": "5",
* "source": "4",
* "email": "AA@gmail.com",
* "website": "www.themesic.com",
* "phonenumber": "123456789",
* "is_public": "on",
* "default_language": "english",
* "client_id": "3",
* "lastcontact": "25/07/2019 08:38:04"
* }
*
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Lead Update Successful.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Lead Update Successful."
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Lead Update Fail.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Lead Update Fail."
* }
*/
public function data_put($id = '') {
// JSON data is now automatically parsed in REST_Controller
if (empty($_POST) || !isset($_POST)) {
$this->load->library('parse_input_stream');
$_POST = $this->parse_input_stream->parse_parameters();
$_FILES = $this->parse_input_stream->parse_files();
if (empty($_POST) || !isset($_POST)) {
$message = array('status' => FALSE, 'message' => 'Data Not Acceptable OR Not Provided');
$this->response($message, REST_Controller::HTTP_NOT_ACCEPTABLE);
}
}
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid Lead ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$update_data = $this->input->post();
$update_file = isset($update_data['file']) ? $update_data['file'] : null;
unset($update_data['file']);
// update data
$this->load->model('leads_model');
$output = $this->leads_model->update($update_data, $id);
if (!empty($update_file) && count($update_file)) {
if ($output <= 0 || empty($output)) {
$output = $id;
}
}
if ($output > 0 && !empty($output)) {
// success
$attachments = $this->leads_model->get_lead_attachments($output);
foreach ($attachments as $attachment) {
$this->leads_model->delete_lead_attachment($attachment['id']);
}
$this->handle_lead_attachments_array($output);
$message = array('status' => TRUE, 'message' => 'Lead Update Successful.');
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('status' => FALSE, 'message' => 'Lead Update Fail.');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
function handle_lead_attachments_array($leadid, $index_name = 'file') {
$path = get_upload_path_by_type('lead') . $leadid . '/';
$CI = & get_instance();
if (isset($_FILES[$index_name]['name']) && ($_FILES[$index_name]['name'] != '' || is_array($_FILES[$index_name]['name']) && count($_FILES[$index_name]['name']) > 0)) {
if (!is_array($_FILES[$index_name]['name'])) {
$_FILES[$index_name]['name'] = [$_FILES[$index_name]['name']];
$_FILES[$index_name]['type'] = [$_FILES[$index_name]['type']];
$_FILES[$index_name]['tmp_name'] = [$_FILES[$index_name]['tmp_name']];
$_FILES[$index_name]['error'] = [$_FILES[$index_name]['error']];
$_FILES[$index_name]['size'] = [$_FILES[$index_name]['size']];
}
_file_attachments_index_fix($index_name);
for ($i = 0; $i < count($_FILES[$index_name]['name']); $i++) {
// Get the temp file path
$tmpFilePath = $_FILES[$index_name]['tmp_name'][$i];
// Make sure we have a filepath
if (!empty($tmpFilePath) && $tmpFilePath != '') {
if (_perfex_upload_error($_FILES[$index_name]['error'][$i]) || !_upload_extension_allowed($_FILES[$index_name]['name'][$i])) {
continue;
}
_maybe_create_upload_path($path);
$filename = unique_filename($path, $_FILES[$index_name]['name'][$i]);
$newFilePath = $path . $filename;
// Upload the file into the temp dir
if (copy($tmpFilePath, $newFilePath)) {
unlink($tmpFilePath);
$CI = & get_instance();
$CI->load->model('leads_model');
$data = [];
$data[] = ['file_name' => $filename, 'filetype' => $_FILES[$index_name]['type'][$i], ];
$CI->leads_model->add_attachment_to_database($leadid, $data, false);
}
}
}
}
return true;
}
}

44
api/controllers/Login.php Normal file
View File

@@ -0,0 +1,44 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
require __DIR__ . '/API_Controller.php';
class Login extends API_Controller {
public function __construct() {
parent::__construct();
}
public function login_api() {
header("Access-Control-Allow-Origin: *");
// API Configuration
$this->_apiConfig(['methods' => ['POST'], ]);
// you user authentication code will go here, you can compare the user with the database or whatever
$payload = ['id' => "Your User's ID", 'other' => "Some other data"];
// Load Authorization Library or Load in autoload config file
$this->load->library('authorization_token');
// generate a token
$token = $this->authorization_token->generateToken($payload);
// return data
$this->api_return(['status' => true, "result" => ['token' => $token, ], ], 200);
}
/**
* view method
*
* @link [api/user/view]
* @method POST
* @return Response|void
*/
public function view() {
header("Access-Control-Allow-Origin: *");
// API Configuration [Return Array: User Token Data]
$user_data = $this->_apiConfig(['methods' => ['POST'], 'requireAuthorization' => true, ]);
// return data
$this->api_return(['status' => true, "result" => ['user_data' => $user_data['token_data']], ], 200);
}
public function api_key() {
$this->_APIConfig(['methods' => ['POST'], 'key' => ['header', 'Set API Key'], ]);
}
}

View File

@@ -0,0 +1,326 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
// This can be removed if you use __autoload() in config.php OR use Modular Extensions
/** @noinspection PhpIncludeInspection */
require __DIR__ . '/REST_Controller.php';
/**
* This is an example of a few basic user interaction methods you could use
* all done with a hardcoded array
*
* @package CodeIgniter
* @subpackage Rest Server
* @category Controller
* @author Phil Sturgeon, Chris Kacerguis
* @license MIT
* @link https://github.com/chriskacerguis/codeigniter-restserver
*/
class Milestones extends REST_Controller {
function __construct() {
// Construct the parent class
parent::__construct();
}
/**
* @api {get} api/milestones/:id Request Milestones information
* @apiName GetMilestones
* @apiGroup Milestones
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {Number} id Milestones unique ID.
*
* @apiSuccess {Object} Milestones information.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "id": "5",
* "name": "MIlestone A",
* "description": "",
* "description_visible_to_customer": "0",
* "due_date": "2019-09-30",
* "project_id": "2",
* "color": null,
* "milestone_order": "1",
* "datecreated": "2019-07-19",
* "total_tasks": "0",
* "total_finished_tasks": "0"
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_get($id = '') {
// If the id parameter doesn't exist return all the
$data = $this->Api_model->get_table('milestones', $id);
// Check if the data store contains
if ($data) {
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
// Set the response and exit
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {get} api/milestones/search/:keysearch Search Milestones Information
* @apiName GetMilestoneSearch
* @apiGroup Milestones
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} keysearch Search Keywords.
*
* @apiSuccess {Object} Milestones information.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "id": "5",
* "name": "MIlestone A",
* "description": "",
* "description_visible_to_customer": "0",
* "due_date": "2019-09-30",
* "project_id": "2",
* "color": null,
* "milestone_order": "1",
* "datecreated": "2019-07-19",
* "total_tasks": "0",
* "total_finished_tasks": "0"
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_search_get($key = '') {
// If the id parameter doesn't exist return all the
$data = $this->Api_model->search('milestones', $key);
// Check if the data store contains
if ($data) {
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
// Set the response and exit
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {post} api/milestones Add New Milestone
* @apiName PostMilestone
* @apiGroup Milestones
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} project_id Mandatory project id.
* @apiParam {String} name Mandatory Milestone Name.
* @apiParam {Date} due_date Mandatory Milestone Due date.
* @apiParam {String} [description] Optional Milestone Description.
* @apiParam {String} [description_visible_to_customer] Show description to customer.
* @apiParam {String} [milestone_order] Optional Milestone Order.
*
*
* @apiParamExample {Multipart Form} Request-Example:
* array (size=6)
* 'project_id' => string '2' (length=1)
* 'name' => string 'Milestone A' (length=11)
* 'due_date' => string '30/07/2019' (length=10)
* 'description' => string 'Description' (length=11)
* 'description_visible_to_customer' => string 'on' (length=2)
* 'milestone_order' => string '1' (length=1)
*
*
* @apiSuccess {String} status Request status.
* @apiSuccess {String} message Milestone add successful.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Milestone add successful."
* }
*
* @apiError {String} status Request status.
* @apiError {String} message Milestone add fail.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Milestone add fail."
* }
*
*/
public function data_post() {
// form validation
$this->form_validation->set_rules('name', 'Milestone Name', 'trim|required|max_length[600]', array('is_unique' => 'This %s already exists please enter another Milestone Name'));
$this->form_validation->set_rules('project_id', 'Project id', 'trim|required', array('is_unique' => 'This %s already exists please enter another Project id'));
$this->form_validation->set_rules('due_date', 'Milestone Due Date', 'trim|required', array('is_unique' => 'This %s already exists please enter another Milestone Due Date'));
if ($this->form_validation->run() == FALSE) {
// form validation error
$message = array('status' => FALSE, 'error' => $this->form_validation->error_array(), 'message' => validation_errors());
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$insert_data = ['name' => $this->input->post('name', TRUE), 'due_date' => $this->input->post('due_date', TRUE), 'project_id' => $this->input->post('project_id', TRUE), 'description' => $this->Api_model->value($this->input->post('description', TRUE)), 'description_visible_to_customer' => $this->Api_model->value($this->input->post('description_visible_to_customer', TRUE)), 'milestone_order' => $this->Api_model->value($this->input->post('milestone_order', TRUE)) ];
// insert data
$this->load->model('projects_model');
$output = $this->projects_model->add_milestone($insert_data);
if ($output > 0 && !empty($output)) {
// success
$message = array(
'status' => TRUE,
'message' => 'Milestone add successful.',
'record_id' => $output // επιστρέφουμε το ID του νέου milestone
);
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('status' => FALSE, 'message' => 'Milestone add fail.');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {delete} api/delete/milestones/:id Delete a Milestone
* @apiName DeleteMilestone
* @apiGroup Milestones
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {Number} id Milestone unique ID.
*
* @apiSuccess {String} status Request status.
* @apiSuccess {String} message Milestone Delete Successful.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Milestone Delete Successful."
* }
*
* @apiError {String} status Request status.
* @apiError {String} message Milestone Delete Fail.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Milestone Delete Fail."
* }
*/
public function data_delete($id = '') {
$id = $this->security->xss_clean($id);
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid Milestone ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
// delete data
$this->load->model('projects_model');
$output = $this->projects_model->delete_milestone($id);
if ($output === TRUE) {
// success
$message = array('status' => TRUE, 'message' => 'Milestone Delete Successful.');
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('status' => FALSE, 'message' => 'Milestone Delete Fail.');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {put} api/milestones/:id Update a Milestone
* @apiName PutMilestone
* @apiGroup Milestones
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} project_id Mandatory project id.
* @apiParam {String} name Mandatory Milestone Name.
* @apiParam {Date} due_date Mandatory Milestone Due date.
* @apiParam {String} [description] Optional Milestone Description.
* @apiParam {String} [description_visible_to_customer] Show description to customer.
* @apiParam {String} [milestone_order] Optional Milestone Order.
*
*
* @apiParamExample {json} Request-Example:
* {
* "project_id": "1",
* "name": "Milestone A",
* "due_date": "30/07/2019",
* "description": "Description",
* "description_visible_to_customer": "on",
* "milestone_order": "1"
* }
* @apiSuccess {String} status Request status.
* @apiSuccess {String} message Milestone Update Successful.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Milestone Update Successful."
* }
*
* @apiError {String} status Request status.
* @apiError {String} message Milestone Update Fail.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Milestone Update Fail."
* }
*/
public function data_put($id = '') {
// JSON data is now automatically parsed in REST_Controller
if (empty($_POST) || !isset($_POST)) {
$message = array('status' => FALSE, 'message' => 'Data Not Acceptable OR Not Provided');
$this->response($message, REST_Controller::HTTP_NOT_ACCEPTABLE);
}
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid Milestone ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$update_data = $this->input->post();
// update data
$this->load->model('projects_model');
$output = $this->projects_model->update_milestone($update_data, $id);
if ($output > 0 && !empty($output)) {
// success
$message = array('status' => TRUE, 'message' => 'Milestone Update Successful.');
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('status' => FALSE, 'message' => 'Milestone Update Fail.');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
}

View File

@@ -0,0 +1,313 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
// This can be removed if you use __autoload() in config.php OR use Modular Extensions
/** @noinspection PhpIncludeInspection */
require __DIR__ . '/REST_Controller.php';
/**
* This is an example of a few basic user interaction methods you could use
* all done with a hardcoded array
*
* @package CodeIgniter
* @subpackage Rest Server
* @category Controller
*/
class Payments extends REST_Controller {
function __construct() {
// Construct the parent class
parent::__construct();
$this->load->model('payments_model');
$this->load->model('Api_model');
}
/**
* @api {get} api/payments/:id List all Payments
* @apiVersion 0.3.0
* @apiName GetPayment
* @apiGroup Payments
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {Number} payment_id Optional payment unique ID <br/><i>Note : if you don't pass Payment id then it will list all payments records</i>
*
* @apiSuccess {Array} Payments List all Payment Records.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* [
* {
* "id": "3",
* "invoiceid": "7",
* "amount": "1000.00",
* "paymentmode": "3",
* "paymentmethod": "",
* "date": "2020-06-08",
* "daterecorded": "2020-06-08 20:29:54",
* "note": "",
* "transactionid": "000355795931",
* "invoiceid": "UPI",
* "description": "",
* "show_on_pdf": "0",
* "invoices_only": "0",
* "expenses_only": "0",
* "selected_by_default": "0",
* "active": "1",
* "paymentid": "1"
* },
* {
* "id": "4",
* "invoiceid": "12",
* "amount": "-3.00",
* "paymentmode": "4",
* "paymentmethod": "",
* "date": "2020-07-04",
* "daterecorded": "2020-07-04 15:32:59",
* "note": "",
* "transactionid": "P228210122733439",
* "invoiceid": "Stripe",
* "description": "",
* "show_on_pdf": "0",
* "invoices_only": "0",
* "expenses_only": "0",
* "selected_by_default": "0",
* "active": "1",
* "paymentid": "2"
* },
* {
* "id": "1",
* "invoiceid": "14",
* "amount": "8.00",
* "paymentmode": "1",
* "paymentmethod": "",
* "date": "2020-07-04",
* "daterecorded": "2020-07-04 15:47:30",
* "note": "",
* "transactionid": "000360166374",
* "invoiceid": "Bank",
* "description": null,
* "show_on_pdf": "0",
* "invoices_only": "0",
* "expenses_only": "0",
* "selected_by_default": "1",
* "active": "1",
* "paymentid": "3"
* },
* {
* "id": "2",
* "invoiceid": "13",
* "amount": "3.00",
* "paymentmode": "2",
* "paymentmethod": "Credit card",
* "date": "2020-07-04",
* "daterecorded": "2020-07-04 15:49:56",
* "note": "",
* "transactionid": "0124875873",
* "invoiceid": "paypal",
* "description": "",
* "show_on_pdf": "0",
* "invoices_only": "0",
* "expenses_only": "0",
* "selected_by_default": "0",
* "active": "1",
* "paymentid": "4"
* }
* ]
* @apiError {Boolean} paymentmode Request paymentmode.
* @apiError {String} message No data were found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "paymentmode": false,
* "message": "No data were found"
* }
*/
public function data_get($id = '') {
// If the id parameter doesn't exist return all the
$data = $this->Api_model->payment_get($id);
// Check if the data store contains
if ($data) {
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
// Set the response and exit
$this->response(['paymentmode' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {get} api/payments/search/:keysearch Search Payments Information
* @apiVersion 0.3.0
* @apiName GetPaymentSearch
* @apiGroup Payments
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} keysearch Search Keywords
*
* @apiSuccess {Array} Payments Payments information
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* [
* {
* "id": "3",
* "invoiceid": "14",
* "amount": "8.00",
* "paymentmode": "2",
* "paymentmethod": "",
* "date": "2020-07-04",
* "daterecorded": "2020-07-04 15:47:30",
* "note": "",
* "transactionid": "",
* ...
* }
* ]
*
* @apiError {Boolean} paymentmode Request paymentmode
* @apiError {String} message No data were found
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "paymentmode": false,
* "message": "No data were found"
* }
*/
public function data_search_get($key = '') {
// If the key parameter doesn't exist return all the
$data = $this->Api_model->search('payments', $key);
// Check if the data store contains
if ($data) {
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
// Set the response and exit
$this->response(['paymentmode' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {post} api/payments Add New Payment
* @apiName PostPayment
* @apiGroup Payments
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} invoiceid Mandatory Invoice ID associated with the payment.
* @apiParam {String} amount Mandatory Payment amount.
* @apiParam {String} paymentmode Mandatory Payment mode (e.g., cash, credit card, etc.).
* @apiParam {String} [paymentmethod] Optional Payment method details.
* @apiParam {String} [note] Optional Additional payment note.
* @apiParam {String} [transactionid] Optional Transaction ID.
* @apiParam {String} [custom_fields] Optional Custom fields data.
*
* @apiParamExample {Multipart Form} Request-Example:
* array (size=6)
* 'invoiceid' => string '123' (length=3)
* 'amount' => string '250.00' (length=6)
* 'paymentmode' => string '1' (length=1)
* 'paymentmethod' => string 'Visa' (length=4)
* 'note' => string 'Payment for Invoice #123' (length=25)
* 'transactionid' => string 'TXN123456789' (length=12)
* 'custom_fields' => string '{"field1": "value1", "field2": "value2"}' (JSON format)
*
* @apiSuccess {Boolean} paymentmode Status of the request.
* @apiSuccess {String} message Payment add successful.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "paymentmode": true,
* "message": "Payment add successful."
* }
*
* @apiError {Boolean} paymentmode Status of the request.
* @apiError {String} message Payment add fail.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "paymentmode": false,
* "message": "Payment add fail."
* }
*/
public function data_post() {
\modules\api\core\Apiinit::the_da_vinci_code('api');
// form validation
$this->form_validation->set_rules('invoiceid', 'Payment Name', 'trim|required|max_length[600]', array('is_unique' => 'This %s already exists please enter another Payment Name'));
$this->form_validation->set_rules('amount', 'Source', 'trim|required', array('is_unique' => 'This %s already exists please enter another Payment amount'));
$this->form_validation->set_rules('paymentmode', 'Status', 'trim|required', array('is_unique' => 'This %s already exists please enter another Status'));
if ($this->form_validation->run() == FALSE) {
// form validation error
$message = array('paymentmode' => FALSE, 'error' => $this->form_validation->error_array(), 'message' => validation_errors());
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$insert_data = [
'invoiceid' => $this->input->post('invoiceid', TRUE),
'amount' => $this->input->post('amount', TRUE),
'paymentmode' => $this->input->post('paymentmode', TRUE),
'paymentmethod' => $this->input->post('paymentmethod', TRUE),
'date' => date('Y-m-d H:i:s'), // Current date and time
'daterecorded' => date('Y-m-d H:i:s'), // Current date and time for recording
'note' => $this->input->post('note', TRUE), // Optional note
'transactionid' => $this->input->post('transactionid', TRUE)
]; if (!empty($this->input->post('custom_fields', TRUE))) {
$insert_data['custom_fields'] = $this->Api_model->value($this->input->post('custom_fields', TRUE));
}
// insert data
$this->load->model('payments_model');
$output = $this->payments_model->add($insert_data);
// το $output πρέπει να είναι το ID του νέου payment
if ($output > 0 && !empty($output)) {
// success
$this->handle_payment_attachments_array($output);
$message = array(
'status' => TRUE,
'message' => 'Payment add successful.',
'record_id' => $output // επιστρέφουμε το ID του νέου payment
);
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('paymentmode' => FALSE, 'message' => 'Payment add fail.');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
}

View File

@@ -0,0 +1,453 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Playground extends CI_Controller
{
public function __construct()
{
parent::__construct();
// Only load essential helpers and libraries
$this->load->helper('url');
$this->load->helper('string');
$this->load->library('session');
// Disable any auto-loaded helpers that might cause issues
$this->load->library('output');
// Override any problematic properties that might be accessed by helpers
$this->load->library('app_modules');
}
/**
* Public playground index page
*/
public function index()
{
$data['title'] = 'API Playground - Test Perfex CRM API';
$data['base_url'] = base_url();
$data['api_base_url'] = base_url('api/');
// Load the sandbox view instead of swagger
$this->load->view('playground/swagger', $data);
}
/**
* Sandbox playground page
*/
public function sandbox()
{
$data['title'] = 'API Sandbox Playground - Test Perfex CRM API';
$data['base_url'] = base_url();
$data['api_base_url'] = base_url('api/');
$this->load->view('playground/sandbox', $data);
}
/**
* Execute API request (public access)
*/
public function execute_request()
{
$method = $this->input->post('method');
$endpoint = $this->input->post('endpoint');
$headers = $this->input->post('headers');
$data = $this->input->post('data');
// Validate inputs
if (empty($method) || empty($endpoint)) {
$this->output
->set_content_type('application/json')
->set_output(json_encode([
'success' => false,
'message' => 'Method and endpoint are required'
]));
return;
}
// Prepare headers
$request_headers = [];
if (!empty($headers)) {
$header_lines = explode("\n", $headers);
foreach ($header_lines as $line) {
$line = trim($line ?? '');
if (strpos($line, ':') !== false) {
list($key, $value) = explode(':', $line, 2);
$request_headers[trim($key ?? '')] = trim($value ?? '');
}
}
}
// Add default headers
$request_headers['Content-Type'] = 'application/json';
$request_headers['Accept'] = 'application/json';
// Prepare request data
$request_data = null;
if (!empty($data) && in_array($method, ['POST', 'PUT', 'PATCH'])) {
$request_data = $data;
}
// Make the API request
$response = $this->make_api_request($method, $endpoint, $request_headers, $request_data);
$this->output
->set_content_type('application/json')
->set_output(json_encode($response));
}
/**
* Get sample requests
*/
public function get_samples()
{
// Load comprehensive samples from config file
$samples = include(dirname(__DIR__) . '/config/api_samples.php');
$this->output
->set_content_type('application/json')
->set_output(json_encode($samples));
}
/**
* Make API request using cURL
*/
private function make_api_request($method, $endpoint, $headers = [], $data = null)
{
$url = base_url('api/' . ltrim($endpoint, '/'));
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_HTTPHEADER, $this->format_headers($headers));
if ($data && in_array($method, ['POST', 'PUT', 'PATCH'])) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
}
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
return [
'success' => false,
'message' => 'cURL Error: ' . $error,
'http_code' => 0
];
}
return [
'success' => true,
'response' => $response,
'http_code' => $http_code,
'url' => $url
];
}
/**
* Format headers for cURL
*/
private function format_headers($headers)
{
$formatted = [];
foreach ($headers as $key => $value) {
$formatted[] = $key . ': ' . $value;
}
return $formatted;
}
/**
* Get API documentation
*/
public function documentation()
{
$data['title'] = 'API Documentation';
$this->load->view('playground/documentation', $data);
}
/**
* Get Swagger file
*/
public function swagger() {
echo file_get_contents(dirname(__DIR__) . '/config/swagger.json');
}
/**
* Get environment configuration
*/
public function get_environment_config()
{
$config = [
'sandbox' => [
'name' => 'Sandbox Environment',
'description' => 'Safe testing environment - no production data affected',
'base_url' => base_url('api/'),
'features' => [
'Safe testing',
'No production data impact',
'Request logging',
'Sample data available'
]
],
'production' => [
'name' => 'Production Environment',
'description' => 'Live production environment - USE WITH EXTREME CAUTION!',
'base_url' => base_url('api/'),
'features' => [
'Live data access',
'Real-time operations',
'Production impact',
'Requires authentication'
],
'warning' => 'This will affect live production data!'
]
];
$this->output
->set_content_type('application/json')
->set_output(json_encode($config));
}
/**
* Get available endpoints by category
*/
public function get_endpoints()
{
$endpoints = [
'leads' => [
'name' => 'Leads',
'description' => 'Manage sales leads and prospects',
'endpoints' => [
['method' => 'GET', 'path' => '/leads', 'description' => 'Get all leads'],
['method' => 'POST', 'path' => '/leads', 'description' => 'Create new lead'],
['method' => 'GET', 'path' => '/leads/{id}', 'description' => 'Get specific lead'],
['method' => 'PUT', 'path' => '/leads/{id}', 'description' => 'Update lead'],
['method' => 'DELETE', 'path' => '/leads/{id}', 'description' => 'Delete lead'],
['method' => 'GET', 'path' => '/leads/search/{keyword}', 'description' => 'Search leads']
]
],
'projects' => [
'name' => 'Projects',
'description' => 'Manage projects and project-related data',
'endpoints' => [
['method' => 'GET', 'path' => '/projects', 'description' => 'Get all projects'],
['method' => 'POST', 'path' => '/projects', 'description' => 'Create new project'],
['method' => 'GET', 'path' => '/projects/{id}', 'description' => 'Get specific project'],
['method' => 'PUT', 'path' => '/projects/{id}', 'description' => 'Update project'],
['method' => 'DELETE', 'path' => '/projects/{id}', 'description' => 'Delete project']
]
],
'tasks' => [
'name' => 'Tasks',
'description' => 'Manage project tasks and assignments',
'endpoints' => [
['method' => 'GET', 'path' => '/tasks', 'description' => 'Get all tasks'],
['method' => 'POST', 'path' => '/tasks', 'description' => 'Create new task'],
['method' => 'GET', 'path' => '/tasks/{id}', 'description' => 'Get specific task'],
['method' => 'PUT', 'path' => '/tasks/{id}', 'description' => 'Update task'],
['method' => 'DELETE', 'path' => '/tasks/{id}', 'description' => 'Delete task']
]
],
'tickets' => [
'name' => 'Support Tickets',
'description' => 'Manage customer support tickets',
'endpoints' => [
['method' => 'GET', 'path' => '/tickets', 'description' => 'Get all tickets'],
['method' => 'POST', 'path' => '/tickets', 'description' => 'Create new ticket'],
['method' => 'GET', 'path' => '/tickets/{id}', 'description' => 'Get specific ticket'],
['method' => 'PUT', 'path' => '/tickets/{id}', 'description' => 'Update ticket'],
['method' => 'DELETE', 'path' => '/tickets/{id}', 'description' => 'Delete ticket']
]
],
'invoices' => [
'name' => 'Invoices',
'description' => 'Manage billing and invoicing',
'endpoints' => [
['method' => 'GET', 'path' => '/invoices', 'description' => 'Get all invoices'],
['method' => 'POST', 'path' => '/invoices', 'description' => 'Create new invoice'],
['method' => 'GET', 'path' => '/invoices/{id}', 'description' => 'Get specific invoice'],
['method' => 'PUT', 'path' => '/invoices/{id}', 'description' => 'Update invoice'],
['method' => 'DELETE', 'path' => '/invoices/{id}', 'description' => 'Delete invoice'],
['method' => 'GET', 'path' => '/invoices/search/{keyword}', 'description' => 'Search invoices']
]
],
'estimates' => [
'name' => 'Estimates',
'description' => 'Manage project estimates and quotes',
'endpoints' => [
['method' => 'GET', 'path' => '/estimates', 'description' => 'Get all estimates'],
['method' => 'POST', 'path' => '/estimates', 'description' => 'Create new estimate'],
['method' => 'GET', 'path' => '/estimates/{id}', 'description' => 'Get specific estimate'],
['method' => 'PUT', 'path' => '/estimates/{id}', 'description' => 'Update estimate'],
['method' => 'DELETE', 'path' => '/estimates/{id}', 'description' => 'Delete estimate'],
['method' => 'GET', 'path' => '/estimates/search/{keyword}', 'description' => 'Search estimates']
]
],
'contracts' => [
'name' => 'Contracts',
'description' => 'Manage client contracts and agreements',
'endpoints' => [
['method' => 'GET', 'path' => '/contracts', 'description' => 'Get all contracts'],
['method' => 'POST', 'path' => '/contracts', 'description' => 'Create new contract'],
['method' => 'GET', 'path' => '/contracts/{id}', 'description' => 'Get specific contract'],
['method' => 'PUT', 'path' => '/contracts/{id}', 'description' => 'Update contract'],
['method' => 'DELETE', 'path' => '/contracts/{id}', 'description' => 'Delete contract']
]
],
'credit_notes' => [
'name' => 'Credit Notes',
'description' => 'Manage credit notes and refunds',
'endpoints' => [
['method' => 'GET', 'path' => '/credit_notes', 'description' => 'Get all credit notes'],
['method' => 'POST', 'path' => '/credit_notes', 'description' => 'Create new credit note'],
['method' => 'GET', 'path' => '/credit_notes/{id}', 'description' => 'Get specific credit note'],
['method' => 'PUT', 'path' => '/credit_notes/{id}', 'description' => 'Update credit note'],
['method' => 'DELETE', 'path' => '/credit_notes/{id}', 'description' => 'Delete credit note'],
['method' => 'GET', 'path' => '/credit_notes/search/{keyword}', 'description' => 'Search credit notes']
]
],
'expenses' => [
'name' => 'Expenses',
'description' => 'Manage business expenses and reimbursements',
'endpoints' => [
['method' => 'GET', 'path' => '/expenses', 'description' => 'Get all expenses'],
['method' => 'POST', 'path' => '/expenses', 'description' => 'Create new expense'],
['method' => 'GET', 'path' => '/expenses/{id}', 'description' => 'Get specific expense'],
['method' => 'PUT', 'path' => '/expenses/{id}', 'description' => 'Update expense'],
['method' => 'DELETE', 'path' => '/expenses/{id}', 'description' => 'Delete expense'],
['method' => 'GET', 'path' => '/expenses/search/{keyword}', 'description' => 'Search expenses']
]
],
'items' => [
'name' => 'Items',
'description' => 'Manage invoice items and products',
'endpoints' => [
['method' => 'GET', 'path' => '/items', 'description' => 'Get all items'],
['method' => 'GET', 'path' => '/items/{id}', 'description' => 'Get specific item'],
['method' => 'GET', 'path' => '/items/search/{keyword}', 'description' => 'Search items']
]
],
'contacts' => [
'name' => 'Contacts',
'description' => 'Manage client contacts and relationships',
'endpoints' => [
['method' => 'GET', 'path' => '/contacts', 'description' => 'Get all contacts'],
['method' => 'POST', 'path' => '/contacts', 'description' => 'Create new contact'],
['method' => 'GET', 'path' => '/contacts/{customer_id}/{contact_id}', 'description' => 'Get specific contact'],
['method' => 'PUT', 'path' => '/contacts/{customer_id}/{contact_id}', 'description' => 'Update contact'],
['method' => 'DELETE', 'path' => '/contacts/{customer_id}', 'description' => 'Delete contact'],
['method' => 'GET', 'path' => '/contacts/search/{keyword}', 'description' => 'Search contacts']
]
],
'staff' => [
'name' => 'Staff',
'description' => 'Manage staff members and team information',
'endpoints' => [
['method' => 'GET', 'path' => '/staff', 'description' => 'Get all staff members'],
['method' => 'GET', 'path' => '/staff/{id}', 'description' => 'Get specific staff member']
]
],
'payments' => [
'name' => 'Payments',
'description' => 'Manage invoice payments and transactions',
'endpoints' => [
['method' => 'GET', 'path' => '/payments', 'description' => 'Get all payments'],
['method' => 'POST', 'path' => '/payments', 'description' => 'Create new payment'],
['method' => 'GET', 'path' => '/payments/{id}', 'description' => 'Get specific payment'],
['method' => 'PUT', 'path' => '/payments/{id}', 'description' => 'Update payment'],
['method' => 'DELETE', 'path' => '/payments/{id}', 'description' => 'Delete payment']
]
],
'proposals' => [
'name' => 'Proposals',
'description' => 'Manage project proposals and quotes',
'endpoints' => [
['method' => 'GET', 'path' => '/proposals', 'description' => 'Get all proposals'],
['method' => 'POST', 'path' => '/proposals', 'description' => 'Create new proposal'],
['method' => 'GET', 'path' => '/proposals/{id}', 'description' => 'Get specific proposal'],
['method' => 'PUT', 'path' => '/proposals/{id}', 'description' => 'Update proposal'],
['method' => 'DELETE', 'path' => '/proposals/{id}', 'description' => 'Delete proposal']
]
],
'subscriptions' => [
'name' => 'Subscriptions',
'description' => 'Manage recurring subscriptions and billing',
'endpoints' => [
['method' => 'GET', 'path' => '/subscriptions', 'description' => 'Get all subscriptions'],
['method' => 'POST', 'path' => '/subscriptions', 'description' => 'Create new subscription'],
['method' => 'GET', 'path' => '/subscriptions/{id}', 'description' => 'Get specific subscription'],
['method' => 'PUT', 'path' => '/subscriptions/{id}', 'description' => 'Update subscription'],
['method' => 'DELETE', 'path' => '/subscriptions/{id}', 'description' => 'Delete subscription']
]
],
'milestones' => [
'name' => 'Milestones',
'description' => 'Manage project milestones and deliverables',
'endpoints' => [
['method' => 'GET', 'path' => '/milestones', 'description' => 'Get all milestones'],
['method' => 'POST', 'path' => '/milestones', 'description' => 'Create new milestone'],
['method' => 'GET', 'path' => '/milestones/{id}', 'description' => 'Get specific milestone'],
['method' => 'PUT', 'path' => '/milestones/{id}', 'description' => 'Update milestone'],
['method' => 'DELETE', 'path' => '/milestones/{id}', 'description' => 'Delete milestone'],
['method' => 'GET', 'path' => '/milestones/search/{keyword}', 'description' => 'Search milestones']
]
],
'timesheets' => [
'name' => 'Timesheets',
'description' => 'Manage time tracking and work logs',
'endpoints' => [
['method' => 'GET', 'path' => '/timesheets', 'description' => 'Get all timesheets'],
['method' => 'POST', 'path' => '/timesheets', 'description' => 'Create new timesheet entry'],
['method' => 'GET', 'path' => '/timesheets/{id}', 'description' => 'Get specific timesheet'],
['method' => 'PUT', 'path' => '/timesheets/{id}', 'description' => 'Update timesheet'],
['method' => 'DELETE', 'path' => '/timesheets/{id}', 'description' => 'Delete timesheet']
]
],
'calendar' => [
'name' => 'Calendar',
'description' => 'Manage calendar events and scheduling',
'endpoints' => [
['method' => 'GET', 'path' => '/calendar', 'description' => 'Get all calendar events'],
['method' => 'POST', 'path' => '/calendar', 'description' => 'Create new calendar event'],
['method' => 'GET', 'path' => '/calendar/{id}', 'description' => 'Get specific calendar event'],
['method' => 'PUT', 'path' => '/calendar/{id}', 'description' => 'Update calendar event'],
['method' => 'DELETE', 'path' => '/calendar/{id}', 'description' => 'Delete calendar event']
]
],
'common' => [
'name' => 'Common Data',
'description' => 'Access common system data and configurations',
'endpoints' => [
['method' => 'GET', 'path' => '/common/expense_category', 'description' => 'Get expense categories'],
['method' => 'GET', 'path' => '/common/payment_mode', 'description' => 'Get payment modes'],
['method' => 'GET', 'path' => '/common/tax_data', 'description' => 'Get tax data']
]
],
'custom_fields' => [
'name' => 'Custom Fields',
'description' => 'Manage custom fields for different modules',
'endpoints' => [
['method' => 'GET', 'path' => '/custom_fields/{type}', 'description' => 'Get custom fields by type'],
['method' => 'GET', 'path' => '/custom_fields/{type}/{id}', 'description' => 'Get specific custom field']
]
],
'authentication' => [
'name' => 'Authentication',
'description' => 'User authentication and API key management',
'endpoints' => [
['method' => 'POST', 'path' => '/login/auth', 'description' => 'Authenticate user'],
['method' => 'GET', 'path' => '/login/key', 'description' => 'Get API key information']
]
]
];
$this->output
->set_content_type('application/json')
->set_output(json_encode($endpoints));
}
}

View File

@@ -0,0 +1,624 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
// This can be removed if you use __autoload() in config.php OR use Modular Extensions
/** @noinspection PhpIncludeInspection */
require __DIR__.'/REST_Controller.php';
/**
* This is an example of a few basic user interaction methods you could use
* all done with a hardcoded array
*
* @package CodeIgniter
* @subpackage Rest Server
* @category Controller
*/
class Projects extends REST_Controller {
function __construct()
{
// Construct the parent class
parent::__construct();
$this->load->model('Api_model');
}
/**
* @api {get} api/projects/:id Request project information
* @apiName GetProject
* @apiGroup Projects
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {Number} id project unique ID.
*
* @apiSuccess {Object} Project information.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "id": "28",
* "name": "Test1",
* "description": null,
* "status": "1",
* "clientid": "11",
* "billing_type": "3",
* "start_date": "2019-04-19",
* "deadline": "2019-08-30",
* "project_created": "2019-07-16",
* "date_finished": null,
* "progress": "0",
* "progress_from_tasks": "1",
* "project_cost": "0.00",
* "project_rate_per_hour": "0.00",
* "estimated_hours": "0.00",
* "addedfrom": "5",
* "rel_type": "lead",
* "potential_revenue": "0.00",
* "potential_margin": "0.00",
* "external": "E",
* ...
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_get($id = '')
{
// If the id parameter doesn't exist return all the
$data = $this->Api_model->get_table('projects', $id);
// Check if the data store contains
if ($data)
{
$data = $this->Api_model->get_api_custom_data($data, "projects", $id);
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
}
else
{
// Set the response and exit
$this->response([
'status' => FALSE,
'message' => 'No data were found'
], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {get} api/projects/search/:keysearch Search Project Information
* @apiName GetProjectSearch
* @apiGroup Projects
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} keysearch Search keywords.
*
* @apiSuccess {Object} Project information.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "id": "28",
* "name": "Test1",
* "description": null,
* "status": "1",
* "clientid": "11",
* "billing_type": "3",
* "start_date": "2019-04-19",
* "deadline": "2019-08-30",
* "project_created": "2019-07-16",
* "date_finished": null,
* "progress": "0",
* "progress_from_tasks": "1",
* "project_cost": "0.00",
* "project_rate_per_hour": "0.00",
* "estimated_hours": "0.00",
* "addedfrom": "5",
* "rel_type": "lead",
* "potential_revenue": "0.00",
* "potential_margin": "0.00",
* "external": "E",
* ...
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_search_get($key = '')
{
$data = $this->Api_model->search('project', $key);
// Check if the data store contains
if ($data)
{
$data = $this->Api_model->get_api_custom_data($data,"projects");
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
}
else
{
// Set the response and exit
$this->response([
'status' => FALSE,
'message' => 'No data were found'
], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {post} api/projects Add New Project
* @apiName PostProject
* @apiGroup Projects
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} name Mandatory Project Name.
* @apiParam {string="lead","customer","internal"} rel_type Mandatory Project Related.
* @apiParam {Number} clientid Mandatory Related ID.
* @apiParam {Number} billing_type Mandatory Billing Type.
* @apiParam {Date} start_date Mandatory Project Start Date.
* @apiParam {Number} status Mandatory Project Status.
* @apiParam {String} [progress_from_tasks] Optional on or off progress from tasks.
* @apiParam {String} [project_cost] Optional Project Cost.
* @apiParam {String} [progress] Optional project progress.
* @apiParam {String} [project_rate_per_hour] Optional project rate per hour.
* @apiParam {String} [estimated_hours] Optional Project estimated hours.
* @apiParam {Number[]} [project_members] Optional Project members.
* @apiParam {Date} [deadline] Optional Project deadline.
* @apiParam {String} [tags] Optional Project tags.
* @apiParam {String} [description] Optional Project description.
*
* @apiParamExample {Multipart Form} Request-Example:
* array (size=15)
* 'name' => string 'Project Name' (length=12)
* 'rel_type' => string 'customer' (length=8)
* 'clientid' => string '3' (length=1)
* 'progress_from_tasks' => string 'on' (length=2)
* 'progress' => string '0' (length=1)
* 'billing_type' => string '3' (length=1)
* 'status' => string '2' (length=1)
* 'project_cost' => string '' (length=0)
* 'project_rate_per_hour' => string '' (length=0)
* 'estimated_hours' => string '' (length=0)
* 'project_members' =>
* array (size=1)
* 0 => string '1' (length=1)
* 'start_date' => string '25/07/2019' (length=10)
* 'deadline' => string '' (length=0)
* 'tags' => string '' (length=0)
* 'description' => string '' (length=0)
*
*
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Project add successful.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Project add successful."
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Project add fail.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Project add fail."
* }
*
*/
public function data_post()
{
\modules\api\core\Apiinit::the_da_vinci_code('api');
// form validation
$this->form_validation->set_rules('name', 'Project Name', 'trim|required|max_length[600]', array('is_unique' => 'This %s already exists please enter another Project Name'));
//$this->form_validation->set_rules('rel_type', 'Related', 'trim|required', array('is_unique' => 'This %s already exists please enter another Project Related'));
$this->form_validation->set_rules('billing_type', 'Billing Type', 'trim|required', array('is_unique' => 'This %s already exists please enter another Project Billing Type'));
$this->form_validation->set_rules('start_date', 'Project Start Date', 'trim|required', array('is_unique' => 'This %s already exists please enter another Project Start Date'));
$this->form_validation->set_rules('status', 'Project Status', 'trim|required', array('is_unique' => 'This %s already exists please enter another Project Status'));
$related = $this->input->post('rel_type', TRUE);
$this->form_validation->set_rules('clientid', ucwords($related), 'trim|required|max_length[11]', array('is_unique' => 'This %s already exists please enter another Project Name'));
if ($this->form_validation->run() == FALSE)
{
// form validation error
$message = array(
'status' => FALSE,
'error' => $this->form_validation->error_array(),
'message' => validation_errors()
);
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
else
{
$project_members = $this->Api_model->value($this->input->post('project_members', TRUE));
$insert_data = [
'name' => $this->input->post('name', TRUE),
//'rel_type' => $this->input->post('rel_type', TRUE),
'clientid' => $this->input->post('clientid', TRUE),
'billing_type' => $this->input->post('billing_type', TRUE),
'start_date' => $this->input->post('start_date', TRUE),
'status' => $this->input->post('status', TRUE),
'project_cost' => $this->Api_model->value($this->input->post('project_cost', TRUE)),
'estimated_hours' => $this->Api_model->value($this->input->post('estimated_hours', TRUE)),
'progress_from_tasks' => $this->Api_model->value($this->input->post('progress_from_tasks', TRUE)),
'progress' => $this->Api_model->value($this->input->post('progress', TRUE)),
'project_rate_per_hour' => $this->Api_model->value($this->input->post('project_rate_per_hour', TRUE)),
'deadline' => $this->Api_model->value($this->input->post('deadline', TRUE)),
'description' => $this->Api_model->value($this->input->post('description', TRUE)),
'tags' => $this->Api_model->value($this->input->post('tags', TRUE)),
'settings' => array( 'available_features' => array( 'project_overview', 'project_milestones', 'project_gantt', 'project_tasks', 'project_estimates', 'project_subscriptions', 'project_invoices', 'project_expenses', 'project_credit_notes', 'project_tickets', 'project_timesheets', 'project_files', 'project_discussions', 'project_notes', 'project_activity'))
];
if ($project_members != '') {
$insert_data['project_members'] = $project_members;
}
if (!empty($this->input->post('custom_fields', TRUE))) {
$insert_data['custom_fields'] = $this->Api_model->value($this->input->post('custom_fields', TRUE));
}
// insert data
$this->load->model('projects_model');
$output = $this->projects_model->add($insert_data);
if ($output > 0 && !empty($output)) {
handle_project_file_uploads($output);
// success
$message = array(
'status' => TRUE,
'message' => 'Project add successful.',
'record_id' => $output // επιστρέφουμε το ID του νέου project
);
$this->response($message, REST_Controller::HTTP_OK);
}
else {
// error
$message = array(
'status' => FALSE,
'message' => 'Project add failed.'
);
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {delete} api/delete/projects/:id Delete a Project
* @apiName DeleteProject
* @apiGroup Projects
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {Number} id project unique ID.
*
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Project Delete successful.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Project Delete Successful."
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Project Delete Fail.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Project Delete Fail."
* }
*/
public function data_delete($id = '')
{
$id = $this->security->xss_clean($id);
if (empty($id) && !is_numeric($id))
{
$message = array(
'status' => FALSE,
'message' => 'Invalid Project ID'
);
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
else
{
// delete data
$this->load->model('projects_model');
$output = $this->projects_model->delete($id);
if ($output === TRUE) {
// success
$message = array(
'status' => TRUE,
'message' => 'Project Delete Successful.'
);
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array(
'status' => FALSE,
'message' => 'Project Delete Fail.'
);
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {put} api/projects/:id Update a project
* @apiName PutProject
* @apiGroup Projects
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} name Mandatory Project Name.
* @apiParam {string="lead","customer","internal"} rel_type Mandatory Project Related.
* @apiParam {Number} clientid Mandatory Related ID.
* @apiParam {Number} billing_type Mandatory Billing Type.
* @apiParam {Date} start_date Mandatory Project Start Date.
* @apiParam {Number} status Mandatory Project Status.
* @apiParam {String} [progress_from_tasks] Optional on or off progress from tasks.
* @apiParam {String} [project_cost] Optional Project Cost.
* @apiParam {String} [progress] Optional project progress.
* @apiParam {String} [project_rate_per_hour] Optional project rate per hour.
* @apiParam {String} [estimated_hours] Optional Project estimated hours.
* @apiParam {Number[]} [project_members] Optional Project members.
* @apiParam {Date} [deadline] Optional Project deadline.
* @apiParam {String} [tags] Optional Project tags.
* @apiParam {String} [description] Optional Project description.
*
*
* @apiParamExample {json} Request-Example:
* {
* "name": "Test1",
* "rel_type": "lead",
* "clientid": "9",
* "status": "2",
* "progress_from_tasks": "on",
* "progress": "0.00",
* "billing_type": "3",
* "project_cost": "0",
* "project_rate_per_hour": "0",
* "estimated_hours": "0",
* "project_members":
* {
* "0": "5"
* }
* "start_date": "19/04/2019",
* "deadline": "30/08/2019",
* "tags": "",
* "description": "",
* "settings":
* {
* "available_features":
* {
* "0": "project_overview",
* "1": "project_milestones" ,
* "2": "project_gantt" ,
* "3": "project_tasks" ,
* "4": "project_estimates" ,
* "5": "project_credit_notes" ,
* "6": "project_invoices" ,
* "7": "project_expenses",
* "8": "project_subscriptions" ,
* "9": "project_activity" ,
* "10": "project_tickets" ,
* "11": "project_timesheets",
* "12": "project_files" ,
* "13": "project_discussions" ,
* "14": "project_notes"
* }
* }
* }
*
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Project Update Successful.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Project Update Successful."
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Project Update Fail.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Project Update Fail."
* }
*/
public function data_put($id = '')
{
// JSON data is now automatically parsed in REST_Controller
if (empty($_POST) || !isset($_POST)) {
$this->load->library('parse_input_stream');
$_POST = $this->parse_input_stream->parse_parameters();
$_FILES = $this->parse_input_stream->parse_files();
if (empty($_POST) || !isset($_POST)) {
$message = array('status' => FALSE, 'message' => 'Data Not Acceptable OR Not Provided');
$this->response($message, REST_Controller::HTTP_NOT_ACCEPTABLE);
}
}
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid Lead ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$update_data = $this->input->post();
$update_file = isset($update_data['file']) ? $update_data['file'] : null;
unset($update_data['file']);
// update data
$this->load->model('projects_model');
$output = $this->projects_model->update($update_data, $id);
if (!empty($update_file) && count($update_file)) {
if ($output <= 0 || empty($output)) {
$output = $id;
}
}
if ($output == true && !empty($output)) {
// success
$attachments = $this->projects_model->get_files($output);
foreach ($attachments as $attachment) {
$this->projects_model->remove_file($attachment['id']);
}
$this->handle_project_attachments_array($output);
$message = array(
'status' => TRUE,
'message' => 'Project Update Successful.'
);
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array(
'status' => FALSE,
'message' => 'Project Update Fail.'
);
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
function handle_project_attachments_array($project_id)
{
$hookData = hooks()->apply_filters('before_handle_project_file_uploads', [
'project_id' => $project_id,
'index_name' => 'file',
'handled_externally' => false, // e.g. module upload to s3
'handled_externally_successfully' => false,
'files' => $_FILES
]);
if ($hookData['handled_externally']) {
return $hookData['handled_externally_successfully'];
}
$filesIDS = [];
$errors = [];
if (isset($_FILES['file']['name'])
&& ($_FILES['file']['name'] != '' || is_array($_FILES['file']['name']) && count($_FILES['file']['name']) > 0)) {
hooks()->do_action('before_upload_project_attachment', $project_id);
if (!is_array($_FILES['file']['name'])) {
$_FILES['file']['name'] = [$_FILES['file']['name']];
$_FILES['file']['type'] = [$_FILES['file']['type']];
$_FILES['file']['tmp_name'] = [$_FILES['file']['tmp_name']];
$_FILES['file']['error'] = [$_FILES['file']['error']];
$_FILES['file']['size'] = [$_FILES['file']['size']];
}
$path = get_upload_path_by_type('project') . $project_id . '/';
for ($i = 0; $i < count($_FILES['file']['name']); $i++) {
if (_perfex_upload_error($_FILES['file']['error'][$i])) {
$errors[$_FILES['file']['name'][$i]] = _perfex_upload_error($_FILES['file']['error'][$i]);
continue;
}
// Get the temp file path
$tmpFilePath = $_FILES['file']['tmp_name'][$i];
// Make sure we have a filepath
if (!empty($tmpFilePath) && $tmpFilePath != '') {
_maybe_create_upload_path($path);
$originalFilename = unique_filename($path, $_FILES['file']['name'][$i]);
$filename = app_generate_hash() . '.' . get_file_extension($originalFilename);
// In case client side validation is bypassed
if (!_upload_extension_allowed($filename)) {
continue;
}
$newFilePath = $path . $filename;
// Upload the file into the company uploads dir
if (copy($tmpFilePath, $newFilePath)) {
unlink($tmpFilePath);
if (is_client_logged_in()) {
$contact_id = get_contact_user_id();
$staffid = 0;
} else {
$staffid = get_staff_user_id();
$contact_id = 0;
}
$data = [
'project_id' => $project_id,
'file_name' => $filename,
'original_file_name' => $originalFilename,
'filetype' => $_FILES['file']['type'][$i],
'dateadded' => date('Y-m-d H:i:s'),
'staffid' => $staffid,
'contact_id' => $contact_id,
'subject' => $originalFilename,
];
if (is_client_logged_in()) {
$data['visible_to_customer'] = 1;
} else {
$data['visible_to_customer'] = ($this->input->post('visible_to_customer') == 'true' ? 1 : 0);
}
$this->db->insert(db_prefix() . 'project_files', $data);
$insert_id = $this->db->insert_id();
if ($insert_id) {
if (is_image($newFilePath)) {
create_img_thumb($path, $filename);
}
array_push($filesIDS, $insert_id);
} else {
unlink($newFilePath);
return false;
}
}
}
}
}
if (count($filesIDS) > 0) {
$this->load->model('projects_model');
end($filesIDS);
$lastFileID = key($filesIDS);
$this->projects_model->new_project_file_notification($filesIDS[$lastFileID], $project_id);
}
if (count($errors) > 0) {
$message = '';
foreach ($errors as $filename => $error_message) {
$message .= $filename . ' - ' . $error_message . '<br />';
}
header('HTTP/1.0 400 Bad error');
echo $message;
die;
}
if (count($filesIDS) > 0) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,556 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
// This can be removed if you use __autoload() in config.php OR use Modular Extensions
/** @noinspection PhpIncludeInspection */
require __DIR__ . '/REST_Controller.php';
/**
* This is an example of a few basic user interaction methods you could use
* all done with a hardcoded array
*
* @package CodeIgniter
* @subpackage Rest Server
* @category Controller
* @author Phil Sturgeon, Chris Kacerguis
* @license MIT
* @link https://github.com/chriskacerguis/codeigniter-restserver
*/
class Proposals extends REST_Controller {
function __construct() {
// Construct the parent class
parent::__construct();
}
/**
* @api {get} api/proposals Request Proposal information
* @apiVersion 0.3.0
* @apiName GetProposal
* @apiGroup Proposals
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiParam {Number} id Proposal unique ID
*
* @apiSuccess {Object} Proposal information.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "id": "1",
* "subject": "Test Proposal",
* "content": "{proposal_items}",
* "addedfrom": "1",
* "datecreated": "2021-08-01 13:38:08",
* "total": "10.00",
* "subtotal": "10.00",
* "total_tax": "0.00",
* "adjustment": "0.00",
* "discount_percent": "0.00",
* "discount_total": "0.00",
* "discount_type": "",
* "show_quantity_as": "1",
* "currency": "1",
* "open_till": "2021-08-08",
* "date": "2021-08-01",
* "rel_id": "1",
* "rel_type": "customer",
* "assigned": "0",
* "hash": "9fc38e5ad2f8256b1b8430ee41069f75",
* "proposal_to": "test",
* "country": "102",
* "zip": "30000202",
* "state": "Test",
* "city": "Test",
* "address": "Test",
* "email": "test@gmail.com",
* "phone": "01324568903",
* "allow_comments": "1",
* "status": "6",
* "estimate_id": null,
* "invoice_id": null,
* "date_converted": null,
* "pipeline_order": "0",
* "is_expiry_notified": "0",
* "acceptance_firstname": null,
* "acceptance_lastname": null,
* "acceptance_email": null,
* "acceptance_date": null,
* "acceptance_ip": null,
* "signature": null,
* "short_link": null,
* "symbol": "$",
* "name": "USD",
* "decimal_separator": ".",
* "thousand_separator": ",",
* "placement": "before",
* "isdefault": "1",
* "currencyid": "1",
* "currency_name": "USD",
* "attachments": [],
* "items": [
* {
* "id": "4",
* "rel_id": "1",
* "rel_type": "proposal",
* "description": "item 1",
* "long_description": "item 1 description",
* "qty": "1.00",
* "rate": "10.00",
* "unit": "1",
* "item_order": "1"
* }
* ],
* "visible_attachments_to_customer_found": false,
* "customfields": [
* {
* "label": "Custom Field",
* "value": "Custom Field value"
* }
* ]
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_get($id = '') {
// If the id parameter doesn't exist return all the
$data = $this->Api_model->get_table('proposals', $id);
// Check if the data store contains
if ($data) {
$data = $this->Api_model->get_api_custom_data($data, "proposal", $id);
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
// Set the response and exit
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {get} api/proposals/search/:keysearch Search proposals information
* @apiVersion 0.3.0
* @apiName GetProposalSearch
* @apiGroup Proposals
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} keysearch Search Keywords.
*
* @apiSuccess {Object} Proposals Information.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "id": "2",
* "subject": "Test 2",
* "content": "{proposal_items}",
* "addedfrom": "1",
* "datecreated": "2021-08-01 13:43:49",
* "total": "10.00",
* "subtotal": "10.00",
* "total_tax": "0.00",
* "adjustment": "0.00",
* "discount_percent": "0.00",
* "discount_total": "0.00",
* "discount_type": "",
* "show_quantity_as": "1",
* "currency": "1",
* "open_till": "2021-08-08",
* "date": "2021-08-01",
* "rel_id": "1",
* "rel_type": "customer",
* "assigned": "0",
* "hash": "6fe6cd0bc66dff03663154660acc1a93",
* "proposal_to": "test",
* "country": "102",
* "zip": "300000",
* "state": "test",
* "city": "test",
* "address": "test",
* "email": "test@gmail.com",
* "phone": "01324568903",
* "allow_comments": "1",
* "status": "6",
* "estimate_id": null,
* "invoice_id": null,
* "date_converted": null,
* "pipeline_order": "0",
* "is_expiry_notified": "0",
* "acceptance_firstname": null,
* "acceptance_lastname": null,
* "acceptance_email": null,
* "acceptance_date": null,
* "acceptance_ip": null,
* "signature": null,
* "short_link": null,
* "symbol": "$",
* "name": "USD",
* "decimal_separator": ".",
* "thousand_separator": ",",
* "placement": "before",
* "isdefault": "1",
* "customfields": [
* {
* "label": "Custom Field",
* "value": "Custom Field value"
* }
* ]
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No Data Were Found"
* }
*/
public function data_search_get($key = '') {
$data = $this->Api_model->search('proposals', $key);
// Check if the data store contains
if ($data) {
$data = $this->Api_model->get_api_custom_data($data, "proposal");
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
// Set the response and exit
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {delete} api/proposals/:id Delete Proposal
* @apiVersion 0.3.0
* @apiName DeleteProposal
* @apiGroup Proposals
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
* @apiParam {Number} id Proposal unique ID.
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Proposals Deleted Successfully
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Proposals Deleted Successfully"
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Proposals Delete Fail
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Proposal Delete Fail"
* }
*/
public function data_delete($id = '') {
$id = $this->security->xss_clean($id);
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid Proposal ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$this->load->model('proposals_model');
$is_exist = $this->proposals_model->get($id);
if (is_object($is_exist)) {
$output = $this->proposals_model->delete($id);
if ($output === TRUE) {
// success
$message = array('status' => TRUE, 'message' => 'Proposal Deleted Successfully');
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('status' => FALSE, 'message' => 'Proposal Delete Fail');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
} else {
$message = array('status' => FALSE, 'message' => 'Invalid Proposal ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {post} api/proposals Add New Proposals
* @apiName PostProposals
* @apiVersion 0.3.0
* @apiGroup Proposals
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} subject Mandatory. Proposal Subject Name.
* @apiParam {string="lead","customer"} Related Mandatory. Proposal Related.
* @apiParam {Number} rel_id Mandatory. Related ID.
* @apiParam {string} proposal_to Mandatory. Lead / Customer name.
* @apiParam {Date} date Mandatory. Proposal Start Date.
* @apiParam {Date} open_till Optional. Proposal Open Till Date.
* @apiParam {string} currency Mandatory. currency id.
* @apiParam {string} discount_type Optional. Proposal Open Till Date.
* @apiParam {string} status Optional. status id.
* @apiParam {string} Assigned Optional. Assignee id.
* @apiParam {string} Email Mandatory. Email id.
* @apiParam {Array} newitems Mandatory. New Items to be added.
*
* @apiParamExample {Multipart Form} Request-Example:
* [
* "subject" => proposal subject
* "rel_type" => customer
* "rel_id" => 1
* "proposal_to" => John Doe
* "email" => customer@mail.com
* "date" => 2021-08-19
* "newitems[0][description]" => item 1 description
* "newitems[0][long_description]" => item 1 long description
* "newitems[0][qty]" => 1
* "newitems[0][rate]" => 1200
* "newitems[0][order]" => 1
* "newitems[0][unit]" => 1
* "newitems[0][unit]" => 1
* "newitems[0][custom_fields][items][1]" => custom field item
* "subtotal" => 1200.00
* "total" => 1200.00
* "currency" => 1
* "date" => 2021-08-19
* "status" => 6
* "custom_fields"[proposal][1] => test
* ....
*]
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Proposal add successful.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Proposal add successful."
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Proposal add fail.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Proposal add fail."
* }
*
*/
public function data_post() {
\modules\api\core\Apiinit::the_da_vinci_code('api');
error_reporting(0);
$data = $this->input->post();
$this->form_validation->set_rules('subject', 'Subject', 'trim|required|max_length[191]');
$this->form_validation->set_rules('rel_type', 'Rel Type', 'trim|required|in_list[lead,customer]');
$this->form_validation->set_rules('rel_id', 'Rel Id', 'trim|required|greater_than[0]');
$this->form_validation->set_rules('proposal_to', 'Proposal to', 'trim|required|max_length[191]');
$this->form_validation->set_rules('email', 'Email', 'trim|valid_email|required|max_length[150]');
$this->form_validation->set_rules('newitems[]', 'Items', 'required');
$this->form_validation->set_rules('currency', 'Currency', 'trim|required|max_length[255]');
$this->form_validation->set_rules('date', 'date', 'trim|required|max_length[255]');
$this->form_validation->set_rules('status', 'Status', 'trim|required|max_length[255]');
$this->form_validation->set_rules('subtotal', 'Sub Total', 'trim|required|decimal|greater_than[0]');
$this->form_validation->set_rules('total', 'Total', 'trim|required|decimal|greater_than[0]');
$data['address'] = $data['address']??"";
if ($this->form_validation->run() == FALSE) {
$message = array('status' => FALSE, 'error' => $this->form_validation->error_array(), 'message' => validation_errors());
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$this->load->model('proposals_model');
$data['open_till'] = _d(date('Y-m-d', strtotime('+' . get_option('proposal_due_after') . ' DAY', strtotime(date('Y-m-d')))));
$id = $this->proposals_model->add($data);
if ($id > 0 && !empty($id)) {
$message = array(
'status' => TRUE,
'message' => 'Proposal Added Successfully',
'record_id' => $id
);
$this->response($message, REST_Controller::HTTP_OK);
}
else {
// error
$message = array('status' => FALSE, 'message' => 'Proposal Add Fail');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {put} api/proposal/:id Update a proposal
* @apiVersion 0.3.0
* @apiName PutProposal
* @apiGroup Proposals
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} subject Mandatory. Proposal Subject Name.
* @apiParam {string="lead","customer"} Mandatory. Proposal Related.
* @apiParam {Number} rel_id Mandatory. Related ID.
* @apiParam {string} proposal_to Mandatory. Lead / Customer name.
* @apiParam {Date} date Mandatory. Proposal Start Date.
* @apiParam {Date} open_till Optional. Proposal Open Till Date.
* @apiParam {string} currency Mandatory. currency id.
* @apiParam {string} discount_type Optional. Proposal Open Till Date.
* @apiParam {string} status Optional. status id.
* @apiParam {string} Assigned Optional. Assignee id.
* @apiParam {string} Email Mandatory. Email id.
* @apiParam {Array} newitems Mandatory. New Items to be added.
* @apiParam {Array} items Optional. Existing items with Id
* @apiParam {Array} removed_items Optional. Items to be removed
*
*
* @apiParamExample {json} Request-Example:
* {
* "subject": "Test",
* "rel_type": "customer",
* "rel_id": 1,
* "proposal_to": "Trueline 1",
* "email": "test@mail.com",
* "date": "2021-08-19",
* "currency": 1,
* "status": 6,
* "items": {
* "1": {
* "itemid": "23",
* "order": "1",
* "description": "item description",
* "long_description": "item long description",
* "qty": "1",
* "unit": "1",
* "rate": "10.00",
* "custom_fields":{
* "items":{
* "31":"test 12 item 1",
* "32":"10",
* "33":"Lorem Ipsum is simply dummy text of the printing and typesetting industry",*
* "34":"Opti*on 1",*
* "35":["Option 1","Option 2"],*
* "36":["Option 1","Option 3"],
* "37":"2021-05-06",
* "38":"2021-05-06 00:23:25",
* "39":"#ffffff",
* "40":"<a href=\"url.com\" target=\"_blank\">Link</a>"
* }
* }
* }
* },
* "newitems": {
* "2": {
* "order": "2",
* "description": "updated item 2 description",
* "long_description": "updated item 2 logn description",
* "qty": "1",
* "unit": "",
* "rate": "100.00",
* "custom_fields":{
* "items":{
* "31":"test 12 item 2",
* "32":"10",
* "33":"Lorem Ipsum is simply dummy text of the printing and typesetting industry",
* "34":"Option 1",
* "35":["Option 1","Option 2"],
* "36":["Option 1","Option 3"],
* "37":"2021-05-06",
* "38":"2021-05-06 00:23:25",
* "39":"#ffffff",
* "40":"<a href=\"url.com\" target=\"_blank\">Link</a>"
* }
* }
* }
* },
* "custom_fields":{
* "proposal":{
* "91":"test 12"
* }
* },
* "subtotal":"110.00",
* "total":"110.00"
* }
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": false,
* "message": "Proposal Updated Successfully"
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Proposal Update Fail"
* }
*
*
*/
public function data_put($id = "") {
// JSON data is now automatically parsed in REST_Controller
if (empty($_POST) || !isset($_POST)) {
$message = array('status' => FALSE, 'message' => 'Data Not Acceptable OR Not Provided');
$this->response($message, REST_Controller::HTTP_NOT_ACCEPTABLE);
}
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid Proposal ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$this->form_validation->set_rules('subject', 'Subject', 'trim|required|max_length[191]');
$this->form_validation->set_rules('rel_type', 'Rel Type', 'trim|required|in_list[lead,customer]');
$this->form_validation->set_rules('rel_id', 'Rel Id', 'trim|required|greater_than[0]');
$this->form_validation->set_rules('proposal_to', 'Proposal to', 'trim|required|max_length[191]');
$this->form_validation->set_rules('email', 'Email', 'trim|valid_email|required|max_length[150]');
$this->form_validation->set_rules('items[]', 'Items', 'required');
$this->form_validation->set_rules('currency', 'Currency', 'trim|required|max_length[255]');
$this->form_validation->set_rules('status', 'Status', 'trim|required|numeric|greater_than[0]');
$this->form_validation->set_rules('date', 'date', 'trim|required|max_length[255]');
$this->form_validation->set_rules('subtotal', 'Sub Total', 'trim|required|decimal|greater_than[0]');
$this->form_validation->set_rules('total', 'Total', 'trim|required|decimal|greater_than[0]');
$_POST['address'] = $_POST['address']??"";
if ($this->form_validation->run() == FALSE) {
$message = array('status' => FALSE, 'error' => $this->form_validation->error_array(), 'message' => validation_errors());
$this->response($message, REST_Controller::HTTP_CONFLICT);
} else {
$this->load->model('proposals_model');
$is_exist = $this->proposals_model->get($id);
if (!is_object($is_exist)) {
$message = array('status' => FALSE, 'message' => 'Proposal ID Doesn\'t Not Exist.');
$this->response($message, REST_Controller::HTTP_CONFLICT);
}
if (is_object($is_exist)) {
$data = $this->input->post();
$data['isedit'] = "";
$success = $this->proposals_model->update($data, $id);
if ($success == true) {
$message = array('status' => TRUE, 'message' => "Proposal Updated Successfully",);
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array('status' => FALSE, 'message' => 'Proposal Update Fail');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
} else {
$message = array('status' => FALSE, 'message' => 'Invalid Proposal ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,147 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Reporting extends AdminController
{
public function __construct()
{
parent::__construct();
$this->load->model('api_metrics_model');
$this->load->model('api_model');
}
/**
* Main reporting dashboard
*/
public function index()
{
$data['title'] = _l('api_reporting');
$data['api_keys'] = $this->api_model->get_all_api_keys();
// Get default date range (last 30 days)
$end_date = date('Y-m-d');
$start_date = date('Y-m-d', strtotime('-30 days'));
$data['start_date'] = $this->input->get('start_date') ?: $start_date;
$data['end_date'] = $this->input->get('end_date') ?: $end_date;
$data['api_key'] = $this->input->get('api_key') ?: '';
// Get usage statistics
$data['usage_stats'] = $this->api_metrics_model->get_usage_stats(
$data['api_key'] ?: null,
$data['start_date'],
$data['end_date']
);
// Get endpoint statistics
$data['endpoint_stats'] = $this->api_metrics_model->get_endpoint_stats(
$data['api_key'] ?: null,
$data['start_date'],
$data['end_date']
);
// Get hourly usage for charts
$data['hourly_usage'] = $this->api_metrics_model->get_hourly_usage(
$data['api_key'] ?: null,
$data['start_date'],
$data['end_date']
);
// Get response code distribution
$data['response_codes'] = $this->api_metrics_model->get_response_code_distribution(
$data['api_key'] ?: null,
$data['start_date'],
$data['end_date']
);
// Get API key summary
$data['api_key_summary'] = $this->api_metrics_model->get_api_key_summary(
$data['start_date'],
$data['end_date']
);
$this->load->view('api_reporting', $data);
}
/**
* Get chart data via AJAX
*/
public function get_chart_data()
{
$chart_type = $this->input->get('chart_type');
$api_key = $this->input->get('api_key') ?: null;
$start_date = $this->input->get('start_date');
$end_date = $this->input->get('end_date');
$data = [];
switch ($chart_type) {
case 'hourly_usage':
$data = $this->api_metrics_model->get_hourly_usage($api_key, $start_date, $end_date);
break;
case 'daily_usage':
$data = $this->api_metrics_model->get_daily_usage($api_key, $start_date, $end_date);
break;
case 'response_codes':
$data = $this->api_metrics_model->get_response_code_distribution($api_key, $start_date, $end_date);
break;
case 'endpoint_stats':
$data = $this->api_metrics_model->get_endpoint_stats($api_key, $start_date, $end_date);
break;
}
$this->output
->set_content_type('application/json')
->set_output(json_encode($data));
}
/**
* Export usage data
*/
public function export()
{
$api_key = $this->input->get('api_key') ?: null;
$start_date = $this->input->get('start_date');
$end_date = $this->input->get('end_date');
$format = $this->input->get('format') ?: 'csv';
$this->load->library('excel');
$data = $this->api_metrics_model->get_api_key_summary($start_date, $end_date);
$excel = new PHPExcel();
$excel->getProperties()->setTitle('API Usage Report');
$sheet = $excel->getActiveSheet();
$sheet->setTitle('API Usage Summary');
// Headers
$headers = ['API Key', 'Total Requests', 'Avg Response Time', 'Success Requests', 'Error Requests'];
$col = 'A';
foreach ($headers as $header) {
$sheet->setCellValue($col . '1', $header);
$col++;
}
// Data
$row = 2;
foreach ($data as $item) {
$sheet->setCellValue('A' . $row, $item->api_key);
$sheet->setCellValue('B' . $row, $item->total_requests);
$sheet->setCellValue('C' . $row, round($item->avg_response_time, 4));
$sheet->setCellValue('D' . $row, $item->success_requests);
$sheet->setCellValue('E' . $row, $item->error_requests);
$row++;
}
$filename = 'api_usage_report_' . date('Y-m-d_H-i-s') . '.xlsx';
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Cache-Control: max-age=0');
$writer = PHPExcel_IOFactory::createWriter($excel, 'Excel2007');
$writer->save('php://output');
}
}

477
api/controllers/Staffs.php Normal file
View File

@@ -0,0 +1,477 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
// This can be removed if you use __autoload() in config.php OR use Modular Extensions
/** @noinspection PhpIncludeInspection */
require __DIR__.'/REST_Controller.php';
/**
* This is an example of a few basic user interaction methods you could use
* all done with a hardcoded array
*
* @package CodeIgniter
* @subpackage Rest Server
* @category Controller
* @author Phil Sturgeon, Chris Kacerguis
* @license MIT
* @link https://github.com/chriskacerguis/codeigniter-restserver
*/
class Staffs extends REST_Controller {
function __construct()
{
// Construct the parent class
parent::__construct();
$this->load->model('Api_model');
}
/**
* @api {get} api/staffs/:id Request Staff information
* @apiName GetStaff
* @apiGroup Staffs
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {Number} id Staff unique ID.
*
* @apiSuccess {Object} Staff information.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "staffid": "8",
* "email": "data1.gsts@gmail.com",
* "firstname": "Đào Quang Dân",
* "lastname": "",
* "facebook": "",
* "linkedin": "",
* "phonenumber": "",
* "skype": "",
* "password": "$2a$08$ySLokLAM.AqmW9ZjY2YREO0CIrd5K4Td\/Bpfp8d9QJamWNUfreQuK",
* "datecreated": "2019-02-25 09:11:31",
* "profile_image": "8.png",
* ...
* }
*
* @apiError StaffNotFound The id of the Staff was not found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_get($id = '')
{
// If the id parameter doesn't exist return all the
$data = $this->Api_model->get_table('staffs', $id);
// Check if the data store contains
if ($data)
{
$data = $this->Api_model->get_api_custom_data($data,"staff", $id);
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
}
else
{
// Set the response and exit
$this->response([
'status' => FALSE,
'message' => 'No data were found'
], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {get} api/staffs/search/:keysearch Search Staff Information
* @apiName GetStaffSearch
* @apiGroup Staffs
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} keysearch Search keywords.
*
* @apiSuccess {Object} Staff information.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "staffid": "8",
* "email": "data1.gsts@gmail.com",
* "firstname": "Đào Quang Dân",
* "lastname": "",
* "facebook": "",
* "linkedin": "",
* "phonenumber": "",
* "skype": "",
* "password": "$2a$08$ySLokLAM.AqmW9ZjY2YREO0CIrd5K4Td\/Bpfp8d9QJamWNUfreQuK",
* "datecreated": "2019-02-25 09:11:31",
* "profile_image": "8.png",
* ...
* }
*
* @apiError StaffNotFound The id of the Staff was not found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_search_get($key = '')
{
$data = $this->Api_model->search('staff', $key);
// Check if the data store contains
if ($data)
{
$data = $this->Api_model->get_api_custom_data($data,"staff");
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
}
else
{
// Set the response and exit
$this->response([
'status' => FALSE,
'message' => 'No data were found'
], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {post} api/staffs Add New Staff
* @apiName PostStaffs
* @apiGroup Staffs
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} firstname Mandatory Staff Name.
* @apiParam {String} email Mandatory Staff Related.
* @apiParam {String} password Mandatory Staff password.
* @apiParam {Number} [hourly_rate] Optional hourly rate.
* @apiParam {String} [phonenumber] Optional Staff phonenumber.
* @apiParam {String} [facebook] Optional Staff facebook.
* @apiParam {String} [linkedin] Optional Staff linkedin.
* @apiParam {String} [skype] Optional Staff skype.
* @apiParam {String} [default_language] Optional Staff default language.
* @apiParam {String} [email_signature] Optional Staff email signature.
* @apiParam {String} [direction] Optional Staff direction.
* @apiParam {String} [send_welcome_email] Optional Staff send welcome email.
* @apiParam {Number[]} [departments] Optional Staff departments.
*
* @apiParamExample {Multipart Form} Request-Example:
* array (size=15)
* 'firstname' => string '4' (length=1)
* 'email' => string 'a@gmail.com' (length=11)
* 'hourly_rate' => string '0' (length=1)
* 'phonenumber' => string '' (length=0)
* 'facebook' => string '' (length=0)
* 'linkedin' => string '' (length=0)
* 'skype' => string '' (length=0)
* 'default_language' => string '' (length=0)
* 'email_signature' => string '' (length=0)
* 'direction' => string '' (length=0)
* 'departments' =>
* array (size=5)
* 0 => string '1' (length=1)
* 1 => string '2' (length=1)
* 2 => string '3' (length=1)
* 3 => string '4' (length=1)
* 4 => string '5' (length=1)
* 'send_welcome_email' => string 'on' (length=2)
* 'fakeusernameremembered' => string '' (length=0)
* 'fakepasswordremembered' => string '' (length=0)
* 'password' => string '1' (length=1)
* 'role' => string '18' (length=2)
*
*
* @apiSuccess {String} status Request status.
* @apiSuccess {String} message Staff add successful.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Staff add successful."
* }
*
* @apiError {String} status Request status.
* @apiError {String} message Staff add fail.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Staff add fail."
* }
*
*/
public function data_post()
{
\modules\api\core\Apiinit::the_da_vinci_code('api');
// form validation
$this->form_validation->set_rules('firstname', 'First Name', 'trim|required|max_length[600]', array('is_unique' => 'This %s already exists please enter another Staff First Name'));
$this->form_validation->set_rules('email', 'Email', 'trim|required|valid_email', array('is_unique' => 'This %s already exists please enter another Staff Email'));
$this->form_validation->set_rules('password', 'Password', 'trim|required', array('is_unique' => 'This %s already exists please enter another Staff password'));
if ($this->form_validation->run() == FALSE)
{
// form validation error
$message = array(
'status' => FALSE,
'error' => $this->form_validation->error_array(),
'message' => validation_errors()
);
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
else
{
$departments = $this->Api_model->value($this->input->post('departments', TRUE));
$insert_data = [
'firstname' => $this->input->post('firstname', TRUE),
'email' => $this->input->post('email', TRUE),
'password' => $this->input->post('password', TRUE),
'lastname' => '',
'hourly_rate' => $this->Api_model->value($this->input->post('hourly_rate', TRUE)),
'phonenumber' => $this->Api_model->value($this->input->post('phonenumber', TRUE)),
'facebook' => $this->Api_model->value($this->input->post('facebook', TRUE)),
'linkedin' => $this->Api_model->value($this->input->post('linkedin', TRUE)),
'skype' => $this->Api_model->value($this->input->post('skype', TRUE)),
'default_language' => $this->Api_model->value($this->input->post('default_language', TRUE)),
'email_signature' => $this->Api_model->value($this->input->post('email_signature', TRUE)),
'direction' => $this->Api_model->value($this->input->post('direction', TRUE)),
'send_welcome_email' => $this->Api_model->value($this->input->post('send_welcome_email', TRUE)),
'role' => '1',
'permissions' => array(
'bulk_pdf_exporter' => array('view'),
'contracts' => array('create','edit','delete'),
'credit_notes' => array('create','edit','delete'),
'customers' => array('view','create','edit','delete'),
'email_templates' => array('view','edit'),
'estimates' => array('create','edit','delete'),
'expenses' => array('create','edit','delete'),
'invoices' => array('create','edit','delete'),
'items' => array('view','create','edit','delete'),
'knowledge_base' => array('view','create','edit','delete'),
'payments' => array('view','create','edit','delete'),
'projects' => array('view','create','edit','delete'),
'proposals' => array('create','edit','delete'),
'contracts' => array('view'),
'roles' => array('view','create','edit','delete'),
'settings' => array('view','edit'),
'staff' => array('view','create','edit','delete'),
'subscriptions' => array('create','edit','delete'),
'tasks' => array('view','create','edit','delete'),
'checklist_templates' => array('create','delete'),
'leads' => array('view','delete'),
'goals' => array('view','create','edit','delete'),
'surveys' => array('view','create','edit','delete'),
)
];
if($departments != ''){
$insert_data['departments'] = $departments;
}
if (!empty($this->input->post('custom_fields', TRUE))) {
$insert_data['custom_fields'] = $this->Api_model->value($this->input->post('custom_fields', TRUE));
}
// insert data
$this->load->model('staff_model');
$output = $this->staff_model->add($insert_data);
if($output > 0 && !empty($output)){
// success
$message = array(
'status' => TRUE,
'message' => 'Staff add successful.',
'record_id' => $output // επιστρέφουμε το ID του νέου staff
);
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array(
'status' => FALSE,
'message' => 'Staff add fail.'
);
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {delete} api/delete/staffs/:id Delete a Staff
* @apiName DeleteStaff
* @apiGroup Staffs
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {Number} id Staff unique ID.
*
* @apiSuccess {String} status Request status.
* @apiSuccess {String} message Staff registration successful.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Staff Delete."
* }
*
* @apiError {String} status Request status.
* @apiError {String} message Not register your accout.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Staff Not Delete."
* }
*/
public function data_delete($id)
{
$id = $this->security->xss_clean($id);
if(empty($id) && !is_numeric($id))
{
$message = array(
'status' => FALSE,
'message' => 'Invalid Staff ID'
);
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
else
{
// delete data
$this->load->model('staff_model');
$output = $this->staff_model->delete($id, 0);
if($output === TRUE){
// success
$message = array(
'status' => TRUE,
'message' => 'Staff Delete Successful.'
);
$this->response($message, REST_Controller::HTTP_OK);
}else{
// error
$message = array(
'status' => FALSE,
'message' => 'Staff Delete Fail.'
);
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {put} api/staffs/:id Update a Staff
* @apiName PutStaff
* @apiGroup Staffs
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} firstname Mandatory Staff Name.
* @apiParam {String} email Mandatory Staff Related.
* @apiParam {String} password Mandatory Staff password.
* @apiParam {Number} [hourly_rate] Optional hourly rate.
* @apiParam {String} [phonenumber] Optional Staff phonenumber.
* @apiParam {String} [facebook] Optional Staff facebook.
* @apiParam {String} [linkedin] Optional Staff linkedin.
* @apiParam {String} [skype] Optional Staff skype.
* @apiParam {String} [default_language] Optional Staff default language.
* @apiParam {String} [email_signature] Optional Staff email signature.
* @apiParam {String} [direction] Optional Staff direction.
* @apiParam {Number[]} [departments] Optional Staff departments.
*
*
* @apiParamExample {json} Request-Example:
* {
* "firstname": "firstname",
* "email": "aa454@gmail.com",
* "hourly_rate": "0.00",
* "phonenumber": "",
* "facebook": "",
* "linkedin": "",
* "skype": "",
* "default_language": "",
* "email_signature": "",
* "direction": "",
* "departments": {
* "0": "1",
* "1": "2"
* },
* "password": "123456"
* }
*
* @apiSuccess {String} status Request status.
* @apiSuccess {String} message Staff Update Successful.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Staff Update Successful."
* }
*
* @apiError {String} status Request status.
* @apiError {String} message Staff Update Fail.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Staff Update Fail."
* }
*/
public function data_put($id)
{
// JSON data is now automatically parsed in REST_Controller
if(empty($_POST ) || !isset($_POST ))
{
$message = array(
'status' => FALSE,
'message' => 'Data Not Acceptable OR Not Provided'
);
$this->response($message, REST_Controller::HTTP_NOT_ACCEPTABLE);
}
if(empty($id) && !is_numeric($id))
{
$message = array(
'status' => FALSE,
'message' => 'Invalid Staff ID'
);
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
else
{
$update_data = $this->input->post();
$update_data['lastname'] = '';
// update data
$this->load->model('staff_model');
$output = $this->staff_model->update($update_data, $id);
if($output > 0 && !empty($output)){
// success
$message = array(
'status' => TRUE,
'message' => 'Staff Update Successful.'
);
$this->response($message, REST_Controller::HTTP_OK);
}else{
// error
$message = array(
'status' => FALSE,
'message' => 'Staff Update Fail.'
);
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
}

View File

@@ -0,0 +1,356 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
require __DIR__ . '/REST_Controller.php';
class Subscriptions extends REST_Controller
{
public function __construct()
{
parent::__construct();
}
/**
* @api {get} api/subscriptions/ Request all Subscriptions
* @apiName Request Subscriptions
* @apiGroup Subscriptions
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiSuccess {Object} Data Information
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* [
* {
* 'name' => varchar 'New subscription'
* 'description' => text 'This is a detailed description of subscription'
* 'description_in_item' => tinyint '1'
* 'clientid' => int '123'
* 'date' => date '2024-01-31'
* 'terms' => text 'subscription payment is due'
* 'currency ' => int '4'
* 'tax_id ' => int '456'
* 'stripe_tax_id_2' => varchar 'tax-789'
* 'stripe_plan_id' => text 'subscription_ABC'
* 'stripe_subscription_id' => text 'subscription_ABC'
* 'tax_id_2': int '12',
* 'stripe_subscription_id' => text 'sub_123456789'
* 'next_billing_cycle' => bigint '1643808000'
* 'ends_at' => bigint '1646486400'
* 'status' => varchar 'active'
* 'quantity' => int '5'
* 'project_id' => int '789'
* 'hash' => varchar 'a1b2c3'
* 'created' => datetime '2024-01-31 12:34:56'
* 'created_from' => int '1'
* 'date_subscribed' => datetime '2024-01-31 10:00:00'
* 'in_test_environment' => int '1'
* 'last_sent_at' => datetime '2024-01-31 14:45:00'
* }
* ]
*
* @apiError DataNotFound The id of the data was not found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
/**
* @api {get} api/subscriptions/:id Request Subscription Information
* @apiName Request Subscription Information
* @apiGroup Subscriptions
* @apiHeader {String} authtoken Authentication token, generated from admin area
* @apiParam {id} id Data id ID.
*
* @apiSuccess {Object} Data Information
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* [
* {
* 'name' => varchar 'New subscription'
* 'description' => text 'This is a detailed description of subscription'
* 'description_in_item' => tinyint '1'
* 'clientid' => int '123'
* 'date' => date '2024-01-31'
* 'terms' => text 'subscription payment is due'
* 'currency ' => int '4'
* 'tax_id ' => int '456'
* 'stripe_tax_id_2' => varchar 'tax-789'
* 'stripe_plan_id' => text 'subscription_ABC'
* 'stripe_subscription_id' => text 'subscription_ABC'
* 'tax_id_2': int '12',
* 'stripe_subscription_id' => text 'sub_123456789'
* 'next_billing_cycle' => bigint '1643808000'
* 'ends_at' => bigint '1646486400'
* 'status' => varchar 'active'
* 'quantity' => int '5'
* 'project_id' => int '789'
* 'hash' => varchar 'a1b2c3'
* 'created' => datetime '2024-01-31 12:34:56'
* 'created_from' => int '1'
* 'date_subscribed' => datetime '2024-01-31 10:00:00'
* 'in_test_environment' => int '1'
* 'last_sent_at' => datetime '2024-01-31 14:45:00'
* }
* ]
*
* @apiError DataNotFound The id of the data was not found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_get($id = '')
{
$data = $this->Api_model->get_table('subscriptions', $id);
if ($data) {
$this->response($data, REST_Controller::HTTP_OK);
} else {
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND);
}
}
/**
* @api {post} api/subscriptions/ Add New Subscription
* @apiName AddNewSubscription
* @apiGroup Subscriptions
* @apiHeader {String} authtoken Authentication token, generated from admin area
* @apiParam {String} name New subscription name.
* @apiParam {Text} description Detailed description of the subscription.
* @apiParam {TinyInt} description_in_item Indicates if the description is included in the item (1 or 0).
* @apiParam {Int} clientid Client ID.
* @apiParam {Date} date Subscription start date (YYYY-MM-DD).
* @apiParam {Text} terms Subscription terms.
* @apiParam {Int} currency Currency ID.
* @apiParam {Int} tax_id Tax ID.
* @apiParam {Varchar} stripe_tax_id_2 Stripe tax ID.
* @apiParam {Text} stripe_plan_id Stripe plan ID.
* @apiParam {Text} stripe_subscription_id Stripe Subscription ID.
* @apiParam {Int} tax_id_2 Second tax ID.
* @apiParam {Varchar} stripe_subscription_id Stripe subscription ID.
* @apiParam {BigInt} next_billing_cycle Next billing cycle timestamp.
* @apiParam {BigInt} ends_at Subscription end timestamp.
* @apiParam {Varchar} status Subscription status (e.g., active).
* @apiParam {Int} quantity Subscription quantity.
* @apiParam {Int} project_id Associated project ID.
* @apiParam {Varchar} hash Unique hash identifier.
* @apiParam {DateTime} created Creation timestamp (YYYY-MM-DD HH:MM:SS).
* @apiParam {Int} created_from ID of the creator.
* @apiParam {DateTime} date_subscribed Subscription date (YYYY-MM-DD HH:MM:SS).
* @apiParam {Int} in_test_environment Indicates if the subscription is in a test environment (1 or 0).
* @apiParam {DateTime} last_sent_at Last sent timestamp (YYYY-MM-DD HH:MM:SS).
*
* @apiParamExample {multipart/form-data} Request Example:
* {
* "name": "New subscription",
* "description": "This is a detailed description of subscription",
* "description_in_item": 1,
* "clientid": 123,
* "date": "2024-01-31",
* "terms": "subscription payment is due",
* "currency": 4,
* "tax_id": 456,
* "stripe_tax_id_2": "tax-789",
* "stripe_plan_id": "subscription_ABC",
* "stripe_subscription_id": "subscription_ABC",
* "tax_id_2": 12,
* "stripe_subscription_id": "sub_123456789",
* "next_billing_cycle": 1643808000,
* "ends_at": 1646486400,
* "status": "active",
* "quantity": 5,
* "project_id": 789,
* "hash": "a1b2c3",
* "created": "2024-01-31 12:34:56",
* "created_from": 1,
* "date_subscribed": "2024-01-31 10:00:00",
* "in_test_environment": 1,
* "last_sent_at": "2024-01-31 14:45:00"
* }
*
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Success message.
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Data Added Successfully"
* }
*
* @apiError DataNotAdded Data could not be added.
*
* @apiErrorExample {json} Error-Response:
* HTTP/1.1 400 Bad Request
* {
* "status": false,
* "error": "Data not Added"
* }
*/
public function data_post()
{
\modules\api\core\Apiinit::the_da_vinci_code('api');
$data = $this->input->post();
$this->form_validation->set_rules('name', 'Subscription Name', 'trim|required');
$this->form_validation->set_rules('quantity', 'Quantity', 'trim|required');
$this->form_validation->set_rules('next_billing_cycle', ' Billing Plan', 'required');
$this->form_validation->set_rules('currency', 'Currency', 'trim|required');
$this->form_validation->set_rules('clientid', 'clientid', 'trim|required');
if ($this->form_validation->run() == FALSE) {
$message = array('status' => FALSE, 'error' => $this->form_validation->error_array(), 'message' => validation_errors());
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$id = $this->Api_model->subscription($data);
if ($id > 0 && !empty($id)) {
$message = array(
'status' => TRUE,
'message' => 'Data Added Successfully',
'record_id' => $id
);
$this->response($message, REST_Controller::HTTP_OK);
}
else {
$message = array('status' => FALSE, 'message' => 'Data Add Fail');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {put} api/subscriptions/:id Update a Subscription
* @apiName Update a Subscription
* @apiParam {id} id ID for update data.
* @apiGroup Subscriptions
* @apiHeader {String} authtoken Authentication token, generated from admin area
* @apiParamExample {json} Request-Example:
* {
* 'name' => varchar 'New subscription updated'
* 'description' => text 'This is a detailed description of subscription'
* 'description_in_item' => tinyint '1'
* 'clientid' => int '123'
* 'date' => date '2024-01-31'
* 'terms' => text 'subscription payment is due'
* 'currency ' => int '4'
* 'tax_id ' => int '456'
* 'stripe_tax_id_2' => varchar 'tax-789'
* 'stripe_plan_id' => text 'subscription_ABC'
* 'stripe_subscription_id' => text 'subscription_ABC'
* "tax_id_2": int '12',
* 'stripe_subscription_id' => text 'sub_123456789'
* 'next_billing_cycle' => bigint '1643808000'
* 'ends_at' => bigint '1646486400'
* 'status' => varchar 'active'
* 'quantity' => int '5'
* 'project_id' => int '789'
* 'hash' => varchar 'a1b2c3'
* 'created' => datetime '2024-01-31 12:34:56'
* 'created_from' => int '1'
* 'date_subscribed' => datetime '2024-01-31 10:00:00'
* 'in_test_environment' => int '1'
* 'last_sent_at' => datetime '2024-01-31 14:45:00'
* }
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Data Update Successful."
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Data Not Acceptable OR Not Provided"
* }
*
* {
* "status": false,
* "message": "Data Update Fail."
* }
*/
public function data_put($id = '')
{
// JSON data is now automatically parsed in REST_Controller
if (empty($_POST) || !isset($_POST)) {
$message = array('status' => FALSE, 'message' => 'Data Not Acceptable OR Not Provided');
$this->response($message, REST_Controller::HTTP_NOT_ACCEPTABLE);
}
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid data or missing Send ID. please provide updated data ID.');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$_POST['id'] = $id;
$update_data = $this->input->post();
$data = $_POST;
$output = $this->Api_model->subscriptions($data);
if ($output > 0 && !empty($output)) {
$message = array('status' => TRUE, 'message' => 'Data Update Successful.');
$this->response($message, REST_Controller::HTTP_OK);
} else {
$message = array('status' => FALSE, 'message' => 'Data Update Fail.');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {delete} api/subscriptions/:id Delete a Subscription
* @apiName Delete a Subscription
* @apiGroup Subscriptions
* @apiHeader {String} authtoken Authentication token, generated from admin area
* @apiParam {id} id ID for data Deletion.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Delete Successful."
* }
*
* @apiError DataNotAdded.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Delete Fail."
* }
*/
public function data_delete($id = '')
{
$id = $this->security->xss_clean($id);
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$this->load->model('api_model');
$output = $this->api_model->delete_subscription($id);
if ($output === TRUE) {
$message = array('status' => TRUE, 'message' => 'Delete Successful.');
$this->response($message, REST_Controller::HTTP_OK);
} else {
$message = array('status' => FALSE, 'message' => 'Delete Fail.');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
}

View File

@@ -0,0 +1,25 @@
<?php
require __DIR__ . '/REST_Controller.php';
defined('BASEPATH') or exit('No direct script access allowed');
class Swagger extends CI_Controller
{
public function __construct()
{
parent::__construct();
$this->load->library('app_modules');
}
public function index() {
$data['title'] = 'Api Guide';
$this->load->view('playground', $data);
}
public function json()
{
return REST_Controller::get_swagger_file();
}
}

505
api/controllers/Tasks.php Normal file
View File

@@ -0,0 +1,505 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
// This can be removed if you use __autoload() in config.php OR use Modular Extensions
/** @noinspection PhpIncludeInspection */
require __DIR__.'/REST_Controller.php';
/**
* This is an example of a few basic user interaction methods you could use
* all done with a hardcoded array
*
* @package CodeIgniter
* @subpackage Rest Server
* @category Controller
* @author Phil Sturgeon, Chris Kacerguis
* @license MIT
* @link https://github.com/chriskacerguis/codeigniter-restserver
*/
class Tasks extends REST_Controller {
function __construct()
{
// Construct the parent class
parent::__construct();
}
/**
* @api {get} api/tasks/:id Request Task information
* @apiName GetTask
* @apiGroup Tasks
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {Number} id Task unique ID.
*
* @apiSuccess {Object} Tasks information.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "id": "10",
* "name": "This is a task",
* "description": "",
* "priority": "2",
* "dateadded": "2019-02-25 12:26:37",
* "startdate": "2019-01-02 00:00:00",
* "duedate": "2019-01-04 00:00:00",
* "datefinished": null,
* "addedfrom": "9",
* "is_added_from_contact": "0",
* "status": "4",
* "recurring_type": null,
* "repeat_every": "0",
* "recurring": "0",
* "is_recurring_from": null,
* ...
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_get($id = '')
{
// If the id parameter doesn't exist return all the
$data = $this->Api_model->get_table('tasks', $id);
// Check if the data store contains
if ($data)
{
$data = $this->Api_model->get_api_custom_data($data, "tasks", $id);
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
}
else
{
// Set the response and exit
$this->response([
'status' => FALSE,
'message' => 'No data were found'
], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {get} api/tasks/search/:keysearch Search Tasks Information
* @apiName GetTaskSearch
* @apiGroup Tasks
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} keysearch Search Keywords.
*
* @apiSuccess {Object} Tasks information.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "id": "10",
* "name": "This is a task",
* "description": "",
* "priority": "2",
* "dateadded": "2019-02-25 12:26:37",
* "startdate": "2019-01-02 00:00:00",
* "duedate": "2019-01-04 00:00:00",
* "datefinished": null,
* "addedfrom": "9",
* "is_added_from_contact": "0",
* "status": "4",
* "recurring_type": null,
* "repeat_every": "0",
* "recurring": "0",
* "is_recurring_from": null,
* ...
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message No data were found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_search_get($key = '')
{
// If the id parameter doesn't exist return all the
$data = $this->Api_model->search('tasks', $key);
// Check if the data store contains
if ($data)
{
usort($data, function($a, $b) {
return $a['id'] - $b['id'];
});
$data = $this->Api_model->get_api_custom_data($data,"tasks");
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
} else {
// Set the response and exit
$this->response([
'status' => FALSE,
'message' => 'No data were found'
], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {post} api/tasks Add New Task
* @apiName PostTask
* @apiGroup Tasks
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} name Mandatory Task Name.
* @apiParam {Date} startdate Mandatory Task Start Date.
* @apiParam {String} [is_public] Optional Task public.
* @apiParam {String} [billable] Optional Task billable.
* @apiParam {String} [hourly_rate] Optional Task hourly rate.
* @apiParam {String} [milestone] Optional Task milestone.
* @apiParam {Date} [duedate] Optional Task deadline.
* @apiParam {String} [priority] Optional Task priority.
* @apiParam {String} [repeat_every] Optional Task repeat every.
* @apiParam {Number} [repeat_every_custom] Optional Task repeat every custom.
* @apiParam {String} [repeat_type_custom] Optional Task repeat type custom.
* @apiParam {Number} [cycles] Optional cycles.
* @apiParam {string="lead","customer","invoice", "project", "quotation", "contract", "annex", "ticket", "expense", "proposal"} rel_type Mandatory Task Related.
* @apiParam {Number} rel_id Optional Related ID.
* @apiParam {String} [tags] Optional Task tags.
* @apiParam {String} [description] Optional Task description.
*
*
* @apiParamExample {Multipart Form} Request-Example:
* array (size=15)
* 'is_public' => string 'on' (length=2)
* 'billable' => string 'on' (length=2)
* 'name' => string 'Task 12' (length=7)
* 'hourly_rate' => string '0' (length=1)
* 'milestone' => string '' (length=0)
* 'startdate' => string '17/07/2019' (length=10)
* 'duedate' => string '31/07/2019 11:07' (length=16)
* 'priority' => string '2' (length=1)
* 'repeat_every' => string '' (length=0)
* 'repeat_every_custom' => string '1' (length=1)
* 'repeat_type_custom' => string 'day' (length=3)
* 'rel_type' => string 'customer' (length=8)
* 'rel_id' => string '9' (length=1)
* 'tags' => string '' (length=0)
* 'description' => string '<span>Task Description</span>' (length=29)
*
*
* @apiSuccess {String} status Request status.
* @apiSuccess {String} message Task add successful.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Task add successful."
* }
*
* @apiError {String} status Request status.
* @apiError {String} message Task add fail.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Task add fail."
* }
*
*/
public function data_post()
{
\modules\api\core\Apiinit::the_da_vinci_code('api');
// form validation
$this->form_validation->set_rules('name', 'Task Name', 'trim|required|max_length[600]', array('is_unique' => 'This %s already exists please enter another Task Name'));
$this->form_validation->set_rules('startdate', 'Task Start Date', 'trim|required', array('is_unique' => 'This %s already exists please enter another Task Start Date'));
$this->form_validation->set_rules('is_public', 'Publicly available task', 'trim', array('is_unique' => 'Public state can be 1. Skip it completely to set it at non-public'));
if ($this->form_validation->run() == FALSE)
{
// form validation error
$message = array(
'status' => FALSE,
'error' => $this->form_validation->error_array(),
'message' => validation_errors()
);
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
else
{
$insert_data = [
'name' => $this->input->post('name', TRUE),
'startdate' => $this->input->post('startdate', TRUE),
'is_public' => $this->input->post('is_public', TRUE),
'billable' => $this->Api_model->value($this->input->post('billable', TRUE)),
'hourly_rate' => $this->Api_model->value($this->input->post('hourly_rate', TRUE)),
'milestone' => $this->Api_model->value($this->input->post('milestone', TRUE)),
'duedate' => $this->Api_model->value($this->input->post('duedate', TRUE)),
'priority' => $this->Api_model->value($this->input->post('priority', TRUE)),
'repeat_every' => $this->Api_model->value($this->input->post('repeat_every', TRUE)),
'repeat_every_custom' => $this->Api_model->value($this->input->post('repeat_every_custom', TRUE)),
'repeat_type_custom' => $this->Api_model->value($this->input->post('repeat_type_custom', TRUE)),
'cycles' => $this->Api_model->value($this->input->post('cycles', TRUE)),
'rel_type' => $this->Api_model->value($this->input->post('rel_type', TRUE)),
'rel_id' => $this->Api_model->value($this->input->post('rel_id', TRUE)),
'tags' => $this->Api_model->value($this->input->post('tags', TRUE)),
'description' => $this->Api_model->value($this->input->post('description', TRUE))
];
if (!empty($this->input->post('custom_fields', TRUE))) {
$insert_data['custom_fields'] = $this->Api_model->value($this->input->post('custom_fields', TRUE));
}
// insert data
$this->load->model('tasks_model');
$output = $this->tasks_model->add($insert_data);
if ($output > 0 && !empty($output)) {
// success
$this->handle_task_attachments_array($output);
$message = array(
'status' => TRUE,
'message' => 'Task add successful.',
'record_id' => $output // επιστρέφουμε το ID του νέου task
);
$this->response($message, REST_Controller::HTTP_OK);
}
else {
// error
$message = array(
'status' => FALSE,
'message' => 'Task add failed.'
);
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {delete} api/delete/tasks/:id Delete a Task
* @apiName DeleteTask
* @apiGroup Tasks
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {Number} id Task unique ID.
*
* @apiSuccess {String} status Request status.
* @apiSuccess {String} message Task Delete Successful.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Task Delete Successful."
* }
*
* @apiError {String} status Request status.
* @apiError {String} message Task Delete Fail.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Task Delete Fail."
* }
*/
public function data_delete($id = '')
{
$id = $this->security->xss_clean($id);
if (empty($id) && !is_numeric($id)) {
$message = array(
'status' => FALSE,
'message' => 'Invalid Task ID'
);
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
// delete data
$this->load->model('tasks_model');
$output = $this->tasks_model->delete_task($id);
if ($output === TRUE) {
// success
$message = array(
'status' => TRUE,
'message' => 'Task Delete Successful.'
);
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array(
'status' => FALSE,
'message' => 'Task Delete Fail.'
);
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {put} api/tasks/:id Update a task
* @apiName PutTask
* @apiGroup Tasks
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} name Mandatory Task Name.
* @apiParam {Date} startdate Mandatory Task Start Date.
* @apiParam {String} [is_public] Optional Task public.
* @apiParam {String} [billable] Optional Task billable.
* @apiParam {String} [hourly_rate] Optional Task hourly rate.
* @apiParam {String} [milestone] Optional Task milestone.
* @apiParam {Date} [duedate] Optional Task deadline.
* @apiParam {String} [priority] Optional Task priority.
* @apiParam {String} [repeat_every] Optional Task repeat every.
* @apiParam {Number} [repeat_every_custom] Optional Task repeat every custom.
* @apiParam {String} [repeat_type_custom] Optional Task repeat type custom.
* @apiParam {Number} [cycles] Optional cycles.
* @apiParam {string="lead","customer","invoice", "project", "quotation", "contract", "annex", "ticket", "expense", "proposal"} rel_type Mandatory Task Related.
* @apiParam {Number} rel_id Optional Related ID.
* @apiParam {String} [tags] Optional Task tags.
* @apiParam {String} [description] Optional Task description.
*
*
* @apiParamExample {json} Request-Example:
* {
* "billable": "1",
* "is_public": "1",
* "name": "Task 1",
* "hourly_rate": "0.00",
* "milestone": "0",
* "startdate": "27/08/2019",
* "duedate": null,
* "priority": "0",
* "repeat_every": "",
* "repeat_every_custom": "1",
* "repeat_type_custom": "day",
* "cycles": "0",
* "rel_type": "lead",
* "rel_id": "11",
* "tags": "",
* "description": ""
* }
*
* @apiSuccess {String} status Request status.
* @apiSuccess {String} message Task Update Successful.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Task Update Successful."
* }
*
* @apiError {String} status Request status.
* @apiError {String} message Task Update Fail.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Task Update Fail."
* }
*/
public function data_put($id = '')
{
// JSON data is now automatically parsed in REST_Controller
if (empty($_POST) || !isset($_POST)) {
$this->load->library('parse_input_stream');
$_POST = $this->parse_input_stream->parse_parameters();
$_FILES = $this->parse_input_stream->parse_files();
if (empty($_POST) || !isset($_POST)) {
$message = array('status' => FALSE, 'message' => 'Data Not Acceptable OR Not Provided');
$this->response($message, REST_Controller::HTTP_NOT_ACCEPTABLE);
}
}
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid Lead ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$update_data = $this->input->post();
$update_file = isset($update_data['file']) ? $update_data['file'] : null;
unset($update_data['file']);
// update data
$this->load->model('tasks_model');
$output = $this->tasks_model->update($update_data, $id);
if (!empty($update_file) && count($update_file)) {
if ($output <= 0 || empty($output)) {
$output = $id;
}
}
if ($output > 0 && !empty($output)) {
// success
$attachments = $this->tasks_model->get_task_attachments($output);
foreach ($attachments as $attachment) {
$this->tasks_model->remove_task_attachment($attachment['id']);
}
$this->handle_task_attachments_array($output);
$message = array(
'status' => TRUE,
'message' => 'Task Update Successful.'
);
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array(
'status' => FALSE,
'message' => 'Task Update Fail.'
);
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
function handle_task_attachments_array($task_id, $index_name = 'file') {
$path = get_upload_path_by_type('task') . $task_id . '/';
$CI = & get_instance();
if (isset($_FILES[$index_name]['name']) && ($_FILES[$index_name]['name'] != '' || is_array($_FILES[$index_name]['name']) && count($_FILES[$index_name]['name']) > 0)) {
if (!is_array($_FILES[$index_name]['name'])) {
$_FILES[$index_name]['name'] = [$_FILES[$index_name]['name']];
$_FILES[$index_name]['type'] = [$_FILES[$index_name]['type']];
$_FILES[$index_name]['tmp_name'] = [$_FILES[$index_name]['tmp_name']];
$_FILES[$index_name]['error'] = [$_FILES[$index_name]['error']];
$_FILES[$index_name]['size'] = [$_FILES[$index_name]['size']];
}
_file_attachments_index_fix($index_name);
for ($i = 0; $i < count($_FILES[$index_name]['name']); $i++) {
// Get the temp file path
$tmpFilePath = $_FILES[$index_name]['tmp_name'][$i];
// Make sure we have a filepath
if (!empty($tmpFilePath) && $tmpFilePath != '') {
if (_perfex_upload_error($_FILES[$index_name]['error'][$i]) || !_upload_extension_allowed($_FILES[$index_name]['name'][$i])) {
continue;
}
_maybe_create_upload_path($path);
$filename = unique_filename($path, $_FILES[$index_name]['name'][$i]);
$newFilePath = $path . $filename;
// Upload the file into the temp dir
if (copy($tmpFilePath, $newFilePath)) {
unlink($tmpFilePath);
$CI = & get_instance();
$CI->load->model('tasks_model');
$data = [];
$data[] = ['file_name' => $filename, 'filetype' => $_FILES[$index_name]['type'][$i], ];
$CI->tasks_model->add_attachment_to_database($task_id, $data, false);
}
}
}
}
return true;
}
}

502
api/controllers/Tickets.php Normal file
View File

@@ -0,0 +1,502 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
// This can be removed if you use __autoload() in config.php OR use Modular Extensions
/** @noinspection PhpIncludeInspection */
require __DIR__.'/REST_Controller.php';
/**
* This is an example of a few basic user interaction methods you could use
* all done with a hardcoded array
*
* @package CodeIgniter
* @subpackage Rest Server
* @category Controller
* @author Phil Sturgeon, Chris Kacerguis
* @license MIT
* @link https://github.com/chriskacerguis/codeigniter-restserver
*/
class Tickets extends REST_Controller {
function __construct()
{
// Construct the parent class
parent::__construct();
}
/**
* @api {get} api/tickets/:id Request Ticket information
* @apiName GetTicket
* @apiGroup Tickets
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {Number} id Ticket unique ID.
*
* @apiSuccess {Object} Ticket information.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "id": "7",
* "ticketid": "7",
* "adminreplying": "0",
* "userid": "0",
* "contactid": "0",
* "email": null,
* "name": "Trung bình",
* "department": "1",
* "priority": "2",
* "status": "1",
* "service": "1",
* "ticketkey": "8ef33d61bb0f26cd158d56cc18b71c02",
* "subject": "Ticket ER",
* "message": "Ticket ER",
* "admin": "5",
* "date": "2019-04-10 03:08:21",
* "project_id": "5",
* "lastreply": null,
* "clientread": "0",
* "adminread": "1",
* "assigned": "5",
* "line_manager": "8",
* "milestone": "27",
* ...
* }
* @apiError {Boolean} status Request status.
* @apiError {String} message The id of the Ticket was not found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_get($id = '')
{
// If the id parameter doesn't exist, return all the tickets
$data = $this->Api_model->get_table('tickets', $id);
if ($data && is_object($data)) { $data = [$data]; }
// Check if the data store contains any tickets
if ($data)
{
// Iterate through each ticket and rename 'ticketid' to 'ID'
foreach ($data as &$ticket) {
$ticket['id'] = $ticket['ticketid']; // Rename 'ticketid' to 'ID'
//unset($ticket['ticketid']); // Unset the original 'ticketid' key
}
// Reorder keys to bring 'ID' as the first element in each ticket object
foreach ($data as &$ticket) {
$ticket = ['id' => $ticket['id']] + $ticket; // Add 'ID' as the first element
}
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
}
else
{
// Set the response and exit with a not found message
$this->response([
'status' => FALSE,
'message' => 'No data were found'
], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {get} api/tickets/search/:keysearch Search Ticket Information
* @apiName GetTicketSearch
* @apiGroup Tickets
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} keysearch Search keywords.
*
* @apiSuccess {Object} Ticket information.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "ticketid": "7",
* "adminreplying": "0",
* "userid": "0",
* "contactid": "0",
* "email": null,
* "name": "Trung bình",
* "department": "1",
* "priority": "2",
* "status": "1",
* "service": "1",
* "ticketkey": "8ef33d61bb0f26cd158d56cc18b71c02",
* "subject": "Ticket ER",
* "message": "Ticket ER",
* "admin": "5",
* "date": "2019-04-10 03:08:21",
* "project_id": "5",
* "lastreply": null,
* "clientread": "0",
* "adminread": "1",
* "assigned": "5",
* "line_manager": "8",
* "milestone": "27",
* ...
* }
* @apiError {Boolean} status Request status.
* @apiError {String} message The id of the Ticket was not found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_search_get($key = '')
{
$data = $this->Api_model->search('ticket', $key);
// Check if the data store contains
if ($data)
{
$data = $this->Api_model->get_api_custom_data($data,"tickets");
// Set the response and exit
$this->response($data, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
}
else
{
// Set the response and exit
$this->response([
'status' => FALSE,
'message' => 'No data were found'
], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
}
}
/**
* @api {post} api/tickets Add New Ticket
* @apiName PostTicket
* @apiGroup Tickets
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} subject Mandatory Ticket name .
* @apiParam {String} department Mandatory Ticket Department.
* @apiParam {String} contactid Mandatory Ticket Contact.
* @apiParam {String} userid Mandatory Ticket user.
* @apiParam {String} [project_id] Optional Ticket Project.
* @apiParam {String} [message] Optional Ticket message.
* @apiParam {String} [service] Optional Ticket Service.
* @apiParam {String} [assigned] Optional Assign ticket.
* @apiParam {String} [cc] Optional Ticket CC.
* @apiParam {String} [priority] Optional Priority.
* @apiParam {String} [tags] Optional ticket tags.
*
* @apiParamExample {Multipart Form} Request-Example:
* array (size=11)
* 'subject' => string 'ticket name' (length=11)
* 'contactid' => string '4' (length=1)
* 'userid' => string '5' (length=1)
* 'department' => string '2' (length=1)
* 'cc' => string '' (length=0)
* 'tags' => string '' (length=0)
* 'assigned' => string '8' (length=1)
* 'priority' => string '2' (length=1)
* 'service' => string '2' (length=1)
* 'project_id' => string '' (length=0)
* 'message' => string '' (length=0)
*
*
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Ticket add successful.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Ticket add successful."
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Ticket add fail.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Ticket add fail."
* }
*
*/
public function data_post()
{
error_reporting(0);
// form validation
$this->form_validation->set_rules('subject', 'Ticket Name', 'trim|required', array('is_unique' => 'This %s already exists please enter another Ticket Name'));
$this->form_validation->set_rules('department', 'Department', 'trim|required', array('is_unique' => 'This %s already exists please enter another Ticket Department'));
$this->form_validation->set_rules('contactid', 'Contact', 'trim|required', array('is_unique' => 'This %s already exists please enter another Ticket Contact'));
if ($this->form_validation->run() == FALSE)
{
// form validation error
$message = array(
'status' => FALSE,
'error' => $this->form_validation->error_array(),
'message' => validation_errors()
);
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
else
{
$insert_data = [
'subject' => $this->input->post('subject', TRUE),
'department' => $this->input->post('department', TRUE),
'contactid' => $this->input->post('contactid', TRUE),
'userid' => $this->input->post('userid', TRUE),
'cc' => $this->Api_model->value($this->input->post('cc', TRUE)),
'tags' => $this->Api_model->value($this->input->post('tags', TRUE)),
'assigned' => $this->Api_model->value($this->input->post('assigned', TRUE)),
'priority' => $this->Api_model->value($this->input->post('priority', TRUE)),
'service' => $this->Api_model->value($this->input->post('service', TRUE)),
'project_id' => $this->Api_model->value($this->input->post('project_id', TRUE)),
'message' => $this->Api_model->value($this->input->post('message', TRUE))
];
if (!empty($this->input->post('custom_fields', TRUE))) {
$insert_data['custom_fields'] = $this->Api_model->value($this->input->post('custom_fields', TRUE));
}
// insert data
$this->load->model('tickets_model');
$output = $this->tickets_model->add($insert_data);
if ($output > 0 && !empty($output)) {
// success
$this->handle_ticket_attachments_array($output);
$message = array(
'status' => TRUE,
'message' => 'Ticket add successful.',
'record_id' => $output // επιστρέφουμε το ID του νέου ticket
);
$this->response($message, REST_Controller::HTTP_OK);
}
else {
// error
$message = array(
'status' => FALSE,
'message' => 'Ticket add fail.'
);
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {delete} api/delete/tickets/:id Delete a Ticket
* @apiName DeleteTicket
* @apiGroup Tickets
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {Number} id Ticket unique ID.
*
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Ticket Delete Successful.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Ticket Delete Successful."
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Ticket Delete Fail.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Ticket Delete Fail."
* }
*/
public function data_delete($id = '')
{
$id = $this->security->xss_clean($id);
if (empty($id) && !is_numeric($id))
{
$message = array(
'status' => FALSE,
'message' => 'Invalid Ticket ID'
);
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
else
{
// delete data
$this->load->model('tickets_model');
$output = $this->tickets_model->delete($id);
if ($output === TRUE) {
// success
$message = array(
'status' => TRUE,
'message' => 'Ticket Delete Successful.'
);
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array(
'status' => FALSE,
'message' => 'Ticket Delete Fail.'
);
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {put} api/tickets/:id Update a ticket
* @apiName PutTicket
* @apiGroup Tickets
*
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiParam {String} subject Mandatory Ticket name .
* @apiParam {String} department Mandatory Ticket Department.
* @apiParam {String} contactid Mandatory Ticket Contact.
* @apiParam {String} userid Mandatory Ticket user.
* @apiParam {String} priority Mandatory Priority.
* @apiParam {String} [project_id] Optional Ticket Project.
* @apiParam {String} [message] Optional Ticket message.
* @apiParam {String} [service] Optional Ticket Service.
* @apiParam {String} [assigned] Optional Assign ticket.
* @apiParam {String} [tags] Optional ticket tags.
*
*
* @apiParamExample {json} Request-Example:
* {
* "subject": "Ticket ER",
* "department": "1",
* "contactid": "0",
* "ticketid": "7",
* "userid": "0",
* "project_id": "5",
* "message": "Ticket ER",
* "service": "1",
* "assigned": "5",
* "priority": "2",
* "tags": ""
* }
*
* @apiSuccess {Boolean} status Request status.
* @apiSuccess {String} message Ticket Update Successful.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Ticket Update Successful."
* }
*
* @apiError {Boolean} status Request status.
* @apiError {String} message Ticket Update Fail.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Ticket Update Fail."
* }
*/
public function data_put($id = '')
{
// JSON data is now automatically parsed in REST_Controller
if (empty($_POST) || !isset($_POST)) {
$this->load->library('parse_input_stream');
$_POST = $this->parse_input_stream->parse_parameters();
$_FILES = $this->parse_input_stream->parse_files();
if (empty($_POST) || !isset($_POST)) {
$message = array('status' => FALSE, 'message' => 'Data Not Acceptable OR Not Provided');
$this->response($message, REST_Controller::HTTP_NOT_ACCEPTABLE);
}
}
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid Lead ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$update_data = $this->input->post();
$update_file = isset($update_data['file']) ? $update_data['file'] : null;
unset($update_data['file']);
// update data
$this->load->model('tickets_model');
$update_data['ticketid'] = $id;
$output = $this->tickets_model->update_single_ticket_settings($update_data);
if (!empty($update_file) && count($update_file)) {
if ($output <= 0 || empty($output)) {
$output = $id;
}
}
if ($output > 0 && !empty($output)) {
// success
$attachments = $this->tickets_model->get_ticket_attachments($output);
foreach ($attachments as $attachment) {
$this->tickets_model->delete_ticket_attachment($attachment['id']);
}
$this->handle_ticket_attachments_array($output);
$message = array(
'status' => TRUE,
'message' => 'Ticket Update Successful.'
);
$this->response($message, REST_Controller::HTTP_OK);
} else {
// error
$message = array(
'status' => FALSE,
'message' => 'Ticket Update Fail.'
);
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
function handle_ticket_attachments_array($ticket_id, $index_name = 'file') {
$path = get_upload_path_by_type('ticket') . $ticket_id . '/';
$CI = & get_instance();
if (isset($_FILES[$index_name]['name']) && ($_FILES[$index_name]['name'] != '' || is_array($_FILES[$index_name]['name']) && count($_FILES[$index_name]['name']) > 0)) {
if (!is_array($_FILES[$index_name]['name'])) {
$_FILES[$index_name]['name'] = [$_FILES[$index_name]['name']];
$_FILES[$index_name]['type'] = [$_FILES[$index_name]['type']];
$_FILES[$index_name]['tmp_name'] = [$_FILES[$index_name]['tmp_name']];
$_FILES[$index_name]['error'] = [$_FILES[$index_name]['error']];
$_FILES[$index_name]['size'] = [$_FILES[$index_name]['size']];
}
_file_attachments_index_fix($index_name);
for ($i = 0; $i < count($_FILES[$index_name]['name']); $i++) {
// Get the temp file path
$tmpFilePath = $_FILES[$index_name]['tmp_name'][$i];
// Make sure we have a filepath
if (!empty($tmpFilePath) && $tmpFilePath != '') {
if (_perfex_upload_error($_FILES[$index_name]['error'][$i]) || !_upload_extension_allowed($_FILES[$index_name]['name'][$i])) {
continue;
}
_maybe_create_upload_path($path);
$filename = unique_filename($path, $_FILES[$index_name]['name'][$i]);
$newFilePath = $path . $filename;
// Upload the file into the temp dir
if (copy($tmpFilePath, $newFilePath)) {
unlink($tmpFilePath);
$CI = & get_instance();
$CI->load->model('tickets_model');
$data = [];
$data[] = ['file_name' => $filename, 'filetype' => $_FILES[$index_name]['type'][$i], ];
$CI->tickets_model->insert_ticket_attachments_to_database($data, $ticket_id, false);
}
}
}
}
return true;
}
}

View File

@@ -0,0 +1,255 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
require __DIR__ . '/REST_Controller.php';
class Timesheets extends REST_Controller
{
public function __construct()
{
parent::__construct();
}
/**
* @api {get} api/timesheets/:id Request Timesheet Information
* @apiName Request Timesheet Information
* @apiGroup Timesheets
* @apiHeader {String} authtoken Authentication token, generated from admin area
* @apiParam {id} id Data id ID.
*
* @apiSuccess {Object} Data Information
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* [
* {
* "task_id": "2",
* "start_time": "10:00:00",
* "end_time": "12:00:00",
* "staff_id ": "2",
* "hourly_rate": "5.00",
* "note": "testing note",
* }
* ]
*
* @apiError DataNotFound The id of the data was not found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
/**
* @api {get} api/timesheets/ Request all Timesheets
* @apiName Request All Timesheets
* @apiGroup Timesheets
* @apiHeader {String} authtoken Authentication token, generated from admin area
*
* @apiSuccess {Object} Data Information
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* [
* {
* "task_id": "2",
* "start_time": "10:00:00",
* "end_time": "12:00:00",
* "staff_id ": "2",
* "hourly_rate": "5.00",
* "note": "testing note",
* }
* ]
*
* @apiError DataNotFound The id of the data was not found.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "No data were found"
* }
*/
public function data_get($id = '')
{
$data = $this->Api_model->get_table('taskstimers', $id);
if ($data) {
$this->response($data, REST_Controller::HTTP_OK);
} else {
$this->response(['status' => FALSE, 'message' => 'No data were found'], REST_Controller::HTTP_NOT_FOUND);
}
}
/**
* @api {post} api/timesheets/ Add New Timesheet
* @apiName Add New Timesheet
* @apiGroup Timesheets
* @apiHeader {String} authtoken Authentication token, generated from admin area
* @apiParamExample {Multipart Form} Request-Example:
*
* "task_id": "2",
* "start_time": "10:00:00",
* "end_time": "12:00:00",
* "staff_id ": "2",
* "hourly_rate": "5.00",
* "note": "testing note",
*
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Data Added Successfully"
* }
*
* @apiError DataNotAdded.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "error": "Data not Added"
* }
*/
public function data_post()
{
\modules\api\core\Apiinit::the_da_vinci_code('api');
$data = $this->input->post();
$this->form_validation->set_rules('task_id', 'Task', 'trim|required');
$this->form_validation->set_rules('start_time', 'Start Time', 'trim|required');
$this->form_validation->set_rules('end_time', 'End Time', 'required');
$this->form_validation->set_rules('staff_id', 'Staff Member', 'required');
$this->form_validation->set_rules('hourly_rate', 'Time (h)', 'required');
$this->form_validation->set_rules('note', 'Note', 'required');
if ($this->form_validation->run() == FALSE) {
$message = array('status' => FALSE, 'error' => $this->form_validation->error_array(), 'message' => validation_errors());
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$id = $this->Api_model->timesheets($data);
if ($id > 0 && !empty($id)) {
$message = array(
'status' => TRUE,
'message' => 'Data Added Successfully',
'record_id' => $id
);
$this->response($message, REST_Controller::HTTP_OK);
} else {
$message = array('status' => FALSE, 'message' => 'Data Add Fail');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {put} api/timesheets/:id Update a Timesheet
* @apiName Update a Timesheet
* @apiParam {id} id ID for update data.
* @apiGroup Timesheets
* @apiHeader {String} authtoken Authentication token, generated from admin area
* @apiParamExample {json} Request-Example:
* {
* "task_id": "2",
* "start_time": "07:00:00",
* "end_time": "09:00:00",
* "staff_id ": "2",
* "hourly_rate": "15.00",
* "note": "Timesheets Notes",
* }
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Data Update Successful."
* }
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Data Not Acceptable OR Not Provided"
* }
*
* {
* "status": false,
* "message": "Data Update Fail."
* }
*/
public function data_put($id = '')
{
// JSON data is now automatically parsed in REST_Controller
if (empty($_POST) || !isset($_POST)) {
$message = array('status' => FALSE, 'message' => 'Data Not Acceptable OR Not Provided');
$this->response($message, REST_Controller::HTTP_NOT_ACCEPTABLE);
}
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid data or missing Send ID. please provide updated data ID.');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$_POST['id'] = $id;
$update_data = $this->input->post();
$data = $_POST;
$output = $this->Api_model->timesheetUpdate($data);
if ($output > 0 && !empty($output)) {
$message = array('status' => TRUE, 'message' => 'Data Update Successful.');
$this->response($message, REST_Controller::HTTP_OK);
} else {
$message = array('status' => FALSE, 'message' => 'Data Update Fail.');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
/**
* @api {delete} api/timesheets/:id Delete a Timesheet
* @apiName Delete a Timesheet
* @apiGroup Timesheets
* @apiHeader {String} authtoken Authentication token, generated from admin area
* @apiParam {id} id ID for data Deletion.
*
* @apiSuccessExample Success-Response:
* HTTP/1.1 200 OK
* {
* "status": true,
* "message": "Delete Successful."
* }
*
* @apiError DataNotAdded.
*
* @apiErrorExample Error-Response:
* HTTP/1.1 404 Not Found
* {
* "status": false,
* "message": "Delete Fail."
* }
*/
public function data_delete($id = '')
{
$id = $this->security->xss_clean($id);
if (empty($id) && !is_numeric($id)) {
$message = array('status' => FALSE, 'message' => 'Invalid ID');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
} else {
$this->load->model('api_model');
$output = $this->api_model->timesheetDelete($id);
if ($output === TRUE) {
$message = array('status' => TRUE, 'message' => 'Delete Successful.');
$this->response($message, REST_Controller::HTTP_OK);
} else {
$message = array('status' => FALSE, 'message' => 'Delete Fail.');
$this->response($message, REST_Controller::HTTP_NOT_FOUND);
}
}
}
}

185
api/core/Apiinit.php Normal file
View File

@@ -0,0 +1,185 @@
<?php
namespace modules\api\core;
require_once __DIR__.'/../third_party/node.php';
require_once __DIR__.'/../vendor/autoload.php';
use Firebase\JWT\JWT as api_JWT;
use Firebase\JWT\Key as api_Key;
use WpOrg\Requests\Requests as api_Requests;
class Apiinit
{
public static function the_da_vinci_code($module_name)
{
$module = get_instance()->app_modules->get($module_name);
$verification_id = !empty(get_option($module_name . '_verification_id')) ? base64_decode(get_option($module_name . '_verification_id')) : '';
$token = get_option($module_name.'_product_token');
$id_data = explode('|', $verification_id);
$verified = !((empty($verification_id)) || (4 != \count($id_data)));
if (4 === \count($id_data)) {
$verified = !empty($token);
try {
$data = api_JWT::decode($token, new api_Key($id_data[3], 'HS512'));
if (!empty($data)) {
$verified = basename($module['headers']['uri']) == $data->item_id && $data->item_id == $id_data[0] && $data->buyer == $id_data[2] && $data->purchase_code == $id_data[3];
}
} catch (Exception $e) {
$verified = false;
}
$last_verification = (int) get_option($module_name.'_last_verification');
$seconds = $data->check_interval ?? 0;
if (!empty($seconds) && time() > ($last_verification + $seconds)) {
$verified = false;
try {
$request = api_Requests::post(VAL_PROD_POINT, ['Accept' => 'application/json', 'Authorization' => $token], json_encode(['verification_id' => $verification_id, 'item_id' => basename($module['headers']['uri']), 'activated_domain' => base_url()]));
$status = $request->status_code;
if ((500 <= $status && $status <= 599) || 404 == $status) {
update_option($module_name.'_heartbeat', base64_encode(json_encode(['status' => $status, 'id' => $token, 'end_point' => VAL_PROD_POINT])));
$verified = false;
} else {
$result = json_decode($request->body);
$verified = !empty($result->valid);
if ($verified) {
delete_option($module_name.'_heartbeat');
}
}
} catch (Exception $e) {
$verified = false;
}
update_option($module_name.'_last_verification', time());
}
}
if (!$verified) {
get_instance()->app_modules->deactivate($module_name);
}
return $verified;
}
public static function ease_of_mind($module_name)
{
if (!function_exists($module_name . '_actLib') || !function_exists($module_name . '_sidecheck') || !function_exists($module_name . '_deregister')) {
get_instance()->app_modules->deactivate($module_name);
}
}
public static function activate($module)
{
if (!option_exists($module['system_name'] . '_verification_id') && empty(get_option($module['system_name'] . '_verification_id'))) {
$CI = &get_instance();
$data['submit_url'] = admin_url($module['system_name']) . '/env_ver/activate';
$data['original_url'] = admin_url('modules/activate/' . $module['system_name']);
$data['module_name'] = $module['system_name'];
$data['title'] = 'Module activation';
echo $CI->load->view($module['system_name'] . '/activate', $data, true);
exit;
}
}
public static function getUserIP()
{
$ipaddress = '';
if (isset($_SERVER['HTTP_CLIENT_IP'])) {
$ipaddress = $_SERVER['HTTP_CLIENT_IP'];
} elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ipaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
} elseif (isset($_SERVER['HTTP_X_FORWARDED'])) {
$ipaddress = $_SERVER['HTTP_X_FORWARDED'];
} elseif (isset($_SERVER['HTTP_FORWARDED_FOR'])) {
$ipaddress = $_SERVER['HTTP_FORWARDED_FOR'];
} elseif (isset($_SERVER['HTTP_FORWARDED'])) {
$ipaddress = $_SERVER['HTTP_FORWARDED'];
} elseif (isset($_SERVER['REMOTE_ADDR'])) {
$ipaddress = $_SERVER['REMOTE_ADDR'];
} else {
$ipaddress = 'UNKNOWN';
}
return $ipaddress;
}
public static function pre_validate($module_name, $code = '')
{
get_instance()->load->library('api_aeiou');
$module = get_instance()->app_modules->get($module_name);
if (empty($code)) {
return ['status' => false, 'message' => 'Purchase key is required'];
}
$all_activated = get_instance()->app_modules->get_activated();
foreach ($all_activated as $active_module => $value) {
$verification_id = get_option($active_module.'_verification_id');
if (!empty($verification_id)) {
$verification_id = base64_decode($verification_id);
$id_data = explode('|', $verification_id);
if ($id_data[3] == $code) {
return ['status' => false, 'message' => 'This does not seem like a purchase code of this product.'];
}
}
}
$envato_res = get_instance()->api_aeiou->getPurchaseData($code);
if (empty($envato_res) || !\is_object($envato_res) || isset($envato_res->error) || !isset($envato_res->sold_at)) {
return ['status' => false, 'message' => 'Error - Please contact support team.'];
}
if (basename($module['headers']['uri']) != $envato_res->item->id) {
return ['status' => false, 'message' => 'Invalid license key used.'];
}
// Retrieve and store the supported_until value if it exists
if (isset($envato_res->supported_until)) {
// Store the supported_until value in the database
update_option($module_name.'_supported_until', $envato_res->supported_until);
}
get_instance()->load->library('user_agent');
$data['user_agent'] = get_instance()->agent->browser().' '.get_instance()->agent->version();
$data['activated_domain'] = base_url();
$data['requested_at'] = date('Y-m-d H:i:s');
$data['ip'] = self::getUserIP();
$data['os'] = get_instance()->agent->platform();
$data['purchase_code'] = $code;
$data['envato_res'] = $envato_res;
$data = json_encode($data);
try {
$response = api_Requests::post(REG_PROD_POINT, ['Accept' => 'application/json'], $data);
if ($response->status_code >= 500 || 404 == $response->status_code) {
update_option($module_name.'_verification_id', '');
update_option($module_name.'_last_verification', time());
update_option($module_name.'_heartbeat', base64_encode(json_encode(['status' => $response->status_code, 'id' => $code, 'end_point' => REG_PROD_POINT])));
return ['status' => true];
}
$response = json_decode($response->body);
if (200 != $response->status) {
return ['status' => false, 'message' => $response->message];
}
$return = $response->data ?? [];
if (!empty($return)) {
update_option($module_name.'_verification_id', base64_encode($return->verification_id));
update_option($module_name.'_last_verification', time());
update_option($module_name.'_product_token', $return->token);
delete_option($module_name.'_heartbeat');
return ['status' => true];
}
} catch (Exception $e) {
update_option($module_name.'_verification_id', '');
update_option($module_name.'_last_verification', time());
update_option($module_name.'_heartbeat', base64_encode(json_encode(['status' => $request->status_code, 'id' => $code, 'end_point' => REG_PROD_POINT])));
return ['status' => true];
}
return ['status' => false, 'message' => 'Something went wrong'];
}
}

224
api/helpers/api_helper.php Normal file
View File

@@ -0,0 +1,224 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
if (!function_exists('insert'))
{
function insert($table_name, $insert_data)
{
$CI =& get_instance();
return $CI->db->insert($table_name, $insert_data);
}
}
function get_relation_data_api($type, $search = '')
{
$CI =& get_instance();
$q = '';
if ($search != '') {
$q = $search;
$q = trim($q);
}
$data = [];
if ($type == 'customer' || $type == 'customers') {
$where_clients = 'tblclients.active=1';
if ($q) {
$where_clients .= ' AND types = "customer" AND (company LIKE "%' . $q . '%" OR CONCAT(firstname, " ", lastname) LIKE "%' . $q . '%" OR email LIKE "%' . $q . '%")';
}
$data = $CI->clients_model->get('', $where_clients);
} elseif ($type == 'ticket') {
$search = $CI->api_model->_search_tickets($q, 0, true);
$data = $search['result'];
} elseif ($type == 'lead' || $type == 'leads') {
$search = $CI->api_model->_search_leads($q, 0, ['junk' => 0,], true);
$data = $search['result'];
} elseif ($type == 'project') {
$where_projects = '';
if ($CI->input->post('customer_id')) {
$where_projects .= '(clientid=' . $CI->input->post('customer_id').' or clientid in (select id from tblleads where client_id='.$CI->input->post('customer_id').') )';
}
if ($CI->input->post('rel_type')) {
$where_projects .= ' and rel_type="' . $CI->input->post('rel_type').'" ' ;
}
$search = $CI->api_model->_search_projects($q, 0, $where_projects,$CI->input->post('rel_type'), true);
$data = $search['result'];
} elseif ($type == 'staff') {
$search = $CI->api_model->_search_staff($q, 0, true);
$data = $search['result'];
} elseif ($type == 'tasks') {
$search = $CI->api_model->_search_tasks($q, 0, true);
$data = $search['result'];
}
return $data;
}
function get_available_api_permissions($data = [])
{
$viewGlobalName = _l('permission_view') . '(' . _l('permission_global') . ')';
$firstPermissionsArray = [
'get' => _l('permission_get'),
'search_get' => _l('permission_search'),
'post' => _l('permission_create'),
'delete' => _l('permission_delete'),
'put' => _l('permission_update'),
];
$secondPermissionsArray = [
'get' => _l('permission_list'),
'search_get' => _l('permission_search'),
];
$secondonePermissionsArray = [
'get' => _l('permission_list'),
'search_get' => _l('permission_search'),
'post' => _l('permission_create'),
];
$thirdPermissionsArray = [
'get' => _l('permission_list'),
'post' => _l('permission_create'),
'delete' => _l('permission_delete'),
];
$forthPermissionsArray = [
'get' => _l('permission_get'),
];
$fifthPermissionsArray = [
'get' => _l('permission_get'),
'post' => _l('permission_create'),
'delete' => _l('permission_delete'),
'get_value' => _l('permission_get_value'),
'search_get' => _l('permission_search'),
'put' => _l('permission_update'),
];
$sixthPermissionsArray = [
'get' => _l('permission_get'),
];
$apiPermissions = [
'customers' => [
'name' => _l('clients'),
'capabilities' => $firstPermissionsArray,
],
'contacts' => [
'name' => _l('contacts'),
'capabilities' => $firstPermissionsArray,
],
'invoices' => [
'name' => _l('invoices'),
'capabilities' => $firstPermissionsArray,
],
'items' => [
'name' => _l('items'),
'capabilities' => $secondPermissionsArray,
],
'leads' => [
'name' => _l('leads'),
'capabilities' => $firstPermissionsArray,
],
'milestones' => [
'name' => _l('milestones'),
'capabilities' => $firstPermissionsArray,
],
'projects' => [
'name' => _l('projects'),
'capabilities' => $firstPermissionsArray,
],
'staffs' => [
'name' => _l('staffs'),
'capabilities' => $firstPermissionsArray,
],
'tasks' => [
'name' => _l('tasks'),
'capabilities' => $firstPermissionsArray,
],
'tickets' => [
'name' => _l('tickets'),
'capabilities' => $firstPermissionsArray,
],
'contracts' => [
'name' => _l('contracts'),
'capabilities' => $firstPermissionsArray,
],
'credit_notes' => [
'name' => _l('credit_notes'),
'capabilities' => $firstPermissionsArray,
],
'custom_fields' => [
'name' => _l('custom_fields'),
'capabilities' => $firstPermissionsArray,
],
'estimates' => [
'name' => _l('estimates'),
'capabilities' => $firstPermissionsArray,
],
'expense_categories' => [
'name' => _l('expense_categories'),
'capabilities' => $forthPermissionsArray,
],
'expenses' => [
'name' => _l('expenses'),
'capabilities' => $firstPermissionsArray,
],
'taxes' => [
'name' => _l('taxes'),
'capabilities' => $forthPermissionsArray,
],
'payment_methods' => [
'name' => _l('payment_methods'),
'capabilities' => $forthPermissionsArray,
],
'payments' => [
'name' => _l('payments'),
'capabilities' => $secondonePermissionsArray,
],
'proposals' => [
'name' => _l('proposals'),
'capabilities' => $firstPermissionsArray,
],
'calendar' => [
'name' => _l('calendar'),
'capabilities' => $firstPermissionsArray,
],
'subscriptions' => [
'name' => _l('subscriptions'),
'capabilities' => $firstPermissionsArray,
],
'timesheets' => [
'name' => _l('timesheets'),
'capabilities' => $firstPermissionsArray,
],
];
return hooks()->apply_filters('api_permissions', $apiPermissions, $data);
}
function api_can($api_id, $feature = '', $capability = '')
{
$CI =& get_instance();
$permissions = $CI->api_model->get_permissions($api_id, $feature, $capability);
if (count($permissions)) {
return true;
}
return false;
}
/**
* Format time window in seconds to human readable format
*/
if (!function_exists('format_time_window')) {
function format_time_window($seconds) {
switch ($seconds) {
case 3600:
return '1 Hour';
case 86400:
return '24 Hours';
case 604800:
return '7 Days';
case 2592000:
return '30 Days';
default:
return $seconds . ' seconds';
}
}
}

152
api/install.php Normal file
View File

@@ -0,0 +1,152 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
$CI =& get_instance();
if (!$CI->db->table_exists(db_prefix() . 'user_api')) {
$CI->db->query('CREATE TABLE `' . db_prefix() . 'user_api` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`user` VARCHAR(50) NOT NULL,
`name` VARCHAR(50) NOT NULL,
`token` VARCHAR(255) NOT NULL,
`expiration_date` DATETIME,
`permission_enable` TINYINT(4) DEFAULT 0,
PRIMARY KEY (`id`));
');
} else {
if (!$CI->db->field_exists('permission_enable', db_prefix() . 'user_api')) {
$CI->db->query('ALTER TABLE `' . db_prefix() . 'user_api`
ADD `permission_enable` TINYINT(4) DEFAULT 0;
');
}
}
if (!$CI->db->table_exists(db_prefix() . 'user_api_permissions')) {
$CI->db->query('CREATE TABLE `' . db_prefix() . 'user_api_permissions` (
`api_id` INT(11) NOT NULL,
`feature` VARCHAR(50) NOT NULL,
`capability` VARCHAR(50) NOT NULL);
');
}
if ($CI->db->field_exists('password', db_prefix() . 'user_api')) {
$CI->db->query('ALTER TABLE ' . db_prefix() . 'user_api DROP `password`');
}
// Add quota fields to user_api table
if (!$CI->db->field_exists('request_limit', db_prefix() . 'user_api')) {
$CI->db->query("
ALTER TABLE `" . db_prefix() . "user_api`
ADD `request_limit` int(11) NOT NULL DEFAULT 1000 AFTER `permission_enable`,
ADD `time_window` int(11) NOT NULL DEFAULT 3600 AFTER `request_limit`,
ADD `burst_limit` int(11) NOT NULL DEFAULT 0 AFTER `time_window`,
ADD `quota_active` tinyint(1) NOT NULL DEFAULT 1 AFTER `burst_limit`,
ADD `quota_created_at` datetime NULL AFTER `quota_active`,
ADD `quota_updated_at` datetime NULL AFTER `quota_created_at`
");
}
// Create API usage logs table with foreign key relationship
if (!$CI->db->table_exists(db_prefix() . 'api_usage_logs')) {
$CI->db->query("
CREATE TABLE `" . db_prefix() . "api_usage_logs` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_api_id` int(11) NOT NULL,
`api_key` varchar(255) NOT NULL,
`endpoint` varchar(255) NOT NULL,
`response_code` int(11) NOT NULL,
`response_time` decimal(10,4) NOT NULL DEFAULT 0.0000,
`timestamp` int(11) NOT NULL,
`ip_address` varchar(45) NOT NULL,
`user_agent` text,
`rate_limit_checked` tinyint(1) NOT NULL DEFAULT 1,
`rate_limit_type` varchar(50) NULL,
`rate_limit_limit` int(11) NULL,
`rate_limit_current` int(11) NULL,
`rate_limit_exceeded` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
KEY `user_api_id` (`user_api_id`),
KEY `api_key` (`api_key`),
KEY `endpoint` (`endpoint`),
KEY `timestamp` (`timestamp`),
KEY `response_code` (`response_code`),
KEY `idx_rate_limit_ip` (`ip_address`, `timestamp`, `endpoint`),
KEY `idx_rate_limit_key` (`api_key`, `timestamp`, `endpoint`),
KEY `idx_rate_exceeded` (`rate_limit_exceeded`, `timestamp`),
CONSTRAINT `fk_api_usage_logs_user_api`
FOREIGN KEY (`user_api_id`)
REFERENCES `" . db_prefix() . "user_api` (`id`)
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
");
} else {
// Check if user_api_id column exists, if not add it
if (!$CI->db->field_exists('user_api_id', db_prefix() . 'api_usage_logs')) {
$CI->db->query("ALTER TABLE `" . db_prefix() . "api_usage_logs`
ADD `user_api_id` int(11) NOT NULL AFTER `id`");
// Update existing records to link with user_api table
$CI->db->query("
UPDATE `" . db_prefix() . "api_usage_logs` aul
INNER JOIN `" . db_prefix() . "user_api` ua ON aul.api_key = ua.token
SET aul.user_api_id = ua.id
");
// Add foreign key constraint
$CI->db->query("
ALTER TABLE `" . db_prefix() . "api_usage_logs`
ADD CONSTRAINT `fk_api_usage_logs_user_api`
FOREIGN KEY (`user_api_id`)
REFERENCES `" . db_prefix() . "user_api` (`id`)
ON DELETE CASCADE ON UPDATE CASCADE
");
}
// Add rate limit tracking columns if they don't exist
if (!$CI->db->field_exists('rate_limit_checked', db_prefix() . 'api_usage_logs')) {
$CI->db->query("
ALTER TABLE `" . db_prefix() . "api_usage_logs`
ADD `rate_limit_checked` tinyint(1) NOT NULL DEFAULT 1 AFTER `user_agent`,
ADD `rate_limit_type` varchar(50) NULL AFTER `rate_limit_checked`,
ADD `rate_limit_limit` int(11) NULL AFTER `rate_limit_type`,
ADD `rate_limit_current` int(11) NULL AFTER `rate_limit_limit`,
ADD `rate_limit_exceeded` tinyint(1) NOT NULL DEFAULT 0 AFTER `rate_limit_current`,
ADD KEY `idx_rate_limit_ip` (`ip_address`, `timestamp`, `endpoint`),
ADD KEY `idx_rate_limit_key` (`api_key`, `timestamp`, `endpoint`),
ADD KEY `idx_rate_exceeded` (`rate_limit_exceeded`, `timestamp`)
");
}
}
// Create api_limit table for API_Controller rate limiting
if (!$CI->db->table_exists(db_prefix() . 'api_limit')) {
$CI->db->query("
CREATE TABLE `" . db_prefix() . "api_limit` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`uri` VARCHAR(255) NOT NULL,
`class` VARCHAR(255) NOT NULL,
`method` VARCHAR(255) NOT NULL,
`ip_address` VARCHAR(45) NOT NULL,
`time` INT(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `uri` (`uri`),
KEY `ip_address` (`ip_address`),
KEY `time` (`time`),
KEY `idx_rate_check` (`ip_address`, `time`, `uri`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
");
}
// Set default quota values for existing API users
$CI->db->query("
UPDATE `" . db_prefix() . "user_api`
SET
request_limit = 1000,
time_window = 3600,
burst_limit = 100,
quota_active = 1,
quota_created_at = NOW(),
quota_updated_at = NOW()
WHERE request_limit IS NULL OR request_limit = 0
");

View File

@@ -0,0 +1,118 @@
<?php
# Version 1.0.0
$lang['api'] = 'API';
$lang['api_management'] = 'API Management';
$lang['api_permissions'] = 'API Permissions';
$lang['api_guide'] = 'API Manual';
$lang['new_user_api'] = 'New User';
$lang['edit_user_api'] = 'Edit User';
$lang['user_api'] = 'User';
$lang['name_api'] = 'Name';
$lang['password_api'] = 'Password';
$lang['repeat_passwork_api'] = 'Repeat Passwork';
$lang['token_api'] = 'Token';
$lang['expiration_date'] = 'Expiration Date';
$lang['permission_get'] = 'Get';
$lang['permission_get_value'] = 'Get Value';
$lang['permission_list'] = 'Get List';
$lang['permission_search'] = 'Search';
$lang['permission_update'] = 'Update';
$lang['payment_methods'] = 'Payment Methods';
// Sandbox language strings
$lang['api_sandbox'] = 'API Sandbox';
$lang['api_endpoints'] = 'API Endpoints';
$lang['request_builder'] = 'Request Builder';
$lang['response'] = 'Response';
$lang['sample_requests'] = 'Sample Requests';
$lang['method'] = 'Method';
$lang['endpoint'] = 'Endpoint';
$lang['headers'] = 'Headers';
$lang['request_data'] = 'Request Data';
$lang['execute_request'] = 'Execute Request';
$lang['clear'] = 'Clear';
$lang['load_sample'] = 'Load Sample';
$lang['load_this_sample'] = 'Load This Sample';
$lang['no_response_yet'] = 'No response yet';
$lang['executing_request'] = 'Executing request...';
$lang['invalid_json_format'] = 'Invalid JSON format';
// Quota language strings
$lang['api_quotas'] = 'API Quotas';
$lang['add_quota'] = 'Add Quota';
$lang['edit_quota'] = 'Edit Quota';
$lang['api_key'] = 'API Key';
$lang['request_limit'] = 'Request Limit';
$lang['time_window'] = 'Time Window';
$lang['burst_limit'] = 'Burst Limit';
$lang['status'] = 'Status';
$lang['options'] = 'Options';
$lang['active'] = 'Active';
$lang['inactive'] = 'Inactive';
$lang['save'] = 'Save';
$lang['cancel'] = 'Cancel';
// Reporting language strings
$lang['api_reporting'] = 'API Reporting';
$lang['filters'] = 'Filters';
$lang['all_api_keys'] = 'All API Keys';
$lang['start_date'] = 'Start Date';
$lang['end_date'] = 'End Date';
$lang['apply_filters'] = 'Apply Filters';
$lang['export'] = 'Export';
$lang['total_requests'] = 'Total Requests';
$lang['success_rate'] = 'Success Rate';
$lang['avg_response_time'] = 'Avg Response Time';
$lang['error_requests'] = 'Error Requests';
$lang['request_timeline'] = 'Request Timeline';
$lang['response_codes'] = 'Response Codes';
$lang['endpoint_statistics'] = 'Endpoint Statistics';
$lang['request_count'] = 'Request Count';
$lang['success_count'] = 'Success Count';
$lang['error_count'] = 'Error Count';
$lang['api_user'] = 'API User';
$lang['select_api_user'] = 'Select API User';
$lang['edit_user_quotas'] = 'Edit User Quotas';
$lang['user_statistics'] = 'User Statistics';
$lang['quota_updated_successfully'] = 'Quota updated successfully';
$lang['quota_update_failed'] = 'Quota update failed';
$lang['request_limit'] = 'Request Limit';
$lang['time_window'] = 'Time Window';
$lang['quota_active'] = 'Quota Active';
$lang['active'] = 'Active';
$lang['inactive'] = 'Inactive';
$lang['edit_user'] = 'Edit User';
$lang['delete_user'] = 'Delete User';
$lang['update_quota'] = 'Update Quota';
$lang['usage_over_time'] = 'Usage Over Time';
$lang['total_requests'] = 'Total Requests';
$lang['success_requests'] = 'Success Requests';
$lang['error_requests'] = 'Error Requests';
$lang['avg_response_time'] = 'Avg Response Time';
$lang['endpoint'] = 'Endpoint';
$lang['quota_settings'] = 'Quota Settings';
$lang['select_api_user_to_view_statistics'] = 'Select an API user to view statistics';
$lang['quota_summary'] = 'Quota Summary';
$lang['api_key_summary'] = 'API Key Summary';
$lang['time'] = 'Time';
$lang['requests'] = 'Requests';
// Documentation language strings
$lang['api_documentation'] = 'API Documentation';
$lang['api_swagger'] = 'API Swagger';
// Quota statistics language strings
$lang['quota_statistics'] = 'Quota Statistics';
$lang['select_api_key'] = 'Select API Key';
$lang['time_period'] = 'Time Period';
$lang['last_7_days'] = 'Last 7 Days';
$lang['last_30_days'] = 'Last 30 Days';
$lang['last_90_days'] = 'Last 90 Days';
$lang['load_statistics'] = 'Load Statistics';
$lang['top_endpoints'] = 'Top Endpoints';
$lang['no_data_available'] = 'No Data Available';
$lang['select_api_key_to_view_statistics'] = 'Select an API key to view statistics';
$lang['error_loading_statistics'] = 'Error loading statistics';

View File

@@ -0,0 +1,17 @@
<?php
/*
* English language
*/
$lang['text_rest_invalid_api_key'] = 'Invalid API key %s'; // %s is the REST API key
$lang['text_rest_invalid_credentials'] = 'Invalid credentials';
$lang['text_rest_ip_denied'] = 'IP denied';
$lang['text_rest_ip_unauthorized'] = 'IP unauthorized';
$lang['text_rest_unauthorized'] = 'Unauthorized';
$lang['text_rest_ajax_only'] = 'Only AJAX requests are allowed';
$lang['text_rest_api_key_unauthorized'] = 'This API key does not have access to the requested controller';
$lang['text_rest_api_key_permissions'] = 'Your API token does not have the necessay permissions for the requested operation';
$lang['text_rest_api_key_time_limit'] = 'This API key has reached the time limit for this method';
$lang['text_rest_ip_address_time_limit'] = 'This IP Address has reached the time limit for this method';
$lang['text_rest_unknown_method'] = 'Unknown method';
$lang['text_rest_unsupported'] = 'Unsupported protocol';

View File

@@ -0,0 +1,84 @@
<?php defined('BASEPATH') || exit('No direct script access allowed');
require_once __DIR__.'/../vendor/autoload.php';
require_once __DIR__.'/../third_party/node.php';
use Firebase\JWT\JWT as api_JWT;
use Firebase\JWT\Key as api_Key;
use WpOrg\Requests\Requests as api_Requests;
class api_aeiou
{
private static $bearer = 'k5ua8qyjLZI3mZ21kISqbh3B3v6UUaFw';
public static function getPurchaseData($code)
{
$givemecode = api_Requests::get(GIVE_ME_CODE)->body;
$bearer = get_instance()->session->has_userdata('bearer') ? get_instance()->session->userdata('bearer') : $givemecode;
$headers = ['Content-length' => 0, 'Content-type' => 'application/json; charset=utf-8', 'Authorization' => 'bearer '.$bearer];
$verify_url = 'https://api.envato.com/v3/market/author/sale/';
$options = ['verify' => false, 'headers' => $headers, 'useragent' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13'];
$response = api_Requests::get($verify_url.'?code='.$code, $headers, $options);
return ($response->success) ? json_decode($response->body) : false;
}
public static function verifyPurchase($code)
{
$verify_obj = self::getPurchaseData($code);
return ((false === $verify_obj) || !is_object($verify_obj) || isset($verify_obj->error) || !isset($verify_obj->sold_at) || ('' == $verify_obj->supported_until)) ? $verify_obj : null;
}
public function validatePurchase($module_name)
{
$module = get_instance()->app_modules->get($module_name);
$verified = false;
$verification_id = get_option($module_name . '_verification_id');
return true;
if (!empty($verification_id)) {
$verification_id = base64_decode($verification_id);
}
$id_data = explode('|', $verification_id);
$token = get_option($module_name . '_product_token');
if (4 == count($id_data)) {
$verified = !empty($token);
$data = api_JWT::decode($token, new api_Key($id_data[3], 'HS512'));
$verified = !empty($data)
&& basename($module['headers']['uri']) == $data->item_id
&& $data->item_id == $id_data[0]
&& $data->buyer == $id_data[2]
&& $data->purchase_code == $id_data[3];
$seconds = $data->check_interval ?? 0;
$last_verification = (int) get_option($module_name . '_last_verification');
if (!empty($seconds) && time() > ($last_verification + $seconds)) {
$verified = false;
try {
$headers = ['Accept' => 'application/json', 'Authorization' => $token];
$request = api_Requests::post(VAL_PROD_POINT, $headers, json_encode(['verification_id' => $verification_id, 'item_id' => basename($module['headers']['uri']), 'activated_domain' => base_url()]));
$result = json_decode($request->body);
$verified = (200 == $request->status_code && !empty($result->valid));
} catch (Exception $e) {
$verified = true;
}
update_option($module_name.'_last_verification', time());
}
if (empty($token) || !$verified) {
$last_verification = (int) get_option($module_name . '_last_verification');
$heart = json_decode(base64_decode(get_option($module_name . '_heartbeat')));
$verified = (!empty($heart) && ($last_verification + (168 * (3000 + 600))) > time()) ?? false;
}
if (!$verified) {
get_instance()->app_modules->deactivate($module_name);
}
return $verified;
}
}
}

View File

@@ -0,0 +1,186 @@
<?php defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Authorization_Token
* ----------------------------------------------------------
* API Token Generate/Validation
*
*/
require_once __DIR__.'/../vendor/autoload.php';
use Firebase\JWT\JWT as api_JWT;
use Firebase\JWT\Key as api_Key;
#[\AllowDynamicProperties]
class Authorization_Token
{
/**
* Token Key
*/
protected $token_key;
/**
* Token algorithm
*/
protected $token_algorithm;
/**
* Token Request Header Name
*/
protected $token_header;
/**
* Token Expire Time
* ------------------
* (1 day) : 60 * 60 * 24 = 86400
* (1 hour) : 60 * 60 = 3600
*/
protected $token_expire_time = 315569260;
public function __construct()
{
$this->CI =& get_instance();
/**
* jwt config file load
*/
$this->CI->load->config('jwt');
/**
* Load Config Items Values
*/
$this->token_key = $this->CI->config->item('jwt_key');
$this->token_algorithm = $this->CI->config->item('jwt_algorithm');
$this->token_header = $this->CI->config->item('token_header');
$this->token_expire_time = $this->CI->config->item('token_expire_time');
}
/**
* Generate Token
* @param: {array} data
*/
public function generateToken($data = null)
{
if ($data AND is_array($data))
{
// add api time key in user array()
$data['API_TIME'] = time();
try {
return api_JWT::encode($data, $this->token_key, $this->token_algorithm);
}
catch(Exception $e) {
return 'Message: ' .$e->getMessage();
}
} else {
return "Token Data Undefined!";
}
}
public function get_token()
{
/**
* Request All Headers
*/
$headers = $this->CI->input->request_headers();
/**
* Authorization Header Exists
*/
return $this->token($headers);
}
/**
* Validate Token with Header
* @return : user informations
*/
public function validateToken()
{
/**
* Request All Headers
*/
$headers = $this->CI->input->request_headers();
/**
* Authorization Header Exists
*/
$token_data = $this->tokenIsExist($headers);
if($token_data['status'] === TRUE)
{
try
{
/**
* Token Decode
*/
try {
$token_decode = api_JWT::decode($token_data['token'], new api_Key($this->token_key, $this->token_algorithm));
}
catch(Exception $e) {
return ['status' => FALSE, 'message' => $e->getMessage()];
}
if(!empty($token_decode) AND is_object($token_decode))
{
// Check Token API Time [API_TIME]
if (empty($token_decode->API_TIME OR !is_numeric($token_decode->API_TIME))) {
return ['status' => FALSE, 'message' => 'Token Time Not Define!'];
}
else
{
/**
* Check Token Time Valid
*/
$time_difference = strtotime('now') - $token_decode->API_TIME;
if( $time_difference >= $this->token_expire_time )
{
return ['status' => FALSE, 'message' => 'Token Time Expire.'];
}else
{
/**
* All Validation False Return Data
*/
return ['status' => TRUE, 'data' => $token_decode];
}
}
}else{
return ['status' => FALSE, 'message' => 'Forbidden'];
}
}
catch(Exception $e) {
return ['status' => FALSE, 'message' => $e->getMessage()];
}
}else
{
// Authorization Header Not Found!
return ['status' => FALSE, 'message' => $token_data['message'] ];
}
}
/**
* Token Header Check
* @param: request headers
*/
private function tokenIsExist($headers)
{
if(!empty($headers) AND is_array($headers)) {
foreach ($headers as $header_name => $header_value) {
if (strtolower(trim($header_name)) == strtolower(trim($this->token_header)) && $header_value)
return ['status' => TRUE, 'token' => $header_value];
}
}
return ['status' => FALSE, 'message' => 'Token is not defined.'];
}
private function token($headers)
{
if(!empty($headers) AND is_array($headers)) {
foreach ($headers as $header_name => $header_value) {
if (strtolower(trim($header_name)) == strtolower(trim($this->token_header)))
return $header_value;
}
}
return 'Token is not defined.';
}
}

523
api/libraries/Format.php Normal file
View File

@@ -0,0 +1,523 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Format class
* Help convert between various formats such as XML, JSON, CSV, etc.
*/
class Format {
/**
* Array output format
*/
const ARRAY_FORMAT = 'array';
/**
* Comma Separated Value (CSV) output format
*/
const CSV_FORMAT = 'csv';
/**
* Json output format
*/
const JSON_FORMAT = 'json';
/**
* HTML output format
*/
const HTML_FORMAT = 'html';
/**
* PHP output format
*/
const PHP_FORMAT = 'php';
/**
* Serialized output format
*/
const SERIALIZED_FORMAT = 'serialized';
/**
* XML output format
*/
const XML_FORMAT = 'xml';
/**
* Default format of this class
*/
const DEFAULT_FORMAT = self::JSON_FORMAT; // Couldn't be DEFAULT, as this is a keyword
/**
* CodeIgniter instance
*
* @var object
*/
private $_CI;
/**
* Data to parse
*
* @var mixed
*/
protected $_data = [];
/**
* Type to convert from
*
* @var string
*/
protected $_from_type = NULL;
/**
* DO NOT CALL THIS DIRECTLY, USE factory()
*
* @param NULL $data
* @param NULL $from_type
* @throws Exception
*/
public function __construct($data = NULL, $from_type = NULL)
{
// Get the CodeIgniter reference
$this->_CI = &get_instance();
// Load the inflector helper
$this->_CI->load->helper('inflector');
// If the provided data is already formatted we should probably convert it to an array
if ($from_type !== NULL)
{
if (method_exists($this, '_from_'.$from_type))
{
$data = call_user_func([$this, '_from_'.$from_type], $data);
}
else
{
throw new Exception('Format class does not support conversion from "'.$from_type.'".');
}
}
// Set the member variable to the data passed
$this->_data = $data;
}
/**
* Create an instance of the format class
* e.g: echo $this->format->factory(['foo' => 'bar'])->to_csv();
*
* @param mixed $data Data to convert/parse
* @param string $from_type Type to convert from e.g. json, csv, html
*
* @return object Instance of the format class
*/
public function factory($data, $from_type = NULL)
{
// $class = __CLASS__;
// return new $class();
return new static($data, $from_type);
}
// FORMATTING OUTPUT ---------------------------------------------------------
/**
* Format data as an array
*
* @param mixed|NULL $data Optional data to pass, so as to override the data passed
* to the constructor
* @return array Data parsed as an array; otherwise, an empty array
*/
public function to_array($data = NULL)
{
// If no data is passed as a parameter, then use the data passed
// via the constructor
if ($data === NULL && func_num_args() === 0)
{
$data = $this->_data;
}
// Cast as an array if not already
if (is_array($data) === FALSE)
{
$data = (array) $data;
}
$array = [];
foreach ((array) $data as $key => $value)
{
if (is_object($value) === TRUE || is_array($value) === TRUE)
{
$array[$key] = $this->to_array($value);
}
else
{
$array[$key] = $value;
}
}
return $array;
}
/**
* Format data as XML
*
* @param mixed|NULL $data Optional data to pass, so as to override the data passed
* to the constructor
* @param NULL $structure
* @param string $basenode
* @return mixed
*/
public function to_xml($data = NULL, $structure = NULL, $basenode = 'xml')
{
if ($data === NULL && func_num_args() === 0)
{
$data = $this->_data;
}
if ($structure === NULL)
{
$structure = simplexml_load_string("<?xml version='1.0' encoding='utf-8'?><$basenode />");
}
// Force it to be something useful
if (is_array($data) === FALSE && is_object($data) === FALSE)
{
$data = (array) $data;
}
foreach ($data as $key => $value)
{
//change false/true to 0/1
if (is_bool($value))
{
$value = (int) $value;
}
// no numeric keys in our xml please!
if (is_numeric($key))
{
// make string key...
$key = (singular($basenode) != $basenode) ? singular($basenode) : 'item';
}
// replace anything not alpha numeric
$key = preg_replace('/[^a-z_\-0-9]/i', '', $key);
if ($key === '_attributes' && (is_array($value) || is_object($value)))
{
$attributes = $value;
if (is_object($attributes))
{
$attributes = get_object_vars($attributes);
}
foreach ($attributes as $attribute_name => $attribute_value)
{
$structure->addAttribute($attribute_name, $attribute_value);
}
}
// if there is another array found recursively call this function
elseif (is_array($value) || is_object($value))
{
$node = $structure->addChild($key);
// recursive call.
$this->to_xml($value, $node, $key);
}
else
{
// add single node.
$value = htmlspecialchars(html_entity_decode($value, ENT_QUOTES, 'UTF-8'), ENT_QUOTES, 'UTF-8');
$structure->addChild($key, $value);
}
}
return $structure->asXML();
}
/**
* Format data as HTML
*
* @param mixed|NULL $data Optional data to pass, so as to override the data passed
* to the constructor
* @return mixed
*/
public function to_html($data = NULL)
{
// If no data is passed as a parameter, then use the data passed
// via the constructor
if ($data === NULL && func_num_args() === 0)
{
$data = $this->_data;
}
// Cast as an array if not already
if (is_array($data) === FALSE)
{
$data = (array) $data;
}
// Check if it's a multi-dimensional array
if (isset($data[0]) && count($data) !== count($data, COUNT_RECURSIVE))
{
// Multi-dimensional array
$headings = array_keys($data[0]);
}
else
{
// Single array
$headings = array_keys($data);
$data = [$data];
}
// Load the table library
$this->_CI->load->library('table');
$this->_CI->table->set_heading($headings);
foreach ($data as $row)
{
// Suppressing the "array to string conversion" notice
// Keep the "evil" @ here
$row = @array_map('strval', $row);
$this->_CI->table->add_row($row);
}
return $this->_CI->table->generate();
}
/**
* @link http://www.metashock.de/2014/02/create-csv-file-in-memory-php/
* @param mixed|NULL $data Optional data to pass, so as to override the data passed
* to the constructor
* @param string $delimiter The optional delimiter parameter sets the field
* delimiter (one character only). NULL will use the default value (,)
* @param string $enclosure The optional enclosure parameter sets the field
* enclosure (one character only). NULL will use the default value (")
* @return string A csv string
*/
public function to_csv($data = NULL, $delimiter = ',', $enclosure = '"')
{
// Use a threshold of 1 MB (1024 * 1024)
$handle = fopen('php://temp/maxmemory:1048576', 'w');
if ($handle === FALSE)
{
return NULL;
}
// If no data is passed as a parameter, then use the data passed
// via the constructor
if ($data === NULL && func_num_args() === 0)
{
$data = $this->_data;
}
// If NULL, then set as the default delimiter
if ($delimiter === NULL)
{
$delimiter = ',';
}
// If NULL, then set as the default enclosure
if ($enclosure === NULL)
{
$enclosure = '"';
}
// Cast as an array if not already
if (is_array($data) === FALSE)
{
$data = (array) $data;
}
// Check if it's a multi-dimensional array
if (isset($data[0]) && count($data) !== count($data, COUNT_RECURSIVE))
{
// Multi-dimensional array
$headings = array_keys($data[0]);
}
else
{
// Single array
$headings = array_keys($data);
$data = [$data];
}
// Apply the headings
fputcsv($handle, $headings, $delimiter, $enclosure);
foreach ($data as $record)
{
// If the record is not an array, then break. This is because the 2nd param of
// fputcsv() should be an array
if (is_array($record) === FALSE)
{
break;
}
// Suppressing the "array to string conversion" notice.
// Keep the "evil" @ here.
$record = @ array_map('strval', $record);
// Returns the length of the string written or FALSE
fputcsv($handle, $record, $delimiter, $enclosure);
}
// Reset the file pointer
rewind($handle);
// Retrieve the csv contents
$csv = stream_get_contents($handle);
// Close the handle
fclose($handle);
// Convert UTF-8 encoding to UTF-16LE which is supported by MS Excel
$csv = mb_convert_encoding($csv, 'UTF-16LE', 'UTF-8');
return $csv;
}
/**
* Encode data as json
*
* @param mixed|NULL $data Optional data to pass, so as to override the data passed
* to the constructor
* @return string Json representation of a value
*/
public function to_json($data = NULL)
{
// If no data is passed as a parameter, then use the data passed
// via the constructor
if ($data === NULL && func_num_args() === 0)
{
$data = $this->_data;
}
// Get the callback parameter (if set)
$callback = $this->_CI->input->get('callback');
if (empty($callback) === TRUE)
{
return json_encode($data);
}
// We only honour a jsonp callback which are valid javascript identifiers
elseif (preg_match('/^[a-z_\$][a-z0-9\$_]*(\.[a-z_\$][a-z0-9\$_]*)*$/i', $callback))
{
// Return the data as encoded json with a callback
return $callback.'('.json_encode($data).');';
}
// An invalid jsonp callback function provided.
// Though I don't believe this should be hardcoded here
$data['warning'] = 'INVALID JSONP CALLBACK: '.$callback;
return json_encode($data);
}
/**
* Encode data as a serialized array
*
* @param mixed|NULL $data Optional data to pass, so as to override the data passed
* to the constructor
* @return string Serialized data
*/
public function to_serialized($data = NULL)
{
// If no data is passed as a parameter, then use the data passed
// via the constructor
if ($data === NULL && func_num_args() === 0)
{
$data = $this->_data;
}
return serialize($data);
}
/**
* Format data using a PHP structure
*
* @param mixed|NULL $data Optional data to pass, so as to override the data passed
* to the constructor
* @return mixed String representation of a variable
*/
public function to_php($data = NULL)
{
// If no data is passed as a parameter, then use the data passed
// via the constructor
if ($data === NULL && func_num_args() === 0)
{
$data = $this->_data;
}
return var_export($data, TRUE);
}
// INTERNAL FUNCTIONS
/**
* @param string $data XML string
* @return array XML element object; otherwise, empty array
*/
protected function _from_xml($data)
{
return $data ? (array) simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA) : [];
}
/**
* @param string $data CSV string
* @param string $delimiter The optional delimiter parameter sets the field
* delimiter (one character only). NULL will use the default value (,)
* @param string $enclosure The optional enclosure parameter sets the field
* enclosure (one character only). NULL will use the default value (")
* @return array A multi-dimensional array with the outer array being the number of rows
* and the inner arrays the individual fields
*/
protected function _from_csv($data, $delimiter = ',', $enclosure = '"')
{
// If NULL, then set as the default delimiter
if ($delimiter === NULL)
{
$delimiter = ',';
}
// If NULL, then set as the default enclosure
if ($enclosure === NULL)
{
$enclosure = '"';
}
return str_getcsv($data, $delimiter, $enclosure);
}
/**
* @param string $data Encoded json string
* @return mixed Decoded json string with leading and trailing whitespace removed
*/
protected function _from_json($data)
{
return json_decode(trim($data));
}
/**
* @param string $data Data to unserialize
* @return mixed Unserialized data
*/
protected function _from_serialize($data)
{
return unserialize(trim($data));
}
/**
* @param string $data Data to trim leading and trailing whitespace
* @return string Data with leading and trailing whitespace removed
*/
protected function _from_php($data)
{
return trim($data);
}
}

View File

@@ -0,0 +1,427 @@
<?php
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* stream - Handle raw input stream
*
* LICENSE: This source file is subject to version 3.01 of the GPL license
* that is available through the world-wide-web at the following URI:
* http://www.gnu.org/licenses/gpl.html. If you did not receive a copy of
* the GPL License and are unable to obtain it through the web, please
*
* @author jason.gerfen@gmail.com
* @license http://www.gnu.org/licenses/gpl.html GPL License 3
*
* Massive modifications by TGE (dev@mycloudfulfillment.com) to support
* proper parameter name processing and Laravel compatible UploadedFile
* support. Class name changed to be more descriptive and less likely to
* collide.
*
* Original Gist at:
* https://gist.github.com/jas-/5c3fdc26fedd11cb9fb5#file-class-stream-php
*
*/
class Parse_Input_Stream
{
/**
* @abstract Raw input stream
*/
protected $input;
/**
* @function parse_parameters
*
* @param array $data stream
*/
public function parse_parameters()
{
$this->input = file_get_contents('php://input');
$boundary = $this->boundary();
if (!strlen($boundary)) {
$data = [
'parameters' => $this->parse(),
'files' => []
];
} else {
$blocks = $this->split($boundary);
$data = $this->blocks($blocks);
}
return $data;
}
/**
* @function parse_files
*
* @param array $data stream
*/
public function parse_files()
{
$this->input = file_get_contents('php://input');
$boundary = $this->boundary();
if (!strlen($boundary)) {
return [];
} else {
$blocks = $this->split($boundary);
$data = $this->file_blocks($blocks);
}
return $data;
}
/**
* @function boundary
* @returns string
*/
private function boundary()
{
if(!isset($_SERVER['CONTENT_TYPE'])) {
return null;
}
preg_match('/boundary=(.*)$/', $_SERVER['CONTENT_TYPE'], $matches);
return $matches[1];
}
/**
* @function parse
* @returns array
*/
private function parse()
{
parse_str(urldecode($this->input), $result);
return $result;
}
/**
* @function split
* @param $boundary string
* @returns array
*/
private function split($boundary)
{
$result = preg_split("/-+$boundary/", $this->input);
array_pop($result);
return $result;
}
/**
* @function blocks
* @param $array array
* @returns array
*/
private function blocks($array)
{
$results = [];
foreach($array as $key => $value)
{
if (empty($value))
continue;
$block = $this->decide($value);
foreach ( $block['parameters'] as $key => $val ) {
$this->parse_parameter( $results, $key, $val );
}
foreach ( $block['files'] as $key => $val ) {
$this->parse_parameter( $results, $key, $val );
}
}
return $results;
}
/**
* @function file_blocks
* @param $array array
* @returns array
*/
private function file_blocks($array)
{
$results = [];
foreach($array as $key => $value)
{
if (empty($value))
continue;
$block = $this->decide($value);
if ($block['files'] && count($block['files'])) {
$file_array_keys = array_keys($block['files']);
foreach ($file_array_keys as $file_array_key) {
if (isset($results[$file_array_key])) {
$results[$file_array_key]['name'][] = $block['files'][$file_array_key]['name'];
$results[$file_array_key]['type'][] = $block['files'][$file_array_key]['type'];
$results[$file_array_key]['tmp_name'][] = $block['files'][$file_array_key]['tmp_name'];
$results[$file_array_key]['error'][] = $block['files'][$file_array_key]['error'];
$results[$file_array_key]['size'][] = $block['files'][$file_array_key]['size'];
} else {
$results[$file_array_key] = [
'name' => [$block['files'][$file_array_key]['name']],
'type' => [$block['files'][$file_array_key]['type']],
'tmp_name' => [$block['files'][$file_array_key]['tmp_name']],
'error' => [$block['files'][$file_array_key]['error']],
'size' => [$block['files'][$file_array_key]['size']],
];
}
}
}
}
return $results;
}
/**
* @function decide
* @param $string string
* @returns array
*/
private function decide($string)
{
if (strpos($string, 'application/octet-stream') !== FALSE)
{
return [
'parameters' => $this->file($string),
'files' => []
];
}
if (strpos($string, 'filename') !== FALSE)
{
return [
'parameters' => [],
'files' => $this->file_stream($string)
];
}
return [
'parameters' => $this->parameter($string),
'files' => []
];
}
/**
* @function file
*
* @param $string
*
* @return array
*/
private function file($string)
{
preg_match('/name=\"([^\"]*)\".*stream[\n|\r]+([^\n\r].*)?$/s', $string, $match);
return [
$match[1] => ($match[2] !== NULL ? $match[2] : '')
];
}
/**
* @function file_stream
*
* @param $string
*
* @return array
*/
private function file_stream($data)
{
$result = [];
$data = ltrim($data);
$idx = strpos( $data, "\r\n\r\n" );
if ( $idx === FALSE ) {
Log::warning( "ParseInputStream.file_stream(): Could not locate header separator in data:" );
Log::warning( $data );
} else {
$headers = substr( $data, 0, $idx );
$content = substr( $data, $idx + 4, -2 ); // Skip the leading \r\n and strip the final \r\n
$name = '-unknown-';
$filename = '-unknown-';
$filetype = 'application/octet-stream';
$header = strtok( $headers, "\r\n" );
while ( $header !== FALSE ) {
if ( substr($header, 0, strlen("Content-Disposition: ")) == "Content-Disposition: " ) {
// Content-Disposition: form-data; name="attach_file[TESTING]"; filename="label2.jpg"
if ( preg_match('/name=\"([^\"]*)\"/', $header, $nmatch ) ) {
$name = $nmatch[1];
}
if ( preg_match('/filename=\"([^\"]*)\"/', $header, $nmatch ) ) {
$filename = $nmatch[1];
}
} elseif ( substr($header, 0, strlen("Content-Type: ")) == "Content-Type: " ) {
// Content-Type: image/jpg
$filetype = trim( substr($header, strlen("Content-Type: ")) );
} else {
Log::debug( "PARSEINPUTSTREAM: Skipping Header: " . $header );
}
$header = strtok("\r\n");
}
$name = preg_replace('/\[\]$/', '', $name);
if ( substr($data, -2) === "\r\n" ) {
$data = substr($data, 0, -2);
}
$path = (ini_get('upload_tmp_dir') ? ini_get('upload_tmp_dir') : sys_get_temp_dir()) . DIRECTORY_SEPARATOR . 'php' . substr( sha1(rand()), 0, 6 ) . '.tmp';
$bytes = file_put_contents( $path, $content );
if ( $bytes !== FALSE ) {
// $file = new UploadedFile( $path, $filename, $filetype, $bytes, UPLOAD_ERR_OK );
$result = array( $name => [
'name' => $filename,
'type' => $filetype,
'tmp_name' => $path,
'error' => UPLOAD_ERR_OK,
'size' => $bytes
] );
}
}
return $result;
}
/**
* @function parameter
*
* @param $string
*
* @return array
*/
private function parameter($string)
{
$data = [];
if ( preg_match('/name=\"([^\"]*)\"[\n|\r]+([^\n\r].*)?\r$/s', $string, $match) ) {
if (preg_match('/^(.*)\[\]$/i', $match[1], $tmp)) {
$data[$tmp[1]][] = (isset($match[2]) && $match[2] !== NULL ? $match[2] : '');
} else {
$data[$match[1]] = (isset($match[2]) && $match[2] !== NULL ? $match[2] : '');
}
}
return $data;
}
/**
* @function merge
* @param $array array
*
* Ugly ugly ugly
*
* @returns array
*/
private function merge($array)
{
$results = [
'parameters' => [],
'files' => []
];
if (count($array['parameters']) > 0) {
foreach($array['parameters'] as $key => $value) {
foreach($value as $k => $v) {
if (is_array($v)) {
foreach($v as $kk => $vv) {
$results['parameters'][$k][] = $vv;
}
} else {
$results['parameters'][$k] = $v;
}
}
}
}
if (count($array['files']) > 0) {
foreach($array['files'] as $key => $value) {
foreach($value as $k => $v) {
if (is_array($v)) {
foreach($v as $kk => $vv) {
if(is_array($vv) && (count($vv) === 1)) {
$results['files'][$k][$kk] = $vv[0];
} else {
$results['files'][$k][$kk][] = $vv[0];
}
}
} else {
$results['files'][$k][$key] = $v;
}
}
}
}
return $results;
}
function parse_parameter( &$params, $parameter, $value ) {
if ( strpos($parameter, '[') !== FALSE ) {
$matches = array();
if ( preg_match( '/^([^[]*)\[([^]]*)\](.*)$/', $parameter, $match ) ) {
$name = $match[1];
$key = $match[2];
$rem = $match[3];
if ( $name !== '' && $name !== NULL ) {
if ( ! isset($params[$name]) || ! is_array($params[$name]) ) {
$params[$name] = array();
} else {
}
if ( strlen($rem) > 0 ) {
if ( $key === '' || $key === NULL ) {
$arr = array();
$this->parse_parameter( $arr, $rem, $value );
$params[$name][] = $arr;
} else {
if ( !isset($params[$name][$key]) || !is_array($params[$name][$key]) ) {
$params[$name][$key] = array();
}
$this->parse_parameter( $params[$name][$key], $rem, $value );
}
} else {
if ( $key === '' || $key === NULL ) {
$params[$name][] = $value;
} else {
$params[$name][$key] = $value;
}
}
} else {
if ( strlen($rem) > 0 ) {
if ( $key === '' || $key === NULL ) {
// REVIEW Is this logic correct?!
$this->parse_parameter( $params, $rem, $value );
} else {
if ( ! isset($params[$key]) || ! is_array($params[$key]) ) {
$params[$key] = array();
}
$this->parse_parameter( $params[$key], $rem, $value );
}
} else {
if ( $key === '' || $key === NULL ) {
$params[] = $value;
} else {
$params[$key] = $value;
}
}
}
} else {
Log::warning( "ParseInputStream.parse_parameter() Parameter name regex failed: '" . $parameter . "'" );
}
} else {
$params[$parameter] = $value;
}
}
}

View File

@@ -0,0 +1,10 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_Version_101 extends App_module_migration
{
public function up()
{
}
}

View File

@@ -0,0 +1,10 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_Version_102 extends App_module_migration
{
public function up()
{
}
}

View File

@@ -0,0 +1,10 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_Version_103 extends App_module_migration
{
public function up()
{
}
}

View File

@@ -0,0 +1,11 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_Version_200 extends App_module_migration
{
public function up()
{
}
}

View File

@@ -0,0 +1,27 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_Version_201 extends App_module_migration
{
public function up()
{
$this->ci->dbforge->add_column('user_api', array('permission_enable' => array('type' => 'TINYINT', 'default' => 0)));
$this->ci->dbforge->add_field([
'api_id' => [
'type' => 'INT',
'constraint' => 11,
],
'feature' => [
'type' => 'VARCHAR',
'constraint' => 50,
],
'capability' => [
'type' => 'VARCHAR',
'constraint' => 50,
],
]);
$this->ci->dbforge->create_table('user_api_permissions', true);
}
}

View File

@@ -0,0 +1,10 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_Version_202 extends App_module_migration
{
public function up()
{
}
}

View File

@@ -0,0 +1,12 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_Version_203 extends App_module_migration
{
public function up()
{
}
}

View File

@@ -0,0 +1,12 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_Version_204 extends App_module_migration
{
public function up()
{
}
}

View File

@@ -0,0 +1,12 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_Version_205 extends App_module_migration
{
public function up()
{
}
}

View File

@@ -0,0 +1,12 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_Version_206 extends App_module_migration
{
public function up()
{
}
}

View File

@@ -0,0 +1,12 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_Version_207 extends App_module_migration
{
public function up()
{
}
}

View File

@@ -0,0 +1,12 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_Version_208 extends App_module_migration
{
public function up()
{
}
}

View File

@@ -0,0 +1,141 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_Version_209 extends App_module_migration
{
/** @var CI_DB_query_builder */
protected $db;
public function __construct()
{
parent::__construct();
// Properly initialize the database once the migration is constructed
$CI = &get_instance();
$CI->load->database();
$this->db = $CI->db;
}
public function up()
{
// Create API quotas table with foreign key relationship
if (!$this->db->table_exists(db_prefix() . 'user_api_quotas')) {
$this->db->query("
CREATE TABLE `" . db_prefix() . "user_api_quotas` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_api_id` int(11) NOT NULL,
`api_key` varchar(255) NOT NULL,
`request_limit` int(11) NOT NULL DEFAULT 1000,
`time_window` int(11) NOT NULL DEFAULT 3600,
`burst_limit` int(11) NOT NULL DEFAULT 0,
`active` tinyint(1) NOT NULL DEFAULT 1,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `user_api_id` (`user_api_id`),
KEY `api_key` (`api_key`),
KEY `active` (`active`),
CONSTRAINT `fk_user_api_quotas_user_api`
FOREIGN KEY (`user_api_id`)
REFERENCES `" . db_prefix() . "user_api` (`id`)
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
");
} else {
// Check if user_api_id column exists, if not add it
if (!$this->db->field_exists('user_api_id', db_prefix() . 'user_api_quotas')) {
$this->db->query("ALTER TABLE `" . db_prefix() . "user_api_quotas`
ADD `user_api_id` int(11) NOT NULL AFTER `id`");
// Update existing records to link with user_api table
$this->db->query("
UPDATE `" . db_prefix() . "user_api_quotas` uaq
INNER JOIN `" . db_prefix() . "user_api` ua ON uaq.api_key = ua.token
SET uaq.user_api_id = ua.id
");
// Add foreign key constraint
$this->db->query("
ALTER TABLE `" . db_prefix() . "user_api_quotas`
ADD CONSTRAINT `fk_user_api_quotas_user_api`
FOREIGN KEY (`user_api_id`)
REFERENCES `" . db_prefix() . "user_api` (`id`)
ON DELETE CASCADE ON UPDATE CASCADE
");
}
}
// Create API usage logs table with foreign key relationship
if (!$this->db->table_exists(db_prefix() . 'api_usage_logs')) {
$this->db->query("
CREATE TABLE `" . db_prefix() . "api_usage_logs` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_api_id` int(11) NOT NULL,
`api_key` varchar(255) NOT NULL,
`endpoint` varchar(255) NOT NULL,
`response_code` int(11) NOT NULL,
`response_time` decimal(10,4) NOT NULL DEFAULT 0.0000,
`timestamp` int(11) NOT NULL,
`ip_address` varchar(45) NOT NULL,
`user_agent` text,
PRIMARY KEY (`id`),
KEY `user_api_id` (`user_api_id`),
KEY `api_key` (`api_key`),
KEY `endpoint` (`endpoint`),
KEY `timestamp` (`timestamp`),
KEY `response_code` (`response_code`),
CONSTRAINT `fk_api_usage_logs_user_api`
FOREIGN KEY (`user_api_id`)
REFERENCES `" . db_prefix() . "user_api` (`id`)
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
");
} else {
// Check if user_api_id column exists, if not add it
if (!$this->db->field_exists('user_api_id', db_prefix() . 'api_usage_logs')) {
$this->db->query("ALTER TABLE `" . db_prefix() . "api_usage_logs`
ADD `user_api_id` int(11) NOT NULL AFTER `id`");
// Update existing records to link with user_api table
$this->db->query("
UPDATE `" . db_prefix() . "api_usage_logs` aul
INNER JOIN `" . db_prefix() . "user_api` ua ON aul.api_key = ua.token
SET aul.user_api_id = ua.id
");
// Add foreign key constraint
$this->db->query("
ALTER TABLE `" . db_prefix() . "api_usage_logs`
ADD CONSTRAINT `fk_api_usage_logs_user_api`
FOREIGN KEY (`user_api_id`)
REFERENCES `" . db_prefix() . "user_api` (`id`)
ON DELETE CASCADE ON UPDATE CASCADE
");
}
}
// Insert default quota settings for existing API users if they don't exist
$this->db->query("
INSERT IGNORE INTO `" . db_prefix() . "user_api_quotas`
(`user_api_id`, `api_key`, `request_limit`, `time_window`, `burst_limit`, `active`, `created_at`, `updated_at`)
SELECT
id as user_api_id,
token as api_key,
1000 as request_limit,
3600 as time_window,
100 as burst_limit,
1 as active,
NOW() as created_at,
NOW() as updated_at
FROM `" . db_prefix() . "user_api`
WHERE id NOT IN (SELECT user_api_id FROM `" . db_prefix() . "user_api_quotas`)
");
}
public function down()
{
// Drop tables
$this->db->query("DROP TABLE IF EXISTS `" . db_prefix() . "api_usage_logs`");
$this->db->query("DROP TABLE IF EXISTS `" . db_prefix() . "user_api_quotas`");
}
}

View File

@@ -0,0 +1,115 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_Version_210 extends App_module_migration
{
/** @var CI_DB_query_builder */
protected $db;
public function __construct()
{
parent::__construct();
// Properly initialize the database once the migration is constructed
$CI = &get_instance();
$CI->load->database();
$this->db = $CI->db;
}
public function up()
{
// Add quota fields to user_api table
if (!$this->db->field_exists('request_limit', db_prefix() . 'user_api')) {
$this->db->query("
ALTER TABLE `" . db_prefix() . "user_api`
ADD `request_limit` int(11) NOT NULL DEFAULT 1000 AFTER `permission_enable`,
ADD `time_window` int(11) NOT NULL DEFAULT 3600 AFTER `request_limit`,
ADD `burst_limit` int(11) NOT NULL DEFAULT 0 AFTER `time_window`,
ADD `quota_active` tinyint(1) NOT NULL DEFAULT 1 AFTER `burst_limit`,
ADD `quota_created_at` datetime NULL AFTER `quota_active`,
ADD `quota_updated_at` datetime NULL AFTER `quota_created_at`
");
}
// Migrate existing quota data to user_api table
if ($this->db->table_exists(db_prefix() . 'user_api_quotas')) {
$this->db->query("
UPDATE `" . db_prefix() . "user_api` ua
INNER JOIN `" . db_prefix() . "user_api_quotas` uaq ON ua.token = uaq.api_key
SET
ua.request_limit = uaq.request_limit,
ua.time_window = uaq.time_window,
ua.burst_limit = uaq.burst_limit,
ua.quota_active = uaq.active,
ua.quota_created_at = uaq.created_at,
ua.quota_updated_at = uaq.updated_at
");
}
// Set default values for users without quota settings
$this->db->query("
UPDATE `" . db_prefix() . "user_api`
SET
request_limit = 1000,
time_window = 3600,
burst_limit = 100,
quota_active = 1,
quota_created_at = NOW(),
quota_updated_at = NOW()
WHERE request_limit IS NULL OR request_limit = 0
");
// Drop the separate user_api_quotas table (no longer needed)
$this->db->query("DROP TABLE IF EXISTS `" . db_prefix() . "user_api_quotas`");
}
public function down()
{
// Recreate user_api_quotas table
$this->db->query("
CREATE TABLE `" . db_prefix() . "user_api_quotas` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_api_id` int(11) NOT NULL,
`api_key` varchar(255) NOT NULL,
`request_limit` int(11) NOT NULL DEFAULT 1000,
`time_window` int(11) NOT NULL DEFAULT 3600,
`burst_limit` int(11) NOT NULL DEFAULT 0,
`active` tinyint(1) NOT NULL DEFAULT 1,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `user_api_id` (`user_api_id`),
KEY `api_key` (`api_key`),
KEY `active` (`active`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
");
// Migrate data back to separate table
$this->db->query("
INSERT INTO `" . db_prefix() . "user_api_quotas`
(`user_api_id`, `api_key`, `request_limit`, `time_window`, `burst_limit`, `active`, `created_at`, `updated_at`)
SELECT
id as user_api_id,
token as api_key,
request_limit,
time_window,
burst_limit,
quota_active as active,
quota_created_at as created_at,
quota_updated_at as updated_at
FROM `" . db_prefix() . "user_api`
WHERE request_limit IS NOT NULL
");
// Remove quota fields from user_api table
$this->db->query("
ALTER TABLE `" . db_prefix() . "user_api`
DROP COLUMN `request_limit`,
DROP COLUMN `time_window`,
DROP COLUMN `burst_limit`,
DROP COLUMN `quota_active`,
DROP COLUMN `quota_created_at`,
DROP COLUMN `quota_updated_at`
");
}
}

View File

@@ -0,0 +1,210 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Api_metrics_model extends App_Model
{
public function __construct()
{
parent::__construct();
}
/**
* Get API usage statistics
*/
public function get_usage_stats($api_key = null, $start_date = null, $end_date = null)
{
$this->db->select('
COUNT(*) as total_requests,
AVG(response_time) as avg_response_time,
MAX(response_time) as max_response_time,
MIN(response_time) as min_response_time,
COUNT(CASE WHEN response_code >= 200 AND response_code < 300 THEN 1 END) as success_requests,
COUNT(CASE WHEN response_code >= 400 THEN 1 END) as error_requests,
COUNT(CASE WHEN response_code >= 500 THEN 1 END) as server_errors
');
$this->db->from(db_prefix() . 'api_usage_logs');
if ($api_key) {
$this->db->where('api_key', $api_key);
}
if ($start_date) {
$this->db->where('timestamp >=', strtotime(date('Y-m-d 00:00:00', strtotime($start_date))));
}
if ($end_date) {
$this->db->where('timestamp <=', strtotime(date('Y-m-d 23:59:59', strtotime($end_date))));
}
return $this->db->get()->row();
}
/**
* Get endpoint usage statistics
*/
public function get_endpoint_stats($api_key = null, $start_date = null, $end_date = null, $limit = 10)
{
$this->db->select('
endpoint,
COUNT(*) as request_count,
AVG(response_time) as avg_response_time,
COUNT(CASE WHEN response_code >= 200 AND response_code < 300 THEN 1 END) as success_count,
COUNT(CASE WHEN response_code >= 400 THEN 1 END) as error_count
');
$this->db->from(db_prefix() . 'api_usage_logs');
if ($api_key) {
$this->db->where('api_key', $api_key);
}
if ($start_date) {
$this->db->where('timestamp >=', strtotime(date('Y-m-d 00:00:00', strtotime($start_date))));
}
if ($end_date) {
$this->db->where('timestamp <=', strtotime(date('Y-m-d 23:59:59', strtotime($end_date))));
}
$this->db->group_by('endpoint');
$this->db->order_by('request_count', 'DESC');
$this->db->limit($limit);
return $this->db->get()->result();
}
/**
* Get hourly usage data for charts
*/
public function get_hourly_usage($api_key = null, $start_date = null, $end_date = null)
{
$this->db->select('
FROM_UNIXTIME(timestamp, "%Y-%m-%d %H:00:00") as hour,
COUNT(*) as request_count,
AVG(response_time) as avg_response_time
');
$this->db->from(db_prefix() . 'api_usage_logs');
if ($api_key) {
$this->db->where('api_key', $api_key);
}
if ($start_date) {
$this->db->where('timestamp >=', strtotime(date('Y-m-d 00:00:00', strtotime($start_date))));
}
if ($end_date) {
$this->db->where('timestamp <=', strtotime(date('Y-m-d 23:59:59', strtotime($end_date))));
}
$this->db->group_by('hour');
$this->db->order_by('hour', 'ASC');
return $this->db->get()->result();
}
/**
* Get daily usage data for charts
*/
public function get_daily_usage($api_key = null, $start_date = null, $end_date = null)
{
$this->db->select('
FROM_UNIXTIME(timestamp, "%Y-%m-%d") as day,
COUNT(*) as request_count,
AVG(response_time) as avg_response_time
');
$this->db->from(db_prefix() . 'api_usage_logs');
if ($api_key) {
$this->db->where('api_key', $api_key);
}
if ($start_date) {
$this->db->where('timestamp >=', strtotime(date('Y-m-d 00:00:00', strtotime($start_date))));
}
if ($end_date) {
$this->db->where('timestamp <=', strtotime(date('Y-m-d 23:59:59', strtotime($end_date))));
}
$this->db->group_by('day');
$this->db->order_by('day', 'ASC');
return $this->db->get()->result();
}
/**
* Get response code distribution
*/
public function get_response_code_distribution($api_key = null, $start_date = null, $end_date = null)
{
$this->db->select('
response_code,
COUNT(*) as count
');
$this->db->from(db_prefix() . 'api_usage_logs');
if ($api_key) {
$this->db->where('api_key', $api_key);
}
if ($start_date) {
$this->db->where('timestamp >=', strtotime(date('Y-m-d 00:00:00', strtotime($start_date))));
}
if ($end_date) {
$this->db->where('timestamp <=', strtotime(date('Y-m-d 23:59:59', strtotime($end_date))));
}
$this->db->group_by('response_code');
$this->db->order_by('count', 'DESC');
return $this->db->get()->result();
}
/**
* Get API key usage summary
*/
public function get_api_key_summary($start_date = null, $end_date = null)
{
$this->db->select('
api_key,
COUNT(*) as total_requests,
AVG(response_time) as avg_response_time,
COUNT(CASE WHEN response_code >= 200 AND response_code < 300 THEN 1 END) as success_requests,
COUNT(CASE WHEN response_code >= 400 THEN 1 END) as error_requests
');
$this->db->from(db_prefix() . 'api_usage_logs');
if ($start_date) {
$this->db->where('timestamp >=', strtotime(date('Y-m-d 00:00:00', strtotime($start_date))));
}
if ($end_date) {
$this->db->where('timestamp <=', strtotime(date('Y-m-d 23:59:59', strtotime($end_date))));
}
$this->db->group_by('api_key');
$this->db->order_by('total_requests', 'DESC');
return $this->db->get()->result();
}
/**
* Clean old logs (older than specified days)
*/
public function clean_old_logs($days = 30)
{
$cutoff_timestamp = time() - ($days * 24 * 60 * 60);
$this->db->where('timestamp <', $cutoff_timestamp);
$this->db->delete(db_prefix() . 'api_usage_logs');
return $this->db->affected_rows();
}
}

2085
api/models/Api_model.php Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,521 @@
<?php
use BaconQrCode\Renderer\Image\ImagickImageBackEnd;
use BaconQrCode\Renderer\ImageRenderer;
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
use BaconQrCode\Writer;
defined('BASEPATH') or exit('No direct script access allowed');
class Authentication_model extends App_Model {
public function __construct() {
parent::__construct();
$this->load->config('api');
$this->load->model('user_autologin');
$this->autologin();
}
/**
* @param string Email address for login
* @param string User Password
* @param boolean Set cookies for user if remember me is checked
* @param boolean Is Staff Or Client
* @return boolean if not redirect url found, if found redirect to the url
*/
public function login($email, $password, $remember, $staff, $playground = false) {
if ((!empty($email)) and (!empty($password))) {
$table = db_prefix() . ($playground ? 'playground_' : '') . 'contacts';
$_id = 'id';
if ($staff == true) {
$table = db_prefix() . ($playground ? 'playground_' : '') . 'staff';
$_id = 'staffid';
}
$this->db->where('email', $email);
$user = $this->db->get($table)->row();
if ($user) {
// Email is okey lets check the password now
if (!$user->password || !app_hasher()->CheckPassword($password, $user->password)) {
hooks()->do_action('failed_login_attempt', ['user' => $user, 'is_staff_member' => $staff, ]);
log_activity('Failed Login Attempt [Email: ' . $email . ', Is Staff Member: ' . ($staff == true ? 'Yes' : 'No') . ', IP: ' . $this->input->ip_address() . ']');
// Password failed, return
return false;
}
} else {
hooks()->do_action('non_existent_user_login_attempt', ['email' => $email, 'is_staff_member' => $staff, ]);
log_activity('Non Existing User Tried to Login [Email: ' . $email . ', Is Staff Member: ' . ($staff == true ? 'Yes' : 'No') . ', IP: ' . $this->input->ip_address() . ']');
return false;
}
if ($user->active == 0) {
hooks()->do_action('inactive_user_login_attempt', ['user' => $user, 'is_staff_member' => $staff, ]);
log_activity('Inactive User Tried to Login [Email: ' . $email . ', Is Staff Member: ' . ($staff == true ? 'Yes' : 'No') . ', IP: ' . $this->input->ip_address() . ']');
return ['memberinactive' => true, ];
}
$twoFactorAuth = false;
if ($staff == true) {
$twoFactorAuth = $user->two_factor_auth_enabled == 0 ? false : true;
if (!$twoFactorAuth) {
hooks()->do_action('before_staff_login', ['email' => $email, 'userid' => $user->$_id, ]);
$user_data = ['staff_user_id' => $user->$_id, 'staff_logged_in' => true, ];
} else {
$user_data = [];
$user_data['tfa_staffid'] = $user->staffid;
if ($remember) {
$user_data['tfa_remember'] = true;
}
}
} else {
hooks()->do_action('before_client_login', ['email' => $email, 'userid' => $user->userid, 'contact_user_id' => $user->$_id, ]);
$user_data = ['client_user_id' => $user->userid, 'contact_user_id' => $user->$_id, 'client_logged_in' => true, ];
}
$this->session->set_userdata($user_data);
if (!$twoFactorAuth) {
if ($remember) {
$this->create_autologin($user->$_id, $staff);
}
$this->update_login_info($user->$_id, $staff);
} else {
return ['two_factor_auth' => true, 'user' => $user];
}
return true;
}
return false;
}
/**
* @param boolean If Client or Staff
* @return none
*/
public function logout($staff = true, $playground = false) {
$this->delete_autologin($staff, $playground);
if (is_client_logged_in()) {
hooks()->do_action('before_contact_logout', get_client_user_id());
$this->session->unset_userdata('client_user_id');
$this->session->unset_userdata('client_logged_in');
} else {
hooks()->do_action('before_staff_logout', get_staff_user_id());
$this->session->unset_userdata('staff_user_id');
$this->session->unset_userdata('staff_logged_in');
}
$this->session->sess_destroy();
}
/**
* @param integer ID to create autologin
* @param boolean Is Client or Staff
* @return boolean
*/
private function create_autologin($user_id, $staff, $playground = false) {
$this->load->helper('cookie');
$key = substr(md5(uniqid(rand() . get_cookie($this->config->item('sess_cookie_name')))), 0, 16);
$this->user_autologin->delete($user_id, $key, $staff, $playground);
if ($this->user_autologin->set($user_id, md5($key), $staff, $playground)) {
set_cookie([
'name' => 'autologin',
'value' => serialize(['user_id' => $user_id, 'key' => $key, ]),
'expire' => 60 * 60 * 24 * 31 * 2, // 2 months
]);
return true;
}
return false;
}
/**
* @param boolean Is Client or Staff
* @return none
*/
private function delete_autologin($staff, $playground = false) {
$this->load->helper('cookie');
if ($cookie = get_cookie('autologin', true)) {
$data = unserialize($cookie);
$this->user_autologin->delete($data['user_id'], md5($data['key']), $staff, $playground);
delete_cookie('autologin', 'aal');
}
}
/**
* @return boolean
* Check if autologin found
*/
public function autologin($playground = false) {
if (!is_logged_in()) {
$this->load->helper('cookie');
if ($cookie = get_cookie('autologin', true)) {
$data = unserialize($cookie);
if (isset($data['key']) and isset($data['user_id'])) {
if (!is_null($user = $this->user_autologin->get($data['user_id'], md5($data['key']), $playground))) {
// Login user
if ($user->staff == 1) {
$user_data = ['staff_user_id' => $user->id, 'staff_logged_in' => true, ];
} else {
// Get the customer id
$this->db->select('userid');
$this->db->where('id', $user->id);
$contact = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'contacts')->row();
$user_data = ['client_user_id' => $contact->userid, 'contact_user_id' => $user->id, 'client_logged_in' => true, ];
}
$this->session->set_userdata($user_data);
// Renew users cookie to prevent it from expiring
set_cookie(['name' => 'autologin', 'value' => $cookie, 'expire' => 60 * 60 * 24 * 31 * 2, // 2 months
]);
$this->update_login_info($user->id, $user->staff, $playground);
return true;
}
}
}
}
return false;
}
/**
* @param integer ID
* @param boolean Is Client or Staff
* @return none
* Update login info on autologin
*/
private function update_login_info($user_id, $staff, $playground = false) {
$table = db_prefix() . ($playground ? 'playground_' : '') . 'contacts';
$_id = 'id';
if ($staff == true) {
$table = db_prefix() . ($playground ? 'playground_' : '') . 'staff';
$_id = 'staffid';
}
$this->db->set('last_ip', $this->input->ip_address());
$this->db->set('last_login', date('Y-m-d H:i:s'));
$this->db->where($_id, $user_id);
$this->db->update($table);
log_activity('User Successfully Logged In [User Id: ' . $user_id . ', Is Staff Member: ' . ($staff == true ? 'Yes' : 'No') . ', IP: ' . $this->input->ip_address() . ']');
}
/**
* Send set password email for contacts
* @param string $email
*/
public function set_password_email($email, $playground = false) {
$this->db->where('email', $email);
$user = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'contacts')->row();
if ($user) {
if ($user->active == 0) {
return ['memberinactive' => true, ];
}
$new_pass_key = app_generate_hash();
$this->db->where('id', $user->id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'contacts', ['new_pass_key' => $new_pass_key, 'new_pass_key_requested' => date('Y-m-d H:i:s'), ]);
if ($this->db->affected_rows() > 0) {
$data['new_pass_key'] = $new_pass_key;
$data['userid'] = $user->id;
$data['email'] = $email;
$sent = send_mail_template('customer_contact_set_password', $user, $data);
if ($sent) {
hooks()->do_action('set_password_email_sent', ['is_staff_member' => false, 'user' => $user]);
return true;
}
return false;
}
return false;
}
return false;
}
/**
* @param string Email from the user
* @param Is Client or Staff
* @return boolean
* Generate new password key for the user to reset the password.
*/
public function forgot_password($email, $staff = false, $playground = false) {
$table = db_prefix() . ($playground ? 'playground_' : '') . 'contacts';
$_id = 'id';
if ($staff == true) {
$table = db_prefix() . ($playground ? 'playground_' : '') . 'staff';
$_id = 'staffid';
}
$this->db->where('email', $email);
$user = $this->db->get($table)->row();
if ($user) {
if ($user->active == 0) {
log_activity('Inactive User Tried Password Reset [Email: ' . $email . ', Is Staff Member: ' . ($staff == true ? 'Yes' : 'No') . ', IP: ' . $this->input->ip_address() . ']');
return ['memberinactive' => true, ];
}
$new_pass_key = app_generate_hash();
$this->db->where($_id, $user->$_id);
$this->db->update($table, ['new_pass_key' => $new_pass_key, 'new_pass_key_requested' => date('Y-m-d H:i:s'), ]);
if ($this->db->affected_rows() > 0) {
$data['new_pass_key'] = $new_pass_key;
$data['staff'] = $staff;
$data['userid'] = $user->$_id;
$merge_fields = [];
if ($staff == false) {
$sent = send_mail_template('customer_contact_forgot_password', $user->email, $user->userid, $user->$_id, $data);
} else {
$sent = send_mail_template('staff_forgot_password', $user->email, $user->$_id, $data);
}
if ($sent) {
log_activity('Password Reset Email sent [Email: ' . $email . ', Is Staff Member: ' . ($staff == true ? 'Yes' : 'No') . ', IP: ' . $this->input->ip_address() . ']');
hooks()->do_action('forgot_password_email_sent', ['is_staff_member' => $staff, 'user' => $user]);
return true;
}
return false;
}
return false;
}
log_activity('Non Existing User Tried Password Reset [Email: ' . $email . ', Is Staff Member: ' . ($staff == true ? 'Yes' : 'No') . ', IP: ' . $this->input->ip_address() . ']');
return false;
}
/**
* Update user password from forgot password feature or set password
* @param boolean $staff is staff or contact
* @param mixed $userid
* @param string $new_pass_key the password generate key
* @param string $password new password
*/
public function set_password($staff, $userid, $new_pass_key, $password, $playground = false) {
if (!$this->can_set_password($staff, $userid, $new_pass_key, $playground)) {
return ['expired' => true, ];
}
$password = app_hash_password($password);
$table = db_prefix() . ($playground ? 'playground_' : '') . 'contacts';
$_id = 'id';
if ($staff == true) {
$table = db_prefix() . ($playground ? 'playground_' : '') . 'staff';
$_id = 'staffid';
}
$this->db->where($_id, $userid);
$this->db->where('new_pass_key', $new_pass_key);
$this->db->update($table, ['password' => $password, ]);
if ($this->db->affected_rows() > 0) {
log_activity('User Set Password [User ID: ' . $userid . ', Is Staff Member: ' . ($staff == true ? 'Yes' : 'No') . ', IP: ' . $this->input->ip_address() . ']');
$this->db->set('new_pass_key', null);
$this->db->set('new_pass_key_requested', null);
$this->db->set('last_password_change', date('Y-m-d H:i:s'));
$this->db->where($_id, $userid);
$this->db->where('new_pass_key', $new_pass_key);
$this->db->update($table);
return true;
}
return null;
}
/**
* @param boolean Is Client or Staff
* @param integer ID
* @param string
* @param string
* @return boolean
* User reset password after successful validation of the key
*/
public function reset_password($staff, $userid, $new_pass_key, $password, $playground = false) {
if (!$this->can_reset_password($staff, $userid, $new_pass_key, $playground)) {
return ['expired' => true, ];
}
$password = app_hash_password($password);
$table = db_prefix() . ($playground ? 'playground_' : '') . 'contacts';
$_id = 'id';
if ($staff == true) {
$table = db_prefix() . ($playground ? 'playground_' : '') . 'staff';
$_id = 'staffid';
}
$this->db->where($_id, $userid);
$this->db->where('new_pass_key', $new_pass_key);
$this->db->update($table, ['password' => $password, ]);
if ($this->db->affected_rows() > 0) {
log_activity('User Reseted Password [User ID: ' . $userid . ', Is Staff Member: ' . ($staff == true ? 'Yes' : 'No') . ', IP: ' . $this->input->ip_address() . ']');
$this->db->set('new_pass_key', null);
$this->db->set('new_pass_key_requested', null);
$this->db->set('last_password_change', date('Y-m-d H:i:s'));
$this->db->where($_id, $userid);
$this->db->where('new_pass_key', $new_pass_key);
$this->db->update($table);
$this->db->where($_id, $userid);
$user = $this->db->get($table)->row();
if ($staff == false) {
$sent = send_mail_template('customer_contact_password_resetted', $user->email, $user->userid, $user->$_id);
} else {
$sent = send_mail_template('staff_password_resetted', $user->email, $user->$_id);
}
if ($sent) {
return true;
}
}
return null;
}
/**
* @param integer Is Client or Staff
* @param integer ID
* @param string Password reset key
* @return boolean
* Check if the key is not expired or not exists in database
*/
public function can_reset_password($staff, $userid, $new_pass_key, $playground = false) {
$table = db_prefix() . ($playground ? 'playground_' : '') . 'contacts';
$_id = 'id';
if ($staff == true) {
$table = db_prefix() . ($playground ? 'playground_' : '') . 'staff';
$_id = 'staffid';
}
$this->db->where($_id, $userid);
$this->db->where('new_pass_key', $new_pass_key);
$user = $this->db->get($table)->row();
if ($user) {
$timestamp_now_minus_1_hour = time() - (60 * 60);
$new_pass_key_requested = strtotime($user->new_pass_key_requested);
if ($timestamp_now_minus_1_hour > $new_pass_key_requested) {
return false;
}
return true;
}
return false;
}
/**
* @param integer Is Client or Staff
* @param integer ID
* @param string Password reset key
* @return boolean
* Check if the key is not expired or not exists in database
*/
public function can_set_password($staff, $userid, $new_pass_key, $playground = false) {
$table = db_prefix() . ($playground ? 'playground_' : '') . 'contacts';
$_id = 'id';
if ($staff == true) {
$table = db_prefix() . ($playground ? 'playground_' : '') . 'staff';
$_id = 'staffid';
}
$this->db->where($_id, $userid);
$this->db->where('new_pass_key', $new_pass_key);
$user = $this->db->get($table)->row();
if ($user) {
$timestamp_now_minus_48_hour = time() - (3600 * 48);
$new_pass_key_requested = strtotime($user->new_pass_key_requested);
if ($timestamp_now_minus_48_hour > $new_pass_key_requested) {
return false;
}
return true;
}
return false;
}
/**
* Get user from database by 2 factor authentication code
* @param string $code authentication code to search for
* @return object
*/
public function get_user_by_two_factor_auth_code($code, $playground = false) {
$this->db->where('two_factor_auth_code', $code);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'staff')->row();
}
/**
* Login user via two factor authentication
* @param object $user user object
* @return boolean
*/
public function two_factor_auth_login($user, $playground = false) {
hooks()->do_action('before_staff_login', ['email' => $user->email, 'userid' => $user->staffid, ]);
$this->session->set_userdata(['staff_user_id' => $user->staffid, 'staff_logged_in' => true, ]);
$remember = null;
if ($this->session->has_userdata('tfa_remember')) {
$remember = true;
$this->session->unset_userdata('tfa_remember');
}
if ($remember) {
$this->create_autologin($user->staffid, true);
}
$this->update_login_info($user->staffid, true, $playground);
return true;
}
/**
* Check if 2 factor authentication code sent to email is valid for usage
* @param string $code auth code
* @param string $email email of staff login in
* @return boolean
*/
public function is_two_factor_code_valid($code, $email, $playground = false) {
$this->db->select('two_factor_auth_code_requested');
$this->db->where('two_factor_auth_code', $code);
$this->db->where('email', $email);
$user = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'staff')->row();
// Code not exists because no user is found
if (!$user) {
return false;
}
$timestamp_minus_1_hour = time() - (60 * 60);
$new_code_key_requested = strtotime($user->two_factor_auth_code_requested);
// The code is older then 1 hour and its not valid
if ($timestamp_minus_1_hour > $new_code_key_requested) {
return false;
}
// Code is valid
return true;
}
/**
* Clears 2 factor authentication code in database
* @param mixed $id
* @return boolean
*/
public function clear_two_factor_auth_code($id, $playground = false) {
$this->db->where('staffid', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'staff', ['two_factor_auth_code' => null, ]);
return true;
}
/**
* Set 2 factor authentication code for staff member
* @param mixed $id staff id
*/
public function set_two_factor_auth_code($id, $playground = false) {
$code = generate_two_factor_auth_key();
$code.= $id;
$this->db->where('staffid', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'staff', ['two_factor_auth_code' => $code, 'two_factor_auth_code_requested' => date('Y-m-d H:i:s'), ]);
return $code;
}
public function get_qr($System_name, $playground = false) {
$staff = get_staff(get_staff_user_id());
$google2fa = new PragmaRX\Google2FA\Google2FA();
$secret = $google2fa->generateSecretKey();
$g2faUrl = $google2fa->getQRCodeUrl($System_name, $staff->email, $secret);
$writer = new Writer(new ImageRenderer(new RendererStyle(200), new ImagickImageBackEnd()));
return ['qrURL' => base64_encode($writer->writeString($g2faUrl)), 'secret' => $secret];
}
public function set_google_two_factor($secret, $playground = false) {
$id = get_staff_user_id();
$secret = $this->encrypt($secret);
$this->db->where('staffid', $id);
$success = $this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'staff', ['two_factor_auth_enabled' => 2, 'google_auth_secret' => $secret, ]);
if ($success) {
return true;
}
return false;
}
public function is_google_two_factor_code_valid($code, $secret = null, $playground = false) {
$g = new PragmaRX\Google2FA\Google2FA();
if (!is_null($secret)) {
return $g->verifyKey($secret, $code, 0);
}
$staffid = $this->session->userdata('tfa_staffid');
$this->db->select('google_auth_secret')->where('staffid', $staffid);
if ($staff = $this->db->get(($playground ? 'playground_' : '') . 'staff')->row()) {
return $g->verifyKey($this->decrypt($staff->google_auth_secret), $code, 0);
}
return false;
}
public function encrypt($string) {
$this->load->library('encryption');
return $this->encryption->encrypt($string);
}
public function decrypt($string) {
$this->load->library('encryption');
return $this->encryption->decrypt($string);
}
}

View File

@@ -0,0 +1,72 @@
<?php
use app\services\utilities\Arr;
defined('BASEPATH') or exit('No direct script access allowed');
class Calendar_model extends App_Model
{
public function __construct()
{
parent::__construct();
}
public function get_events($id = '', $playground = false)
{
$this->db->select('*');
$this->db->from(db_prefix() . ($playground ? 'playground_' : '') . 'events');
if ($id >0) {
$this->db->where('eventid', $id);
}
return $this->db->get()->result_array();
}
/**
* Add new event
* @param array $data event $_POST data
*/
public function event($data, $playground = false)
{
$data['start'] = to_sql_date($data['start'], true);
if ($data['end'] == '') {
unset($data['end']);
} else {
$data['end'] = to_sql_date($data['end'], true);
}
$data['description'] = nl2br($data['description']);
if (isset($data['eventid'])) {
$this->db->where('eventid', $data['eventid']);
$event = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'events')->row();
if (!$event) {
return false;
}
if ($event->isstartnotified == 1) {
if ($data['start'] > $event->start) {
$data['isstartnotified'] = 0;
}
}
$data = hooks()->apply_filters('event_update_data', $data, $data['eventid']);
$this->db->where('eventid', $data['eventid']);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'events', $data);
if ($this->db->affected_rows() > 0) {
return true;
}
return false;
}
$data = hooks()->apply_filters('event_create_data', $data);
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'events', $data);
$insert_id = $this->db->insert_id();
if ($insert_id) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,146 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Client_groups_model extends App_Model {
public function __construct() {
parent::__construct();
}
/**
* Add new customer group
* @param array $data $_POST data
*/
public function add($data, $playground = false) {
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'customers_groups', $data);
$insert_id = $this->db->insert_id();
if ($insert_id) {
log_activity('New Customer Group Created [ID:' . $insert_id . ', Name:' . $data['name'] . ']');
return $insert_id;
}
return false;
}
/**
* Get customer groups where customer belongs
* @param mixed $id customer id
* @return array
*/
public function get_customer_groups($id, $playground = false) {
$this->db->where('customer_id', $id);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'customer_groups')->result_array();
}
/**
* Get all customer groups
* @param string $id
* @return mixed
*/
public function get_groups($id = '', $playground = false) {
if (is_numeric($id)) {
$this->db->where('id', $id);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'customers_groups')->row();
}
$this->db->order_by('name', 'asc');
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'customers_groups')->result_array();
}
/**
* Edit customer group
* @param array $data $_POST data
* @return boolean
*/
public function edit($data, $playground = false) {
$this->db->where('id', $data['id']);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'customers_groups', ['name' => $data['name'], ]);
if ($this->db->affected_rows() > 0) {
log_activity('Customer Group Updated [ID:' . $data['id'] . ']');
return true;
}
return false;
}
/**
* Delete customer group
* @param mixed $id group id
* @return boolean
*/
public function delete($id, $playground = false) {
$this->db->where('id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'customers_groups');
if ($this->db->affected_rows() > 0) {
$this->db->where('groupid', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'customer_groups');
hooks()->do_action('customer_group_deleted', $id);
log_activity('Customer Group Deleted [ID:' . $id . ']');
return true;
}
return false;
}
/**
* Update/sync customer groups where belongs
* @param mixed $id customer id
* @param mixed $groups_in
* @return boolean
*/
public function sync_customer_groups($id, $groups_in, $playground = false) {
if ($groups_in == false) {
unset($groups_in);
}
$affectedRows = 0;
$customer_groups = $this->get_customer_groups($id);
if (sizeof($customer_groups) > 0) {
foreach ($customer_groups as $customer_group) {
if (isset($groups_in)) {
if (!in_array($customer_group['groupid'], $groups_in)) {
$this->db->where('customer_id', $id);
$this->db->where('id', $customer_group['id']);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'customer_groups');
if ($this->db->affected_rows() > 0) {
$affectedRows++;
}
}
} else {
$this->db->where('customer_id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'customer_groups');
if ($this->db->affected_rows() > 0) {
$affectedRows++;
}
}
}
if (isset($groups_in)) {
foreach ($groups_in as $group) {
$this->db->where('customer_id', $id);
$this->db->where('groupid', $group);
$_exists = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'customer_groups')->row();
if (!$_exists) {
if (empty($group)) {
continue;
}
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'customer_groups', ['customer_id' => $id, 'groupid' => $group, ]);
if ($this->db->affected_rows() > 0) {
$affectedRows++;
}
}
}
}
} else {
if (isset($groups_in)) {
foreach ($groups_in as $group) {
if (empty($group)) {
continue;
}
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'customer_groups', ['customer_id' => $id, 'groupid' => $group, ]);
if ($this->db->affected_rows() > 0) {
$affectedRows++;
}
}
}
}
if ($affectedRows > 0) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,86 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Client_vault_entries_model extends App_Model {
public function __construct() {
parent::__construct();
}
/**
* Get single vault entry
* @param mixed $id vault entry id
* @return object
*/
public function get($id, $playground = false) {
$this->db->where('id', $id);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'vault')->row();
}
/**
* Get customer vault entries
* @param mixed $customer_id
* @param array $where additional wher
* @return array
*/
public function get_by_customer_id($customer_id, $where = [], $playground = false) {
$this->db->where('customer_id', $customer_id);
$this->db->order_by('date_created', 'desc');
$this->db->where($where);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'vault')->result_array();
}
/**
* Create new vault entry
* @param array $data $_POST data
* @param mixed $customer_id customer id
* @return boolean
*/
public function create($data, $customer_id, $playground = false) {
$data['date_created'] = date('Y-m-d H:i:s');
$data['customer_id'] = $customer_id;
$data['share_in_projects'] = isset($data['share_in_projects']) ? 1 : 0;
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'vault', $data);
log_activity('Vault Entry Created [Customer ID: ' . $customer_id . ']');
}
/**
* Update vault entry
* @param mixed $id vault entry id
* @param array $data $_POST data
* @return boolean
*/
public function update($id, $data, $playground = false) {
$vault = $this->get($id, $playground);
$last_updated_from = $data['last_updated_from'];
unset($data['last_updated_from']);
$data['share_in_projects'] = isset($data['share_in_projects']) ? 1 : 0;
$this->db->where('id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'vault', $data);
if ($this->db->affected_rows() > 0) {
$this->db->where('id', $id);
$this->db->update(db_prefix() . 'vault', ['last_updated' => date('Y-m-d H:i:s'), 'last_updated_from' => $last_updated_from]);
log_activity('Vault Entry Updated [Customer ID: ' . $vault->customer_id . ']');
return true;
}
return false;
}
/**
* Delete vault entry
* @param mixed $id entry id
* @return boolean
*/
public function delete($id, $playground = false) {
$vault = $this->get($id, $playground);
$this->db->where('id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'vault');
if ($this->db->affected_rows() > 0) {
log_activity('Vault Entry Deleted [Customer ID: ' . $vault->customer_id . ']');
hooks()->do_action('customer_vault_entry_deleted', ['vault_id' => $id, 'customer_id' => $vault->customer_id]);
return true;
}
return false;
}
}

1360
api/models/Clients_model.php Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,128 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Contract_types_model extends App_Model {
public function __construct() {
parent::__construct();
}
/**
* Add new contract type
* @param mixed $data All $_POST data
*/
public function add($data, $playground = false) {
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'contracts_types', $data);
$insert_id = $this->db->insert_id();
if ($insert_id) {
log_activity('New Contract Type Added [' . $data['name'] . ']');
return $insert_id;
}
return false;
}
/**
* Edit contract type
* @param mixed $data All $_POST data
* @param mixed $id Contract type id
*/
public function update($data, $id, $playground = false) {
$this->db->where('id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'contracts_types', $data);
if ($this->db->affected_rows() > 0) {
log_activity('Contract Type Updated [' . $data['name'] . ', ID:' . $id . ']');
return true;
}
return false;
}
/**
* @param integer ID (optional)
* @return mixed
* Get contract type object based on passed id if not passed id return array of all types
*/
public function get($id = '', $playground = false) {
if (is_numeric($id)) {
$this->db->where('id', $id);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'contracts_types')->row();
}
$types = $this->app_object_cache->get(($playground ? 'playground_' : '') . 'contract-types');
if (!$types && !is_array($types)) {
$types = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'contracts_types')->result_array();
$this->app_object_cache->add(($playground ? 'playground_' : '') . 'contract-types', $types);
}
return $types;
}
/**
* @param integer ID
* @return mixed
* Delete contract type from database, if used return array with key referenced
*/
public function delete($id, $playground = false) {
if (is_reference_in_table('contract_type', db_prefix() . ($playground ? 'playground_' : '') . 'contracts', $id)) {
return ['referenced' => true, ];
}
$this->db->where('id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'contracts_types');
if ($this->db->affected_rows() > 0) {
log_activity('Contract Deleted [' . $id . ']');
return true;
}
return false;
}
/**
* Get contract types data for chart
* @return array
*/
public function get_chart_data($playground = false) {
$labels = [];
$totals = [];
$types = $this->get();
foreach ($types as $type) {
$total_rows_where = ['contract_type' => $type['id'], 'trash' => 0, ];
if (is_client_logged_in()) {
$total_rows_where['client'] = get_client_user_id();
$total_rows_where['not_visible_to_client'] = 0;
} else {
if (staff_cant('view', 'contracts')) {
$total_rows_where['addedfrom'] = get_staff_user_id();
}
}
$total_rows = total_rows(db_prefix() . ($playground ? 'playground_' : '') . 'contracts', $total_rows_where);
if ($total_rows == 0 && is_client_logged_in()) {
continue;
}
array_push($labels, $type['name']);
array_push($totals, $total_rows);
}
$chart = ['labels' => $labels, 'datasets' => [['label' => _l('contract_summary_by_type'), 'backgroundColor' => 'rgba(3,169,244,0.2)', 'borderColor' => '#03a9f4', 'borderWidth' => 1, 'data' => $totals, ], ], ];
return $chart;
}
/**
* Get contract types values for chart
* @return array
*/
public function get_values_chart_data($playground = false) {
$labels = [];
$totals = [];
$types = $this->get();
foreach ($types as $type) {
array_push($labels, $type['name']);
$where = ['where' => ['contract_type' => $type['id'], 'trash' => 0, ], 'field' => 'contract_value', ];
if (staff_cant('view', 'contracts')) {
$where['where']['addedfrom'] = get_staff_user_id();
}
$total = sum_from_table(db_prefix() . ($playground ? 'playground_' : '') . 'contracts', $where);
if ($total == null) {
$total = 0;
}
array_push($totals, $total);
}
$chart = ['labels' => $labels, 'datasets' => [['label' => _l('contract_summary_by_type_value'), 'backgroundColor' => 'rgba(37,155,35,0.2)', 'borderColor' => '#84c529', 'tension' => false, 'borderWidth' => 1, 'data' => $totals, ], ], ];
return $chart;
}
}

View File

@@ -0,0 +1,697 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Contracts_model extends App_Model {
public function __construct() {
parent::__construct();
$this->load->model('contract_types_model');
$this->load->model('clients_model');
}
/**
* Get contract/s
* @param mixed $id contract id
* @param array $where perform where
* @param boolean $for_editor if for editor is false will replace the field if not will not replace
* @return mixed
*/
public function get($id = '', $where = [], $for_editor = false, $playground = false) {
$this->db->select('*,' . db_prefix() . ($playground ? 'playground_' : '') . 'contracts_types.name as type_name,' . db_prefix() . ($playground ? 'playground_' : '') . 'contracts.id as id, ' . db_prefix() . ($playground ? 'playground_' : '') . 'contracts.addedfrom');
$this->db->where($where);
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'contracts_types', '' . db_prefix() . ($playground ? 'playground_' : '') . 'contracts_types.id = ' . db_prefix() . ($playground ? 'playground_' : '') . 'contracts.contract_type', 'left');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'clients', '' . db_prefix() . ($playground ? 'playground_' : '') . 'clients.userid = ' . db_prefix() . ($playground ? 'playground_' : '') . 'contracts.client');
if (is_numeric($id)) {
$this->db->where(db_prefix() . ($playground ? 'playground_' : '') . 'contracts.id', $id);
$contract = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'contracts')->row();
if ($contract) {
$merge_fields = $this->get_merge_fields($contract, $playground);
$contract->attachments = $this->get_contract_attachments('', $contract->id, $playground);
if ($contract->content !== null && $for_editor == false) {
foreach ($merge_fields as $key => $val) {
if (stripos($contract->content, $key) !== false) {
$contract->content = str_ireplace($key, $val, $contract->content);
} else {
$contract->content = str_ireplace($key, '', $contract->content);
}
}
}
}
return $contract;
}
$contracts = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'contracts')->result_array();
$i = 0;
foreach ($contracts as $contract) {
$contracts[$i]['attachments'] = $this->get_contract_attachments('', $contract['id'], $playground);
$i++;
}
return $contracts;
}
/**
* Select unique contracts years
* @return array
*/
public function get_contracts_years($playground = false) {
return $this->db->query('SELECT DISTINCT(YEAR(datestart)) as year FROM ' . db_prefix() . ($playground ? 'playground_' : '') . 'contracts')->result_array();
}
/**
* @param integer ID
* @return object
* Retrieve contract attachments from database
*/
public function get_contract_attachments($attachment_id = '', $id = '', $playground = false) {
if (is_numeric($attachment_id)) {
$this->db->where('id', $attachment_id);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'files')->row();
}
$this->db->order_by('dateadded', 'desc');
$this->db->where('rel_id', $id);
$this->db->where('rel_type', 'contract');
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'files')->result_array();
}
/**
* @param array $_POST data
* @return integer Insert ID
* Add new contract
*/
public function add($data, $playground = false) {
$data['dateadded'] = date('Y-m-d H:i:s');
$data['addedfrom'] = get_staff_user_id();
$data['datestart'] = to_sql_date($data['datestart']);
unset($data['attachment']);
if ($data['dateend'] == '') {
unset($data['dateend']);
} else {
$data['dateend'] = to_sql_date($data['dateend']);
}
if (isset($data['trash']) && ($data['trash'] == 1 || $data['trash'] === 'on')) {
$data['trash'] = 1;
} else {
$data['trash'] = 0;
}
if (isset($data['not_visible_to_client']) && ($data['not_visible_to_client'] == 1 || $data['not_visible_to_client'] === 'on')) {
$data['not_visible_to_client'] = 1;
} else {
$data['not_visible_to_client'] = 0;
}
if (isset($data['custom_fields'])) {
$custom_fields = $data['custom_fields'];
unset($data['custom_fields']);
}
$data['hash'] = app_generate_hash();
$data = hooks()->apply_filters('before_contract_added', $data);
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'contracts', $data);
$insert_id = $this->db->insert_id();
if ($insert_id) {
if (isset($custom_fields)) {
$this->load->model('custom_fields_model');
$this->custom_fields_model->handle_custom_fields_post($insert_id, $custom_fields, false, $playground);
}
hooks()->do_action('after_contract_added', $insert_id);
log_activity('New Contract Added [' . $data['subject'] . ']');
return $insert_id;
}
return false;
}
/**
* @param array $_POST data
* @param integer Contract ID
* @return boolean
*/
public function update($data, $id, $playground = false) {
$affectedRows = 0;
$contract = $this->db->where('id', $id)->get(($playground ? 'playground_' : '') . 'contracts')->row();
if (isset($data['datestart'])) {
$data['datestart'] = to_sql_date($data['datestart']);
}
if (isset($data['dateend'])) {
$data['dateend'] = $data['dateend'] == '' ? null : to_sql_date($data['dateend']);
}
if (isset($data['dateend']) && $data['dateend'] !== $contract->dateend) {
$data['isexpirynotified'] = 0;
}
if ($data['dateend'] !== $contract) {
if (isset($data['trash'])) {
$data['trash'] = 1;
} else {
$data['trash'] = 0;
}
}
if (isset($data['not_visible_to_client'])) {
$data['not_visible_to_client'] = 1;
} else {
$data['not_visible_to_client'] = 0;
}
$data = hooks()->apply_filters('before_contract_updated', $data, $id);
if (isset($data['custom_fields'])) {
$custom_fields = $data['custom_fields'];
$this->load->model('custom_fields_model');
if ($this->custom_fields_model->handle_custom_fields_post($id, $custom_fields, false, $playground)) {
$affectedRows++;
}
unset($data['custom_fields']);
}
$this->db->where('id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'contracts', $data);
if ($this->db->affected_rows() > 0) {
hooks()->do_action('after_contract_updated', $id);
log_activity('Contract Updated [' . $data['subject'] . ']');
return true;
}
return $affectedRows > 0;
}
public function add_signature($id, $playground = false): bool {
$contract = $this->get($id, [], true, $playground);
if ($contract) {
$content = override_merge_fields($this->get_merge_fields($contract), $contract->content);
$this->db->where('id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'contracts', array_merge(get_acceptance_info_array(), ['signed' => 1, 'content' => $content]));
// Notify contract creator that customer signed the contract
send_contract_signed_notification_to_staff($id);
return true;
}
return false;
}
public function clear_signature($id, $playground = false): bool {
$this->db->select('signature');
$this->db->where('id', $id);
$contract = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'contracts')->row();
if ($contract) {
$contractData = $this->get($id, [], true, $playground);
$this->db->where('id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'contracts', array_merge(get_acceptance_info_array(true), ['signed' => 0, 'content' => restore_merge_fields($contractData->content) ]));
if (!empty($contract->signature)) {
$this->load->model('misc_model');
unlink($this->misc_model->get_upload_path_by_type('contract', $playground) . $id . '/' . $contract->signature);
}
return true;
}
return false;
}
/**
* Add contract comment
* @param mixed $data $_POST comment data
* @param boolean $client is request coming from the client side
*/
public function add_comment($data, $client = false, $playground = false) {
if (is_staff_logged_in()) {
$client = false;
}
if (isset($data['action'])) {
unset($data['action']);
}
$data['dateadded'] = date('Y-m-d H:i:s');
if ($client == false) {
$data['staffid'] = get_staff_user_id();
}
$data['content'] = nl2br($data['content']);
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'contract_comments', $data);
$insert_id = $this->db->insert_id();
if ($insert_id) {
$contract = $this->get($data['contract_id'], [], false, $playground);
if (($contract->not_visible_to_client == '1' || $contract->trash == '1') && $client == false) {
return true;
}
if ($client == true) {
// Get creator
$this->db->select('staffid, email, phonenumber');
$this->db->where('staffid', $contract->addedfrom);
$staff_contract = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'staff')->result_array();
$notifiedUsers = [];
foreach ($staff_contract as $member) {
$notified = add_notification(['description' => 'not_contract_comment_from_client', 'touserid' => $member['staffid'], 'fromcompany' => 1, 'fromuserid' => 0, 'link' => 'contracts/contract/' . $data['contract_id'], 'additional_data' => serialize([$contract->subject, ]), ]);
if ($notified) {
array_push($notifiedUsers, $member['staffid']);
}
$template = mail_template('contract_comment_to_staff', $contract, $member);
$merge_fields = $template->get_merge_fields();
$template->send();
// Send email/sms to admin that client commented
$this->app_sms->trigger(SMS_TRIGGER_CONTRACT_NEW_COMMENT_TO_STAFF, $member['phonenumber'], $merge_fields);
}
pusher_trigger_notification($notifiedUsers);
} else {
$contacts = $this->clients_model->get_contacts($contract->client, ['active' => 1, 'contract_emails' => 1]);
foreach ($contacts as $contact) {
$template = mail_template('contract_comment_to_customer', $contract, $contact);
$merge_fields = $template->get_merge_fields();
$template->send();
$this->app_sms->trigger(SMS_TRIGGER_CONTRACT_NEW_COMMENT_TO_CUSTOMER, $contact['phonenumber'], $merge_fields);
}
}
return true;
}
return false;
}
public function edit_comment($data, $id, $playground = false) {
$this->db->where('id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'contract_comments', ['content' => nl2br($data['content']), ]);
if ($this->db->affected_rows() > 0) {
return true;
}
return false;
}
/**
* Get contract comments
* @param mixed $id contract id
* @return array
*/
public function get_comments($id, $playground = false) {
$this->db->where('contract_id', $id);
$this->db->order_by('dateadded', 'ASC');
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'contract_comments')->result_array();
}
/**
* Get contract single comment
* @param mixed $id comment id
* @return object
*/
public function get_comment($id, $playground = false) {
$this->db->where('id', $id);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'contract_comments')->row();
}
/**
* Remove contract comment
* @param mixed $id comment id
* @return boolean
*/
public function remove_comment($id, $playground = false) {
$comment = $this->get_comment($id);
$this->db->where('id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'contract_comments');
if ($this->db->affected_rows() > 0) {
log_activity('Contract Comment Removed [Contract ID:' . $comment->contract_id . ', Comment Content: ' . $comment->content . ']');
return true;
}
return false;
}
public function copy($id, $playground = false) {
$contract = $this->get($id, [], true, $playground);
$fields = $this->db->list_fields(db_prefix() . ($playground ? 'playground_' : '') . 'contracts');
$newContactData = [];
$contract->content = restore_merge_fields($contract->content);
foreach ($fields as $field) {
if (isset($contract->$field)) {
$newContactData[$field] = $contract->$field;
}
}
unset($newContactData['id']);
$newContactData['trash'] = 0;
$newContactData['isexpirynotified'] = 0;
$newContactData['signed'] = 0;
$newContactData['marked_as_signed'] = 0;
$newContactData['signature'] = null;
$newContactData = array_merge($newContactData, get_acceptance_info_array(true));
if ($contract->dateend) {
$dStart = new DateTime($contract->datestart);
$dEnd = new DateTime($contract->dateend);
$dDiff = $dStart->diff($dEnd);
$newContactData['dateend'] = _d(date('Y-m-d', strtotime(date('Y-m-d', strtotime('+' . $dDiff->days . 'DAY')))));
} else {
$newContactData['dateend'] = '';
}
$newId = $this->add($newContactData);
if ($newId) {
$this->load->model('custom_fields_model');
$custom_fields = $this->custom_fields_model->get_custom_fields('contracts', [], false, $playgroun);
foreach ($custom_fields as $field) {
$value = $this->custom_fields_model->get_custom_field_value($id, $field['id'], 'contracts', false, $playground);
if ($value != '') {
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'customfieldsvalues', ['relid' => $newId, 'fieldid' => $field['id'], 'fieldto' => 'contracts', 'value' => $value, ]);
}
}
}
return $newId;
}
/**
* @param integer ID
* @return boolean
* Delete contract, also attachment will be removed if any found
*/
public function delete($id, $playground = false) {
hooks()->do_action('before_contract_deleted', $id);
$this->clear_signature($id);
$contract = $this->get($id, [], false, $playground);
$this->db->where('id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'contracts');
if ($this->db->affected_rows() > 0) {
$this->db->where('contract_id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'contract_comments');
// Delete the custom field values
$this->db->where('relid', $id);
$this->db->where('fieldto', 'contracts');
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'customfieldsvalues');
$this->db->where('rel_id', $id);
$this->db->where('rel_type', 'contract');
$attachments = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'files')->result_array();
foreach ($attachments as $attachment) {
$this->delete_contract_attachment($attachment['id'], $playground);
}
$this->db->where('rel_id', $id);
$this->db->where('rel_type', 'contract');
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'notes');
$this->db->where('contractid', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'contract_renewals');
// Get related tasks
$this->db->where('rel_type', 'contract');
$this->db->where('rel_id', $id);
$tasks = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'tasks')->result_array();
$this->load->model('tasks_model');
foreach ($tasks as $task) {
$this->tasks_model->delete_task($task['id'], true, $playground);
}
$this->load->model('misc_model');
$this->misc_model->delete_tracked_emails($id, 'contract', $playgroun);
log_activity('Contract Deleted [' . $id . ']');
hooks()->do_action('after_contract_deleted', $id);
return true;
}
return false;
}
/**
* Mark the contract as signed manually
*
* @param int $id contract id
*
* @return boolean
*/
public function mark_as_signed($id, $playground = false) {
$contract = $this->get($id, [], true, $playground);
if (!is_object($contract)) {
return false;
}
$content = override_merge_fields($this->get_merge_fields($contract), $contract->content);
$this->db->where('id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'contracts', ['marked_as_signed' => 1, 'content' => $content, ]);
return $this->db->affected_rows() > 0;
}
/**
* Unmark the contract as signed manually
*
* @param int $id contract id
*
* @return boolean
*/
public function unmark_as_signed($id, $playground = false) {
$contract = $this->get($id, [], true, $playground);
if (!is_object($contract)) {
return false;
}
$this->db->where('id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'contracts', ['marked_as_signed' => 0, 'content' => restore_merge_fields($contract->content), ]);
return $this->db->affected_rows() > 0;
}
/**
* Function that send contract to customer
* @param mixed $id contract id
* @param boolean $attachpdf to attach pdf or not
* @param string $cc Email CC
* @return boolean
*/
public function send_contract_to_client($id, $attachpdf = true, $cc = '', $playground = false) {
$contract = $this->get($id, [], false, $playground);
if ($attachpdf) {
set_mailing_constant();
$pdf = contract_pdf($contract);
$attach = $pdf->Output(slug_it($contract->subject) . '.pdf', 'S');
}
$sent_to = $this->input->post('sent_to');
$sent = false;
if (is_array($sent_to)) {
$i = 0;
foreach ($sent_to as $contact_id) {
if ($contact_id != '') {
$contact = $this->clients_model->get_contact($contact_id, ['active' => 1], [], $playground);
// Send cc only for the first contact
if (!empty($cc) && $i > 0) {
$cc = '';
}
$template = mail_template('contract_send_to_customer', $contract, $contact, $cc);
if ($attachpdf) {
$template->add_attachment(['attachment' => $attach, 'filename' => slug_it($contract->subject) . '.pdf', 'type' => 'application/pdf', ]);
}
if ($template->send()) {
$sent = true;
}
}
$i++;
}
} else {
return false;
}
if ($sent) {
$contactsSent = [];
if (!empty($contract->contacts_sent_to)) {
$sentTo = json_decode($contract->contacts_sent_to, true);
$cc = array_unique(array_merge(is_array($sentTo['cc']) ? $sentTo['cc'] : explode(',', $sentTo['cc']), explode(',', $cc)));
$contactsSent = $sentTo['contact_ids'];
}
$this->db->where('id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'contracts', ['last_sent_at' => date('c'), 'contacts_sent_to' => json_encode(['contact_ids' => array_unique(array_merge($contactsSent, $sent_to)), 'cc' => is_array($cc) ? join(',', $cc) : $cc, ]), ]);
return true;
}
return false;
}
/**
* Delete contract attachment
* @param mixed $attachment_id
* @return boolean
*/
public function delete_contract_attachment($attachment_id, $playground = false) {
$deleted = false;
$attachment = $this->get_contract_attachments($attachment_id, $playground);
$this->load->model('misc_model');
if ($attachment) {
if (empty($attachment->external)) {
unlink($this->misc_model->get_upload_path_by_type('contract', $playground) . $attachment->rel_id . '/' . $attachment->file_name);
}
$this->db->where('id', $attachment->id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'files');
if ($this->db->affected_rows() > 0) {
$deleted = true;
log_activity('Contract Attachment Deleted [ContractID: ' . $attachment->rel_id . ']');
}
if (is_dir($this->misc_model->get_upload_path_by_type('contract', $playground) . $attachment->rel_id)) {
// Check if no attachments left, so we can delete the folder also
$other_attachments = list_files($this->misc_model->get_upload_path_by_type('contract', $playground) . $attachment->rel_id);
if (count($other_attachments) == 0) {
// okey only index.html so we can delete the folder also
delete_dir($this->misc_model->get_upload_path_by_type('contract', $playground) . $attachment->rel_id);
}
}
}
return $deleted;
}
/**
* Renew contract
* @param mixed $data All $_POST data
* @return mixed
*/
public function renew($data, $playground = false) {
$keepSignature = isset($data['renew_keep_signature']);
if ($keepSignature) {
unset($data['renew_keep_signature']);
}
$contract = $this->get($data['contractid'], [], false, $playground);
if ($keepSignature) {
$data['new_value'] = $contract->contract_value;
}
$data['new_start_date'] = to_sql_date($data['new_start_date']);
$data['new_end_date'] = to_sql_date($data['new_end_date']);
$data['date_renewed'] = date('Y-m-d H:i:s');
$this->load->model('staff_model');
$data['renewed_by'] = $this->staff_model->get_staff_full_name(get_staff_user_id());
$data['renewed_by_staff_id'] = get_staff_user_id();
if (!is_date($data['new_end_date'])) {
unset($data['new_end_date']);
}
// get the original contract so we can check if is expiry notified on delete the expiry to revert
$_contract = $this->get($data['contractid'], [], false, $playground);
$data['is_on_old_expiry_notified'] = $_contract->isexpirynotified;
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'contract_renewals', $data);
$insert_id = $this->db->insert_id();
if ($insert_id) {
$this->db->where('id', $data['contractid']);
$_data = ['datestart' => $data['new_start_date'], 'contract_value' => $data['new_value'], 'isexpirynotified' => 0, ];
if (isset($data['new_end_date'])) {
$_data['dateend'] = $data['new_end_date'];
}
if (!$keepSignature) {
$_data = array_merge($_data, get_acceptance_info_array(true));
$_data['signed'] = 0;
if (!empty($_contract->signature)) {
$this->load->model('misc_model');
unlink($this->misc_model->get_upload_path_by_type('contract', $playground) . $data['contractid'] . '/' . $_contract->signature);
}
}
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'contracts', $_data);
if ($this->db->affected_rows() > 0) {
log_activity('Contract Renewed [ID: ' . $data['contractid'] . ']');
return true;
}
// delete the previous entry
$this->db->where('id', $insert_id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'contract_renewals');
return false;
}
return false;
}
/**
* Delete contract renewal
* @param mixed $id renewal id
* @param mixed $contractid contract id
* @return boolean
*/
public function delete_renewal($id, $contractid, $playground = false) {
// check if this renewal is last so we can revert back the old values, if is not last we wont do anything
$this->db->select('id')->from(db_prefix() . ($playground ? 'playground_' : '') . 'contract_renewals')->where('contractid', $contractid)->order_by('id', 'desc')->limit(1);
$query = $this->db->get();
$last_contract_renewal = $query->row()->id;
$is_last = false;
if ($last_contract_renewal == $id) {
$is_last = true;
$this->db->where('id', $id);
$original_renewal = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'contract_renewals')->row();
}
$contract = $this->get($id, [], false, $playground);
$this->db->where('id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'contract_renewals');
if ($this->db->affected_rows() > 0) {
if (!is_null($contract->short_link)) {
app_archive_short_link($contract->short_link);
}
if ($is_last == true) {
$this->db->where('id', $contractid);
$data = ['datestart' => $original_renewal->old_start_date, 'contract_value' => $original_renewal->old_value, 'isexpirynotified' => $original_renewal->is_on_old_expiry_notified, ];
if ($original_renewal->old_end_date != '0000-00-00') {
$data['dateend'] = $original_renewal->old_end_date;
}
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'contracts', $data);
}
log_activity('Contract Renewed [RenewalID: ' . $id . ', ContractID: ' . $contractid . ']');
return true;
}
return false;
}
/**
* Get the contracts about to expired in the given days
*
* @param integer|null $staffId
* @param integer $days
*
* @return array
*/
public function get_contracts_about_to_expire($staffId = null, $days = 7, $playground = false) {
$diff1 = date('Y-m-d', strtotime('-' . $days . ' days'));
$diff2 = date('Y-m-d', strtotime('+' . $days . ' days'));
if ($staffId && !staff_can('view', 'contracts', $staffId)) {
$this->db->where('addedfrom', $staffId);
}
$this->db->select('id,subject,client,datestart,dateend');
$this->db->where('dateend IS NOT NULL');
$this->db->where('trash', 0);
$this->db->where('dateend >=', $diff1);
$this->db->where('dateend <=', $diff2);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'contracts')->result_array();
}
/**
* Get contract renewals
* @param mixed $id contract id
* @return array
*/
public function get_contract_renewal_history($id, $playground = false) {
$this->db->where('contractid', $id);
$this->db->order_by('date_renewed', 'asc');
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'contract_renewals')->result_array();
}
/**
* @param integer ID (optional)
* @return mixed
* Get contract type object based on passed id if not passed id return array of all types
*/
public function get_contract_types($id = '', $playground = false) {
return $this->contract_types_model->get($id, $playground);
}
/**
* @param integer ID
* @return mixed
* Delete contract type from database, if used return array with key referenced
*/
public function delete_contract_type($id, $playground = false) {
return $this->contract_types_model->delete($id, $playground);
}
/**
* Add new contract type
* @param mixed $data All $_POST data
*/
public function add_contract_type($data, $playground = false) {
return $this->contract_types_model->add($data, $playground);
}
/**
* Edit contract type
* @param mixed $data All $_POST data
* @param mixed $id Contract type id
*/
public function update_contract_type($data, $id, $playground = false) {
return $this->contract_types_model->update($data, $id, $playground);
}
/**
* Get contract types data for chart
* @return array
*/
public function get_contracts_types_chart_data($playground = false) {
return $this->contract_types_model->get_chart_data($playground);
}
/**
* Get contract types values for chart
* @return array
*/
public function get_contracts_types_values_chart_data($playground = false) {
return $this->contract_types_model->get_values_chart_data($playground);
}
/**
* @param object $contract
* @return array<string, string> i.e. ['{merge_field}' => 'value']
*/
public function get_merge_fields(object $contract): array {
$this->load->library('merge_fields/client_merge_fields');
$this->load->library('merge_fields/contract_merge_fields');
$this->load->library('merge_fields/other_merge_fields');
$merge_fields = [];
$merge_fields = array_merge($merge_fields, $this->contract_merge_fields->format($contract->id));
$merge_fields = array_merge($merge_fields, $this->client_merge_fields->format($contract->client));
$merge_fields = array_merge($merge_fields, $this->other_merge_fields->format());
return $merge_fields;
}
}

View File

@@ -0,0 +1,731 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Credit_notes_model extends App_Model {
private $shipping_fields = ['shipping_street', 'shipping_city', 'shipping_city', 'shipping_state', 'shipping_zip', 'shipping_country'];
public function __construct() {
parent::__construct();
$this->load->model('invoices_model');
}
public function get_statuses() {
return hooks()->apply_filters('before_get_credit_notes_statuses', [['id' => 1, 'color' => '#03a9f4', 'name' => _l('credit_note_status_open'), 'order' => 1, 'filter_default' => true, ], ['id' => 2, 'color' => '#84c529', 'name' => _l('credit_note_status_closed'), 'order' => 2, 'filter_default' => true, ], ['id' => 3, 'color' => '#777', 'name' => _l('credit_note_status_void'), 'order' => 3, 'filter_default' => false, ], ]);
}
public function get_available_creditable_invoices($credit_note_id, $playground = false) {
$has_permission_view = staff_can('view', 'invoices');
$invoices_statuses_available_for_credits = invoices_statuses_available_for_credits();
$this->db->select('clientid');
$this->db->where('id', $credit_note_id);
$credit_note = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes')->row();
$this->db->select('' . db_prefix() . ($playground ? 'playground_' : '') . 'invoices.id as id, status, total, date, ' . db_prefix() . ($playground ? 'playground_' : '') . 'currencies.name as currency_name');
$this->db->where('clientid', $credit_note->clientid);
$this->db->where('status IN (' . implode(', ', $invoices_statuses_available_for_credits) . ')');
if (!$has_permission_view) {
$this->db->where('addedfrom', get_staff_user_id());
}
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'currencies', '' . db_prefix() . ($playground ? 'playground_' : '') . 'currencies.id = ' . db_prefix() . ($playground ? 'playground_' : '') . 'invoices.currency');
$invoices = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'invoices')->result_array();
foreach ($invoices as $key => $invoice) {
$invoices[$key]['total_left_to_pay'] = get_invoice_total_left_to_pay($invoice['id'], $invoice['total']);
}
return $invoices;
}
/**
* Send credit note to client
* @param mixed $id credit note id
* @param string $template email template to sent
* @param boolean $attachpdf attach credit note pdf or not
* @return boolean
*/
public function send_credit_note_to_client($id, $attachpdf = true, $cc = '', $manually = false, $playground = false) {
$credit_note = $this->get($id);
$number = format_credit_note_number($credit_note->id);
$sent = false;
$sent_to = $this->input->post('sent_to');
$this->load->model('clients_model');
if ($manually === true) {
$sent_to = [];
$contacts = $this->clients_model->get_contacts($credit_note->clientid, ['active' => 1, 'credit_note_emails' => 1]);
foreach ($contacts as $contact) {
array_push($sent_to, $contact['id']);
}
}
if (is_array($sent_to) && count($sent_to) > 0) {
if ($attachpdf) {
set_mailing_constant();
$pdf = credit_note_pdf($credit_note);
$attach = $pdf->Output($number . '.pdf', 'S');
}
$i = 0;
foreach ($sent_to as $contact_id) {
if ($contact_id != '') {
if (!empty($cc) && $i > 0) {
$cc = '';
}
$contact = $this->clients_model->get_contact($contact_id, ['active' => 1], [], $playground);
$template = mail_template('credit_note_send_to_customer', $credit_note, $contact, $cc);
if ($attachpdf) {
$template->add_attachment(['attachment' => $attach, 'filename' => str_replace('/', '-', $number . '.pdf'), 'type' => 'application/pdf', ]);
}
if ($template->send()) {
$sent = true;
}
}
$i++;
}
} else {
return false;
}
if ($sent) {
hooks()->do_action('credit_note_sent', $id);
return true;
}
return false;
}
/**
* Get credit note/s
* @param mixed $id credit note id
* @param array $where perform where
* @return mixed
*/
public function get($id = '', $where = [], $playground = false) {
$this->db->select('*,' . db_prefix() . ($playground ? 'playground_' : '') . 'currencies.id as currencyid, ' . db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes.id as id, ' . db_prefix() . ($playground ? 'playground_' : '') . 'currencies.name as currency_name');
$this->db->from(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'currencies', '' . db_prefix() . ($playground ? 'playground_' : '') . 'currencies.id = ' . db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes.currency', 'left');
$this->db->where($where);
$this->load->model('clients_model');
if (is_numeric($id)) {
$this->db->where(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes.id', $id);
$credit_note = $this->db->get()->row();
if ($credit_note) {
$credit_note->refunds = $this->get_refunds($id, $playground);
$credit_note->total_refunds = $this->total_refunds_by_credit_note($id, $playground);
$credit_note->applied_credits = $this->get_applied_credits($id, $playground);
$credit_note->remaining_credits = $this->total_remaining_credits_by_credit_note($id, $playground);
$credit_note->credits_used = $this->total_credits_used_by_credit_note($id, $playground);
$this->load->model('invoice_items_model');
$credit_note->items = $this->invoice_items_model->get_items_by_type('credit_note', $id, $playground);
$credit_note->client = $this->clients_model->get($credit_note->clientid, $playground);
if (!$credit_note->client) {
$credit_note->client = new stdClass();
$credit_note->client->company = $credit_note->deleted_customer_name;
}
$credit_note->attachments = $this->get_attachments($id, $playground);
}
return $credit_note;
}
$this->db->order_by('number,YEAR(date)', 'desc');
return $this->db->get()->result_array();
}
public function add($data, $playground = false) {
$save_and_send = isset($data['save_and_send']);
$data['prefix'] = get_option('credit_note_prefix', $playground);
$data['number_format'] = get_option('credit_note_number_format', $playground);
$data['datecreated'] = date('Y-m-d H:i:s');
$data['addedfrom'] = get_staff_user_id();
$items = [];
if (isset($data['newitems'])) {
$items = $data['newitems'];
unset($data['newitems']);
}
if (isset($data['custom_fields'])) {
$custom_fields = $data['custom_fields'];
unset($data['custom_fields']);
}
$data = $this->map_shipping_columns($data, $playground);
$hook = hooks()->apply_filters('before_create_credit_note', ['data' => $data, 'items' => $items]);
$data = $hook['data'];
$items = $hook['items'];
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes', $data);
$insert_id = $this->db->insert_id();
if ($insert_id) {
$this->save_formatted_number($insert_id, $playground);
// Update next credit note number in settings
$this->db->where('name', 'next_credit_note_number');
$this->db->set('value', 'value+1', false);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'options');
if (isset($custom_fields)) {
$this->load->model('custom_fields_model');
$this->custom_fields_model->handle_custom_fields_post($insert_id, $custom_fields, false, $playground);
}
$this->load->model('invoice_items_model');
foreach ($items as $key => $item) {
if ($itemid = $this->invoice_items_model->add_new_sales_item_post($item, $insert_id, 'credit_note', $playground)) {
$this->invoice_items_model->_maybe_insert_post_item_tax($itemid, $item, $insert_id, 'credit_note', $playground);
}
}
update_sales_total_tax_column($insert_id, 'credit_note', db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes');
log_activity('Credit Note Created [ID: ' . $insert_id . ']');
hooks()->do_action('after_create_credit_note', $insert_id);
if ($save_and_send === true) {
$this->send_credit_note_to_client($insert_id, true, '', true, $playground);
}
return $insert_id;
}
return false;
}
/**
* Update proposal
* @param mixed $data $_POST data
* @param mixed $id proposal id
* @return boolean
*/
public function update($data, $id, $playground = false) {
$affectedRows = 0;
$save_and_send = isset($data['save_and_send']);
$items = [];
if (isset($data['items'])) {
$items = $data['items'];
unset($data['items']);
}
$newitems = [];
if (isset($data['newitems'])) {
$newitems = $data['newitems'];
unset($data['newitems']);
}
if (isset($data['custom_fields'])) {
$custom_fields = $data['custom_fields'];
$this->load->model('custom_fields_model');
if ($this->custom_fields_model->handle_custom_fields_post($id, $custom_fields, false, $playground)) {
$affectedRows++;
}
unset($data['custom_fields']);
}
$data = $this->map_shipping_columns($data, $playground);
$hook = hooks()->apply_filters('before_update_credit_note', ['data' => $data, 'items' => $items, 'newitems' => $newitems, 'removed_items' => isset($data['removed_items']) ? $data['removed_items'] : [], ], $id);
$data = $hook['data'];
$items = $hook['items'];
$newitems = $hook['newitems'];
$data['removed_items'] = $hook['removed_items'];
// Delete items checked to be removed from database
foreach ($data['removed_items'] as $remove_item_id) {
if (handle_removed_sales_item_post($remove_item_id, 'credit_note', $playground)) {
$affectedRows++;
}
}
unset($data['removed_items']);
$this->db->where('id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes', $data);
if ($this->db->affected_rows() > 0) {
$this->save_formatted_number($id, $playground);
$affectedRows++;
}
$this->load->model('custom_fields_model');
$this->load->model('invoice_items_model');
foreach ($items as $key => $item) {
if (update_sales_item_post($item['itemid'], $item, $playground)) {
$affectedRows++;
}
if (isset($item['custom_fields'])) {
if ($this->custom_fields_model->handle_custom_fields_post($item['itemid'], $item['custom_fields'], false, $playground)) {
$affectedRows++;
}
}
if (!isset($item['taxname']) || (isset($item['taxname']) && count($item['taxname']) == 0)) {
if (delete_taxes_from_item($item['itemid'], 'credit_note', $playground)) {
$affectedRows++;
}
} else {
$item_taxes = get_credit_note_item_taxes($item['itemid'], $playground);
$_item_taxes_names = [];
foreach ($item_taxes as $_item_tax) {
array_push($_item_taxes_names, $_item_tax['taxname']);
}
$i = 0;
foreach ($_item_taxes_names as $_item_tax) {
if (!in_array($_item_tax, $item['taxname'])) {
$this->db->where('id', $item_taxes[$i]['id'])->delete(db_prefix() . ($playground ? 'playground_' : '') . 'item_tax');
if ($this->db->affected_rows() > 0) {
$affectedRows++;
}
}
$i++;
}
if ($this->invoice_items_model->_maybe_insert_post_item_tax($item['itemid'], $item, $id, 'credit_note', $playground)) {
$affectedRows++;
}
}
}
foreach ($newitems as $key => $item) {
if ($new_item_added = $this->invoice_items_model->add_new_sales_item_post($item, $id, 'credit_note', $playground)) {
$this->invoice_items_model->_maybe_insert_post_item_tax($new_item_added, $item, $id, 'credit_note', $playground);
$affectedRows++;
}
}
if ($save_and_send === true) {
$this->send_credit_note_to_client($id, true, '', true, $playground);
}
if ($affectedRows > 0) {
$this->update_credit_note_status($id, $playground);
update_sales_total_tax_column($id, 'credit_note', db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes');
}
if ($affectedRows > 0) {
log_activity('Credit Note Updated [ID:' . $id . ']');
hooks()->do_action('after_update_credit_note', $id);
return true;
}
return false;
}
/**
* Delete credit note attachment
* @param mixed $id attachmentid
* @return boolean
*/
public function delete_attachment($id, $playground = false) {
$this->load->model('misc_model');
$attachment = $this->misc_model->get_file($id, $playground);
$deleted = false;
if ($attachment) {
if (empty($attachment->external)) {
unlink($this->misc_model->get_upload_path_by_type('credit_note', $playground) . $attachment->rel_id . '/' . $attachment->file_name);
}
$this->db->where('id', $attachment->id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'files');
if ($this->db->affected_rows() > 0) {
$deleted = true;
log_activity('Credit Note Attachment Deleted [Credite Note: ' . format_credit_note_number($attachment->rel_id) . ']');
}
if (is_dir($this->misc_model->get_upload_path_by_type('credit_note', $playground) . $attachment->rel_id)) {
// Check if no attachments left, so we can delete the folder also
$other_attachments = list_files($this->misc_model->get_upload_path_by_type('credit_note', $playground) . $attachment->rel_id);
if (count($other_attachments) == 0) {
// okey only index.html so we can delete the folder also
delete_dir($this->misc_model->get_upload_path_by_type('credit_note', $playground) . $attachment->rel_id);
}
}
}
return $deleted;
}
public function get_attachments($credit_note_id, $playground = false) {
$this->db->where('rel_id', $credit_note_id);
$this->db->where('rel_type', 'credit_note');
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'files')->result_array();
}
/**
* Delete credit note
* @param mixed $id credit note id
* @return boolean
*/
public function delete($id, $simpleDelete = false, $playground = false) {
hooks()->do_action('before_credit_note_deleted', $id);
$this->db->where('id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes');
if ($this->db->affected_rows() > 0) {
$current_credit_note_number = get_option('next_credit_note_number');
if ($current_credit_note_number > 1 && $simpleDelete == false && is_last_credit_note($id)) {
// Decrement next credit note number
$this->db->where('name', 'next_credit_note_number');
$this->db->set('value', 'value-1', false);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'options');
}
$this->load->model('misc_model');
$this->misc_model->delete_tracked_emails($id, 'credit_note', $playground);
// Delete the custom field values
$this->db->where('relid IN (SELECT id from ' . db_prefix() . ($playground ? 'playground_' : '') . 'itemable WHERE rel_type="credit_note" AND rel_id="' . $this->db->escape_str($id) . '")');
$this->db->where('fieldto', 'items');
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'customfieldsvalues');
$this->db->where('relid', $id);
$this->db->where('fieldto', 'credit_note');
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'customfieldsvalues');
$this->db->where('credit_id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'credits');
$this->db->where('credit_note_id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'creditnote_refunds');
$this->db->where('rel_id', $id);
$this->db->where('rel_type', 'credit_note');
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'itemable');
$this->db->where('rel_id', $id);
$this->db->where('rel_type', 'credit_note');
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'item_tax');
$attachments = $this->get_attachments($id, $playground);
foreach ($attachments as $attachment) {
$this->delete_attachment($attachment['id'], $playground);
}
$this->db->where('rel_type', 'credit_note');
$this->db->where('rel_id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'reminders');
hooks()->do_action('after_credit_note_deleted', $id);
return true;
}
return false;
}
public function mark($id, $status, $playground = false) {
$this->db->where('id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes', ['status' => $status]);
if ($this->db->affected_rows() > 0) {
hooks()->do_action('credit_note_status_changed', $id, ['status' => $status]);
return true;
}
return false;
}
public function total_remaining_credits_by_customer($customer_id, $playground = false) {
$has_permission_view = staff_can('view', 'credit_notes');
$this->db->select('total,id');
$this->db->where('clientid', $customer_id);
$this->db->where('status', 1);
if (!$has_permission_view) {
$this->db->where('addedfrom', get_staff_user_id());
}
$credits = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes')->result_array();
$total = $this->calc_remaining_credits($credits, $playground);
return $total;
}
public function total_remaining_credits_by_credit_note($credit_note_id, $playground = false) {
$this->db->select('total,id');
$this->db->where('id', $credit_note_id);
$credits = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes')->result_array();
$total = $this->calc_remaining_credits($credits, $playground);
return $total;
}
private function calc_remaining_credits($credits, $playground = false) {
$total = 0;
$credits_ids = [];
$bcadd = function_exists('bcadd');
foreach ($credits as $credit) {
if ($bcadd) {
$total = bcadd($total, $credit['total'], get_decimal_places());
} else {
$total+= $credit['total'];
}
array_push($credits_ids, $credit['id']);
}
if (count($credits_ids) > 0) {
$this->db->where('credit_id IN (' . implode(', ', $credits_ids) . ')');
$applied_credits = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'credits')->result_array();
$bcsub = function_exists('bcsub');
foreach ($applied_credits as $credit) {
if ($bcsub) {
$total = bcsub($total, $credit['amount'], get_decimal_places());
} else {
$total-= $credit['amount'];
}
}
foreach ($credits_ids as $credit_note_id) {
$total_refunds_by_credit_note = $this->total_refunds_by_credit_note($credit_note_id, $playground) ? : 0;
if ($bcsub) {
$total = bcsub($total, $total_refunds_by_credit_note, get_decimal_places());
} else {
$total-= $total_refunds_by_credit_note;
}
}
}
return $total;
}
public function delete_applied_credit($id, $credit_id, $invoice_id, $playground = false) {
$this->db->where('id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'credits');
if ($this->db->affected_rows() > 0) {
$this->update_credit_note_status($credit_id, $playground);
update_invoice_status($invoice_id, $playground);
}
}
public function credit_note_from_invoice($invoice_id, $playground = false) {
$_invoice = $this->invoices_model->get($invoice_id, $playground);
$new_credit_note_data = [];
$new_credit_note_data['clientid'] = $_invoice->clientid;
$new_credit_note_data['number'] = get_option('next_credit_note_number');
$new_credit_note_data['date'] = _d(date('Y-m-d'));
$new_credit_note_data['show_quantity_as'] = $_invoice->show_quantity_as;
$new_credit_note_data['currency'] = $_invoice->currency;
$new_credit_note_data['subtotal'] = $_invoice->subtotal;
$new_credit_note_data['total'] = $_invoice->total;
$new_credit_note_data['adminnote'] = $_invoice->adminnote;
$new_credit_note_data['adjustment'] = $_invoice->adjustment;
$new_credit_note_data['discount_percent'] = $_invoice->discount_percent;
$new_credit_note_data['discount_total'] = $_invoice->discount_total;
$new_credit_note_data['discount_type'] = $_invoice->discount_type;
$new_credit_note_data['billing_street'] = clear_textarea_breaks($_invoice->billing_street);
$new_credit_note_data['billing_city'] = $_invoice->billing_city;
$new_credit_note_data['billing_state'] = $_invoice->billing_state;
$new_credit_note_data['billing_zip'] = $_invoice->billing_zip;
$new_credit_note_data['billing_country'] = $_invoice->billing_country;
$new_credit_note_data['shipping_street'] = clear_textarea_breaks($_invoice->shipping_street);
$new_credit_note_data['shipping_city'] = $_invoice->shipping_city;
$new_credit_note_data['shipping_state'] = $_invoice->shipping_state;
$new_credit_note_data['shipping_zip'] = $_invoice->shipping_zip;
$new_credit_note_data['shipping_country'] = $_invoice->shipping_country;
$new_credit_note_data['reference_no'] = format_invoice_number($_invoice->id);
if ($_invoice->include_shipping == 1) {
$new_credit_note_data['include_shipping'] = $_invoice->include_shipping;
}
$new_credit_note_data['show_shipping_on_credit_note'] = $_invoice->show_shipping_on_invoice;
$new_credit_note_data['clientnote'] = get_option('predefined_clientnote_credit_note');
$new_credit_note_data['terms'] = get_option('predefined_terms_credit_note');
$new_credit_note_data['adminnote'] = '';
$new_credit_note_data['newitems'] = [];
$this->load->model('custom_fields_items');
$custom_fields_items = $this->custom_fields_model->get_custom_fields('items', [], false, $playground);
$key = 1;
foreach ($_invoice->items as $item) {
$new_credit_note_data['newitems'][$key]['description'] = $item['description'];
$new_credit_note_data['newitems'][$key]['long_description'] = clear_textarea_breaks($item['long_description']);
$new_credit_note_data['newitems'][$key]['qty'] = $item['qty'];
$new_credit_note_data['newitems'][$key]['unit'] = $item['unit'];
$new_credit_note_data['newitems'][$key]['taxname'] = [];
$taxes = get_invoice_item_taxes($item['id']);
foreach ($taxes as $tax) {
// tax name is in format TAX1|10.00
array_push($new_credit_note_data['newitems'][$key]['taxname'], $tax['taxname']);
}
$new_credit_note_data['newitems'][$key]['rate'] = $item['rate'];
$new_credit_note_data['newitems'][$key]['order'] = $item['item_order'];
foreach ($custom_fields_items as $cf) {
$new_credit_note_data['newitems'][$key]['custom_fields']['items'][$cf['id']] = $this->custom_fields_model->get_custom_field_value($item['id'], $cf['id'], 'items', false, $playground);
if (!defined('COPY_CUSTOM_FIELDS_LIKE_HANDLE_POST')) {
define('COPY_CUSTOM_FIELDS_LIKE_HANDLE_POST', true);
}
}
$key++;
}
$id = $this->add($new_credit_note_data);
if ($id) {
if ($_invoice->status != Invoices_model::STATUS_PAID) {
if ($_invoice->status == Invoices_model::STATUS_DRAFT) {
$this->invoices_model->change_invoice_number_when_status_draft($invoice_id, $playground);
}
if ($this->apply_credits($id, ['invoice_id' => $invoice_id, 'amount' => $_invoice->total_left_to_pay])) {
update_invoice_status($invoice_id, true);
}
$this->invoices_model->save_formatted_number($invoice_id, $playground);
}
$invoiceNumber = format_invoice_number($_invoice->id);
$this->db->where('id', $id);
$this->db->update(($playground ? 'playground_' : '') . 'creditnotes', ['reference_no' => $invoiceNumber]);
log_activity('Created Credit Note From Invoice [Invoice: ' . $invoiceNumber . ', Credit Note: ' . format_credit_note_number($id) . ']');
hooks()->do_action('created_credit_note_from_invoice', ['invoice_id' => $invoice_id, 'credit_note_id' => $id]);
return $id;
}
return false;
}
public function create_refund($id, $data, $playground = false) {
if ($data['amount'] == 0) {
return false;
}
$data['note'] = trim($data['note']);
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'creditnote_refunds', ['created_at' => date('Y-m-d H:i:s'), 'credit_note_id' => $id, 'staff_id' => $data['staff_id'], 'refunded_on' => $data['refunded_on'], 'payment_mode' => $data['payment_mode'], 'amount' => $data['amount'], 'note' => nl2br($data['note']), ]);
$insert_id = $this->db->insert_id();
if ($insert_id) {
$this->update_credit_note_status($id, $playground);
hooks()->do_action('credit_note_refund_created', ['data' => $data, 'credit_note_id' => $id]);
}
return $insert_id;
}
public function edit_refund($id, $data, $playground = false) {
if ($data['amount'] == 0) {
return false;
}
$refund = $this->get_refund($id, $playground);
$data['note'] = trim($data['note']);
$this->db->where('id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'creditnote_refunds', ['refunded_on' => $data['refunded_on'], 'payment_mode' => $data['payment_mode'], 'amount' => $data['amount'], 'note' => nl2br($data['note']), ]);
$insert_id = $this->db->insert_id();
if ($this->db->affected_rows() > 0) {
$this->update_credit_note_status($refund->credit_note_id, $playground);
hooks()->do_action('credit_note_refund_updated', ['data' => $data, 'refund_id' => $refund->credit_note_id]);
}
return $insert_id;
}
public function get_refund($id, $playground = false) {
$this->db->where('id', $id);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'creditnote_refunds')->row();
}
public function get_refunds($credit_note_id, $playground = false) {
$this->db->select(prefixed_table_fields_array(db_prefix() . ($playground ? 'playground_' : '') . 'creditnote_refunds', true) . ',' . db_prefix() . ($playground ? 'playground_' : '') . 'payment_modes.id as payment_mode_id, ' . db_prefix() . ($playground ? 'playground_' : '') . 'payment_modes.name as payment_mode_name');
$this->db->where('credit_note_id', $credit_note_id);
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'payment_modes', db_prefix() . ($playground ? 'playground_' : '') . 'payment_modes.id = ' . db_prefix() . ($playground ? 'playground_' : '') . 'creditnote_refunds.payment_mode', 'left');
$this->db->order_by('refunded_on', 'desc');
$refunds = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'creditnote_refunds')->result_array();
$this->load->model('payment_modes_model');
$payment_gateways = $this->payment_modes_model->get_payment_gateways(true, $playground);
$i = 0;
foreach ($refunds as $refund) {
if (is_null($refund['payment_mode_id'])) {
foreach ($payment_gateways as $gateway) {
if ($refund['payment_mode'] == $gateway['id']) {
$refunds[$i]['payment_mode_id'] = $gateway['id'];
$refunds[$i]['payment_mode_name'] = $gateway['name'];
}
}
}
$i++;
}
return $refunds;
}
public function delete_refund($refund_id, $credit_note_id, $playground = false) {
$this->db->where('id', $refund_id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'creditnote_refunds');
if ($this->db->affected_rows() > 0) {
$this->update_credit_note_status($credit_note_id, $playground);
hooks()->do_action('credit_note_refund_deleted', ['refund_id' => $refund_id, 'credit_note_id' => $credit_note_id]);
return true;
}
return false;
}
private function total_refunds_by_credit_note($id, $playground = false) {
return sum_from_table(db_prefix() . ($playground ? 'playground_' : '') . 'creditnote_refunds', ['field' => 'amount', 'where' => ['credit_note_id' => $id], ]);
}
public function apply_credits($id, $data, $playground = false) {
if ($data['amount'] == 0) {
return false;
}
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'credits', ['invoice_id' => $data['invoice_id'], 'credit_id' => $id, 'staff_id' => get_staff_user_id(), 'date' => date('Y-m-d'), 'date_applied' => date('Y-m-d H:i:s'), 'amount' => $data['amount'], ]);
$insert_id = $this->db->insert_id();
if ($insert_id) {
$this->update_credit_note_status($id, $playground);
$invoice = $this->db->select('id,status')->where('id', $data['invoice_id'])->get(db_prefix() . ($playground ? 'playground_' : '') . 'invoices')->row();
if ($invoice->status == Invoices_model::STATUS_DRAFT) {
// update invoice number for invoice with draft - V2.7.2
$this->invoices_model->change_invoice_number_when_status_draft($invoice->id, $playground);
$this->invoices_model->save_formatted_number($invoice->id, $playground);
}
$this->db->select(db_prefix() . ($playground ? 'playground_' : '') . 'currencies.name as currency_name');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'currencies', '' . db_prefix() . ($playground ? 'playground_' : '') . 'currencies.id = ' . db_prefix() . ($playground ? 'playground_' : '') . 'invoices.currency');
$this->db->where(db_prefix() . ($playground ? 'playground_' : '') . 'invoices.id', $data['invoice_id']);
$invoice = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'invoices')->row();
$inv_number = format_invoice_number($data['invoice_id']);
$credit_note_number = format_credit_note_number($id);
$this->invoices_model->log_invoice_activity($data['invoice_id'], 'invoice_activity_applied_credits', false, serialize([app_format_money($data['amount'], $invoice->currency_name), $credit_note_number, ]), $playground);
hooks()->do_action('credits_applied', ['data' => $data, 'credit_note_id' => $id]);
log_activity('Credit Applied to Invoice [ Invoice: ' . $inv_number . ', Credit: ' . $credit_note_number . ' ]');
}
return $insert_id;
}
private function total_credits_used_by_credit_note($id, $playground = false) {
return sum_from_table(db_prefix() . ($playground ? 'playground_' : '') . 'credits', ['field' => 'amount', 'where' => ['credit_id' => $id], ]);
}
public function update_credit_note_status($id, $playground = false) {
$total_refunds_by_credit_note = $this->total_refunds_by_credit_note($id, $playground);
$total_credits_used = $this->total_credits_used_by_credit_note($id, $playground);
$status = 1;
// sum from table returns null if nothing found
if ($total_credits_used || $total_refunds_by_credit_note) {
$compare = $total_credits_used + $total_refunds_by_credit_note;
$this->db->select('total');
$this->db->where('id', $id);
$credit = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes')->row();
if ($credit) {
if (function_exists('bccomp')) {
if (bccomp($credit->total, $compare, get_decimal_places()) === 0) {
$status = 2;
}
} else {
if ($credit->total == $compare) {
$status = 2;
}
}
}
}
$this->db->where('id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes', ['status' => $status]);
return $this->db->affected_rows() > 0 ? true : false;
}
public function get_open_credits($customer_id, $playground = false) {
$has_permission_view = staff_can('view', 'credit_notes');
$this->db->where('status', 1);
$this->db->where('clientid', $customer_id);
if (!$has_permission_view) {
$this->db->where('addedfrom', get_staff_user_id());
}
$credits = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes')->result_array();
foreach ($credits as $key => $credit) {
$credits[$key]['available_credits'] = $this->calculate_available_credits($credit['id'], $credit['total'], $playground);
}
return $credits;
}
public function get_applied_invoice_credits($invoice_id, $playground = false) {
$this->db->order_by('date', 'desc');
$this->db->where('invoice_id', $invoice_id);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'credits')->result_array();
}
public function get_applied_credits($credit_id, $playground = false) {
$this->db->where('credit_id', $credit_id);
$this->db->order_by('date', 'desc');
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'credits')->result_array();
}
private function calculate_available_credits($credit_id, $credit_amount = false, $playground = false) {
if ($credit_amount === false) {
$this->db->select('total')->from(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes')->where('id', $credit_id);
$credit_amount = $this->db->get()->row()->total;
}
$available_total = $credit_amount;
$bcsub = function_exists('bcsub');
$applied_credits = $this->get_applied_credits($credit_id, $playground);
foreach ($applied_credits as $credit) {
if ($bcsub) {
$available_total = bcsub($available_total, $credit['amount'], get_decimal_places());
} else {
$available_total-= $credit['amount'];
}
}
$total_refunds = $this->total_refunds_by_credit_note($credit_id, $playground);
if ($total_refunds) {
if ($bcsub) {
$available_total = bcsub($available_total, $total_refunds, get_decimal_places());
} else {
$available_total-= $total_refunds;
}
}
return $available_total;
}
public function save_formatted_number($id, $playground = false) {
$formattedNumber = format_credit_note_number($id);
$this->db->where('id', $id);
$this->db->update(($playground ? 'playground_' : '') . 'creditnotes', ['formatted_number' => $formattedNumber]);
}
public function get_credits_years($playground = false) {
return $this->db->query('SELECT DISTINCT(YEAR(date)) as year FROM ' . db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes ORDER BY year DESC')->result_array();
}
private function map_shipping_columns($data, $playground = false) {
if (!isset($data['include_shipping'])) {
foreach ($this->shipping_fields as $_s_field) {
if (isset($data[$_s_field])) {
$data[$_s_field] = null;
}
}
$data['show_shipping_on_credit_note'] = 1;
$data['include_shipping'] = 0;
} else {
$data['include_shipping'] = 1;
// set by default for the next time to be checked
if (isset($data['show_shipping_on_credit_note']) && ($data['show_shipping_on_credit_note'] == 1 || $data['show_shipping_on_credit_note'] == 'on')) {
$data['show_shipping_on_credit_note'] = 1;
} else {
$data['show_shipping_on_credit_note'] = 0;
}
}
return $data;
}
}

View File

@@ -0,0 +1,165 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Currencies_model extends App_Model {
public function __construct() {
parent::__construct();
}
/**
* @param integer ID (optional)
* @return mixed
* Get currency object based on passed id if not passed id return array of all currencies
*/
public function get($id = false, $playground = false) {
if (is_numeric($id)) {
$this->db->where('id', $id);
$currency = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'currencies')->row();
$this->app_object_cache->set('currency-' . $currency->name, $currency);
return $currency;
}
$currencies = $this->app_object_cache->get('currencies-data');
if (!$currencies && !is_array($currencies)) {
$currencies = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'currencies')->result_array();
$this->app_object_cache->add('currencies-data', $currencies);
}
return $currencies;
}
/**
* Get currency by name/iso code
* @since 2.3.2
* @param string $name currency name/iso code
* @return object
*/
public function get_by_name($name, $playground = false) {
$currency = $this->app_object_cache->get('currency-' . $name);
if (!$currency && !is_object($currency)) {
$this->db->where('name', $name);
$currency = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'currencies')->row();
$this->app_object_cache->add('currency-' . $name, $currency);
}
return $currency;
}
/**
* @param array $_POST data
* @return boolean
*/
public function add($data, $playground = false) {
unset($data['currencyid']);
$data['name'] = strtoupper($data['name']);
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'currencies', $data);
$insert_id = $this->db->insert_id();
if ($insert_id) {
log_activity('New Currency Added [ID: ' . $data['name'] . ']');
return true;
}
return false;
}
/**
* @param array $_POST data
* @return boolean
* Update currency values
*/
public function edit($data, $playground = false) {
$currencyid = $data['currencyid'];
unset($data['currencyid']);
$data['name'] = strtoupper($data['name']);
$this->db->where('id', $currencyid);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'currencies', $data);
if ($this->db->affected_rows() > 0) {
log_activity('Currency Updated [' . $data['name'] . ']');
return true;
}
return false;
}
/**
* @param integer ID
* @return mixed
* Delete currency from database, if used return array with key referenced
*/
public function delete($id, $playground = false) {
foreach ($this->app->get_tables_with_currency($playground) as $tt) {
if (is_reference_in_table($tt['field'], $tt['table'], $id)) {
return ['referenced' => true, ];
}
}
$currency = $this->get($id);
if ($currency->isdefault == 1) {
return ['is_default' => true, ];
}
$this->db->where('id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'currencies');
if ($this->db->affected_rows() > 0) {
$this->load->dbforge();
$columns = $this->db->list_fields(db_prefix() . ($playground ? 'playground_' : '') . 'items');
foreach ($columns as $column) {
if ($column == 'rate_currency_' . $id) {
$this->dbforge->drop_column('items', 'rate_currency_' . $id);
}
}
log_activity('Currency Deleted [' . $id . ']');
return true;
}
return false;
}
/**
* @param integer ID
* @return boolean
* Make currency your base currency for better using reports if found invoices with more then 1 currency
*/
public function make_base_currency($id, $playground = false) {
$base = $this->get_base_currency($playground);
foreach ($this->app->get_tables_with_currency($playground) as $tt) {
if (is_reference_in_table($tt['field'], $tt['table'], $base->id)) {
return ['has_transactions_currency' => true, ];
}
}
$this->db->where('id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'currencies', ['isdefault' => 1, ]);
if ($this->db->affected_rows() > 0) {
$this->db->where('id !=', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'currencies', ['isdefault' => 0, ]);
return true;
}
return false;
}
/**
* @return object
* Get base currency
*/
public function get_base_currency($playground = false) {
$this->db->where('isdefault', 1);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'currencies')->row();
}
/**
* @param integer ID
* @return string
* Get the symbol from the currency
*/
public function get_currency_symbol($id, $playground = false) {
if (!is_numeric($id)) {
$id = $this->get_base_currency($playground)->id;
}
$currencies = $this->app_object_cache->get('currencies-data');
if ($currencies) {
foreach ($currencies as $currency) {
if ($id == $currency['id']) {
return $currency['symbol'];
}
}
}
$this->db->select('symbol');
$this->db->from(db_prefix() . ($playground ? 'playground_' : '') . 'currencies');
$this->db->where('id', $id);
return $this->db->get()->row()->symbol;
}
}

View File

@@ -0,0 +1,567 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Custom_fields_model extends App_Model {
private $pdf_fields = ['estimate', 'invoice', 'credit_note', 'items'];
private $client_portal_fields = ['customers', 'estimate', 'invoice', 'proposal', 'contracts', 'tasks', 'projects', 'contacts', 'tickets', 'company', 'credit_note'];
private $client_editable_fields = ['customers', 'contacts', 'tasks'];
public function __construct() {
parent::__construct();
}
/**
* @param integer (optional)
* @return object
* Get single custom field
*/
public function get($id = false, $playground = false) {
if (is_numeric($id)) {
$this->db->where('id', $id);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'customfields')->row();
}
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'customfields')->result_array();
}
/**
* Add new custom field
* @param mixed $data All $_POST data
* @return boolean
*/
public function add($data, $playground = false) {
if (isset($data['disabled'])) {
$data['active'] = 0;
unset($data['disabled']);
} else {
$data['active'] = 1;
}
if (isset($data['show_on_pdf'])) {
if (in_array($data['fieldto'], $this->pdf_fields)) {
$data['show_on_pdf'] = 1;
} else {
$data['show_on_pdf'] = 0;
}
} else {
$data['show_on_pdf'] = 0;
}
if (isset($data['required'])) {
$data['required'] = 1;
} else {
$data['required'] = 0;
}
if (isset($data['disalow_client_to_edit'])) {
$data['disalow_client_to_edit'] = 1;
} else {
$data['disalow_client_to_edit'] = 0;
}
if (isset($data['show_on_table'])) {
$data['show_on_table'] = 1;
} else {
$data['show_on_table'] = 0;
}
if (isset($data['only_admin'])) {
$data['only_admin'] = 1;
} else {
$data['only_admin'] = 0;
}
if (isset($data['show_on_client_portal'])) {
if (in_array($data['fieldto'], $this->client_portal_fields)) {
$data['show_on_client_portal'] = 1;
} else {
$data['show_on_client_portal'] = 0;
}
} else {
$data['show_on_client_portal'] = 0;
}
if ($data['field_order'] == '') {
$data['field_order'] = 0;
}
$data['slug'] = slug_it($data['fieldto'] . '_' . $data['name'], ['separator' => '_', ]);
$slugs_total = total_rows(db_prefix() . ($playground ? 'playground_' : '') . 'customfields', ['slug' => $data['slug']]);
if ($slugs_total > 0) {
$data['slug'].= '_' . ($slugs_total + 1);
}
if ($data['fieldto'] == 'company') {
$data['show_on_pdf'] = 1;
$data['show_on_client_portal'] = 1;
$data['show_on_table'] = 1;
$data['only_admin'] = 0;
$data['disalow_client_to_edit'] = 0;
} else if ($data['fieldto'] == 'items') {
$data['show_on_pdf'] = 1;
$data['show_on_client_portal'] = 1;
$data['show_on_table'] = 1;
$data['only_admin'] = 0;
$data['disalow_client_to_edit'] = 0;
}
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'customfields', $data);
$insert_id = $this->db->insert_id();
if ($insert_id) {
log_activity('New Custom Field Added [' . $data['name'] . ']');
return $insert_id;
}
return false;
}
/**
* Update custom field
* @param mixed $data All $_POST data
* @return boolean
*/
public function update($data, $id, $playground = false) {
$original_field = $this->get($id, $playground);
if (isset($data['disabled'])) {
$data['active'] = 0;
unset($data['disabled']);
} else {
$data['active'] = 1;
}
if (isset($data['disalow_client_to_edit'])) {
$data['disalow_client_to_edit'] = 1;
} else {
$data['disalow_client_to_edit'] = 0;
}
if (isset($data['only_admin'])) {
$data['only_admin'] = 1;
} else {
$data['only_admin'] = 0;
}
if (isset($data['required'])) {
$data['required'] = 1;
} else {
$data['required'] = 0;
}
if (isset($data['show_on_pdf'])) {
if (in_array($data['fieldto'], $this->pdf_fields)) {
$data['show_on_pdf'] = 1;
} else {
$data['show_on_pdf'] = 0;
}
} else {
$data['show_on_pdf'] = 0;
}
if ($data['field_order'] == '') {
$data['field_order'] = 0;
}
if (isset($data['show_on_client_portal'])) {
if (in_array($data['fieldto'], $this->client_portal_fields)) {
$data['show_on_client_portal'] = 1;
} else {
$data['show_on_client_portal'] = 0;
}
} else {
$data['show_on_client_portal'] = 0;
}
if (isset($data['show_on_table'])) {
$data['show_on_table'] = 1;
} else {
$data['show_on_table'] = 0;
}
if (!isset($data['display_inline'])) {
$data['display_inline'] = 0;
}
if (!isset($data['show_on_ticket_form'])) {
$data['show_on_ticket_form'] = 0;
}
if ($data['fieldto'] == 'company') {
$data['show_on_pdf'] = 1;
$data['show_on_client_portal'] = 1;
$data['show_on_table'] = 1;
$data['only_admin'] = 0;
$data['disalow_client_to_edit'] = 0;
} else if ($data['fieldto'] == 'items') {
$data['show_on_pdf'] = 1;
$data['show_on_client_portal'] = 1;
$data['show_on_table'] = 1;
$data['only_admin'] = 0;
$data['disalow_client_to_edit'] = 0;
}
$this->db->where('id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'customfields', $data);
if ($this->db->affected_rows() > 0) {
log_activity('Custom Field Updated [' . $data['name'] . ']');
if ($data['type'] == 'checkbox' || $data['type'] == 'select' || $data['type'] == 'multiselect') {
if (trim($data['options']) != trim($original_field->options)) {
$options_now = explode(',', $data['options']);
foreach ($options_now as $key => $val) {
$options_now[$key] = trim($val);
}
$options_before = explode(',', $original_field->options);
foreach ($options_before as $key => $val) {
$options_before[$key] = trim($val);
}
$removed_options_in_use = [];
foreach ($options_before as $option) {
if (!in_array($option, $options_now) && total_rows(db_prefix() . ($playground ? 'playground_' : '') . 'customfieldsvalues', ['fieldid' => $id, 'value' => $option, ])) {
array_push($removed_options_in_use, $option);
}
}
if (count($removed_options_in_use) > 0) {
$this->db->where('id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'customfields', ['options' => implode(',', $options_now) . ',' . implode(',', $removed_options_in_use), ]);
return ['cant_change_option_custom_field' => true, ];
}
}
}
return true;
}
return false;
}
/**
* @param integer
* @return boolean
* Delete Custom fields
* All values for this custom field will be deleted from database
*/
public function delete($id, $playground = false) {
$this->db->where('id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'customfields');
if ($this->db->affected_rows() > 0) {
// Delete the values
$this->db->where('fieldid', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'customfieldsvalues');
log_activity('Custom Field Deleted [' . $id . ']');
return true;
}
return false;
}
public function get_custom_data($data, $custom_field_type, $id = '', $is_invoice_item = false, $playground = false)
{
$this->db->where('active', 1);
$this->db->where('fieldto', $custom_field_type);
$this->db->order_by('field_order', 'asc');
$fields = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'customfields')->result_array();
$customfields = [];
if ('' === $id) {
foreach ($data as $data_key => $value) {
$data[$data_key]['customfields'] = [];
$value_id = $value['id'] ?? '';
if ('customers' == $custom_field_type) {
$value_id = $value['userid'];
}
if ('tickets' == $custom_field_type) {
$value_id = $value['ticketid'];
}
if ('staff' == $custom_field_type) {
$value_id = $value['staffid'];
}
foreach ($fields as $key => $field) {
$customfields[$key] = new StdClass();
$customfields[$key]->label = $field['name'];
if ('items' == $custom_field_type && !$is_invoice_item) {
$custom_field_type = 'items_pr';
$value_id = $value['itemid'] ?? $value['id'];
}
$customfields[$key]->value = $this->custom_fields_model->get_custom_field_value($value_id, $field['id'], $custom_field_type, false, $playground);
}
$data[$data_key]['customfields'] = $customfields;
}
}
if ('' !== $id && is_numeric($id)) {
$data->customfields = new StdClass();
foreach ($fields as $key => $field) {
$customfields[$key] = new StdClass();
$customfields[$key]->label = $field['name'];
if ('items' == $custom_field_type && !$is_invoice_item) {
$custom_field_type = 'items_pr';
}
$customfields[$key]->value = $this->custom_fields_model->get_custom_field_value($id, $field['id'], $custom_field_type, false, $playground);
}
$data->customfields = $customfields;
}
return $data;
}
/**
* Change custom field status / active / inactive
* @param mixed $id customfield id
* @param integer $status active or inactive
*/
public function change_custom_field_status($id, $status, $playground = false) {
$this->db->where('id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'customfields', ['active' => $status, ]);
log_activity('Custom Field Status Changed [FieldID: ' . $id . ' - Active: ' . $status . ']');
}
public function get_relation_data_api($type, $search = '', $playground = false)
{
$q = '';
if ('' != $search) {
$q = $search;
$q = trim(urldecode($q));
}
$this->load->model('clients_model');
$this->load->model('misc_model');
$this->load->model('payment_modes_model');
$data = [];
if ('customer' == $type || 'customers' == $type) {
$where_clients = db_prefix() . ($playground ? 'playground_' : '') . 'clients.active=1';
if ($q) {
$where_clients .= ' AND (';
$where_clients .= 'company LIKE "%'.$q.'%" OR CONCAT(firstname, " ", lastname) LIKE "%'.$q.'%" OR email LIKE "%'.$q.'%"';
$fields = $this->get_custom_fields('customers', [], false, $playground);
foreach ($fields as $key => $value) {
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'customfieldsvalues as ctable_'.$key.'', db_prefix() . ($playground ? 'playground_' : '') . 'clients.userid = ctable_'.$key.'.relid and ctable_'.$key.'.fieldto="customers" AND ctable_'.$key.'.fieldid='.$value['id'], 'LEFT');
$where_clients .= ' OR ctable_'.$key.'.value LIKE "%'.$q.'%"';
}
$where_clients .= ')';
}
$data = $this->clients_model->get('', $where_clients);
} else if ('contacts' == $type) {
$where_clients = db_prefix() . ($playground ? 'playground_' : '') . 'clients.active=1';
if ($q) {
$where_clients .= ' AND (';
$where_clients .= ' company LIKE "%'.$this->db->escape_like_str($q).'%" ESCAPE \'!\' OR CONCAT(firstname, " ", lastname) LIKE "%'.$this->db->escape_like_str($q).'%" ESCAPE \'!\' OR email LIKE "%'.$this->db->escape_like_str($q).'%" ESCAPE \'!\'';
$fields = $this->get_custom_fields('contacts', [], false, $playground);
foreach ($fields as $key => $value) {
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'customfieldsvalues as ctable_'.$key.'', db_prefix() . ($playground ? 'playground_' : '') . 'contacts.id = ctable_'.$key.'.relid and ctable_'.$key.'.fieldto="contacts" AND ctable_'.$key.'.fieldid='.$value['id'], 'LEFT');
$where_clients .= ' OR ctable_'.$key.'.value LIKE "%'.$q.'%"';
}
$where_clients .= ') AND '.db_prefix() . ($playground ? 'playground_' : '') . 'clients.active = 1';
}
$this->db->select('contacts.id AS id,clients.*,contacts.*');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'clients', ''.db_prefix() . ($playground ? 'playground_' : '') . 'contacts.userid = '.db_prefix() . ($playground ? 'playground_' : '') . 'clients.userid', 'left');
$data = $this->clients_model->get_contacts('', $where_clients, [], $playground);
// echo $this->db->last_query();
} else if ('ticket' == $type) {
$search = $this->_search_tickets($q, 0, true, $playground);
$data = $search['result'];
} else if ('lead' == $type || 'leads' == $type) {
$search = $this->_search_leads($q, 0, ['junk' => 0,], true, $playground);
$data = $search['result'];
} else if ('invoice' == $type || 'invoices' == $type) {
$search = $this->_search_invoices($q, 0, [], true, $playground);
$data = $search['result'];
} else if ('invoice_items' == $type) {
$fields = $this->get_custom_fields('items', [], false, $playground);
$this->db->select('rate, items.id, description as name, long_description as subtext');
$this->db->like('description', $q);
$this->db->or_like('long_description', $q);
foreach ($fields as $key => $value) {
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'customfieldsvalues as ctable_'.$key.'', db_prefix() . ($playground ? 'playground_' : '') . 'items.id = ctable_'.$key.'.relid and ctable_'.$key.'.fieldto="items_pr" AND ctable_'.$key.'.fieldid='.$value['id'], 'LEFT');
$this->db->or_like('ctable_'.$key.'.value', $q);
}
$items = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'items')->result_array();
foreach ($items as $key => $item) {
$items[$key]['subtext'] = strip_tags(mb_substr($item['subtext'], 0, 200)).'...';
$items[$key]['name'] = '('.app_format_number($item['rate']).') '.$item['name'];
}
$data = $items;
} else if ('project' == $type) {
$where_projects = '';
if ($this->input->post('customer_id')) {
$where_projects .= '(clientid='.$this->input->post('customer_id').' or clientid in (select id from tblleads where client_id='.$this->input->post('customer_id').') )';
}
if ($this->input->post('rel_type')) {
$where_projects .= ' and rel_type="'.$this->input->post('rel_type').'" ';
}
$search = $this->misc_model->search_projects($q, 0, $where_projects, $this->input->post('rel_type'), $playground);
$data = $search['result'];
} else if ('staff' == $type) {
$search = $this->misc_model->search_staff($q, 0, $playground);
$data = $search['result'];
} else if ('tasks' == $type) {
$search = $this->misc_model->search_tasks($q, 0, $playground);
$data = $search['result'];
} else if ('payments' == $type) {
$search = $this->payment_modes_model->search($q, 0, $playground);
$data = $search['result'];
} else if ('proposals' == $type) {
$search = $this->misc_model->search_proposals($q, 0, $playground);
$data = $search['result'];
} else if ('estimates' == $type) {
$search = $this->misc_model->search_estimates($q, 0, $playground);
$data = $search['result'];
} else if ('expenses' == $type) {
$search = $this->misc_model->search_expenses($q, 0, $playground);
$data = $search['result'];
} else if ('creditnotes' == $type) {
$search = $this->misc_model->search_credit_notes($q, 0, $playground);
$data = $search['result'];
} else if ('milestones' == $type) {
$where_milestones = '';
if ($q) {
$where_milestones .= '(name LIKE "%'.$q.'%" OR id LIKE "%'.$q.'%")';
}
$data = $this->misc_model->get_milestones('', $where_milestones, $playground);
}
return $data;
}
/**
* Check for custom fields, update on $_POST
* @param mixed $rel_id the main ID from the table
* @param array $custom_fields all custom fields with id and values
* @return boolean
*/
public function handle_custom_fields_post($rel_id, $custom_fields, $is_cf_items = false, $playground = false)
{
$affectedRows = 0;
foreach ($custom_fields as $key => $fields) {
foreach ($fields as $field_id => $field_value) {
$this->db->where('relid', $rel_id);
$this->db->where('fieldid', $field_id);
$this->db->where('fieldto', ($is_cf_items ? 'items_pr' : $key));
$row = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'customfieldsvalues')->row();
if (!is_array($field_value)) {
$field_value = trim($field_value);
}
// Make necessary checkings for fields
if (!defined('COPY_CUSTOM_FIELDS_LIKE_HANDLE_POST')) {
$this->db->where('id', $field_id);
$field_checker = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'customfields')->row();
if ($field_checker->type == 'date_picker') {
$field_value = to_sql_date($field_value);
} else if ($field_checker->type == 'date_picker_time') {
$field_value = to_sql_date($field_value, true);
} else if ($field_checker->type == 'textarea') {
$field_value = nl2br($field_value);
} else if ($field_checker->type == 'checkbox' || $field_checker->type == 'multiselect') {
if ($field_checker->disalow_client_to_edit == 1 && is_client_logged_in()) {
continue;
}
if (is_array($field_value)) {
$v = 0;
foreach ($field_value as $chk) {
if ($chk == 'cfk_hidden') {
unset($field_value[$v]);
}
$v++;
}
$field_value = implode(', ', $field_value);
}
}
}
if ($row) {
$this->db->where('id', $row->id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'customfieldsvalues', [
'value' => $field_value,
]);
if ($this->db->affected_rows() > 0) {
$affectedRows++;
}
} else {
if ($field_value != '') {
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'customfieldsvalues', [
'relid' => $rel_id,
'fieldid' => $field_id,
'fieldto' => $is_cf_items ? 'items_pr' : $key,
'value' => $field_value,
]);
$insert_id = $this->db->insert_id();
if ($insert_id) {
$affectedRows++;
}
}
}
}
}
if ($affectedRows > 0) {
return true;
}
return false;
}
/**
* Get custom fields
* @param string $field_to
* @param array $where
* @param boolean $exclude_only_admin
* @return array
*/
public function get_custom_fields($field_to, $where = [], $exclude_only_admin = false, $playground = false)
{
$is_admin = is_admin();
$this->db->where('fieldto', $field_to);
if ((is_array($where) && count($where) > 0) || (!is_array($where) && $where != '')) {
$this->db->where($where);
}
if (!$is_admin || $exclude_only_admin == true) {
$this->db->where('only_admin', 0);
}
$this->db->where('active', 1);
$this->db->order_by('field_order', 'asc');
$results = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'customfields')->result_array();
foreach ($results as $key => $result) {
$results[$key]['name'] = _maybe_translate_custom_field_name(e($result['name']), $result['slug']);
}
return $results;
}
/**
* Get custom field value
* @param mixed $rel_id the main ID from the table, e.q. the customer id, invoice id
* @param mixed $field_id_or_slug field id, the custom field ID or custom field slug
* @param string $field_to belongs to e.q leads, customers, staff
* @param string $format format date values
* @return string
*/
public function get_custom_field_value($rel_id, $field_id_or_slug, $field_to, $format = true, $playground = false)
{
$this->db->select(db_prefix() . ($playground ? 'playground_' : '') . 'customfieldsvalues.value,' . db_prefix() . ($playground ? 'playground_' : '') . 'customfields.type');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'customfields', db_prefix() . ($playground ? 'playground_' : '') . 'customfields.id=' . db_prefix() . ($playground ? 'playground_' : '') . 'customfieldsvalues.fieldid');
$this->db->where(db_prefix() . ($playground ? 'playground_' : '') . 'customfieldsvalues.relid', $rel_id);
if (is_numeric($field_id_or_slug)) {
$this->db->where(db_prefix() . ($playground ? 'playground_' : '') . 'customfieldsvalues.fieldid', $field_id_or_slug);
} else {
$this->db->where(db_prefix() . ($playground ? 'playground_' : '') . 'customfields.slug', $field_id_or_slug);
}
$this->db->where(db_prefix() . ($playground ? 'playground_' : '') . 'customfieldsvalues.fieldto', $field_to);
$row = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'customfieldsvalues')->row();
$result = '';
if ($row) {
$result = $row->value;
if ($format == true) {
if ($row->type == 'date_picker') {
$result = _d($result);
} elseif ($row->type == 'date_picker_time') {
$result = _dt($result);
}
}
}
return $result;
}
/**
* Return field where Shown on PDF is allowed
* @return array
*/
public function get_pdf_allowed_fields() {
return $this->pdf_fields;
}
/**
* Return fields where Show on customer portal is allowed
* @return array
*/
public function get_client_portal_allowed_fields() {
return $this->client_portal_fields;
}
/**
* Return fields where are editable in customers area
* @return array
*/
public function get_client_editable_fields() {
return $this->client_editable_fields;
}
}

View File

@@ -0,0 +1,166 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Departments_model extends App_Model {
public function __construct() {
parent::__construct();
}
/**
* @param integer ID (optional)
* @param boolean (optional)
* @return mixed
* Get department object based on passed id if not passed id return array of all departments
* Second parameter is to check if the request is coming from clientarea, so if any departments are hidden from client to exclude
*/
public function get($id = false, $clientarea = false, $playground = false) {
if ($clientarea == true) {
$this->db->where('hidefromclient', 0);
}
if (is_numeric($id)) {
$this->db->where('departmentid', $id);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'departments')->row();
}
$departments = $this->app_object_cache->get('departments');
if (!$departments && !is_array($departments)) {
$departments = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'departments')->result_array();
$this->app_object_cache->add('departments', $departments);
}
return $departments;
}
/**
* @param array $_POST data
* @return integer
* Add new department
*/
public function add($data, $playground = false) {
if (isset($data['hidefromclient'])) {
$data['hidefromclient'] = 1;
} else {
$data['hidefromclient'] = 0;
}
if (!empty($data['password'])) {
$data['password'] = $this->encryption->encrypt($data['password']);
}
if (!isset($data['encryption'])) {
$data['encryption'] = '';
}
if (!isset($data['delete_after_import'])) {
$data['delete_after_import'] = 0;
} else {
$data['delete_after_import'] = 1;
}
$data = hooks()->apply_filters('before_department_added', $data);
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'departments', $data);
$insert_id = $this->db->insert_id();
if ($insert_id) {
hooks()->do_action('after_department_added', $insert_id);
log_activity('New Department Added [' . $data['name'] . ', ID: ' . $insert_id . ']');
}
return $insert_id;
}
/**
* @param array $_POST data
* @param integer ID
* @return boolean
* Update department to database
*/
public function update($data, $id, $playground = false) {
$dep_original = $this->get($id);
if (!$dep_original) {
return false;
}
if (!isset($data['encryption'])) {
$data['encryption'] = '';
}
if (!isset($data['delete_after_import'])) {
$data['delete_after_import'] = 0;
} else {
$data['delete_after_import'] = 1;
}
if ($data['email'] == '') {
$data['email'] = null;
}
if (isset($data['hidefromclient'])) {
$data['hidefromclient'] = 1;
} else {
$data['hidefromclient'] = 0;
}
// Check if not empty $data['password']
// Get original
// Decrypt original
// Compare with $data['password']
// If equal unset
// If not encrypt and save
if (!empty($data['password'])) {
$or_decrypted = $this->encryption->decrypt($dep_original->password);
if ($or_decrypted == $data['password']) {
unset($data['password']);
} else {
$data['password'] = $this->encryption->encrypt($data['password']);
}
}
$data = hooks()->apply_filters('before_department_updated', $data, $id);
$this->db->where('departmentid', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'departments', $data);
if ($this->db->affected_rows() > 0) {
log_activity('Department Updated [Name: ' . $data['name'] . ', ID: ' . $id . ']');
return true;
}
return false;
}
/**
* @param integer ID
* @return mixed
* Delete department from database, if used return array with key referenced
*/
public function delete($id, $playground = false) {
$current = $this->get($id);
if (is_reference_in_table('department', db_prefix() . ($playground ? 'playground_' : '') . 'tickets', $id)) {
return ['referenced' => true, ];
}
hooks()->do_action('before_delete_department', $id);
$this->db->where('departmentid', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'departments');
if ($this->db->affected_rows() > 0) {
log_activity('Department Deleted [ID: ' . $id . ']');
return true;
}
return false;
}
/**
* @param integer ID (option)
* @param boolean (optional)
* @return mixed
* Get departments where staff belongs
* If $onlyids passed return only departmentsID (simple array) if not returns array of all departments
*/
public function get_staff_departments($userid = false, $onlyids = false, $playground = false) {
if ($userid == false) {
$userid = get_staff_user_id();
}
if ($onlyids == false) {
$this->db->select();
} else {
$this->db->select(db_prefix() . ($playground ? 'playground_' : '') . 'staff_departments.departmentid');
}
$this->db->from(db_prefix() . ($playground ? 'playground_' : '') . 'staff_departments');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'departments', db_prefix() . ($playground ? 'playground_' : '') . 'staff_departments.departmentid = ' . db_prefix() . ($playground ? 'playground_' : '') . 'departments.departmentid', 'left');
$this->db->where('staffid', $userid);
$departments = $this->db->get()->result_array();
if ($onlyids == true) {
$departmentsid = [];
foreach ($departments as $department) {
array_push($departmentsid, $department['departmentid']);
}
return $departmentsid;
}
return $departments;
}
}

View File

@@ -0,0 +1,38 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Email_schedule_model extends App_Model {
public function __construct() {
parent::__construct();
}
public function create($rel_id, $rel_type, $data, $playground = false) {
$contacts = $data['contacts'];
if (is_array($contacts)) {
$contacts = implode(',', $contacts);
}
$this->db->insert(($playground ? 'playground_' : '') . 'scheduled_emails', ['rel_type' => $rel_type, 'rel_id' => $rel_id, 'scheduled_at' => $data['scheduled_at'], 'contacts' => $contacts, 'cc' => $data['cc'], 'attach_pdf' => $data['attach_pdf'], 'template' => $data['template'], ]);
}
public function update($id, $data, $playground = false) {
if (is_array($data['contacts'])) {
$data['contacts'] = implode(',', $data['contacts']);
}
$this->db->where('id', $id);
$this->db->update(($playground ? 'playground_' : '') . 'scheduled_emails', $data);
return $this->db->affected_rows() > 0;
}
public function getById($id, $playground = false) {
$this->db->where('id', $id);
return $this->db->get(($playground ? 'playground_' : '') . 'scheduled_emails')->row();
}
public function get($rel_id, $rel_type, $playground = false) {
$this->db->where('rel_id', $rel_id);
$this->db->where('rel_type', $rel_type);
return $this->db->get(($playground ? 'playground_' : '') . 'scheduled_emails')->row();
}
}

View File

@@ -0,0 +1,380 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Estimate_request_model extends App_Model {
public const STATUS_PROCESSING = 2;
public function __construct() {
parent::__construct();
}
public function update_request_assigned($data, $playground = false) {
$this->db->select('assigned');
$this->db->where('id', $data['requestid']);
$_old_assigned = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'estimate_requests')->row();
$this->db->where('id', $data['requestid']);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'estimate_requests', ['assigned' => $data['assigned'], ]);
$this->db->select('assigned');
$this->db->where('id', $data['requestid']);
$_current_assigned = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'estimate_requests')->row();
if ($this->db->affected_rows() > 0) {
if ($_current_assigned != $_old_assigned && $_old_assigned != '') {
hooks()->do_action('estimate_request_assigned_changed', ['estimate_request_id' => $data['requestid'], 'old_staff' => $_old_assigned, 'new_staff' => $_current_assigned, ]);
$this->load->model('staff_model');
log_activity(sprintf('Estimate Request Assigned to %s', $this->staff_model->get_staff_full_name($_current_assigned->assigned, $playground)) . ' [ID: ' . $data['requestid'] . ']');
}
if ($_current_assigned == $_old_assigned) {
return false;
}
$this->assigned_member_notification($data['requestid'], $_current_assigned->assigned, $playground);
return true;
}
return false;
}
public function update_request_status($data, $playground = false) {
$this->db->select('status');
$this->db->where('id', $data['requestid']);
$_old = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'estimate_requests')->row();
$old_status = '';
if ($_old) {
$old_status = $this->get_status($_old->status, $playground);
if ($old_status) {
$old_status = $old_status->name;
}
}
$affectedRows = 0;
$current_status = $this->get_status($data['status'], $playground)->name;
$this->db->where('id', $data['requestid']);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'estimate_requests', ['status' => $data['status'], ]);
$_log_message = '';
if ($this->db->affected_rows() > 0) {
$affectedRows++;
if ($current_status != $old_status && $old_status != '') {
$_log_message = 'not_estimate_request_activity_status_updated';
$this->load->model('staff_model');
$additional_data = serialize([$this->staff_model->get_staff_full_name('', $playground), $old_status, $current_status, ]);
hooks()->do_action('estimate_request_status_changed', ['estimate_request_id' => $data['requestid'], 'old_status' => $old_status, 'new_status' => $current_status, ]);
}
}
if ($affectedRows > 0) {
if ($_log_message == '') {
return true;
}
return true;
}
return false;
}
/**
* Get estimate_request
*
* @param string $id Optional - estimate_requestid
* @param mixed $where
*
* @return mixed
*/
public function get($id = '', $where = [], $playground = false) {
$this->db->select('*,' . db_prefix() . ($playground ? 'playground_' : '') . 'estimate_requests.id,' . db_prefix() . ($playground ? 'playground_' : '') . 'estimate_request_status.name as status_name');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'estimate_request_status', db_prefix() . ($playground ? 'playground_' : '') . 'estimate_request_status.id=' . db_prefix() . ($playground ? 'playground_' : '') . 'estimate_requests.status', 'left');
$this->db->where($where);
if (is_numeric($id)) {
$this->db->where(db_prefix() . ($playground ? 'playground_' : '') . 'estimate_requests.id', $id);
$request = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'estimate_requests')->row();
if ($request) {
if ($request->from_form_id != 0) {
$request->form_data = $this->get_form(['id' => $request->from_form_id, ], $playground);
}
$request->attachments = $this->get_estimate_request_attachments($id, $playground);
}
return $request;
}
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'estimate_requests')->result_array();
}
public function get_forms($playground = false) {
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'estimate_request_forms')->result_array();
}
public function get_form($where, $playground = false) {
$this->db->where($where);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'estimate_request_forms')->row();
}
public function add_form($data) {
$data = $this->_do_estimate_request_form_responsibles($data);
$data['success_submit_msg'] = nl2br($data['success_submit_msg']);
$data['form_key'] = app_generate_hash();
$data['dateadded'] = date('Y-m-d H:i:s');
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'estimate_request_forms', $data);
$insert_id = $this->db->insert_id();
if ($insert_id) {
log_activity('New Estimate Request Form Added [' . $data['name'] . ']');
return $insert_id;
}
return false;
}
public function update_form($id, $data, $playground = false) {
$data = $this->_do_estimate_request_form_responsibles($data, $playground);
$data['success_submit_msg'] = nl2br($data['success_submit_msg']);
$this->db->where('id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'estimate_request_forms', $data);
return $this->db->affected_rows() > 0 ? true : false;
}
public function delete_form($id, $playground = false) {
$this->db->where('id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'estimate_request_forms');
$this->db->where('from_form_id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'estimate_requests', ['from_form_id' => 0, ]);
if ($this->db->affected_rows() > 0) {
log_activity('Estimate Request Form Deleted [' . $id . ']');
return true;
}
return false;
}
private function _do_estimate_request_form_responsibles($data, $playground = false) {
if (isset($data['notify_request_submitted'])) {
$data['notify_request_submitted'] = 1;
} else {
$data['notify_request_submitted'] = 0;
}
if ($data['responsible'] == '') {
$data['responsible'] = 0;
}
if ($data['notify_request_submitted'] != 0) {
if ($data['notify_type'] == 'specific_staff') {
if (isset($data['notify_ids_staff'])) {
$data['notify_ids'] = serialize($data['notify_ids_staff']);
unset($data['notify_ids_staff']);
} else {
$data['notify_ids'] = serialize([]);
unset($data['notify_ids_staff']);
}
if (isset($data['notify_ids_roles'])) {
unset($data['notify_ids_roles']);
}
} else {
if (isset($data['notify_ids_roles'])) {
$data['notify_ids'] = serialize($data['notify_ids_roles']);
unset($data['notify_ids_roles']);
} else {
$data['notify_ids'] = serialize([]);
unset($data['notify_ids_roles']);
}
if (isset($data['notify_ids_staff'])) {
unset($data['notify_ids_staff']);
}
}
} else {
$data['notify_ids'] = serialize([]);
$data['notify_type'] = null;
if (isset($data['notify_ids_staff'])) {
unset($data['notify_ids_staff']);
}
if (isset($data['notify_ids_roles'])) {
unset($data['notify_ids_roles']);
}
}
return $data;
}
/**
* Delete estimate request from database and all connections
*
* @param mixed $id estimate request id
*
* @return bool
*/
public function delete($id, $playground = false) {
$affectedRows = 0;
hooks()->do_action('before_estimate_request_deleted', $id);
$this->db->where('id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'estimate_requests');
if ($this->db->affected_rows() > 0) {
$this->load->model('staff_model');
log_activity('Estimate Request Deleted [Deleted by: ' . $this->staff_model->get_staff_full_name('', $playground) . ', ID: ' . $id . ']');
$attachments = $this->get_estimate_request_attachments($id, $playground);
foreach ($attachments as $attachment) {
$this->delete_estimate_request_attachment($attachment['id'], $playground);
}
// Delete the tags
$this->db->where('rel_type', 'estimate_request');
$this->db->where('rel_id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'taggables');
$affectedRows++;
}
return $affectedRows > 0;
}
// Statuses
/**
* Get estimate_request statuses
*
* @param mixed $id status id
* @param mixed $where
*
* @return mixed object if id passed else array
*/
public function get_status($id = '', $where = [], $playground = false) {
$this->db->where($where);
if (is_numeric($id)) {
$this->db->where('id', $id);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'estimate_request_status')->row();
}
$statuses = $this->app_object_cache->get('estimate-request-all-statuses');
if (!$statuses) {
$this->db->order_by('statusorder', 'asc');
$statuses = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'estimate_request_status')->result_array();
$this->app_object_cache->add('estimate-request-all-statuses', $statuses);
}
return $statuses;
}
/**
* Add new estimate_request status
*
* @param array $data estimate_request status data
*/
public function add_status($data, $playground = false) {
if (isset($data['color']) && $data['color'] == '') {
$data['color'] = hooks()->apply_filters('default_estimate_request_status_color', '#757575');
}
if (!isset($data['statusorder'])) {
$data['statusorder'] = total_rows(db_prefix() . ($playground ? 'playground_' : '') . 'estimate_request_status') + 1;
}
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'estimate_request_status', $data);
$insert_id = $this->db->insert_id();
if ($insert_id) {
log_activity('New Estimate Request Status Added [StatusID: ' . $insert_id . ', Name: ' . $data['name'] . ']');
return $insert_id;
}
return false;
}
/**
* Update statuses
*
* @param mixed $data
* @param mixed $id
*/
public function update_status($data, $id, $playground = false) {
$this->db->where('id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'estimate_request_status', $data);
if ($this->db->affected_rows() > 0) {
log_activity('Estimate Request Status Updated [StatusID: ' . $id . ', Name: ' . $data['name'] . ']');
return true;
}
return false;
}
/**
* Delete estimate_request status from database
*
* @param mixed $id status id
*
* @return bool
*/
public function delete_status($id, $playground = false) {
$current = $this->get_status($id, $playground);
// Check if is already using in table
if (is_reference_in_table('status', db_prefix() . ($playground ? 'playground_' : '') . 'estimate_requests', $id)) {
return ['referenced' => true, ];
}
if ($current->flag != '') {
return ['flag' => true, ];
}
$this->db->where('id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'estimate_request_status');
if ($this->db->affected_rows() > 0) {
log_activity('Estimate Request Status Deleted [StatusID: ' . $id . ']');
return true;
}
return false;
}
/**
* Get estimate_request attachments
*
* @param mixed $id estimate_request id
* @param mixed $attachment_id
* @param mixed $where
*
* @return array
*
* @since Version 1.0.4
*/
public function get_estimate_request_attachments($id = '', $attachment_id = '', $where = [], $playground = false) {
$this->db->where($where);
$idIsHash = !is_numeric($attachment_id) && strlen($attachment_id) == 32;
if (is_numeric($attachment_id) || $idIsHash) {
$this->db->where($idIsHash ? 'attachment_key' : 'id', $attachment_id);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'files')->row();
}
$this->db->where('rel_id', $id);
$this->db->where('rel_type', 'estimate_request');
$this->db->order_by('dateadded', 'DESC');
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'files')->result_array();
}
public function add_attachment_to_database($estimate_request_id, $attachment, $external = false, $playground = false) {
$this->load->model('misc_model');
$this->misc_model->add_attachment_to_database($estimate_request_id, 'estimate_request', $attachment, $external, $playground);
}
/**
* Delete estimate_request attachment
*
* @param mixed $id attachment id
*
* @return bool
*/
public function delete_estimate_request_attachment($id, $playground = false) {
$attachment = $this->get_estimate_request_attachments('', $id, $playground);
$deleted = false;
$this->load->model('misc_model');
if ($attachment) {
if (empty($attachment->external)) {
unlink($this->misc_model->get_upload_path_by_type('estimate_request', $playground) . $attachment->rel_id . '/' . $attachment->file_name);
}
$this->db->where('id', $attachment->id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'files');
if ($this->db->affected_rows() > 0) {
$deleted = true;
log_activity('Estima Request Attachment Deleted [ID: ' . $attachment->rel_id . ']');
}
if (is_dir($this->misc_model->get_upload_path_by_type('estimate_request') . $attachment->rel_id)) {
// Check if no attachments left, so we can delete the folder also
$other_attachments = list_files($this->misc_model->get_upload_path_by_type('estimate_request', $playground) . $attachment->rel_id);
if (count($other_attachments) == 0) {
// okey only index.html so we can delete the folder also
delete_dir($this->misc_model->get_upload_path_by_type('estimate_request', $playground) . $attachment->rel_id);
}
}
}
return $deleted;
}
public function assigned_member_notification($estimate_request_id, $assigned, $playground = false) {
if ((!empty($assigned) && $assigned != 0)) {
$notified = add_notification(['description' => 'estimate_request_assigned_to_staff', 'touserid' => $assigned, 'additional_data' => serialize([]), 'link' => 'estimate_request/view/' . $estimate_request_id, ], $playground);
if ($notified) {
pusher_trigger_notification([$assigned]);
}
$this->db->select('email');
$this->db->where('staffid', $assigned);
$email = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'staff')->row()->email;
send_mail_template('estimate_request_assigned', $estimate_request_id, $email);
return true;
}
return false;
}
public function get_status_by_flag($flag, $playground = false) {
$this->db->where('flag', $flag);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'estimate_request_status')->row();
}
}

File diff suppressed because it is too large Load Diff

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