Skip to content
Snippets Groups Projects
Commit ee841ba5 authored by Maximilian Giller's avatar Maximilian Giller :squid:
Browse files

Implemented all actions for graph manager

parent 0f33c776
No related branches found
No related tags found
No related merge requests found
Pipeline #57029 passed
......@@ -3,9 +3,10 @@
/**
* Uses the fetch API to create an ajax call for the WordPress API to fetch/post data from/to the server.
* @param data {FormData} Data for the ajax call. Must contain an entry which specifies the ajax `action`
* @param parseJsonResult If true, tries to parse response as JSON. Should be disabled if backend does not return JSON data.
* @returns {Promise<any>} The response from the server
*/
function ajaxCall(data) {
function ajaxCall(data, parseJsonResult = true) {
let opts = {
method: "POST",
body: data,
......@@ -13,7 +14,7 @@ function ajaxCall(data) {
return fetch(ks_global.ajax_url, opts).then(
function (response) {
return response.json();
return parseJsonResult ? response.json() : response;
},
function (reason) {
console.log(reason);
......@@ -23,6 +24,7 @@ function ajaxCall(data) {
/**
* Returns the json object from the stored graph as promise.
* Creates new graph if spaceId does not exist yet.
*
* @param {String} spaceId Identification of graph to load.
*
......@@ -36,6 +38,21 @@ export function loadGraphJson(spaceId) {
return ajaxCall(data);
}
/**
* Removes a graph entry from the database.
*
* @param {String} spaceId Identification of graph to remove.
*
* @returns Promise returning state of query.
*/
export function deleteGraphJson(spaceId) {
const data = new FormData();
data.append("action", "delete_space");
data.append("space", spaceId);
return ajaxCall(data, false);
}
/**
* Takes the graph json object and stores it in the backend.
*
......
......@@ -53,6 +53,22 @@ function ks_update_space() {
}
}
add_action("wp_ajax_delete_space", "ks_delete_space"); // Fires only for logged-in-users
//add_action("wp_ajax_nopriv_delete_space", 'delete_space' ); // Fires for everyone
function ks_delete_space()
{
// Check user capabilities
if (current_user_can("edit_posts")) {
$name = ks_escape_space_name($_POST["space"]);
ks_delete_graph($name);
wp_die();
} else {
echo "Insufficient permissions!";
}
}
function ks_escape_space_name($space_name) {
// Cleaning up the space id
$space_name = str_replace("/", "-", $space_name);
......@@ -61,5 +77,6 @@ function ks_escape_space_name($space_name) {
$space_name = str_replace(";", "-", $space_name);
$space_name = str_replace(":", "-", $space_name);
$space_name = str_replace(",", "-", $space_name);
return strtolower($space_name);
}
return $space_name;
// return strtolower($space_name); Not used, since it reduces readability, but is not really required
}
\ No newline at end of file
......@@ -2,6 +2,7 @@ import React, { useState } from "react";
import "./spacemanager.css";
interface SpaceManagerProps {
spaces: string[];
spaceId: string;
onDeleteSpace: (spaceId: string) => void;
onRenameSpace: (newId: string) => void;
......@@ -10,6 +11,7 @@ interface SpaceManagerProps {
}
function SpaceManager({
spaces,
spaceId,
onDeleteSpace,
onRenameSpace,
......@@ -25,6 +27,9 @@ function SpaceManager({
onDeleteSpace(spaceId);
};
const isSpaceCreationAllowed = () =>
newSpaceName.length === 0 || spaces.includes(newSpaceName.trim());
return (
<div id="space-manager">
<label htmlFor="space-name">New graph name</label>
......@@ -39,7 +44,7 @@ function SpaceManager({
<ul>
<li>
<button
disabled={newSpaceName.length === 0}
disabled={isSpaceCreationAllowed()}
onClick={() => onRenameSpace(newSpaceName)}
>
Rename to {'"' + newSpaceName + '"'}
......@@ -47,7 +52,7 @@ function SpaceManager({
</li>
<li>
<button
disabled={newSpaceName.length === 0}
disabled={isSpaceCreationAllowed()}
onClick={() => onDuplicateSpace(newSpaceName)}
>
Duplicate as {'"' + newSpaceName + '"'}
......@@ -55,7 +60,7 @@ function SpaceManager({
</li>
<li>
<button
disabled={newSpaceName.length === 0}
disabled={isSpaceCreationAllowed()}
onClick={() => onCreateSpace(newSpaceName)}
>
Create empty graph {'"' + newSpaceName + '"'}
......
......@@ -3,7 +3,7 @@ import SpaceManager from "./spacemanager";
import "./spaceselect.css";
interface SpaceSelectProps {
onLoadSpace: (spaceId: string) => boolean;
onLoadSpace: (spaceId: string) => Promise<boolean>;
onDeleteSpace: (spaceId: string) => void;
onRenameSpace: (newId: string) => void;
onDuplicateSpace: (newId: string) => void;
......@@ -21,22 +21,21 @@ function SpaceSelect({
onCreateSpace,
onDeleteSpace,
}: SpaceSelectProps) {
const [selected, setSelected] = useState(spaceId);
const [managerVisible, setMangerVisible] = useState(false);
const handleDeleteSpace = (spaceId: string) => {
setMangerVisible(false);
onDeleteSpace(spaceId);
const hideManagerAndCall = (call: (id: string) => void) => {
return (spaceId: string) => {
setMangerVisible(false);
call(spaceId);
};
};
return (
<div id="spaceselect">
<select
onChange={(event) => {
setSelected(event.target.value);
onLoadSpace(event.target.value);
}}
value={selected}
onChange={(event) => onLoadSpace(event.target.value)}
value={spaceId}
onFocus={() => setMangerVisible(false)}
>
{spaces.map((spaceName: string) => (
<option key={spaceName} value={spaceName}>
......@@ -51,11 +50,12 @@ function SpaceSelect({
</button>
{managerVisible && (
<SpaceManager
spaces={spaces}
spaceId={spaceId}
onCreateSpace={onCreateSpace}
onRenameSpace={onRenameSpace}
onDeleteSpace={handleDeleteSpace}
onDuplicateSpace={onDuplicateSpace}
onCreateSpace={hideManagerAndCall(onCreateSpace)}
onRenameSpace={hideManagerAndCall(onRenameSpace)}
onDeleteSpace={hideManagerAndCall(onDeleteSpace)}
onDuplicateSpace={hideManagerAndCall(onDuplicateSpace)}
/>
)}
</div>
......
import React from "react";
import { DynamicGraph } from "./graph";
import {
deleteGraphJson,
listAllSpaces,
loadGraphJson,
saveGraphJson,
......@@ -10,7 +11,6 @@ import "./editor.css";
import * as Helpers from "../common/helpers";
import { Node, NodeProperties } from "../common/graph/node";
import { SpaceManager } from "./components/spacemanager";
import SelectLayer from "./components/selectlayer";
import { Coordinate2D, GraphData, SimGraphData } from "../common/graph/graph";
import { NodeType, NodeTypeData } from "../common/graph/nodetype";
......@@ -70,6 +70,7 @@ type stateTypes = {
*/
export class Editor extends React.PureComponent<any, stateTypes> {
private rendererRef: React.RefObject<GraphRenderer2D>;
private defaultSpaceId = "Graph";
constructor(props: any) {
super(props);
......@@ -77,6 +78,10 @@ export class Editor extends React.PureComponent<any, stateTypes> {
// Making sure, all functions retain the proper this-bind
this.loadGraph = this.loadGraph.bind(this);
this.loadSpace = this.loadSpace.bind(this);
this.renameSpace = this.renameSpace.bind(this);
this.createSpace = this.createSpace.bind(this);
this.deleteSpace = this.deleteSpace.bind(this);
this.duplicateSpace = this.duplicateSpace.bind(this);
this.saveSpace = this.saveSpace.bind(this);
this.forceUpdate = this.forceUpdate.bind(this);
this.handleNodeTypeSelect = this.handleNodeTypeSelect.bind(this);
......@@ -107,7 +112,7 @@ export class Editor extends React.PureComponent<any, stateTypes> {
this.rendererRef = React.createRef();
listAllSpaces().then((spaces) => this.setState({ spaces: spaces }));
this.loadListOfSpaces();
// Set as new state
this.state = {
......@@ -140,10 +145,22 @@ export class Editor extends React.PureComponent<any, stateTypes> {
* Tries to load initial graph after webpage finished loading.
*/
componentDidMount() {
if (this.state.spaceId !== undefined) {
// Load initial space
this.loadSpace(this.state.spaceId);
}
// Try to load given spaceId from Config
// If not available, just load default space
const initialSpaceId = this.state.spaceId
? this.state.spaceId
: this.defaultSpaceId;
this.loadSpace(initialSpaceId);
}
/**
* Fetches the most current list of available spaces from the server and updates the state accordingly.
*/
private loadListOfSpaces(): Promise<string[]> {
return listAllSpaces().then((spaces) => {
this.setState({ spaces: spaces });
return spaces;
});
}
/**
......@@ -151,13 +168,18 @@ export class Editor extends React.PureComponent<any, stateTypes> {
* @param spaceId Id of space to load.
* @returns Promise with boolean value that is true, if successful.
*/
public loadSpace(spaceId: string): any {
return loadGraphJson(spaceId).then((data: GraphData) =>
this.loadGraph(data, spaceId)
);
public loadSpace(spaceId: string): Promise<boolean> {
return loadGraphJson(spaceId)
.then((data: GraphData) => {
// Loading space might have created a new space, if requested space was not available
// Just in case, reload list of spaces
this.loadListOfSpaces();
return data;
})
.then((data: GraphData) => this.loadGraph(data, spaceId));
}
public saveSpace() {
public saveSpace(): Promise<void> {
return saveGraphJson(
this.state.spaceId,
this.state.graph.toJSONSerializableObject()
......@@ -429,37 +451,45 @@ export class Editor extends React.PureComponent<any, stateTypes> {
/**
* @param newId Explicit id of space that should be deleted.
*/
private deleteSpace(spaceId: string) {
throw new Error(
'Function "deleteSpace(spaceId)" has not been implemented.'
);
private deleteSpace(spaceId: string): Promise<void> {
return deleteGraphJson(spaceId).then(() => {
// Select first space in list if available, otherwise select defaul space (which will be created)
const selectSpaceId: string =
this.state.spaces.length > 0
? this.state.spaces[0]
: this.defaultSpaceId;
this.loadSpace(selectSpaceId);
});
}
/**
* @param newId New id for currently selected space.
* @returns Promise is true, if new, renamed graph could be loaded successfully.
*/
private renameSpace(newId: string) {
throw new Error(
'Function "renameSpace(newId)" has not been implemented.'
);
private renameSpace(newId: string): Promise<boolean> {
return saveGraphJson(newId, this.state.graph.toJSONSerializableObject())
.then(() => deleteGraphJson(this.state.spaceId))
.then(() => this.loadSpace(newId));
}
/**
* @param newId Id for the newly created space with the data of the currently selected space copied over.
* @returns Promise is true, if newly created graph could be loaded successfully.
*/
private duplicateSpace(newId: string) {
throw new Error(
'Function "duplicateSpace(newId)" has not been implemented.'
);
private duplicateSpace(newId: string): Promise<boolean> {
return saveGraphJson(
newId,
this.state.graph.toJSONSerializableObject()
).then(() => this.loadSpace(newId));
}
/**
* @param newSpaceId Id for newly created space with the default empty space data.
* @returns Promise is true, if newly created graph could be loaded successfully.
*/
private createSpace(newSpaceId: string) {
throw new Error(
'Function "createSpace(newSpaceId)" has not been implemented.'
);
private createSpace(newSpaceId: string): Promise<boolean> {
return this.loadSpace(newSpaceId);
}
render(): React.ReactNode {
......
......@@ -11,6 +11,14 @@ function ks_insert_or_update_graph($name, $graph)
return ks_insert_space($name, $graph);
}
function ks_delete_graph($name)
{
// Delete graph
global $SPACES_TABLE;
global $wpdb;
$wpdb->delete($SPACES_TABLE, array("name" => $name), array("%s"));
}
function ks_select_all_spaces()
{
global $SPACES_TABLE;
......@@ -69,4 +77,4 @@ function ks_insert($table, $data)
$wpdb->insert($table, $data);
return $wpdb->insert_id;
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment