]>
git.wh0rd.org - tt-rss.git/blob - plugins/af_psql_trgm/init.php
f41455f832ae63b49d5be43d41ed2d2bbf06ba7c
2 class Af_Psql_Trgm
extends Plugin
{
8 "Marks similar articles as read (requires pg_trgm)" ,
13 $similarity = ( float ) db_escape_string ( $_POST [ "similarity" ]);
14 $min_title_length = ( int ) db_escape_string ( $_POST [ "min_title_length" ]);
16 if ( $similarity < 0 ) $similarity = 0 ;
17 if ( $similarity > 1 ) $similarity = 1 ;
19 if ( $min_title_length < 0 ) $min_title_length = 0 ;
21 $similarity = sprintf ( "%.2f" , $similarity );
23 $this -> host
-> set ( $this , "similarity" , $similarity );
24 $this -> host
-> set ( $this , "min_title_length" , $min_title_length );
26 echo T_sprintf ( "Data saved ( %s )" , $similarity );
29 function init ( $host ) {
32 $host -> add_hook ( $host :: HOOK_ARTICLE_FILTER
, $this );
33 $host -> add_hook ( $host :: HOOK_PREFS_TAB
, $this );
34 $host -> add_hook ( $host :: HOOK_PREFS_EDIT_FEED
, $this );
35 $host -> add_hook ( $host :: HOOK_PREFS_SAVE_FEED
, $this );
36 $host -> add_hook ( $host :: HOOK_ARTICLE_BUTTON
, $this );
41 return file_get_contents ( __DIR__
. "/init.js" );
44 function showrelated () {
45 $id = ( int ) db_escape_string ( $_REQUEST [ 'param' ]);
46 $owner_uid = $_SESSION [ "uid" ];
48 $result = db_query ( "SELECT title FROM ttrss_entries, ttrss_user_entries
49 WHERE ref_id = id AND id = $id AND owner_uid = $owner_uid " );
51 $title = db_fetch_result ( $result , 0 , "title" );
53 print "<h2> $title </h2>" ;
55 $title = db_escape_string ( $title );
56 $result = db_query ( "SELECT ttrss_entries.id AS id,
58 ttrss_entries.title AS title,
60 ttrss_feeds.title AS feed_title,
61 SIMILARITY(ttrss_entries.title, ' $title ') AS sm
63 ttrss_entries, ttrss_user_entries LEFT JOIN ttrss_feeds ON (ttrss_feeds.id = feed_id)
65 ttrss_entries.id = ref_id AND
66 ttrss_user_entries.owner_uid = $owner_uid AND
67 ttrss_entries.id != $id AND
68 date_entered >= NOW() - INTERVAL '2 weeks'
70 sm DESC, date_entered DESC
73 print "<ul class= \" browseFeedList \" style= \" border-width : 1px \" >" ;
75 while ( $line = db_fetch_assoc ( $result )) {
77 print "<div class='insensitive small' style='margin-left : 20px; float : right'>" .
78 smart_date_time ( strtotime ( $line [ "updated" ]))
81 $sm = sprintf ( "%.2f" , $line [ 'sm' ]);
82 print "<img src='images/score_high.png' title=' $sm '
83 style='vertical-align : middle'>" ;
85 $article_link = htmlspecialchars ( $line [ "link" ]);
86 print " <a target= \" _blank \" href= \" $article_link\" >" .
87 $line [ "title" ]. "</a>" ;
89 print " (<a href= \" # \" onclick= \" viewfeed(" . $line [ "feed_id" ]. ") \" >" .
90 htmlspecialchars ( $line [ "feed_title" ]). "</a>)" ;
92 print " <span class='insensitive'>( $sm )</span>" ;
99 print "<div style='text-align : center'>" ;
100 print "<button dojoType= \" dijit.form.Button \" onclick= \" dijit.byId('trgmRelatedDlg').hide() \" >" . __ ( 'Close this window' ). "</button>" ;
106 function hook_article_button ( $line ) {
107 return "<img src= \" plugins/af_psql_trgm/button.png \"
108 style= \" cursor : pointer \" style= \" cursor : pointer \"
109 onclick= \" showTrgmRelated(" . $line [ "id" ]. ") \"
110 class='tagsPic' title='" . __ ( 'Show related articles' ). "'>" ;
113 function hook_prefs_tab ( $args ) {
114 if ( $args != "prefFeeds" ) return ;
116 print "<div dojoType= \" dijit.layout.AccordionPane \" title= \" " . __ ( 'Mark similar articles as read' ). " \" >" ;
118 if ( DB_TYPE
!= "pgsql" ) {
119 print_error ( "Database type not supported." );
122 $result = db_query ( "select 'similarity'::regproc" );
124 if ( db_num_rows ( $result ) == 0 ) {
125 print_error ( "pg_trgm extension not found." );
128 $similarity = $this -> host
-> get ( $this , "similarity" );
129 $min_title_length = $this -> host
-> get ( $this , "min_title_length" );
131 if (! $similarity ) $similarity = '0.75' ;
132 if (! $min_title_length ) $min_title_length = '32' ;
134 print "<form dojoType= \" dijit.form.Form \" >" ;
136 print "<script type= \" dojo/method \" event= \" onSubmit \" args= \" evt \" >
137 evt.preventDefault();
138 if (this.validate()) {
139 console.log(dojo.objectToQuery(this.getValues()));
140 new Ajax.Request('backend.php', {
141 parameters: dojo.objectToQuery(this.getValues()),
142 onComplete: function(transport) {
143 notify_info(transport.responseText);
150 print "<input dojoType= \" dijit.form.TextBox \" style= \" display : none \" name= \" op \" value= \" pluginhandler \" >" ;
151 print "<input dojoType= \" dijit.form.TextBox \" style= \" display : none \" name= \" method \" value= \" save \" >" ;
152 print "<input dojoType= \" dijit.form.TextBox \" style= \" display : none \" name= \" plugin \" value= \" af_psql_trgm \" >" ;
154 print_notice ( "PostgreSQL trigram extension returns string similarity as a floating point number (0-1). Setting it too low might produce false positives, zero disables checking." );
157 print_notice ( "Enable the plugin for specific feeds in the feed editor." );
159 print "<h3>" . __ ( "Global settings" ) . "</h3>" ;
163 print "<tr><td width= \" 40% \" >" . __ ( "Minimum similarity:" ). "</td>" ;
165 <input dojoType= \" dijit.form.ValidationTextBox \"
167 required= \" 1 \" name= \" similarity \" value= \" $similarity\" ></td></tr>" ;
168 print "<tr><td width= \" 40% \" >" . __ ( "Minimum title length:" ). "</td>" ;
170 <input dojoType= \" dijit.form.ValidationTextBox \"
172 required= \" 1 \" name= \" min_title_length \" value= \" $min_title_length\" ></td></tr>" ;
176 print "<p><button dojoType= \" dijit.form.Button \" type= \" submit \" >" .
177 __ ( "Save" ). "</button>" ;
181 $enabled_feeds = $this -> host
-> get ( $this , "enabled_feeds" );
182 if (! array ( $enabled_feeds )) $enabled_feeds = array ();
184 $enabled_feeds = $this -> filter_unknown_feeds ( $enabled_feeds );
185 $this -> host
-> set ( $this , "enabled_feeds" , $enabled_feeds );
187 if ( count ( $enabled_feeds ) > 0 ) {
188 print "<h3>" . __ ( "Currently enabled for (click to edit):" ) . "</h3>" ;
190 print "<ul class= \" browseFeedList \" style= \" border-width : 1px \" >" ;
191 foreach ( $enabled_feeds as $f ) {
193 "<img src='images/pub_set.png'
194 style='vertical-align : middle'> <a href='#'
195 onclick='editFeed( $f )'>" .
196 getFeedTitle ( $f ) . "</a></li>" ;
204 function hook_prefs_edit_feed ( $feed_id ) {
205 print "<div class= \" dlgSec \" >" . __ ( "Similarity (pg_trgm)" ). "</div>" ;
206 print "<div class= \" dlgSecCont \" >" ;
208 $enabled_feeds = $this -> host
-> get ( $this , "enabled_feeds" );
209 if (! array ( $enabled_feeds )) $enabled_feeds = array ();
211 $key = array_search ( $feed_id , $enabled_feeds );
212 $checked = $key !== FALSE ?
"checked" : "" ;
214 print "<hr/><input dojoType= \" dijit.form.CheckBox \" type= \" checkbox \" id= \" trgm_similarity_enabled \"
215 name= \" trgm_similarity_enabled \"
216 $checked > <label for= \" trgm_similarity_enabled \" >" . __ ( 'Mark similar articles as read' ). "</label>" ;
221 function hook_prefs_save_feed ( $feed_id ) {
222 $enabled_feeds = $this -> host
-> get ( $this , "enabled_feeds" );
223 if (! is_array ( $enabled_feeds )) $enabled_feeds = array ();
225 $enable = checkbox_to_sql_bool ( $_POST [ "trgm_similarity_enabled" ]) == 'true' ;
226 $key = array_search ( $feed_id , $enabled_feeds );
229 if ( $key === FALSE ) {
230 array_push ( $enabled_feeds , $feed_id );
233 if ( $key !== FALSE ) {
234 unset ( $enabled_feeds [ $key ]);
238 $this -> host
-> set ( $this , "enabled_feeds" , $enabled_feeds );
241 function hook_article_filter ( $article ) {
243 if ( DB_TYPE
!= "pgsql" ) return $article ;
245 $result = db_query ( "select 'similarity'::regproc" );
246 if ( db_num_rows ( $result ) == 0 ) return $article ;
248 $enabled_feeds = $this -> host
-> get ( $this , "enabled_feeds" );
249 $key = array_search ( $article [ "feed" ][ "id" ], $enabled_feeds );
250 if ( $key === FALSE ) return $article ;
252 $similarity = ( float ) $this -> host
-> get ( $this , "similarity" );
253 if ( $similarity < 0.01 ) return $article ;
255 $min_title_length = ( int ) $this -> host
-> get ( $this , "min_length" );
256 if ( mb_strlen ( $article [ "title" ]) < $min_title_length ) return $article ;
259 $owner_uid = $article [ "owner_uid" ];
260 $feed_id = $article [ "feed" ][ "id" ];
261 $title_escaped = db_escape_string ( $article [ "title" ]);
263 // trgm does not return similarity=1 for completely equal strings
265 $result = db_query ( "SELECT COUNT(id) AS nequal
266 FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id AND
267 date_entered >= NOW() - interval '1 day' AND
268 title = ' $title_escaped ' AND
269 feed_id != ' $feed_id ' AND
270 owner_uid = $owner_uid " );
272 $nequal = db_fetch_result ( $result , 0 , "nequal" );
273 _debug ( "af_psql_trgm: num equals: $nequal " );
276 $article [ "force_catchup" ] = true ;
280 $result = db_query ( "SELECT MAX(SIMILARITY(title, ' $title_escaped ')) AS ms
281 FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id AND
282 date_entered >= NOW() - interval '1 day' AND
283 feed_id != ' $feed_id ' AND
284 owner_uid = $owner_uid " );
286 $similarity_result = db_fetch_result ( $result , 0 , "ms" );
288 _debug ( "af_psql_trgm: similarity result: $similarity_result " );
290 if ( $similarity_result >= $similarity ) {
291 $article [ "force_catchup" ] = true ;
298 function api_version () {
302 private function filter_unknown_feeds ( $enabled_feeds ) {
305 foreach ( $enabled_feeds as $feed ) {
307 $result = db_query ( "SELECT id FROM ttrss_feeds WHERE id = ' $feed ' AND owner_uid = " . $_SESSION [ "uid" ]);
309 if ( db_num_rows ( $result ) != 0 ) {
310 array_push ( $tmp , $feed );