From: Banana Date: Sun, 4 Aug 2024 08:54:25 +0000 (+0200) Subject: edit by id. Work in progress X-Git-Url: http://91.132.146.200/gitweb/?a=commitdiff_plain;h=ca57c51822c5acdb99b4baf9ba554c946bcdf8b9;p=scientia.git edit by id. Work in progress Signed-off-by: Banana --- diff --git a/client/go-cli/scientia/cmd/edit.go b/client/go-cli/scientia/cmd/edit.go index 872272e..dadc1c0 100644 --- a/client/go-cli/scientia/cmd/edit.go +++ b/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()) + }, } diff --git a/client/go-cli/scientia/cmd/edit_list.go b/client/go-cli/scientia/cmd/edit_list.go deleted file mode 100644 index e7adfe2..0000000 --- a/client/go-cli/scientia/cmd/edit_list.go +++ /dev/null @@ -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") - } -} diff --git a/client/go-cli/scientia/cmd/list.go b/client/go-cli/scientia/cmd/list.go new file mode 100644 index 0000000..680e3ee --- /dev/null +++ b/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 +} diff --git a/client/go-cli/scientia/cmd/root.go b/client/go-cli/scientia/cmd/root.go index 93b37ab..d29855d 100644 --- a/client/go-cli/scientia/cmd/root.go +++ b/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 { diff --git a/documentation/scructure/structure-edit.d2 b/documentation/scructure/structure-edit.d2 index a23f2cc..c566afa 100644 --- a/documentation/scructure/structure-edit.d2 +++ b/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 diff --git a/webroot/get.php b/webroot/get.php index c9ae3a3..aaa0cd8 100644 --- a/webroot/get.php +++ b/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(); diff --git a/webroot/lib/entry.class.php b/webroot/lib/entry.class.php index bbf236d..5c49531 100644 --- a/webroot/lib/entry.class.php +++ b/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 * diff --git a/webroot/view/list/list.php b/webroot/view/list/list.php index 23a03be..705f8a3 100644 --- a/webroot/view/list/list.php +++ b/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']; }