Bug Fixes + IN Operator + User deletion
This commit is contained in:
parent
e6361a3c91
commit
d8846ff132
@ -110,6 +110,7 @@ namespace Api\User {
|
||||
use Api\UserAPI;
|
||||
use DateTime;
|
||||
use Driver\SQL\Condition\Compare;
|
||||
use Driver\SQL\Condition\CondIn;
|
||||
use Objects\User;
|
||||
|
||||
class Create extends UserAPI {
|
||||
@ -190,6 +191,28 @@ namespace Api\User {
|
||||
return $this->success;
|
||||
}
|
||||
|
||||
private function selectIds($page, $count) {
|
||||
$sql = $this->user->getSQL();
|
||||
$res = $sql->select("User.uid")
|
||||
->from("User")
|
||||
->limit($count)
|
||||
->offset(($page - 1) * $count)
|
||||
->orderBy("User.uid")
|
||||
->ascending()
|
||||
->execute();
|
||||
|
||||
$this->success = ($res !== NULL);
|
||||
$this->lastError = $sql->getLastError();
|
||||
|
||||
if ($this->success) {
|
||||
$ids = array();
|
||||
foreach($res as $row) $ids[] = $row["uid"];
|
||||
return $ids;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function execute($values = array()) {
|
||||
if (!parent::execute($values)) {
|
||||
return false;
|
||||
@ -209,16 +232,18 @@ namespace Api\User {
|
||||
return false;
|
||||
}
|
||||
|
||||
$userIds = $this->selectIds($page, $count);
|
||||
if ($userIds === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$sql = $this->user->getSQL();
|
||||
$res = $sql->select("User.uid as userId", "User.name", "User.email", "User.registered_at",
|
||||
"Group.uid as groupId", "Group.name as groupName", "Group.color as groupColor")
|
||||
->from("User")
|
||||
->leftJoin("UserGroup", "User.uid", "UserGroup.user_id")
|
||||
->leftJoin("Group", "Group.uid", "UserGroup.group_id")
|
||||
->orderBy("User.uid")
|
||||
->ascending()
|
||||
->limit($count)
|
||||
->offset(($page - 1) * $count)
|
||||
->where(new CondIn("User.uid", $userIds))
|
||||
->execute();
|
||||
|
||||
$this->success = ($res !== FALSE);
|
||||
@ -289,6 +314,7 @@ namespace Api\User {
|
||||
);
|
||||
|
||||
foreach($user as $row) {
|
||||
if (!is_null($row["groupId"])) {
|
||||
$this->result["user"]["groups"][$row["groupId"]] = array(
|
||||
"name" => $row["groupName"],
|
||||
"color" => $row["groupColor"],
|
||||
@ -296,6 +322,7 @@ namespace Api\User {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->success;
|
||||
}
|
||||
@ -629,8 +656,8 @@ If the registration was not intended, you can simply ignore this email.<br><br><
|
||||
}
|
||||
|
||||
// Check for duplicate username, email
|
||||
$usernameChanged = !is_null($username) ? strcasecmp($username, $this->user->getUsername()) !== 0 : false;
|
||||
$emailChanged = !is_null($email) ? strcasecmp($email, $this->user->getEmail()) !== 0 : false;
|
||||
$usernameChanged = !is_null($username) ? strcasecmp($username, $user[0]["name"]) !== 0 : false;
|
||||
$emailChanged = !is_null($email) ? strcasecmp($email, $user[0]["email"]) !== 0 : false;
|
||||
if($usernameChanged || $emailChanged) {
|
||||
if (!$this->userExists($usernameChanged ? $username : NULL, $emailChanged ? $email : NULL)) {
|
||||
return false;
|
||||
@ -668,4 +695,41 @@ If the registration was not intended, you can simply ignore this email.<br><br><
|
||||
return $this->success;
|
||||
}
|
||||
}
|
||||
|
||||
class Delete extends UserAPI {
|
||||
|
||||
public function __construct(User $user, bool $externalCall) {
|
||||
parent::__construct($user, $externalCall, array(
|
||||
'id' => new Parameter('id', Parameter::TYPE_INT)
|
||||
));
|
||||
|
||||
$this->requiredGroup = array(USER_GROUP_ADMIN);
|
||||
$this->loginRequired = true;
|
||||
}
|
||||
|
||||
public function execute($values = array()) {
|
||||
if (!parent::execute($values)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$id = $this->getParam("id");
|
||||
if ($id === $this->user->getId()) {
|
||||
return $this->createError("You cannot delete your own user.");
|
||||
}
|
||||
|
||||
$user = $this->getUser($id);
|
||||
if ($this->success) {
|
||||
if (empty($user)) {
|
||||
return $this->createError("User not found");
|
||||
} else {
|
||||
$sql = $this->user->getSQL();
|
||||
$res = $sql->delete("User")->where(new Compare("uid", $id))->execute();
|
||||
$this->success = ($res !== FALSE);
|
||||
$this->lastError = $sql->getLastError();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->success;
|
||||
}
|
||||
}
|
||||
}
|
19
core/Driver/SQL/Condition/CondIn.class.php
Normal file
19
core/Driver/SQL/Condition/CondIn.class.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Driver\SQL\Condition;
|
||||
|
||||
use Driver\SQL\Column\Column;
|
||||
|
||||
class CondIn extends Condition {
|
||||
|
||||
private string $column;
|
||||
private array $values;
|
||||
|
||||
public function __construct(string $column, array $values) {
|
||||
$this->column = $column;
|
||||
$this->values = $values;
|
||||
}
|
||||
|
||||
public function getColumn() { return $this->column; }
|
||||
public function getValues() { return $this->values; }
|
||||
}
|
@ -5,6 +5,7 @@ namespace Driver\SQL;
|
||||
use Driver\SQL\Column\Column;
|
||||
use Driver\SQL\Condition\Compare;
|
||||
use Driver\SQL\Condition\CondBool;
|
||||
use Driver\SQL\Condition\CondIn;
|
||||
use Driver\SQL\Condition\CondOr;
|
||||
use Driver\SQL\Condition\Regex;
|
||||
use Driver\SQL\Constraint\Constraint;
|
||||
@ -334,6 +335,14 @@ abstract class SQL {
|
||||
}
|
||||
return implode(" AND ", $conditions);
|
||||
}
|
||||
} else if($condition instanceof CondIn) {
|
||||
$values = array();
|
||||
foreach ($condition->getValues() as $value) {
|
||||
$values[] = $this->addValue($value, $params);
|
||||
}
|
||||
|
||||
$values = implode(",", $values);
|
||||
return $this->columnName($condition->getColumn()) . " IN ($values)";
|
||||
} else {
|
||||
$this->lastError = "Unsupported condition type: " . get_class($condition);
|
||||
return false;
|
||||
|
8
js/admin.min.js
vendored
8
js/admin.min.js
vendored
File diff suppressed because one or more lines are too long
@ -51,6 +51,10 @@ export default class API {
|
||||
return this.apiCall("user/get", { id: id });
|
||||
}
|
||||
|
||||
async deleteUser(id) {
|
||||
return this.apiCall("user/delete", { id: id });
|
||||
}
|
||||
|
||||
async fetchUsers(pageNum = 1, count = 20) {
|
||||
return this.apiCall("user/fetch", { page: pageNum, count: count });
|
||||
}
|
||||
|
@ -6,6 +6,17 @@ export default function Dialog(props) {
|
||||
const classes = "modal fade" + (show ? " show" : "");
|
||||
const style = { paddingRight: "12px", display: (show ? "block" : "none") };
|
||||
const onClose = props.onClose || function() { };
|
||||
const onOption = props.onOption || function() { };
|
||||
const options = props.options || ["Close"];
|
||||
|
||||
let buttons = [];
|
||||
for (let name of options) {
|
||||
buttons.push(
|
||||
<button type="button" key={"button-" + name} className="btn btn-default" data-dismiss={"modal"} onClick={() => { onClose(); onOption(name); }}>
|
||||
{name}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes} id="modal-default" style={style} aria-modal="true" onClick={() => onClose()}>
|
||||
@ -20,8 +31,8 @@ export default function Dialog(props) {
|
||||
<div className="modal-body">
|
||||
<p>{props.message}</p>
|
||||
</div>
|
||||
<div className="modal-footer justify-content-between">
|
||||
<button type="button" className="btn btn-default" data-dismiss="modal" onClick={() => onClose()}>Close</button>
|
||||
<div className="modal-footer">
|
||||
{ buttons }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -34,8 +34,8 @@ class AdminDashboard extends React.Component {
|
||||
this.fetchNotifications();
|
||||
}
|
||||
|
||||
showDialog(message, title) {
|
||||
const props = { show: true, message: message, title: title };
|
||||
showDialog(message, title, options=["Close"], onOption = null) {
|
||||
const props = { show: true, message: message, title: title, options: options, onOption: onOption };
|
||||
this.setState({ ...this.state, dialog: { ...this.state.dialog, ...props } });
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,8 @@ export default class EditUser extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.parent = {
|
||||
api: props.api
|
||||
api: props.api,
|
||||
showDialog: props.showDialog,
|
||||
};
|
||||
|
||||
this.state = {
|
||||
@ -18,6 +19,7 @@ export default class EditUser extends React.Component {
|
||||
fetchError: null,
|
||||
loaded: false,
|
||||
isSaving: false,
|
||||
isDeleting: false,
|
||||
groups: { },
|
||||
searchString: "",
|
||||
searchActive: false
|
||||
@ -37,7 +39,13 @@ export default class EditUser extends React.Component {
|
||||
componentDidMount() {
|
||||
this.parent.api.getUser(this.props.match.params["userId"]).then((res) => {
|
||||
if (res.success) {
|
||||
this.setState({ ...this.state, user: {... res.user, password: ""} });
|
||||
this.setState({ ...this.state, user: {
|
||||
name: res.user.name,
|
||||
email: res.user.email || "",
|
||||
groups: res.user.groups,
|
||||
password: ""
|
||||
}
|
||||
});
|
||||
this.parent.api.fetchGroups(1, 50).then((res) => {
|
||||
if (res.success) {
|
||||
this.setState({ ...this.state, groups: res.groups, loaded: true });
|
||||
@ -92,6 +100,27 @@ export default class EditUser extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
onDeleteUser(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const id = this.props.match.params["userId"];
|
||||
|
||||
this.parent.showDialog("Are you sure you want to delete this user permanently?", "Delete User?", ["Yes", "No"], (btn) => {
|
||||
if (btn === "Yes") {
|
||||
this.parent.api.deleteUser(id).then((res) => {
|
||||
if (res.success) {
|
||||
this.props.history.push("/admin/users");
|
||||
} else {
|
||||
let alerts = this.state.alerts.slice();
|
||||
alerts.push({ title: "Error deleting user", message: res.msg, type: "danger" });
|
||||
this.setState({ ...this.state, isSaving: false, alerts: alerts, user: { ...this.state.user, password: "" } });
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onRemoveGroup(event, groupId) {
|
||||
event.stopPropagation();
|
||||
if (this.state.user.groups.hasOwnProperty(groupId)) {
|
||||
@ -178,7 +207,7 @@ export default class EditUser extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
form = <form role={"form"} onSubmit={this.onSubmitForm.bind(this)}>
|
||||
form = <form role={"form"} onSubmit={(e) => e.preventDefault()}>
|
||||
<div className={"form-group"}>
|
||||
<label htmlFor={"username"}>Username</label>
|
||||
<input type={"text"} className={"form-control"} placeholder={"Enter username"}
|
||||
@ -238,8 +267,12 @@ export default class EditUser extends React.Component {
|
||||
Back
|
||||
</Link>
|
||||
{ this.state.isSaving
|
||||
? <button type={"submit"} className={"btn btn-primary mt-2"} disabled>Saving… <Icon icon={"circle-notch"} /></button>
|
||||
: <button type={"submit"} className={"btn btn-primary mt-2"}>Save</button>
|
||||
? <button type={"submit"} className={"btn btn-primary mt-2 mr-2"} disabled>Saving… <Icon icon={"circle-notch"} /></button>
|
||||
: <button type={"submit"} className={"btn btn-primary mt-2 mr-2"} onClick={this.onSubmitForm.bind(this)}>Save</button>
|
||||
}
|
||||
{ this.state.isDeleting
|
||||
? <button type={"submit"} className={"btn btn-danger mt-2"} disabled>Deleting… <Icon icon={"circle-notch"} /></button>
|
||||
: <button type={"submit"} className={"btn btn-danger mt-2"} onClick={this.onDeleteUser.bind(this)}>Delete</button>
|
||||
}
|
||||
</form>
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user