1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2025-01-23 23:46:52 +00:00

MWS - added DELETE functionality to both Recipes and Bags

This commit is contained in:
Thomas E Tuoti 2024-12-16 16:29:06 -07:00
parent 060bea89ae
commit 1758a9ce12
5 changed files with 2065 additions and 1896 deletions

View File

@ -30,6 +30,33 @@ exports.useACL = true;
exports.entityName = "bag"
exports.handler = function(request,response,state) {
var server = state.server,
sqlTiddlerDatabase = server.sqlTiddlerDatabase;
// Handle DELETE request
if(state.data._method === "DELETE") {
if(state.data.bag_name) {
const result = $tw.mws.store.deleteBag(state.data.bag_name);
if(!result) {
state.sendResponse(302,{
"Content-Type": "text/plain",
"Location": "/"
});
} else {
state.sendResponse(400,{
"Content-Type": "text/plain"
},
result.message,
"utf8");
}
} else {
state.sendResponse(400,{
"Content-Type": "text/plain"
});
}
return;
}
if(state.data.bag_name) {
const result = $tw.mws.store.createBag(state.data.bag_name,state.data.description);
if(!result) {
@ -52,3 +79,4 @@ exports.handler = function(request,response,state) {
};
}());

View File

@ -4,9 +4,9 @@ type: application/javascript
module-type: mws-route
POST /recipes
DELETE /recipes (via _method=DELETE)
Parameters:
recipe_name
description
bag_names: space separated list of bags
@ -32,7 +32,33 @@ exports.entityName = "recipe"
exports.handler = function(request,response,state) {
var server = state.server,
sqlTiddlerDatabase = server.sqlTiddlerDatabase
sqlTiddlerDatabase = server.sqlTiddlerDatabase;
// Check and handle if this is a DELETE request
if(state.data._method === "DELETE") {
if(state.data.recipe_name && state.data.bag_names) {
const result = sqlTiddlerDatabase.deleteRecipe(state.data.recipe_name);
if(!result) {
state.sendResponse(302,{
"Content-Type": "text/plain",
"Location": "/"
});
} else {
state.sendResponse(400,{
"Content-Type": "text/plain"
},
result.message,
"utf8");
}
} else {
state.sendResponse(400,{
"Content-Type": "text/plain"
});
}
return;
}
// Handle POST request (original code)
if(state.data.recipe_name && state.data.bag_names) {
const result = $tw.mws.store.createRecipe(state.data.recipe_name,$tw.utils.parseStringArray(state.data.bag_names),state.data.description);
if(!result) {

View File

@ -203,6 +203,31 @@ SqlTiddlerDatabase.prototype.listBags = function() {
return rows;
};
/*
Delete a recipe and its bag associations
*/
SqlTiddlerDatabase.prototype.deleteRecipe = function(recipe_name) {
// Delete recipe_bags entries first (due to foreign key constraints)
this.engine.runStatement(`
DELETE FROM recipe_bags
WHERE recipe_id = (
SELECT recipe_id
FROM recipes
WHERE recipe_name = $recipe_name
)
`, {
$recipe_name: recipe_name
});
// Then delete the recipe itself
this.engine.runStatement(`
DELETE FROM recipes
WHERE recipe_name = $recipe_name
`, {
$recipe_name: recipe_name
});
};
/*
Create or update a bag
Returns the bag_id of the bag
@ -229,6 +254,19 @@ SqlTiddlerDatabase.prototype.createBag = function(bag_name,description,accesscon
return updateBags.lastInsertRowid;
};
/*
Delete a bag and all its associated data
*/
SqlTiddlerDatabase.prototype.deleteBag = function(bag_name) {
// Delete the bag (cascade will handle related records)
this.engine.runStatement(`
DELETE FROM bags
WHERE bag_name = $bag_name
`, {
$bag_name: bag_name
});
};
/*
Returns array of {recipe_name:,recipe_id:,description:,bag_names: []}
*/
@ -1446,3 +1484,4 @@ SqlTiddlerDatabase.prototype.getRoleById = function(roleId) {
exports.SqlTiddlerDatabase = SqlTiddlerDatabase;
})();

View File

@ -95,6 +95,46 @@ SqlTiddlerStore.prototype.validateItemName = function(name,allowPrivilegedCharac
return null;
};
/*
Delete a recipe. Returns null on success, or {message:} on error
*/
SqlTiddlerStore.prototype.deleteRecipe = function(recipe_name) {
var self = this;
return this.sqlTiddlerDatabase.transaction(function() {
// Check if recipe exists
const recipes = self.sqlTiddlerDatabase.listRecipes();
const recipeExists = recipes.some(recipe => recipe.recipe_name === recipe_name);
if(!recipeExists) {
return {message: "Recipe does not exist"};
}
// Delete the recipe
self.sqlTiddlerDatabase.deleteRecipe(recipe_name);
self.dispatchEvent("change");
return null;
});
};
/*
Delete a bag. Returns null on success, or {message:} on error
*/
SqlTiddlerStore.prototype.deleteBag = function(bag_name) {
var self = this;
return this.sqlTiddlerDatabase.transaction(function() {
// Check if bag exists
const bags = self.sqlTiddlerDatabase.listBags();
const bagExists = bags.some(bag => bag.bag_name === bag_name);
if(!bagExists) {
return {message: "Bag does not exist"};
}
// Delete the bag
self.sqlTiddlerDatabase.deleteBag(bag_name);
self.dispatchEvent("change");
return null;
});
};
/*
Returns null if the argument is an array of valid bag/recipe names, or a string error message if not
*/

View File

@ -86,6 +86,14 @@ title: $:/plugins/tiddlywiki/multiwikiserver/templates/get-index
</div>
<div class="mws-wiki-card-description">
<$text text={{{ [<recipe-info>jsonget[description]] }}}/>
</div>
<div class="mws-wiki-card-actions">
<form action="/recipes" method="post">
<input type="hidden" name="_method" value="DELETE"/>
<input type="hidden" name="recipe_name" value={{{ [<recipe-info>jsonget[recipe_name]] }}}/>
<input type="hidden" name="bag_names" value={{{ [<recipe-info>jsonget[bag_names]join[ ]] }}}/>
<button type="submit" class="mws-delete-button">Delete Recipe</button>
</form>
</div>
</div>
</div>
@ -133,6 +141,13 @@ title: $:/plugins/tiddlywiki/multiwikiserver/templates/get-index
>
<$transclude $variable="bagPill"/>
<$text text={{{ [<bag-info>jsonget[description]] }}}/>
<div class="mws-wiki-card-actions">
<form action="/bags" method="post" onsubmit="return confirmBagDelete(this)">
<input type="hidden" name="_method" value="DELETE"/>
<input type="hidden" name="bag_name" value={{{ [<bag-info>jsonget[bag_name]] }}}/>
<button type="submit" class="mws-delete-button">Delete Bag</button>
</form>
</div>
</$let>
</li>
</$list>
@ -241,6 +256,27 @@ title: $:/plugins/tiddlywiki/multiwikiserver/templates/get-index
display: block;
}
.mws-delete-button {
background-color: #f44336;
color: white;
padding: 5px 10px;
border: none;
border-radius: 4px;
cursor: pointer;
margin-left: 10px;
}
.mws-delete-button:hover {
background-color: #d32f2f;
}
.mws-wiki-card-actions {
display: flex;
justify-content: flex-end;
margin-top: 10px;
margin-left: 1em;
}
.mws-admin-dropdown-content a:hover {background-color: #ddd;}
.mws-admin-dropdown:hover .mws-admin-dropdown-content {display: block;}