# ==================================================================
# Plugins::SocialMedia - Auto Generated Program Module
#
# Plugins::SocialMedia
# Author : Gossamer Threads Inc.
# Version : 1.0
# Updated : Tue Oct 29 11:32:05 2013
#
# ==================================================================
#
package Plugins::SocialMedia;
# ==================================================================
use strict;
use GT::Base;
use GT::Plugins qw/STOP CONTINUE/;
use Links qw/:objects/;
use Links::Build;
use Links::SiteHTML;
use Net::Twitter;
use Links::Plugins;
use LWP::Simple;
use JSON;
use URI;
use utf8;
use vars qw/$USE_HTML $TIME_START $TOTAL_TIME @CARP_NOT $GRAND_TOTAL/;
#use Scalar::Util 'blessed';
use GT::File::Tools qw/mkpath dirname/;
use Carp;
@CARP_NOT = 'GT::Plugins';
# Inherit from base class for debug and error methods
@Plugins::SocialMedia::ISA = qw(GT::Base);
# Your code begins here.
# PLUGIN HOOKS
# ===================================================================
sub post_twitter {
my $link = shift;
$link = format_link($link);
$link || return;
# When no authentication is required:
my $nt = Net::Twitter->new(legacy => 0);
my $consumer_key = "u4xwsqHZBKrdWYPrKCm8Lw";
my $consumer_secret = "vjDF6FjeoPJW0WVwgqMEJeuJzilgSIu5QbPGQnWrMI";
my $token = "1921299666-fMicJMBunBjgBb4ieszHo6tYV0mQcbbaMZU5wSB";
my $token_secret = "Ko9gPpBaLxqQj6u68EWdlgnPinGSseVzrzUvytWric";
#return { error => 'error posting...unknown' };
my $tags = $IN->param('twitter_hash_tags');
$tags =~ s/\s+//g;
my @tags = split(',',$tags);
my $newtags;
for (@tags) {
$newtags .= ' #' . $_;
}
my $post_title = $IN->param('twitter_status');
my $len = length $link->{detailed_url};
$len = 140 - 5 - $len;
$post_title = substr($post_title,0,$len) . "..." if (length $post_title > $len);
$post_title .= " " . $link->{detailed_url};
$post_title .= $newtags;
# As of 13-Aug-2010, Twitter requires OAuth for authenticated requests
$nt = Net::Twitter->new(
traits => [qw/API::RESTv1_1/],
consumer_key => $consumer_key,
consumer_secret => $consumer_secret,
access_token => $token,
access_token_secret => $token_secret,
);
my $result;
my $res = eval { $result = $nt->update($post_title) };
if ( my $err = $@ ) {
die $@ unless $err and $err->isa('Net::Twitter::Error');
#warn "HTTP Response Code: ", $err->code, "\n",
#"HTTP Message......: ", $err->message, "\n",
#"Twitter error.....: ", $err->error, "\n";
$result->{error} = $err->error;
}
return $result;
}
sub post_facebook {
# -------------------------------------------------------------------
my $access_token = shift;
my $id = shift;
my $link = format_link($id);
if ($link->{facebook_hashtags}) {
$link->{Description} .= " ";
$link->{Description} .= $link->{facebook_hashtags};
}
#Publish to a facebook page as admin
my $cfg = Links::Plugins::get_plugin_user_cfg('Auth_Facebook');
my $result = facebook_graph_api('/' . $cfg->{fb_fanpageid} . '/links',{
link => "http://www.slowtwitch.com/temp/$id.html",
#link => $link->{detailed_url},
message => $link->{Description},
picture => $link->{Picture},
access_token => $access_token,
method => 'post'
});
return $result;
}
sub facebook_graph_api {
# -------------------------------------------------------------------
my $uri = new URI('https://graph.facebook.com/' . shift);
$uri->query_form(shift);
require GT::WWW;
my $www = GT::WWW->get("$uri");
my $resp = $www->content;
return defined $resp ? decode_json($resp) : undef;
}
sub format_link {
# -------------------------------------------------------------------
my $link = shift;
if ($link =~ /^(\d+)$/) {
$link = $DB->table('Links')->get($link);
}
$link or return;
$link = Links::SiteHTML::tags('link',$link);
my $len = length $link->{detailed_url};
$len = 140 - 5 - $len;
$link->{Description} = substr($link->{Description},0,$len) . "..." if (length $link->{Description} > $len);
my @cats = $DB->table('Category', 'CatLinks')->select('Category.Full_Name', { 'CatLinks.LinkID' => $link->{ID} })->fetchall_list;
$link->{Category} = join "\n", sort @cats;
use Plugins::SlideShow;
my $paths = Plugins::SlideShow::generate_paths($link->{ID});
$link->{image_paths} = $paths;
my $img = $link->{facebook_published_image} || "Image1";
$link->{Picture} = $paths->{$img . '_largest_path'} if ($paths->{$img . '_largest_path'});
return $link;
}
sub publish_it {
# -------------------------------------------------------------------
if ($IN->param('build')) {
my $linkid = $IN->param('linkid');
my ($twitter, $fb);
use Time::HiRes;
#Time::HiRes::sleep(5); #.1 seconds
#print $IN->header;
$IN->param('t','dev'); #FIXME
#print $IN->header;
build_it($linkid);
my $hash = {};
#print Links::user_page('add_success_publish.html', { success => '1', built => 1, twitter => $twitter, %$hash });
#return;
if ($IN->param('post_facebook')) {
if ($IN->param('post_twitter')) {
my $res = post_twitter($linkid);
if ($res->{id}) {
$DB->table('Links')->update({ twitter_published => $res->{id} }, { ID => $linkid });
$hash->{twitter_published} = 1;
}
else {
$DB->table('Links')->update({ twitter_published_message => $res->{error} }, { ID => $linkid });
$hash->{twitter_published_message} = $res->{error} if ($res->{error});
}
}
use Plugins::Auth_Facebook;
Plugins::Auth_Facebook::user_auth($linkid);
return;
}
elsif ($IN->param('post_twitter')) {
my $res = post_twitter($linkid);
if ($res->{id}) {
$DB->table('Links')->update({ twitter_published => $res->{id} }, { ID => $linkid });
$hash->{twitter_published} = 1;
}
else {
$DB->table('Links')->update({ twitter_published_message => $res->{error} }, { ID => $linkid });
$hash->{twitter_published_message} = $res->{error} if ($res->{error});
}
}
$IN->param('t','dev'); #FIXME
print $IN->header;
print Links::user_page('add_success_publish.html', { success => '1', built => 1, twitter => $twitter, %$hash });
}
else {
print $IN->header;
print Links::user_page('error.html', { error => 'no error' });
}
}
sub build_it {
# -------------------------------------------------------------------
#
my $linkid = shift || return;
my $unix_time = $CFG->{last_build} ? $CFG->{last_build} : time;
Links::init_date();
my $time = GT::Date::date_get($unix_time - $CFG->{date_offset} * 3600, '%yyyy%-%mm%-%dd% %HH%:%MM%:%ss%');
=tag
# Do any backups.
_build_backup();
# Update isNew, isCool, isPopular flags.
_build_reset_hits();
_build_new_flags();
_build_changed_flags();
_build_cool_flags();
# Build Home Page.
$PLG->dispatch('create_home', \&_build_home, {});
=cut
=tag
# Build New Page.
$PLG->dispatch('create_new', \&_build_new, {});
# Build Cool Page.
$PLG->dispatch('create_cool', \&_build_cool, {});
# Build Ratings Page.
$PLG->dispatch('create_ratings', \&_build_ratings, {});
=cut
# Build Changed Detailed Page.
$PLG->dispatch('create_detailed_changed', \&_build_detailed, GT::SQL::Condition->new('Links.ID', '=', $linkid));
=tag
# Build Changed Category Pages.
$PLG->dispatch('create_category_changed', \&_build_category, GT::SQL::Condition->new('Timestmp', '>', $time));
$CFG->{last_build} = time;
$CFG->save;
=cut
}
sub get_path {
# -------------------------------------------------------------------
#
my $field = shift;
my $filefield = $field . "_largest_path";
my $link = GT::Template->tags();
if (!$link->{$filefield}) {
$filefield = $field . "_medium_path";
}
return $link->{$filefield};
}
# ------------------------------------------------------------------
# MISC BUILD functions
# ------------------------------------------------------------------
sub _build_home {
# ------------------------------------------------------------------
# Generate the home page.
#
_time_start();
my $index = $CFG->{build_home} || $CFG->{build_index};
my $page = "$CFG->{build_root_path}/$index";
print $USE_HTML
? qq'Building Home Page...\n'
: qq'Building Home Page...\n';
my $fh = _open_write($page);
print $fh Links::Build::build(home => {});
close $fh;
my $perms = oct $CFG->{build_file_per};
chmod $perms, $page;
_display_time();
}
sub _build_detailed {
# ------------------------------------------------------------------
# Generate one html page per link.
#
require Links::Tools;
my ($cond, $cust_page, $cust_limit);
if (ref $_[0] eq 'HASH') {
$cust_page = $_[0]->{page};
$cust_limit = $_[0]->{limit};
}
else {
$cond = shift;
}
unless ($CFG->{build_detailed}) {
print "Skipping Detailed Build (disabled).\n\n";
return;
}
_time_start();
#print "Building Detailed pages...\n";
# Only build validated links
$cond ||= GT::SQL::Condition->new;
$cond->add(VIEWABLE);
# Loop through, building 1000 at a time
my ($limit, $offset, $count, $second_pass) = (1000, 0, 0);
my $rel = $DB->table(qw/Links CatLinks Category/);
#print "\t";
my $Links = $DB->table('Links');
while () {
# Links can be in multiple categories, make sure their detailed pages are only built once
$rel->select_options("GROUP BY LinkID") if $CFG->{build_detail_format} eq '%ID%';
$rel->select_options("ORDER BY LinkID");
if ($cust_page or $cust_limit) {
last if $second_pass++;
$rel->select_options(sprintf "LIMIT %d OFFSET %d", $cust_limit, ($cust_page-1) * $cust_limit);
}
else {
$rel->select_options(sprintf "LIMIT %d OFFSET %d", $limit, $offset*$limit);
}
my %links_cols = %{$Links->cols};
# Only select Category columns that don't conflict with Links columns.
my @cat_cols = grep !$links_cols{$_}, keys %{$DB->table('Category')->cols};
my $sth = $rel->select('Links.*', @cat_cols, 'CategoryID' => $cond);
last unless $sth->rows;
while (my $link = $sth->fetchrow_hashref) {
my $format = $Links->detailed_url($link);
$format = "temp/" . $link->{ID} . ".html"; #FIXME
my $page = "$CFG->{build_detail_path}/$format";
my $url = "$CFG->{build_detail_url}/$format";
{
my $fh = _open_write($page);
print $fh Links::Build::build(detailed => $link);
}
my $perms = oct $CFG->{build_file_per};
chmod $perms, $page;
=tag
$USE_HTML ?
print qq'$link->{ID} ' :
print "$link->{ID} ";
print "\n\t" if ++$count % 20 == 0;
=cut
}
$offset++;
}
=tag
print "\n";
_display_time();
=cut
}
sub _build_category {
# ------------------------------------------------------------------
# Generate the category pages.
#
my ($cond, $cust_page, $cust_limit);
if (ref $_[0] eq 'HASH') {
$cust_page = $_[0]->{page};
$cust_limit = $_[0]->{offset};
$cond = {};
}
else {
$cond = shift;
}
_time_start();
print "Building Category pages...\n\n";
my $Cat = $DB->table('Category');
my $CatLinks = $DB->table('Links', 'CatLinks');
$Cat->select_options('ORDER BY Full_Name');
if (defined $cust_page and $cust_limit) {
$Cat->select_options(sprintf "LIMIT %d OFFSET %d", $cust_limit, ($cust_page-1)*$cust_limit);
}
my $sth = $Cat->select(ID => Full_Name => $cond);
while (my ($id, $name) = $sth->fetchrow_array) {
my $clean_name = $Cat->as_url($name);
my $page = $CFG->{build_root_path} . "/" . $clean_name . '/' . $CFG->{build_index};
my $url = $CFG->{build_root_url} . "/" . $clean_name . '/' . $CFG->{build_index};
print $USE_HTML
? "\tBuilding category $name...\n"
: "\tBuilding category $name...\n";
my $total = $CatLinks->count({ 'CatLinks.CategoryID' => $id }, VIEWABLE);
print "\t\tLinks: $total\n";
# Do sub-pages if requested.
if ($CFG->{build_span_pages}) {
my $lpp = $CFG->{build_links_per_page} || 25;
my $num_pages = int($total / $lpp);
$num_pages++ if $total % $lpp;
# Create the main page.
{
my $fh = _open_write($page);
print $fh Links::Build::build(category => { id => $id, nh => 1, mh => $lpp });
}
my $perms = oct $CFG->{build_file_per};
chmod $perms, $page;
# Create the sub pages.
for (2 .. $num_pages) {
$page = "$CFG->{build_root_path}/$clean_name/$CFG->{build_more}$_$CFG->{build_extension}";
$url = "$CFG->{build_root_url}/$clean_name/$CFG->{build_more}$_$CFG->{build_extension}";
print "\t\tBuilding subpage: " . ($USE_HTML
? "$_\n"
: "$_\n"
);
{
my $fh = _open_write($page);
print $fh Links::Build::build(category => { id => $id, nh => $_, mh => $lpp });
}
chmod $perms, $page;
}
}
else {
{
my $fh = _open_write($page);
print $fh Links::Build::build(category => { id => $id });
}
my $perms = oct $CFG->{build_file_per};
chmod $perms, $page;
}
print "\tDone\n\n";
}
_display_time("Finished building categories");
}
sub _build_backup {
# ------------------------------------------------------------------
# Create a backup file in our backup directory.
#
if (! $CFG->{build_use_backup}) {
print "Creating backup file... skipped\n\n";
return;
}
_time_start();
print "Creating backup file...\n";
require Links::Import::S2BK;
my $max_keep = 7;
my $root = $CFG->{admin_root_path} . '/backup';
my $filename = 'BACKUP';
for my $n (reverse 0 .. $max_keep) {
my $oldname = join '.', $filename, $n || ();
my $newname = join '.', $filename, $n+1;
if (-e "$root/$oldname") {
rename "$root/$oldname", "$root/$newname" or print "\tCouldn't rename '$root/$oldname' -> '$root/$newname': $!";
}
}
Links::Import::S2BK::import({ source => "$CFG->{admin_root_path}/defs", destination => "$root/$filename", delimiter => "\t" }, sub { print "\n\tWARNING: @_\n" }, sub { die @_ }, sub { print "\n\tWARNING: @_\n" }, sub { });
_display_time();
}
sub _build_reset_hits {
# ------------------------------------------------------------------
# Updates the What's New flags.
#
_time_start();
print "Resetting hits and rates...\n";
my $ret = Links::Build::build(reset_hits => shift || {});
_display_time();
return $ret;
}
sub _build_new_flags {
# ------------------------------------------------------------------
# Updates the What's New flags.
#
_time_start();
print "Updating new flags...\n";
my $ret = Links::Build::build(new_flags => shift || {});
_display_time();
return $ret;
}
sub _build_changed_flags {
# ------------------------------------------------------------------
# Updates the isChanged flags.
#
_time_start();
print "Updating changed flags...\n";
my $ret = Links::Build::build(changed_flags => shift || {});
_display_time();
return $ret;
}
sub _build_cool_flags {
# ------------------------------------------------------------------
# Updates the What's Cool flags.
#
_time_start();
print "Updating Cool Flags...\n";
my $ret = Links::Build::build(cool_flags => shift || {});
_display_time();
return $ret;
}
sub _time_start {
# ------------------------------------------------------------------
# Start a timer.
#
$TIME_START = time;
}
sub _display_time {
# ------------------------------------------------------------------
# Return time results.
#
my $message = shift || 'Done';
return;
printf "%s (%.2fs)\n\n", $message, time - $TIME_START;
}
sub _header {
# ------------------------------------------------------------------
# Print intro.
#
my ($msg, $msg2, $refresh, $started) = @_;
my $time = scalar localtime;
$refresh ||= '';
$TOTAL_TIME = $started || time;
$refresh &&= "";
if ($USE_HTML) {
print $IN->header(-nph => $CFG->{nph_headers});
print <
$refresh
Building HTML Pages
BUILDING
print Links::header("Building HTML Pages: $msg", $msg2, 0);
print <Started at $time.
STARTED
}
else {
print "Started at $time.\n\nBuilding HTML pages...\n\n";
}
}
sub _footer {
# ------------------------------------------------------------------
# Print the footer.
#
my $end = time;
my $elapsed = sprintf "%.2f", $end - $TOTAL_TIME;
print "All done. Total time: (${elapsed}s)\n";
print "" if $USE_HTML;
}
sub _open_write {
# -----------------------------------------------------------------------------
# Opens a file for writing (overwriting anything already there), and returns a
# filehandle reference. Dies with a more user-friendly error then Links::fatal
# if the open fails. Can take a second argument which, if true, will cause the
# function _not_ to attempt to make the containing directory.
#
my ($page, $nomkdir) = @_;
unless ($nomkdir) {
mkpath(dirname($page), oct $CFG->{build_dir_per});
}
my $fh = \do { local *FH; *FH };
open $fh, "> $page" and return $fh;
my $error = "$!";
my $user = eval { getpwuid($>) } || 'webserver';
if ($error =~ /permission/i) {
print "\n\nERROR: Unable to open '$page': $error\n\n";
if (-e $page) {
print <