02d1ed6ad2 2008-02-02 stephan: /* 02d1ed6ad2 2008-02-02 stephan: ** Copyright (c) 2007 D. Richard Hipp 2ab3a2f603 2008-02-03 stephan: ** Copyright (c) 2008 Stephan Beal 02d1ed6ad2 2008-02-02 stephan: ** 02d1ed6ad2 2008-02-02 stephan: ** This program is free software; you can redistribute it and/or 02d1ed6ad2 2008-02-02 stephan: ** modify it under the terms of the GNU General Public 02d1ed6ad2 2008-02-02 stephan: ** License as published by the Free Software Foundation; either 02d1ed6ad2 2008-02-02 stephan: ** version 2 of the License, or (at your option) any later version. 02d1ed6ad2 2008-02-02 stephan: ** 02d1ed6ad2 2008-02-02 stephan: ** This program is distributed in the hope that it will be useful, 02d1ed6ad2 2008-02-02 stephan: ** but WITHOUT ANY WARRANTY; without even the implied warranty of 02d1ed6ad2 2008-02-02 stephan: ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 02d1ed6ad2 2008-02-02 stephan: ** General Public License for more details. 02d1ed6ad2 2008-02-02 stephan: ** 02d1ed6ad2 2008-02-02 stephan: ** You should have received a copy of the GNU General Public 02d1ed6ad2 2008-02-02 stephan: ** License along with this library; if not, write to the 02d1ed6ad2 2008-02-02 stephan: ** Free Software Foundation, Inc., 59 Temple Place - Suite 330, 02d1ed6ad2 2008-02-02 stephan: ** Boston, MA 02111-1307, USA. 02d1ed6ad2 2008-02-02 stephan: ** 02d1ed6ad2 2008-02-02 stephan: ** Author contact information: 02d1ed6ad2 2008-02-02 stephan: ** drh@hwaci.com 02d1ed6ad2 2008-02-02 stephan: ** http://www.hwaci.com/drh/ 02d1ed6ad2 2008-02-02 stephan: ** 02d1ed6ad2 2008-02-02 stephan: ******************************************************************************* 02d1ed6ad2 2008-02-02 stephan: ** 02d1ed6ad2 2008-02-02 stephan: ** Implementation of the Tag View page 02d1ed6ad2 2008-02-02 stephan: */ 02d1ed6ad2 2008-02-02 stephan: #include <assert.h> 02d1ed6ad2 2008-02-02 stephan: #include "config.h" 02d1ed6ad2 2008-02-02 stephan: #include "tagview.h" 02d1ed6ad2 2008-02-02 stephan: 02a7c850b4 2008-02-03 stephan: /** b81e93f576 2008-02-03 stephan: tagview_strxform_f is a typedef for funcs with the following policy: b81e93f576 2008-02-03 stephan: b81e93f576 2008-02-03 stephan: They accept a const string which they then transform into some other b81e93f576 2008-02-03 stephan: form. They return a transformed copy, which the caller is responsible b81e93f576 2008-02-03 stephan: for freeing. b81e93f576 2008-02-03 stephan: b81e93f576 2008-02-03 stephan: The intention of this is to provide a way for a generic query routine b81e93f576 2008-02-03 stephan: to format specific column data (e.g. transform an object ID into a b81e93f576 2008-02-03 stephan: link to that object). 02d1ed6ad2 2008-02-02 stephan: */ 02a7c850b4 2008-02-03 stephan: typedef char * (*tagview_strxform_f)( char const * ); 02a7c850b4 2008-02-03 stephan: 02a7c850b4 2008-02-03 stephan: #if 0 02a7c850b4 2008-02-03 stephan: /** A no-op transformer which can be used as a placeholder. */ 02a7c850b4 2008-02-03 stephan: static char * tagview_xf_copy( char const * uuid ) 02a7c850b4 2008-02-03 stephan: { 02a7c850b4 2008-02-03 stephan: int len = strlen(uuid) + 1; 02a7c850b4 2008-02-03 stephan: char * ret = (char *) malloc( len ); 02a7c850b4 2008-02-03 stephan: ret[len] = '\0'; 02a7c850b4 2008-02-03 stephan: strncpy( ret, uuid, len-1 ); 02a7c850b4 2008-02-03 stephan: return ret; 02a7c850b4 2008-02-03 stephan: } 02a7c850b4 2008-02-03 stephan: #endif 02a7c850b4 2008-02-03 stephan: 02a7c850b4 2008-02-03 stephan: /** Returns a hyperlink to uuid. */ 02a7c850b4 2008-02-03 stephan: static char * tagview_xf_link_to_uuid( char const * uuid ) 02a7c850b4 2008-02-03 stephan: { b81e93f576 2008-02-03 stephan: const int offset = 10; b81e93f576 2008-02-03 stephan: char shortname[offset+1]; b81e93f576 2008-02-03 stephan: shortname[offset] = '\0'; b81e93f576 2008-02-03 stephan: memcpy( shortname, uuid, offset ); b81e93f576 2008-02-03 stephan: return mprintf( "<tt><a href='%s/vinfo/%s'><strong>%s</strong>%s</a></tt>", b81e93f576 2008-02-03 stephan: g.zBaseURL, uuid, shortname, uuid+offset ); 02a7c850b4 2008-02-03 stephan: } 02a7c850b4 2008-02-03 stephan: 02a7c850b4 2008-02-03 stephan: /** Returns a hyperlink to the given tag. */ 02a7c850b4 2008-02-03 stephan: static char * tagview_xf_link_to_tagid( char const * tagid ) 02a7c850b4 2008-02-03 stephan: { 02a7c850b4 2008-02-03 stephan: return mprintf( "<a href='%s/tagview?tagid=%s'>%s</a>", b81e93f576 2008-02-03 stephan: g.zBaseURL, tagid, tagid ); 02a7c850b4 2008-02-03 stephan: } 02a7c850b4 2008-02-03 stephan: 02a7c850b4 2008-02-03 stephan: /** Returns a hyperlink to the named tag. */ 02a7c850b4 2008-02-03 stephan: static char * tagview_xf_link_to_tagname( char const * tagid ) 02a7c850b4 2008-02-03 stephan: { 02a7c850b4 2008-02-03 stephan: return mprintf( "<a href='%s/tagview/%s'>%s</a>", b81e93f576 2008-02-03 stephan: g.zBaseURL, tagid, tagid ); 02d1ed6ad2 2008-02-02 stephan: } 02d1ed6ad2 2008-02-02 stephan: 02a7c850b4 2008-02-03 stephan: 02a7c850b4 2008-02-03 stephan: 02a7c850b4 2008-02-03 stephan: /** 02a7c850b4 2008-02-03 stephan: * tagview_run_query(): 02a7c850b4 2008-02-03 stephan: * 02a7c850b4 2008-02-03 stephan: * A very primitive helper to run an SQL query and table-ize the 02a7c850b4 2008-02-03 stephan: * results. 02a7c850b4 2008-02-03 stephan: * 02a7c850b4 2008-02-03 stephan: * The sql parameter should be a single, complete SQL statement. 02a7c850b4 2008-02-03 stephan: * 02a7c850b4 2008-02-03 stephan: * The coln parameter is optional (it may be 0). If it is 0 then the b81e93f576 2008-02-03 stephan: * column names used in the output will be taken directly from the 02a7c850b4 2008-02-03 stephan: * SQL. If it is not null then it must have as many entries as the SQL 02a7c850b4 2008-02-03 stephan: * result has columns. Each entry is a column name for the SQL result 02a7c850b4 2008-02-03 stephan: * column of the same index. Any given entry may be 0, in which case 02a7c850b4 2008-02-03 stephan: * the column name from the SQL is used. 02a7c850b4 2008-02-03 stephan: * 02a7c850b4 2008-02-03 stephan: * The xform argument is an array of transformation functions (type 02a7c850b4 2008-02-03 stephan: * tagview_strxform_f). The array, or any single entry, may be 0, but 02a7c850b4 2008-02-03 stephan: * if the array is non-0 then it must have at least as many entries as 02a7c850b4 2008-02-03 stephan: * colnames does. Each index corresponds directly to an entry in b81e93f576 2008-02-03 stephan: * colnames and the SQL results. Any given entry may be 0. If it has 02a7c850b4 2008-02-03 stephan: * fewer, undefined behaviour results. If a column has an entry in 02a7c850b4 2008-02-03 stephan: * xform, then the xform function will be called to transform the 02a7c850b4 2008-02-03 stephan: * column data before rendering it. This function takes care of freeing 02a7c850b4 2008-02-03 stephan: * the strings created by the xform functions. 02a7c850b4 2008-02-03 stephan: * 02a7c850b4 2008-02-03 stephan: * Example: 02a7c850b4 2008-02-03 stephan: * 02a7c850b4 2008-02-03 stephan: * char const * const colnames[] = { 02a7c850b4 2008-02-03 stephan: * "Tag ID", "Tag Name", "Something Else", "UUID" 02a7c850b4 2008-02-03 stephan: * }; 02a7c850b4 2008-02-03 stephan: * tagview_strxform_f xf[] = { 02a7c850b4 2008-02-03 stephan: * tagview_xf_link_to_tagid, 02a7c850b4 2008-02-03 stephan: * tagview_xf_link_to_tagname, 02a7c850b4 2008-02-03 stephan: * 0, 02a7c850b4 2008-02-03 stephan: * tagview_xf_link_to_uuid 02a7c850b4 2008-02-03 stephan: * }; 02a7c850b4 2008-02-03 stephan: * tagview_run_query( "select a,b,c,d from foo", colnames, xf ); 02a7c850b4 2008-02-03 stephan: * 02d1ed6ad2 2008-02-02 stephan: */ 02a7c850b4 2008-02-03 stephan: static void tagview_run_query( 02a7c850b4 2008-02-03 stephan: char const * sql, 02a7c850b4 2008-02-03 stephan: char const * const * coln, 02a7c850b4 2008-02-03 stephan: tagview_strxform_f * xform ) 2ab3a2f603 2008-02-03 stephan: { 02d1ed6ad2 2008-02-02 stephan: 2ab3a2f603 2008-02-03 stephan: Stmt st; 02d1ed6ad2 2008-02-02 stephan: @ <table cellpadding='4px' border='1'><tbody> 02a7c850b4 2008-02-03 stephan: int i = 0; 02a7c850b4 2008-02-03 stephan: int rc = db_prepare( &st, sql ); 02a7c850b4 2008-02-03 stephan: /** 02a7c850b4 2008-02-03 stephan: Achtung: makeheaders apparently can't pull the function 02a7c850b4 2008-02-03 stephan: name from this: 02a7c850b4 2008-02-03 stephan: if( SQLITE_OK != db_prepare( &st, sql ) ) 02a7c850b4 2008-02-03 stephan: */ 02a7c850b4 2008-02-03 stephan: if( SQLITE_OK != rc ) 02d1ed6ad2 2008-02-02 stephan: { 02a7c850b4 2008-02-03 stephan: @ tagview_run_query(): Error processing SQL: [%s(sql)] 02a7c850b4 2008-02-03 stephan: return; 02a7c850b4 2008-02-03 stephan: } 02a7c850b4 2008-02-03 stephan: int colc = db_column_count(&st); 02a7c850b4 2008-02-03 stephan: @ <tr> 02a7c850b4 2008-02-03 stephan: for( i = 0; i < colc; ++i ) { 02a7c850b4 2008-02-03 stephan: if( coln ) 02a7c850b4 2008-02-03 stephan: { 02a7c850b4 2008-02-03 stephan: @ <th>%s(coln[i] ? coln[i] : db_column_name(&st,i))</th> 02a7c850b4 2008-02-03 stephan: } 02a7c850b4 2008-02-03 stephan: else 02a7c850b4 2008-02-03 stephan: { 02a7c850b4 2008-02-03 stephan: @ <td>%s(db_column_name(&st,i))</td> 02a7c850b4 2008-02-03 stephan: } 02a7c850b4 2008-02-03 stephan: } 02a7c850b4 2008-02-03 stephan: @ </tr> 02a7c850b4 2008-02-03 stephan: 02a7c850b4 2008-02-03 stephan: while( SQLITE_ROW == db_step(&st) ){ 10437374a7 2008-02-02 drh: @ <tr> 02a7c850b4 2008-02-03 stephan: for( i = 0; i < colc; ++i ) { 02a7c850b4 2008-02-03 stephan: char * xf = 0; 02a7c850b4 2008-02-03 stephan: char const * xcf = 0; 02a7c850b4 2008-02-03 stephan: xcf = (xform && xform[i]) 02a7c850b4 2008-02-03 stephan: ? (xf=(xform[i])(db_column_text(&st,i))) 02a7c850b4 2008-02-03 stephan: : db_column_text(&st,i); 02a7c850b4 2008-02-03 stephan: @ <td>%s(xcf)</td> 02a7c850b4 2008-02-03 stephan: if( xf ) free( xf ); 02a7c850b4 2008-02-03 stephan: } 02a7c850b4 2008-02-03 stephan: @ </tr> 02d1ed6ad2 2008-02-02 stephan: } 02d1ed6ad2 2008-02-02 stephan: db_finalize( &st ); 02d1ed6ad2 2008-02-02 stephan: @ </tbody></table> 02a7c850b4 2008-02-03 stephan: } 02a7c850b4 2008-02-03 stephan: 02a7c850b4 2008-02-03 stephan: /** 02a7c850b4 2008-02-03 stephan: Lists all tags matching the given LIKE clause (which 02a7c850b4 2008-02-03 stephan: may be 0). 02a7c850b4 2008-02-03 stephan: */ 02a7c850b4 2008-02-03 stephan: static void tagview_page_list_tags( char const * like ) 02a7c850b4 2008-02-03 stephan: { 2ab3a2f603 2008-02-03 stephan: char * likeclause = 0; 2ab3a2f603 2008-02-03 stephan: const int limit = 10; 2ab3a2f603 2008-02-03 stephan: char * limitstr = 0; 2ab3a2f603 2008-02-03 stephan: if( like && strlen(like) ) 2ab3a2f603 2008-02-03 stephan: { 2ab3a2f603 2008-02-03 stephan: likeclause = mprintf( "AND t.tagname LIKE '%%%%%q%%%%'", like ); 2ab3a2f603 2008-02-03 stephan: @ <h2>Tags matching [%s(likeclause)]:</h2> 2ab3a2f603 2008-02-03 stephan: } 2ab3a2f603 2008-02-03 stephan: else 2ab3a2f603 2008-02-03 stephan: { 2ab3a2f603 2008-02-03 stephan: limitstr = mprintf( "LIMIT %d", limit ); b81e93f576 2008-02-03 stephan: @ <h2>%d(limit) most recent non-wiki tags:</h2> 02a7c850b4 2008-02-03 stephan: } 2ab3a2f603 2008-02-03 stephan: char * sql = mprintf( 2ab3a2f603 2008-02-03 stephan: "SELECT t.tagid, t.tagname, DATETIME(tx.mtime), b.uuid " 2ab3a2f603 2008-02-03 stephan: "FROM tag t, tagxref tx, blob b " 2ab3a2f603 2008-02-03 stephan: "WHERE (t.tagid=tx.tagid) and (tx.srcid=b.rid) " 2ab3a2f603 2008-02-03 stephan: "AND (tx.tagtype != 0) %s " b81e93f576 2008-02-03 stephan: "AND t.tagname NOT GLOB 'wiki-*' " 2ab3a2f603 2008-02-03 stephan: "ORDER BY tx.mtime DESC %s", 2ab3a2f603 2008-02-03 stephan: likeclause ? likeclause : " ", 2ab3a2f603 2008-02-03 stephan: limitstr ? limitstr : " " 2ab3a2f603 2008-02-03 stephan: ); 02a7c850b4 2008-02-03 stephan: /* " AND t.tagname NOT GLOB 'wiki-*'" // Do we want this?? */ 2ab3a2f603 2008-02-03 stephan: 02a7c850b4 2008-02-03 stephan: char const * const colnames[] = { 02a7c850b4 2008-02-03 stephan: "Tag ID", "Name", "Timestamp", "Version" 02a7c850b4 2008-02-03 stephan: }; 02a7c850b4 2008-02-03 stephan: tagview_strxform_f xf[] = { 02a7c850b4 2008-02-03 stephan: tagview_xf_link_to_tagid, 02a7c850b4 2008-02-03 stephan: tagview_xf_link_to_tagname, 02a7c850b4 2008-02-03 stephan: 0, 02a7c850b4 2008-02-03 stephan: tagview_xf_link_to_uuid 02a7c850b4 2008-02-03 stephan: }; 02a7c850b4 2008-02-03 stephan: tagview_run_query( sql, colnames, xf ); 02a7c850b4 2008-02-03 stephan: free( sql ); 2ab3a2f603 2008-02-03 stephan: } 2ab3a2f603 2008-02-03 stephan: 02a7c850b4 2008-02-03 stephan: /** 02a7c850b4 2008-02-03 stephan: A small search form which forwards to ?like=SEARCH_STRING 02a7c850b4 2008-02-03 stephan: */ 2ab3a2f603 2008-02-03 stephan: static void tagview_page_search_miniform(void){ 2ab3a2f603 2008-02-03 stephan: char const * like = P("like"); 2ab3a2f603 2008-02-03 stephan: @ <div style='font-size:smaller'> 2ab3a2f603 2008-02-03 stephan: @ <form action='/tagview' method='post'> 2ab3a2f603 2008-02-03 stephan: @ Search for tags: 2ab3a2f603 2008-02-03 stephan: @ <input type='text' name='like' value='%s((like?like:""))' size='10'/> 2ab3a2f603 2008-02-03 stephan: @ <input type='submit'/> 2ab3a2f603 2008-02-03 stephan: @ </form> 2ab3a2f603 2008-02-03 stephan: @ </div> 2ab3a2f603 2008-02-03 stephan: } 2ab3a2f603 2008-02-03 stephan: b81e93f576 2008-02-03 stephan: /** b81e93f576 2008-02-03 stephan: tagview_page_default() renders the default page for tagview_page(). b81e93f576 2008-02-03 stephan: */ 2ab3a2f603 2008-02-03 stephan: static void tagview_page_default(void){ 2ab3a2f603 2008-02-03 stephan: tagview_page_list_tags( 0 ); 2ab3a2f603 2008-02-03 stephan: } 2ab3a2f603 2008-02-03 stephan: 02a7c850b4 2008-02-03 stephan: /** 02a7c850b4 2008-02-03 stephan: Lists all tags matching the given tagid. 02a7c850b4 2008-02-03 stephan: */ 2ab3a2f603 2008-02-03 stephan: static void tagview_page_tag_by_id( int tagid ) 2ab3a2f603 2008-02-03 stephan: { 02a7c850b4 2008-02-03 stephan: @ <h2>Tag #%d(tagid):</h2> 2ab3a2f603 2008-02-03 stephan: char * sql = mprintf( 2ab3a2f603 2008-02-03 stephan: "SELECT DISTINCT (t.tagname), DATETIME(tx.mtime), b.uuid " 2ab3a2f603 2008-02-03 stephan: "FROM tag t, tagxref tx, blob b " 2ab3a2f603 2008-02-03 stephan: "WHERE (t.tagid=%d) AND (t.tagid=tx.tagid) AND (tx.srcid=b.rid) " b81e93f576 2008-02-03 stephan: "AND t.tagname NOT GLOB 'wiki-*' " 2ab3a2f603 2008-02-03 stephan: "ORDER BY tx.mtime DESC", 2ab3a2f603 2008-02-03 stephan: tagid); 02a7c850b4 2008-02-03 stephan: char const * const colnames[] = { 02a7c850b4 2008-02-03 stephan: "Tag Name", "Timestamp", "Version" b81e93f576 2008-02-03 stephan: }; 02a7c850b4 2008-02-03 stephan: tagview_strxform_f xf[] = { 02a7c850b4 2008-02-03 stephan: tagview_xf_link_to_tagname, 02a7c850b4 2008-02-03 stephan: 0, 02a7c850b4 2008-02-03 stephan: tagview_xf_link_to_uuid b81e93f576 2008-02-03 stephan: }; 02a7c850b4 2008-02-03 stephan: tagview_run_query( sql, colnames, xf ); 02a7c850b4 2008-02-03 stephan: free(sql); 2ab3a2f603 2008-02-03 stephan: } 2ab3a2f603 2008-02-03 stephan: 02a7c850b4 2008-02-03 stephan: /** 02a7c850b4 2008-02-03 stephan: Lists all tags matching the given tag name. 02a7c850b4 2008-02-03 stephan: */ 2ab3a2f603 2008-02-03 stephan: static void tagview_page_tag_by_name( char const * tagname ) 2ab3a2f603 2008-02-03 stephan: { 02a7c850b4 2008-02-03 stephan: @ <h2>Tag '%s(tagname)':</h2> 2ab3a2f603 2008-02-03 stephan: char * sql = mprintf( 2ab3a2f603 2008-02-03 stephan: "SELECT DISTINCT t.tagid, DATETIME(tx.mtime), b.uuid " 2ab3a2f603 2008-02-03 stephan: "FROM tag t, tagxref tx, blob b " 2ab3a2f603 2008-02-03 stephan: "WHERE (t.tagname='%q') AND (t.tagid=tx.tagid) AND (tx.srcid=b.rid) " b81e93f576 2008-02-03 stephan: "AND t.tagname NOT GLOB 'wiki-*' " 2ab3a2f603 2008-02-03 stephan: "ORDER BY tx.mtime DESC", 2ab3a2f603 2008-02-03 stephan: tagname); 02a7c850b4 2008-02-03 stephan: char const * const colnames[] = { 02a7c850b4 2008-02-03 stephan: "Tag ID", "Timestamp", "Version" 02a7c850b4 2008-02-03 stephan: }; 02a7c850b4 2008-02-03 stephan: tagview_strxform_f xf[] = { 02a7c850b4 2008-02-03 stephan: tagview_xf_link_to_tagid, 02a7c850b4 2008-02-03 stephan: 0, 02a7c850b4 2008-02-03 stephan: tagview_xf_link_to_uuid 02a7c850b4 2008-02-03 stephan: }; 02a7c850b4 2008-02-03 stephan: tagview_run_query( sql, colnames, xf ); 02a7c850b4 2008-02-03 stephan: free( sql ); 02d1ed6ad2 2008-02-02 stephan: } 02d1ed6ad2 2008-02-02 stephan: 2ab3a2f603 2008-02-03 stephan: 2ab3a2f603 2008-02-03 stephan: /* 2ab3a2f603 2008-02-03 stephan: ** WEBPAGE: /tagview 2ab3a2f603 2008-02-03 stephan: */ 2ab3a2f603 2008-02-03 stephan: void tagview_page(void){ 2ab3a2f603 2008-02-03 stephan: 2ab3a2f603 2008-02-03 stephan: login_check_credentials(); b81e93f576 2008-02-03 stephan: if( !g.okRdWiki ){ 2ab3a2f603 2008-02-03 stephan: login_needed(); 2ab3a2f603 2008-02-03 stephan: } 2ab3a2f603 2008-02-03 stephan: style_header("Tags"); 2ab3a2f603 2008-02-03 stephan: tagview_page_search_miniform(); 2ab3a2f603 2008-02-03 stephan: @ <hr/> 2ab3a2f603 2008-02-03 stephan: char const * check = 0; 2ab3a2f603 2008-02-03 stephan: if( 0 != (check = P("tagid")) ) 2ab3a2f603 2008-02-03 stephan: { 2ab3a2f603 2008-02-03 stephan: tagview_page_tag_by_id( atoi(check) ); 2ab3a2f603 2008-02-03 stephan: } 2ab3a2f603 2008-02-03 stephan: else if( 0 != (check = P("like")) ) 2ab3a2f603 2008-02-03 stephan: { 2ab3a2f603 2008-02-03 stephan: tagview_page_list_tags( check ); 2ab3a2f603 2008-02-03 stephan: } 2ab3a2f603 2008-02-03 stephan: else if( 0 != (check = P("name")) ) 2ab3a2f603 2008-02-03 stephan: { 2ab3a2f603 2008-02-03 stephan: tagview_page_tag_by_name( check ); 2ab3a2f603 2008-02-03 stephan: } 2ab3a2f603 2008-02-03 stephan: else 2ab3a2f603 2008-02-03 stephan: { 2ab3a2f603 2008-02-03 stephan: tagview_page_default(); 2ab3a2f603 2008-02-03 stephan: } 10437374a7 2008-02-02 drh: style_footer(); 10437374a7 2008-02-02 drh: }