implement filters
parent
a43f9862bb
commit
647d913dba
|
@ -35,9 +35,9 @@ sub create {
|
|||
my %data = @_;
|
||||
|
||||
Tweetodon::DB->doINSERThash($class->dbTable, %data);
|
||||
my $data = Tweetodon::DB->doSELECT("SELECT * FROM ".$class->dbTable." WHERE id = LAST_INSERT_ID()");
|
||||
my $data = Tweetodon::DB->doSELECT("SELECT * FROM ".$class->dbTable." WHERE ID = LAST_INSERT_ID()");
|
||||
$data = $$data[0];
|
||||
return $class->get_by("id", $$data{id});
|
||||
return $class->get_by("ID", $$data{ID});
|
||||
}
|
||||
sub new {
|
||||
my $this = shift;
|
||||
|
@ -55,10 +55,15 @@ sub save {
|
|||
my $self = shift;
|
||||
|
||||
if (exists($self->{"data"}->{"password"}) && "x".$self->{"data"}->{"password"} ne "x"){
|
||||
$self->{"data"}->{"password"} = md5_hex($self->{"data"}->{"id"}.$self->{"data"}->{"password"});
|
||||
$self->{"data"}->{"password"} = md5_hex($self->{"data"}->{"ID"}.$self->{"data"}->{"password"});
|
||||
} else {
|
||||
delete $self->{"data"}->{"password"};
|
||||
}
|
||||
return Tweetodon::DB->doUPDATEhash($self->dbTable, "id = ".$self->{"data"}->{"id"}, %{$self->{"data"}});
|
||||
return Tweetodon::DB->doUPDATEhash($self->dbTable, "ID = ".$self->{"data"}->{"ID"}, %{$self->{"data"}});
|
||||
}
|
||||
sub delete {
|
||||
my $self = shift;
|
||||
|
||||
return Tweetodon::DB->doDELETE("DELETE FROM ".$self->dbTable." WHERE ID = ?", $self->{data}->{ID});
|
||||
}
|
||||
1;
|
||||
|
|
|
@ -47,14 +47,15 @@ sub doINSERThash {
|
|||
my $sth = $DBH->prepare($SQL);
|
||||
if ($DBI::err) {
|
||||
&main::Error("SQL Error", "doINSERThash prepare failed:\n'".$DBI::errstr."'\nSQL was: $SQL");
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
$sth->execute(@RESTARGS);
|
||||
if ($DBI::err) {
|
||||
&main::Error("SQL Error", "doINSERThash execute failed:'".$DBI::errstr."'\nSQL was: $SQL\nRestargs:".Dumper(@RESTARGS));
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
$sth->finish();
|
||||
return 1;
|
||||
}
|
||||
sub doSELECT {
|
||||
shift;
|
||||
|
@ -90,15 +91,15 @@ sub doDELETE {
|
|||
my $sth = $DBH->prepare($SQL);
|
||||
if ($DBI::err) {
|
||||
&main::Error("SQL Error", "doDELETE prepare failed:\n'".$DBI::errstr."'\nSQL was: $SQL");
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
$sth->execute(@RESTARGS);
|
||||
if ($DBI::err) {
|
||||
&main::Error("SQL Error", "doDELETE execute failed:'".$DBI::errstr."'\nSQL was: $SQL\nRestargs:".Dumper(@RESTARGS));
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
$sth->finish();
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
sub doUPDATE {
|
||||
my $this = shift;
|
||||
|
@ -108,15 +109,15 @@ sub doUPDATE {
|
|||
my $sth = $DBH->prepare($SQL);
|
||||
if ($DBI::err) {
|
||||
&main::Error("SQL Error", "doUPDATE prepare failed:\n'".$DBI::errstr."'\nSQL was: $SQL");
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
$sth->execute(@RESTARGS);
|
||||
if ($DBI::err) {
|
||||
&main::Error("SQL Error", "doUPDATE execute failed:'".$DBI::errstr."'\nSQL was: $SQL\nRestargs:".Dumper(@RESTARGS));
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
$sth->finish();
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
sub doUPDATEhash {
|
||||
my $this = shift;
|
||||
|
@ -136,14 +137,15 @@ sub doUPDATEhash {
|
|||
my $sth = $DBH->prepare($SQL);
|
||||
if ($DBI::err) {
|
||||
&main::Error("SQL Error", "doUPDATEhash prepare failed:\n'".$DBI::errstr."'\nSQL was: $SQL");
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
$sth->execute(@RESTARGS);
|
||||
if ($DBI::err) {
|
||||
&main::Error("SQL Error", "doUPDATEhash execute failed:'".$DBI::errstr."'\nSQL was: $SQL\nRestargs:".Dumper(@RESTARGS));
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
$sth->finish();
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub DBInit {
|
||||
|
|
|
@ -6,6 +6,7 @@ package Tweetodon::Feed;
|
|||
@Tweetodon::Feed::ISA = qw(Tweetodon::Base);
|
||||
use JSON;
|
||||
use Data::Dumper;
|
||||
use Tweetodon::Filter;
|
||||
|
||||
sub dbTable :lvalue { "feeds"; }
|
||||
sub orderBy :lvalue { "url ASC"; }
|
||||
|
@ -27,5 +28,15 @@ sub get_by_user_instance {
|
|||
}
|
||||
|
||||
# Object methods
|
||||
sub filters {
|
||||
my $self = shift;
|
||||
my $filters = Tweetodon::DB->doSELECT("SELECT * FROM filters WHERE feed_id = ? ORDER BY ID ASC", $self->{data}->{ID});
|
||||
|
||||
my @retVal;
|
||||
foreach my $r (@$filters){
|
||||
push @retVal, Tweetodon::Filter->new($r);
|
||||
}
|
||||
return @retVal;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
# vim: set foldmarker={,}:
|
||||
use strict;
|
||||
use Tweetodon::Base;
|
||||
|
||||
package Tweetodon::Filter;
|
||||
@Tweetodon::Filter::ISA = qw(Tweetodon::Base);
|
||||
use JSON;
|
||||
use Data::Dumper;
|
||||
use URI::Escape;
|
||||
|
||||
sub dbTable :lvalue { "filters"; }
|
||||
sub orderBy :lvalue { "ID ASC"; }
|
||||
|
||||
# Class functions
|
||||
|
||||
# Object methods
|
||||
sub apply {
|
||||
my $self = shift;
|
||||
my $entry = shift;
|
||||
|
||||
my $match = 1;
|
||||
my $arg = $self->{data}->{field};
|
||||
if ($arg eq "content"){
|
||||
$arg = $entry->content()->body;
|
||||
} else {
|
||||
$arg = $entry->$arg();
|
||||
}
|
||||
my $regex = uri_unescape($self->{data}->{regex});
|
||||
$regex =~ s,\\\\,\\,g;
|
||||
|
||||
if ($self->{data}->{match} eq "positive"){
|
||||
if ($arg =~ /$regex/i){
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
if ($arg !~ /$regex/i){
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
1;
|
|
@ -54,7 +54,7 @@ sub render {
|
|||
}
|
||||
}
|
||||
foreach ($self->{"set_cookie"}){
|
||||
print "Set-Cookie: $_\n";
|
||||
print "Set-Cookie: $_\n" if defined($_);
|
||||
}
|
||||
print "\n";
|
||||
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
#!/usr/bin/perl -w
|
||||
# vim: set foldmarker={,}:
|
||||
|
||||
use strict;
|
||||
use HTML::Template;
|
||||
use Tweetodon::DB;
|
||||
use Tweetodon::Feed;
|
||||
use Tweetodon::Filter;
|
||||
use Tweetodon::Website;
|
||||
|
||||
package Tweetodon::Website::EditFeed;
|
||||
use Data::Dumper;
|
||||
use XML::Feed;
|
||||
use URI;
|
||||
@Tweetodon::Website::EditFeed::ISA = qw(Tweetodon::Website);
|
||||
|
||||
sub requires_authentication {
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub fill_content {
|
||||
my $class = shift;
|
||||
my $output = shift;
|
||||
my $feed = Tweetodon::Feed->get_by("ID", $main::FORM{id});
|
||||
unless ($feed){
|
||||
main::Error("Unknown feed", "This feed id is not known");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($feed->{data}->{username} ne $main::CURRENTUSER->{data}->{acct} or $feed->{data}->{instance} ne $main::FORM{instance}){
|
||||
main::Error("Unknown feed", "This feed id is not known");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($main::FORM{action} and "x".$main::FORM{action} eq "xsave"){
|
||||
my @filters = $feed->filters();
|
||||
FILTER: foreach my $filter (@filters){
|
||||
if ($main::FORM{"delete_".$filter->{data}->{ID}}){
|
||||
$filter->delete();
|
||||
next FILTER;
|
||||
}
|
||||
foreach my $key (keys(%{$filter->{data}})){
|
||||
if ($key ne "ID" and $main::FORM{$key."_".$filter->{data}->{ID}}){
|
||||
$filter->{data}->{$key} = $main::FORM{$key."_".$filter->{data}->{ID}};
|
||||
}
|
||||
}
|
||||
$filter->save();
|
||||
}
|
||||
|
||||
foreach my $key (grep(/^field_new/, keys(%main::FORM))){
|
||||
$key =~ /^field_new([1-9][0-9]*)$/;
|
||||
my $id = $1;
|
||||
my %newfilter;
|
||||
$newfilter{feed_id} = $main::FORM{id};
|
||||
$newfilter{field} = $main::FORM{"field_new".$id};
|
||||
$newfilter{regex} = $main::FORM{"regex_new".$id};
|
||||
$newfilter{type} = $main::FORM{"type_new".$id};
|
||||
$newfilter{match} = $main::FORM{"match_new".$id};
|
||||
my $nf = Tweetodon::Filter->create(%newfilter);
|
||||
}
|
||||
}
|
||||
|
||||
$XML::Feed::MULTIPLE_ENCLOSURES = 1;
|
||||
my $feeddata = XML::Feed->parse(URI->new($feed->{data}->{url}));
|
||||
my @param_entries;
|
||||
my @filters = $feed->filters();
|
||||
foreach my $entry ($feeddata->items){
|
||||
my %entry;
|
||||
$entry{title} = $entry->title();
|
||||
$entry{link} = $entry->link();
|
||||
$entry{content} = $entry->content()->body;
|
||||
$entry{author} = $entry->author();
|
||||
$entry{issued} = $entry->issued();
|
||||
$entry{modified} = $entry->modified();
|
||||
$entry{id} = $entry->id();
|
||||
$entry{tags} = join(", ", $entry->tags());
|
||||
|
||||
$entry{class} = "green";
|
||||
foreach my $filter (@filters){
|
||||
if ($filter->apply($entry)){
|
||||
if ($filter->{data}->{type} eq "white"){
|
||||
$entry{class} = "green";
|
||||
} else {
|
||||
$entry{class} = "red";
|
||||
}
|
||||
} else {
|
||||
if ($filter->{data}->{type} eq "white"){
|
||||
$entry{class} = "red";
|
||||
} else {
|
||||
$entry{class} = "green";
|
||||
}
|
||||
}
|
||||
}
|
||||
push @param_entries, \%entry;
|
||||
}
|
||||
$output->param("ENTRIES", \@param_entries);
|
||||
|
||||
my @param_filters;
|
||||
foreach my $filter (@filters){
|
||||
my %filter;
|
||||
$filter{ID} = $filter->{data}->{ID};
|
||||
$filter{regex} = $filter->{data}->{regex};
|
||||
$filter{field} = $filter->{data}->{field};
|
||||
$filter{type} = $filter->{data}->{type};
|
||||
$filter{match} = $filter->{data}->{match};
|
||||
push @param_filters, \%filter;
|
||||
}
|
||||
$output->param("FILTERS", \@param_filters);
|
||||
|
||||
$output->param("url", $feed->{data}->{url});
|
||||
$output->param("feed_id", $feed->{data}->{ID});
|
||||
return 1;
|
||||
}
|
||||
sub prerender {
|
||||
my $self = shift;
|
||||
$self->{"template"} = "EditFeed";
|
||||
$self->{"content_type"} = "html";
|
||||
$self->{"params"}->{"currentmode"} = "EditFeed";
|
||||
}
|
||||
|
||||
1;
|
3
index.pl
3
index.pl
|
@ -47,6 +47,7 @@ sub Error {{{
|
|||
sub populateAddToFORM {{{
|
||||
my $key = shift;
|
||||
my $value = shift;
|
||||
return unless defined($value);
|
||||
$key =~ s/\+/ /g;
|
||||
$key = uri_unescape($key);
|
||||
$key =~ s/\[\]$//;
|
||||
|
@ -119,7 +120,7 @@ my $object;
|
|||
# TODO: This is a very bad solution but not as bad as an uncontrolled eval...
|
||||
# The @main::modules array holds a list of all permissible values of the $main::FORM{"mode"} variable.
|
||||
# If the value is not in this array, the request is not processed and an error is displayed.
|
||||
my @modules = ("Login", "OAuthLogin", "Dashboard", "Callback", "JSON");
|
||||
my @modules = ("Login", "OAuthLogin", "Dashboard", "Callback", "JSON", "EditFeed");
|
||||
|
||||
if (! grep {$_ eq $FORM{mode}} @modules) {
|
||||
Error("Validation Error", "$FORM{mode} is not a valid module");
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
.green {
|
||||
background-color: #80FF80;
|
||||
}
|
||||
.red {
|
||||
background-color: #FF8080;
|
||||
}
|
|
@ -59,9 +59,13 @@ body {
|
|||
padding-right: 20px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
.onlywhenactive {
|
||||
visibility: hidden;
|
||||
}
|
||||
.nav-sidebar > .active > a,
|
||||
.nav-sidebar > .active > a:hover,
|
||||
.nav-sidebar > .active > a:focus {
|
||||
visibility: visible !important;
|
||||
color: #fff;
|
||||
background-color: #428bca;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
var newnum = 0;
|
||||
function appendFilter(id, field, regex, type, match){
|
||||
console.log("id: "+id+" field: "+field+" regex: "+regex+" type: "+type);
|
||||
var t = $("div#filters table tbody");
|
||||
var a = "<tr>";
|
||||
a += "<td>";
|
||||
a += "<select name='field_"+id+"' id='field_"+id+"'>";
|
||||
a += "<option disabled selected value='0'>Please select</option>";
|
||||
a += "<option disabled value='0'>-------------</option>";
|
||||
a += "<option value='id'>ID</option>";
|
||||
a += "<option value='title'>Title</option>";
|
||||
a += "<option value='link'>Link</option>";
|
||||
a += "<option value='content'>Content</option>";
|
||||
a += "<option value='author'>Author</option>";
|
||||
a += "<option value='issued'>Issued</option>";
|
||||
a += "<option value='modified'>Modified</option>";
|
||||
a += "<option value='tags'>Tags</option>";
|
||||
a += "</select>";
|
||||
a += "</td>";
|
||||
a += "<td>";
|
||||
a += "<select name='match_"+id+"' id='match_"+id+"'>";
|
||||
a += "<option value='positive'>Matches</option>";
|
||||
a += "<option value='negative'>Does not match</option>";
|
||||
a += "</select>";
|
||||
a += "</td>";
|
||||
a += "<td><input type='text' placeholder='Match.*?some.*regex' name='regex_"+id+"' id='regex_"+id+"'></td>";
|
||||
a += "<td>";
|
||||
a += "<select name='type_"+id+"' id='type_"+id+"'>";
|
||||
a += "<option disabled selected value='0'>Please select</option>";
|
||||
a += "<option disabled value='0'>-------------</option>";
|
||||
a += "<option value='white'>Whitelist</option>";
|
||||
a += "<option value='black'>Blacklist</option>";
|
||||
a += "</select>";
|
||||
a += "</td>";
|
||||
if (!(""+id).match(/^new/)){
|
||||
a += "<td><input type='checkbox' name='delete_"+id+"'></td>";
|
||||
}
|
||||
a += "</tr>";
|
||||
t.append(a);
|
||||
if (field){
|
||||
t.find("#field_"+id).val(field);
|
||||
}
|
||||
if (regex){
|
||||
t.find("#regex_"+id).val(regex);
|
||||
}
|
||||
if (type){
|
||||
t.find("#type_"+id).val(type);
|
||||
}
|
||||
if (match){
|
||||
t.find("#match_"+id).val(match);
|
||||
}
|
||||
}
|
||||
function TweetodonOnReady(){
|
||||
$("#rawentries").hide();
|
||||
$("a#togglerawentries").on("click", function(){
|
||||
$("#rawentries").toggle();
|
||||
});
|
||||
|
||||
$("a#addfilter").on("click", function(){
|
||||
newnum++;
|
||||
appendFilter("new"+newnum);
|
||||
});
|
||||
$("a#savefilters").one("click", function(){
|
||||
document.forms.form_filters.submit();
|
||||
});
|
||||
|
||||
for (i=0; i<filters.length; i++){
|
||||
appendFilter(filters[i].ID, filters[i].field, filters[i].regex, filters[i].type, filters[i].match);
|
||||
}
|
||||
}
|
|
@ -48,6 +48,8 @@
|
|||
<tr>
|
||||
<td><TMPL_VAR NAME="count"></td>
|
||||
<td><TMPL_VAR NAME="url"></td>
|
||||
<td><a class="btn btn-default" href="index.pl?mode=EditFeed&id=<TMPL_VAR NAME="ID">">Edit</a></td>
|
||||
<td><a class="btn btn-danger">Delete</a></td>
|
||||
</tr>
|
||||
</TMPL_LOOP>
|
||||
</tbody>
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
<TMPL_INCLUDE NAME='_header.html'>
|
||||
<TMPL_INCLUDE NAME='_navbar.html'>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<TMPL_INCLUDE NAME='_sidebar.html'>
|
||||
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
|
||||
<h2 class="sub-header"><TMPL_VAR NAME="url"></h2>
|
||||
<a id="togglerawentries" class="btn btn-info">Show raw entries</a><br >
|
||||
|
||||
<div id="rawentries" class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Title</th>
|
||||
<th>Link</th>
|
||||
<th>Content</th>
|
||||
<th>Author</th>
|
||||
<th>Issued</th>
|
||||
<th>Modified</th>
|
||||
<th>Tags</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<TMPL_LOOP NAME="ENTRIES">
|
||||
<tr class="<TMPL_VAR NAME="class">">
|
||||
<td><TMPL_VAR NAME="id"></td>
|
||||
<td><TMPL_VAR NAME="title"></td>
|
||||
<td><TMPL_VAR NAME="link"></td>
|
||||
<td><TMPL_VAR NAME="content"></td>
|
||||
<td><TMPL_VAR NAME="author"></td>
|
||||
<td><TMPL_VAR NAME="issued"></td>
|
||||
<td><TMPL_VAR NAME="modified"></td>
|
||||
<td><TMPL_VAR NAME="tags"></td>
|
||||
</tr>
|
||||
</TMPL_LOOP>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<form id="form_filters" method="POST">
|
||||
<input type="hidden" name="action" value="save">
|
||||
<h3>Filters</h3>
|
||||
<div id="filters" class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Matches</th>
|
||||
<th>Regex</th>
|
||||
<th>Type</th>
|
||||
<th>Delete</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<a id="savefilters" class="btn btn-default" href="#">Save filters</a> <a id="addfilter" class="btn btn-danger">Add filter</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var filters = [];
|
||||
<TMPL_LOOP NAME="FILTERS">
|
||||
filters.push({ID: <TMPL_VAR NAME="ID">, field: "<TMPL_VAR NAME="field">", regex: decodeURIComponent("<TMPL_VAR ESCAPE=URL NAME="regex">"), type: "<TMPL_VAR NAME="type">", match: "<TMPL_VAR NAME="match">"});
|
||||
</TMPL_LOOP>
|
||||
</script>
|
||||
<TMPL_INCLUDE NAME='_footer.html'>
|
|
@ -1,5 +1,6 @@
|
|||
<div class="col-sm-3 col-md-2 sidebar">
|
||||
<ul class="nav nav-sidebar">
|
||||
<li id="Dashboard"><a href="index.pl?mode=Dashboard">Dashboard</a></li>
|
||||
<li class="onlywhenactive" id="EditFeed"><a href="index.pl?mode=EditFeed">Edit Feed</a></li>
|
||||
</ul>
|
||||
</div><!-- /sidebar -->
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#!/bin/bash
|
||||
|
||||
echo curl -X GET --header "Authorization: Bearer ${1}" "${2}/api/v1/accounts/verify_credentials" >&2
|
||||
curl -X GET --header "Authorization: Bearer ${1}" "${2}/api/v1/accounts/verify_credentials" 2>/dev/null
|
||||
#https://cloud.digitalocean.com/v1/oauth/token?client_id=CLIENT_ID&client_secret=CLIENT_SECRET&grant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=CALLBACK_URL
|
||||
|
|
Loading…
Reference in New Issue