diff --git a/TODO.md b/TODO.md

index 61b8a55447d832b09932d121bceac32c53552478..92d1ba407b76d776d4860603d0e142e7ce600226 100644

--- a/TODO.md

+++ b/TODO.md

@@ -1,10 +1,7 @@

# initial todo

- avoid serving of data files?!

-- implement display of question

- - single question

-- implement search

- - check soundslike search

-- URI:encode

+- search using FTS

+- URI:encode/decode for params

- built admin interface

- consolidate things

- header generation

diff --git a/data/data.sqlite.example b/data/data.sqlite.example

index a1480e5b5f170772dbe223cdc1a859996ed4f9ad..8ec065e65fff590b317f61acd2b74a8f3dd25909 100644

Binary files a/data/data.sqlite.example and b/data/data.sqlite.example differ

diff --git a/faqs.pl b/faqs.pl

index df5ddc88784c02a33f9c55236a8837d4c4e0eee9..0e6fde0690fb597cc5e4537205edfd17e9c4f24c 100755

--- a/faqs.pl

+++ b/faqs.pl

@@ -40,35 +40,35 @@ if ( $query =~ /tag=([0-9]+)/i ) {

return "SELECT q.* FROM questions q JOIN tags_questions tq ON q.id = tq.q_id WHERE tq.t_id = $1";

}

+ if ( $query =~ /faq=([0-9]+)/i ) {

+ return "SELECT q.* FROM questions q JOIN tags_questions tq ON q.id = tq.q_id WHERE q.id = $1";

+ }

write_response('INTERNAL_SERVER_ERROR', 'CGI execution error', undef);

}

sub faqs

