forked from Business/BinStack
Merge ../BusinessAppTemplate
# Conflicts: # README.md # api.php # index.php # langs/en/core.json # langs/en/titles.json # mobile/index.php # required.php # settings.template.phpmaster
commit
e1837a22fe
@ -0,0 +1,5 @@
|
|||||||
|
# Rewrite for Nextcloud Notes API
|
||||||
|
<IfModule mod_rewrite.c>
|
||||||
|
RewriteEngine on
|
||||||
|
RewriteRule ([a-zA-Z0-9]+) index.php?action=$1 [PT]
|
||||||
|
</IfModule>
|
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
sendJsonResp();
|
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$APIS = [
|
||||||
|
"ping" => [
|
||||||
|
"load" => "ping.php",
|
||||||
|
"vars" => [
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
@ -0,0 +1,144 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build and send a simple JSON response.
|
||||||
|
* @param string $msg A message
|
||||||
|
* @param string $status "OK" or "ERROR"
|
||||||
|
* @param array $data More JSON data
|
||||||
|
*/
|
||||||
|
function sendJsonResp(string $msg = null, string $status = "OK", array $data = null) {
|
||||||
|
$resp = [];
|
||||||
|
if (!is_null($data)) {
|
||||||
|
$resp = $data;
|
||||||
|
}
|
||||||
|
if (!is_null($msg)) {
|
||||||
|
$resp["msg"] = $msg;
|
||||||
|
}
|
||||||
|
$resp["status"] = $status;
|
||||||
|
header("Content-Type: application/json");
|
||||||
|
exit(json_encode($resp));
|
||||||
|
}
|
||||||
|
|
||||||
|
function exitWithJson(array $json) {
|
||||||
|
header("Content-Type: application/json");
|
||||||
|
exit(json_encode($json));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the API key with most of the characters replaced with *s.
|
||||||
|
* @global string $key
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function getCensoredKey() {
|
||||||
|
global $key;
|
||||||
|
$resp = $key;
|
||||||
|
if (strlen($key) > 5) {
|
||||||
|
for ($i = 2; $i < strlen($key) - 2; $i++) {
|
||||||
|
$resp[$i] = "*";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the request is allowed
|
||||||
|
* @global array $VARS
|
||||||
|
* @return bool true if the request should continue, false if the request is bad
|
||||||
|
*/
|
||||||
|
function authenticate(): bool {
|
||||||
|
global $VARS;
|
||||||
|
// HTTP basic auth
|
||||||
|
if (!empty($_SERVER['PHP_AUTH_USER']) && !empty($_SERVER['PHP_AUTH_PW'])) {
|
||||||
|
$user = User::byUsername($_SERVER['PHP_AUTH_USER']);
|
||||||
|
if (!$user->checkPassword($_SERVER['PHP_AUTH_PW'])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Form auth
|
||||||
|
if (empty($VARS['username']) || empty($VARS['password'])) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
$username = $VARS['username'];
|
||||||
|
$password = $VARS['password'];
|
||||||
|
$user = User::byUsername($username);
|
||||||
|
if ($user->exists() !== true || Login::auth($username, $password) !== Login::LOGIN_OK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the User whose credentials were used to make the request.
|
||||||
|
*/
|
||||||
|
function getRequestUser(): User {
|
||||||
|
global $VARS;
|
||||||
|
if (!empty($_SERVER['PHP_AUTH_USER'])) {
|
||||||
|
return User::byUsername($_SERVER['PHP_AUTH_USER']);
|
||||||
|
} else {
|
||||||
|
return User::byUsername($VARS['username']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkVars($vars, $or = false) {
|
||||||
|
global $VARS;
|
||||||
|
$ok = [];
|
||||||
|
foreach ($vars as $key => $val) {
|
||||||
|
if (strpos($key, "OR") === 0) {
|
||||||
|
checkVars($vars[$key], true);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only check type of optional variables if they're set, and don't
|
||||||
|
// mark them as bad if they're not set
|
||||||
|
if (strpos($key, " (optional)") !== false) {
|
||||||
|
$key = str_replace(" (optional)", "", $key);
|
||||||
|
if (empty($VARS[$key])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (empty($VARS[$key])) {
|
||||||
|
$ok[$key] = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($val, "/") === 0) {
|
||||||
|
// regex
|
||||||
|
$ok[$key] = preg_match($val, $VARS[$key]) === 1;
|
||||||
|
} else {
|
||||||
|
$checkmethod = "is_$val";
|
||||||
|
$ok[$key] = !($checkmethod($VARS[$key]) !== true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($or) {
|
||||||
|
$success = false;
|
||||||
|
$bad = "";
|
||||||
|
foreach ($ok as $k => $v) {
|
||||||
|
if ($v) {
|
||||||
|
$success = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
$bad = $k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$success) {
|
||||||
|
http_response_code(400);
|
||||||
|
die("400 Bad request: variable $bad is missing or invalid");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
foreach ($ok as $key => $bool) {
|
||||||
|
if (!$bool) {
|
||||||
|
http_response_code(400);
|
||||||
|
die("400 Bad request: variable $key is missing or invalid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require __DIR__ . '/../required.php';
|
||||||
|
require __DIR__ . '/functions.php';
|
||||||
|
require __DIR__ . '/apisettings.php';
|
||||||
|
|
||||||
|
$VARS = $_GET;
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] != "GET") {
|
||||||
|
$VARS = array_merge($VARS, $_POST);
|
||||||
|
}
|
||||||
|
|
||||||
|
$requestbody = file_get_contents('php://input');
|
||||||
|
$requestjson = json_decode($requestbody, TRUE);
|
||||||
|
if (json_last_error() == JSON_ERROR_NONE) {
|
||||||
|
$VARS = array_merge($VARS, $requestjson);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're not using the old api.php file, allow more flexible requests
|
||||||
|
if (strpos($_SERVER['REQUEST_URI'], "/api.php") === FALSE) {
|
||||||
|
$route = explode("/", substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], "api/") + 4));
|
||||||
|
|
||||||
|
if (count($route) >= 1) {
|
||||||
|
$VARS["action"] = $route[0];
|
||||||
|
}
|
||||||
|
if (count($route) >= 2 && strpos($route[1], "?") !== 0) {
|
||||||
|
for ($i = 1; $i < count($route); $i++) {
|
||||||
|
if (empty($route[$i]) || strpos($route[$i], "=") === false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$key = explode("=", $route[$i], 2)[0];
|
||||||
|
$val = explode("=", $route[$i], 2)[1];
|
||||||
|
$VARS[$key] = $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($route[count($route) - 1], "?") === 0) {
|
||||||
|
$morevars = explode("&", substr($route[count($route) - 1], 1));
|
||||||
|
foreach ($morevars as $var) {
|
||||||
|
$key = explode("=", $var, 2)[0];
|
||||||
|
$val = explode("=", $var, 2)[1];
|
||||||
|
$VARS[$key] = $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!authenticate()) {
|
||||||
|
header('WWW-Authenticate: Basic realm="' . $SETTINGS['site_title'] . '"');
|
||||||
|
header('HTTP/1.1 401 Unauthorized');
|
||||||
|
die("401 Unauthorized: you need to supply valid credentials.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($VARS['action'])) {
|
||||||
|
http_response_code(404);
|
||||||
|
die("404 No action specified");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($APIS[$VARS['action']])) {
|
||||||
|
http_response_code(404);
|
||||||
|
die("404 Action not defined");
|
||||||
|
}
|
||||||
|
|
||||||
|
$APIACTION = $APIS[$VARS["action"]];
|
||||||
|
|
||||||
|
if (!file_exists(__DIR__ . "/actions/" . $APIACTION["load"])) {
|
||||||
|
http_response_code(404);
|
||||||
|
die("404 Action not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($APIACTION["vars"])) {
|
||||||
|
checkVars($APIACTION["vars"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . "/actions/" . $APIACTION["load"];
|
@ -1,114 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
define("STRINGS", [
|
|
||||||
"sign in" => "Sign In",
|
|
||||||
"username" => "Username",
|
|
||||||
"password" => "Password",
|
|
||||||
"continue" => "Continue",
|
|
||||||
"authcode" => "Authentication code",
|
|
||||||
"2fa prompt" => "Enter the six-digit code from your mobile authenticator app.",
|
|
||||||
"2fa incorrect" => "Authentication code incorrect.",
|
|
||||||
"login incorrect" => "Login incorrect.",
|
|
||||||
"login server unavailable" => "Login server unavailable. Try again later or contact technical support.",
|
|
||||||
"account locked" => "This account has been disabled. Contact technical support.",
|
|
||||||
"password expired" => "You must change your password before continuing.",
|
|
||||||
"account terminated" => "Account terminated. Access denied.",
|
|
||||||
"account state error" => "Your account state is not stable. Log out, restart your browser, and try again.",
|
|
||||||
"welcome user" => "Welcome, {user}!",
|
|
||||||
"no permission" => "You do not have permission to access this system.",
|
|
||||||
"sign out" => "Sign out",
|
|
||||||
"settings" => "Settings",
|
|
||||||
"options" => "Options",
|
|
||||||
"404 error" => "404 Error",
|
|
||||||
"page not found" => "Page not found.",
|
|
||||||
"invalid parameters" => "Invalid request parameters.",
|
|
||||||
"login server error" => "The login server returned an error: {arg}",
|
|
||||||
"login server user data error" => "The login server refused to provide account information. Try again or contact technical support.",
|
|
||||||
"captcha error" => "There was a problem with the CAPTCHA (robot test). Try again.",
|
|
||||||
"no edit permission" => "You do not have permission to modify records.",
|
|
||||||
"no access permission" => "You do not have permission to access this system.",
|
|
||||||
"home" => "Home",
|
|
||||||
"more" => "More",
|
|
||||||
"invalid itemid" => "The item ID is invalid.",
|
|
||||||
"invalid category" => "The category is invalid.",
|
|
||||||
"invalid location" => "The location does not exist.",
|
|
||||||
"item saved" => "Item saved.",
|
|
||||||
"item deleted" => "Item deleted.",
|
|
||||||
"total items" => "Total Items",
|
|
||||||
"items" => "Items",
|
|
||||||
"locations" => "Locations",
|
|
||||||
"categories" => "Categories",
|
|
||||||
"actions" => "Actions",
|
|
||||||
"name" => "Name",
|
|
||||||
"category" => "Category",
|
|
||||||
"location" => "Location",
|
|
||||||
"code" => "Code",
|
|
||||||
"code 1" => "Code 1",
|
|
||||||
"code 2" => "Code 2",
|
|
||||||
"qty" => "Qty",
|
|
||||||
"quantity" => "Quantity",
|
|
||||||
"assigned to" => "Assigned To",
|
|
||||||
"view items" => "View Items",
|
|
||||||
"nobody" => "Nobody",
|
|
||||||
"view categories" => "View Categories",
|
|
||||||
"view locations" => "View Locations",
|
|
||||||
"edit" => "Edit",
|
|
||||||
"clone" => "Copy",
|
|
||||||
"item count" => "Items",
|
|
||||||
"delete" => "Delete",
|
|
||||||
"new item" => "New Item",
|
|
||||||
"editing item" => "Editing {item}",
|
|
||||||
"editing category" => "Editing {cat}",
|
|
||||||
"editing location" => "Editing {loc}",
|
|
||||||
"cloning item" => "Copying {oitem} <i class=\"fa fa-angle-right\"></i> {nitem}",
|
|
||||||
"adding item" => "Adding new item",
|
|
||||||
"adding category" => "Adding new category",
|
|
||||||
"invalid catid" => "Invalid category ID.",
|
|
||||||
"category deleted" => "Category deleted.",
|
|
||||||
"category in use" => "Cannot delete category because there is at least one item still in it.",
|
|
||||||
"new category" => "New Category",
|
|
||||||
"category saved" => "Category saved.",
|
|
||||||
"adding location" => "Adding new location",
|
|
||||||
"invalid locid" => "Invalid location ID.",
|
|
||||||
"location deleted" => "Location deleted.",
|
|
||||||
"location in use" => "Cannot delete location because there is at least one item still in it.",
|
|
||||||
"new location" => "New Location",
|
|
||||||
"location saved" => "Location saved.",
|
|
||||||
"name" => "Name",
|
|
||||||
"save" => "Save",
|
|
||||||
"placeholder item name" => "Foo Bar",
|
|
||||||
"placeholder category name" => "Widgets",
|
|
||||||
"placeholder location name" => "Over the Hills",
|
|
||||||
"description" => "Description",
|
|
||||||
"notes" => "Notes",
|
|
||||||
"comments" => "Comments",
|
|
||||||
"minwant" => "Minimum On Hand",
|
|
||||||
"want" => "Need",
|
|
||||||
"field not a number" => "You entered something that isn't a number when a number was expected.",
|
|
||||||
"understocked items" => "Understocked Items",
|
|
||||||
"view understocked" => "View Understocked",
|
|
||||||
"only showing understocked" => "Only showing understocked items.",
|
|
||||||
"show all items" => "Show all items",
|
|
||||||
"missing name" => "You need to enter a name.",
|
|
||||||
"use the dropdowns" => "Whoops, you need to use the category and location autocomplete boxes.",
|
|
||||||
"make categories and locations" => "Please create at least one category and location before adding an item.",
|
|
||||||
"search" => "Search Items",
|
|
||||||
"report export" => "Reports/Export",
|
|
||||||
"report type" => "Report type",
|
|
||||||
"format" => "Format",
|
|
||||||
"generate report" => "Generate report",
|
|
||||||
"choose an option" => "Choose an option",
|
|
||||||
"csv file" => "CSV text file",
|
|
||||||
"ods file" => "ODS spreadsheet",
|
|
||||||
"html file" => "HTML web page",
|
|
||||||
"itemid" => "Item ID",
|
|
||||||
"id" => "ID",
|
|
||||||
"item cost" => "Item cost",
|
|
||||||
"sale price" => "Sale price",
|
|
||||||
"cost" => "Cost",
|
|
||||||
"price" => "Price"
|
|
||||||
]);
|
|
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"You have been logged out.": "You have been logged out.",
|
||||||
|
"Log in again": "Log in again",
|
||||||
|
"login server unavailable": "Login server unavailable. Try again later or contact technical support.",
|
||||||
|
"no access permission": "You do not have permission to access this system.",
|
||||||
|
"Logged in": "Logged in",
|
||||||
|
"Continue": "Continue"
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class AccountHubApi {
|
||||||
|
|
||||||
|
public static function get(string $action, array $data = null, bool $throwex = false) {
|
||||||
|
global $SETTINGS;
|
||||||
|
|
||||||
|
$content = [
|
||||||
|
"action" => $action,
|
||||||
|
"key" => $SETTINGS['accounthub']['key']
|
||||||
|
];
|
||||||
|
if (!is_null($data)) {
|
||||||
|
$content = array_merge($content, $data);
|
||||||
|
}
|
||||||
|
$options = [
|
||||||
|
'http' => [
|
||||||
|
'method' => 'POST',
|
||||||
|
'content' => json_encode($content),
|
||||||
|
'header' => "Content-Type: application/json\r\n" .
|
||||||
|
"Accept: application/json\r\n",
|
||||||
|
"ignore_errors" => true
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$context = stream_context_create($options);
|
||||||
|
$result = file_get_contents($SETTINGS['accounthub']['api'], false, $context);
|
||||||
|
$response = json_decode($result, true);
|
||||||
|
if ($result === false || !AccountHubApi::checkHttpRespCode($http_response_header) || json_last_error() != JSON_ERROR_NONE) {
|
||||||
|
if ($throwex) {
|
||||||
|
throw new Exception($result);
|
||||||
|
} else {
|
||||||
|
sendError($result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function checkHttpRespCode(array $headers): bool {
|
||||||
|
foreach ($headers as $header) {
|
||||||
|
if (preg_match("/HTTP\/[0-9]\.[0-9] [0-9]{3}.*/", $header)) {
|
||||||
|
$respcode = explode(" ", $header)[1] * 1;
|
||||||
|
if ($respcode >= 200 && $respcode < 300) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,275 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class FormBuilder {
|
||||||
|
|
||||||
|
private $items = [];
|
||||||
|
private $hiddenitems = [];
|
||||||
|
private $title = "";
|
||||||
|
private $icon = "";
|
||||||
|
private $buttons = [];
|
||||||
|
private $action = "action.php";
|
||||||
|
private $method = "POST";
|
||||||
|
private $id = "editform";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a form with autogenerated HTML.
|
||||||
|
*
|
||||||
|
* @param string $title Form title/heading
|
||||||
|
* @param string $icon FontAwesone icon next to the title.
|
||||||
|
* @param string $action URL to submit the form to.
|
||||||
|
* @param string $method Form submission method (POST, GET, etc.)
|
||||||
|
*/
|
||||||
|
public function __construct(string $title = "Untitled Form", string $icon = "fas fa-file-alt", string $action = "action.php", string $method = "POST") {
|
||||||
|
$this->title = $title;
|
||||||
|
$this->icon = $icon;
|
||||||
|
$this->action = $action;
|
||||||
|
$this->method = $method;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the title of the form.
|
||||||
|
* @param string $title
|
||||||
|
*/
|
||||||
|
public function setTitle(string $title) {
|
||||||
|
$this->title = $title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the icon for the form.
|
||||||
|
* @param string $icon FontAwesome icon (example: "fas fa-toilet-paper")
|
||||||
|
*/
|
||||||
|
public function setIcon(string $icon) {
|
||||||
|
$this->icon = $icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the URL the form will submit to.
|
||||||
|
* @param string $action
|
||||||
|
*/
|
||||||
|
public function setAction(string $action) {
|
||||||
|
$this->action = $action;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the form submission method (GET, POST, etc)
|
||||||
|
* @param string $method
|
||||||
|
*/
|
||||||
|
public function setMethod(string $method = "POST") {
|
||||||
|
$this->method = $method;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the form ID.
|
||||||
|
* @param string $id
|
||||||
|
*/
|
||||||
|
public function setID(string $id = "editform") {
|
||||||
|
$this->id = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an input to the form.
|
||||||
|
*
|
||||||
|
* @param string $name Element name
|
||||||
|
* @param string $value Element value
|
||||||
|
* @param string $type Input type (text, number, date, select, tel...)
|
||||||
|
* @param bool $required If the element is required for form submission.
|
||||||
|
* @param string $id Element ID
|
||||||
|
* @param array $options Array of [value => text] pairs for a select element
|
||||||
|
* @param string $label Text label to display near the input
|
||||||
|
* @param string $icon FontAwesome icon (example: "fas fa-toilet-paper")
|
||||||
|
* @param int $width Bootstrap column width for the input, out of 12.
|
||||||
|
* @param int $minlength Minimum number of characters for the input.
|
||||||
|
* @param int $maxlength Maximum number of characters for the input.
|
||||||
|
* @param string $pattern Regex pattern for custom client-side validation.
|
||||||
|
* @param string $error Message to show if the input doesn't validate.
|
||||||
|
*/
|
||||||
|
public function addInput(string $name, string $value = "", string $type = "text", bool $required = true, string $id = null, array $options = null, string $label = "", string $icon = "", int $width = 4, int $minlength = 1, int $maxlength = 100, string $pattern = "", string $error = "") {
|
||||||
|
$item = [
|
||||||
|
"name" => $name,
|
||||||
|
"value" => $value,
|
||||||
|
"type" => $type,
|
||||||
|
"required" => $required,
|
||||||
|
"label" => $label,
|
||||||
|
"icon" => $icon,
|
||||||
|
"width" => $width,
|
||||||
|
"minlength" => $minlength,
|
||||||
|
"maxlength" => $maxlength
|
||||||
|
];
|
||||||
|
if (!empty($id)) {
|
||||||
|
$item["id"] = $id;
|
||||||
|
}
|
||||||
|
if (!empty($options) && $type == "select") {
|
||||||
|
$item["options"] = $options;
|
||||||
|
}
|
||||||
|
if (!empty($pattern)) {
|
||||||
|
$item["pattern"] = $pattern;
|
||||||
|
}
|
||||||
|
if (!empty($error)) {
|
||||||
|
$item["error"] = $error;
|
||||||
|
}
|
||||||
|
$this->items[] = $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a button to the form.
|
||||||
|
*
|
||||||
|
* @param string $text Text string to show on the button.
|
||||||
|
* @param string $icon FontAwesome icon to show next to the text.
|
||||||
|
* @param string $href If not null, the button will actually be a hyperlink.
|
||||||
|
* @param string $type Usually "button" or "submit". Ignored if $href is set.
|
||||||
|
* @param string $id The element ID.
|
||||||
|
* @param string $name The element name for the button.
|
||||||
|
* @param string $value The form value for the button. Ignored if $name is null.
|
||||||
|
* @param string $class The CSS classes for the button, if a standard success-colored one isn't right.
|
||||||
|
*/
|
||||||
|
public function addButton(string $text, string $icon = "", string $href = null, string $type = "button", string $id = null, string $name = null, string $value = "", string $class = "btn btn-success") {
|
||||||
|
$button = [
|
||||||
|
"text" => $text,
|
||||||
|
"icon" => $icon,
|
||||||
|
"class" => $class,
|
||||||
|
"type" => $type,
|
||||||
|
"id" => $id,
|
||||||
|
"href" => $href,
|
||||||
|
"name" => $name,
|
||||||
|
"value" => $value
|
||||||
|
];
|
||||||
|
$this->buttons[] = $button;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a hidden input.
|
||||||
|
* @param string $name
|
||||||
|
* @param string $value
|
||||||
|
*/
|
||||||
|
public function addHiddenInput(string $name, string $value) {
|
||||||
|
$this->hiddenitems[$name] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the form HTML.
|
||||||
|
* @param bool $echo If false, returns HTML string instead of outputting it.
|
||||||
|
*/
|
||||||
|
public function generate(bool $echo = true) {
|
||||||
|
$html = <<<HTMLTOP
|
||||||
|
<form action="$this->action" method="$this->method" id="$this->id">
|
||||||
|
<div class="card">
|
||||||
|
<h3 class="card-header d-flex">
|
||||||
|
<div>
|
||||||
|
<i class="$this->icon"></i> $this->title
|
||||||
|
</div>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
HTMLTOP;
|
||||||
|
|
||||||
|
foreach ($this->items as $item) {
|
||||||
|
$required = $item["required"] ? "required" : "";
|
||||||
|
$id = empty($item["id"]) ? "" : "id=\"$item[id]\"";
|
||||||
|
$pattern = empty($item["pattern"]) ? "" : "pattern=\"$item[pattern]\"";
|
||||||
|
if (empty($item['type'])) {
|
||||||
|
$item['type'] = "text";
|
||||||
|
}
|
||||||
|
$itemhtml = "";
|
||||||
|
$itemlabel = "";
|
||||||
|
if ($item['type'] != "checkbox") {
|
||||||
|
$itemlabel = "<label class=\"mb-0\">$item[label]:</label>";
|
||||||
|
}
|
||||||
|
$strippedlabel = strip_tags($item['label']);
|
||||||
|
$itemhtml .= <<<ITEMTOP
|
||||||
|
\n\n <div class="col-12 col-md-$item[width]">
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
$itemlabel
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-prepend">
|
||||||
|
<span class="input-group-text"><i class="$item[icon]"></i></span>
|
||||||
|
</div>
|
||||||
|
ITEMTOP;
|
||||||
|
switch ($item['type']) {
|
||||||
|
case "select":
|
||||||
|
$itemhtml .= <<<SELECT
|
||||||
|
\n <select class="form-control" name="$item[name]" aria-label="$strippedlabel" $required>
|
||||||
|
SELECT;
|
||||||
|
foreach ($item['options'] as $value => $label) {
|
||||||
|
$selected = "";
|
||||||
|
if (!empty($item['value']) && $value == $item['value']) {
|
||||||
|
$selected = " selected";
|
||||||
|
}
|
||||||
|
$itemhtml .= "\n <option value=\"$value\"$selected>$label</option>";
|
||||||
|
}
|
||||||
|
$itemhtml .= "\n </select>";
|
||||||
|
break;
|
||||||
|
case "checkbox":
|
||||||
|
$itemhtml .= <<<CHECKBOX
|
||||||
|
\n <div class="form-group form-check">
|
||||||
|
<input type="checkbox" name="$item[name]" $id class="form-check-input" value="$item[value]" $required aria-label="$strippedlabel">
|
||||||
|
<label class="form-check-label">$item[label]</label>
|
||||||
|
</div>
|
||||||
|
CHECKBOX;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$itemhtml .= <<<INPUT
|
||||||
|
\n <input type="$item[type]" name="$item[name]" $id class="form-control" aria-label="$strippedlabel" minlength="$item[minlength]" maxlength="$item[maxlength]" $pattern value="$item[value]" $required />
|
||||||
|
INPUT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($item["error"])) {
|
||||||
|
$itemhtml .= <<<ERROR
|
||||||
|
\n <div class="invalid-feedback">
|
||||||
|
$item[error]
|
||||||
|
</div>
|
||||||
|
ERROR;
|
||||||
|
}
|
||||||
|
$itemhtml .= <<<ITEMBOTTOM
|
||||||
|
\n </div>
|
||||||
|
</div>
|
||||||
|
</div>\n
|
||||||
|
ITEMBOTTOM;
|
||||||
|
$html .= $itemhtml;
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= <<<HTMLBOTTOM
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
HTMLBOTTOM;
|
||||||
|
|
||||||
|
if (!empty($this->buttons)) {
|
||||||
|
$html .= "\n <div class=\"card-footer\">";
|
||||||
|
foreach ($this->buttons as $btn) {
|
||||||
|
$btnhtml = "";
|
||||||
|
$inner = "<i class=\"$btn[icon]\"></i> $btn[text]";
|
||||||
|
$id = empty($btn['id']) ? "" : "id=\"$btn[id]\"";
|
||||||
|
if (!empty($btn['href'])) {
|
||||||
|
$btnhtml = "<a href=\"$btn[href]\" class=\"$btn[class]\" $id>$inner</a>";
|
||||||
|
} else {
|
||||||
|
$name = empty($btn['name']) ? "" : "name=\"$btn[name]\"";
|
||||||
|
$value = (!empty($btn['name']) && !empty($btn['value'])) ? "value=\"$btn[value]\"" : "";
|
||||||
|
$btnhtml = "<button type=\"$btn[type]\" class=\"$btn[class]\" $id $name $value>$inner</button>";
|
||||||
|
}
|
||||||
|
$html .= "\n $btnhtml";
|
||||||
|
}
|
||||||
|
$html .= "\n </div>";
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= "\n </div>";
|
||||||
|
foreach ($this->hiddenitems as $name => $value) {
|
||||||
|
$value = htmlentities($value);
|
||||||
|
$html .= "\n <input type=\"hidden\" name=\"$name\" value=\"$value\" />";
|
||||||
|
}
|
||||||
|
$html .= "\n</form>\n";
|
||||||
|
|
||||||
|
if ($echo) {
|
||||||
|
echo $html;
|
||||||
|
}
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,52 +1,56 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
// Whether to show debugging data in output.
|
|
||||||
// DO NOT SET TO TRUE IN PRODUCTION!!!
|
// Settings for the app.
|
||||||
define("DEBUG", false);
|
// Copy to settings.php and customize.
|
||||||
|
|
||||||
|
$SETTINGS = [
|
||||||
|
// Whether to output debugging info like PHP notices, warnings,
|
||||||
|
// and stacktraces.
|
||||||
|
// Turning this on in production is a security risk and can sometimes break
|
||||||
|
// things, such as JSON output where extra content is not expected.
|
||||||
|
"debug" => false,
|
||||||
// Database connection settings
|
// Database connection settings
|
||||||
// See http://medoo.in/api/new for info
|
// See http://medoo.in/api/new for info
|
||||||
define("DB_TYPE", "mysql");
|
"database" => [
|
||||||
define("DB_NAME", "inventory");
|
"type" => "mysql",
|
||||||
define("DB_SERVER", "localhost");
|
"name" => "binstack",
|
||||||
define("DB_USER", "inventory");
|
"server" => "localhost",
|
||||||
define("DB_PASS", "");
|
"user" => "app",
|
||||||
define("DB_CHARSET", "utf8");
|
"password" => "",
|
||||||
|
"charset" => "utf8"
|
||||||
|
],
|
||||||
// Name of the app.
|
// Name of the app.
|
||||||
define("SITE_TITLE", "BinStack");
|
"site_title" => "BinStack",
|
||||||
|
// Settings for connecting to the AccountHub server.
|
||||||
|
"accounthub" => [
|
||||||
// URL of the AccountHub API endpoint
|
// URL for the API endpoint
|
||||||
define("PORTAL_API", "http://localhost/accounthub/api.php");
|
"api" => "http://localhost/accounthub/api/",
|
||||||
// URL of the AccountHub home page
|
// URL of the home page
|
||||||
define("PORTAL_URL", "http://localhost/accounthub/home.php");
|
"home" => "http://localhost/accounthub/home.php",
|
||||||
// AccountHub API Key
|
// API key
|
||||||
define("PORTAL_KEY", "123");
|
"key" => "123"
|
||||||
|
],
|
||||||
|
"file_upload_path" => __DIR__ . '/images',
|
||||||
|
// List of required user permissions to access this app.
|
||||||
|
"permissions" => [
|
||||||
|
"INV_VIEW"
|
||||||
|
],
|
||||||
// For supported values, see http://php.net/manual/en/timezones.php
|
// For supported values, see http://php.net/manual/en/timezones.php
|
||||||
define("TIMEZONE", "America/Denver");
|
"timezone" => "America/Denver",
|
||||||
|
// Language to use for localization. See langs folder to add a language.
|
||||||
// Base URL for site links.
|
"language" => "en",
|
||||||
define('URL', '.');
|
// Shown in the footer of all the pages.
|
||||||
|
"footer_text" => "",
|
||||||
// Folder for item images
|
// Also shown in the footer, but with "Copyright <current_year>" in front.
|
||||||
// If in the webroot, verify that the contents of the folder are not accessible
|
"copyright" => "Netsyms Technologies",
|
||||||
// from a client (web browser).
|
// Base URL for building links relative to the location of the app.
|
||||||
define('FILE_UPLOAD_PATH', __DIR__ . '/images');
|
// Only used when there's no good context for the path.
|
||||||
|
// The default is almost definitely fine.
|
||||||
// Use Captcheck on login screen
|
"url" => "."
|
||||||
// https://captcheck.netsyms.com
|
];
|
||||||
define("CAPTCHA_ENABLED", FALSE);
|
|
||||||
define('CAPTCHA_SERVER', 'https://captcheck.netsyms.com');
|
|
||||||
|
|
||||||
// See lang folder for language options
|
|
||||||
define('LANGUAGE', "en_us");
|
|
||||||
|
|
||||||
|
|
||||||
define("FOOTER_TEXT", "");
|
|
||||||
define("COPYRIGHT_NAME", "Netsyms Technologies");
|
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
.banner-image {
|
|
||||||
max-height: 100px;
|
|
||||||
margin: 2em auto;
|
|
||||||
border: 1px solid grey;
|
|
||||||
border-radius: 15%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
margin-top: 10em;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
/*!
|
/*!
|
||||||
* Font Awesome Free 5.3.1 by @fontawesome - https://fontawesome.com
|
* Font Awesome Free 5.6.0 by @fontawesome - https://fontawesome.com
|
||||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||||
*/
|
*/
|
||||||
.svg-inline--fa,svg:not(:root).svg-inline--fa{overflow:visible}.svg-inline--fa{display:inline-block;font-size:inherit;height:1em;vertical-align:-.125em}.svg-inline--fa.fa-lg{vertical-align:-.225em}.svg-inline--fa.fa-w-1{width:.0625em}.svg-inline--fa.fa-w-2{width:.125em}.svg-inline--fa.fa-w-3{width:.1875em}.svg-inline--fa.fa-w-4{width:.25em}.svg-inline--fa.fa-w-5{width:.3125em}.svg-inline--fa.fa-w-6{width:.375em}.svg-inline--fa.fa-w-7{width:.4375em}.svg-inline--fa.fa-w-8{width:.5em}.svg-inline--fa.fa-w-9{width:.5625em}.svg-inline--fa.fa-w-10{width:.625em}.svg-inline--fa.fa-w-11{width:.6875em}.svg-inline--fa.fa-w-12{width:.75em}.svg-inline--fa.fa-w-13{width:.8125em}.svg-inline--fa.fa-w-14{width:.875em}.svg-inline--fa.fa-w-15{width:.9375em}.svg-inline--fa.fa-w-16{width:1em}.svg-inline--fa.fa-w-17{width:1.0625em}.svg-inline--fa.fa-w-18{width:1.125em}.svg-inline--fa.fa-w-19{width:1.1875em}.svg-inline--fa.fa-w-20{width:1.25em}.svg-inline--fa.fa-pull-left{margin-right:.3em;width:auto}.svg-inline--fa.fa-pull-right{margin-left:.3em;width:auto}.svg-inline--fa.fa-border{height:1.5em}.svg-inline--fa.fa-li{width:2em}.svg-inline--fa.fa-fw{width:1.25em}.fa-layers svg.svg-inline--fa{bottom:0;left:0;margin:auto;position:absolute;right:0;top:0}.fa-layers{display:inline-block;height:1em;position:relative;text-align:center;vertical-align:-.125em;width:1em}.fa-layers svg.svg-inline--fa{transform-origin:center center}.fa-layers-counter,.fa-layers-text{display:inline-block;position:absolute;text-align:center}.fa-layers-text{left:50%;top:50%;transform:translate(-50%,-50%);transform-origin:center center}.fa-layers-counter{background-color:#ff253a;border-radius:1em;box-sizing:border-box;color:#fff;height:1.5em;line-height:1;max-width:5em;min-width:1.5em;overflow:hidden;padding:.25em;right:0;text-overflow:ellipsis;top:0;transform:scale(.25);transform-origin:top right}.fa-layers-bottom-right{bottom:0;right:0;top:auto;transform:scale(.25);transform-origin:bottom right}.fa-layers-bottom-left{bottom:0;left:0;right:auto;top:auto;transform:scale(.25);transform-origin:bottom left}.fa-layers-top-right{right:0;top:0;transform:scale(.25);transform-origin:top right}.fa-layers-top-left{left:0;right:auto;top:0;transform:scale(.25);transform-origin:top left}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{animation:fa-spin 2s infinite linear}.fa-pulse{animation:fa-spin 1s infinite steps(8)}@keyframes fa-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";transform:scaleX(-1)}.fa-flip-vertical{transform:scaleY(-1)}.fa-flip-horizontal.fa-flip-vertical,.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.fa-flip-horizontal.fa-flip-vertical{transform:scale(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{-webkit-filter:none;filter:none}.fa-stack{display:inline-block;height:2em;position:relative;width:2em}.fa-stack-1x,.fa-stack-2x{bottom:0;left:0;margin:auto;position:absolute;right:0;top:0}.svg-inline--fa.fa-stack-1x{height:1em;width:1em}.svg-inline--fa.fa-stack-2x{height:2em;width:2em}.fa-inverse{color:#fff}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}
|
.svg-inline--fa,svg:not(:root).svg-inline--fa{overflow:visible}.svg-inline--fa{display:inline-block;font-size:inherit;height:1em;vertical-align:-.125em}.svg-inline--fa.fa-lg{vertical-align:-.225em}.svg-inline--fa.fa-w-1{width:.0625em}.svg-inline--fa.fa-w-2{width:.125em}.svg-inline--fa.fa-w-3{width:.1875em}.svg-inline--fa.fa-w-4{width:.25em}.svg-inline--fa.fa-w-5{width:.3125em}.svg-inline--fa.fa-w-6{width:.375em}.svg-inline--fa.fa-w-7{width:.4375em}.svg-inline--fa.fa-w-8{width:.5em}.svg-inline--fa.fa-w-9{width:.5625em}.svg-inline--fa.fa-w-10{width:.625em}.svg-inline--fa.fa-w-11{width:.6875em}.svg-inline--fa.fa-w-12{width:.75em}.svg-inline--fa.fa-w-13{width:.8125em}.svg-inline--fa.fa-w-14{width:.875em}.svg-inline--fa.fa-w-15{width:.9375em}.svg-inline--fa.fa-w-16{width:1em}.svg-inline--fa.fa-w-17{width:1.0625em}.svg-inline--fa.fa-w-18{width:1.125em}.svg-inline--fa.fa-w-19{width:1.1875em}.svg-inline--fa.fa-w-20{width:1.25em}.svg-inline--fa.fa-pull-left{margin-right:.3em;width:auto}.svg-inline--fa.fa-pull-right{margin-left:.3em;width:auto}.svg-inline--fa.fa-border{height:1.5em}.svg-inline--fa.fa-li{width:2em}.svg-inline--fa.fa-fw{width:1.25em}.fa-layers svg.svg-inline--fa{bottom:0;left:0;margin:auto;position:absolute;right:0;top:0}.fa-layers{display:inline-block;height:1em;position:relative;text-align:center;vertical-align:-.125em;width:1em}.fa-layers svg.svg-inline--fa{transform-origin:center center}.fa-layers-counter,.fa-layers-text{display:inline-block;position:absolute;text-align:center}.fa-layers-text{left:50%;top:50%;transform:translate(-50%,-50%);transform-origin:center center}.fa-layers-counter{background-color:#ff253a;border-radius:1em;box-sizing:border-box;color:#fff;height:1.5em;line-height:1;max-width:5em;min-width:1.5em;overflow:hidden;padding:.25em;right:0;text-overflow:ellipsis;top:0;transform:scale(.25);transform-origin:top right}.fa-layers-bottom-right{bottom:0;right:0;top:auto;transform:scale(.25);transform-origin:bottom right}.fa-layers-bottom-left{bottom:0;left:0;right:auto;top:auto;transform:scale(.25);transform-origin:bottom left}.fa-layers-top-right{right:0;top:0;transform:scale(.25);transform-origin:top right}.fa-layers-top-left{left:0;right:auto;top:0;transform:scale(.25);transform-origin:top left}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{animation:fa-spin 2s infinite linear}.fa-pulse{animation:fa-spin 1s infinite steps(8)}@keyframes fa-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";transform:scaleX(-1)}.fa-flip-vertical{transform:scaleY(-1)}.fa-flip-horizontal.fa-flip-vertical,.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.fa-flip-horizontal.fa-flip-vertical{transform:scale(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{display:inline-block;height:2em;position:relative;width:2.5em}.fa-stack-1x,.fa-stack-2x{bottom:0;left:0;margin:auto;position:absolute;right:0;top:0}.svg-inline--fa.fa-stack-1x{height:1em;width:1.25em}.svg-inline--fa.fa-stack-2x{height:2em;width:2.5em}.fa-inverse{color:#fff}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
$("#savebtn").click(function (event) {
|
||||||
|
var form = $("#sampleform");
|
||||||
|
|
||||||
|
if (form[0].checkValidity() === false) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
form.addClass('was-validated');
|
||||||
|
});
|
Loading…
Reference in New Issue