Просмотр исходного кода

edit by id. Work in progress

Signed-off-by: Banana <mail@bananas-playground.net>
Banana 1 месяц назад
Родитель
Сommit
ca57c51822

+ 44 - 4
client/go-cli/scientia/cmd/edit.go

@@ -1,8 +1,11 @@
 package cmd
 
 import (
+	"fmt"
 	"github.com/spf13/cobra"
 	"os"
+	"os/exec"
+	Helper "scientia/lib"
 )
 
 /**
@@ -22,14 +25,51 @@ import (
  */
 
 var editCmd = &cobra.Command {
-	Use: "edit",
-	Short: "Modify an entry",
-	Long: "Edit an existing entry.",
+	Use: "edit ID",
+	Short: "Modify an entry by its ID",
+	Long: "Edit an existing entry. Get the ID from the list command.",
 	Run: func(cmd *cobra.Command, args []string) {
-		if len(args) == 0 {
+		var entryId string
+
+		if len(args) == 1 {
+			entryId = args[0]
+		} else {
 			cmd.Help()
 			os.Exit(0)
 		}
+
+		response := getEndpointRequest("?p=entry&id=" + entryId);
+
+		body := response.Data[0].Body
+		ident := response.Data[0].Ident
+
+		fh, err := os.CreateTemp("", ident)
+		Helper.ErrorCheck(err, "Can not create tmp file for editing.")
+		_, err = fmt.Fprintf(fh, body)
+
+		// default editor
+		var editor = "vim"
+
+		if e := os.Getenv("VISUAL"); e != "" {
+			editor = e
+		} else if e := os.Getenv("EDITOR"); e != "" {
+			editor = e
+		}
+
+		editCmd := exec.Command(editor, fh.Name())
+		editCmd.Stdin = os.Stdin
+		editCmd.Stdout = os.Stdout
+		editCmd.Stderr = os.Stderr
+		err = editCmd.Start()
+		Helper.ErrorCheck(err, "Can not open tmp file")
+
+		fmt.Println("Waiting for command to finish...")
+		err = editCmd.Wait()
+		Helper.ErrorCheck(err, "Command finished with error")
+		fmt.Println("Done.")
+
+		defer os.Remove(fh.Name())
+
 	},
 }
 

+ 0 - 45
client/go-cli/scientia/cmd/edit_list.go

@@ -1,45 +0,0 @@
-package cmd
-
-import (
-	"fmt"
-	"github.com/spf13/cobra"
-)
-
-/**
- * scientia
- *
- * Copyright 2023 - 2024 Johannes Keßler
- *
- * https://www.bananas-playground.net/projekt/scientia/
- *
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the COMMON DEVELOPMENT AND DISTRIBUTION LICENSE
- *
- * You should have received a copy of the
- * COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
- * along with this program.  If not, see http://www.sun.com/cddl/cddl.html
- */
-
-
-// Subcommand of edit
-// to list all available entries
-
-var editListCmd = &cobra.Command {
-	Use:   "list",
-	Short: "List all available entries",
-	Long:  "List all available entries",
-	Run: func(cmd *cobra.Command, args []string) {
-		listEntries()
-	},
-}
-
-func init() {
-	editCmd.AddCommand(editListCmd)
-}
-
-func listEntries() {
-	if FlagVerbose {
-		fmt.Println("Starting to request entries")
-	}
-}

+ 95 - 0
client/go-cli/scientia/cmd/list.go

@@ -0,0 +1,95 @@
+package cmd
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/spf13/cobra"
+	"io"
+	"net/http"
+	Helper "scientia/lib"
+	"strings"
+)
+
+/**
+ * scientia
+ *
+ * Copyright 2023 - 2024 Johannes Keßler
+ *
+ * https://www.bananas-playground.net/projekt/scientia/
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the COMMON DEVELOPMENT AND DISTRIBUTION LICENSE
+ *
+ * You should have received a copy of the
+ * COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+ * along with this program.  If not, see http://www.sun.com/cddl/cddl.html
+ */
+
+
+// to list all available entries
+
+var listCmd = &cobra.Command {
+	Use:   "list",
+	Short: "List all available entries",
+	Long:  "List all available entries with its ID, date and content preview.",
+	Run: func(cmd *cobra.Command, args []string) {
+		listEntries()
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(listCmd)
+}
+
+func listEntries() {
+	response := getEndpointRequest("")
+
+	for _, entry := range response.Data {
+		leftOfDelimiter, _, _ := strings.Cut(entry.Body, "\n")
+		fmt.Println(entry.Ident, entry.Date, leftOfDelimiter)
+	}
+}
+
+
+func getEndpointRequest(params string) GetResponse {
+	if FlagVerbose {
+		fmt.Println("Starting to request get endpoint")
+	}
+
+	req, err := http.NewRequest(http.MethodGet, ScientiaConfig.Endpoint.Get + params, nil)
+	Helper.ErrorCheck(err, "Can not create http request")
+	// We need to set the content type from the writer, it includes necessary boundary as well
+	req.Header.Set("User-Agent", "scientiaAgent/1.0")
+	req.Header.Set("X-ASL", ScientiaConfig.Endpoint.Secret)
+
+	// Do the request
+	client := &http.Client{}
+	response, err := client.Do(req)
+	Helper.ErrorCheck(err, "GET request failed")
+
+	responseBody, err := io.ReadAll(response.Body)
+	Helper.ErrorCheck(err, "Can not read response body")
+
+	if FlagVerbose {
+		fmt.Println("Request done")
+	}
+	if FlagDebug {
+		fmt.Printf("DEBUG Response status code: %d\n", response.StatusCode)
+		fmt.Printf("DEBUG Response headers: %#v\n", response.Header)
+		fmt.Println("DEBUG Response body:\n", string(responseBody))
+	}
+
+	returnResponse := GetResponse{}
+	if response.StatusCode != 200 {
+		returnResponse = GetResponse {
+			Message: "Status not as expected.",
+			Status: response.StatusCode,
+			Data: []GetResponseEntry{}}
+	} else {
+		err := json.Unmarshal([]byte(responseBody), &returnResponse)
+		Helper.ErrorCheck(err, "Can not parse return json")
+	}
+
+	return returnResponse
+}

+ 14 - 2
client/go-cli/scientia/cmd/root.go

@@ -6,7 +6,6 @@ import (
 	"github.com/adrg/xdg"
 	"github.com/spf13/cobra"
 	"gopkg.in/yaml.v3"
-	"log"
 	"os"
 	Helper "scientia/lib"
 )
@@ -26,6 +25,19 @@ type ConfigStruct struct {
 	} `yaml:"endpoint"`
 }
 
