Mercurial > paster
changeset 25:f4e8a7920b94
pasterd: implement search, closes #2474 @30m
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 07 Feb 2020 13:23:48 +0100 |
parents | 6702a87420d1 |
children | 6a45977c82b4 |
files | database.c database.h http.c themes/minimal/404.html themes/minimal/500.html themes/minimal/header.html themes/minimal/search.html themes/siimple/header.html themes/siimple/search.html |
diffstat | 9 files changed, 222 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/database.c Thu Feb 06 21:48:32 2020 +0100 +++ b/database.c Fri Feb 07 13:23:48 2020 +0100 @@ -92,6 +92,23 @@ "\n" "END TRANSACTION"; +static const char *sql_search = + "SELECT uuid\n" + " , title\n" + " , author\n" + " , language\n" + " , code\n" + " , strftime('%s', date) AS date\n" + " , visible\n" + " , duration\n" + " FROM paste\n" + " WHERE title like ?\n" + " AND author like ?\n" + " AND language like ?\n" + " AND visible = 1\n" + " ORDER BY date DESC\n" + " LIMIT ?\n"; + /* sqlite3 use const unsigned char *. */ static char * dup(const unsigned char *s) @@ -177,7 +194,6 @@ sqlite3_finalize(stmt); *max = i; - return true; sqlite_err: @@ -279,6 +295,61 @@ return false; } +bool +database_search(struct paste *pastes, + size_t *max, + const char *title, + const char *author, + const char *language) +{ + assert(pastes); + assert(max); + + sqlite3_stmt *stmt = NULL; + + log_debug("database: searching title=%s, author=%s, language=%s", + title ? title : "", + author ? author : "", + language ? language : ""); + + memset(pastes, 0, *max * sizeof (struct paste)); + + /* Select everything if not specified. */ + title = title ? title : "%"; + author = author ? author : "%"; + language = language ? language : "%"; + + if (sqlite3_prepare(db, sql_search, -1, &stmt, NULL) != SQLITE_OK) + goto sqlite_err; + if (sqlite3_bind_text(stmt, 1, title, -1, NULL) != SQLITE_OK) + goto sqlite_err; + if (sqlite3_bind_text(stmt, 2, author, -1, NULL) != SQLITE_OK) + goto sqlite_err; + if (sqlite3_bind_text(stmt, 3, language, -1, NULL) != SQLITE_OK) + goto sqlite_err; + if (sqlite3_bind_int64(stmt, 4, *max) != SQLITE_OK) + goto sqlite_err; + + size_t i = 0; + + for (; i < *max && sqlite3_step(stmt) == SQLITE_ROW; ++i) + convert(stmt, &pastes[i]); + + log_debug("database: found %zu pastes", i); + sqlite3_finalize(stmt); + *max = i; + + return true; + +sqlite_err: + log_warn("database: error (search): %s\n", sqlite3_errmsg(db)); + + if (stmt) + sqlite3_finalize(stmt); + + return (*max = 0); +} + void database_clear(void) {
--- a/database.h Thu Feb 06 21:48:32 2020 +0100 +++ b/database.h Fri Feb 07 13:23:48 2020 +0100 @@ -36,6 +36,13 @@ bool database_insert(struct paste *paste); +bool +database_search(struct paste *pastes, + size_t *max, + const char *title, + const char *author, + const char *language); + void database_clear(void);
--- a/http.c Thu Feb 06 21:48:32 2020 +0100 +++ b/http.c Fri Feb 07 13:23:48 2020 +0100 @@ -43,6 +43,7 @@ static void page_fork(struct kreq *); static void page_paste(struct kreq *); static void page_download(struct kreq *); +static void page_search(struct kreq *); static void page_static(struct kreq *); enum page { @@ -51,6 +52,7 @@ PAGE_FORK, PAGE_PASTE, PAGE_DOWNLOAD, + PAGE_SEARCH, PAGE_STATIC, PAGE_LAST /* Not used. */ }; @@ -61,6 +63,7 @@ [PAGE_FORK] = "fork", [PAGE_PASTE] = "paste", [PAGE_DOWNLOAD] = "download", + [PAGE_SEARCH] = "search", [PAGE_STATIC] = "static" }; @@ -70,6 +73,7 @@ [PAGE_FORK] = page_fork, [PAGE_PASTE] = page_paste, [PAGE_DOWNLOAD] = page_download, + [PAGE_SEARCH] = page_search, [PAGE_STATIC] = page_static }; @@ -789,6 +793,88 @@ } static void +page_search_get(struct kreq *req) +{ + /* We re-use the /new form with an empty paste. */ + struct tmpl_paste data = { + .req = req + }; + const struct ktemplate kt = { + .key = tmpl_new_keywords, + .keysz = 6, + .cb = tmpl_new, + .arg = &data + }; + + page(req, &kt, KHTTP_200, "search.html"); +} + +static void +page_search_post(struct kreq *req) +{ + struct tmpl_index data = { + .req = req, + .count = 10 + }; + + const char *title = NULL; + const char *author = NULL; + const char *language = NULL; + + for (size_t i = 0; i < req->fieldsz; ++i) { + const char *key = req->fields[i].key; + const char *val = req->fields[i].val; + + if (strcmp(key, "title") == 0) + title = val; + else if (strcmp(key, "author") == 0) + author = val; + else if (strcmp(key, "language") == 0) + language = val; + } + + /* Sets to null if they are empty. */ + if (title && strlen(title) == 0) + title = NULL; + if (author && strlen(author) == 0) + author = NULL; + if (language && strlen(language) == 0) + language = NULL; + + if (!database_search(data.pastes, &data.count, title, author, language)) + page(req, NULL, KHTTP_500, "500.html"); + else { + struct ktemplate kt = { + .key = tmpl_index_keywords, + .keysz = 1, + .arg = &data, + .cb = tmpl_index + }; + + page(req, &kt, KHTTP_200, "index.html"); + } + + for (size_t i = 0; i < data.count; ++i) + paste_finish(&data.pastes[i]); +} + +static void +page_search(struct kreq *req) +{ + switch (req->method) { + case KMETHOD_GET: + page_search_get(req); + break; + case KMETHOD_POST: + page_search_post(req); + break; + default: + page(req, NULL, KHTTP_400, "400.html"); + break; + } +} + +static void page_static_get(struct kreq *req) { struct stat st;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/minimal/404.html Fri Feb 07 13:23:48 2020 +0100 @@ -0,0 +1,1 @@ +<h1>Not found</h1>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/minimal/500.html Fri Feb 07 13:23:48 2020 +0100 @@ -0,0 +1,1 @@ +<h1>Internal server error</h1>
--- a/themes/minimal/header.html Thu Feb 06 21:48:32 2020 +0100 +++ b/themes/minimal/header.html Fri Feb 07 13:23:48 2020 +0100 @@ -7,5 +7,7 @@ <h1>Menu</h1> <ul> + <li><a href="/">Home</a></li> <li><a href="/new">New</a></li> + <li><a href="/search">Search</a></li> </ul>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/minimal/search.html Fri Feb 07 13:23:48 2020 +0100 @@ -0,0 +1,26 @@ + <h1>Search pastes</h1> + + <form action="/search" method="post"> + <table> + <tr> + <td>Title</td> + <td><input name="title" type="text" placeholder="Title" /></td> + </tr> + + <tr> + <td>Author</td> + <td><input name="author" type="text" placeholder="Author" /></td> + </tr> + + <tr> + <td>Language</td> + <td> + <select name="language"> + @@languages@@ + </select> + </td> + </tr> + </table> + + <button>Submit</button> + </form>
--- a/themes/siimple/header.html Thu Feb 06 21:48:32 2020 +0100 +++ b/themes/siimple/header.html Fri Feb 07 13:23:48 2020 +0100 @@ -14,6 +14,7 @@ <div class="siimple-navbar-subtitle">simple paste service</div> <div class="siimple--float-right"> <a class="siimple-navbar-item" href="/new">Create</a> + <a class="siimple-navbar-item" href="/search">Search</a> </div> </div>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/siimple/search.html Fri Feb 07 13:23:48 2020 +0100 @@ -0,0 +1,26 @@ + <h1>Search pastes</h1> + + <div class="siimple-form"> + <form action="/search" method="post"> + <div class="siimple-field"> + <label class="siimple-field-label">Title</label> + <input class="siimple-input" name="title" type="text" placeholder="Title" /> + </div> + + <div class="siimple-field"> + <label class="siimple-field-label">Author</label> + <input class="siimple-input" name="author" type="text" placeholder="Author" /> + </div> + + <div class="siimple-field"> + <label class="siimple-field-label">Language</label> + <select class="siimple-select" name="language"> + @@languages@@ + </select> + </div> + + <div class="siimple-form-field"> + <input type="submit" class="siimple-btn siimple-btn--blue"></input> + </div> + </form> + </div>