{

+ my @return;

my $dbh = DBI->connect($CONF{'dsn'}, '', '', { RaiseError => 1 }) or die $DBI::errstr;

- my @return;

- my $stmt = $dbh->prepare(sql());

- $stmt->execute();

-

- my $rows = $stmt->fetchall_arrayref;

+ my @rows = $dbh->selectall_array(sql());

$dbh->disconnect();

- if ( !scalar @$rows ) {

+ if ( !scalar @rows ) {

push @return, 'No faqs found!';

}

else {

- foreach (@$rows) {

+ foreach (@rows) {

push @return, sprintf("### %s", @$_[1]);

push @return, '';

push @return, @$_[2];

- push @return, '';

}

}

+ push @return, '';

return @return;

}

sub header

{

- return ( '# FAQs on '. $CONF{'name'}, '');

+ return ( '# '. $CONF{'name'}, '');

}

diff --git a/index.pl b/index.pl

index d24d102d296328527890cec3ac8fc4900b57c7f2..9b27f07e619d9075b8f8947788a7f825b4d6b097 100755

--- a/index.pl

+++ b/index.pl

@@ -30,7 +30,7 @@ exit;

sub body

{

- return ('## Meta', '', 'Search', '=> tags.pl Tags', '=> faqs.pl View all');

+ return ('## Meta', '', '=> search.pl Search', '=> tags.pl Tags', '=> faqs.pl View all', '');

}

sub header

@@ -41,5 +41,5 @@ my $tagcount = $dbh->selectrow_array("SELECT count(id) from tags");

my $faqcount = $dbh->selectrow_array("SELECT count(id) from questions");

$dbh->disconnect();

- return ('# Welcome to '. $CONF{'name'}, '', $CONF{'intro'}, '', sprintf('We are currently serving %d FAQs categorized with %d tags!', $faqcount, $tagcount), '');

+ return ('# '. $CONF{'name'}, '', $CONF{'intro'}, '', sprintf('We are currently serving %d FAQs categorized with %d tags!', $faqcount, $tagcount), '');

}

diff --git a/search.pl b/search.pl

new file mode 100755

index 0000000000000000000000000000000000000000..4d157ecfa7227b4663ab821c3d0360a06326c5c0

--- /dev/null

+++ b/search.pl

@@ -0,0 +1,69 @@

+#!/usr/bin/perl

+# Copyright René Wagner 2020

+# licenced under BSD 3-Clause licence

+# https://git.sr.ht/~rwa/gmni-perl-cgi-demo

+

+use strict;

+use DBI;

+use lib 'lib/';

+use gmnifaq;

+

+config();

+

+# enable UTF-8 mode for everything

+use utf8;

+binmode STDOUT, ':utf8';

+binmode STDERR, ':utf8';

+

+if (!defined($ENV{'SERVER_PROTOCOL'}) || $ENV{'SERVER_PROTOCOL'} ne 'GEMINI') {

+ write_response('CGI_ERROR', 'CGI execution error', undef);

+}

+

+my $query = lc($ENV{'QUERY_STRING'});

+if ($query eq '')

+{

+ write_response('INPUT', 'Search faqs for the following terms (blank-space separated):', undef);

+}

+

+my @body = ();

+push @body, header();

+push @body, search($query);

+push @body, footer();

+

+write_response('SUCCESS', 'text/gemini', @body);

+

+exit;

+

+sub search

+{

+ my ($term) = @_;

+ my @result = ();

+

+ my $dbh = DBI->connect($CONF{'dsn'}, '', '', { RaiseError => 1 }) or die $DBI::errstr;

+ my @matchingtags = $dbh->selectall_array("SELECT id, name, count(t_id) FROM tags LEFT JOIN tags_questions ON tags_questions.t_id = tags.id WHERE name LIKE '%$term%' GROUP BY t_id");

+ my @matchingfaqs = $dbh->selectall_array("SELECT * FROM questions WHERE question LIKE '%$term%' OR answer LIKE '%$term%'");

+ $dbh->disconnect();

+

+ if ( scalar(@matchingtags)) {

+ push @result, '## matching tags';

+ push @result, '';

+ foreach (@matchingtags) {

+ push @result, sprintf("=> faqs.pl?tag=%d %s (%d entrys)", @$_[0], @$_[1], @$_[2]);

+ }

+ }

+

+ if ( scalar(@matchingfaqs)) {

+ push @result, '## matching tags';

+ push @result, '';

+ foreach (@matchingfaqs) {

+ push @result, sprintf("=> faqs.pl?faq=%d %s", @$_[0], @$_[1]);

+ }

+ }

+ push @result, '';

+ return @result;

+}

+

+sub header

+{

+ return ('# '. $CONF{'name'}, '', "Matching results for your search term '$query':", '');

+}

diff --git a/tags.pl b/tags.pl

index 60cd01025990abd3d72a6cee6469be7ee589a600..805b3235d2e0ca22d56c67162c35178a842f98c0 100755

--- a/tags.pl

+++ b/tags.pl

@@ -32,25 +32,23 @@ sub tags

{

my $dbh = DBI->connect($CONF{'dsn'}, '', '', { RaiseError => 1 }) or die $DBI::errstr;

- my @tags;

- my $stmt = $dbh->prepare('SELECT id, name, count(t_id) FROM tags LEFT JOIN tags_questions ON tags_questions.t_id = tags.id GROUP BY t_id');

- $stmt->execute();

-

- my $rows = $stmt->fetchall_arrayref;

+ my @result;

+ my @rows = $dbh->selectall_array('SELECT id, name, count(t_id) FROM tags LEFT JOIN tags_questions ON tags_questions.t_id = tags.id GROUP BY t_id');

$dbh->disconnect();

- if ( !scalar @$rows ) {

- push @tags, 'No tags found!';

+ if ( !scalar @rows ) {

+ push @result, 'No tags found!';

}

else {

- foreach (@$rows) {

- push @tags, sprintf("=> faqs.pl?tag=%d %s (%d entrys)", @$_[0], @$_[1], @$_[2]);

+ foreach (@rows) {

+ push @result, sprintf("=> faqs.pl?tag=%d %s (%d entrys)", @$_[0], @$_[1], @$_[2]);

}

}

- return @tags;

+ push @result, '';

+ return @result;

}

sub header

{

- return ('# Welcome to '. $CONF{'name'}, '', 'Select a tag to browse the questions associated with this tag.', '', '## Tags', '');

+ return ('# '. $CONF{'name'}, '', 'Select a tag to browse the questions associated with this tag.', '', '## Tags', '');

}