+// GetResponse struct for the get.php request
+type GetResponse struct {
+	Data []GetResponseEntry `json:"data"`
+	Status int `json:"status"`
+	Message string `json:"message"`
+}
+// GetResponseEntry struct is the entry itself
+type GetResponseEntry struct {
+	Ident string `json:"ident"`
+	Date string `json:"date"`
+	Body string `json:"body"`
+}
+
 // The ScientiaConfig used globally
 var ScientiaConfig ConfigStruct
 
@@ -84,7 +96,7 @@ func loadConfig() {
 	Helper.ErrorCheck(err, "Can not decode config file")
 
 	if ScientiaConfig.Endpoint.Add == "" || ScientiaConfig.Endpoint.Get == "" || ScientiaConfig.Endpoint.Secret == "" {
-		log.Fatal("Empty or outdated config?")
+		fmt.Println("WARNING Empty or outdated config?")
 	}
 
 	if FlagDebug {

+ 0 - 3
documentation/scructure/structure-edit.d2

@@ -1,10 +1,7 @@
-edit_list: list
 edit_id: id
 
 server: server
 server.shape: cloud
 
-edit -> edit_list
 edit -> edit_id
-edit_list <-> server
 edit_id <-> server

+ 58 - 0
webroot/get.php

@@ -50,3 +50,61 @@ date_default_timezone_set(TIMEZONE);
 
 # required libs
 require_once('lib/summoner.class.php');
+
+# validate key
+if(!isset($_SERVER['HTTP_X_ASL']) || empty($_SERVER['HTTP_X_ASL']) || !isset(UPLOAD_SECRET[$_SERVER['HTTP_X_ASL']])) {
+    header('X-PROVIDED-BY: scientia');
+    http_response_code(400);
+    exit();
+}
+
+
+$_requestMode = "list";
+if(isset($_GET['p']) && !empty($_GET['p'])) {
+    $_requestMode = trim($_GET['p']);
+    $_requestMode = Summoner::validate($_requestMode,'nospace') ? $_requestMode : "list";
+}
+$_id = '';
+if(isset($_GET['id']) && Summoner::validate($_GET['id'], 'shortlink',4)) {
+    $_id = trim($_GET['id']);
+    $_view = 'entry';
+}
+
+# default response
+$contentType = 'Content-Type: application/json; charset=utf-8';
+$httpResponseCode = 200;
+$contentBody = array (
+    'data' => array(),
+    'message' => '',
+    'status' => $httpResponseCode
+);
+
+## DB connection
+$DB = new mysqli(DB_HOST, DB_USERNAME,DB_PASSWORD, DB_NAME);
+if ($DB->connect_errno) exit('Can not connect to MySQL Server');
+$DB->set_charset("utf8mb4");
+$DB->query("SET collation_connection = 'utf8mb4_bin'");
+$driver = new mysqli_driver();
+$driver->report_mode = MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT;
+
+require_once 'lib/entry.class.php';
+$Entry = new Entry($DB);
+
+switch($_requestMode) {
+    case "entry":
+        if(!empty($_id)) {
+            $contentBody['data'][] = $Entry->loadById($_id);
+        }
+    break;
+
+    case "list":
+    default:
+        $contentBody['data'] = $Entry->list();
+}
+
+## return
+header('X-PROVIDED-BY: scientia');
+header($contentType);
+http_response_code($httpResponseCode);
+echo json_encode($contentBody);
+$DB->close();

+ 65 - 0
webroot/lib/entry.class.php

@@ -100,6 +100,35 @@ class Entry {
         return $ret;
     }
 
+    /**
+     * Load an entry by given $id.
+     * Used by get api
+     *
+     * @param string $id Id of the entry
+     * @return array
+     */
+    public function loadById(string $id): array {
+        $ret = array();
+
+        if(!empty($id)) {
+            $queryStr = "SELECT `ident`,`date`,`body`
+                            FROM `".DB_PREFIX."_entry`
+                            WHERE `ident` = '".$this->_DB->real_escape_string($id)."'";
+            if(QUERY_DEBUG) error_log("[QUERY] ".__METHOD__." query: ".var_export($queryStr,true));
+            try {
+                $query = $this->_DB->query($queryStr);
+                if($query !== false && $query->num_rows > 0) {
+                    $ret = $query->fetch_assoc();
+                }
+            }
+            catch(Exception $e) {
+                error_log("[ERROR] ".__METHOD__." catch: ".$e->getMessage());
+            }
+        }
+
+        return $ret;
+    }
+
     /**
      * Update an entry by given $id and $data
      *
@@ -154,6 +183,42 @@ class Entry {
         return $ret;
     }
 
+    /**
+     * Get all entries which match the specified options
+     * Body is trimmed to the first 100 chars
+     *
+     * @param String $searchTerm
+     * @param String $intervalStart
+     * @param String $intervalEnd
+     * @param int $limit
+     * @return array
+     */
+    public function list(string $searchTerm='', string $intervalStart='', string $intervalEnd='', int $limit=100): array {
+        $ret = array();
+
+        $queryStr = "SELECT e.ident, e.date, SUBSTRING(e.body,1,100) AS body 
+                    FROM `".DB_PREFIX."_entry` AS e";
+        if(!empty($intervalStart) && !empty($intervalEnd)) {
+            $queryStr .= " WHERE e.date >= '".$intervalStart."' AND e.date <= '".$intervalEnd."'";
+        }
+        if(!empty($searchTerm)) {
+            $queryStr .= " AND MATCH(e.words) AGAINST('".$this->_DB->real_escape_string($searchTerm)."' IN BOOLEAN MODE)";
+        }
+        $queryStr .= " ORDER BY `created` DESC";
+        $queryStr .= " LIMIT $limit";
+
+        if(QUERY_DEBUG) error_log("[QUERY] ".__METHOD__." query: ".var_export($queryStr,true));
+        try {
+            $query = $this->_DB->query($queryStr);
+            $ret = $query->fetch_all(MYSQLI_ASSOC);
+        }
+        catch(Exception $e) {
+            error_log("[ERROR] ".__METHOD__." catch: ".$e->getMessage());
+        }
+
+        return $ret;
+    }
+
     /**
      * Create unique words from the given data
      *

+ 13 - 35
webroot/view/list/list.php

@@ -17,8 +17,8 @@
 
 $TemplateData['entries'] = array();
 
-$queryStr = "SELECT e.ident, e.date, e.words, SUBSTRING(e.body,1,100) AS body FROM `".DB_PREFIX."_entry` AS e";
-$queryLimit = " LIMIT 100";
+require_once 'lib/entry.class.php';
+$Entry = new Entry($DB);
 
 $searchTerm = '';
 if(isset($_POST['submitForm']) && isset($_POST['searchInput'])) {
@@ -32,10 +32,9 @@ if(isset($_POST['submitForm']) && isset($_POST['searchInput'])) {
 // the single date infos come from index.php
 $_groupByFormat = $_year;
 $breadcrumb = array('Y');
+$_intervalStart = '';
+$_intervalEnd = '';
 if(!empty($_requestDateProvided)) {
-    $_intervalStart = '';
-    $_intervalEnd = '';
-
     if($_requestDateProvided === 'Y-m-d') {
         $queryLimit = "";
         $_groupByFormat = $_year.'-'.$_month.'-'.$_day;
@@ -56,39 +55,18 @@ if(!empty($_requestDateProvided)) {
         $_intervalStart = $_groupByFormat.'-01-01';
         $_intervalEnd = $_groupByFormat.'-12-31';
     }
-
-    if(!empty($_intervalStart) && !empty($_intervalEnd)) {
-        $queryStr .= " WHERE e.date >= '".$_intervalStart."' AND e.date <= '".$_intervalEnd."'";
-        if(!empty($searchTerm)) {
-            $queryStr .= " AND MATCH(e.words) AGAINST('".$DB->real_escape_string($searchTerm)."' IN BOOLEAN MODE)";
-        }
-    }
 } else {
     $_requestDateProvided = 'Y';
-    if(!empty($searchTerm)) {
-        $queryStr .= " WHERE MATCH(e.words) AGAINST('".$DB->real_escape_string($searchTerm)."' IN BOOLEAN MODE)";
-    }
 }
 
-$queryStr .= " ORDER BY `created` DESC";
-$queryStr .= $queryLimit;
-if(QUERY_DEBUG) error_log("[QUERY] query: ".var_export($queryStr,true));
-
-try {
-    $query = $DB->query($queryStr);
-    if($query !== false && $query->num_rows > 0) {
-        while(($result = $query->fetch_assoc()) != false) {
-            $_d = new DateTime($result['date']);
-            $_breadcrumb = array();
-            foreach($breadcrumb as $_b) {
-                $_breadcrumb[] = $_d->format($_b);
-            }
-            $TemplateData['entries'][$_d->format($_requestDateProvided)]['breadcrumb'] = $_breadcrumb;
-            $TemplateData['entries'][$_d->format($_requestDateProvided)]['e'][$result['ident']] = $result;
-            $TemplateData['entries'][$_d->format($_requestDateProvided)]['e'][$result['ident']]['link'] = str_replace('-','/',$result['date']).'/'.$result['ident'];
-        }
+$entries = $Entry->list($searchTerm, $_intervalStart, $_intervalEnd);
+foreach($entries as $k=>$entry) {
+    $_d = new DateTime($entry['date']);
+    $_breadcrumb = array();
+    foreach($breadcrumb as $_b) {
+        $_breadcrumb[] = $_d->format($_b);
     }
-}
-catch(Exception $e) {
-    error_log("[ERROR] catch: ".$e->getMessage());
+    $TemplateData['entries'][$_d->format($_requestDateProvided)]['breadcrumb'] = $_breadcrumb;
+    $TemplateData['entries'][$_d->format($_requestDateProvided)]['e'][$entry['ident']] = $entry;
+    $TemplateData['entries'][$_d->format($_requestDateProvided)]['e'][$entry['ident']]['link'] = str_replace('-','/',$entry['date']).'/'.$entry['ident'];
 }