2015-01-19 10:52:15 +01:00
< ? php
class Af_Psql_Trgm extends Plugin {
2017-12-03 08:26:38 +01:00
/* @var PluginHost $host */
2015-01-19 10:52:15 +01:00
private $host ;
function about () {
return array ( 1.0 ,
" Marks similar articles as read (requires pg_trgm) " ,
" fox " );
}
function save () {
2017-12-03 08:26:38 +01:00
$similarity = ( float ) $_POST [ " similarity " ];
$min_title_length = ( int ) $_POST [ " min_title_length " ];
2017-12-02 12:07:48 +01:00
$enable_globally = checkbox_to_sql_bool ( $_POST [ " enable_globally " ]);
2015-01-19 10:52:15 +01:00
if ( $similarity < 0 ) $similarity = 0 ;
if ( $similarity > 1 ) $similarity = 1 ;
if ( $min_title_length < 0 ) $min_title_length = 0 ;
$similarity = sprintf ( " %.2f " , $similarity );
$this -> host -> set ( $this , " similarity " , $similarity );
$this -> host -> set ( $this , " min_title_length " , $min_title_length );
2015-06-23 06:36:25 +02:00
$this -> host -> set ( $this , " enable_globally " , $enable_globally );
2015-01-19 10:52:15 +01:00
2015-06-23 06:36:25 +02:00
echo T_sprintf ( " Data saved (%s, %d) " , $similarity , $enable_globally );
2015-01-19 10:52:15 +01:00
}
function init ( $host ) {
$this -> host = $host ;
$host -> add_hook ( $host :: HOOK_ARTICLE_FILTER , $this );
$host -> add_hook ( $host :: HOOK_PREFS_TAB , $this );
$host -> add_hook ( $host :: HOOK_PREFS_EDIT_FEED , $this );
$host -> add_hook ( $host :: HOOK_PREFS_SAVE_FEED , $this );
2015-01-19 13:46:15 +01:00
$host -> add_hook ( $host :: HOOK_ARTICLE_BUTTON , $this );
2015-01-19 10:52:15 +01:00
}
2015-01-19 13:46:15 +01:00
function get_js () {
2015-01-19 12:59:33 +01:00
return file_get_contents ( __DIR__ . " /init.js " );
}
function showrelated () {
2017-12-03 08:26:38 +01:00
$id = ( int ) $_REQUEST [ 'param' ];
2015-01-19 12:59:33 +01:00
$owner_uid = $_SESSION [ " uid " ];
2017-12-03 08:26:38 +01:00
$sth = $this -> pdo -> prepare ( " SELECT title FROM ttrss_entries, ttrss_user_entries
WHERE ref_id = id AND id = ? AND owner_uid = ? " );
$sth -> execute ([ $id , $owner_uid ]);
2015-01-19 12:59:33 +01:00
2017-12-03 08:26:38 +01:00
if ( $row = $sth -> fetch ()) {
2015-01-19 12:59:33 +01:00
2017-12-03 08:26:38 +01:00
$title = $row [ 'title' ];
2015-01-19 12:59:33 +01:00
2017-12-03 08:26:38 +01:00
print " <h2> $title </h2> " ;
$sth = $this -> pdo -> prepare ( " SELECT ttrss_entries.id AS id,
2015-01-19 13:46:15 +01:00
feed_id ,
ttrss_entries . title AS title ,
updated , link ,
ttrss_feeds . title AS feed_title ,
SIMILARITY ( ttrss_entries . title , '$title' ) AS sm
FROM
ttrss_entries , ttrss_user_entries LEFT JOIN ttrss_feeds ON ( ttrss_feeds . id = feed_id )
WHERE
ttrss_entries . id = ref_id AND
2017-12-03 08:26:38 +01:00
ttrss_user_entries . owner_uid = ? AND
ttrss_entries . id != ? AND
2015-01-19 14:42:10 +01:00
date_entered >= NOW () - INTERVAL '2 weeks'
2015-01-19 13:46:15 +01:00
ORDER BY
sm DESC , date_entered DESC
LIMIT 10 " );
2015-01-19 12:59:33 +01:00
2017-12-03 08:26:38 +01:00
$sth -> execute ([ $owner_uid , $id ]);
print " <ul class= \" browseFeedList \" style= \" border-width : 1px \" > " ;
2015-01-19 12:59:33 +01:00
2017-12-03 08:26:38 +01:00
while ( $line = $sth -> fetch ()) {
print " <li> " ;
print " <div class='insensitive small' style='margin-left : 20px; float : right'> " .
smart_date_time ( strtotime ( $line [ " updated " ]))
. " </div> " ;
2015-01-19 13:46:15 +01:00
2017-12-03 08:26:38 +01:00
$sm = sprintf ( " %.2f " , $line [ 'sm' ]);
print " <img src='images/score_high.png' title=' $sm '
2015-01-19 13:46:15 +01:00
style = 'vertical-align : middle' > " ;
2017-12-03 08:26:38 +01:00
$article_link = htmlspecialchars ( $line [ " link " ]);
print " <a target= \" _blank \" rel= \" noopener noreferrer \" href= \" $article_link\ " > " .
$line [ " title " ] . " </a> " ;
2015-01-19 13:46:15 +01:00
2017-12-03 08:26:38 +01:00
print " (<a href= \" # \" onclick= \" viewfeed( { feed: " . $line [ " feed_id " ] . " }) \" > " .
htmlspecialchars ( $line [ " feed_title " ]) . " </a>) " ;
2015-01-19 13:46:15 +01:00
2017-12-03 08:26:38 +01:00
print " <span class='insensitive'>( $sm )</span> " ;
2015-06-23 03:35:24 +02:00
2017-12-03 08:26:38 +01:00
print " </li> " ;
}
2015-01-19 12:59:33 +01:00
2017-12-03 08:26:38 +01:00
print " </ul> " ;
}
2015-01-19 12:59:33 +01:00
2015-01-19 13:46:15 +01:00
print " <div style='text-align : center'> " ;
print " <button dojoType= \" dijit.form.Button \" onclick= \" dijit.byId('trgmRelatedDlg').hide() \" > " . __ ( 'Close this window' ) . " </button> " ;
print " </div> " ;
2015-01-19 12:59:33 +01:00
2015-01-19 13:46:15 +01:00
}
function hook_article_button ( $line ) {
2015-01-19 12:59:33 +01:00
return " <img src= \" plugins/af_psql_trgm/button.png \"
style = \ " cursor : pointer \" style= \" cursor : pointer \"
onclick = \ " showTrgmRelated( " . $line [ " id " ] . " ) \"
class = 'tagsPic' title = '".__(' Show related articles ')."' > " ;
2015-01-19 13:46:15 +01:00
}
2015-01-19 12:59:33 +01:00
2015-01-19 10:52:15 +01:00
function hook_prefs_tab ( $args ) {
if ( $args != " prefFeeds " ) return ;
print " <div dojoType= \" dijit.layout.AccordionPane \" title= \" " . __ ( 'Mark similar articles as read' ) . " \" > " ;
if ( DB_TYPE != " pgsql " ) {
print_error ( " Database type not supported. " );
2016-08-25 08:47:02 +02:00
} else {
2015-01-19 10:52:15 +01:00
2017-12-03 08:26:38 +01:00
$res = $this -> pdo -> query ( " select 'similarity'::regproc " );
2015-01-19 10:52:15 +01:00
2017-12-03 08:26:38 +01:00
if ( ! $res -> fetch ()) {
2016-08-25 08:47:02 +02:00
print_error ( " pg_trgm extension not found. " );
2015-01-19 10:52:15 +01:00
}
2015-01-19 12:22:41 +01:00
2016-08-25 08:47:02 +02:00
$similarity = $this -> host -> get ( $this , " similarity " );
$min_title_length = $this -> host -> get ( $this , " min_title_length " );
$enable_globally = $this -> host -> get ( $this , " enable_globally " );
if ( ! $similarity ) $similarity = '0.75' ;
if ( ! $min_title_length ) $min_title_length = '32' ;
print " <form dojoType= \" dijit.form.Form \" > " ;
print " <script type= \" dojo/method \" event= \" onSubmit \" args= \" evt \" >
evt . preventDefault ();
if ( this . validate ()) {
console . log ( dojo . objectToQuery ( this . getValues ()));
new Ajax . Request ( 'backend.php' , {
parameters : dojo . objectToQuery ( this . getValues ()),
onComplete : function ( transport ) {
notify_info ( transport . responseText );
}
});
//this.reset();
}
</ script > " ;
2017-02-10 12:36:21 +01:00
print_hidden ( " op " , " pluginhandler " );
print_hidden ( " method " , " save " );
print_hidden ( " plugin " , " af_psql_trgm " );
2016-08-25 08:47:02 +02:00
print " <p> " . __ ( " PostgreSQL trigram extension returns string similarity as a floating point number (0-1). Setting it too low might produce false positives, zero disables checking. " ) . " </p> " ;
print_notice ( " Enable the plugin for specific feeds in the feed editor. " );
print " <h3> " . __ ( " Global settings " ) . " </h3> " ;
print " <table> " ;
print " <tr><td width= \" 40% \" > " . __ ( " Minimum similarity: " ) . " </td> " ;
print " <td>
< input dojoType = \ " dijit.form.ValidationTextBox \"
placeholder = \ " 0.75 \"
required = \ " 1 \" name= \" similarity \" value= \" $similarity\ " ></ td ></ tr > " ;
print " <tr><td width= \" 40% \" > " . __ ( " Minimum title length: " ) . " </td> " ;
print " <td>
< input dojoType = \ " dijit.form.ValidationTextBox \"
placeholder = \ " 32 \"
required = \ " 1 \" name= \" min_title_length \" value= \" $min_title_length\ " ></ td ></ tr > " ;
print " <tr><td width= \" 40% \" > " . __ ( " Enable for all feeds: " ) . " </td> " ;
2017-02-10 12:57:25 +01:00
print " <td> " ;
print_checkbox ( " enable_globally " , $enable_globally );
print " </td></tr> " ;
2016-08-25 08:47:02 +02:00
print " </table> " ;
2017-02-10 12:57:25 +01:00
print " <p> " ; print_button ( " submit " , __ ( " Save " ));
2016-08-25 08:47:02 +02:00
print " </form> " ;
$enabled_feeds = $this -> host -> get ( $this , " enabled_feeds " );
if ( ! array ( $enabled_feeds )) $enabled_feeds = array ();
$enabled_feeds = $this -> filter_unknown_feeds ( $enabled_feeds );
$this -> host -> set ( $this , " enabled_feeds " , $enabled_feeds );
if ( count ( $enabled_feeds ) > 0 ) {
print " <h3> " . __ ( " Currently enabled for (click to edit): " ) . " </h3> " ;
print " <ul class= \" browseFeedList \" style= \" border-width : 1px \" > " ;
foreach ( $enabled_feeds as $f ) {
print " <li> " .
" <img src='images/pub_set.png'
style = 'vertical-align : middle' > < a href = '#'
onclick = 'editFeed($f)' > " .
move the following to Feeds:
+ static function catchup_feed($feed, $cat_view, $owner_uid = false, $mode = 'all', $search = false) {
+ static function getFeedArticles($feed, $is_cat = false, $unread_only = false,
+ static function subscribe_to_feed($url, $cat_id = 0,
+ static function getFeedIcon($id) {
+ static function getFeedTitle($id, $cat = false) {
+ static function getCategoryUnread($cat, $owner_uid = false) {
+ static function getCategoryChildrenUnread($cat, $owner_uid = false) {
2017-05-04 13:50:56 +02:00
Feeds :: getFeedTitle ( $f ) . " </a></li> " ;
2016-08-25 08:47:02 +02:00
}
print " </ul> " ;
2015-01-19 12:22:41 +01:00
}
}
2015-01-19 10:52:15 +01:00
print " </div> " ;
}
function hook_prefs_edit_feed ( $feed_id ) {
print " <div class= \" dlgSec \" > " . __ ( " Similarity (pg_trgm) " ) . " </div> " ;
print " <div class= \" dlgSecCont \" > " ;
$enabled_feeds = $this -> host -> get ( $this , " enabled_feeds " );
if ( ! array ( $enabled_feeds )) $enabled_feeds = array ();
$key = array_search ( $feed_id , $enabled_feeds );
$checked = $key !== FALSE ? " checked " : " " ;
print " <hr/><input dojoType= \" dijit.form.CheckBox \" type= \" checkbox \" id= \" trgm_similarity_enabled \"
name = \ " trgm_similarity_enabled \"
$checked >& nbsp ; < label for = \ " trgm_similarity_enabled \" > " . __ ( 'Mark similar articles as read' ) . " </label> " ;
print " </div> " ;
}
function hook_prefs_save_feed ( $feed_id ) {
$enabled_feeds = $this -> host -> get ( $this , " enabled_feeds " );
if ( ! is_array ( $enabled_feeds )) $enabled_feeds = array ();
2017-12-02 12:07:48 +01:00
$enable = checkbox_to_sql_bool ( $_POST [ " trgm_similarity_enabled " ]);
2015-01-19 10:52:15 +01:00
$key = array_search ( $feed_id , $enabled_feeds );
if ( $enable ) {
if ( $key === FALSE ) {
array_push ( $enabled_feeds , $feed_id );
}
} else {
if ( $key !== FALSE ) {
unset ( $enabled_feeds [ $key ]);
}
}
$this -> host -> set ( $this , " enabled_feeds " , $enabled_feeds );
}
function hook_article_filter ( $article ) {
if ( DB_TYPE != " pgsql " ) return $article ;
2017-12-03 08:26:38 +01:00
$res = $this -> pdo -> query ( " select 'similarity'::regproc " );
if ( ! $res -> fetch ()) return $article ;
2015-01-19 10:52:15 +01:00
2015-06-23 06:36:25 +02:00
$enable_globally = $this -> host -> get ( $this , " enable_globally " );
if ( ! $enable_globally ) {
$enabled_feeds = $this -> host -> get ( $this , " enabled_feeds " );
$key = array_search ( $article [ " feed " ][ " id " ], $enabled_feeds );
if ( $key === FALSE ) return $article ;
}
2015-01-19 10:52:15 +01:00
$similarity = ( float ) $this -> host -> get ( $this , " similarity " );
if ( $similarity < 0.01 ) return $article ;
2016-01-17 09:32:10 +01:00
$min_title_length = ( int ) $this -> host -> get ( $this , " min_title_length " );
2015-01-19 10:52:15 +01:00
if ( mb_strlen ( $article [ " title " ]) < $min_title_length ) return $article ;
$owner_uid = $article [ " owner_uid " ];
2015-07-15 12:15:00 +02:00
$entry_guid = $article [ " guid_hashed " ];
2017-12-03 08:26:38 +01:00
$title_escaped = $article [ " title " ];
2015-01-19 10:52:15 +01:00
2015-06-23 03:35:24 +02:00
// trgm does not return similarity=1 for completely equal strings
2017-12-03 08:26:38 +01:00
$sth = $this -> pdo -> prepare ( " SELECT COUNT(id) AS nequal
2015-06-23 03:35:24 +02:00
FROM ttrss_entries , ttrss_user_entries WHERE ref_id = id AND
2016-08-01 07:26:11 +02:00
date_entered >= NOW () - interval '3 days' AND
2017-12-03 08:26:38 +01:00
title = ? AND
guid != ? AND
owner_uid = ? " );
$sth -> execute ([ $title_escaped , $entry_guid , $owner_uid ]);
$row = $sth -> fetch ();
$nequal = $row [ 'nequal' ];
2015-06-23 03:35:24 +02:00
_debug ( " af_psql_trgm: num equals: $nequal " );
if ( $nequal != 0 ) {
$article [ " force_catchup " ] = true ;
return $article ;
}
2017-12-03 08:26:38 +01:00
$sth = $this -> pdo -> prepare ( " SELECT MAX(SIMILARITY(title, ?)) AS ms
2015-01-19 10:52:15 +01:00
FROM ttrss_entries , ttrss_user_entries WHERE ref_id = id AND
date_entered >= NOW () - interval '1 day' AND
2017-12-03 08:26:38 +01:00
guid != ? AND
owner_uid = ? " );
$sth -> execute ([ $title_escaped , $entry_guid , $owner_uid ]);
2015-01-19 10:52:15 +01:00
2017-12-03 08:26:38 +01:00
$row = $sth -> fetch ();
$similarity_result = $row [ 'ms' ];
2015-01-19 10:52:15 +01:00
2015-06-23 03:35:24 +02:00
_debug ( " af_psql_trgm: similarity result: $similarity_result " );
2015-01-19 10:52:15 +01:00
if ( $similarity_result >= $similarity ) {
$article [ " force_catchup " ] = true ;
}
return $article ;
}
function api_version () {
return 2 ;
}
2015-06-11 16:17:19 +02:00
private function filter_unknown_feeds ( $enabled_feeds ) {
$tmp = array ();
foreach ( $enabled_feeds as $feed ) {
2017-12-03 08:26:38 +01:00
$sth = $this -> pdo -> prepare ( " SELECT id FROM ttrss_feeds WHERE id = ? AND owner_uid = ? " );
$sth -> execute ([ $feed , $_SESSION [ 'uid' ]]);
2015-06-11 16:17:19 +02:00
2017-12-03 08:26:38 +01:00
if ( $row = $sth -> fetch ()) {
2015-06-11 16:17:19 +02:00
array_push ( $tmp , $feed );
}
}
return $tmp ;
}
2017-04-26 19:57:36 +02:00
}