Group Creation
This commit is contained in:
parent
d8846ff132
commit
d80de63765
@ -2,8 +2,21 @@
|
||||
|
||||
namespace Api {
|
||||
|
||||
use Driver\SQL\Condition\Compare;
|
||||
|
||||
class GroupsAPI extends Request {
|
||||
|
||||
protected function groupExists($name) {
|
||||
$sql = $this->user->getSQL();
|
||||
$res = $sql->select($sql->count())
|
||||
->from("Group")
|
||||
->where(new Compare("name", $name))
|
||||
->execute();
|
||||
|
||||
$this->success = ($res !== FALSE);
|
||||
$this->lastError = $sql->getLastError();
|
||||
return $this->success && $res[0]["count"] > 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -12,6 +25,7 @@ namespace Api\Groups {
|
||||
|
||||
use Api\GroupsAPI;
|
||||
use Api\Parameter\Parameter;
|
||||
use Api\Parameter\StringType;
|
||||
|
||||
class Fetch extends GroupsAPI {
|
||||
|
||||
@ -96,4 +110,53 @@ namespace Api\Groups {
|
||||
}
|
||||
}
|
||||
|
||||
class Create extends GroupsAPI {
|
||||
public function __construct($user, $externalCall = false) {
|
||||
parent::__construct($user, $externalCall, array(
|
||||
'name' => new StringType('name', 32),
|
||||
'color' => new StringType('color', 10),
|
||||
));
|
||||
|
||||
$this->loginRequired = true;
|
||||
$this->requiredGroup = array(USER_GROUP_ADMIN);
|
||||
}
|
||||
|
||||
public function execute($values = array()) {
|
||||
if (!parent::execute($values)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$name = $this->getParam("name");
|
||||
if (preg_match("/^[a-zA-Z][a-zA-Z0-9_-]*$/", $name) !== 1) {
|
||||
return $this->createError("Invalid name");
|
||||
}
|
||||
|
||||
$color = $this->getParam("color");
|
||||
if (preg_match("/^#[a-fA-F0-9]{3,6}$/", $color) !== 1) {
|
||||
return $this->createError("Invalid color");
|
||||
}
|
||||
|
||||
$exists = $this->groupExists($name);
|
||||
if (!$this->success) {
|
||||
return false;
|
||||
} else if ($exists) {
|
||||
return $this->createError("A group with this name already exists");
|
||||
}
|
||||
|
||||
$sql = $this->user->getSQL();
|
||||
$res = $sql->insert("Group", array("name", "color"))
|
||||
->addRow($name, $color)
|
||||
->returning("uid")
|
||||
->execute();
|
||||
|
||||
$this->success = ($res !== FALSE);
|
||||
$this->lastError = $sql->getLastError();
|
||||
|
||||
if ($this->success) {
|
||||
$this->result["uid"] = $sql->getLastInsertId();
|
||||
}
|
||||
|
||||
return $this->success;
|
||||
}
|
||||
}
|
||||
}
|
@ -118,7 +118,7 @@ namespace Api\User {
|
||||
public function __construct($user, $externalCall = false) {
|
||||
parent::__construct($user, $externalCall, array(
|
||||
'username' => new StringType('username', 32),
|
||||
'email' => new StringType('email', 64, true),
|
||||
'email' => new Parameter('email', Parameter::TYPE_EMAIL, true, NULL),
|
||||
'password' => new StringType('password'),
|
||||
'confirmPassword' => new StringType('confirmPassword'),
|
||||
));
|
||||
@ -501,7 +501,7 @@ If the invitation was not intended, you can simply ignore this email.<br><br><a
|
||||
public function __construct($user, $externalCall = false) {
|
||||
parent::__construct($user, $externalCall, array(
|
||||
"username" => new StringType("username", 32),
|
||||
"email" => new StringType("email", 64),
|
||||
'email' => new Parameter('email', Parameter::TYPE_EMAIL),
|
||||
"password" => new StringType("password"),
|
||||
"confirmPassword" => new StringType("confirmPassword"),
|
||||
));
|
||||
@ -608,7 +608,7 @@ If the registration was not intended, you can simply ignore this email.<br><br><
|
||||
parent::__construct($user, $externalCall, array(
|
||||
'id' => new Parameter('id', Parameter::TYPE_INT),
|
||||
'username' => new StringType('username', 32, true, NULL),
|
||||
'email' => new StringType('email', 64, true, NULL),
|
||||
'email' => new Parameter('email', Parameter::TYPE_EMAIL, true, NULL),
|
||||
'password' => new StringType('password', -1, true, NULL),
|
||||
'groups' => new Parameter('groups', Parameter::TYPE_ARRAY, true, NULL),
|
||||
));
|
||||
|
18
js/admin.min.js
vendored
18
js/admin.min.js
vendored
File diff suppressed because one or more lines are too long
@ -80,6 +80,10 @@ export default class API {
|
||||
}
|
||||
|
||||
async saveRoutes(routes) {
|
||||
return this.apiCall("routes/save", { "routes": routes });
|
||||
return this.apiCall("routes/save", { routes: routes });
|
||||
}
|
||||
|
||||
async createGroup(name, color) {
|
||||
return this.apiCall("groups/create", { name: name, color: color });
|
||||
}
|
||||
};
|
@ -17,6 +17,7 @@ import PageOverview from "./views/pages";
|
||||
import HelpPage from "./views/help";
|
||||
import Footer from "./footer";
|
||||
import EditUser from "./views/edituser";
|
||||
import CreateGroup from "./views/addgroup";
|
||||
|
||||
class AdminDashboard extends React.Component {
|
||||
|
||||
@ -89,6 +90,7 @@ class AdminDashboard extends React.Component {
|
||||
let newProps = {...props, ...this.controlObj};
|
||||
return <EditUser {...newProps} />
|
||||
}}/>
|
||||
<Route path={"/admin/group/add"}><CreateGroup {...this.controlObj} /></Route>
|
||||
<Route path={"/admin/logs"}><Logs {...this.controlObj} /></Route>
|
||||
<Route path={"/admin/pages"}><PageOverview {...this.controlObj} /></Route>
|
||||
<Route path={"/admin/help"}><HelpPage {...this.controlObj} /></Route>
|
||||
|
115
src/src/views/addgroup.js
Normal file
115
src/src/views/addgroup.js
Normal file
@ -0,0 +1,115 @@
|
||||
import Alert from "../elements/alert";
|
||||
import {Link} from "react-router-dom";
|
||||
import * as React from "react";
|
||||
import Icon from "../elements/icon";
|
||||
import ReactTooltip from "react-tooltip";
|
||||
|
||||
export default class CreateGroup extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
alerts: [],
|
||||
isSubmitting: false,
|
||||
name: "",
|
||||
color: "#123456"
|
||||
};
|
||||
|
||||
this.parent = {
|
||||
api: props.api,
|
||||
};
|
||||
}
|
||||
|
||||
removeAlert(i) {
|
||||
if (i >= 0 && i < this.state.alerts.length) {
|
||||
let alerts = this.state.alerts.slice();
|
||||
alerts.splice(i, 1);
|
||||
this.setState({...this.state, alerts: alerts});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
let alerts = [];
|
||||
for (let i = 0; i < this.state.alerts.length; i++) {
|
||||
alerts.push(<Alert key={"error-" + i} onClose={() => this.removeAlert(i)} {...this.state.alerts[i]}/>)
|
||||
}
|
||||
|
||||
return <>
|
||||
<div className="content-header">
|
||||
<div className="container-fluid">
|
||||
<div className="row mb-2">
|
||||
<div className="col-sm-6">
|
||||
<h1 className="m-0 text-dark">Create a new user</h1>
|
||||
</div>
|
||||
<div className="col-sm-6">
|
||||
<ol className="breadcrumb float-sm-right">
|
||||
<li className="breadcrumb-item"><Link to={"/admin/dashboard"}>Home</Link></li>
|
||||
<li className="breadcrumb-item"><Link to={"/admin/users"}>Users</Link></li>
|
||||
<li className="breadcrumb-item active">Add User</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={"content"}>
|
||||
<div className={"row"}>
|
||||
<div className={"col-lg-6 pl-5 pr-5"}>
|
||||
{alerts}
|
||||
<form role={"form"} onSubmit={(e) => this.submitForm(e)}>
|
||||
<div className={"form-group"}>
|
||||
<label htmlFor={"name"}>Group Name</label>
|
||||
<input type={"text"} className={"form-control"} placeholder={"Name"}
|
||||
name={"name"} id={"name"} maxLength={32} value={this.state.name}
|
||||
onChange={this.onChangeInput.bind(this)}/>
|
||||
</div>
|
||||
|
||||
{/* TODO: add color picker */}
|
||||
<div className={"form-group"}>
|
||||
<label htmlFor={"color"}>Color</label>
|
||||
<input type={"text"} className={"form-control"} placeholder={"Color"}
|
||||
id={"color"} name={"color"} maxLength={64} value={this.state.color}
|
||||
onChange={this.onChangeInput.bind(this)}/>
|
||||
</div>
|
||||
|
||||
<Link to={"/admin/users"} className={"btn btn-info mt-2 mr-2"}>
|
||||
<Icon icon={"arrow-left"}/>
|
||||
Back
|
||||
</Link>
|
||||
{ this.state.isSubmitting
|
||||
? <button type={"submit"} className={"btn btn-primary mt-2"} disabled>Loading… <Icon icon={"circle-notch"} /></button>
|
||||
: <button type={"submit"} className={"btn btn-primary mt-2"}>Submit</button>
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ReactTooltip/>
|
||||
</>;
|
||||
}
|
||||
|
||||
onChangeInput(event) {
|
||||
const target = event.target;
|
||||
const value = target.value;
|
||||
const name = target.name;
|
||||
this.setState({ ...this.state, [name]: value });
|
||||
}
|
||||
|
||||
submitForm(e) {
|
||||
e.preventDefault();
|
||||
const name = this.state.name;
|
||||
const color = this.state.color;
|
||||
this.setState({ ...this.state, isSubmitting: true });
|
||||
this.parent.api.createGroup(name, color).then((res) => {
|
||||
let alerts = this.state.alerts.slice();
|
||||
if (res.success) {
|
||||
alerts.push({message: "Group was successfully created", title: "Success!", type: "success"});
|
||||
this.setState({ ...this.state, name: "", color: "", alerts: alerts, isSubmitting: false });
|
||||
} else {
|
||||
alerts.push({message: res.msg, title: "Error creating Group", type: "danger"});
|
||||
this.setState({ ...this.state, name: "", color: "", alerts: alerts, isSubmitting: false });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -327,7 +327,7 @@ export default class UserOverview extends React.Component {
|
||||
<div className={"card-header border-0"}>
|
||||
<h3 className={"card-title"}>Groups</h3>
|
||||
<div className={"card-tools"}>
|
||||
<Link href={"#"} className={"btn btn-tool btn-sm"} to={"/admin/users/addgroup"} >
|
||||
<Link href={"#"} className={"btn btn-tool btn-sm"} to={"/admin/group/add"} >
|
||||
<Icon icon={"plus"}/>
|
||||
</Link>
|
||||
<a href={"#"} className={"btn btn-tool btn-sm"} onClick={() => this.fetchGroups()}>
|
||||
|
Loading…
Reference in New Issue
Block a user