Skip to content

Commit 3de7847

Browse files
committed
Added: restore and delete admin api and ui logic (#217)
1 parent 159c51d commit 3de7847

File tree

3 files changed

+309
-83
lines changed

3 files changed

+309
-83
lines changed

libreforms_fastapi/app/__init__.py

+176-3
Original file line numberDiff line numberDiff line change
@@ -2685,7 +2685,7 @@ async def api_admin_get_groups(
26852685

26862686

26872687

2688-
# Get all groups
2688+
# Get all form submissions
26892689
@app.get(
26902690
"/api/admin/get_documents",
26912691
dependencies=[Depends(api_key_auth)],
@@ -2715,9 +2715,9 @@ async def api_admin_get_documents(
27152715
documents = []
27162716

27172717
for form_name in get_form_names(config_path=config.FORM_CONFIG_PATH):
2718-
documents = documents + doc_db.get_all_documents(form_name=form_name)
2718+
documents = documents + doc_db.get_all_documents(form_name=form_name, exclude_deleted=False)
27192719

2720-
print("\n\n\n", documents)
2720+
# print("\n\n\n", documents)
27212721

27222722
# Write this query to the TransactionLog
27232723
if config.COLLECT_USAGE_STATISTICS:
@@ -2738,6 +2738,179 @@ async def api_admin_get_documents(
27382738
content={"status": "success", "documents": documents},
27392739
)
27402740

2741+
2742+
2743+
2744+
# Delete form
2745+
@app.delete("/api/admin/delete_form/{form_name}/{document_id}", dependencies=[Depends(api_key_auth)])
2746+
async def api_form_delete(
2747+
form_name: str,
2748+
document_id:str,
2749+
background_tasks: BackgroundTasks,
2750+
request: Request,
2751+
config = Depends(get_config_depends),
2752+
mailer = Depends(get_mailer),
2753+
doc_db = Depends(get_doc_db),
2754+
session: SessionLocal = Depends(get_db),
2755+
key: str = Depends(X_API_KEY)
2756+
):
2757+
"""
2758+
Deletes a specific document from a form based on the form name and document ID in the URL.
2759+
Validates the existence of the document, user permissions, and logs the deletion.
2760+
"""
2761+
2762+
if form_name not in get_form_names(config_path=config.FORM_CONFIG_PATH):
2763+
raise HTTPException(status_code=404, detail=f"Form '{form_name}' not found")
2764+
2765+
# Ugh, I'd like to find a more efficient way to get the user data. But alas, that
2766+
# the sqlalchemy-signing table is not optimized alongside the user model...
2767+
user = session.query(User).filter_by(api_key=key).first()
2768+
2769+
if not user or not user.site_admin:
2770+
raise HTTPException(status_code=404)
2771+
2772+
metadata={
2773+
doc_db.last_editor_field: user.username,
2774+
}
2775+
2776+
# Add the remote addr host if enabled
2777+
if config.COLLECT_USAGE_STATISTICS:
2778+
metadata[doc_db.ip_address_field] = request.client.host
2779+
2780+
try:
2781+
# Process the request as needed
2782+
success = doc_db.delete_document(
2783+
form_name=form_name,
2784+
document_id=document_id,
2785+
metadata=metadata,
2786+
)
2787+
2788+
# Unlike other methods, like get_one_document or fuzzy_search_documents, this method raises exceptions when
2789+
# it fails to ensure the user knows their operation was not successful.
2790+
except DocumentDoesNotExist as e:
2791+
raise HTTPException(status_code=404, detail=f"{e}")
2792+
2793+
except DocumentIsDeleted as e:
2794+
raise HTTPException(status_code=410, detail=f"{e}")
2795+
2796+
except InsufficientPermissions as e:
2797+
raise HTTPException(status_code=403, detail=f"{e}")
2798+
2799+
2800+
# Send email
2801+
if config.SMTP_ENABLED:
2802+
background_tasks.add_task(
2803+
mailer.send_mail,
2804+
subject="Form Deleted",
2805+
content=f"This email servers to notify you that a form was deleted at {config.DOMAIN} by the user registered at this email address. The form's document ID is '{document_id}'. If you believe this was a mistake, or did not submit a form, please contact your system administrator.",
2806+
to_address=user.email,
2807+
)
2808+
2809+
2810+
# Write this query to the TransactionLog
2811+
if config.COLLECT_USAGE_STATISTICS:
2812+
2813+
endpoint = request.url.path
2814+
remote_addr = request.client.host
2815+
2816+
background_tasks.add_task(
2817+
write_api_call_to_transaction_log,
2818+
api_key=key,
2819+
endpoint=endpoint,
2820+
remote_addr=remote_addr,
2821+
query_params={},
2822+
)
2823+
2824+
return {
2825+
"message": "Form successfully deleted",
2826+
"document_id": document_id,
2827+
}
2828+
2829+
2830+
@app.patch("/api/admin/restore_form/{form_name}/{document_id}", dependencies=[Depends(api_key_auth)])
2831+
async def api_form_restore(
2832+
form_name: str,
2833+
document_id:str,
2834+
background_tasks: BackgroundTasks,
2835+
request: Request,
2836+
config = Depends(get_config_depends),
2837+
mailer = Depends(get_mailer),
2838+
doc_db = Depends(get_doc_db),
2839+
session: SessionLocal = Depends(get_db),
2840+
key: str = Depends(X_API_KEY)
2841+
):
2842+
"""
2843+
Restores a previously deleted document in a form, identified by form name and document ID in the URL.
2844+
Checks document existence, validates user permissions, and logs the restoration.
2845+
"""
2846+
2847+
if form_name not in get_form_names(config_path=config.FORM_CONFIG_PATH):
2848+
raise HTTPException(status_code=404, detail=f"Form '{form_name}' not found")
2849+
2850+
# Ugh, I'd like to find a more efficient way to get the user data. But alas, that
2851+
# the sqlalchemy-signing table is not optimized alongside the user model...
2852+
user = session.query(User).filter_by(api_key=key).first()
2853+
2854+
if not user or not user.site_admin:
2855+
raise HTTPException(status_code=404)
2856+
2857+
metadata={
2858+
doc_db.last_editor_field: user.username,
2859+
}
2860+
2861+
# Add the remote addr host if enabled
2862+
if config.COLLECT_USAGE_STATISTICS:
2863+
metadata[doc_db.ip_address_field] = request.client.host
2864+
2865+
try:
2866+
# Process the request as needed
2867+
success = doc_db.restore_document(
2868+
form_name=form_name,
2869+
document_id=document_id,
2870+
metadata=metadata,
2871+
)
2872+
2873+
# Unlike other methods, like get_one_document or fuzzy_search_documents, this method raises exceptions when
2874+
# it fails to ensure the user knows their operation was not successful.
2875+
except DocumentDoesNotExist as e:
2876+
raise HTTPException(status_code=404, detail=f"{e}")
2877+
2878+
except DocumentIsNotDeleted as e:
2879+
raise HTTPException(status_code=200, detail=f"{e}")
2880+
2881+
except InsufficientPermissions as e:
2882+
raise HTTPException(status_code=403, detail=f"{e}")
2883+
2884+
2885+
# Send email
2886+
if config.SMTP_ENABLED:
2887+
background_tasks.add_task(
2888+
mailer.send_mail,
2889+
subject="Form Restored",
2890+
content=f"This email servers to notify you that a deleted form was restored at {config.DOMAIN} by the user registered at this email address. The form's document ID is '{document_id}'. If you believe this was a mistake, or did not submit a form, please contact your system administrator.",
2891+
to_address=user.email,
2892+
)
2893+
2894+
# Write this query to the TransactionLog
2895+
if config.COLLECT_USAGE_STATISTICS:
2896+
2897+
endpoint = request.url.path
2898+
remote_addr = request.client.host
2899+
2900+
background_tasks.add_task(
2901+
write_api_call_to_transaction_log,
2902+
api_key=key,
2903+
endpoint=endpoint,
2904+
remote_addr=remote_addr,
2905+
query_params={},
2906+
)
2907+
2908+
return {
2909+
"message": "Form successfully restored",
2910+
"document_id": document_id,
2911+
}
2912+
2913+
27412914
# Add new group
27422915
@app.post(
27432916
"/api/admin/create_group",

libreforms_fastapi/app/static/js/custom.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,9 @@ function prettifyTimeDiff(dateTimeStr) {
116116
const timeDiff = (now - date) / 1000; // convert milliseconds to seconds
117117

118118
if (timeDiff < 3600) {
119-
if (timeDiff / 60 < 1) {
119+
if (timeDiff / 30 < 1) {
120+
return "seconds ago";
121+
} else if (timeDiff / 60 < 1) {
120122
return "less than a minute ago";
121123
} else if (timeDiff / 90 < 1) {
122124
return "about a minute ago";

0 commit comments

Comments
 (0)