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 @@ ...@@ -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. * 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 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 * @returns {Promise<any>} The response from the server
*/ */
function ajaxCall(data) { function ajaxCall(data, parseJsonResult = true) {
let opts = { let opts = {
method: "POST", method: "POST",
body: data, body: data,
...@@ -13,7 +14,7 @@ function ajaxCall(data) { ...@@ -13,7 +14,7 @@ function ajaxCall(data) {
return fetch(ks_global.ajax_url, opts).then( return fetch(ks_global.ajax_url, opts).then(
function (response) { function (response) {
return response.json(); return parseJsonResult ? response.json() : response;
}, },
function (reason) { function (reason) {
console.log(reason); console.log(reason);
...@@ -23,6 +24,7 @@ function ajaxCall(data) { ...@@ -23,6 +24,7 @@ function ajaxCall(data) {
/** /**
* Returns the json object from the stored graph as promise. * 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. * @param {String} spaceId Identification of graph to load.
* *
...@@ -36,6 +38,21 @@ export function loadGraphJson(spaceId) { ...@@ -36,6 +38,21 @@ export function loadGraphJson(spaceId) {
return ajaxCall(data); 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. * Takes the graph json object and stores it in the backend.
* *
......
...@@ -53,6 +53,22 @@ function ks_update_space() { ...@@ -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) { function ks_escape_space_name($space_name) {
// Cleaning up the space id // Cleaning up the space id
$space_name = str_replace("/", "-", $space_name); $space_name = str_replace("/", "-", $space_name);
...@@ -61,5 +77,6 @@ function ks_escape_space_name($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); $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"; ...@@ -2,6 +2,7 @@ import React, { useState } from "react";
import "./spacemanager.css"; import "./spacemanager.css";
interface SpaceManagerProps { interface SpaceManagerProps {
spaces: string[];
spaceId: string; spaceId: string;
onDeleteSpace: (spaceId: string) => void; onDeleteSpace: (spaceId: string) => void;
onRenameSpace: (newId: string) => void; onRenameSpace: (newId: string) => void;
...@@ -10,6 +11,7 @@ interface SpaceManagerProps { ...@@ -10,6 +11,7 @@ interface SpaceManagerProps {
} }
function SpaceManager({ function SpaceManager({
spaces,
spaceId, spaceId,
onDeleteSpace, onDeleteSpace,
onRenameSpace, onRenameSpace,
...@@ -25,6 +27,9 @@ function SpaceManager({ ...@@ -25,6 +27,9 @@ function SpaceManager({
onDeleteSpace(spaceId); onDeleteSpace(spaceId);
}; };
const isSpaceCreationAllowed = () =>
newSpaceName.length === 0 || spaces.includes(newSpaceName.trim());
return ( return (
<div id="space-manager"> <div id="space-manager">
<label htmlFor="space-name">New graph name</label> <label htmlFor="space-name">New graph name</label>
...@@ -39,7 +44,7 @@ function SpaceManager({ ...@@ -39,7 +44,7 @@ function SpaceManager({
<ul> <ul>
<li> <li>
<button <button
disabled={newSpaceName.length === 0} disabled={isSpaceCreationAllowed()}
onClick={() => onRenameSpace(newSpaceName)} onClick={() => onRenameSpace(newSpaceName)}
> >
Rename to {'"' + newSpaceName + '"'} Rename to {'"' + newSpaceName + '"'}
...@@ -47,7 +52,7 @@ function SpaceManager({ ...@@ -47,7 +52,7 @@ function SpaceManager({
</li> </li>
<li> <li>
<button <button
disabled={newSpaceName.length === 0} disabled={isSpaceCreationAllowed()}
onClick={() => onDuplicateSpace(newSpaceName)} onClick={() => onDuplicateSpace(newSpaceName)}
> >
Duplicate as {'"' + newSpaceName + '"'} Duplicate as {'"' + newSpaceName + '"'}
...@@ -55,7 +60,7 @@ function SpaceManager({ ...@@ -55,7 +60,7 @@ function SpaceManager({
</li> </li>
<li> <li>
<button <button
disabled={newSpaceName.length === 0} disabled={isSpaceCreationAllowed()}
onClick={() => onCreateSpace(newSpaceName)} onClick={() => onCreateSpace(newSpaceName)}
> >
Create empty graph {'"' + newSpaceName + '"'} Create empty graph {'"' + newSpaceName + '"'}
......
...@@ -3,7 +3,7 @@ import SpaceManager from "./spacemanager"; ...@@ -3,7 +3,7 @@ import SpaceManager from "./spacemanager";
import "./spaceselect.css"; import "./spaceselect.css";
interface SpaceSelectProps { interface SpaceSelectProps {
onLoadSpace: (spaceId: string) => boolean; onLoadSpace: (spaceId: string) => Promise<boolean>;
onDeleteSpace: (spaceId: string) => void; onDeleteSpace: (spaceId: string) => void;
onRenameSpace: (newId: string) => void; onRenameSpace: (newId: string) => void;
onDuplicateSpace: (newId: string) => void; onDuplicateSpace: (newId: string) => void;
...@@ -21,22 +21,21 @@ function SpaceSelect({ ...@@ -21,22 +21,21 @@ function SpaceSelect({
onCreateSpace, onCreateSpace,
onDeleteSpace, onDeleteSpace,
}: SpaceSelectProps) { }: SpaceSelectProps) {
const [selected, setSelected] = useState(spaceId);
const [managerVisible, setMangerVisible] = useState(false); const [managerVisible, setMangerVisible] = useState(false);
const handleDeleteSpace = (spaceId: string) => { const hideManagerAndCall = (call: (id: string) => void) => {
setMangerVisible(false); return (spaceId: string) => {
onDeleteSpace(spaceId); setMangerVisible(false);
call(spaceId);
};
}; };
return ( return (
<div id="spaceselect"> <div id="spaceselect">
<select <select
onChange={(event) => { onChange={(event) => onLoadSpace(event.target.value)}
setSelected(event.target.value); value={spaceId}
onLoadSpace(event.target.value); onFocus={() => setMangerVisible(false)}
}}
value={selected}
> >
{spaces.map((spaceName: string) => ( {spaces.map((spaceName: string) => (
<option key={spaceName} value={spaceName}> <option key={spaceName} value={spaceName}>
...@@ -51,11 +50,12 @@ function SpaceSelect({ ...@@ -51,11 +50,12 @@ function SpaceSelect({
</button> </button>
{managerVisible && ( {managerVisible && (
<SpaceManager <SpaceManager
spaces={spaces}
spaceId={spaceId} spaceId={spaceId}
onCreateSpace={onCreateSpace} onCreateSpace={hideManagerAndCall(onCreateSpace)}
onRenameSpace={onRenameSpace} onRenameSpace={hideManagerAndCall(onRenameSpace)}
onDeleteSpace={handleDeleteSpace} onDeleteSpace={hideManagerAndCall(onDeleteSpace)}
onDuplicateSpace={onDuplicateSpace} onDuplicateSpace={hideManagerAndCall(onDuplicateSpace)}
/> />
)} )}
</div> </div>
......
import React from "react"; import React from "react";
import { DynamicGraph } from "./graph"; import { DynamicGraph } from "./graph";
import { import {
deleteGraphJson,
listAllSpaces, listAllSpaces,
loadGraphJson, loadGraphJson,
saveGraphJson, saveGraphJson,
...@@ -10,7 +11,6 @@ import "./editor.css"; ...@@ -10,7 +11,6 @@ import "./editor.css";
import * as Helpers from "../common/helpers"; import * as Helpers from "../common/helpers";
import { Node, NodeProperties } from "../common/graph/node"; import { Node, NodeProperties } from "../common/graph/node";
import { SpaceManager } from "./components/spacemanager";
import SelectLayer from "./components/selectlayer"; import SelectLayer from "./components/selectlayer";
import { Coordinate2D, GraphData, SimGraphData } from "../common/graph/graph"; import { Coordinate2D, GraphData, SimGraphData } from "../common/graph/graph";
import { NodeType, NodeTypeData } from "../common/graph/nodetype"; import { NodeType, NodeTypeData } from "../common/graph/nodetype";
...@@ -70,6 +70,7 @@ type stateTypes = { ...@@ -70,6 +70,7 @@ type stateTypes = {
*/ */
export class Editor extends React.PureComponent<any, stateTypes> { export class Editor extends React.PureComponent<any, stateTypes> {
private rendererRef: React.RefObject<GraphRenderer2D>; private rendererRef: React.RefObject<GraphRenderer2D>;
private defaultSpaceId = "Graph";
constructor(props: any) { constructor(props: any) {
super(props); super(props);
...@@ -77,6 +78,10 @@ export class Editor extends React.PureComponent<any, stateTypes> { ...@@ -77,6 +78,10 @@ export class Editor extends React.PureComponent<any, stateTypes> {
// Making sure, all functions retain the proper this-bind // Making sure, all functions retain the proper this-bind
this.loadGraph = this.loadGraph.bind(this); this.loadGraph = this.loadGraph.bind(this);
this.loadSpace = this.loadSpace.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.saveSpace = this.saveSpace.bind(this);
this.forceUpdate = this.forceUpdate.bind(this); this.forceUpdate = this.forceUpdate.bind(this);
this.handleNodeTypeSelect = this.handleNodeTypeSelect.bind(this); this.handleNodeTypeSelect = this.handleNodeTypeSelect.bind(this);
...@@ -107,7 +112,7 @@ export class Editor extends React.PureComponent<any, stateTypes> { ...@@ -107,7 +112,7 @@ export class Editor extends React.PureComponent<any, stateTypes> {
this.rendererRef = React.createRef(); this.rendererRef = React.createRef();
listAllSpaces().then((spaces) => this.setState({ spaces: spaces })); this.loadListOfSpaces();
// Set as new state // Set as new state
this.state = { this.state = {
...@@ -140,10 +145,22 @@ export class Editor extends React.PureComponent<any, stateTypes> { ...@@ -140,10 +145,22 @@ export class Editor extends React.PureComponent<any, stateTypes> {
* Tries to load initial graph after webpage finished loading. * Tries to load initial graph after webpage finished loading.
*/ */
componentDidMount() { componentDidMount() {
if (this.state.spaceId !== undefined) { // Try to load given spaceId from Config
// Load initial space // If not available, just load default space
this.loadSpace(this.state.spaceId); 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> { ...@@ -151,13 +168,18 @@ export class Editor extends React.PureComponent<any, stateTypes> {
* @param spaceId Id of space to load. * @param spaceId Id of space to load.
* @returns Promise with boolean value that is true, if successful. * @returns Promise with boolean value that is true, if successful.
*/ */
public loadSpace(spaceId: string): any { public loadSpace(spaceId: string): Promise<boolean> {
return loadGraphJson(spaceId).then((data: GraphData) => return loadGraphJson(spaceId)
this.loadGraph(data, 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( return saveGraphJson(
this.state.spaceId, this.state.spaceId,
this.state.graph.toJSONSerializableObject() this.state.graph.toJSONSerializableObject()
...@@ -429,37 +451,45 @@ export class Editor extends React.PureComponent<any, stateTypes> { ...@@ -429,37 +451,45 @@ export class Editor extends React.PureComponent<any, stateTypes> {
/** /**
* @param newId Explicit id of space that should be deleted. * @param newId Explicit id of space that should be deleted.
*/ */
private deleteSpace(spaceId: string) { private deleteSpace(spaceId: string): Promise<void> {
throw new Error( return deleteGraphJson(spaceId).then(() => {
'Function "deleteSpace(spaceId)" has not been implemented.' // 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. * @param newId New id for currently selected space.
* @returns Promise is true, if new, renamed graph could be loaded successfully.
*/ */
private renameSpace(newId: string) { private renameSpace(newId: string): Promise<boolean> {
throw new Error( return saveGraphJson(newId, this.state.graph.toJSONSerializableObject())
'Function "renameSpace(newId)" has not been implemented.' .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. * @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) { private duplicateSpace(newId: string): Promise<boolean> {
throw new Error( return saveGraphJson(
'Function "duplicateSpace(newId)" has not been implemented.' newId,
); this.state.graph.toJSONSerializableObject()
).then(() => this.loadSpace(newId));
} }
/** /**
* @param newSpaceId Id for newly created space with the default empty space data. * @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) { private createSpace(newSpaceId: string): Promise<boolean> {
throw new Error( return this.loadSpace(newSpaceId);
'Function "createSpace(newSpaceId)" has not been implemented.'
);
} }
render(): React.ReactNode { render(): React.ReactNode {
......
...@@ -11,6 +11,14 @@ function ks_insert_or_update_graph($name, $graph) ...@@ -11,6 +11,14 @@ function ks_insert_or_update_graph($name, $graph)
return ks_insert_space($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() function ks_select_all_spaces()
{ {
global $SPACES_TABLE; global $SPACES_TABLE;
...@@ -69,4 +77,4 @@ function ks_insert($table, $data) ...@@ -69,4 +77,4 @@ function ks_insert($table, $data)
$wpdb->insert($table, $data); $wpdb->insert($table, $data);
return $wpdb->insert_id; 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