/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* Copyright 2013-2021 The Libfossil Authors, see LICENSES/BSD-2-Clause.txt SPDX-License-Identifier: BSD-2-Clause-FreeBSD SPDX-FileCopyrightText: 2021 The Libfossil Authors SPDX-ArtifactOfProjectName: Libfossil SPDX-FileType: Code Heavily indebted to the Fossil SCM project (https://fossil-scm.org). */ /************************************************************************* This file implements wiki-related parts of the library. */ #include "fossil-scm/internal.h" #include <assert.h> /* Only for debugging */ #include <stdio.h> #define MARKER(pfexp) \ do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ printf pfexp; \ } while(0) int fsl_wiki_names_get( fsl_cx * const f, fsl_list * const tgt ){ fsl_db * db = fsl_needs_repo(f); if(!f || !tgt) return FSL_RC_MISUSE; else if(!db) return FSL_RC_NOT_A_REPO; else { int rc = fsl_db_select_slist( db, tgt, "SELECT substr(tagname,6) AS name " "FROM tag " "WHERE tagname GLOB 'wiki-*' " "ORDER BY lower(name)"); if(rc && db->error.code && !f->error.code){ fsl_cx_uplift_db_error(f, db); } return rc; } } int fsl_wiki_latest_rid( fsl_cx * const f, char const * pageName, fsl_id_t * const rid ){ fsl_db * db = f ? fsl_needs_repo(f) : NULL; if(!f || !pageName) return FSL_RC_MISUSE; else if(!*pageName) return FSL_RC_RANGE; else if(!db) return FSL_RC_NOT_A_REPO; else return fsl_db_get_id(db, rid, "SELECT x.rid FROM tag t, tagxref x " "WHERE x.tagid=t.tagid " "AND t.tagname='wiki-%q' " "AND TYPEOF(x.value+0)='integer' " // ^^^^ only 'wiki-%' tags which are wiki pages "ORDER BY mtime DESC LIMIT 1", pageName); } bool fsl_wiki_page_exists(fsl_cx * const f, char const * pageName){ fsl_id_t rid = 0; return (0==fsl_wiki_latest_rid(f, pageName, &rid)) && (rid>0); } int fsl_wiki_load_latest( fsl_cx * const f, char const * pageName, fsl_deck * d ){ fsl_db * db = f ? fsl_needs_repo(f) : NULL; if(!f || !pageName || !d) return FSL_RC_MISUSE; else if(!*pageName) return FSL_RC_RANGE; else if(!db) return FSL_RC_NOT_A_REPO; else{ fsl_id_t rid = 0; int rc = fsl_wiki_latest_rid(f, pageName, &rid); if(rc) return rc; else if(0==rid) return FSL_RC_NOT_FOUND; return fsl_deck_load_rid( f, d, rid, FSL_SATYPE_WIKI); } } int fsl_wiki_foreach_page( fsl_cx * const f, fsl_deck_visitor_f cb, void * state ){ if(!cb) return FSL_RC_MISUSE; else if(!fsl_needs_repo(f)) return FSL_RC_NOT_A_REPO; else{ fsl_stmt st = fsl_stmt_empty; fsl_stmt names = fsl_stmt_empty; int rc; bool doBreak = false; rc = fsl_cx_prepare(f, &names, "SELECT substr(tagname,6) AS name " "FROM tag " "WHERE tagname GLOB 'wiki-*' " "ORDER BY lower(name)"); if(rc) return rc; while( !doBreak && !rc && (FSL_RC_STEP_ROW==fsl_stmt_step(&names))){ fsl_size_t nameLen = 0; char const * pageName = fsl_stmt_g_text(&names, 0, &nameLen); if(!st.stmt){ rc = fsl_cx_prepare(f, &st, "SELECT x.rid AS mrid FROM tag t, tagxref x " "WHERE x.tagid=t.tagid " "AND t.tagname='wiki-'||?1 " "AND TYPEOF(x.value+0)='integer' " // ^^^^ only 'wiki-%' tags which are wiki pages "ORDER BY x.mtime DESC LIMIT 1"); if(rc) goto end; } rc = fsl_stmt_bind_step(&st, "s", pageName); if(rc!=FSL_RC_STEP_ROW) continue; fsl_deck d = fsl_deck_empty; fsl_id_t const rid = fsl_stmt_g_id(&st, 0); rc = fsl_deck_load_rid( f, &d, rid, FSL_SATYPE_WIKI); if(!rc){ assert(d.rid==rid); rc = cb(f, &d, state); if(FSL_RC_BREAK==rc){ rc = 0; doBreak = true; } } fsl_deck_finalize(&d); fsl_stmt_reset(&st); } end: fsl_stmt_finalize(&st); fsl_stmt_finalize(&names); return rc; } } int fsl_wiki_save(fsl_cx * f, char const * pageName, fsl_buffer const * b, char const * userName, char const * mimeType, fsl_wiki_save_mode_t createPolicy ){ fsl_db * db = f ? fsl_needs_repo(f) : NULL; if(!f || !pageName || !b) return FSL_RC_MISUSE; else if(!*pageName) return FSL_RC_RANGE; else if(!db) return FSL_RC_NOT_A_REPO; else{ fsl_deck d = fsl_deck_empty; fsl_id_t parentRid = 0; int rc = fsl_wiki_latest_rid(f, pageName, &parentRid); double mtime; if(rc) return rc; else if((FSL_WIKI_SAVE_MODE_UPDATE==createPolicy) && !parentRid){ return fsl_cx_err_set(f, FSL_RC_NOT_FOUND, "No such wiki page: %s", pageName); } else if((FSL_WIKI_SAVE_MODE_CREATE==createPolicy) && (parentRid>0)){ return fsl_cx_err_set(f, FSL_RC_ALREADY_EXISTS, "Wiki page already exists: %s", pageName); } mtime = fsl_db_julian_now(db); fsl_deck_init(f, &d, FSL_SATYPE_WIKI); rc = fsl_deck_D_set(&d, mtime); assert(!rc); rc = fsl_deck_L_set(&d, pageName, -1); if(!rc && mimeType && *mimeType){ rc = fsl_deck_N_set(&d, mimeType, -1); } if( !rc && parentRid ){ char * zUuid = fsl_rid_to_uuid(f, parentRid); if(!zUuid){ rc = FSL_RC_OOM; }else{ rc = fsl_deck_P_add(&d,zUuid); fsl_free(zUuid); } } if(rc) goto end; { char * u = NULL; if(!userName) userName = fsl_cx_user_get(f); if(!userName){ u = fsl_user_name_guess(); if(!u) rc = FSL_RC_OOM; } if(!rc) rc = fsl_deck_U_set(&d, u ? u : userName); if(u) fsl_free(u); if(rc) goto end; } rc = fsl_deck_W_set(&d, fsl_buffer_cstr(b), (fsl_int_t)b->used); #if 0 fsl_deck_output(f, &d, fsl_output_f_FILE, stdout); #endif if(!rc) rc = fsl_deck_save(&d, 0); end: fsl_deck_finalize(&d); return rc; } } #undef MARKER