First pass at adding key files
This commit is contained in:
437
site/slowtwitch.com/cgi-bin/articles/admin/Links/Admin.pm
Normal file
437
site/slowtwitch.com/cgi-bin/articles/admin/Links/Admin.pm
Normal file
@@ -0,0 +1,437 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Admin.pm,v 1.16 2005/03/05 01:29:09 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::Admin;
|
||||
# ==================================================================
|
||||
use strict;
|
||||
use GT::SQL::Admin;
|
||||
use Links qw/$DB/;
|
||||
use GT::AutoLoader;
|
||||
use vars qw/@ISA $ERROR_MESSAGE $FONT $DEBUG/;
|
||||
|
||||
@ISA = qw/GT::SQL::Admin/;
|
||||
$DEBUG = 0;
|
||||
$ERROR_MESSAGE = 'GT::SQL';
|
||||
$FONT = $GT::SQL::Admin::FONT;
|
||||
|
||||
# Make sure AUTOLOAD does not catch destroyed objects.
|
||||
sub DESTROY {}
|
||||
|
||||
$COMPILE{modify_multi_records} = __LINE__ . <<'END_OF_SUB';
|
||||
sub modify_multi_records {
|
||||
# -------------------------------------------------------------------
|
||||
# Overrides the Links table to format the category name properly.
|
||||
#
|
||||
my $self = shift;
|
||||
my $name = $self->{table}->name;
|
||||
my $prefix = $DB->prefix;
|
||||
return $self->SUPER::modify_multi_records(@_) unless ( $name eq $prefix . 'Links');
|
||||
|
||||
if (! exists $self->{cgi}->{modify}) {
|
||||
return $self->modify_error("Please select a record to modify before continuing.");
|
||||
}
|
||||
# If they selected only one record to modify we still need an array ref
|
||||
ref $self->{cgi}->{modify} eq 'ARRAY' or $self->{cgi}->{modify} = [$self->{cgi}->{modify}];
|
||||
|
||||
# Format the cgi for inserting
|
||||
$self->format_insert_cgi;
|
||||
|
||||
# Hash to handle errors if there are any errors.
|
||||
my $errors = {};
|
||||
my $errcode = {};
|
||||
|
||||
# Need to know the names of the columns for this Table.
|
||||
my @columns = keys %{$self->{table}->cols};
|
||||
push @columns, 'CatLinks.CategoryID';
|
||||
|
||||
# Need to know the number of records modified
|
||||
my $rec_modified = 0;
|
||||
|
||||
# For through the record numbers. These are the values of the
|
||||
# check boxes
|
||||
foreach my $rec_num (@{$self->{cgi}->{modify}}) {
|
||||
my $change = {};
|
||||
foreach my $column (@columns) {
|
||||
$change->{$column} = $self->{cgi}->{"$rec_num-$column"} if exists $self->{cgi}->{"$rec_num-$column"};
|
||||
}
|
||||
|
||||
# Make the changes and capture any errors.
|
||||
my $ret = $self->{table}->modify($change);
|
||||
if (defined $ret) {
|
||||
$rec_modified++;
|
||||
}
|
||||
else {
|
||||
if ($self->{table}->error) {
|
||||
my $error = $self->{table}->error;
|
||||
$error =~ s/\n/<br>\n<li>/g;
|
||||
$errors->{$rec_num} = "<li>$error";
|
||||
}
|
||||
$errcode->{$rec_num} = $GT::SQL::errcode if ($GT::SQL::errcode);
|
||||
}
|
||||
}
|
||||
|
||||
# Return the results page with the proper arguments depending on if we got an error or not.
|
||||
return (keys %$errors) ? $self->modify_multi_results($rec_modified, $errors, $errcode) : $self->modify_multi_results($rec_modified);
|
||||
}
|
||||
END_OF_SUB
|
||||
|
||||
$COMPILE{editor_import_data_form} = __LINE__ . <<'END_OF_SUB';
|
||||
sub editor_import_data_form {
|
||||
# -------------------------------------------------------------------
|
||||
# Allow the import to import category/link data. Only used if called with the
|
||||
# Links database.
|
||||
#
|
||||
my $self = shift;
|
||||
my $name = $self->{table}->name;
|
||||
my $prefix = $DB->prefix;
|
||||
return $self->SUPER::editor_import_data_form(@_) unless ( $name eq $prefix . 'Links');
|
||||
|
||||
my $msg = shift;
|
||||
print $self->{in}->header;
|
||||
$msg &&= qq|<FONT COLOR="red"><B>$msg</B></FONT>|;
|
||||
my $table = $self->{record};
|
||||
|
||||
|
||||
print $self->_start_html ( { title => "Links Table Editor: $table" });
|
||||
print $self->_header ("Links Table Editor", $msg || "Import Data to $table.");
|
||||
print $self->_start_form ( { do => 'editor_import_data', db => $self->{cgi}->{db} }, { name => 'ImportForm'});
|
||||
|
||||
print qq~
|
||||
<table border=0 width=500><tr><td>
|
||||
<p><font $FONT>You can either import from a file or you can cut and paste the contents into a textarea box. If you
|
||||
have a large number of records, you should really import from a file. The first row of your input should be the
|
||||
fully qualified column names. You must also include the Category ID and Category Name of the category the link
|
||||
will be imported to.<br>
|
||||
|
||||
</td></tr></table>
|
||||
<br>
|
||||
<table border=0 width=500><tr><td>
|
||||
<p><font $FONT>
|
||||
Import data from file: <input type=text name="import-file" size=10> or from textarea box:<br>
|
||||
<textarea name="import-text" rows=3 cols=40></textarea><br>
|
||||
Use <input type=text name="import-delim" value="|" size=1> as delimiter.
|
||||
<input type=checkbox name="import-delete" value=1> Delete old data first
|
||||
<br>
|
||||
</tr></td></table>
|
||||
~;
|
||||
print $self->_buttons ("Import Data into");
|
||||
print "<P>";
|
||||
print $self->_end_form;
|
||||
print $self->_prop_navbar;
|
||||
print "<P>";
|
||||
print $self->_footer;
|
||||
print $self->_end_html;
|
||||
|
||||
}
|
||||
END_OF_SUB
|
||||
|
||||
$COMPILE{editor_import_data} = __LINE__ . <<'END_OF_SUB';
|
||||
sub editor_import_data {
|
||||
# -------------------------------------------------------------------
|
||||
# Allow the import to import category/link data. Only used if called with the
|
||||
# Links database.
|
||||
#
|
||||
my $self = shift;
|
||||
my $name = $self->{table}->name;
|
||||
my $prefix = $DB->prefix;
|
||||
return $self->SUPER::editor_import_data(@_) unless ( $name eq $prefix . 'Links');
|
||||
|
||||
my $delim = $self->{cgi}->{'import-delim'} or return $self->editor_import_data_form ("No import delimiter specified!");
|
||||
my $file = $self->{cgi}->{'import-file'};
|
||||
my $text = $self->{cgi}->{'import-text'};
|
||||
|
||||
# Make sure there is some data to import
|
||||
$file or $text or return $self->editor_import_data_form ("You must enter in at least a filename or data in the textarea box.");
|
||||
$file and $text and return $self->editor_import_data_form ("Please only enter either a filename or data in the textarea box, not both.");
|
||||
|
||||
$delim =~ s/\\t/\t/g;
|
||||
$delim =~ /%/ and $self->editor_import_data_form("% may not be used as a delimited.");
|
||||
|
||||
# Store the lines to import in @lines and the header in $header.
|
||||
my ($good_cnt, $err_cnt, $line, $line_num, @lines, @data, $error, %record, $i);
|
||||
if ($file) {
|
||||
local *FILE;
|
||||
open (FILE, "<$file") or return $self->editor_import_data_form ("Unable to open file: '$file'. Reason: $!");
|
||||
local $/;
|
||||
@lines = split /[\r\n]+/, scalar <FILE>;
|
||||
close FILE;
|
||||
}
|
||||
else {
|
||||
@lines = split /[\r\n]+/, $text;
|
||||
}
|
||||
|
||||
# Fetch the header.
|
||||
my @header = split /\Q$delim\E/, shift @lines;
|
||||
unless (grep { $_ eq ($prefix . 'Category.ID')} @header) {
|
||||
return $self->editor_import_data_form ("Unable to find Category ID column in header!");
|
||||
}
|
||||
|
||||
# Remove Links table prefix.
|
||||
my $full_name = $prefix . 'Links';
|
||||
@header = map { s/\Q$full_name\E\.//; $_; } @header;
|
||||
|
||||
my $Links = $DB->table('Links');
|
||||
my $CatLinks = $DB->table('CatLinks');
|
||||
my $Category = $DB->table('Category');
|
||||
|
||||
# Remove old data if requested.
|
||||
my $delete = $self->{cgi}->{'import-delete'};
|
||||
if ($delete) {
|
||||
$Links->delete_all or die $GT::SQL::error;
|
||||
$CatLinks->delete_all or die $GT::SQL::error;
|
||||
}
|
||||
|
||||
# Do the import.
|
||||
$good_cnt = $err_cnt = 0;
|
||||
|
||||
my %link_map;
|
||||
|
||||
LINE: for my $line_num (0 .. $#lines) {
|
||||
($err_cnt > 10) and last LINE;
|
||||
$line = $lines[$line_num];
|
||||
@data = split /\Q$delim/, $line, -1;
|
||||
if ($#data != $#header) {
|
||||
$error .= "<li>" . ($line_num+2) . ": Row count: " . ($#data+1) .
|
||||
" does not match header count: (@data) (@header)" . ($#header+1) . "\n";
|
||||
$err_cnt++;
|
||||
next LINE;
|
||||
}
|
||||
for (@data) {
|
||||
s/%([0-9a-fA-F]{2})/chr hex $1/eg;
|
||||
}
|
||||
$i = 0;
|
||||
%record = map { $data[$i] =~ s,^"|"$,,g; $header[$i] => $data[$i++] } @data;
|
||||
my $cat_id = delete $record{$prefix . 'Category.ID'};
|
||||
my $cat_name = delete $record{$prefix . 'Category.Name'};
|
||||
unless ($Category->count({ ID => $cat_id })) {
|
||||
$cat_id = $Category->insert({ Name => $cat_name })->insert_id;
|
||||
}
|
||||
my $link_id = delete $record{ID};
|
||||
if ($link_id and $link_map{$link_id}) {
|
||||
$link_id = $link_map{$link_id};
|
||||
}
|
||||
else {
|
||||
my $count = $Links->count({ ID => $link_id });
|
||||
if ($link_id and $count) {
|
||||
if ($count) {
|
||||
unless ($Links->update(\%record, { ID => $link_id })) {
|
||||
$error .= "<li>" . ($line_num+2) . ": Failed to update record. Error <ul>$GT::SQL::error</ul>\n";
|
||||
$err_cnt++;
|
||||
next LINE;
|
||||
}
|
||||
$link_map{$link_id} = $link_id;
|
||||
}
|
||||
else {
|
||||
my $old_id = $link_id;
|
||||
my $sth = $Links->insert(\%record);
|
||||
unless ($sth and ($link_id = $sth->insert_id)) {
|
||||
$error .= "<li>" . ($line_num+2) . ": Failed to add new record. Error <ul>$GT::SQL::error</ul>\n";
|
||||
$err_cnt++;
|
||||
next LINE;
|
||||
}
|
||||
$link_map{$old_id} = $link_id;
|
||||
}
|
||||
}
|
||||
else {
|
||||
my $old_id = $link_id;
|
||||
my $sth = $Links->insert(\%record);
|
||||
unless ($sth and ($link_id = $sth->insert_id)) {
|
||||
$error .= "<li>" . ($line_num+2) . ": Failed to add new record. Error <ul>$GT::SQL::error</ul>\n";
|
||||
$err_cnt++;
|
||||
next LINE;
|
||||
}
|
||||
$link_map{$old_id} = $link_id;
|
||||
}
|
||||
}
|
||||
unless ($CatLinks->count ( { LinkID => $link_id, CategoryID => $cat_id })) {
|
||||
$CatLinks->insert({ LinkID => $link_id, CategoryID => $cat_id });
|
||||
}
|
||||
$good_cnt++;
|
||||
last if ($err_cnt > 100);
|
||||
}
|
||||
|
||||
# Return the results.
|
||||
if ($error) {
|
||||
return $self->editor_import_data_form (($err_cnt >= 100) ?
|
||||
"Aborting, too many errors!<br><br>Rows imported: $good_cnt<br>Errors with the following rows:
|
||||
<font size=-1><ul>$error</ul></font><br>" :
|
||||
"Rows imported: $good_cnt<br>Errors with the following rows: <font size=-1><ul>$error</ul></font><br>");
|
||||
}
|
||||
return $self->editor_import_data_form ("Rows imported: $good_cnt.");
|
||||
}
|
||||
END_OF_SUB
|
||||
|
||||
$COMPILE{editor_export_data_form} = __LINE__ . <<'END_OF_SUB';
|
||||
sub editor_export_data_form {
|
||||
# -------------------------------------------------------------------
|
||||
# Allow the export to export category/link data. Only used if called with the
|
||||
# Links database.
|
||||
#
|
||||
my $self = shift;
|
||||
my $name = $self->{table}->name;
|
||||
my $prefix = $DB->prefix;
|
||||
return $self->SUPER::editor_export_data_form(@_) unless ( $name eq $prefix . 'Links');
|
||||
|
||||
my $msg = shift;
|
||||
|
||||
print $self->{in}->header;
|
||||
|
||||
$msg &&= qq|<FONT COLOR="red"><B>$msg</B></FONT>|;
|
||||
my $table = $self->{record};
|
||||
print $self->_start_html ( { title => "Table Editor: $table" });
|
||||
print $self->_header ("Table Editor", $msg || "Export Data from $table.");
|
||||
print $self->_start_form ( { do => 'editor_export_data', db => $self->{cgi}->{db} }, {name => 'ExportForm'});
|
||||
|
||||
print qq~
|
||||
<table border=0 width=500><tr><td>
|
||||
<p><font $FONT>From here you can export your Links to either the screen or to
|
||||
a file on your server. The first line of the export will be a list of the column
|
||||
headers. The last two fields is the Category ID the link is in, and the Category Name.
|
||||
If a link is in more then one category, you will get one row for each occurrence.
|
||||
</font>
|
||||
</td></tr></table>
|
||||
<br>
|
||||
<table border=0 width=500><tr><td><font $FONT>
|
||||
Export data to: <select name="export-mode"><option>file<option>screen</select>
|
||||
filename: <input type=text name="export-file" size=10><br>
|
||||
Use <input type=text name="export-delim" value="|" size=1> as delimiter.
|
||||
</font>
|
||||
</td></tr></table>
|
||||
<br>
|
||||
~;
|
||||
print $self->_buttons ("Export Data from");
|
||||
print "<P>";
|
||||
print $self->_end_form;
|
||||
print $self->_prop_navbar;
|
||||
print "<P>";
|
||||
print $self->_footer;
|
||||
print $self->_end_html;
|
||||
|
||||
}
|
||||
END_OF_SUB
|
||||
|
||||
$COMPILE{editor_export_data} = __LINE__ . <<'END_OF_SUB';
|
||||
sub editor_export_data {
|
||||
# -------------------------------------------------------------------
|
||||
# Allow the export to export category/link data. Only used if called with the
|
||||
# Links database.
|
||||
#
|
||||
my $self = shift;
|
||||
my $name = $self->{table}->name;
|
||||
my $prefix = $DB->prefix;
|
||||
return $self->SUPER::editor_export_data(@_) unless ( $name eq $prefix . 'Links');
|
||||
|
||||
print $self->{in}->header;
|
||||
|
||||
ref $self->{cgi}->{db} and return $self->error('BADARGS','FATAL', "Editor can only be called with one table, not a relation.");
|
||||
|
||||
my @order = $self->{table}->ordered_columns;
|
||||
@order or return $self->editor_export_data_form("No fields selected to export (@order).");
|
||||
|
||||
# Add on the prefix.
|
||||
for (@order) { $_ = $prefix . 'Links.' . $_; }
|
||||
|
||||
# Add the ID and Category Name.
|
||||
push @order, $prefix . 'Category.ID', $prefix .'Category.Name';
|
||||
|
||||
my $delim = $self->{cgi}->{'export-delim'};
|
||||
$delim = "\t" if $delim eq '\t';
|
||||
length $delim or $self->editor_export_data_form("No delimiter entered.");
|
||||
$delim =~ /%/ and $self->editor_export_data_form("% may not be used as a delimited.");
|
||||
|
||||
my $screen = $self->{cgi}->{'export-mode'} ne 'file';
|
||||
|
||||
local *FILE;
|
||||
if ($screen) {
|
||||
open FILE, ">&STDOUT";
|
||||
print FILE $self->{in}->header(); # print FILE to avoid STDOUT vs. FILE buffering issues
|
||||
print FILE "<html><head><title>Links Export</title></head><body><pre>";
|
||||
}
|
||||
else {
|
||||
my $filename = $self->{cgi}->{'export-file'} or return $self->editor_export_data_form("Please enter a file name!");
|
||||
open FILE, "> $filename" or return $self->editor_export_data_form("Unable to open file '$filename': $!");
|
||||
}
|
||||
|
||||
# Print the row header.
|
||||
print FILE join ($delim, @order), "\n";
|
||||
|
||||
# Print the data.
|
||||
my $db = $DB->table(qw/Links CatLinks Category/);
|
||||
my $sth = $db->select(\@order) or return $self->editor_export_data_form($GT::SQL::error);
|
||||
my $delim_re = quotemeta $delim;
|
||||
my $delim_str = join '', map sprintf("%%%02x", ord($_)), split '', $delim;
|
||||
{
|
||||
local $, = $delim;
|
||||
local $\ = "\n";
|
||||
while (my $row = $sth->fetchrow_arrayref) {
|
||||
for (@$row) {
|
||||
s{$delim_re}{$delim_str}g;
|
||||
s{%} {%25}g;
|
||||
s{\r} {}g;
|
||||
s{\n} {%0a}g;
|
||||
$_ = $self->{in}->html_escape($_) if $screen;
|
||||
}
|
||||
print FILE @$row;
|
||||
}
|
||||
}
|
||||
print FILE "</pre></body></html>" if $screen;
|
||||
return $self->editor_export_data_form("Data has been exported to: $self->{cgi}->{'export-file'}") unless $screen;
|
||||
return;
|
||||
}
|
||||
END_OF_SUB
|
||||
|
||||
sub _check_opts {
|
||||
# -------------------------------------------------------------------
|
||||
# Need to override this so searching for categories works.
|
||||
#
|
||||
my $self = shift;
|
||||
my $sel = 0;
|
||||
|
||||
# Relation does not plat fare :(
|
||||
my $cols = $self->{table}->cols;
|
||||
for (keys %{$self->{cgi}}) { $sel = 1 if (($self->{cgi}->{$_} =~ /\S/) and exists $cols->{$_}) }
|
||||
if ((exists $self->{cgi}->{query} and $self->{cgi}->{query} =~ /\S/) or
|
||||
(exists $self->{cgi}->{keyword} and $self->{cgi}->{keyword} =~ /\S/)) {
|
||||
$sel = 1;
|
||||
}
|
||||
my $prefix = $DB->prefix;
|
||||
if (! $sel and ($self->{table}->name eq $prefix . 'Links') and (exists $self->{cgi}->{'CatLinks.CategoryID'})) {
|
||||
$sel = 1;
|
||||
}
|
||||
$sel or return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub _buttons {
|
||||
# -------------------------------------------------------------------
|
||||
# Adds a warning message to delete Users and Categories.
|
||||
#
|
||||
my $self = shift;
|
||||
my $name = shift;
|
||||
my $prefix = GT::SQL->prefix();
|
||||
my $msg = '';
|
||||
if (($self->{table}->name eq $prefix . "Users") and ($name eq 'Delete')) {
|
||||
$msg = qq~<p><font face="Tahoma,Arial,Helvetica" size="2"><font color="red"><b>Warning:</b></font> deleting a user will also delete all links associated with that user!</font></p>~;
|
||||
}
|
||||
if (($self->{table}->name eq $prefix . "Category") and ($name eq 'Delete')) {
|
||||
$msg = qq~<p><font face="Tahoma,Arial,Helvetica" size="2"><font color="red"><b>Warning:</b></font> deleting a category will also delete all sub categories and links in those categories!</font></p>~;
|
||||
}
|
||||
|
||||
return qq~
|
||||
<table border=1 cellpadding=0 cellspacing=0><tr><td align=center>
|
||||
<table border=0 width=500><tr><td align=center>$msg<center><font face="Tahoma,Arial,Helvetica" size="2"><input type=submit value="$name $self->{record}"></font></center></td></tr></table>
|
||||
</td></tr></table>
|
||||
~;
|
||||
}
|
||||
|
||||
1;
|
||||
244
site/slowtwitch.com/cgi-bin/articles/admin/Links/Authenticate.pm
Normal file
244
site/slowtwitch.com/cgi-bin/articles/admin/Links/Authenticate.pm
Normal file
@@ -0,0 +1,244 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Authenticate.pm,v 1.34 2008/10/06 17:41:18 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::Authenticate;
|
||||
# ==================================================================
|
||||
use strict;
|
||||
use Links qw/:objects/;
|
||||
use GT::Session::SQL;
|
||||
|
||||
# This package lets you integrate Gossamer Links into another authentication
|
||||
# system. You can do this by replacing the functions with your own
|
||||
# code. Note: to return error results, simply set error => message in
|
||||
# the passed in hash.
|
||||
|
||||
sub auth {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Runs the request auth function through the plugin system.
|
||||
#
|
||||
shift if UNIVERSAL::isa($_[0], 'Links::Authenticate');
|
||||
my ($auth, $args) = @_;
|
||||
my $code = exists $Links::Authenticate::{"auth_$auth"}
|
||||
? $Links::Authenticate::{"auth_$auth"}
|
||||
: die "Invalid Authenticate method 'auth_$auth' called";
|
||||
$PLG->dispatch("auth_$auth", $code, $args);
|
||||
}
|
||||
|
||||
sub auth_init {
|
||||
# -----------------------------------------------------------------------------
|
||||
# This function is guaranteed to be called before any other authentication
|
||||
# function, but may be called multiple times during one request.
|
||||
#
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub auth_add_user {
|
||||
# -----------------------------------------------------------------------------
|
||||
# This function is called whenever a user is added to the database. It takes a
|
||||
# hash reference with Username and Password as input. If there is an error, set
|
||||
# $args->{error} to the message.
|
||||
#
|
||||
my $args = shift;
|
||||
return { Username => $args->{Username}, Password => $args->{Password} };
|
||||
}
|
||||
|
||||
sub auth_del_user {
|
||||
# -----------------------------------------------------------------------------
|
||||
# This function is called whenever a user is trying to be deleted. It returns
|
||||
# the username on success, or undef on failure.
|
||||
#
|
||||
my $args = shift;
|
||||
return $args->{Username};
|
||||
}
|
||||
|
||||
sub auth_valid_user {
|
||||
# -----------------------------------------------------------------------------
|
||||
# This function returns true if the user/pass combo is valid, 0/undef
|
||||
# otherwise.
|
||||
#
|
||||
my $args = shift;
|
||||
return int $DB->table('Users')->count({ Username => $args->{Username}, Password => $args->{Password} });
|
||||
}
|
||||
|
||||
sub auth_valid_format {
|
||||
# -----------------------------------------------------------------------------
|
||||
# This function returns 1 if the user format is valid, undef otherwise.
|
||||
#
|
||||
my $args = shift;
|
||||
my $user = $args->{Username};
|
||||
return if length $user > 50 or $user !~ /^[\w\s\-\@\.]+$/;
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub auth_change_pass {
|
||||
# -----------------------------------------------------------------------------
|
||||
# This function takes the username, old pass and new pass and returns 1 if
|
||||
# successful, false otherwise.
|
||||
#
|
||||
my $args = shift;
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub auth_get_pass {
|
||||
# -----------------------------------------------------------------------------
|
||||
# This function returns the password (if available) of a given user, undef
|
||||
# otherwise.
|
||||
#
|
||||
my $args = shift;
|
||||
my $user = $args->{Username};
|
||||
my $pass = $DB->table('Users')->select(Password => { Username => $user })->fetchrow;
|
||||
return $pass;
|
||||
}
|
||||
|
||||
sub auth_get_user {
|
||||
# -----------------------------------------------------------------------------
|
||||
# This function returns user information for a given user, auto creating if it
|
||||
# doesn't exist.
|
||||
#
|
||||
my $args = shift;
|
||||
my $user = $args->{Username};
|
||||
my $pass = $args->{Password};
|
||||
my $db = $DB->table('Users');
|
||||
my $user_r = $db->get($user);
|
||||
if (!$user_r and $args->{auto_create}) {
|
||||
$user_r->{Username} = $user;
|
||||
$user_r->{Password} = defined $pass ? $pass : Links::Authenticate::auth('get_pass', { Username => $user });
|
||||
$user_r->{Email} = $user . '@noemail.nodomain';
|
||||
$user_r->{ReceiveMail} = 'No';
|
||||
$user_r->{Password} = '' unless defined $user_r->{Password};
|
||||
my $defaults = $db->default();
|
||||
for (keys %$defaults) {
|
||||
$user_r->{$_} = $defaults->{$_} unless exists $user_r->{$_};
|
||||
}
|
||||
$db->insert($user_r) or die "Unable to auto-create user: $user. Reason: $GT::SQL::error";
|
||||
}
|
||||
return $user_r;
|
||||
}
|
||||
|
||||
sub auth_valid_session {
|
||||
# -----------------------------------------------------------------------------
|
||||
# This functions checks to see if the session is valid, and returns the
|
||||
# username.
|
||||
#
|
||||
my $args = shift;
|
||||
my $session_id = $IN->param('s') || $IN->cookie($CFG->{user_cookie_prefix} . 's') || return;
|
||||
my $session;
|
||||
unless ($session = GT::Session::SQL->new({
|
||||
_debug => $CFG->{debug_level},
|
||||
tb => $DB->table('Sessions'),
|
||||
session_id => $session_id,
|
||||
expires => $CFG->{user_session_length},
|
||||
session_data => { sessions => $CFG->{user_sessions}, d => scalar($IN->param('d')) },
|
||||
})) { # Possibly an expired session
|
||||
GT::Session::SQL->new({
|
||||
tb => $DB->table('Sessions'),
|
||||
expires => $CFG->{user_session_length}
|
||||
})->cleanup; # Clear out old sessions
|
||||
return;
|
||||
}
|
||||
|
||||
return $session->{info}->{session_user_id};
|
||||
}
|
||||
|
||||
sub auth_create_session {
|
||||
# -----------------------------------------------------------------------------
|
||||
# This function creates a session, and prints the header and returns a hash
|
||||
# reference with session => $id, and redirect => 0/1.
|
||||
#
|
||||
my $args = shift;
|
||||
my $user = $args->{Username};
|
||||
my $remember = ($CFG->{user_sessions} eq 'Cookies' and ($args->{Remember} or $IN->param('Remember')));
|
||||
|
||||
# Create a new session.
|
||||
my $session = GT::Session::SQL->new({
|
||||
_debug => $CFG->{debug_level},
|
||||
tb => $DB->table('Sessions'),
|
||||
session_user_id => $user,
|
||||
session_data => { sessions => $CFG->{user_sessions}, d => scalar($IN->param('d')) },
|
||||
expires => ($remember ? 0 : $CFG->{user_session_length}),
|
||||
});
|
||||
|
||||
# Clear out old sessions.
|
||||
$session->cleanup;
|
||||
|
||||
# Get session id
|
||||
my $session_id = $session->{info}->{session_id};
|
||||
|
||||
# Now redirect to another URL and set cookies, or set URL string.
|
||||
my $url = $IN->param('url');
|
||||
my $redirect = 0;
|
||||
if ($CFG->{user_sessions} eq 'Cookies') {
|
||||
my $session_cookie = $IN->cookie(
|
||||
-name => $CFG->{user_cookie_prefix} . 's',
|
||||
-value => $session_id,
|
||||
-path => '/',
|
||||
-domain => $CFG->{user_cookie_domain},
|
||||
-expires => ($remember ? '+10y' : '')
|
||||
);
|
||||
if ($url) {
|
||||
print $IN->redirect(-force => 1, -cookie => [$session_cookie], -url => $url);
|
||||
$redirect = 1;
|
||||
}
|
||||
else {
|
||||
print $IN->header(-force => 1, -cookie => [$session_cookie]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
# If URL sessions are used, then the user will be forced into dynamic mode
|
||||
# since there's no way to pass around the session id with the static URLs.
|
||||
if ($url) {
|
||||
unless ($url =~ s/([;&\?]s=)([^&;]+)/$1$session_id/) {
|
||||
$url .= ($url =~ /\?/ ? ';' : '?') . "s=$session_id";
|
||||
}
|
||||
unless ($url =~ /([;&\?]d=)([^&;]+)/) {
|
||||
$url .= ($url =~ /\?/ ? ';' : '?') . "d=1";
|
||||
}
|
||||
print $IN->redirect($url);
|
||||
$redirect = 1;
|
||||
}
|
||||
else {
|
||||
$IN->param(s => $session_id);
|
||||
$IN->param(d => 1);
|
||||
print $IN->header();
|
||||
}
|
||||
}
|
||||
return { session => $session_id, redirect => $redirect };
|
||||
}
|
||||
|
||||
sub auth_delete_session {
|
||||
# -----------------------------------------------------------------------------
|
||||
# This function removes a session, returns 1 on success, undef on failure.
|
||||
#
|
||||
print $IN->header(
|
||||
-cookie => $IN->cookie(
|
||||
-name => $CFG->{user_cookie_prefix} . 's',
|
||||
-value => '',
|
||||
-path => '/',
|
||||
-domain => $CFG->{user_cookie_domain},
|
||||
-expires => '-1y'
|
||||
)
|
||||
);
|
||||
my $session_id = $IN->cookie($CFG->{user_cookie_prefix} . 's') || $IN->param('s') || return;
|
||||
|
||||
my $session = GT::Session::SQL->new({
|
||||
_debug => $CFG->{debug_level},
|
||||
tb => $DB->table('Sessions'),
|
||||
session_id => $session_id
|
||||
}) or return;
|
||||
|
||||
# Delete the cookie
|
||||
$session->delete or return;
|
||||
1;
|
||||
}
|
||||
|
||||
1;
|
||||
753
site/slowtwitch.com/cgi-bin/articles/admin/Links/Bookmark.pm
Normal file
753
site/slowtwitch.com/cgi-bin/articles/admin/Links/Bookmark.pm
Normal file
@@ -0,0 +1,753 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Bookmark.pm,v 1.35 2007/08/28 22:57:14 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::Bookmark;
|
||||
# ==================================================================
|
||||
|
||||
use strict;
|
||||
use Links qw/:objects/;
|
||||
use Links::Build;
|
||||
use Links::SiteHTML;
|
||||
|
||||
sub handle {
|
||||
# -------------------------------------------------------------------
|
||||
#
|
||||
my $action = $IN->param('action');
|
||||
my %valid = (
|
||||
map { $_ => 1 } qw(
|
||||
show_folders
|
||||
show_links
|
||||
folder_add
|
||||
folder_edit
|
||||
folder_remove
|
||||
folder_view
|
||||
edit_preferences
|
||||
link_add
|
||||
edit_bookmark
|
||||
links_manage
|
||||
users_list
|
||||
users_folder
|
||||
users_links
|
||||
)
|
||||
);
|
||||
|
||||
if ($action !~ /^users_/ and !$USER) {
|
||||
print $IN->redirect(Links::redirect_login_url('bookmark'));
|
||||
return;
|
||||
}
|
||||
|
||||
no strict 'refs';
|
||||
if ($action eq 'folder_manage') {
|
||||
return $PLG->dispatch("bookmark_$action", \&show_links);
|
||||
}
|
||||
if ($action eq 'users_links') {
|
||||
return $PLG->dispatch("bookmark_$action", \&folder_view);
|
||||
}
|
||||
if (defined &$action and $valid{$action}) {
|
||||
return $PLG->dispatch("bookmark_$action", \&$action);
|
||||
}
|
||||
|
||||
# Otherwise display the modify form.
|
||||
$PLG->dispatch("bookmark_show_folders", \&show_folders);
|
||||
}
|
||||
|
||||
sub show_folders {
|
||||
# --------------------------------------------------------
|
||||
# Show Folders
|
||||
#
|
||||
my $username = $USER->{Username};
|
||||
my $folders = _folder_list($username);
|
||||
if (exists $folders->{paging} and exists $folders->{paging}->{url}) {
|
||||
$folders->{paging}->{url} .= (index($folders->{paging}->{url}, '?') != -1 ? ';' : '?') . "action=show_folders";
|
||||
}
|
||||
|
||||
$folders->{link_count} ||= $DB->table('Bookmark_Links', 'Links')->count({ my_user_username_fk => $username }, VIEWABLE);
|
||||
$folders->{message} = shift;
|
||||
$folders->{error} = shift;
|
||||
$folders->{main_title_loop} = Links::Build::build('title', Links::language('LINKS_BOOKMARK'), "$CFG->{db_cgi_url}/bookmark.cgi");
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('bookmark_list', $folders);
|
||||
}
|
||||
|
||||
sub users_list {
|
||||
# --------------------------------------------------------
|
||||
# Display the users list with public folders
|
||||
#
|
||||
my $error = shift || "";
|
||||
my $db = $DB->table('Users', 'Bookmark_Folders');
|
||||
|
||||
# Get our options.
|
||||
my ($limit, $offset, $nh) = Links::limit_offset($CFG->{bookmark_users_per_page});
|
||||
|
||||
$db->select_options("GROUP BY my_folder_user_username_fk");
|
||||
$db->select_options("ORDER BY my_folder_user_username_fk ASC");
|
||||
$db->select_options("LIMIT $limit OFFSET $offset");
|
||||
|
||||
my @users;
|
||||
my $sth = $db->select('my_folder_user_username_fk', { my_folder_public => 1 });
|
||||
my $total = $db->select('COUNT(DISTINCT(my_folder_user_username_fk))', { my_folder_public => 1 })->fetchrow;
|
||||
|
||||
while (my $row = $sth->fetchrow_hashref()) {
|
||||
$row->{public_folders} = $db->count({ my_folder_user_username_fk => $row->{my_folder_user_username_fk}, my_folder_public => 1 });
|
||||
$row->{public_links} = _total_pub_links($row->{my_folder_user_username_fk});
|
||||
push @users, $row;
|
||||
}
|
||||
|
||||
my ($toolbar, %paging);
|
||||
if ($total > $limit) {
|
||||
my $url = _bookmark_url();
|
||||
$url .= (index($url, '?') != -1 ? ';' : '?') . 'action=users_list';
|
||||
$toolbar = $DB->html($db, $IN)->toolbar($nh, $limit, $total, $url);
|
||||
%paging = (
|
||||
url => $url,
|
||||
num_hits => $total,
|
||||
max_hits => $limit,
|
||||
current_page => $nh
|
||||
);
|
||||
}
|
||||
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('bookmark_users', {
|
||||
users => \@users,
|
||||
total_users => $total,
|
||||
error => $error,
|
||||
toolbar => $toolbar,
|
||||
paging => \%paging,
|
||||
main_title_loop => Links::Build::build('title', Links::language('LINKS_BOOKMARK'), "$CFG->{db_cgi_url}/bookmark.cgi")
|
||||
});
|
||||
}
|
||||
|
||||
sub users_folder {
|
||||
# --------------------------------------------------------
|
||||
# Display user's public folders
|
||||
#
|
||||
my $username = $IN->param('my_folder_username');
|
||||
|
||||
unless ($DB->table('Users')->get($username)) {
|
||||
return users_list(Links::language('BOOKMARK_USER_NOTEXISTS', $username));
|
||||
}
|
||||
my $folders = _folder_list($username);
|
||||
if (exists $folders->{paging} and exists $folders->{paging}->{url}) {
|
||||
$folders->{paging}->{url} .= (index($folders->{paging}->{url}, '?') != -1 ? ';' : '?') . "action=users_folder;my_folder_username=" . $IN->escape($username);
|
||||
}
|
||||
|
||||
$folders->{link_count} ||= $DB->table('Bookmark_Links', 'Bookmark_Folders', 'Links')->count({ my_user_username_fk => $username, my_folder_public => 1 }, VIEWABLE);
|
||||
$folders->{message} = shift;
|
||||
$folders->{error} = shift;
|
||||
$folders->{main_title_loop} = Links::Build::build('title', Links::language('LINKS_BOOKMARK'), "$CFG->{db_cgi_url}/bookmark.cgi");
|
||||
if ($folders->{link_count} == 0 and $folders->{folder_count} == 0) {
|
||||
my $error = Links::language('BOOKMARK_PUBLIC_USER', $username);
|
||||
return users_list($error);
|
||||
}
|
||||
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('bookmark_folder_view', $folders);
|
||||
}
|
||||
|
||||
sub folder_view {
|
||||
# --------------------------------------------------------
|
||||
# View the links in the folder
|
||||
#
|
||||
my $message = shift;
|
||||
my $error = shift;
|
||||
|
||||
my $mtl = Links::Build::build('title', Links::language('LINKS_BOOKMARK'), "$CFG->{db_cgi_url}/bookmark.cgi");
|
||||
my $folderid = $IN->param('my_folder_id') || $IN->param('my_folder_id_fk');
|
||||
my $folder = $DB->table('Bookmark_Folders')->get($folderid);
|
||||
unless ($folder) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('BOOKMARK_FOLDER_NOTEXISTS'), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
if ($USER->{Username} ne $folder->{my_folder_user_username_fk} and not $folder->{my_folder_public}) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('BOOKMARK_FOLDER_NOTPUBLIC'), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
my $username = $folder->{my_folder_user_username_fk};
|
||||
my $links = _links_list($username, $folderid);
|
||||
if (exists $links->{paging} and exists $links->{paging}->{url}) {
|
||||
my $action = $IN->param('action') eq 'users_links' ? 'users_links' : 'folder_view';
|
||||
$links->{paging}->{url} .= (index($links->{paging}->{url}, '?') != -1 ? ';' : '?') . "action=$action;my_folder_id=$folderid";
|
||||
}
|
||||
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('bookmark_folder_view', {
|
||||
%$links,
|
||||
my_folder_username => $username,
|
||||
message => $message,
|
||||
error => $error,
|
||||
main_title_loop => $mtl
|
||||
});
|
||||
}
|
||||
|
||||
sub show_links {
|
||||
# --------------------------------------------------------
|
||||
# Show user's link for the user to manage
|
||||
#
|
||||
my $message = shift;
|
||||
my $error = shift;
|
||||
my $username = shift || $USER->{Username};
|
||||
my $folderid = $IN->param('my_folder_id') || $IN->param('my_folder_id_fk');
|
||||
|
||||
my $mtl = Links::Build::build('title', Links::language('LINKS_BOOKMARK'), "$CFG->{db_cgi_url}/bookmark.cgi");
|
||||
my $folder = $DB->table('Bookmark_Folders')->get($folderid);
|
||||
if (not $error and $username ne $folder->{my_folder_user_username_fk} and not $folder->{my_folder_public}) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('BOOKMARK_FOLDER_NOTPUBLIC'), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
my $links = _links_list($username, $folderid);
|
||||
if (exists $links->{paging} and exists $links->{paging}->{url}) {
|
||||
$links->{paging}->{url} .= (index($links->{paging}->{url}, '?') != -1 ? ';' : '?') . "action=folder_manage;my_folder_id=$folderid";
|
||||
}
|
||||
|
||||
my $folders = _folder_list($username, $folderid, 1);
|
||||
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('bookmark_list', {
|
||||
%$links,
|
||||
folder_select => $folders->{Folders},
|
||||
message => $message,
|
||||
error => $error,
|
||||
main_title_loop => $mtl
|
||||
});
|
||||
}
|
||||
|
||||
sub _folder_list {
|
||||
# --------------------------------------------------------
|
||||
# Generate folder lists
|
||||
#
|
||||
my $username = shift;
|
||||
my $exclude = shift;
|
||||
my $dropdown = shift;
|
||||
my $bf = $DB->table('Bookmark_Folders');
|
||||
return unless $username;
|
||||
|
||||
# Get our options.
|
||||
my ($limit, $offset, $nh) = Links::limit_offset($CFG->{bookmark_folders_per_page});
|
||||
|
||||
$bf->select_options("ORDER BY my_folder_name ASC");
|
||||
unless ($dropdown) {
|
||||
$bf->select_options("LIMIT $limit OFFSET $offset");
|
||||
}
|
||||
|
||||
my $cond = GT::SQL::Condition->new(my_folder_user_username_fk => '=' => $username);
|
||||
if ($USER->{Username} ne $username) {
|
||||
$cond->add(my_folder_public => '=' => 1);
|
||||
}
|
||||
if ($exclude > 0) {
|
||||
$cond->add(my_folder_id => '!=' => $exclude);
|
||||
}
|
||||
my $sth = $bf->select($cond);
|
||||
my $total = $bf->hits;
|
||||
my $folder = [];
|
||||
if ($sth->rows) {
|
||||
my $i = 0;
|
||||
while (my $row = $sth->fetchrow_hashref) {
|
||||
$row->{num_links} = _count_links($username, $row->{my_folder_id});
|
||||
$row->{my_folder_name} = $IN->html_escape($row->{my_folder_name});
|
||||
$row->{my_folder_description} = $IN->html_escape($row->{my_folder_description});
|
||||
push @$folder, $row;
|
||||
$i++;
|
||||
}
|
||||
my ($toolbar, %paging);
|
||||
if (!$dropdown and $total > $limit) {
|
||||
my $url = _bookmark_url();
|
||||
$toolbar = $DB->html($bf, $IN)->toolbar($nh, $limit, $total, $url);
|
||||
%paging = (
|
||||
url => $url,
|
||||
num_hits => $total,
|
||||
max_hits => $limit,
|
||||
current_page => $nh
|
||||
);
|
||||
}
|
||||
|
||||
return { Folders => $folder, folder_count => $total, toolbar => $toolbar, paging => \%paging };
|
||||
}
|
||||
return { Folders => '', folder_count => 0 };
|
||||
}
|
||||
|
||||
sub _count_links {
|
||||
# --------------------------------------------------------
|
||||
# Return the number of links in folder
|
||||
#
|
||||
my ($username, $folderid) = @_;
|
||||
return $DB->table('Bookmark_Links', 'Links')->count({ my_folder_id_fk => $folderid }, VIEWABLE);
|
||||
}
|
||||
|
||||
sub _total_pub_links {
|
||||
# --------------------------------------------------------
|
||||
# Return the number of public links for a user
|
||||
#
|
||||
my $username = shift;
|
||||
my $links_db = $DB->table('Bookmark_Folders', 'Bookmark_Links', 'Links');
|
||||
return $links_db->count({ my_folder_public => 1, my_user_username_fk => $username }, VIEWABLE);
|
||||
}
|
||||
|
||||
sub _links_list {
|
||||
# --------------------------------------------------------
|
||||
# Generate links list
|
||||
#
|
||||
my ($username, $folderid) = @_;
|
||||
my $db = $DB->table('Bookmark_Links', 'Links');
|
||||
my $html = $DB->html($db, $IN);
|
||||
return unless $username;
|
||||
|
||||
if ($username eq $USER->{Username}) {
|
||||
$db->select_options("ORDER BY $USER->{SortField} $USER->{SortOrd}");
|
||||
}
|
||||
else {
|
||||
$db->select_options("ORDER BY $CFG->{bookmark_links_sort} $CFG->{bookmark_links_sort_order}");
|
||||
}
|
||||
|
||||
my ($limit, $offset, $nh) = Links::limit_offset(
|
||||
($USER->{Username} eq $username and $USER->{PerPage})
|
||||
? $USER->{PerPage}
|
||||
: $CFG->{bookmark_links_per_page}
|
||||
);
|
||||
|
||||
$db->select_options("LIMIT $limit OFFSET $offset");
|
||||
|
||||
my $sth = $db->select({ my_user_username_fk => $username, my_folder_id_fk => $folderid }, VIEWABLE);
|
||||
my $link_count = $db->hits;
|
||||
my $links = [];
|
||||
my $folder = $DB->table('Bookmark_Folders')->get($folderid);
|
||||
|
||||
# Generate a toolbar if requested.
|
||||
my ($toolbar, %paging);
|
||||
if ($link_count > $limit) {
|
||||
my $url = _bookmark_url();
|
||||
$toolbar = $html->toolbar($nh, $limit, $link_count, $url);
|
||||
%paging = (
|
||||
url => $url,
|
||||
num_hits => $link_count,
|
||||
max_hits => $limit,
|
||||
current_page => $nh
|
||||
);
|
||||
}
|
||||
|
||||
my @link_results_loop;
|
||||
my ($link_results, %link_output);
|
||||
if ($link_count) {
|
||||
my $results = $sth->fetchall_hashref;
|
||||
my $links_tb = $DB->table('Links');
|
||||
for (@$results) {
|
||||
$_->{my_comment} = $IN->html_escape($_->{my_comment});
|
||||
$links_tb->add_reviews($_);
|
||||
}
|
||||
if ($USER->{Grouping}) {
|
||||
my @ids = map { $_->{ID} } @$results;
|
||||
my $catlink = $DB->table('CatLinks', 'Category');
|
||||
my %names = $catlink->select('LinkID', 'Full_Name', { LinkID => \@ids })->fetchall_list;
|
||||
foreach my $link (@$results) {
|
||||
push @{$link_output{$names{$link->{ID}}}}, $link;
|
||||
}
|
||||
}
|
||||
else {
|
||||
push @{$link_output{none}}, @$results;
|
||||
}
|
||||
}
|
||||
$folder ||= {};
|
||||
$folder->{my_folder_name} = $IN->html_escape($folder->{my_folder_name});
|
||||
$folder->{my_folder_description} = $IN->html_escape($folder->{my_folder_description});
|
||||
|
||||
if ($link_count) {
|
||||
my $i = 0;
|
||||
if ($USER->{Grouping}) {
|
||||
foreach my $cat (sort keys %link_output) {
|
||||
$link_output{$cat}->[0]->{title_linked} = sub { Links::Build::build('title_linked', { name => $cat, complete => 1, home => 0 }) };
|
||||
$link_output{$cat}->[0]->{title_loop} = Links::Build::build('title', $cat);
|
||||
push @link_results_loop, @{$link_output{$cat}};
|
||||
}
|
||||
}
|
||||
else {
|
||||
push @link_results_loop, @{$link_output{none}};
|
||||
}
|
||||
|
||||
return { Bookmarks => \@link_results_loop, link_count => $link_count, %$folder, toolbar => $toolbar, paging => \%paging };
|
||||
}
|
||||
return { Bookmarks => "", link_count => 0, %$folder, toolbar => $toolbar };
|
||||
}
|
||||
|
||||
sub folder_add {
|
||||
# --------------------------------------------------------
|
||||
# Add Folder
|
||||
#
|
||||
my $args = $IN->get_hash();
|
||||
my $bf = $DB->table('Bookmark_Folders');
|
||||
|
||||
my $mtl = Links::Build::build('title', Links::language('LINKS_BOOKMARK'), "$CFG->{db_cgi_url}/bookmark.cgi");
|
||||
if ($IN->param('add')) {
|
||||
unless ($args->{my_folder_name}) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('bookmark_folder_add', { error => Links::language('BOOKMARK_BAD_FOLDER'), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
if ($bf->count({ my_folder_user_username_fk => $USER->{Username} }) >= $CFG->{bookmark_folder_limit}) {
|
||||
return show_folders('', Links::language('BOOKMARK_FOLDER_LIMIT'));
|
||||
}
|
||||
if ($bf->count({ my_folder_name => $args->{my_folder_name}, my_folder_user_username_fk => $USER->{Username} })) {
|
||||
$args->{error} = Links::language('BOOKMARK_FOLDER_DUPLICATE', $args->{my_folder_name});
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('bookmark_folder_add', { %$args, main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
$args->{my_folder_user_username_fk} = $USER->{Username};
|
||||
$args->{my_folder_public} = $args->{my_folder_public} ? 1 : 0;
|
||||
$args->{my_folder_default} = $args->{my_folder_default} ? 1 : 0;
|
||||
if ($args->{my_folder_default}) {
|
||||
$bf->update({ my_folder_default => 0 }, { my_folder_user_username_fk => $USER->{Username}, my_folder_default => 1 });
|
||||
}
|
||||
my $fid = $bf->add($args);
|
||||
if ($fid) {
|
||||
return show_folders();
|
||||
}
|
||||
else {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('bookmark_folder_add', { error => Links::language('BOOKMARK_BAD_FOLDER', $GT::SQL::error), main_title_loop => $mtl });
|
||||
}
|
||||
}
|
||||
else {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('bookmark_folder_add', { main_title_loop => $mtl });
|
||||
}
|
||||
}
|
||||
|
||||
sub folder_edit {
|
||||
# --------------------------------------------------------
|
||||
# Edit Folder
|
||||
#
|
||||
my $args = $IN->get_hash();
|
||||
my $bf = $DB->table('Bookmark_Folders');
|
||||
my $folderid = $args->{my_folder_id};
|
||||
my $folder = $bf->get($folderid);
|
||||
my $mtl = Links::Build::build('title', Links::language('LINKS_BOOKMARK'), "$CFG->{db_cgi_url}/bookmark.cgi");
|
||||
if (!$folder or $folder->{my_folder_user_username_fk} ne $USER->{Username}) {
|
||||
return show_folders('', Links::language('BOOKMARK_BAD_FOLDER_ID', $folderid));
|
||||
}
|
||||
if ($IN->param('modify')) {
|
||||
unless ($args->{my_folder_name}) {
|
||||
$args->{error} = Links::language('BOOKMARK_BAD_FOLDER');
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('bookmark_folder_edit', { %$args, main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
if ($args->{my_folder_name} ne $folder->{my_folder_name} and $bf->count({ my_folder_name => $args->{my_folder_name}, my_folder_user_username_fk => $USER->{Username} })) {
|
||||
$args->{error} = Links::language('BOOKMARK_FOLDER_DUPLICATE', $args->{my_folder_name});
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('bookmark_folder_edit', { %$args, main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
$args->{my_folder_user_username_fk} = $USER->{Username};
|
||||
$args->{my_folder_public} = $args->{my_folder_public} ? 1 : 0;
|
||||
$args->{my_folder_default} = $args->{my_folder_default} ? 1 : 0;
|
||||
if ($args->{my_folder_default}) {
|
||||
$bf->update({ my_folder_default => 0 }, { my_folder_user_username_fk => $USER->{Username}, my_folder_default => 1 });
|
||||
}
|
||||
my $rec = $bf->modify($args);
|
||||
if ($rec) {
|
||||
$IN->param('my_folder_name', '');
|
||||
show_folders(Links::language('BOOKMARK_FOLDER_MODIFIED', $folderid));
|
||||
}
|
||||
else {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('bookmark_folder_edit', { error => Links::language('BOOKMARK_BAD_FOLDER', $GT::SQL::error), main_title_loop => $mtl });
|
||||
}
|
||||
}
|
||||
else {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('bookmark_folder_edit', { %$folder, main_title_loop => $mtl });
|
||||
}
|
||||
}
|
||||
|
||||
sub folder_remove {
|
||||
# -------------------------------------------------------------------
|
||||
# Remove folder and the links in it
|
||||
#
|
||||
my $folderid = $IN->param('my_folder_id');
|
||||
my $bf = $DB->table('Bookmark_Folders');
|
||||
my $folder = $bf->get($folderid);
|
||||
my $error;
|
||||
if ($folder->{my_folder_default}) {
|
||||
$error = Links::language('BOOKMARK_FOLDER_DEFAULT');
|
||||
}
|
||||
elsif ($bf->count({ my_folder_id => $folderid, my_folder_user_username_fk => $USER->{Username} })) {
|
||||
my $rc = $bf->delete({ my_folder_id => $folderid, my_folder_user_username_fk => $USER->{Username} });
|
||||
unless ($rc) {
|
||||
$error = $GT::SQL::error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$error = Links::language('BOOKMARK_FOLDER_NOTEXISTS', $folderid);
|
||||
}
|
||||
if ($error) {
|
||||
return show_folders('', $error);
|
||||
}
|
||||
else {
|
||||
return show_folders(Links::language('BOOKMARK_FOLDER_REMOVED', $folder->{my_folder_name}));
|
||||
}
|
||||
}
|
||||
|
||||
sub edit_bookmark {
|
||||
# -------------------------------------------------------------------
|
||||
# edit Bookmark Comments
|
||||
#
|
||||
my $args = $IN->get_hash();
|
||||
my $id = $args->{my_id} || shift;
|
||||
my $bl = $DB->table('Bookmark_Links');
|
||||
my $mtl = Links::Build::build('title', Links::language('LINKS_BOOKMARK'), "$CFG->{db_cgi_url}/bookmark.cgi");
|
||||
|
||||
# <=3.2 template backwards compatibility
|
||||
# Previously the link id was passed to edit, but the ability to bookmark a link
|
||||
# multiple times was added in 3.2.
|
||||
my $lid = $args->{id} || shift;
|
||||
if (!$id and $lid) {
|
||||
$bl->select_options('ORDER BY my_id');
|
||||
$id = $bl->select('my_id', { my_link_id_fk => $lid, my_user_username_fk => $USER->{Username} })->fetchrow;
|
||||
}
|
||||
|
||||
if (not $bl->count({ my_id => $id, my_user_username_fk => $USER->{Username} })) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('bookmark_link_edit', { error => Links::language('BOOKMARK_LINK_NOTEXISTS', $id), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
|
||||
my $link = $DB->table('Bookmark_Links', 'Links')->select({ my_id => $id, my_user_username_fk => $USER->{Username} })->fetchrow_hashref;
|
||||
my $folders = _folder_list($USER->{Username}, undef, 1);
|
||||
|
||||
if ($args->{edit}) {
|
||||
my %set = (my_comment => $args->{my_comment});
|
||||
if ($link->{my_folder_id_fk} != $args->{my_folder_id_fk} and $bl->count({ my_link_id_fk => $link->{my_link_id_fk}, my_folder_id_fk => $args->{my_folder_id_fk}, my_user_username_fk => $USER->{Username} })) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('bookmark_link_edit', { error => Links::language('BOOKMARK_LINK_EXISTS', $id), %$folders, %$link, main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
if ($DB->table('Bookmark_Folders')->count({ my_folder_id => $args->{my_folder_id_fk}, my_folder_user_username_fk => $USER->{Username} })) {
|
||||
$set{my_folder_id_fk} = $args->{my_folder_id_fk};
|
||||
}
|
||||
my $rec = $bl->update(\%set, { my_id => $id, my_user_username_fk => $USER->{Username} });
|
||||
if ($rec) {
|
||||
$IN->param(my_folder_id_fk => $args->{my_folder_id_fk});
|
||||
$IN->param(id => '');
|
||||
$IN->param(my_comment => '');
|
||||
show_links(Links::language('BOOKMARK_COMMENTS_EDITED'));
|
||||
}
|
||||
else {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('bookmark_link_edit', { error => $GT::SQL::error, main_title_loop => $mtl });
|
||||
}
|
||||
}
|
||||
else {
|
||||
print $IN->header();
|
||||
$link->{my_comment} = $IN->html_escape($link->{my_comment});
|
||||
print Links::SiteHTML::display('bookmark_link_edit', { %$folders, %$link, main_title_loop => $mtl });
|
||||
}
|
||||
}
|
||||
|
||||
sub edit_preferences {
|
||||
# -------------------------------------------------------------------
|
||||
# edit Bookmark Preferences
|
||||
#
|
||||
my $args = $IN->get_hash();
|
||||
my $username = $USER->{Username} || shift;
|
||||
my $mtl = Links::Build::build('title', Links::language('LINKS_BOOKMARK'), "$CFG->{db_cgi_url}/bookmark.cgi");
|
||||
|
||||
if ($args->{modify}) {
|
||||
if ($args->{PerPage} <= 0) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('bookmark_preferences', { error => Links::language('BOOKMARK_PREF_INVALIDPERPAGE'), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
my $rec = $DB->table('Users')->update({
|
||||
SortField => $args->{SortField},
|
||||
SortOrd => $args->{SortOrd},
|
||||
PerPage => $args->{PerPage},
|
||||
Grouping => $args->{Grouping}
|
||||
}, { Username => $username });
|
||||
if ($rec) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('bookmark_preferences', { %$args, message => Links::language('BOOKMARK_PREFERENCES'), main_title_loop => $mtl });
|
||||
}
|
||||
else {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('bookmark_preferences', { error => $GT::SQL::error, main_title_loop => $mtl });
|
||||
}
|
||||
}
|
||||
else {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('bookmark_preferences', { main_title_loop => $mtl });
|
||||
}
|
||||
}
|
||||
|
||||
sub link_add {
|
||||
# -------------------------------------------------------------------
|
||||
# add a link to a folder
|
||||
#
|
||||
my $args = $IN->get_hash();
|
||||
my $linkid = $args->{ID} || $args->{my_link_id_fk};
|
||||
my $username = $USER->{Username} || shift;
|
||||
my $bl = $DB->table('Bookmark_Links');
|
||||
my $bf = $DB->table('Bookmark_Folders');
|
||||
my $mtl = Links::Build::build('title', Links::language('LINKS_BOOKMARK'), "$CFG->{db_cgi_url}/bookmark.cgi");
|
||||
|
||||
# check if they've reached the limit of number of bookmarks
|
||||
if ($bl->count({ my_user_username_fk => $username }) >= $CFG->{bookmark_links_limit}) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('BOOKMARK_LINK_LIMIT'), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
# check to make sure the link exists
|
||||
my $link = $DB->table('Links')->get($linkid);
|
||||
unless ($link) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('bookmark_link_add', { error => Links::language('RATE_INVALIDID', $linkid), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
# check if the user has any folder, if not, create one default for them.
|
||||
unless ($DB->table('Bookmark_Folders')->count({ my_folder_user_username_fk => $username })) {
|
||||
my $rc = $bf->insert({
|
||||
my_folder_user_username_fk => $username,
|
||||
my_folder_name => $CFG->{bookmark_folder_default_name},
|
||||
my_folder_default => 1,
|
||||
my_folder_public => 0,
|
||||
});
|
||||
unless ($rc) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => $GT::SQL::error, main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
else {
|
||||
$args->{my_folder_id_fk} = $rc->insert_id;
|
||||
}
|
||||
}
|
||||
|
||||
my $folders = _folder_list($username, undef, 1);
|
||||
if ($args->{add}) {
|
||||
my $error;
|
||||
if (!$bf->count({ my_folder_id => $args->{my_folder_id_fk}, my_folder_user_username_fk => $username })) {
|
||||
$error = Links::language('BOOKMARK_FOLDER_INVALID');
|
||||
}
|
||||
# Don't allow duplicate links in a folder
|
||||
elsif ($bl->count({ my_link_id_fk => $linkid, my_folder_id_fk => $args->{my_folder_id_fk}, my_user_username_fk => $username })) {
|
||||
$error = Links::language('BOOKMARK_LINK_EXISTS', $linkid);
|
||||
}
|
||||
|
||||
if ($error) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('bookmark_link_add', { error => $error, %$folders, %$link, main_title_loop => $mtl });
|
||||
}
|
||||
else {
|
||||
$args->{my_user_username_fk} = $username;
|
||||
my $rec = $bl->add($args);
|
||||
if ($rec) {
|
||||
folder_view(Links::language('BOOKMARK_LINK_ADDED'));
|
||||
}
|
||||
else {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => $GT::SQL::error, main_title_loop => $mtl });
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('bookmark_link_add', { %$folders, %$link, main_title_loop => $mtl });
|
||||
}
|
||||
}
|
||||
|
||||
sub links_manage {
|
||||
# -------------------------------------------------------------------
|
||||
# manage links, such as move or delete
|
||||
#
|
||||
my $folderid = $IN->param('move_folderid');
|
||||
my $old_folderid = $IN->param('my_folder_id');
|
||||
my $username = $USER->{Username};
|
||||
my $db = $DB->table('Bookmark_Links');
|
||||
my ($i, $error, $message) = 0;
|
||||
my @ids = $IN->param('my_id');
|
||||
my @lids = $IN->param('m-id');
|
||||
# <=3.2 template backwards compatibility
|
||||
if (!@ids and @lids) {
|
||||
$db->select_options('GROUP BY my_link_id_fk');
|
||||
@ids = $db->select('my_id', { my_link_id_fk => \@lids, my_folder_id_fk => $old_folderid, my_user_username_fk => $username })->fetchall_list;
|
||||
}
|
||||
if ($IN->param('move')) {
|
||||
if (not $DB->table('Bookmark_Folders')->count({ my_folder_id => $folderid, my_folder_user_username_fk => $username })) {
|
||||
$error .= Links::language('BOOKMARK_FOLDER_NO_MOVE', $folderid);
|
||||
}
|
||||
else {
|
||||
for my $id (@ids) {
|
||||
my $link = $db->select({ my_id => $id, my_user_username_fk => $username })->fetchrow_hashref;
|
||||
if (!$link or $db->count({ my_link_id_fk => $link->{my_link_id_fk}, my_folder_id_fk => $folderid, my_user_username_fk => $username })) {
|
||||
$error .= Links::language('BOOKMARK_LINK_EXISTS', $id);
|
||||
next;
|
||||
}
|
||||
my $rc = $db->update({ my_folder_id_fk => $folderid }, { my_id => $id, my_user_username_fk => $username });
|
||||
unless ($rc) {
|
||||
$error .= $GT::SQL::error;
|
||||
}
|
||||
else {
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
my $folder = $DB->table('Bookmark_Folders')->get($folderid);
|
||||
$message = Links::language('BOOKMARK_LINK_MOVED', $i, $folder->{my_folder_name});
|
||||
}
|
||||
}
|
||||
elsif ($IN->param('remove')) {
|
||||
foreach my $id (@ids) {
|
||||
if ($db->count({ my_id => $id, my_folder_id_fk => $old_folderid, my_user_username_fk => $username })) {
|
||||
my $rc = $db->delete({ my_id => $id, my_folder_id_fk => $old_folderid, my_user_username_fk => $username });
|
||||
unless ($rc) {
|
||||
$error = $GT::SQL::error;
|
||||
}
|
||||
else {
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$error .= Links::language('BOOKMARK_LINK_NOTEXISTS', $id);
|
||||
}
|
||||
}
|
||||
$message = Links::language('BOOKMARK_LINK_REMOVED', $i);
|
||||
}
|
||||
else {
|
||||
$error .= Links::language('BOOKMARK_NO_ACTION');
|
||||
}
|
||||
|
||||
if ($error) {
|
||||
return show_links("", $error);
|
||||
}
|
||||
else {
|
||||
return show_links($message);
|
||||
}
|
||||
}
|
||||
|
||||
sub _bookmark_url {
|
||||
# -------------------------------------------------------------------
|
||||
# Generate a bookmark.cgi url (mainly used by paging).
|
||||
#
|
||||
my $url = $CFG->{db_cgi_url} . "/bookmark.cgi";
|
||||
my $in_hash = $IN->get_hash(0);
|
||||
my @url_hidden;
|
||||
foreach (@{$CFG->{dynamic_preserve}}) {
|
||||
next unless defined $in_hash->{$_} and $in_hash->{$_} =~ /\S/;
|
||||
push @url_hidden, $IN->escape($_) . "=" . $IN->escape($in_hash->{$_});
|
||||
}
|
||||
if (@url_hidden) {
|
||||
$url .= "?" . join ';', @url_hidden;
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
1;
|
||||
2369
site/slowtwitch.com/cgi-bin/articles/admin/Links/Browser.pm
Normal file
2369
site/slowtwitch.com/cgi-bin/articles/admin/Links/Browser.pm
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,499 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Controller.pm,v 1.9 2009/07/09 23:13:41 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
|
||||
package Links::Browser::Controller;
|
||||
# ==================================================================
|
||||
use strict;
|
||||
use vars qw/@ISA $AUTOLOAD $ATTRIBS/;
|
||||
use GT::Base;
|
||||
use Links qw/$CFG $IN $DB/;
|
||||
use Links::Browser;
|
||||
|
||||
@ISA = qw/GT::Base/;
|
||||
$ATTRIBS = {
|
||||
user_base_node => [],
|
||||
load_tree => 0,
|
||||
perms => {},
|
||||
admin => 0,
|
||||
user => {},
|
||||
admin_templates => 0
|
||||
};
|
||||
|
||||
sub can_run {
|
||||
# -------------------------------------------------------------------
|
||||
# Determines whether or not the user can run the requested function.
|
||||
#
|
||||
my $self = shift;
|
||||
my $action = $IN->param ("action") || return "main_panel_init";
|
||||
|
||||
if (exists $Links::Browser::COMPILE{$action}) {
|
||||
if ($self->{admin}) {
|
||||
return $action;
|
||||
}
|
||||
if ($self->$action()) { return $action }
|
||||
else { return }
|
||||
}
|
||||
else { return }
|
||||
return $action;
|
||||
}
|
||||
|
||||
# Everyone can load the browser.
|
||||
sub main_panel_init { return 1 }
|
||||
sub tree_panel_init { return 1 }
|
||||
sub info_panel_init { return 1 }
|
||||
sub code_panel_init { return 1 }
|
||||
sub code_panel_reload_empty { return 1 }
|
||||
sub code_panel_reload_full { return 1 }
|
||||
|
||||
sub category_click {
|
||||
# -------------------------------------------------------------------
|
||||
# Determine whether the user can view a category.
|
||||
#
|
||||
my $self = shift;
|
||||
return $self->is_in_subtree ($IN->param ('category_id'));
|
||||
}
|
||||
|
||||
sub code_panel_category_expand {
|
||||
# -------------------------------------------------------------------
|
||||
# Expand a section of the tree.
|
||||
#
|
||||
my $self = shift;
|
||||
return $self->is_in_subtree ($IN->param ('category_id'));
|
||||
}
|
||||
|
||||
sub category_add_form {
|
||||
# -------------------------------------------------------------------
|
||||
# Display add form.
|
||||
#
|
||||
my $self = shift;
|
||||
my $base = $self->is_in_subtree ($IN->param ('category_id')) or return;
|
||||
return ($self->{perms}->{$base}->{CanAddCat} eq 'Yes') ? 1 : 0;
|
||||
}
|
||||
|
||||
sub category_add {
|
||||
# -------------------------------------------------------------------
|
||||
# Determines whether you can actually add a category.
|
||||
#
|
||||
my $self = shift;
|
||||
my $base = $self->is_in_subtree ($IN->param ('FatherID')) or return;
|
||||
return ($self->{perms}->{$base}->{CanAddCat} eq 'Yes') ? 1 : 0;
|
||||
}
|
||||
|
||||
sub category_del_form {
|
||||
# -------------------------------------------------------------------
|
||||
# Display category delete form.
|
||||
#
|
||||
my $self = shift;
|
||||
my $base = $self->is_in_subtree ($IN->param ('category_id')) or return;
|
||||
return ($self->{perms}->{$base}->{CanDelCat} eq 'Yes') ? 1 : 0;
|
||||
}
|
||||
sub category_del { return shift->category_del_form (@_); }
|
||||
|
||||
sub category_modify_form {
|
||||
# -------------------------------------------------------------------
|
||||
# Display category modify form.
|
||||
#
|
||||
my $self = shift;
|
||||
my $base = $self->is_in_subtree ($IN->param ('category_id')) or return;
|
||||
return ($self->{perms}->{$base}->{CanModCat} eq 'Yes') ? 1 : 0;
|
||||
}
|
||||
|
||||
sub category_modify {
|
||||
# -------------------------------------------------------------------
|
||||
# Determines whether you can actually modify the given category.
|
||||
#
|
||||
my $self = shift;
|
||||
my $base = $self->is_in_subtree ($IN->param ('ID')) or return;
|
||||
return ($self->{perms}->{$base}->{CanModCat} eq 'Yes') ? 1 : 0;
|
||||
}
|
||||
|
||||
sub category_move_form {
|
||||
# -------------------------------------------------------------------
|
||||
# Display category move form.
|
||||
#
|
||||
my $self = shift;
|
||||
my $base = $self->is_in_subtree ($IN->param ('category_id')) or return;
|
||||
return ($self->{perms}->{$base}->{CanMoveCat} eq 'Yes') ? 1 : 0;
|
||||
}
|
||||
|
||||
sub category_move {
|
||||
# -------------------------------------------------------------------
|
||||
# Display category move form.
|
||||
#
|
||||
my $self = shift;
|
||||
my $base1 = $self->is_in_subtree ($IN->param ('category_from')) or return;
|
||||
my $base2 = $self->is_in_subtree ($IN->param ('category_to')) or return;
|
||||
$self->{perms}->{$base1}->{CanMoveCat} eq 'Yes' or return;
|
||||
$self->{perms}->{$base2}->{CanMoveCat} eq 'Yes' or return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub category_editors_form {
|
||||
# -------------------------------------------------------------------
|
||||
# Display category editors form and process edits.
|
||||
#
|
||||
my $self = shift;
|
||||
return if (defined $self->{perms}->{CanAddEdit} and $self->{perms}->{CanAddEdit} eq 'No');
|
||||
|
||||
my $base = $self->is_in_subtree ($IN->param ('category_id')) or return;
|
||||
$self->{perms}->{$base}->{CanAddEdit} eq 'Yes' or return;
|
||||
|
||||
foreach my $key ($IN->param('to_delete')) {
|
||||
my ($name, $id) = split /\|/, $key;
|
||||
$base = $self->is_in_subtree ($id) or return;
|
||||
$self->{perms}->{$base}->{CanAddEdit} eq 'Yes' or return;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub category_related_form {
|
||||
# -------------------------------------------------------------------
|
||||
# Display related categories form and process relations.
|
||||
#
|
||||
my $self = shift;
|
||||
return if (defined $self->{perms}->{CanAddRel} and $self->{perms}->{CanAddRel} eq 'No');
|
||||
|
||||
my $base = $self->is_in_subtree ($IN->param ('category_id')) or return;
|
||||
$self->{perms}->{$base}->{CanAddRel} eq 'Yes' or return;
|
||||
|
||||
foreach my $id ($IN->param('to_delete')) {
|
||||
$base = $self->is_in_subtree ($id) or return;
|
||||
$self->{perms}->{$base}->{CanAddRel} eq 'Yes' or return;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub link_user_list {
|
||||
# -------------------------------------------------------------------
|
||||
# Display list of links this user owns.
|
||||
#
|
||||
my $self = shift;
|
||||
my $base = $self->is_in_subtree ($IN->param('category_id')) or return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub link_add_form {
|
||||
# -------------------------------------------------------------------
|
||||
# Display add link form.
|
||||
my $self = shift;
|
||||
my $base = $self->is_in_subtree ($IN->param ('category_id')) or return;
|
||||
return ($self->{perms}->{$base}->{CanAddLink} eq 'Yes') ? 1 : 0;
|
||||
}
|
||||
|
||||
sub link_add {
|
||||
# -------------------------------------------------------------------
|
||||
# Display add link form.
|
||||
my $self = shift;
|
||||
my $base = $self->is_in_subtree ($IN->param ('CatLinks.CategoryID')) or return;
|
||||
return ($self->{perms}->{$base}->{CanAddLink} eq 'Yes') ? 1 : 0;
|
||||
}
|
||||
|
||||
sub link_modify_form {
|
||||
# -------------------------------------------------------------------
|
||||
# Display modify link form.
|
||||
#
|
||||
my $self = shift;
|
||||
|
||||
my $catlinks = $DB->table('CatLinks');
|
||||
my $q = $catlinks->select({ LinkID => $IN->param("link_id") || $IN->param('ID') });
|
||||
my $allowed = 0;
|
||||
while (my $h = $q->fetchrow_hashref) {
|
||||
my $base = $self->is_in_subtree ($h->{CategoryID}) or next;
|
||||
if ($self->{perms}->{$base}->{CanModLink} eq 'Yes') {
|
||||
$allowed = 1;
|
||||
last;
|
||||
}
|
||||
}
|
||||
return $allowed;
|
||||
}
|
||||
sub link_modify {
|
||||
# -------------------------------------------------------------------
|
||||
# Display modify link form.
|
||||
#
|
||||
my $self = shift;
|
||||
$self->link_modify_form(@_);
|
||||
}
|
||||
|
||||
sub link_del_form {
|
||||
# -------------------------------------------------------------------
|
||||
# Display delete link form.
|
||||
#
|
||||
my $self = shift;
|
||||
return if (defined $self->{perms}->{CanDelLink} and $self->{perms}->{CanDelLink} eq 'No');
|
||||
my $catlinks = $DB->table (qw /CatLinks/);
|
||||
my $q = $catlinks->select ( { LinkID => scalar $IN->param ("link_id") } );
|
||||
my $allowed = 0;
|
||||
while (my $h = $q->fetchrow_hashref) {
|
||||
my $base = $self->is_in_subtree ($h->{CategoryID}) or next;
|
||||
if ($self->{perms}->{$base}->{CanDelLink} eq 'Yes') {
|
||||
$allowed = 1;
|
||||
last;
|
||||
}
|
||||
}
|
||||
return $allowed;
|
||||
}
|
||||
sub link_del { shift->link_del_form (@_); }
|
||||
|
||||
sub link_move_form {
|
||||
# -------------------------------------------------------------------
|
||||
# Display form to move link.
|
||||
#
|
||||
my $self = shift;
|
||||
my $base = $self->is_in_subtree ($IN->param ('category_id')) or return;
|
||||
return ($self->{perms}->{$base}->{CanMoveLink} eq 'Yes') ? 1 : 0;
|
||||
}
|
||||
|
||||
sub link_move {
|
||||
# -------------------------------------------------------------------
|
||||
# Checks whether the link can be moved into the requested category.
|
||||
#
|
||||
my $self = shift;
|
||||
my $old_category_id = $IN->param ("old_category_id");
|
||||
my $new_category_id = $IN->param ("new_category_id");
|
||||
my $base1 = $self->is_in_subtree ($old_category_id) or return;
|
||||
my $base2 = $self->is_in_subtree ($new_category_id) or return;
|
||||
$self->{perms}->{$base1}->{CanMoveLink} eq 'Yes' or return;
|
||||
$self->{perms}->{$base2}->{CanMoveLink} eq 'Yes' or return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub link_copy_form {
|
||||
# -------------------------------------------------------------------
|
||||
# Display form to copy a link.
|
||||
#
|
||||
my $self = shift;
|
||||
my $base = $self->is_in_subtree ($IN->param ('category_id')) or return;
|
||||
return ($self->{perms}->{$base}->{CanCopyLink} eq 'Yes') ? 1 : 0;
|
||||
}
|
||||
|
||||
sub link_copy {
|
||||
# -------------------------------------------------------------------
|
||||
# Checks whether a link can be moved into requested category.
|
||||
#
|
||||
my $self = shift;
|
||||
my $old_category_id = $IN->param ("old_category_id");
|
||||
my $new_category_id = $IN->param ("new_category_id");
|
||||
my $base1 = $self->is_in_subtree ($old_category_id) or return;
|
||||
my $base2 = $self->is_in_subtree ($new_category_id) or return;
|
||||
$self->{perms}->{$base1}->{CanCopyLink} eq 'Yes' or return;
|
||||
$self->{perms}->{$base2}->{CanCopyLink} eq 'Yes' or return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub link_validate_list {
|
||||
# -------------------------------------------------------------------
|
||||
# Checks whether a user can display links awaiting validation.
|
||||
#
|
||||
my $self = shift;
|
||||
my $base = $self->is_in_subtree ($IN->param ('category_id')) or return;
|
||||
return ($self->{perms}->{$base}->{CanValLink} eq 'Yes') ? 1 : 0;
|
||||
}
|
||||
|
||||
sub link_validate_detailed {
|
||||
# -------------------------------------------------------------------
|
||||
# Checks whether a user can display links awaiting validation.
|
||||
#
|
||||
my $self = shift;
|
||||
my $base = $self->is_in_subtree ($IN->param ('category_id')) or return;
|
||||
|
||||
# Let's parse out the form, and group our links together.
|
||||
my $args = $IN->get_hash();
|
||||
my $catlinks_db = $DB->table( 'CatLinks' );
|
||||
my ( @denied, @allowed );
|
||||
while (my ($key, $param) = each %$args) {
|
||||
if ($key =~ /^validate-(\d+)/) {
|
||||
my $id = $1;
|
||||
my $q = $catlinks_db->select ( { LinkID => $id } );
|
||||
my $base;
|
||||
while (my $h = $q->fetchrow_hashref ) {
|
||||
if ( $base = $self->is_in_subtree ($h->{CategoryID})
|
||||
and $self->{perms}->{$base}->{CanValLink} eq 'Yes' ) {
|
||||
push @allowed, $id;
|
||||
next;
|
||||
}
|
||||
push @denied, $id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Remove action verbs for any listings the user is not allowed to validate
|
||||
for my $id ( @denied ) {
|
||||
$IN->param( "validate-$id", undef );
|
||||
}
|
||||
|
||||
return ($self->{perms}->{$base}->{CanValLink} eq 'Yes') ? 1 : 0;
|
||||
}
|
||||
|
||||
sub link_validate_changes_list {
|
||||
# -------------------------------------------------------------------
|
||||
# Checks whether a user can display links awaiting validation.
|
||||
#
|
||||
my $self = shift;
|
||||
my $base = $self->is_in_subtree ($IN->param ('category_id')) or return;
|
||||
return ($self->{perms}->{$base}->{CanValLink} eq 'Yes') ? 1 : 0;
|
||||
}
|
||||
|
||||
sub link_validate_form {
|
||||
# -------------------------------------------------------------------
|
||||
# Checks whether a user can display links awaiting validation.
|
||||
#
|
||||
my $self = shift;
|
||||
my $base = $self->is_in_subtree ($IN->param ('category_id')) or return;
|
||||
return ($self->{perms}->{$base}->{CanValLink} eq 'Yes') ? 1 : 0;
|
||||
}
|
||||
|
||||
sub link_validate {
|
||||
# -------------------------------------------------------------------
|
||||
# Checks whether user can actually validate link.
|
||||
#
|
||||
my $self = shift;
|
||||
my $base = $self->is_in_subtree ($IN->param ('category_id')) or return;
|
||||
return ($self->{perms}->{$base}->{CanValLink} eq 'Yes') ? 1 : 0;
|
||||
}
|
||||
|
||||
sub review_list {
|
||||
# -------------------------------------------------------------------
|
||||
# Checks whether a user can display reviews awaiting validation.
|
||||
#
|
||||
my $self = shift;
|
||||
my $base = $self->is_in_subtree ($IN->param ('category_id')) or return;
|
||||
return ($self->{perms}->{$base}->{CanModReview} eq 'Yes') ? 1 : 0;
|
||||
}
|
||||
|
||||
sub review_del_form {
|
||||
# -------------------------------------------------------------------
|
||||
# Checks whether a user can delete reviews.
|
||||
#
|
||||
my $self = shift;
|
||||
my $base = $self->is_in_subtree ($IN->param ('category_id')) or return;
|
||||
return ($self->{perms}->{$base}->{CanModReview} eq 'Yes') ? 1 : 0;
|
||||
}
|
||||
|
||||
sub review_del {
|
||||
# -------------------------------------------------------------------
|
||||
# Checks whether a user can delete reviews.
|
||||
#
|
||||
my $self = shift;
|
||||
my $base = $self->is_in_subtree ($IN->param ('category_id')) or return;
|
||||
return ($self->{perms}->{$base}->{CanModReview} eq 'Yes') ? 1 : 0;
|
||||
}
|
||||
|
||||
sub review_modify_form {
|
||||
# -------------------------------------------------------------------
|
||||
# Checks whether a user can display the review modify form.
|
||||
#
|
||||
my $self = shift;
|
||||
my $base = $self->is_in_subtree ($IN->param ('category_id')) or return;
|
||||
return ($self->{perms}->{$base}->{CanModReview} eq 'Yes') ? 1 : 0;
|
||||
}
|
||||
|
||||
sub review_modify {
|
||||
# -------------------------------------------------------------------
|
||||
# Checks whether user can actually validate reviews.
|
||||
#
|
||||
my $self = shift;
|
||||
my $base = $self->is_in_subtree ($IN->param ('category_id')) or return;
|
||||
return ($self->{perms}->{$base}->{CanModReview} eq 'Yes') ? 1 : 0;
|
||||
}
|
||||
|
||||
sub link_search_form {
|
||||
# -------------------------------------------------------------------
|
||||
# Display search link form.
|
||||
my $self = shift;
|
||||
if (!$IN->param('category_id')) {
|
||||
my @check_ids;
|
||||
if (ref $self->{ctrl}->user_base_node) {
|
||||
@check_ids = @{$self->{ctrl}->user_base_node};
|
||||
}
|
||||
else {
|
||||
$check_ids[0] = $self->{ctrl}->user_base_node;
|
||||
}
|
||||
$IN->param('category_id',$check_ids[0]);
|
||||
}
|
||||
|
||||
my $base = $self->is_in_subtree ($IN->param ('category_id')) or return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub link_search_results {
|
||||
# -------------------------------------------------------------------
|
||||
# Display search link form.
|
||||
my $self = shift;
|
||||
if ($IN->param('in_category')) {
|
||||
return if (!$self->is_in_subtree ($IN->param ('in_category')));
|
||||
}
|
||||
if (!$IN->param('category_id')) {
|
||||
my @check_ids;
|
||||
if (ref $self->user_base_node) {
|
||||
@check_ids = @{$self->user_base_node};
|
||||
}
|
||||
else {
|
||||
$check_ids[0] = $self->user_base_node;
|
||||
}
|
||||
$IN->param('category_id',$check_ids[0]);
|
||||
}
|
||||
|
||||
my $base = $self->is_in_subtree ($IN->param ('category_id')) or return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub is_in_subtree {
|
||||
# -------------------------------------------------------------------
|
||||
# Returns the category ID of the base node this user is in.
|
||||
#
|
||||
my $self = shift;
|
||||
my $base_r = $self->user_base_node();
|
||||
@$base_r || return 1; # Root can do anything, no base specified.
|
||||
my $node = shift or return; # No node specified!
|
||||
|
||||
my $category = $DB->table (qw /Category/);
|
||||
my $info_node = $category->get ( { ID => $node }, 'HASH', ['ID','Full_Name']);
|
||||
defined $info_node or return; # Invalid node requested.
|
||||
|
||||
# Get closest permissions first.
|
||||
$category->select_options ("ORDER BY Full_Name DESC");
|
||||
my $sth = $category->select ( ['ID', 'Full_Name'], { ID => $base_r });
|
||||
while (my ($id, $name) = $sth->fetchrow_array) {
|
||||
($info_node->{Full_Name} =~ m,^\Q$name\E(?:/|$),) and return $id;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub perms {
|
||||
# -------------------------------------------------------------------
|
||||
# Returns a list of permissions the user has for a requested category.
|
||||
#
|
||||
my ($self, $category_id) = @_;
|
||||
if ($self->{admin}) {
|
||||
return { CanAddCat => 'Yes', CanDelCat => 'Yes', CanModCat => 'Yes', CanMoveCat => 'Yes',
|
||||
CanAddLink => 'Yes', CanDelLink => 'Yes', CanModLink => 'Yes', CanMoveLink => 'Yes', CanCopyLink => 'Yes',
|
||||
CanValLink => 'Yes', CanModReview => 'Yes',
|
||||
CanAddRel => 'Yes', CanAddEdit => 'Yes' };
|
||||
}
|
||||
my $base = $self->is_in_subtree($category_id) or return {};
|
||||
if (exists $self->{perms}->{$base}) {
|
||||
return $self->{perms}->{$base};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
##
|
||||
# $obj->user_base_node;
|
||||
# ---------------------
|
||||
# Returns an array ref of categories the user can edit.
|
||||
##
|
||||
sub user_base_node { return shift->{user_base_node} || [] }
|
||||
|
||||
1;
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: JFunction.pm,v 1.16 2005/03/22 01:42:22 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::Browser::JFunction;
|
||||
# ==================================================================
|
||||
use strict;
|
||||
use Links qw/$CFG $IN $DB/;
|
||||
|
||||
##
|
||||
# Path to image urls.
|
||||
##
|
||||
sub node_expand_button_fake { return $CFG->{build_static_url} . "/browser/expandfake.gif"; }
|
||||
sub node_expand_button_plus { return $CFG->{build_static_url} . "/browser/expandplus.gif"; }
|
||||
sub node_expand_button_less { return $CFG->{build_static_url} . "/browser/expandless.gif"; }
|
||||
sub node_unselected_button { return $CFG->{build_static_url} . "/browser/unselected.gif"; }
|
||||
|
||||
|
||||
##
|
||||
# $obj->tree_loadnode;
|
||||
# --------------------
|
||||
# This function must return the URL that the Javascript
|
||||
# must point the user to when an unloaded node has been expanded.
|
||||
##
|
||||
sub tree_loadnode
|
||||
{
|
||||
my $cgix = new GT::CGI ($IN);
|
||||
$cgix->param ("action", "code_panel_category_expand");
|
||||
return $cgix->url;
|
||||
}
|
||||
|
||||
|
||||
##
|
||||
# $obj->tree_selectnode;
|
||||
# ----------------------
|
||||
# This function must return the URL that the Javascript
|
||||
# must point the user to when a node is selected, i.e
|
||||
# showing a certain category.
|
||||
##
|
||||
sub tree_selectnode
|
||||
{
|
||||
my $cgix = new GT::CGI ($IN);
|
||||
$cgix->param ("action", "category_click");
|
||||
return $cgix->url;
|
||||
}
|
||||
|
||||
|
||||
##
|
||||
# $obj->tree_panel_url;
|
||||
# ---------------------
|
||||
# This function returns the URL that should be used
|
||||
# in order to display ...
|
||||
##
|
||||
sub tree_panel_url
|
||||
{
|
||||
my $cgix = new GT::CGI ($IN);
|
||||
$cgix->param ("action", "tree_panel_init");
|
||||
return $cgix->url;
|
||||
}
|
||||
|
||||
|
||||
##
|
||||
# $obj->info_panel_url;
|
||||
# ---------------------
|
||||
# This function returns the URL that should be used
|
||||
# in order to display ...
|
||||
##
|
||||
sub info_panel_url
|
||||
{
|
||||
my $cgix = new GT::CGI ($IN);
|
||||
$cgix->param ("action", "info_panel_init");
|
||||
return $cgix->url;
|
||||
}
|
||||
|
||||
|
||||
##
|
||||
# $obj->code_panel_url;
|
||||
# ---------------------
|
||||
# This function returns the URL that should be used
|
||||
# in order to display ...
|
||||
##
|
||||
sub code_panel_url
|
||||
{
|
||||
my $cgix = new GT::CGI ($IN);
|
||||
$cgix->param ("action", "code_panel_init");
|
||||
return $cgix->url;
|
||||
}
|
||||
|
||||
|
||||
##
|
||||
# $obj->tree_reload_empty;
|
||||
# ------------------------
|
||||
# This method returns the URL that when called in the code
|
||||
# panel empties the Javascript Tree.
|
||||
##
|
||||
sub tree_reload_empty
|
||||
{
|
||||
my $cgix = new GT::CGI ($IN);
|
||||
$cgix->param ("action", "code_panel_reload_empty");
|
||||
return $cgix->url;
|
||||
}
|
||||
|
||||
|
||||
##
|
||||
# $obj->tree_reload_full;
|
||||
# -----------------------
|
||||
# This method returns the URL that when called in the code
|
||||
# panel replaces the Javascript tree with a fully loaded
|
||||
# tree (no need to database for requests).
|
||||
##
|
||||
sub tree_reload_full
|
||||
{
|
||||
my $cgix = new GT::CGI ($IN);
|
||||
$cgix->param ("action", "code_panel_reload_full");
|
||||
return $cgix->url;
|
||||
}
|
||||
|
||||
|
||||
##
|
||||
# $obj->tree_refreshnode_url;
|
||||
# ---------------------------
|
||||
# This method should return the URL that has to be invoked
|
||||
# when an user has updated a node and wants to refresh the
|
||||
# node's contents.
|
||||
##
|
||||
sub tree_refreshnode_url
|
||||
{
|
||||
my $cgix = new GT::CGI ($IN);
|
||||
$cgix->param ("action", "code_panel_init");
|
||||
return $cgix->url;
|
||||
}
|
||||
|
||||
|
||||
##
|
||||
# $obj->user_add_node;
|
||||
# --------------------
|
||||
# This method should return the URL that must be invoked
|
||||
# by the code frame when an user has added a subcategory
|
||||
# and that the javascript tree needs being refreshed.
|
||||
##
|
||||
sub user_add_node
|
||||
{
|
||||
my $cgix = new GT::CGI ($IN);
|
||||
$cgix->param ("action", "code_panel_category_add");
|
||||
return $cgix->url;
|
||||
}
|
||||
|
||||
|
||||
##
|
||||
# $obj->tree_movenode;
|
||||
# ---------------------
|
||||
# This method should return the URL that must be invoked
|
||||
# by the code frame when an user has added a subcategory
|
||||
# and that the javascript tree needs being refreshed.
|
||||
##
|
||||
sub tree_movenode
|
||||
{
|
||||
my $cgix = new GT::CGI ($IN);
|
||||
$cgix->param ("action", "category_move");
|
||||
return $cgix->url;
|
||||
}
|
||||
|
||||
|
||||
##
|
||||
# $obj->tree_movelink;
|
||||
# --------------------
|
||||
##
|
||||
sub movelink
|
||||
{
|
||||
my $cgix = new GT::CGI ($IN);
|
||||
$cgix->param ("action", "link_move");
|
||||
return $cgix->url;
|
||||
}
|
||||
|
||||
|
||||
##
|
||||
# $obj->tree_copylink;
|
||||
# --------------------
|
||||
##
|
||||
sub copylink
|
||||
{
|
||||
my $cgix = new GT::CGI ($IN);
|
||||
$cgix->param ("action", "link_copy");
|
||||
return $cgix->url;
|
||||
}
|
||||
|
||||
##
|
||||
# $obj->category_related_url;
|
||||
# ---------------------
|
||||
# This function returns the URL that should be used
|
||||
# in order to relate categories ...
|
||||
##
|
||||
sub category_related_url
|
||||
{
|
||||
my $cgix = new GT::CGI ($IN);
|
||||
$cgix->param ("action", "category_related_form");
|
||||
return $cgix->url;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
|
||||
|
||||
1277
site/slowtwitch.com/cgi-bin/articles/admin/Links/Build.pm
Normal file
1277
site/slowtwitch.com/cgi-bin/articles/admin/Links/Build.pm
Normal file
File diff suppressed because it is too large
Load Diff
365
site/slowtwitch.com/cgi-bin/articles/admin/Links/Config.pm
Normal file
365
site/slowtwitch.com/cgi-bin/articles/admin/Links/Config.pm
Normal file
@@ -0,0 +1,365 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Config.pm,v 1.117 2009/05/12 02:24:18 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::Config;
|
||||
# ======================================================================
|
||||
# Sets up our config variables -- the data itself is stored in
|
||||
# Links/Config/Data.pm, but you shouldn't need to edit it directly!
|
||||
#
|
||||
use GT::Config;
|
||||
use strict;
|
||||
use vars qw/@ISA $BIN %FILE_CACHE/;
|
||||
@ISA = 'GT::Config';
|
||||
|
||||
sub new {
|
||||
# --------------------------------------------------------------------
|
||||
# Takes one optional argument, the path to the admin directory.
|
||||
#
|
||||
my $class = ref $_[0] ? ref shift : shift;
|
||||
my $path = shift || '.';
|
||||
my $file = $path . '/Links/Config/Data.pm';
|
||||
my $header = <<END_OF_CONFIG;
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/support/
|
||||
# Updated : [localtime]
|
||||
#
|
||||
# Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
END_OF_CONFIG
|
||||
|
||||
my $self = $class->load($file, { inheritance => 0, cache => 1, header => $header });
|
||||
exists $self->{admin_root_path} or ($self->{admin_root_path} = $path || '.');
|
||||
exists $self->{version} or ($self->{version} = $Links::VERSION);
|
||||
exists $self->{setup} or ($self->{setup} = 0);
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub textarea {
|
||||
# ------------------------------------------------------------------
|
||||
# Taken from gforum. This takes a string of a config hash key whose value is an array reference.
|
||||
# Returns the array elements separated by \n's
|
||||
#
|
||||
my $want = shift;
|
||||
if (ref $Links::CFG->{$want} eq 'ARRAY') {
|
||||
return join "\n", @{$Links::CFG->{$want}};
|
||||
}
|
||||
elsif (ref $Links::CFG->{$want} eq 'HASH') {
|
||||
return join "\n", map "$_ => $Links::CFG->{$want}->{$_}", sort { $a <=> $b } keys %{$Links::CFG->{$want}};
|
||||
}
|
||||
else {
|
||||
return $Links::CFG->{$want};
|
||||
}
|
||||
}
|
||||
|
||||
sub load_vars {
|
||||
# ------------------------------------------------------------------
|
||||
# Returns a hash of config variables for use in templates.
|
||||
#
|
||||
my $t = {};
|
||||
while (my ($key, $val) = each %{$Links::CFG}) {
|
||||
if (ref $val eq 'ARRAY') { $val = join ", ", @$val }
|
||||
elsif (ref $val eq 'HASH') { $val = join ", ", map "$_ = $val->{$_}", keys %$val }
|
||||
$t->{"cfg_$key"} = $Links::IN->html_escape($val);
|
||||
}
|
||||
return $t;
|
||||
}
|
||||
|
||||
sub set_defaults {
|
||||
# ------------------------------------------------------------------
|
||||
# Set sensible defaults for the config values, overwriting old values.
|
||||
#
|
||||
my ($self, $val) = @_;
|
||||
|
||||
$self->{setup} = 1;
|
||||
$self->default_path($val);
|
||||
$self->default_build($val);
|
||||
$self->default_search($val);
|
||||
$self->default_review($val);
|
||||
$self->default_user($val);
|
||||
$self->default_email($val);
|
||||
$self->default_misc($val);
|
||||
$self->default_date($val);
|
||||
$self->default_other($val);
|
||||
}
|
||||
sub default { shift->set_defaults(1); } # Overwrite
|
||||
sub create_defaults { shift->set_defaults(0); } # Don't Overwrite
|
||||
|
||||
sub set {
|
||||
# ------------------------------------------------------------------
|
||||
# Sets a value.
|
||||
#
|
||||
my ($self, $key, $val, $overwrite) = @_;
|
||||
if ($overwrite or ! exists $self->{$key}) { $self->{$key} = $val; }
|
||||
}
|
||||
|
||||
sub default_path {
|
||||
# ------------------------------------------------------------------
|
||||
# Set the path settings to default values.
|
||||
#
|
||||
my ($self, $overwrite) = @_;
|
||||
$self->set('admin_root_url', _find_admin_url(), $overwrite);
|
||||
$self->set('db_cgi_url', _find_cgi_url(), $overwrite);
|
||||
$self->set('db_cgi_url_https', '', $overwrite);
|
||||
$self->set('build_root_url', _find_pages_url(), $overwrite);
|
||||
$self->set('path_to_perl', _find_perl(), $overwrite);
|
||||
$self->set('build_static_path', "$self->{build_root_path}/static", $overwrite);
|
||||
$self->set('build_static_url', "$self->{build_root_url}/static", $overwrite);
|
||||
$self->set('fileman_root_dir', $self->{admin_root_path}, $overwrite);
|
||||
$self->set('build_images_url', "$self->{build_root_url}/images", $overwrite);
|
||||
$self->set('build_css_url', "$self->{build_root_url}/links.css", $overwrite);
|
||||
$self->set('build_new_path', "$self->{build_root_path}/New", $overwrite);
|
||||
$self->set('build_new_url', "$self->{build_root_url}/New", $overwrite);
|
||||
$self->set('build_cool_path', "$self->{build_root_path}/Cool", $overwrite);
|
||||
$self->set('build_cool_url', "$self->{build_root_url}/Cool", $overwrite);
|
||||
$self->set('build_ratings_path', "$self->{build_root_path}/Ratings", $overwrite);
|
||||
$self->set('build_ratings_url', "$self->{build_root_url}/Ratings", $overwrite);
|
||||
$self->set('build_detail_path', "$self->{build_root_path}/Detailed", $overwrite);
|
||||
$self->set('build_detail_url', "$self->{build_root_url}/Detailed", $overwrite);
|
||||
}
|
||||
|
||||
sub default_build {
|
||||
# ------------------------------------------------------------------
|
||||
# Set the build settings to default values.
|
||||
#
|
||||
my ($self, $overwrite) = @_;
|
||||
$self->set('build_default_tpl', 'luna', $overwrite);
|
||||
$self->set('build_new_cutoff', 7, $overwrite);
|
||||
$self->set('build_pop_cutoff', 0.01, $overwrite);
|
||||
$self->set('build_use_backup', 1, $overwrite);
|
||||
$self->set('db_gen_category_list', 2, $overwrite);
|
||||
$self->set('add_system_fields', { Hits => 0, isNew => 'No', isPopular => 'No', isChanged => 'No', Status => 0, Rating => 0, Votes => 0 }, $overwrite);
|
||||
$self->set('build_auto_validate', 0, $overwrite);
|
||||
$self->set('db_referers', [], $overwrite);
|
||||
$self->set('links_cols_update_category', 'isPopular, Rating, Votes', $overwrite);
|
||||
$self->set('protected_vars', [qw/error message secondarynav Meta_Description Meta_Keywords/], $overwrite);
|
||||
|
||||
$self->set('build_sort_order_category', "isNew DESC,isPopular DESC,Title", $overwrite);
|
||||
$self->set('build_sort_paid_first', 1, $overwrite);
|
||||
$self->set('build_sort_order_new', "Add_Date DESC,Title", $overwrite);
|
||||
$self->set('build_sort_order_cool', "Title", $overwrite);
|
||||
$self->set('build_sort_order_editor', "isValidated DESC, Title ASC", $overwrite);
|
||||
$self->set('build_span_pages', 1, $overwrite);
|
||||
$self->set('build_links_per_page', 25, $overwrite);
|
||||
$self->set('build_new_date_span_pages', 1, $overwrite);
|
||||
$self->set('build_new_gb', 1, $overwrite);
|
||||
$self->set('build_cool_gb', 1, $overwrite);
|
||||
$self->set('build_category_sort', 'Name', $overwrite);
|
||||
$self->set('build_category_yahoo', 1, $overwrite);
|
||||
$self->set('build_category_columns', 2, $overwrite);
|
||||
$self->set('build_category_table', 'border=0 width="100%"', $overwrite);
|
||||
$self->set('dynamic_no_url_transform', ['<%build_static_url%>'], $overwrite);
|
||||
$self->set('dynamic_pages', 1, $overwrite);
|
||||
$self->set('dynamic_preserve', ['t', 'd', 's'], $overwrite);
|
||||
$self->set('dynamic_preserve_sort_pages', ['category', 'detailed'], $overwrite);
|
||||
$self->set('compress', 0, $overwrite);
|
||||
|
||||
$self->set('build_detailed', 0, $overwrite);
|
||||
$self->set('build_home', '', $overwrite);
|
||||
$self->set('build_index', 'index.html', $overwrite);
|
||||
$self->set('build_index_include', 1, $overwrite);
|
||||
$self->set('build_more', 'more', $overwrite);
|
||||
$self->set('build_extension', '.html', $overwrite);
|
||||
$self->set('build_detail_format', '%ID%', $overwrite);
|
||||
$self->set('build_category_format', '%Full_Name%', $overwrite);
|
||||
$self->set('build_format_compat', 0, $overwrite);
|
||||
$self->set('build_category_dynamic', 'Full_Name', $overwrite);
|
||||
$self->set('build_dir_per', '0777', $overwrite);
|
||||
$self->set('build_file_per', '0666', $overwrite);
|
||||
}
|
||||
|
||||
sub default_user {
|
||||
# ------------------------------------------------------------------
|
||||
# Set the user settings to default values.
|
||||
#
|
||||
my ($self, $overwrite) = @_;
|
||||
$self->set('user_validation', 1, $overwrite);
|
||||
$self->set('user_required', 1, $overwrite);
|
||||
$self->set('user_rate_required', 1, $overwrite);
|
||||
$self->set('user_direct_mod', 0, $overwrite);
|
||||
$self->set('user_allow_pass', 1, $overwrite);
|
||||
$self->set('user_sessions', 'Cookies', $overwrite);
|
||||
$self->set('user_session_length', 3, $overwrite);
|
||||
|
||||
$self->set('framed_jump', 0, $overwrite);
|
||||
|
||||
$self->set('bookmark_enabled', 1, $overwrite);
|
||||
$self->set('bookmark_folder_default_name', 'Default', $overwrite);
|
||||
$self->set('bookmark_folder_limit', 5, $overwrite);
|
||||
$self->set('bookmark_folders_per_page', 10, $overwrite);
|
||||
$self->set('bookmark_links_limit', 25, $overwrite);
|
||||
$self->set('bookmark_links_per_page', 25, $overwrite);
|
||||
$self->set('bookmark_links_sort', 'Title', $overwrite);
|
||||
$self->set('bookmark_links_sort_order', 'ASC', $overwrite);
|
||||
$self->set('bookmark_users_per_page', 25, $overwrite);
|
||||
|
||||
$self->set('newsletter_enabled', 0, $overwrite);
|
||||
$self->set('newsletter_global_subscribe', 0, $overwrite);
|
||||
$self->set('newsletter_max_depth', 2, $overwrite);
|
||||
}
|
||||
|
||||
sub default_email {
|
||||
# ------------------------------------------------------------------
|
||||
# Set the email settings to default values.
|
||||
#
|
||||
my ($self, $overwrite) = @_;
|
||||
$self->set('db_admin_email', '', $overwrite);
|
||||
$self->set('db_smtp_server', '', $overwrite);
|
||||
$self->set('db_mail_path', _find_sendmail(), $overwrite);
|
||||
$self->set('email_add', 1, $overwrite);
|
||||
$self->set('email_mod', 1, $overwrite);
|
||||
$self->set('email_review_add', 1, $overwrite);
|
||||
$self->set('email_payment', 1, $overwrite);
|
||||
$self->set('admin_email_add', 1, $overwrite);
|
||||
$self->set('admin_email_mod', 1, $overwrite);
|
||||
$self->set('admin_email_review_add', 1, $overwrite);
|
||||
$self->set('admin_email_review_mod', 1, $overwrite);
|
||||
}
|
||||
|
||||
sub default_date {
|
||||
# ------------------------------------------------------------------
|
||||
# Set the default settings for any date routines.
|
||||
#
|
||||
my ($self, $overwrite) = @_;
|
||||
$self->set('date_db_format', '%yyyy%-%mm%-%dd%', $overwrite);
|
||||
$self->set('date_review_format', '%mmm% %d% %yyyy% %h%:%MM%%tt%', $overwrite);
|
||||
$self->set('date_user_format', '%ddd% %mmm% %dd% %yyyy%', $overwrite);
|
||||
$self->set('date_long_format', '%dddd%, %mmmm% %dd% %yyyy%', $overwrite);
|
||||
$self->set('date_expiry_format', '%dddd% %mmm% %d% %yyyy% %h%:%MM% %tt%', $overwrite);
|
||||
$self->set('date_offset', 0, $overwrite);
|
||||
$self->set('date_days_short', [qw/Sun Mon Tue Wed Thu Fri Sat/], $overwrite);
|
||||
$self->set('date_days_long', [qw/Sunday Monday Tuesday Wednesday Thursday Friday Saturday/], $overwrite);
|
||||
$self->set('date_month_short', [qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/], $overwrite);
|
||||
$self->set('date_month_long', [qw/January February March April May June July August September October November December/], $overwrite);
|
||||
}
|
||||
|
||||
sub default_search {
|
||||
# ------------------------------------------------------------------
|
||||
# Update the search settings to default values.
|
||||
#
|
||||
my ($self, $overwrite) = @_;
|
||||
$self->set('search_maxhits', 25, $overwrite);
|
||||
$self->set('search_bool', 'AND', $overwrite);
|
||||
$self->set('search_substring', 0, $overwrite);
|
||||
$self->set('build_sort_order_search', "score", $overwrite);
|
||||
$self->set('build_sort_order_search_cat', "score", $overwrite);
|
||||
$self->set('build_search_gb', 1, $overwrite);
|
||||
$self->set('search_blocked', [], $overwrite);
|
||||
$self->set('search_highlighting', 1, $overwrite);
|
||||
$self->set('search_highlight_colors', 5, $overwrite);
|
||||
}
|
||||
|
||||
sub default_review {
|
||||
# ------------------------------------------------------------------
|
||||
# Update the review settings to default values.
|
||||
#
|
||||
my ($self, $overwrite) = @_;
|
||||
$self->set('user_review_required', 1, $overwrite);
|
||||
$self->set('review_auto_validate', 0, $overwrite);
|
||||
$self->set('review_allow_modify', 1, $overwrite);
|
||||
$self->set('review_modify_timeout', 0, $overwrite);
|
||||
$self->set('review_max_reviews', 1, $overwrite);
|
||||
$self->set('reviews_per_page', 5, $overwrite);
|
||||
$self->set('review_sort_by', 'Review_Date', $overwrite);
|
||||
$self->set('review_sort_order', 'desc', $overwrite);
|
||||
$self->set('review_convert_br_tags', 1, $overwrite);
|
||||
$self->set('review_days_old', 7, $overwrite);
|
||||
}
|
||||
|
||||
|
||||
sub default_misc {
|
||||
# ------------------------------------------------------------------
|
||||
# Set the misc settings to default values.
|
||||
#
|
||||
my ($self, $overwrite) = @_;
|
||||
$self->set('reg_number', '', $overwrite);
|
||||
$self->set('nph_headers', 1, $overwrite);
|
||||
$self->set('header_charset', 'ISO-8859-1', $overwrite);
|
||||
$self->set('debug_level', 0, $overwrite);
|
||||
$self->set('error_message', '', $overwrite);
|
||||
$self->set('disabled', 0, $overwrite);
|
||||
$self->set('bans', [], $overwrite);
|
||||
$self->set('link_validate_date', 1, $overwrite);
|
||||
}
|
||||
|
||||
sub default_other {
|
||||
# ------------------------------------------------------------------
|
||||
# Update settings not available throught the web.
|
||||
#
|
||||
my ($self, $overwrite) = @_;
|
||||
$self->set('private_sessions', 1, $overwrite);
|
||||
$self->set('db_hit_expire', 2, $overwrite);
|
||||
$self->set('db_rate_expire', 2, $overwrite);
|
||||
$self->set('quick_links', {
|
||||
'admin.cgi?do=page&page=tools_validate.html' => 'Validate Links',
|
||||
'admin.cgi?do=page&page=tools_validate_changes.html' => 'Validate Changes',
|
||||
'admin.cgi?do=page&page=tools_validate_reviews.html' => 'Validate Reviews'
|
||||
}, $overwrite);
|
||||
}
|
||||
|
||||
sub _find_admin_url {
|
||||
# ------------------------------------------------------------------
|
||||
# Return base url of current script.
|
||||
#
|
||||
my $url = GT::CGI->url({ absolute => 1, query_string => 0 });
|
||||
$url =~ s,/[^/]*$,,;
|
||||
return $url;
|
||||
}
|
||||
|
||||
sub _find_cgi_url {
|
||||
# ------------------------------------------------------------------
|
||||
# Returns base url of one level back.
|
||||
#
|
||||
my $url = _find_admin_url();
|
||||
$url =~ s,/admin$,,;
|
||||
return $url;
|
||||
}
|
||||
|
||||
sub _find_pages_url {
|
||||
# ------------------------------------------------------------------
|
||||
# Returns pages url.
|
||||
#
|
||||
return _find_cgi_url();
|
||||
}
|
||||
|
||||
sub _find_perl {
|
||||
# ------------------------------------------------------------------
|
||||
# Returns path to perl.
|
||||
#
|
||||
my @poss_perls = qw!
|
||||
/usr/local/bin/perl /usr/bin/perl /bin/perl
|
||||
/usr/local/bin/perl5 /usr/bin/perl5 /bin/perl
|
||||
/perl/bin/perl.exe c:/perl/bin/perl.exe d:/perl/bin/perl.exe
|
||||
!;
|
||||
foreach my $perl_path (@poss_perls) {
|
||||
return $perl_path if -f $perl_path and -x _;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
sub _find_sendmail {
|
||||
# ------------------------------------------------------------------
|
||||
# Looks for sendmail.
|
||||
#
|
||||
for my $sendmail (qw|/usr/sbin/sendmail /usr/lib/sendmail /usr/bin/sendmail /bin/sendmail|) {
|
||||
return $sendmail if -f $sendmail and -x _;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
1;
|
||||
356
site/slowtwitch.com/cgi-bin/articles/admin/Links/Config/Data.pm
Normal file
356
site/slowtwitch.com/cgi-bin/articles/admin/Links/Config/Data.pm
Normal file
@@ -0,0 +1,356 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/support/
|
||||
# Updated : Sun May 19 21:05:55 2024
|
||||
#
|
||||
# Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
{
|
||||
'add_system_fields' => {
|
||||
'Hits' => '0',
|
||||
'Rating' => '0',
|
||||
'SlideShowCache' => '{}',
|
||||
'Status' => '0',
|
||||
'Votes' => '0',
|
||||
'isChanged' => 'No',
|
||||
'isNew' => 'No',
|
||||
'isPopular' => 'No'
|
||||
},
|
||||
'admin_email_add' => '1',
|
||||
'admin_email_mod' => '1',
|
||||
'admin_email_review_add' => '1',
|
||||
'admin_email_review_mod' => '1',
|
||||
'admin_root_path' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin',
|
||||
'admin_root_url' => '/cgi-bin/articles/admin',
|
||||
'bans' => [],
|
||||
'bookmark_enabled' => '0',
|
||||
'bookmark_folder_default_name' => 'Default',
|
||||
'bookmark_folder_limit' => '5',
|
||||
'bookmark_folders_per_page' => '25',
|
||||
'bookmark_links_limit' => '100',
|
||||
'bookmark_links_per_page' => '25',
|
||||
'bookmark_links_sort' => 'Title',
|
||||
'bookmark_links_sort_order' => 'ASC',
|
||||
'bookmark_users_per_page' => '25',
|
||||
'build_auto_validate' => '1',
|
||||
'build_category_columns' => '2',
|
||||
'build_category_dynamic' => 'Full_Name',
|
||||
'build_category_format' => '%Full_Name%',
|
||||
'build_category_sort' => 'Name',
|
||||
'build_category_table' => 'border=0 width="100%"',
|
||||
'build_category_yahoo' => '1',
|
||||
'build_cool_gb' => '1',
|
||||
'build_cool_path' => '/var/home/slowtwitch/slowtwitch.com/www/Cool',
|
||||
'build_cool_url' => '/Cool',
|
||||
'build_css_url' => '/articles/static/css/links.css',
|
||||
'build_default_tpl' => 'twitch',
|
||||
'build_detail_format' => '%Full_Name%/%Title%_%ID%',
|
||||
'build_detail_path' => '/var/home/slowtwitch/slowtwitch.com/www',
|
||||
'build_detail_url' => 'https://www.slowtwitch.com',
|
||||
'build_detailed' => '1',
|
||||
'build_dir_per' => '0777',
|
||||
'build_extension' => '.html',
|
||||
'build_file_per' => '0666',
|
||||
'build_format_compat' => '2',
|
||||
'build_home' => '',
|
||||
'build_images_url' => '/images',
|
||||
'build_index' => 'index.html',
|
||||
'build_index_include' => '1',
|
||||
'build_links_per_page' => '25',
|
||||
'build_more' => 'more',
|
||||
'build_new_cutoff' => '7',
|
||||
'build_new_date_span_pages' => '1',
|
||||
'build_new_gb' => '1',
|
||||
'build_new_path' => '/var/home/slowtwitch/slowtwitch.com/www/New',
|
||||
'build_new_url' => '/New',
|
||||
'build_pop_cutoff' => '0.01',
|
||||
'build_ratings_path' => '/var/home/slowtwitch/slowtwitch.com/www/Ratings',
|
||||
'build_ratings_url' => '/Ratings',
|
||||
'build_root_path' => '/var/home/slowtwitch/slowtwitch.com/www',
|
||||
'build_root_url' => 'https://www.slowtwitch.com',
|
||||
'build_search_gb' => '0',
|
||||
'build_sort_order_category' => 'Mod_Date DESC, Add_Date DESC,Title ASC',
|
||||
'build_sort_order_cool' => 'Title',
|
||||
'build_sort_order_editor' => 'isValidated DESC, Title ASC',
|
||||
'build_sort_order_new' => 'Add_Date DESC,Title',
|
||||
'build_sort_order_search' => 'Mod_Date DESC',
|
||||
'build_sort_order_search_cat' => 'score',
|
||||
'build_sort_paid_first' => '1',
|
||||
'build_span_pages' => '1',
|
||||
'build_static_path' => '/var/home/slowtwitch/slowtwitch.com/www/articles/static',
|
||||
'build_static_url' => '/articles/static',
|
||||
'build_use_backup' => '0',
|
||||
'compress' => '1',
|
||||
'date_days_long' => [
|
||||
'Sunday',
|
||||
'Monday',
|
||||
'Tuesday',
|
||||
'Wednesday',
|
||||
'Thursday',
|
||||
'Friday',
|
||||
'Saturday'
|
||||
],
|
||||
'date_days_short' => [
|
||||
'Sun',
|
||||
'Mon',
|
||||
'Tue',
|
||||
'Wed',
|
||||
'Thu',
|
||||
'Fri',
|
||||
'Sat'
|
||||
],
|
||||
'date_db_format' => '%yyyy%-%mm%-%dd%',
|
||||
'date_expiry_format' => '%dddd% %mmm% %d% %yyyy% %h%:%MM% %tt%',
|
||||
'date_long_format' => '%dddd%, %mmmm% %dd% %yyyy%',
|
||||
'date_month_long' => [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December'
|
||||
],
|
||||
'date_month_short' => [
|
||||
'Jan',
|
||||
'Feb',
|
||||
'Mar',
|
||||
'Apr',
|
||||
'May',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Aug',
|
||||
'Sep',
|
||||
'Oct',
|
||||
'Nov',
|
||||
'Dec'
|
||||
],
|
||||
'date_offset' => '0',
|
||||
'date_review_format' => '%mmm% %d% %yyyy% %h%:%MM%%tt%',
|
||||
'date_user_format' => '%ddd% %mmm% %dd% %yyyy%',
|
||||
'db_admin_email' => 'webmaster@slowtwitch.com',
|
||||
'db_cgi_url' => '/cgi-bin/articles',
|
||||
'db_cgi_url_https' => '/cgi-bin/articles',
|
||||
'db_gen_category_list' => '2',
|
||||
'db_hit_expire' => '2',
|
||||
'db_mail_path' => '/usr/sbin/sendmail',
|
||||
'db_rate_expire' => '2',
|
||||
'db_referers' => [],
|
||||
'db_smtp_server' => '',
|
||||
'debug_level' => '0',
|
||||
'disabled' => '0',
|
||||
'dynamic_404_status' => '0',
|
||||
'dynamic_no_url_transform' => [
|
||||
'<%build_static_url%>'
|
||||
],
|
||||
'dynamic_pages' => '1',
|
||||
'dynamic_preserve' => [
|
||||
't',
|
||||
'd',
|
||||
's'
|
||||
],
|
||||
'dynamic_preserve_sort_pages' => [
|
||||
'category',
|
||||
'detailed'
|
||||
],
|
||||
'email_add' => '1',
|
||||
'email_mod' => '1',
|
||||
'email_payment' => '1',
|
||||
'email_review_add' => '1',
|
||||
'error_message' => '',
|
||||
'featured_articles' => [
|
||||
'8954',
|
||||
'8953',
|
||||
'8951',
|
||||
'8950'
|
||||
],
|
||||
'featured_photos' => [
|
||||
'8823',
|
||||
'8822'
|
||||
],
|
||||
'fileman_root_dir' => '/var/home/slowtwitch/slowtwitch.com',
|
||||
'framed_jump' => '0',
|
||||
'header_charset' => 'utf-8',
|
||||
'last_build' => '1598637754.56255',
|
||||
'last_clicktrack_cleanup' => '2024-05-17',
|
||||
'link_validate_date' => '1',
|
||||
'links_cols_update_category' => 'isPopular, Rating, Votes',
|
||||
'newsletter_enabled' => '0',
|
||||
'newsletter_global_subscribe' => '0',
|
||||
'newsletter_max_depth' => '2',
|
||||
'nph_headers' => '1',
|
||||
'path_to_perl' => '/usr/local/bin/perl',
|
||||
'payment' => {
|
||||
'auto_validate' => '1',
|
||||
'description' => '',
|
||||
'direct' => {
|
||||
'methods' => {
|
||||
'AuthorizeDotNet' => {
|
||||
'module' => 'Links/Payment/Direct/AuthorizeDotNet.pm',
|
||||
'package' => 'Links::Payment::Direct::AuthorizeDotNet',
|
||||
'types' => [
|
||||
'AMEX',
|
||||
'DINERS',
|
||||
'DISC',
|
||||
'JCB',
|
||||
'MC',
|
||||
'VISA'
|
||||
]
|
||||
},
|
||||
'Moneris' => {
|
||||
'module' => 'Links/Payment/Direct/Moneris.pm',
|
||||
'package' => 'Links::Payment::Direct::Moneris',
|
||||
'types' => [
|
||||
'AMEX',
|
||||
'MC',
|
||||
'VISA',
|
||||
'DISC',
|
||||
'NOVA',
|
||||
'DINERS'
|
||||
]
|
||||
}
|
||||
},
|
||||
'used' => {}
|
||||
},
|
||||
'discounts' => {},
|
||||
'enabled' => '0',
|
||||
'expired_is_free' => '0',
|
||||
'expiry_notify' => '7',
|
||||
'mode' => '3',
|
||||
'postback' => [
|
||||
{
|
||||
'method' => 'PayPal',
|
||||
'type' => 'remote',
|
||||
'var' => 'txn_type'
|
||||
},
|
||||
{
|
||||
'method' => 'WorldPay',
|
||||
'type' => 'remote',
|
||||
'var' => 'transStatus'
|
||||
},
|
||||
{
|
||||
'method' => '2CheckOut',
|
||||
'type' => 'remote',
|
||||
'var' => 'cart_order_id'
|
||||
}
|
||||
],
|
||||
'remote' => {
|
||||
'methods' => {
|
||||
'2CheckOut' => {
|
||||
'module' => 'Links/Payment/Remote/2CheckOut.pm',
|
||||
'package' => 'Links::Payment::Remote::2CheckOut',
|
||||
'recurring' => '0',
|
||||
'types' => [
|
||||
'VISA',
|
||||
'MC',
|
||||
'AMEX',
|
||||
'DISC',
|
||||
'DINERS',
|
||||
'JCB'
|
||||
]
|
||||
},
|
||||
'Manual' => {
|
||||
'module' => 'Links/Payment/Remote/Manual.pm',
|
||||
'package' => 'Links::Payment::Remote::Manual',
|
||||
'recurring' => '0',
|
||||
'types' => [
|
||||
'MANUAL'
|
||||
]
|
||||
},
|
||||
'PayPal' => {
|
||||
'module' => 'Links/Payment/Remote/PayPal.pm',
|
||||
'package' => 'Links::Payment::Remote::PayPal',
|
||||
'recurring' => '1',
|
||||
'types' => [
|
||||
'PAYPAL'
|
||||
]
|
||||
},
|
||||
'WorldPay' => {
|
||||
'module' => 'Links/Payment/Remote/WorldPay.pm',
|
||||
'package' => 'Links::Payment::Remote::WorldPay',
|
||||
'recurring' => '1',
|
||||
'types' => [
|
||||
'VISA',
|
||||
'MC',
|
||||
'EURO',
|
||||
'VISA_DEBIT',
|
||||
'SWITCH',
|
||||
'SOLO',
|
||||
'DELTA',
|
||||
'JCB',
|
||||
'AMEX',
|
||||
'DINERS'
|
||||
]
|
||||
}
|
||||
},
|
||||
'used' => {}
|
||||
},
|
||||
'term' => {}
|
||||
},
|
||||
'private_sessions' => '1',
|
||||
'protected_vars' => [
|
||||
'error',
|
||||
'message',
|
||||
'secondarynav',
|
||||
'Meta_Description',
|
||||
'Meta_Keywords'
|
||||
],
|
||||
'quick_links' => {
|
||||
'admin.cgi?do=page&page=tools_validate.html' => 'Validate Links',
|
||||
'admin.cgi?do=page&page=tools_validate_changes.html' => 'Validate Changes',
|
||||
'admin.cgi?do=page&page=tools_validate_reviews.html' => 'Validate Reviews',
|
||||
'https://forum.slowtwitch.com/admin/delete_spam_comments.php' => 'Delete Spam',
|
||||
'https://www.slowtwitch.com/cgi-bin/articles/admin/cullGlist.php' => 'Cull gList',
|
||||
'https://www.slowtwitch.com/cgi-bin/articles/admin/nph-build.cgi?do=all' => 'Build All',
|
||||
'https://www.slowtwitch.com/cgi-bin/articles/admin/nph-build.cgi?do=changed' => 'Build Changed',
|
||||
'https://www.slowtwitch.com/myadminphp/index.php?token=b521b01fa6ed8c9f890b24b148ac74cb' => 'PHPMyAdmin'
|
||||
},
|
||||
'reg_number' => 'GL0807-06828-37',
|
||||
'review_allow_modify' => '1',
|
||||
'review_auto_validate' => '0',
|
||||
'review_convert_br_tags' => '1',
|
||||
'review_days_old' => '7',
|
||||
'review_max_reviews' => '1',
|
||||
'review_modify_timeout' => '0',
|
||||
'review_sort_by' => 'Review_Date',
|
||||
'review_sort_order' => 'desc',
|
||||
'reviews_per_page' => '25',
|
||||
'search_blocked' => [],
|
||||
'search_bool' => 'AND',
|
||||
'search_highlight_colors' => '5',
|
||||
'search_highlighting' => '1',
|
||||
'search_logging' => '0',
|
||||
'search_maxhits' => '25',
|
||||
'search_substring' => '0',
|
||||
'setup' => '1',
|
||||
'show_upgrade_rerun' => '0',
|
||||
'treecats_enabled' => '0',
|
||||
'updates' => {},
|
||||
'updates_check_on_home' => '1',
|
||||
'user_allow_pass' => '1',
|
||||
'user_cookie_domain' => '',
|
||||
'user_cookie_prefix' => '',
|
||||
'user_direct_mod' => '1',
|
||||
'user_link_validation' => '0',
|
||||
'user_rate_required' => '1',
|
||||
'user_required' => '1',
|
||||
'user_review_required' => '1',
|
||||
'user_session_length' => '3',
|
||||
'user_sessions' => 'Cookies',
|
||||
'user_validation' => '1',
|
||||
'verify_chunk' => '10',
|
||||
'verify_max_children' => '3',
|
||||
'version' => '3.3.0'
|
||||
};
|
||||
|
||||
# vim:syn=perl:ts=4:noet
|
||||
@@ -0,0 +1,701 @@
|
||||
{
|
||||
'cache' => {
|
||||
'time' => '1672499734',
|
||||
'updates' => [
|
||||
bless(
|
||||
{
|
||||
'deps' => [],
|
||||
'description' => 'This update fixes a bug in GT::WWW where it would cause link verify to fail with a -4 (Could not connect) error.',
|
||||
'files' => [
|
||||
[
|
||||
'186',
|
||||
'library',
|
||||
'',
|
||||
'0644',
|
||||
'GT/WWW.pm'
|
||||
]
|
||||
],
|
||||
'id' => '132',
|
||||
'installed' => '1',
|
||||
'paths' => {
|
||||
'fixed' => {
|
||||
'build' => '/var/home/slowtwitch/slowtwitch.com/www',
|
||||
'cool' => '/var/home/slowtwitch/slowtwitch.com/www/Cool',
|
||||
'detail' => '/var/home/slowtwitch/slowtwitch.com/www',
|
||||
'new' => '/var/home/slowtwitch/slowtwitch.com/www/New',
|
||||
'ratings' => '/var/home/slowtwitch/slowtwitch.com/www/Ratings',
|
||||
'static' => '/var/home/slowtwitch/slowtwitch.com/www/articles/static'
|
||||
},
|
||||
'library' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin'
|
||||
},
|
||||
'script' => {
|
||||
'admin' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin',
|
||||
'cgi' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles'
|
||||
},
|
||||
'static' => {
|
||||
'static' => '/var/home/slowtwitch/slowtwitch.com/www/articles/static'
|
||||
},
|
||||
'template' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/templates'
|
||||
},
|
||||
'version' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin'
|
||||
}
|
||||
},
|
||||
'revdeps' => [],
|
||||
'reversible' => '1',
|
||||
'severity' => '2',
|
||||
'title' => 'GT::WWW unresolvable host error fix',
|
||||
'unique' => '0'
|
||||
},
|
||||
'GT::Update::Update'
|
||||
),
|
||||
bless(
|
||||
{
|
||||
'deps' => [],
|
||||
'description' => 'This update fixes an error where editors using browser.cgi get an error, "You are not authorized to perform this action," when attempting to modify a link that they should be able to modify.',
|
||||
'files' => [
|
||||
[
|
||||
'187',
|
||||
'library',
|
||||
'',
|
||||
'0644',
|
||||
'Links/Browser/Controller.pm'
|
||||
]
|
||||
],
|
||||
'id' => '133',
|
||||
'installed' => '1',
|
||||
'paths' => {
|
||||
'fixed' => {
|
||||
'build' => '/var/home/slowtwitch/slowtwitch.com/www',
|
||||
'cool' => '/var/home/slowtwitch/slowtwitch.com/www/Cool',
|
||||
'detail' => '/var/home/slowtwitch/slowtwitch.com/www',
|
||||
'new' => '/var/home/slowtwitch/slowtwitch.com/www/New',
|
||||
'ratings' => '/var/home/slowtwitch/slowtwitch.com/www/Ratings',
|
||||
'static' => '/var/home/slowtwitch/slowtwitch.com/www/articles/static'
|
||||
},
|
||||
'library' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin'
|
||||
},
|
||||
'script' => {
|
||||
'admin' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin',
|
||||
'cgi' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles'
|
||||
},
|
||||
'static' => {
|
||||
'static' => '/var/home/slowtwitch/slowtwitch.com/www/articles/static'
|
||||
},
|
||||
'template' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/templates'
|
||||
},
|
||||
'version' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin'
|
||||
}
|
||||
},
|
||||
'revdeps' => [],
|
||||
'reversible' => '1',
|
||||
'severity' => '1',
|
||||
'title' => 'Editor browser error on link modify',
|
||||
'unique' => '0'
|
||||
},
|
||||
'GT::Update::Update'
|
||||
),
|
||||
bless(
|
||||
{
|
||||
'deps' => [],
|
||||
'description' => 'This update fixes "ColumnName cannot contain the value \'\'" error when attempting to modify a link with an INT column (null = yes, no default).',
|
||||
'files' => [
|
||||
[
|
||||
'188',
|
||||
'library',
|
||||
'',
|
||||
'0644',
|
||||
'Links/User/Modify.pm'
|
||||
]
|
||||
],
|
||||
'id' => '134',
|
||||
'installed' => '1',
|
||||
'paths' => {
|
||||
'fixed' => {
|
||||
'build' => '/var/home/slowtwitch/slowtwitch.com/www',
|
||||
'cool' => '/var/home/slowtwitch/slowtwitch.com/www/Cool',
|
||||
'detail' => '/var/home/slowtwitch/slowtwitch.com/www',
|
||||
'new' => '/var/home/slowtwitch/slowtwitch.com/www/New',
|
||||
'ratings' => '/var/home/slowtwitch/slowtwitch.com/www/Ratings',
|
||||
'static' => '/var/home/slowtwitch/slowtwitch.com/www/articles/static'
|
||||
},
|
||||
'library' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin'
|
||||
},
|
||||
'script' => {
|
||||
'admin' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin',
|
||||
'cgi' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles'
|
||||
},
|
||||
'static' => {
|
||||
'static' => '/var/home/slowtwitch/slowtwitch.com/www/articles/static'
|
||||
},
|
||||
'template' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/templates'
|
||||
},
|
||||
'version' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin'
|
||||
}
|
||||
},
|
||||
'revdeps' => [],
|
||||
'reversible' => '1',
|
||||
'severity' => '1',
|
||||
'title' => 'INT validation error on modify',
|
||||
'unique' => '0'
|
||||
},
|
||||
'GT::Update::Update'
|
||||
),
|
||||
bless(
|
||||
{
|
||||
'deps' => [],
|
||||
'description' => 'This update fixes two bugs in the category browser: 1) Error deleting a link on the user side category browser (ie. not the admin one) from search results when the category depth is greater than 1. 2) When the link_validate_date option was turned on, Add_Date was getting set to the current date on modify.',
|
||||
'files' => [
|
||||
[
|
||||
'189',
|
||||
'library',
|
||||
'',
|
||||
'0644',
|
||||
'Links/Browser.pm'
|
||||
]
|
||||
],
|
||||
'id' => '135',
|
||||
'installed' => '1',
|
||||
'paths' => {
|
||||
'fixed' => {
|
||||
'build' => '/var/home/slowtwitch/slowtwitch.com/www',
|
||||
'cool' => '/var/home/slowtwitch/slowtwitch.com/www/Cool',
|
||||
'detail' => '/var/home/slowtwitch/slowtwitch.com/www',
|
||||
'new' => '/var/home/slowtwitch/slowtwitch.com/www/New',
|
||||
'ratings' => '/var/home/slowtwitch/slowtwitch.com/www/Ratings',
|
||||
'static' => '/var/home/slowtwitch/slowtwitch.com/www/articles/static'
|
||||
},
|
||||
'library' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin'
|
||||
},
|
||||
'script' => {
|
||||
'admin' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin',
|
||||
'cgi' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles'
|
||||
},
|
||||
'static' => {
|
||||
'static' => '/var/home/slowtwitch/slowtwitch.com/www/articles/static'
|
||||
},
|
||||
'template' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/templates'
|
||||
},
|
||||
'version' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin'
|
||||
}
|
||||
},
|
||||
'revdeps' => [
|
||||
'140'
|
||||
],
|
||||
'reversible' => '1',
|
||||
'severity' => '1',
|
||||
'title' => 'Category Browser fixes',
|
||||
'unique' => '0'
|
||||
},
|
||||
'GT::Update::Update'
|
||||
),
|
||||
bless(
|
||||
{
|
||||
'deps' => [],
|
||||
'description' => 'This update fixes a bug in the payment code where the name and e-mail address of the user aren\'t correctly passed to the link_expired.eml and link_expiry_notify.eml e-mail templates.',
|
||||
'files' => [
|
||||
[
|
||||
'190',
|
||||
'library',
|
||||
'',
|
||||
'0644',
|
||||
'Links/Payment.pm'
|
||||
]
|
||||
],
|
||||
'id' => '136',
|
||||
'installed' => '1',
|
||||
'paths' => {
|
||||
'fixed' => {
|
||||
'build' => '/var/home/slowtwitch/slowtwitch.com/www',
|
||||
'cool' => '/var/home/slowtwitch/slowtwitch.com/www/Cool',
|
||||
'detail' => '/var/home/slowtwitch/slowtwitch.com/www',
|
||||
'new' => '/var/home/slowtwitch/slowtwitch.com/www/New',
|
||||
'ratings' => '/var/home/slowtwitch/slowtwitch.com/www/Ratings',
|
||||
'static' => '/var/home/slowtwitch/slowtwitch.com/www/articles/static'
|
||||
},
|
||||
'library' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin'
|
||||
},
|
||||
'script' => {
|
||||
'admin' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin',
|
||||
'cgi' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles'
|
||||
},
|
||||
'static' => {
|
||||
'static' => '/var/home/slowtwitch/slowtwitch.com/www/articles/static'
|
||||
},
|
||||
'template' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/templates'
|
||||
},
|
||||
'version' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin'
|
||||
}
|
||||
},
|
||||
'revdeps' => [],
|
||||
'reversible' => '1',
|
||||
'severity' => '1',
|
||||
'title' => 'Broken payment link expiry e-mails',
|
||||
'unique' => '0'
|
||||
},
|
||||
'GT::Update::Update'
|
||||
),
|
||||
bless(
|
||||
{
|
||||
'deps' => [],
|
||||
'description' => 'This update fixes the "defined(%hash) is deprecated" warnings that occur when using Gossamer Links with Perl 5.12 and higher.',
|
||||
'files' => [
|
||||
[
|
||||
'191',
|
||||
'library',
|
||||
'',
|
||||
'0644',
|
||||
'bases.pm'
|
||||
],
|
||||
[
|
||||
'192',
|
||||
'library',
|
||||
'',
|
||||
'0644',
|
||||
'GT/MIMETypes.pm'
|
||||
],
|
||||
[
|
||||
'194',
|
||||
'library',
|
||||
'',
|
||||
'0644',
|
||||
'GT/SQL/Base.pm'
|
||||
],
|
||||
[
|
||||
'193',
|
||||
'library',
|
||||
'',
|
||||
'0644',
|
||||
'GT/Template.pm'
|
||||
]
|
||||
],
|
||||
'id' => '137',
|
||||
'installed' => '1',
|
||||
'paths' => {
|
||||
'fixed' => {
|
||||
'build' => '/var/home/slowtwitch/slowtwitch.com/www',
|
||||
'cool' => '/var/home/slowtwitch/slowtwitch.com/www/Cool',
|
||||
'detail' => '/var/home/slowtwitch/slowtwitch.com/www',
|
||||
'new' => '/var/home/slowtwitch/slowtwitch.com/www/New',
|
||||
'ratings' => '/var/home/slowtwitch/slowtwitch.com/www/Ratings',
|
||||
'static' => '/var/home/slowtwitch/slowtwitch.com/www/articles/static'
|
||||
},
|
||||
'library' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin'
|
||||
},
|
||||
'script' => {
|
||||
'admin' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin',
|
||||
'cgi' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles'
|
||||
},
|
||||
'static' => {
|
||||
'static' => '/var/home/slowtwitch/slowtwitch.com/www/articles/static'
|
||||
},
|
||||
'template' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/templates'
|
||||
},
|
||||
'version' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin'
|
||||
}
|
||||
},
|
||||
'revdeps' => [],
|
||||
'reversible' => '1',
|
||||
'severity' => '1',
|
||||
'title' => '"defined(%hash) is deprecated" warning',
|
||||
'unique' => '0'
|
||||
},
|
||||
'GT::Update::Update'
|
||||
),
|
||||
bless(
|
||||
{
|
||||
'deps' => [],
|
||||
'description' => 'This update fixes an issue with the template parser if there were extra spaces after the "and" in an "if" condition.',
|
||||
'files' => [
|
||||
[
|
||||
'195',
|
||||
'library',
|
||||
'',
|
||||
'0644',
|
||||
'GT/Template/Parser.pm'
|
||||
]
|
||||
],
|
||||
'id' => '138',
|
||||
'installed' => '1',
|
||||
'paths' => {
|
||||
'fixed' => {
|
||||
'build' => '/var/home/slowtwitch/slowtwitch.com/www',
|
||||
'cool' => '/var/home/slowtwitch/slowtwitch.com/www/Cool',
|
||||
'detail' => '/var/home/slowtwitch/slowtwitch.com/www',
|
||||
'new' => '/var/home/slowtwitch/slowtwitch.com/www/New',
|
||||
'ratings' => '/var/home/slowtwitch/slowtwitch.com/www/Ratings',
|
||||
'static' => '/var/home/slowtwitch/slowtwitch.com/www/articles/static'
|
||||
},
|
||||
'library' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin'
|
||||
},
|
||||
'script' => {
|
||||
'admin' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin',
|
||||
'cgi' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles'
|
||||
},
|
||||
'static' => {
|
||||
'static' => '/var/home/slowtwitch/slowtwitch.com/www/articles/static'
|
||||
},
|
||||
'template' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/templates'
|
||||
},
|
||||
'version' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin'
|
||||
}
|
||||
},
|
||||
'revdeps' => [],
|
||||
'reversible' => '1',
|
||||
'severity' => '1',
|
||||
'title' => 'Template parser syntax error',
|
||||
'unique' => '0'
|
||||
},
|
||||
'GT::Update::Update'
|
||||
),
|
||||
bless(
|
||||
{
|
||||
'deps' => [],
|
||||
'description' => 'This update fixes a bug where some files (eg. ones with parenthesis in them) uploaded to FILE columns would trigger an ILLEGALCHARS error.',
|
||||
'files' => [
|
||||
[
|
||||
'196',
|
||||
'library',
|
||||
'',
|
||||
'0644',
|
||||
'GT/SQL/File.pm'
|
||||
]
|
||||
],
|
||||
'id' => '139',
|
||||
'installed' => '1',
|
||||
'paths' => {
|
||||
'fixed' => {
|
||||
'build' => '/var/home/slowtwitch/slowtwitch.com/www',
|
||||
'cool' => '/var/home/slowtwitch/slowtwitch.com/www/Cool',
|
||||
'detail' => '/var/home/slowtwitch/slowtwitch.com/www',
|
||||
'new' => '/var/home/slowtwitch/slowtwitch.com/www/New',
|
||||
'ratings' => '/var/home/slowtwitch/slowtwitch.com/www/Ratings',
|
||||
'static' => '/var/home/slowtwitch/slowtwitch.com/www/articles/static'
|
||||
},
|
||||
'library' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin'
|
||||
},
|
||||
'script' => {
|
||||
'admin' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin',
|
||||
'cgi' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles'
|
||||
},
|
||||
'static' => {
|
||||
'static' => '/var/home/slowtwitch/slowtwitch.com/www/articles/static'
|
||||
},
|
||||
'template' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/templates'
|
||||
},
|
||||
'version' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin'
|
||||
}
|
||||
},
|
||||
'revdeps' => [],
|
||||
'reversible' => '1',
|
||||
'severity' => '1',
|
||||
'title' => 'Filename errors with uploaded files',
|
||||
'unique' => '0'
|
||||
},
|
||||
'GT::Update::Update'
|
||||
),
|
||||
bless(
|
||||
{
|
||||
'deps' => [
|
||||
'135'
|
||||
],
|
||||
'description' => 'This update fixes a bug in the last category browser update where deleting a link that\'s in multiple categories deletes the link from all categories.',
|
||||
'files' => [
|
||||
[
|
||||
'197',
|
||||
'library',
|
||||
'',
|
||||
'0644',
|
||||
'Links/Browser.pm'
|
||||
]
|
||||
],
|
||||
'id' => '140',
|
||||
'installed' => '1',
|
||||
'paths' => {
|
||||
'fixed' => {
|
||||
'build' => '/var/home/slowtwitch/slowtwitch.com/www',
|
||||
'cool' => '/var/home/slowtwitch/slowtwitch.com/www/Cool',
|
||||
'detail' => '/var/home/slowtwitch/slowtwitch.com/www',
|
||||
'new' => '/var/home/slowtwitch/slowtwitch.com/www/New',
|
||||
'ratings' => '/var/home/slowtwitch/slowtwitch.com/www/Ratings',
|
||||
'static' => '/var/home/slowtwitch/slowtwitch.com/www/articles/static'
|
||||
},
|
||||
'library' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin'
|
||||
},
|
||||
'script' => {
|
||||
'admin' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin',
|
||||
'cgi' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles'
|
||||
},
|
||||
'static' => {
|
||||
'static' => '/var/home/slowtwitch/slowtwitch.com/www/articles/static'
|
||||
},
|
||||
'template' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/templates'
|
||||
},
|
||||
'version' => {
|
||||
'' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin'
|
||||
}
|
||||
},
|
||||
'revdeps' => [],
|
||||
'reversible' => '1',
|
||||
'severity' => '2',
|
||||
'title' => 'Category Browser fixes fix',
|
||||
'unique' => '0'
|
||||
},
|
||||
'GT::Update::Update'
|
||||
)
|
||||
],
|
||||
'version' => '1.1'
|
||||
},
|
||||
'installed' => {
|
||||
'3.3.0' => {
|
||||
'132' => {
|
||||
'deps' => [],
|
||||
'description' => 'This update fixes a bug in GT::WWW where it would cause link verify to fail with a -4 (Could not connect) error.',
|
||||
'files' => [
|
||||
{
|
||||
'backup' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/updates/132-1254088697-library--GT-WWW.pm.backup',
|
||||
'dir' => '',
|
||||
'file' => 'GT/WWW.pm',
|
||||
'id' => '186',
|
||||
'mode' => '0644',
|
||||
'path' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/GT/WWW.pm',
|
||||
'subtype' => '',
|
||||
'type' => 'library'
|
||||
}
|
||||
],
|
||||
'installed' => '1254088697',
|
||||
'reversible' => '1',
|
||||
'severity' => '2',
|
||||
'title' => 'GT::WWW unresolvable host error fix',
|
||||
'unique' => '0'
|
||||
},
|
||||
'133' => {
|
||||
'deps' => [],
|
||||
'description' => 'This update fixes an error where editors using browser.cgi get an error, "You are not authorized to perform this action," when attempting to modify a link that they should be able to modify.',
|
||||
'files' => [
|
||||
{
|
||||
'backup' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/updates/133-1254088697-library--Links-Browser-Controller.pm.backup',
|
||||
'dir' => '',
|
||||
'file' => 'Links/Browser/Controller.pm',
|
||||
'id' => '187',
|
||||
'mode' => '0644',
|
||||
'path' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/Links/Browser/Controller.pm',
|
||||
'subtype' => '',
|
||||
'type' => 'library'
|
||||
}
|
||||
],
|
||||
'installed' => '1254088697',
|
||||
'reversible' => '1',
|
||||
'severity' => '1',
|
||||
'title' => 'Editor browser error on link modify',
|
||||
'unique' => '0'
|
||||
},
|
||||
'134' => {
|
||||
'deps' => [],
|
||||
'description' => 'This update fixes "ColumnName cannot contain the value \'\'" error when attempting to modify a link with an INT column (null = yes, no default).',
|
||||
'files' => [
|
||||
{
|
||||
'backup' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/updates/134-1362433281-library--Links-User-Modify.pm.backup',
|
||||
'dir' => '',
|
||||
'file' => 'Links/User/Modify.pm',
|
||||
'id' => '188',
|
||||
'mode' => '0644',
|
||||
'path' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/Links/User/Modify.pm',
|
||||
'subtype' => '',
|
||||
'type' => 'library'
|
||||
}
|
||||
],
|
||||
'installed' => '1362433281',
|
||||
'reversible' => '1',
|
||||
'severity' => '1',
|
||||
'title' => 'INT validation error on modify',
|
||||
'unique' => '0'
|
||||
},
|
||||
'135' => {
|
||||
'deps' => [],
|
||||
'description' => 'This update fixes two bugs in the category browser: 1) Error deleting a link on the user side category browser (ie. not the admin one) from search results when the category depth is greater than 1. 2) When the link_validate_date option was turned on, Add_Date was getting set to the current date on modify.',
|
||||
'files' => [
|
||||
{
|
||||
'backup' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/updates/135-1362433281-library--Links-Browser.pm.backup',
|
||||
'dir' => '',
|
||||
'file' => 'Links/Browser.pm',
|
||||
'id' => '189',
|
||||
'mode' => '0644',
|
||||
'path' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/Links/Browser.pm',
|
||||
'subtype' => '',
|
||||
'type' => 'library'
|
||||
}
|
||||
],
|
||||
'installed' => '1362433281',
|
||||
'reversible' => '1',
|
||||
'severity' => '1',
|
||||
'title' => 'Category Browser fixes',
|
||||
'unique' => '0'
|
||||
},
|
||||
'136' => {
|
||||
'deps' => [],
|
||||
'description' => 'This update fixes a bug in the payment code where the name and e-mail address of the user aren\'t correctly passed to the link_expired.eml and link_expiry_notify.eml e-mail templates.',
|
||||
'files' => [
|
||||
{
|
||||
'backup' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/updates/136-1362433281-library--Links-Payment.pm.backup',
|
||||
'dir' => '',
|
||||
'file' => 'Links/Payment.pm',
|
||||
'id' => '190',
|
||||
'mode' => '0644',
|
||||
'path' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/Links/Payment.pm',
|
||||
'subtype' => '',
|
||||
'type' => 'library'
|
||||
}
|
||||
],
|
||||
'installed' => '1362433281',
|
||||
'reversible' => '1',
|
||||
'severity' => '1',
|
||||
'title' => 'Broken payment link expiry e-mails',
|
||||
'unique' => '0'
|
||||
},
|
||||
'137' => {
|
||||
'deps' => [],
|
||||
'description' => 'This update fixes the "defined(%hash) is deprecated" warnings that occur when using Gossamer Links with Perl 5.12 and higher.',
|
||||
'files' => [
|
||||
{
|
||||
'backup' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/updates/137-1362433281-library--bases.pm.backup',
|
||||
'dir' => '',
|
||||
'file' => 'bases.pm',
|
||||
'id' => '191',
|
||||
'mode' => '0644',
|
||||
'path' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/bases.pm',
|
||||
'subtype' => '',
|
||||
'type' => 'library'
|
||||
},
|
||||
{
|
||||
'backup' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/updates/137-1362433281-library--GT-MIMETypes.pm.backup',
|
||||
'dir' => '',
|
||||
'file' => 'GT/MIMETypes.pm',
|
||||
'id' => '192',
|
||||
'mode' => '0644',
|
||||
'path' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/GT/MIMETypes.pm',
|
||||
'subtype' => '',
|
||||
'type' => 'library'
|
||||
},
|
||||
{
|
||||
'backup' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/updates/137-1362433281-library--GT-SQL-Base.pm.backup',
|
||||
'dir' => '',
|
||||
'file' => 'GT/SQL/Base.pm',
|
||||
'id' => '194',
|
||||
'mode' => '0644',
|
||||
'path' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/GT/SQL/Base.pm',
|
||||
'subtype' => '',
|
||||
'type' => 'library'
|
||||
},
|
||||
{
|
||||
'backup' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/updates/137-1362433281-library--GT-Template.pm.backup',
|
||||
'dir' => '',
|
||||
'file' => 'GT/Template.pm',
|
||||
'id' => '193',
|
||||
'mode' => '0644',
|
||||
'path' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/GT/Template.pm',
|
||||
'subtype' => '',
|
||||
'type' => 'library'
|
||||
}
|
||||
],
|
||||
'installed' => '1362433281',
|
||||
'reversible' => '1',
|
||||
'severity' => '1',
|
||||
'title' => '"defined(%hash) is deprecated" warning',
|
||||
'unique' => '0'
|
||||
},
|
||||
'138' => {
|
||||
'deps' => [],
|
||||
'description' => 'This update fixes an issue with the template parser if there were extra spaces after the "and" in an "if" condition.',
|
||||
'files' => [
|
||||
{
|
||||
'backup' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/updates/138-1362433281-library--GT-Template-Parser.pm.backup',
|
||||
'dir' => '',
|
||||
'file' => 'GT/Template/Parser.pm',
|
||||
'id' => '195',
|
||||
'mode' => '0644',
|
||||
'path' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/GT/Template/Parser.pm',
|
||||
'subtype' => '',
|
||||
'type' => 'library'
|
||||
}
|
||||
],
|
||||
'installed' => '1362433281',
|
||||
'reversible' => '1',
|
||||
'severity' => '1',
|
||||
'title' => 'Template parser syntax error',
|
||||
'unique' => '0'
|
||||
},
|
||||
'139' => {
|
||||
'deps' => [],
|
||||
'description' => 'This update fixes a bug where some files (eg. ones with parenthesis in them) uploaded to FILE columns would trigger an ILLEGALCHARS error.',
|
||||
'files' => [
|
||||
{
|
||||
'backup' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/updates/139-1362433281-library--GT-SQL-File.pm.backup',
|
||||
'dir' => '',
|
||||
'file' => 'GT/SQL/File.pm',
|
||||
'id' => '196',
|
||||
'mode' => '0644',
|
||||
'path' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/GT/SQL/File.pm',
|
||||
'subtype' => '',
|
||||
'type' => 'library'
|
||||
}
|
||||
],
|
||||
'installed' => '1362433281',
|
||||
'reversible' => '1',
|
||||
'severity' => '1',
|
||||
'title' => 'Filename errors with uploaded files',
|
||||
'unique' => '0'
|
||||
},
|
||||
'140' => {
|
||||
'deps' => [
|
||||
'135'
|
||||
],
|
||||
'description' => 'This update fixes a bug in the last category browser update where deleting a link that\'s in multiple categories deletes the link from all categories.',
|
||||
'files' => [
|
||||
{
|
||||
'backup' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/updates/140-1362433281-library--Links-Browser.pm.backup',
|
||||
'dir' => '',
|
||||
'file' => 'Links/Browser.pm',
|
||||
'id' => '197',
|
||||
'mode' => '0644',
|
||||
'path' => '/var/home/slowtwitch/slowtwitch.com/cgi-bin/articles/admin/Links/Browser.pm',
|
||||
'subtype' => '',
|
||||
'type' => 'library'
|
||||
}
|
||||
],
|
||||
'installed' => '1362433281',
|
||||
'reversible' => '1',
|
||||
'severity' => '2',
|
||||
'title' => 'Category Browser fixes fix',
|
||||
'unique' => '0'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
# vim:syn=perl:ts=4:noet
|
||||
30
site/slowtwitch.com/cgi-bin/articles/admin/Links/Custom.pm
Normal file
30
site/slowtwitch.com/cgi-bin/articles/admin/Links/Custom.pm
Normal file
@@ -0,0 +1,30 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,068,085,094,083
|
||||
# Revision : $Id: Custom.pm,v 1.3 2005/03/05 01:29:09 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
#
|
||||
# Description:
|
||||
# By default, this file is empty, however it is here to allow installations
|
||||
# to perform special operations required to make Gossamer Links load.
|
||||
# For example, some installations might need a 'use lib' line to work
|
||||
# properly.
|
||||
#
|
||||
# This file will NOT be overwritten when upgrading your installation, so you
|
||||
# do not need to worry about additions made here being overwritten. This is
|
||||
# generally loaded after Links.pm has started loading, but before any other
|
||||
# modules are loaded.
|
||||
#
|
||||
package Links::Custom;
|
||||
|
||||
|
||||
|
||||
|
||||
1; # This must remain at the bottom of the file.
|
||||
@@ -0,0 +1,170 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Category.pm,v 1.14 2007/09/25 06:19:32 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::HTML::Category;
|
||||
# ==================================================================
|
||||
# Handles displaying of forms and HTML.
|
||||
#
|
||||
use strict;
|
||||
use vars qw/@ISA/;
|
||||
use Links qw/:payment :objects/;
|
||||
use GT::SQL::Display::HTML::Table;
|
||||
|
||||
@ISA = qw/GT::SQL::Display::HTML::Table/;
|
||||
|
||||
my $FORM_HIDE = 'add_form|modify_form|modify_multi_search_results|modify_multi_result_changed|modify_multi_results_err';
|
||||
my $FORM_HIDE_FIELDS = [qw/Full_Name Number_of_Links Direct_Links Has_New_Links Has_Changed_Links Newest_Link Timestmp/];
|
||||
|
||||
sub display {
|
||||
# -------------------------------------------------------------------
|
||||
# Displays a category, but passes through the plugin system.
|
||||
#
|
||||
my $self = shift;
|
||||
my $p = (ref $_[0] eq 'HASH') ? shift : {@_};
|
||||
|
||||
$PLG->dispatch('display_category', sub { return $self->_plg_display(@_); }, $p);
|
||||
}
|
||||
|
||||
sub form {
|
||||
# -------------------------------------------------------------------
|
||||
# Displays a category form, but passes through the plugin system.
|
||||
#
|
||||
my $self = shift;
|
||||
my $p = (ref $_[0] eq 'HASH') ? shift : {@_};
|
||||
|
||||
$PLG->dispatch('form_category', sub { return $self->_plg_form(@_); }, $p);
|
||||
}
|
||||
|
||||
sub _plg_display {
|
||||
# -------------------------------------------------------------------
|
||||
# Displays a record.
|
||||
#
|
||||
my ($self, $opts) = @_;
|
||||
$opts->{hide} ||= [];
|
||||
|
||||
if (!exists $opts->{code}->{FatherID} and !grep { $_ eq 'FatherID' } @{$opts->{hide}}) {
|
||||
$opts->{code}->{FatherID} = \&disp_fatherid_html
|
||||
}
|
||||
|
||||
push @{$opts->{hide}}, qw/Full_Name/;
|
||||
$CFG->{payment}->{enabled} or push @{$opts->{hide}}, 'Payment_Mode';
|
||||
|
||||
my $out = $self->SUPER::display($opts);
|
||||
|
||||
my $id = $opts->{values}->{ID};
|
||||
if ($CFG->{payment}->{enabled} and $id and $opts->{values}->{Payment_Mode} >= OPTIONAL) {
|
||||
my $font = $self->{font};
|
||||
$out .= qq~
|
||||
<p><table border=1 cellpadding=0 bgcolor="#FFFFFF" cellspacing=0 width="500"><tr><td>
|
||||
<table border=0 bgcolor="#FFFFFF" width="500"><tr>
|
||||
<td width="100%" valign="top" align="center"><font $font><a href="admin.cgi?do=page;page=payment_cat_price.html;ID=$id;not_global=1">Add/Update payment terms for this category</a></td>
|
||||
</tr></table>
|
||||
</td></tr></table>
|
||||
~;
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
sub _plg_form {
|
||||
# -------------------------------------------------------------------
|
||||
# Displays a form.
|
||||
#
|
||||
my ($self, $opts) = @_;
|
||||
$opts->{hide} ||= [];
|
||||
|
||||
if ($opts->{mode} and $opts->{mode} =~ /$FORM_HIDE/o) {
|
||||
push @{$opts->{hide}}, @{$FORM_HIDE_FIELDS};
|
||||
}
|
||||
|
||||
if (!exists $opts->{code}->{FatherID} and !grep { $_ eq 'FatherID' } @{$opts->{hide}}) {
|
||||
$opts->{code}->{FatherID} = \&disp_fatherid_form;
|
||||
}
|
||||
|
||||
$CFG->{payment}->{enabled} or push @{$opts->{hide}}, 'Payment_Mode';
|
||||
|
||||
return $self->SUPER::form($opts);
|
||||
}
|
||||
|
||||
sub select {
|
||||
# -------------------------------------------------------------------
|
||||
# Override Payment_Mode select field in add form.
|
||||
#
|
||||
my ($self, $opts) = @_;
|
||||
$CFG->{payment}->{enabled} and $opts->{name} eq 'Payment_Mode'
|
||||
and $self->{input}->{do} and ($self->{input}->{do} eq 'add_form' or $self->{input}->{do} eq 'modify_form')
|
||||
and $opts->{blank} = 0;
|
||||
return $self->SUPER::select($opts);
|
||||
}
|
||||
|
||||
sub disp_fatherid_form {
|
||||
# -------------------------------------------------------------------
|
||||
# Display the list of subcategories as either a drop down list of a text box.
|
||||
#
|
||||
my ($self, $col, $rec) = @_;
|
||||
my $font = $self->{font};
|
||||
my $out;
|
||||
my $form_name = $self->{multiple} ? "$self->{multiple}-FatherID" : 'FatherID';
|
||||
|
||||
if ($CFG->{db_gen_category_list} == 2) {
|
||||
if ($rec->{FatherID}) {
|
||||
$out .= qq|<input type="hidden" name="FatherID" value="$rec->{FatherID}" />|;
|
||||
}
|
||||
$out .= qq|<script type="text/javascript" src="$CFG->{build_static_url}/treecats.js"></script>
|
||||
<link type="text/css" rel="stylesheet" href="$CFG->{build_static_url}/admin/treecats.css" />
|
||||
<input type="hidden" name="FatherID-opt" value="=" />
|
||||
<tr><td valign="top"><font $font>Subcategory of</font></td><td><font $font><div id="treecats"></div></font></td></tr>
|
||||
<script type="text/javascript">var tc = new treecats({ selectionRequired : false, inputName : 'FatherID', cgiURL : '$CFG->{db_cgi_url}', imageURL : '$CFG->{build_static_url}/admin' }, { noSelection : 'Root', rootText : 'Root' }); tc.load();</script>\n|;
|
||||
}
|
||||
elsif ($CFG->{db_gen_category_list}) {
|
||||
my $sth = $self->{db}->select(["DISTINCT Full_Name, ID"]);
|
||||
my %names;
|
||||
if ($sth) {
|
||||
while (my ($name, $id) = $sth->fetchrow_array) {
|
||||
$names{$id} = $name;
|
||||
}
|
||||
}
|
||||
$names{0} = '--Root--';
|
||||
my $select = $self->select({ name => $form_name, values => \%names, blank => 1, sort => sub { lc $_[0] cmp lc $_[1] }, value => defined $rec->{FatherID} ? $rec->{FatherID} : "" });
|
||||
$out = qq~
|
||||
<tr><td valign=top><font $font>Subcategory of</font></td><td><font $font>$select<input type=hidden name="FatherID-opt" value="="></td></tr>
|
||||
~;
|
||||
}
|
||||
else {
|
||||
my $value = $rec->{FatherID} || '';
|
||||
if ($value =~ /^\d+$/) {
|
||||
my $sth = $self->{db}->select('Full_Name', { ID => $value });
|
||||
if ($sth) {
|
||||
($value) = $sth->fetchrow_array;
|
||||
}
|
||||
}
|
||||
$out = qq~
|
||||
<tr><td><font $font>Full Sub Category<br><font size=1>Separated with /'s</font></font></td><td><input type=text size="40" name="$form_name" value="$value"></td></tr>
|
||||
~;
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
sub disp_fatherid_html {
|
||||
# -------------------------------------------------------------------
|
||||
# Display the father.
|
||||
#
|
||||
my ($self, $col, $rec) = @_;
|
||||
my ($parent) = $rec->{Full_Name} =~ m,^(.*)/[^/]+$,;
|
||||
my $font = $self->{font};
|
||||
$parent ||= 'Root';
|
||||
return qq~
|
||||
<tr><td><font $font>Subcategory of</font></td><td><font $font>$parent</td></tr>
|
||||
~;
|
||||
}
|
||||
|
||||
1;
|
||||
409
site/slowtwitch.com/cgi-bin/articles/admin/Links/HTML/Links.pm
Normal file
409
site/slowtwitch.com/cgi-bin/articles/admin/Links/HTML/Links.pm
Normal file
@@ -0,0 +1,409 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Links.pm,v 1.25 2007/11/14 02:40:26 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::HTML::Links;
|
||||
# ==================================================================
|
||||
# Handles displaying of forms and HTML.
|
||||
#
|
||||
use strict;
|
||||
use vars qw/@ISA/;
|
||||
use GT::SQL::Display::HTML::Table;
|
||||
use Links qw/:payment :objects/;
|
||||
|
||||
@ISA = qw/GT::SQL::Display::HTML::Table/;
|
||||
|
||||
my $FORM_HIDE = '^(add_form|modify_form|modify_multi_search_results|modify_multi_result_changed|modify_multi_results_err|validate)$';
|
||||
my $FORM_HIDE_FIELDS = [qw/isNew isChanged isPopular Status Date_Checked/];
|
||||
my $SHOW_CAT_LIST = '^(search_results|add_success|delete_search_results|modify_search_results|modify_success|modify_multi_search_results|modify_multi_results_norec)$';
|
||||
my $SHOW_CAT_FORM = '^(search_form|add_form|delete_search_form|modify_form|modify_search_form|modify_multi_search_results|modify_multi_result_changed|modify_multi_results_err|validate)$';
|
||||
|
||||
sub display {
|
||||
# -------------------------------------------------------------------
|
||||
# Displays a link, but passes through the plugin system.
|
||||
#
|
||||
my $self = shift;
|
||||
my $p = (ref $_[0] eq 'HASH') ? shift : {@_};
|
||||
|
||||
$PLG->dispatch('display_link', sub { return $self->_plg_display (@_); }, $p );
|
||||
}
|
||||
|
||||
sub form {
|
||||
# -------------------------------------------------------------------
|
||||
# Displays a link form, but passes through the plugin system.
|
||||
#
|
||||
my $self = shift;
|
||||
my $p = (ref $_[0] eq 'HASH') ? shift : {@_};
|
||||
|
||||
$PLG->dispatch('form_link', sub { return $self->_plg_form (@_); }, $p );
|
||||
}
|
||||
|
||||
sub _plg_display {
|
||||
# -------------------------------------------------------------------
|
||||
# Displays a record.
|
||||
#
|
||||
my ($self, $opts) = @_;
|
||||
$opts->{code}->{LinkOwner} ||= \&disp_username;
|
||||
$opts->{code}->{ExpiryDate} ||= \&disp_expiry;
|
||||
|
||||
my $hidden = sub { '' };
|
||||
$opts->{code}->{ExpiryCounted} ||= $hidden;
|
||||
$opts->{code}->{ExpiryNotify} ||= $hidden;
|
||||
$opts->{code}->{LinkExpired} ||= $hidden;
|
||||
|
||||
my $out = $self->SUPER::display($opts);
|
||||
if ($opts->{mode} =~ /$SHOW_CAT_LIST/o) {
|
||||
my $id = $opts->{values}->{ID};
|
||||
if ($id) {
|
||||
my $font = $self->{font};
|
||||
my $output = $self->disp_categories($id);
|
||||
$out .= qq~
|
||||
<p><table border=1 cellpadding=0 bgcolor="#FFFFFF" cellspacing=0 width="500"><tr><td>
|
||||
<table border=0 bgcolor="#FFFFFF" width="500"><tr>
|
||||
<td width="20%" valign="top"><font $font>Categories</td>
|
||||
<td width="80%"><font $font>$output</td>
|
||||
</tr></table>
|
||||
</td></tr></table>
|
||||
~;
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
sub _plg_form {
|
||||
# -------------------------------------------------------------------
|
||||
# Displays a form.
|
||||
#
|
||||
my ($self, $opts) = @_;
|
||||
|
||||
my $link_id = $opts->{values}->{ID} || $self->{input}->{ID};
|
||||
|
||||
# Hide fields we don't want to show on add/modify forms.
|
||||
if ($opts->{mode} and $opts->{mode} =~ /$FORM_HIDE/o) {
|
||||
$opts->{hide} ||= [];
|
||||
push @{$opts->{hide}}, @{$FORM_HIDE_FIELDS};
|
||||
}
|
||||
|
||||
$opts->{code}->{ExpiryDate} ||= \&form_expiry;
|
||||
|
||||
# Add javascript to display the original values for text/textarea columns
|
||||
if ($opts->{show_diff} and $link_id) {
|
||||
my $current = $DB->table('Links')->select({ ID => $link_id })->fetchrow_hashref;
|
||||
my $cols = $DB->table('Links')->cols;
|
||||
my $textarea = sub {
|
||||
my ($self, $opts, $values, $col) = @_;
|
||||
|
||||
my $field_name = $self->{multiple} ? "$self->{multiple}-$col" : $col;
|
||||
my $display_name = (exists $self->{cols}->{$col}->{form_display} and length $self->{cols}->{$col}->{form_display})
|
||||
? $self->{cols}->{$col}->{form_display} : $col;
|
||||
my $value = $values->{$col};
|
||||
my $disp = $opts->{form_type} eq 'TEXT' ? 'text' : 'textarea';
|
||||
|
||||
my $ret = qq|<tr $self->{tr}><td $self->{td} width='30%'><font $self->{col_font}><a href="javascript:toggleOriginal('$field_name-original')" title="Show/Hide original $display_name value">$display_name</a></font></td><td $self->{td} width='70%'><font $self->{val_font}>|;
|
||||
$ret .= $self->$disp({ name => $field_name, def => $opts, value => (defined $value ? $value : '')});
|
||||
$ret .= qq|</font></td></tr>\n<tr id="$field_name-original" style="display: none" $self->{tr}><td $self->{td} width="30%"><font $self->{col_font}>Original $display_name</font></td><td $self->{td} width="70%"><font $self->{val_font}>|;
|
||||
if ($opts->{form_type} eq 'TEXT') {
|
||||
$ret .= qq|<input type="text" value="$current->{$col}" size="| . ($opts->{form_size} || 20) . qq|" readonly="readonly" />|;
|
||||
}
|
||||
else {
|
||||
my ($cols, $rows) = ref $opts->{form_size} ? (@{$opts->{form_size}}) : ($opts->{form_size} || 20, 4);
|
||||
$ret .= qq|<textarea rows="$rows" cols="$cols" readonly="readonly">$current->{$col}</textarea>|
|
||||
}
|
||||
$ret .= "</font></td></tr>\n";
|
||||
};
|
||||
|
||||
COL: for my $col (keys %$current) {
|
||||
next if !$cols->{$col}->{form_type} or ($cols->{$col}->{form_type} ne 'TEXT' and $cols->{$col}->{form_type} ne 'TEXTAREA');
|
||||
# Skip hidden fields
|
||||
for (@{$opts->{hide}}) {
|
||||
next COL if $_ eq $col;
|
||||
}
|
||||
|
||||
if ((not defined $opts->{values}->{$col} or $current->{$col} ne $opts->{values}->{$col}) and not $opts->{code}->{$col}) {
|
||||
$opts->{code}->{$col} = $textarea;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Display the form.
|
||||
my $out = $self->SUPER::form($opts);
|
||||
|
||||
# Display the category select box.
|
||||
if ($opts->{mode} and $opts->{mode} =~ /$SHOW_CAT_FORM/o) {
|
||||
my $name = $opts->{multiple} ? "$opts->{multiple}-CatLinks.CategoryID" : 'CatLinks.CategoryID';
|
||||
my $id = $opts->{values}->{$name} || $self->{input}->{$name};
|
||||
$id = (ref $id eq 'ARRAY') ? $id : $id ? [$id] : [];
|
||||
my $font = $self->{font};
|
||||
my ($output, $h);
|
||||
|
||||
# Add javascript to display the original categories
|
||||
my $cats_modified;
|
||||
if ($opts->{show_diff} and @$id and $link_id) {
|
||||
my $ccl = $DB->table('Category', 'CatLinks');
|
||||
$ccl->select_options("ORDER BY CategoryID");
|
||||
my $sth = $ccl->select('CategoryID', 'Full_Name', { LinkID => $link_id });
|
||||
my (@cid, @cats);
|
||||
while (my $cat = $sth->fetchrow_hashref) {
|
||||
push @cid, $cat->{CategoryID};
|
||||
push @cats, $cat->{Full_Name};
|
||||
}
|
||||
if (@$id == @cid) {
|
||||
my @sorted = sort { $a > $b } @$id;
|
||||
for (my $i = 0; $i < @cid; $i++) {
|
||||
if ($cid[$i] != $sorted[$i]) {
|
||||
$cats_modified = join "\n", sort @cats;
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$cats_modified = join "\n", sort @cats;
|
||||
}
|
||||
}
|
||||
|
||||
# Display the category using treecats
|
||||
if ($CFG->{db_gen_category_list} == 2) {
|
||||
my $name = $opts->{multiple} ? "$opts->{multiple}-CatLinks.CategoryID" : 'CatLinks.CategoryID';
|
||||
my $jsname = $opts->{multiple} ? "tc$opts->{multiple}" : 'tc';
|
||||
if (!@$id and $link_id) {
|
||||
$h = $self->{db}->get_categories($link_id);
|
||||
for (keys %$h) {
|
||||
push @$id, $_;
|
||||
}
|
||||
}
|
||||
$out .= qq~<p><table border=1 cellpadding=0 bgcolor="#FFFFFF" cellspacing=0><tr><td>~ unless exists $opts->{extra_table} and $opts->{extra_table} == 0;
|
||||
$out .= qq~
|
||||
<table border=0 bgcolor="#FFFFFF" width="500"><tr>
|
||||
<td width="30%" valign="top"><font $font>~;
|
||||
$out .= qq|<a href="javascript:toggleOriginal('$name-original')" title="Show/Hide original Category value">| if $cats_modified;
|
||||
$out .= "Categories";
|
||||
$out .= qq|</a>| if $cats_modified;
|
||||
for (@$id) {
|
||||
$out .= qq|<input type="hidden" name="$name" value="$_" />|;
|
||||
}
|
||||
$out .= qq~</td>
|
||||
<td>
|
||||
<script type="text/javascript" src="$CFG->{build_static_url}/treecats.js"></script>
|
||||
<link type="text/css" rel="stylesheet" href="$CFG->{build_static_url}/admin/treecats.css" />
|
||||
<font $font><div id="$jsname"></div></font>
|
||||
<script type="text/javascript">var $jsname = new treecats({ workspace : '$jsname', objName : '$jsname', inputName : '$name', selectionMode : 'multiple', cgiURL : '$CFG->{db_cgi_url}', imageURL : '$CFG->{build_static_url}/admin' }); $jsname.load();</script>
|
||||
</td>
|
||||
</tr></table>
|
||||
~;
|
||||
$out .= qq~</p></td></tr></table>~ unless exists $opts->{extra_table} and $opts->{extra_table} == 0;
|
||||
}
|
||||
# Display category as a select box.
|
||||
elsif ($CFG->{db_gen_category_list}) {
|
||||
if (!@$id and $link_id) {
|
||||
$h = $self->{db}->get_categories($link_id);
|
||||
$output = $self->get_categories_with_html([keys %$h], $name);
|
||||
}
|
||||
else {
|
||||
$output = $self->get_categories_with_html($id, $name);
|
||||
}
|
||||
$out .= "<p>";
|
||||
$out .= qq~<table border=1 cellpadding=0 bgcolor="#FFFFFF" cellspacing=0><tr><td>~ unless exists $opts->{extra_table} and $opts->{extra_table} == 0;
|
||||
$out .= qq~
|
||||
<table border=0 bgcolor="#FFFFFF" width="500"><tr>
|
||||
<td width="20%" valign="top"><font $font>~;
|
||||
$out .= qq|<a href="javascript:toggleOriginal('$name-original')" title="Show/Hide original Category value">| if $cats_modified;
|
||||
$out .= "Categories";
|
||||
$out .= qq|</a>| if $cats_modified;
|
||||
$out .= qq~</td>
|
||||
<td width="80%"><font $font>$output</td>
|
||||
</tr></table>
|
||||
~;
|
||||
$out .= qq~</td></tr></table>~ unless exists $opts->{extra_table} and $opts->{extra_table} == 0;
|
||||
}
|
||||
# Display category as a textarea box.
|
||||
else {
|
||||
my ($vals);
|
||||
if (@$id) {
|
||||
my $db = $DB->table('Category');
|
||||
foreach (@$id) {
|
||||
if (/^\d+$/) {
|
||||
$vals .= $db->get_name_from_id($_) . "\n";
|
||||
}
|
||||
else {
|
||||
$vals .= $_ . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
elsif ($link_id) {
|
||||
$h = $self->{db}->get_categories($link_id);
|
||||
$vals = join("\n", sort values %$h);
|
||||
}
|
||||
else {
|
||||
$vals = '';
|
||||
}
|
||||
$out .= "<p>";
|
||||
$out .= qq~<table border=1 cellpadding=0 bgcolor="#FFFFFF" cellspacing=0><tr><td>~ unless exists $opts->{extra_table} and $opts->{extra_table} == 0;
|
||||
$out .= qq~
|
||||
<table border=0 bgcolor="#FFFFFF" width="500"><tr>
|
||||
<td width="20%" valign="top"><font $font>~;
|
||||
$out .= qq|<a href="javascript:toggleOriginal('$name-original')" title="Show/Hide original Category value">| if $cats_modified;
|
||||
$out .= "Categories";
|
||||
$out .= qq|</a>| if $cats_modified;
|
||||
$out .= qq~<br><font size=1>One per line</font></td>
|
||||
<td width="80%"><font $font><textarea rows="3" cols="50" name="$name">$vals</textarea></td>
|
||||
</tr></table>
|
||||
~;
|
||||
$out .= qq~</td></tr></table>~ unless exists $opts->{extra_table} and $opts->{extra_table} == 0;
|
||||
|
||||
}
|
||||
|
||||
if ($cats_modified) {
|
||||
$out .= qq~
|
||||
<table border=0 bgcolor="#FFFFFF" width="500" id="$name-original" style="display: none"><tr>
|
||||
<td width="20%" valign="top"><font $font>Original Categories</font></td>
|
||||
<td width="80%"><font $font><textarea rows="3" cols="50" readonly="readonly">$cats_modified</textarea></td>
|
||||
</tr></table>
|
||||
~;
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
sub disp_username {
|
||||
# -------------------------------------------------------------------
|
||||
# Display the username with links to edit.
|
||||
#
|
||||
my ($self, $col, $rec) = @_;
|
||||
my $val = $rec->{LinkOwner};
|
||||
my $val_e = GT::CGI->escape($val);
|
||||
my $font = $self->{font};
|
||||
return qq~
|
||||
<tr><td><font $font>$col->{form_display}</font></td><td><font $font>$val <font size=1><a href="admin.cgi?db=Users&do=modify_form&modify=1&1-Username=$val_e&ww=1">edit</a></font></font></td></tr>
|
||||
~;
|
||||
}
|
||||
|
||||
sub disp_categories {
|
||||
# -------------------------------------------------------------------
|
||||
# Displays a list of categories for the form.
|
||||
#
|
||||
my $self = shift;
|
||||
my $id = shift;
|
||||
my $cat = $self->{db}->get_categories ($id);
|
||||
my $out = '';
|
||||
foreach my $id (sort { lc $cat->{$a} cmp lc $cat->{$b} } keys %$cat) {
|
||||
$out .= "$id: $cat->{$id}<br>\n";
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
sub disp_expiry {
|
||||
# -------------------------------------------------------------------
|
||||
#
|
||||
my ($self, $col, $rec) = @_;
|
||||
my $val = $rec->{ExpiryDate};
|
||||
my $name = $col->{form_display};
|
||||
my $font = $self->{font};
|
||||
my $td = $self->{td};
|
||||
|
||||
my $out = qq|<tr><td $td><font $font>$name</font></td><td $td><font $font>|;
|
||||
if ($val == UNLIMITED) {
|
||||
$out .= "<i>Never</i>";
|
||||
}
|
||||
elsif ($val == UNPAID) {
|
||||
$out .= "<i>Awaiting Payment</i>";
|
||||
}
|
||||
elsif ($val == FREE) {
|
||||
$out .= "<i>No Payment Required (free)";
|
||||
if ($rec->{LinkExpired}) {
|
||||
require GT::Date;
|
||||
$out .= " - Payment Expired " . GT::Date::date_get($rec->{LinkExpired}, '%yyyy%/%m%/%d% %HH%:%MM%:%ss%');
|
||||
}
|
||||
$out .= "</i>";
|
||||
}
|
||||
elsif ($val == 0) {
|
||||
$out .= "<i>Invalid Date (0)!</i>";
|
||||
}
|
||||
else {
|
||||
require GT::Date;
|
||||
$out .= GT::Date::date_get($val, '%yyyy%/%m%/%d% %HH%:%MM%:%ss%');
|
||||
}
|
||||
$out .= qq|</font></td>|;
|
||||
return $out;
|
||||
}
|
||||
|
||||
sub form_expiry {
|
||||
# -------------------------------------------------------------------
|
||||
#
|
||||
my ($self, $col, $rec) = @_;
|
||||
require GT::Date;
|
||||
my $val = $rec->{ExpiryDate};
|
||||
my $name = $col->{form_display};
|
||||
my $font = $self->{font};
|
||||
my $td = $self->{td};
|
||||
my $got_date = $val && $val < UNLIMITED && $val > 0;
|
||||
($got_date and $val !~ m|^\d+$|) and $val = Links::date_to_time($val);
|
||||
my $multiple = $self->{multiple} ? "$self->{multiple}-" : '';
|
||||
$name .= '<br><i><font size=-2>Dates can be entered in the following formats: YYYY-MM-DD, YYYY/MM/DD, YYYY/MM/DD HH:MM:SS</font></i>';
|
||||
my $out = qq|<tr><td $td><font $font>$name</font></td><td $td><font $font><input type="hidden" name="${multiple}ExpiryDate" value="$val" id="${multiple}ExpiryDate">|;
|
||||
$out .= qq|<input type="radio" name="${multiple}pe_radio" onclick="document.getElementById('${multiple}ExpiryDate').value = document.getElementById('${multiple}pe_date').value"|;
|
||||
$out .= qq| checked| if $got_date;
|
||||
$out .= qq|><input type="text" name="${multiple}pe_date" id="${multiple}pe_date" onchange="document.getElementById('${multiple}ExpiryDate').value = document.getElementById('${multiple}pe_date').value"|;
|
||||
$out .= qq| value="| . GT::Date::date_get($val, '%yyyy%/%m%/%d% %HH%:%MM%:%ss%') . qq|"| if $got_date;
|
||||
$out .= qq|><br><input type="radio" name="${multiple}pe_radio" onclick="document.getElementById('${multiple}ExpiryDate').value = | . UNLIMITED . qq|"|;
|
||||
$out .= qq| checked| if $val && $val == UNLIMITED;
|
||||
$out .= qq|> Never<br><input type="radio" name="${multiple}pe_radio" onclick="document.getElementById('${multiple}ExpiryDate').value = | . UNPAID . qq|"|;
|
||||
$out .= qq| checked| if $val && $val == UNPAID;
|
||||
$out .= qq|> Awaiting Payment<br><input type="radio" name="${multiple}pe_radio" onclick="document.getElementById('${multiple}ExpiryDate').value = | . FREE . qq|"|;
|
||||
$out .= qq| checked| if $val && $val == FREE || !$val;
|
||||
$out .= qq|> No Payment Required (free)|;
|
||||
$out .= qq| - Expired | . GT::Date::date_get($rec->{LinkExpired}, '%yyyy%/%m%/%d% %HH%:%MM%:%ss%') if $val and $val == FREE and $rec->{LinkExpired};
|
||||
$out .= qq|</font></td>|;
|
||||
|
||||
if ($self->{mode} =~ /search/ or (exists $self->{input}->{action} and $self->{input}->{action} =~ /search/)) { # Hack to get this to show up on the Browser search
|
||||
$out .= qq|<td $td><select name="${multiple}ExpiryDate-opt"><option value="=">Exact Match</option><option value=">">Greater Than</option><option value="<">Less Than</option><option value="<>">Not Equal</option></select></td>|;
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
sub get_all_categories {
|
||||
# -------------------------------------------------------------------
|
||||
# Returns a select box of all categories.
|
||||
#
|
||||
my $self = shift;
|
||||
my $id = shift;
|
||||
my $name = shift || 'CatLinks.CategoryID';
|
||||
my $mult = shift || 5;
|
||||
my $db = $DB->table ('Category');
|
||||
my $sth = $db->select ( ['ID', 'Full_Name'] );
|
||||
my %res = ();
|
||||
while (my ($id, $name) = $sth->fetchrow_array) {
|
||||
$res{$id} = $name;
|
||||
}
|
||||
return $self->select ( { name => $name, values => \%res, value => $id, blank => 0, multiple => $mult, sort => sub { lc $_[0] cmp lc $_[1] } } );
|
||||
}
|
||||
|
||||
sub get_categories_with_html {
|
||||
# -------------------------------------------------------------------
|
||||
# Returns select list, and adds which categories are selected as text.
|
||||
#
|
||||
my ($self, @param) = @_;
|
||||
my $select = $self->get_all_categories(@param);
|
||||
my $output = '';
|
||||
my @vals = ref $param[0] ? @{$param[0]} : ($param[0]);
|
||||
if (@vals) {
|
||||
my $db = $DB->table ('Category');
|
||||
foreach my $id (@vals) {
|
||||
next unless ($id and $id =~ /^\d+$/);
|
||||
my $name_r = $db->get ($id, 'ARRAY', ['Full_Name']);
|
||||
$output .= $name_r->[0] . "<BR>";
|
||||
}
|
||||
}
|
||||
$output .= $select;
|
||||
return $output;
|
||||
}
|
||||
|
||||
1;
|
||||
101
site/slowtwitch.com/cgi-bin/articles/admin/Links/HTML/Users.pm
Normal file
101
site/slowtwitch.com/cgi-bin/articles/admin/Links/HTML/Users.pm
Normal file
@@ -0,0 +1,101 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Users.pm,v 1.4 2007/03/22 22:05:44 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::HTML::Users;
|
||||
# ==================================================================
|
||||
# Handles displaying of forms and HTML.
|
||||
#
|
||||
use strict;
|
||||
use vars qw/@ISA/;
|
||||
use Links qw/:objects/;
|
||||
use GT::SQL::Display::HTML::Table;
|
||||
|
||||
@ISA = qw/GT::SQL::Display::HTML::Table/;
|
||||
|
||||
sub display {
|
||||
# -------------------------------------------------------------------
|
||||
# Displays a link, but passes through the plugin system.
|
||||
#
|
||||
my $self = shift;
|
||||
my $p = ref $_[0] eq 'HASH' ? shift : {@_};
|
||||
|
||||
$PLG->dispatch('display_user', sub { $self->SUPER::display(@_) }, $p);
|
||||
}
|
||||
|
||||
sub form {
|
||||
# -------------------------------------------------------------------
|
||||
# Displays a user form, but passes through the plugin system.
|
||||
#
|
||||
my $self = shift;
|
||||
my $p = (ref $_[0] eq 'HASH') ? shift : {@_};
|
||||
|
||||
$PLG->dispatch('form_user', sub { return $self->SUPER::form(@_) }, $p);
|
||||
}
|
||||
|
||||
sub _display {
|
||||
# -------------------------------------------------------------------
|
||||
# Adds on a box with quick links to the users links.
|
||||
#
|
||||
my ($self, $opts) = @_;
|
||||
my $user = $opts->{values}->{Username};
|
||||
my $output = '';
|
||||
|
||||
# If we are modifying, then add a hidden field for the original record.
|
||||
if ($opts->{mode} eq 'modify_form') {
|
||||
$opts->{code}->{Username} ||= \&disp_username;
|
||||
my $user_q = GT::CGI->html_escape($user);
|
||||
$output .= qq~<input type="hidden" name="orig_username" value="$user_q">~;
|
||||
}
|
||||
else {
|
||||
delete $self->{code}->{Username};
|
||||
}
|
||||
$output .= $self->SUPER::_display($opts);
|
||||
if ($user) {
|
||||
my $link_db = $DB->table('Links');
|
||||
my $count = $link_db->count({ LinkOwner => $user });
|
||||
my $url = GT::CGI->url({ query_string => 0 });
|
||||
my $user_q = GT::CGI->escape($user);
|
||||
$output .= <<HTML;
|
||||
<p>
|
||||
<table border=1 cellpadding=0 bgcolor="#FFFFFF" cellspacing=0 width="500"><tr><td>
|
||||
<table border=0 bgcolor="#FFFFFF" width="500"><tr>
|
||||
<td><font face="Tahoma,Arial,Helvetica" size="2">
|
||||
Links ($count):
|
||||
<a href="$url?db=Links&do=search_results&LinkOwner=$user_q&ww=1">View</a> |
|
||||
<a href="$url?db=Links&do=modify_search_results&LinkOwner=$user_q&ww=1">Modify</a> |
|
||||
<a href="$url?db=Links&do=delete_search_results&LinkOwner=$user_q&ww=1">Delete</a>
|
||||
</font></td>
|
||||
</tr></table>
|
||||
</td></tr></table>
|
||||
HTML
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
sub disp_username {
|
||||
# -------------------------------------------------------------------
|
||||
# Display the username with links to edit.
|
||||
#
|
||||
my ($self, $col, $rec) = @_;
|
||||
my $val = $rec->{Username};
|
||||
my $val_e = GT::CGI->html_escape($val);
|
||||
my $font = $self->{font};
|
||||
return <<HTML;
|
||||
<tr>
|
||||
<td><font $font>Username</font></td>
|
||||
<td><font $font><input type="text" name="Username" value="$val_e" size="20"></font></td>
|
||||
</tr>
|
||||
HTML
|
||||
}
|
||||
|
||||
1;
|
||||
183
site/slowtwitch.com/cgi-bin/articles/admin/Links/Import/BKS2.pm
Normal file
183
site/slowtwitch.com/cgi-bin/articles/admin/Links/Import/BKS2.pm
Normal file
@@ -0,0 +1,183 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: BKS2.pm,v 1.14 2005/03/05 01:46:07 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::Import::BKS2;
|
||||
|
||||
use 5.004_04;
|
||||
use strict;
|
||||
use vars qw/$Warning_Code $Critical_Code $Mild_Code $Print_Out/;
|
||||
|
||||
use GT::SQL;
|
||||
|
||||
sub critical {
|
||||
$Critical_Code->(@_) if ref $Critical_Code eq 'CODE';
|
||||
}
|
||||
|
||||
sub warning {
|
||||
$Warning_Code->(@_) if ref $Warning_Code eq 'CODE';
|
||||
}
|
||||
|
||||
sub mild_warning {
|
||||
ref $Mild_Code eq 'CODE' and $Mild_Code->(@_);
|
||||
}
|
||||
|
||||
sub import_print {
|
||||
if (ref $Print_Out eq 'CODE') {
|
||||
$Print_Out->(@_);
|
||||
}
|
||||
else {
|
||||
print @_;
|
||||
}
|
||||
}
|
||||
|
||||
# Takes 3-4 arguments: hash reference, 2 or 3 code refs
|
||||
# The hash reference is the options hash for an import.
|
||||
# The first code reference will be called when a warning occurs.
|
||||
# The second code reference will be called when a critical error occurs.
|
||||
# If provided, the third code reference will be called when a mild warning occurs
|
||||
sub import {
|
||||
my $opt = shift;
|
||||
return if ref $opt ne 'HASH';
|
||||
{
|
||||
my $warning = shift;
|
||||
return if ref $warning ne 'CODE';
|
||||
$Warning_Code = $warning;
|
||||
|
||||
my $critical = shift;
|
||||
return if ref $critical ne 'CODE';
|
||||
$Critical_Code = $critical;
|
||||
|
||||
my $mild = shift;
|
||||
$Mild_Code = $mild if ref $mild eq 'CODE';
|
||||
|
||||
my $output = shift;
|
||||
$Print_Out = $output if ref $output eq 'CODE';
|
||||
}
|
||||
|
||||
my $DB = new GT::SQL(def_path => $$opt{destination}, subclass => 0);
|
||||
my $prefix = $DB->prefix || "";
|
||||
|
||||
my $odbc = 0;
|
||||
my $e_dbh;
|
||||
{
|
||||
my $table = $DB->table("Links");
|
||||
$table->connect();
|
||||
$e_dbh = $table->{driver}->connect();
|
||||
if ($table->{connect}->{driver} eq 'ODBC') {
|
||||
$odbc = 1;
|
||||
$e_dbh->{LongReadLen} = 1000000;
|
||||
}
|
||||
}
|
||||
|
||||
my $delimiter;
|
||||
|
||||
local (*IMPORT_FH);
|
||||
local $/ = "\0"; # "Lines" are actually delimited by \0 (hex & ascii 0)
|
||||
|
||||
import_print "Verifying table headers ...\n";
|
||||
|
||||
my $all_problems = "";
|
||||
open IMPORT_FH, "<$$opt{source}" or critical "Unable to open $$opt{source}: $!";
|
||||
binmode IMPORT_FH; # Don't want to worry about windows line feeds!
|
||||
while (<IMPORT_FH>) {
|
||||
last if substr($_,0,2) eq '\\\\';
|
||||
} # Eat up until a \\
|
||||
while (<IMPORT_FH>) {
|
||||
chomp;
|
||||
my $table = $_;
|
||||
import_print "\tChecking $table\n";
|
||||
my $has_problems = 0;
|
||||
TABLE: while (<IMPORT_FH>) {
|
||||
chomp;
|
||||
my $header = $_;
|
||||
my $delimiter = substr($header,0,1);
|
||||
substr($header,0,1) = '';
|
||||
my @cols = map BK_unescape($_,$delimiter), split /\Q$delimiter/, $header, -1;
|
||||
my %cols = $DB->table($table)->cols;
|
||||
my $problem = "";
|
||||
for (grep !$cols{$_}, @cols) {
|
||||
$problem .= ($problem ? ", " : "") . $_;
|
||||
}
|
||||
if ($problem) {
|
||||
my $plural = $problem =~ /, /;
|
||||
$all_problems .= "\nThe following column".($plural?"s":"")." in the $table table ($$opt{source}) ".($plural?"are":"is")." NOT in the Gossamer Links database: $problem. ".($plural?"They":"It")." will have to be created prior to performing this import.";
|
||||
$has_problems++;
|
||||
}
|
||||
while (<IMPORT_FH>) {
|
||||
last TABLE if substr($_,0,2) eq '\\\\';
|
||||
}
|
||||
}
|
||||
}
|
||||
close IMPORT_FH;
|
||||
critical $all_problems if $all_problems;
|
||||
|
||||
import_print "All tables verified successfully\n\n\n";
|
||||
|
||||
open IMPORT_FH, "<$$opt{source}" or critical "Unable to open $$opt{source}: $!";
|
||||
binmode IMPORT_FH; # Don't want to worry about windows line feeds!
|
||||
while (<IMPORT_FH>) {
|
||||
last if substr($_,0,2) eq '\\\\';
|
||||
} # Eat up until \\
|
||||
while (<IMPORT_FH>) {
|
||||
chomp;
|
||||
my $table = $_;
|
||||
$e_dbh->do("DELETE FROM $prefix$_");
|
||||
import_print "Importing $prefix$table ... (starting at line ".($.+2)." of $$opt{source})\n";
|
||||
my $imported = 0;
|
||||
TABLE: while (<IMPORT_FH>) {
|
||||
chomp;
|
||||
my $header = $_;
|
||||
my $delimiter = substr($header,0,1);
|
||||
substr($header,0,1) = '';
|
||||
my @cols = map BK_unescape($_,$delimiter), split /\Q$delimiter/, $header, -1;
|
||||
|
||||
# If this is an ODBC db, we need to turn identity insert on.
|
||||
my $insert = "INSERT INTO $prefix$table (" . join(",", @cols) . ") VALUES (" . join(",",("?") x @cols) . ")";
|
||||
if ($odbc) {
|
||||
if ($DB->table($table)->ai) {
|
||||
$insert = "SET IDENTITY_INSERT $prefix$table ON; $insert";
|
||||
}
|
||||
}
|
||||
my $sth = $e_dbh->prepare($insert) or critical "Unable to prepare query `$insert': ".$e_dbh->errstr;
|
||||
|
||||
import_print "\tStarting import to table $prefix$table ...\n";
|
||||
while (<IMPORT_FH>) {
|
||||
last TABLE if substr($_,0,2) eq '\\\\';
|
||||
chomp;
|
||||
my @data = map BK_unescape($_,$delimiter), split /\Q$delimiter/, $_, -1;
|
||||
$sth->execute(@data) or warning "\tUnable to import `$_' (line $. of $$opt{source}): ".$sth->errstr;
|
||||
import_print "\t$imported imported ...\n" unless ++$imported % 500;
|
||||
}
|
||||
}
|
||||
import_print "\t$imported records imported to $prefix$table.\n",
|
||||
"All records have been imported to $prefix$table.\n\n";
|
||||
}
|
||||
import_print "All tables contained in $$opt{source} have been imported.\n\nNOTE: You must run Repair Tables and Rebuild Search after performing an import!\n";
|
||||
}
|
||||
|
||||
|
||||
# Takes two parameters: The field to escape, and the delimiter. It will return
|
||||
# the field unescaped.
|
||||
sub BK_unescape ($$) {
|
||||
my $field = shift;
|
||||
my $delimiter = shift;
|
||||
$delimiter = "" unless defined $delimiter;
|
||||
critical "Bad delimiter `$delimiter'" unless length $delimiter == 1 and $delimiter ne '\\';
|
||||
critical "An escaped field cannot be undefined. You have data errors!" unless defined $field;
|
||||
return undef if $field eq 'NULL';
|
||||
my $escape_chr = '\\';
|
||||
$field =~ s/\Q$escape_chr\E([0-9A-Fa-f]{2})/chr hex $1/ge;
|
||||
$field;
|
||||
}
|
||||
|
||||
2;
|
||||
@@ -0,0 +1,189 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: BKS2.pm,v 1.14 2005/03/05 01:46:07 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::Import::BKS2;
|
||||
|
||||
use 5.004_04;
|
||||
use strict;
|
||||
use vars qw/$Warning_Code $Critical_Code $Mild_Code $Print_Out/;
|
||||
|
||||
use GT::SQL;
|
||||
|
||||
sub critical {
|
||||
$Critical_Code->(@_) if ref $Critical_Code eq 'CODE';
|
||||
}
|
||||
|
||||
sub warning {
|
||||
$Warning_Code->(@_) if ref $Warning_Code eq 'CODE';
|
||||
}
|
||||
|
||||
sub mild_warning {
|
||||
ref $Mild_Code eq 'CODE' and $Mild_Code->(@_);
|
||||
}
|
||||
|
||||
sub import_print {
|
||||
if (ref $Print_Out eq 'CODE') {
|
||||
$Print_Out->(@_);
|
||||
}
|
||||
else {
|
||||
print @_;
|
||||
}
|
||||
}
|
||||
|
||||
# Takes 3-4 arguments: hash reference, 2 or 3 code refs
|
||||
# The hash reference is the options hash for an import.
|
||||
# The first code reference will be called when a warning occurs.
|
||||
# The second code reference will be called when a critical error occurs.
|
||||
# If provided, the third code reference will be called when a mild warning occurs
|
||||
sub import {
|
||||
my $opt = shift;
|
||||
return if ref $opt ne 'HASH';
|
||||
{
|
||||
my $warning = shift;
|
||||
return if ref $warning ne 'CODE';
|
||||
$Warning_Code = $warning;
|
||||
|
||||
my $critical = shift;
|
||||
return if ref $critical ne 'CODE';
|
||||
$Critical_Code = $critical;
|
||||
|
||||
my $mild = shift;
|
||||
$Mild_Code = $mild if ref $mild eq 'CODE';
|
||||
|
||||
my $output = shift;
|
||||
$Print_Out = $output if ref $output eq 'CODE';
|
||||
}
|
||||
|
||||
my $DB = new GT::SQL(def_path => $$opt{destination}, subclass => 0);
|
||||
my $prefix = $DB->prefix || "";
|
||||
|
||||
my $odbc = 0;
|
||||
my $e_dbh;
|
||||
{
|
||||
my $table = $DB->table("Links");
|
||||
$table->connect();
|
||||
$e_dbh = $table->{driver}->connect();
|
||||
if ($table->{connect}->{driver} eq 'ODBC') {
|
||||
$odbc = 1;
|
||||
$e_dbh->{LongReadLen} = 1000000;
|
||||
}
|
||||
}
|
||||
|
||||
my $delimiter;
|
||||
|
||||
local (*IMPORT_FH);
|
||||
local $/ = "\0"; # "Lines" are actually delimited by \0 (hex & ascii 0)
|
||||
|
||||
import_print "Verifying table headers ...\n";
|
||||
|
||||
my $all_problems = "";
|
||||
open IMPORT_FH, "<$$opt{source}" or critical "Unable to open $$opt{source}: $!";
|
||||
binmode IMPORT_FH; # Don't want to worry about windows line feeds!
|
||||
while (<IMPORT_FH>) {
|
||||
last if substr($_,0,2) eq '\\\\';
|
||||
} # Eat up until a \\
|
||||
while (<IMPORT_FH>) {
|
||||
chomp;
|
||||
my $table = $_;
|
||||
import_print "\tChecking $table\n";
|
||||
my $has_problems = 0;
|
||||
TABLE: while (<IMPORT_FH>) {
|
||||
chomp;
|
||||
my $header = $_;
|
||||
my $delimiter = substr($header,0,1);
|
||||
substr($header,0,1) = '';
|
||||
my @cols = map BK_unescape($_,$delimiter), split /\Q$delimiter/, $header, -1;
|
||||
my %cols = $DB->table($table)->cols;
|
||||
my $problem = "";
|
||||
for (grep !$cols{$_}, @cols) {
|
||||
$problem .= ($problem ? ", " : "") . $_;
|
||||
}
|
||||
if ($problem) {
|
||||
my $plural = $problem =~ /, /;
|
||||
$all_problems .= "\nThe following column".($plural?"s":"")." in the $table table ($$opt{source}) ".($plural?"are":"is")." NOT in the Gossamer Links database: $problem. ".($plural?"They":"It")." will have to be created prior to performing this import.";
|
||||
$has_problems++;
|
||||
}
|
||||
while (<IMPORT_FH>) {
|
||||
last TABLE if substr($_,0,2) eq '\\\\';
|
||||
}
|
||||
}
|
||||
}
|
||||
close IMPORT_FH;
|
||||
critical $all_problems if $all_problems;
|
||||
|
||||
import_print "All tables verified successfully\n\n\n";
|
||||
|
||||
open IMPORT_FH, "<$$opt{source}" or critical "Unable to open $$opt{source}: $!";
|
||||
binmode IMPORT_FH; # Don't want to worry about windows line feeds!
|
||||
while (<IMPORT_FH>) {
|
||||
last if substr($_,0,2) eq '\\\\';
|
||||
} # Eat up until \\
|
||||
while (<IMPORT_FH>) {
|
||||
chomp;
|
||||
my $table = $_;
|
||||
#$e_dbh->do("DELETE FROM $prefix$_");
|
||||
import_print "Importing $prefix$table ... (starting at line ".($.+2)." of $$opt{source})\n";
|
||||
my $imported = 0;
|
||||
TABLE: while (<IMPORT_FH>) {
|
||||
chomp;
|
||||
my $header = $_;
|
||||
my $delimiter = substr($header,0,1);
|
||||
substr($header,0,1) = '';
|
||||
my @cols = map BK_unescape($_,$delimiter), split /\Q$delimiter/, $header, -1;
|
||||
|
||||
if ($table =~ /Category$/) {
|
||||
push @cols, 'tmp_col';
|
||||
}
|
||||
# If this is an ODBC db, we need to turn identity insert on.
|
||||
my $insert = "INSERT INTO $prefix$table (" . join(",", @cols) . ") VALUES (" . join(",",("?") x @cols) . ")";
|
||||
if ($odbc) {
|
||||
if ($DB->table($table)->ai) {
|
||||
$insert = "SET IDENTITY_INSERT $prefix$table ON; $insert";
|
||||
}
|
||||
}
|
||||
my $sth = $e_dbh->prepare($insert) or critical "Unable to prepare query `$insert': ".$e_dbh->errstr;
|
||||
|
||||
import_print "\tStarting import to table $prefix$table ...\n";
|
||||
while (<IMPORT_FH>) {
|
||||
last TABLE if substr($_,0,2) eq '\\\\';
|
||||
chomp;
|
||||
if ($table =~ /Category$/) {
|
||||
print $_ . "\n";
|
||||
my @data = map BK_unescape($_,$delimiter), split /\Q$delimiter/, $_, -1;
|
||||
$sth->execute(@data) or warning "\tUnable to import `$_' (line $. of $$opt{source}): ".$sth->errstr;
|
||||
import_print "\t$imported imported ...\n" unless ++$imported % 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
import_print "\t$imported records imported to $prefix$table.\n",
|
||||
"All records have been imported to $prefix$table.\n\n";
|
||||
}
|
||||
import_print "All tables contained in $$opt{source} have been imported.\n\nNOTE: You must run Repair Tables and Rebuild Search after performing an import!\n";
|
||||
}
|
||||
|
||||
|
||||
# Takes two parameters: The field to escape, and the delimiter. It will return
|
||||
# the field unescaped.
|
||||
sub BK_unescape ($$) {
|
||||
my $field = shift;
|
||||
my $delimiter = shift;
|
||||
$delimiter = "" unless defined $delimiter;
|
||||
critical "Bad delimiter `$delimiter'" unless length $delimiter == 1 and $delimiter ne '\\';
|
||||
critical "An escaped field cannot be undefined. You have data errors!" unless defined $field;
|
||||
return undef if $field eq 'NULL';
|
||||
my $escape_chr = '\\';
|
||||
$field =~ s/\Q$escape_chr\E([0-9A-Fa-f]{2})/chr hex $1/ge;
|
||||
$field;
|
||||
}
|
||||
|
||||
2;
|
||||
@@ -0,0 +1,581 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# Revision : $Id: CGI.pm,v 1.17 2005/04/05 08:44:30 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::Import::Interface::CGI;
|
||||
# ==================================================================
|
||||
|
||||
use strict;
|
||||
use Links qw/$IN $CFG/;
|
||||
|
||||
sub new {
|
||||
my $this = shift;
|
||||
my $class = ref($this) || $this;
|
||||
my $self = {};
|
||||
bless $self, $class;
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub isin {
|
||||
my $val = shift;
|
||||
for (@_) {
|
||||
return 1 if $val eq $_;
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub html_escape {
|
||||
shift if ref $_[0];
|
||||
my $to_escape = shift;
|
||||
$to_escape = "" unless defined $to_escape;
|
||||
$to_escape =~ s/&/&/g;
|
||||
$to_escape =~ s/ / /g;
|
||||
$to_escape =~ s/</</g;
|
||||
$to_escape =~ s/>/>/g;
|
||||
$to_escape =~ s/"/"/g;
|
||||
$to_escape;
|
||||
}
|
||||
|
||||
sub make_opts {
|
||||
my $self = shift;
|
||||
return if ref $self->{cgi} eq 'HASH';
|
||||
$self->{cgi} = { };
|
||||
$self->{cgi}{help} = 1, return if $IN->param("help");
|
||||
return unless $IN->param("Interface_CGI");
|
||||
|
||||
$self->{cgi}{transfer} = isin($IN->param("transfer"),qw/L1S2 L2S2 S1S2 BKS2 S2BK/)
|
||||
? $IN->param("transfer")
|
||||
: "";
|
||||
|
||||
for ($IN->param) { $self->{cgi}{$_} = $IN->param($_); }
|
||||
}
|
||||
|
||||
sub get_options {
|
||||
my $self = shift;
|
||||
$self->make_opts;
|
||||
return wantarray ? (help => 1) : { help => 1 } if $self->{cgi}{'help'};
|
||||
$self->start_page(),exit unless $IN->param("Interface_CGI");
|
||||
if ($self->{cgi}{'errors_to_browser'}) {
|
||||
if ($self->{cgi}{error_file}) {
|
||||
my $fh = \do { local *FH; *FH };
|
||||
unless (open $fh, "> $self->{cgi}{error_file}") {
|
||||
_print_headers();
|
||||
print "<pre>Unable to open error file @{[html_pre_format(qq($self->{cgi}{error_file}: $!))]}</pre>";
|
||||
exit;
|
||||
}
|
||||
$self->{cgi}{error_file} = sub {
|
||||
for (@_) {
|
||||
print html_pre_format("Import error: $_\n");
|
||||
print $fh "Import error: $_\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$self->{cgi}{error_file} = sub {
|
||||
for (@_) {
|
||||
print html_pre_format("Import error: $_\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$self->{cgi}{error_file} = 'STDOUT';
|
||||
}
|
||||
return wantarray ? %{$self->{cgi}} : $self->{cgi};
|
||||
}
|
||||
|
||||
sub usage ($$$) {
|
||||
my $self = shift;
|
||||
push @{$self->{usage_list}}, shift if @_;
|
||||
# Don't care about the third argument; it is exclusively for Interface::Text
|
||||
}
|
||||
|
||||
sub has_usage {
|
||||
my $self = shift;
|
||||
return ref $self->{usage_list} ? scalar @{$self->{usage_list}} : undef;
|
||||
}
|
||||
|
||||
sub show_usage {
|
||||
my $self = shift;
|
||||
$self->start_page(1);
|
||||
}
|
||||
|
||||
sub pre_import {
|
||||
require Links;
|
||||
_print_headers();
|
||||
print "<html>\n<head>\n<title>Import Results</title>\n</head>\n<body bgcolor=#FFFFFF>\n";
|
||||
print Links::header("Import/Export", "Please be patient, this can take a while...");
|
||||
print "<blockquote><pre>";
|
||||
}
|
||||
|
||||
sub finished {
|
||||
print "</pre></blockquote>\n<b><font face='Tahoma,Arial,Helvetica' size=2>Data has been successfully import/exported!</font></b>\n</body>\n</html>";
|
||||
exit;
|
||||
}
|
||||
|
||||
# Takes one optional argument which, if true, will make it print usage messages
|
||||
sub start_page {
|
||||
my $self = shift;
|
||||
$self->make_opts unless ref $self->{cgi} eq 'HASH';
|
||||
_print_headers();
|
||||
$self->_start_page_top;
|
||||
if (shift and ref $self->{usage_list} and @{$self->{usage_list}}) {
|
||||
print "\n\n<ul>\n";
|
||||
for (@{$self->{usage_list}}) {
|
||||
print " <li><font color=red><b>$_ </b></font></li>\n";
|
||||
}
|
||||
print "</ul>\n\n";
|
||||
}
|
||||
$self->_start_page_bottom;
|
||||
exit;
|
||||
}
|
||||
|
||||
sub _start_page_top {
|
||||
print <<'HTML';
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Gossamer Links Import</title>
|
||||
</head>
|
||||
|
||||
<body bgcolor="#FFFFFF">
|
||||
<table border="1" cellpadding="0" cellspacing="0"><tr><td>
|
||||
<table bgColor="#ffffff" border="0" cellPadding="3" cellSpacing="3" width="500" valign="top">
|
||||
<tr>
|
||||
<td align="left" bgColor="navy"><b><font color="#ffffff" size="2" face="Tahoma,Arial,Helvetica">Gossamer Links Import/Backup/Restore</font></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p align="center"><b><font color="#000000" size="2" face="Tahoma,Arial,Helvetica">Gossamer Links Import/Backup/Restore</font></b></p>
|
||||
<p><font size="2" face="Tahoma,Arial,Helvetica">This tool will allow you to easily migrate from a previous
|
||||
version of Links, or backup and restore your data. For more information on the specific options, please
|
||||
consult the <b><a href="nph-import.cgi?help=1&Interface_CGI=1">Help</a></b></font></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
HTML
|
||||
}
|
||||
|
||||
sub _start_page_bottom {
|
||||
my $self = shift;
|
||||
print qq[
|
||||
<form action="nph-import.cgi" method="POST">
|
||||
<input type=hidden name="Interface_CGI" value=1>
|
||||
<input type="hidden" name="destination" value="$CFG->{admin_root_path}/defs">
|
||||
<table border="1" cellspacing="0" cellpadding="0"><tr><td>
|
||||
<table border="0" cellspacing="0" cellpadding="3" width=500>
|
||||
<tr>
|
||||
<td colspan="2" bgcolor="#DDDDDD"><b><font face="Tahoma,Arial,Helvetica" size="2">Import Data from previous versions of Links</font></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="left">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
Import From:
|
||||
</font>
|
||||
</td>
|
||||
<td valign="top" align="left"><font face="Tahoma,Arial,Helvetica" size="2">
|
||||
<select size="1" name="transfer" style="font-family: Tahoma, Arial, Helvetica; font-size: 10pt; color: #000000">
|
||||
<option ];
|
||||
print "selected " if $self->{cgi}{transfer} and $self->{cgi}{transfer} eq "S1S2";
|
||||
print qq[value="S1S2">Links SQL 1.x</option>
|
||||
<option ];
|
||||
print "selected " if $self->{cgi}{transfer} and $self->{cgi}{transfer} eq "L1S2";
|
||||
print qq[value="L1S2">Links 1.x</option>
|
||||
<option ];
|
||||
print "selected " if $self->{cgi}{transfer} and $self->{cgi}{transfer} eq "L2S2";
|
||||
print qq[value="L2S2">Links 2.x</option>
|
||||
</select></font>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="left">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
Location of def files:
|
||||
</font>
|
||||
</td>
|
||||
<td valign="top" align="left">
|
||||
<input type="text" name="source" size="40" ];
|
||||
print qq[value="].html_escape($self->{cgi}{source}).qq[" ] if $self->{cgi}{source} && ($self->{cgi}{transfer} =~ /^(?:L[12]|S1)S2$/);
|
||||
print qq[style="font-family: Tahoma, Arial, Helvetica; font-size: 10pt">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="left">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
Error File (optional):
|
||||
</font>
|
||||
</td>
|
||||
<td valign="top" align="left">
|
||||
<input type="text" name="error_file" size="40" ];
|
||||
print qq[value="].html_escape($self->{cgi}{error_file}).qq[" ] if $self->{cgi}{error_file} && ($self->{cgi}{transfer} =~ /^(?:L[12]|S1)S2$/);
|
||||
print qq[style="font-family: Tahoma, Arial, Helvetica; font-size: 10pt">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table border="0" cellspacing="0" width="100%">
|
||||
<tr>
|
||||
<td valign="top" align="left" colspan="6">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
<br><b>Options:</b>
|
||||
</font>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="left">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
<input type="checkbox" name="show_mild_warnings" value=1];
|
||||
print " checked" if $self->{cgi}{show_mild_warnings};
|
||||
print qq[>
|
||||
Show Mild Warnings
|
||||
</font>
|
||||
</td>
|
||||
<td valign="top" align="left" colspan=2>
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
<input type="checkbox" name="critical_warnings" value=1];
|
||||
print " checked" if $self->{cgi}{critical_warnings};
|
||||
print qq[>
|
||||
Critical Warnings
|
||||
</font>
|
||||
</td>
|
||||
<td valign="top" align="left">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
<input type="checkbox" name="data_integrity" value=1];
|
||||
print " checked" if $self->{cgi}{data_integrity};
|
||||
print qq[>
|
||||
Extra Data Integrity
|
||||
</font>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="left">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
<input type="checkbox" name="clear_tables" value=1];
|
||||
print " checked" if not keys %{$self->{cgi}} or $self->{cgi}{clear_tables} and ($self->{cgi}{transfer} =~ /^(?:L[12]|S1)S2$/);
|
||||
print qq[>
|
||||
Clear Tables
|
||||
</font>
|
||||
</td>
|
||||
<td valign="top" align="left" colspan=2>
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
<input type="checkbox" name="errors_to_browser" value=1];
|
||||
print " checked" if ($self->{cgi}{errors_to_browser} or not keys %{$self->{cgi}});
|
||||
print qq[>
|
||||
Show Errors
|
||||
</font>
|
||||
</td>
|
||||
<td valign="top" align="left">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
<input type="checkbox" name="straight_import" value=1];
|
||||
print " checked" if $self->{cgi}{straight_import};
|
||||
print qq[>
|
||||
Straight Import
|
||||
</font>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign=top align=left colspan=2>
|
||||
<font face="Tahoma,Arial,Helvetica" size=2>
|
||||
<input type="checkbox" name="create_columns" value=1];
|
||||
print " checked" if $self->{cgi}{create_columns} or not keys %{$self->{cgi}};
|
||||
print qq[>
|
||||
Recreate Non-standard Columns
|
||||
</font>
|
||||
</td>
|
||||
<td valign=top align=left colspan=2>
|
||||
<font face="Tahoma,Arial,Helvetica" size=2>
|
||||
<input type=checkbox name=create_missing_categories value=1];
|
||||
print " checked" if $self->{cgi}{create_missing_categories} or not keys %{$self->{cgi}};
|
||||
print qq[>
|
||||
Create Missing Categories
|
||||
</font>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="8"><br><center><input type="submit" value="Import Data"></center><br></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td></tr></table>
|
||||
</form>
|
||||
<br>
|
||||
|
||||
<form action="nph-import.cgi" method="POST">
|
||||
<input type=hidden name="Interface_CGI" value=1>
|
||||
<input type="hidden" name="source" value="$CFG->{admin_root_path}/defs">
|
||||
<input type="hidden" name="transfer" value="S2BK">
|
||||
<input type="hidden" name="delimiter" value="|">
|
||||
<table border="1" cellspacing="0" cellpadding="0"><tr><td>
|
||||
<table border="0" cellspacing="0" cellpadding="3" width=500>
|
||||
<tr>
|
||||
<td colspan="2" bgcolor="#DDDDDD"><b><font face="Tahoma,Arial,Helvetica" size="2">Create backup file of all Gossamer Links data</font></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="left">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
Location of Backup File:
|
||||
</font>
|
||||
</td>
|
||||
<td valign="top" align="left">
|
||||
<input type="text" name="destination" size="40" ];
|
||||
print qq[value="].html_escape($self->{cgi}{destination}).qq[" ] if $self->{cgi}{destination} && ($self->{cgi}{transfer} eq 'S2BK');
|
||||
print qq[style="font-family: Tahoma, Arial, Helvetica; font-size: 10pt">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><br><center><input type="submit" value="Backup Data"></center><br></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td></tr></table>
|
||||
</form>
|
||||
<br>
|
||||
|
||||
<form action="nph-import.cgi" method="POST">
|
||||
<input type=hidden name="Interface_CGI" value=1>
|
||||
<input type="hidden" name="destination" value="$CFG->{admin_root_path}/defs">
|
||||
<input type="hidden" name="transfer" value="BKS2">
|
||||
<input type="hidden" name="delimiter" value="|">
|
||||
<input type="hidden" name="clear_tables" value="1">
|
||||
<table border="1" cellspacing="0" cellpadding="0"><tr><td>
|
||||
<table border="0" cellspacing="0" cellpadding="3" width=500>
|
||||
<tr>
|
||||
<td colspan="2" bgcolor="#DDDDDD"><b><font face="Tahoma,Arial,Helvetica" size="2">Restore Gossamer Links from backup file</font></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="left">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
Location of Backup File:
|
||||
</font>
|
||||
</td>
|
||||
<td valign="top" align="left">
|
||||
<input type="text" name="source" size="40" ];
|
||||
print qq[value="].html_escape($self->{cgi}{source}).qq[" ] if $self->{cgi}{source} && ($self->{cgi}{transfer} eq 'BKS2');
|
||||
print qq[style="font-family: Tahoma, Arial, Helvetica; font-size: 10pt">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><br><center><input type="submit" value="Restore Data"></center><br></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td></tr></table>
|
||||
</form>
|
||||
<br><br>
|
||||
|
||||
</form>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
];
|
||||
}
|
||||
|
||||
sub show_help {
|
||||
my $self = shift;
|
||||
_print_headers();
|
||||
print <<'HTML';
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Gossamer Links Import Help</title>
|
||||
</head>
|
||||
|
||||
<body bgcolor="#FFFFFF">
|
||||
<table border="1" cellpadding="0" cellspacing="0"><tr><td>
|
||||
<table bgColor="#ffffff" border="0" cellPadding="3" cellSpacing="3" width="500" valign="top">
|
||||
<tr>
|
||||
<td align="left" bgColor="navy"><b><font color="#ffffff" size="2" face="Tahoma,Arial,Helvetica">Links
|
||||
SQL Import Help</font></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p align="center"><b><font color="#000000" size="2" face="Tahoma,Arial,Helvetica">Links SQL Import Help</font></b></p>
|
||||
<p><font size="2" face="Tahoma,Arial,Helvetica">Below is a list of all the options available to you when importing
|
||||
data into Gossamer Links.</font></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td></tr>
|
||||
</table>
|
||||
<br><br>
|
||||
<table cellpadding="3" cellspacing="0" border="1" width="500">
|
||||
<tr>
|
||||
<td valign="top" align="left" width="25%">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
<b><u>Column</u></b>
|
||||
</font>
|
||||
</td>
|
||||
<td valign="top" align="center">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
<b><u>Description</u></b>
|
||||
</font>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="left" width="25%">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
Error File:
|
||||
</font>
|
||||
</td>
|
||||
<td valign="top" align="left">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
If present, all errors will be written to the filename provided. The
|
||||
errors will be appended to the end, with a header including the date
|
||||
written before any errors.
|
||||
</font>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="left" width="25%">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
Show Mild Warnings
|
||||
</font>
|
||||
</td>
|
||||
<td valign="top" align="left">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
If this option is selected, 'mild' warnings (indicating minor errors
|
||||
such as setting the username associated with a link to 'admin' because
|
||||
of insufficient information to create a user) will be displayed in the
|
||||
error file. If unchecked, such errors are never displayed.
|
||||
</font>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="left" width="25%">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
Critical Warnings
|
||||
</font>
|
||||
</td>
|
||||
<td valign="top" align="left">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
If this option is enabled, all warnings (such as not being able to
|
||||
import a Category or Link for whatever reason) will be promoted to
|
||||
critical errors, stopping the import. This field has NO effect on mild
|
||||
warnings - this is, mild warnings will NOT cause the script to abort.
|
||||
</font>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="left" width="25%">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
Clear Tables
|
||||
</font>
|
||||
</td>
|
||||
<td valign="top" align="left">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
With this option enabled, all tables will be cleared before importing.
|
||||
This has no effect when exporting to a delimited text file.
|
||||
</font>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="left" width="25%">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
Straight Import
|
||||
</font>
|
||||
</td>
|
||||
<td valign="top" align="left">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
With this option enabled, Link IDs will not be changed for the new
|
||||
database. That is, a Link with ID 12 in the old database will still be
|
||||
12 in the new Gossamer Links database. This option is not recommended unless
|
||||
you are linking directly to a link using its ID and must preserve the
|
||||
existing link numbering. This option <b>requires</b> that the <i>Clear
|
||||
Tables</i> option be enabled.
|
||||
</font>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="left" width="25%">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
Show Warnings
|
||||
</font>
|
||||
</td>
|
||||
<td valign="top" align="left">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
With this option enabled, all warnings will be displayed to the
|
||||
browser (as well as the log if a log is specified). This option is
|
||||
automatically enabled if no log file is specified.
|
||||
</font>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="left" width="25%">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
Recreate Non-standard Columns
|
||||
</font>
|
||||
</td>
|
||||
<td valign="top" align="left">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
If this option is enabled, when the import finds extra (custom) columns
|
||||
in the source database that do not have an equivelant extra column in
|
||||
the destination table, they will be created in the destination table so
|
||||
that all data will be imported.
|
||||
</font>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="left" width="25%">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
Create Missing Categories
|
||||
</font>
|
||||
</td>
|
||||
<td valign="top" align="left">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
This option, if enabled, causes the import to create any categories
|
||||
that are "missing". A category can be missing when a category such as
|
||||
"A/B/C" exists, but the category "A/B" does not. This option will make
|
||||
the import create the "A/B" category, as well as the "A" category (if
|
||||
necessary (i.e. it doesn't exist)).<br>
|
||||
A category is also considered "missing" if a link refers to a category
|
||||
that does not exist (Links 1.x and 2.x only).
|
||||
</font>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="left" width="25%">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
Extra Data Integrity
|
||||
</font>
|
||||
</td>
|
||||
<td valign="top" align="left">
|
||||
<font face="Tahoma,Arial,Helvetica" size="2">
|
||||
This option makes the import check each time a category is imported to
|
||||
ensure that no duplicate categories will be created by the import. If a
|
||||
duplicate is identified, the duplicated category will only be inserted
|
||||
once. Note that this option will most likely make the script take
|
||||
several times longer to import data, and should only be used if you
|
||||
suspect that there may be duplicate categories.
|
||||
</font>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
HTML
|
||||
}
|
||||
|
||||
sub html_pre_format {
|
||||
local $_ = shift;
|
||||
s/&/&/g;
|
||||
s/</</g;
|
||||
s/>/>/g;
|
||||
$_;
|
||||
}
|
||||
|
||||
|
||||
sub _print_headers {
|
||||
# ------------------------------------------------------------------
|
||||
# Prints the HTTP headers. Loads Links config file to see if we
|
||||
# should use nph headers or not.
|
||||
#
|
||||
print $IN->header ( -nph => $CFG->{nph_headers} );
|
||||
}
|
||||
|
||||
"Do I *look* like a false value?"
|
||||
@@ -0,0 +1,294 @@
|
||||
# ==================================================================
|
||||
# Links SQL - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# Revision : $Id: Text.pm,v 1.14 2004/05/04 00:50:09 jagerman Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::Import::Interface::Text;
|
||||
# ==================================================================
|
||||
|
||||
use vars qw/%IMPORT_OPT_MAP/;
|
||||
|
||||
use strict;
|
||||
use Getopt::Long;
|
||||
|
||||
%IMPORT_OPT_MAP = (
|
||||
LINKSSQL1 => 'S1S2',
|
||||
LINKSQL1 => 'S1S2',
|
||||
LINKS1 => 'L1S2',
|
||||
LINKS2 => 'L2S2',
|
||||
S1 => 'S1S2',
|
||||
L2 => 'L2S2',
|
||||
L1 => 'L1S2',
|
||||
LINKS => 'L2S2',
|
||||
RDF => 'RDFS2',
|
||||
DMOZ => 'RDFS2'
|
||||
);
|
||||
|
||||
sub new {
|
||||
my $this = shift;
|
||||
my $class = ref($this) || $this;
|
||||
my $self = { };
|
||||
bless $self, $class;
|
||||
return $self;
|
||||
$self->_init();
|
||||
}
|
||||
|
||||
sub get_options {
|
||||
my $self = shift;
|
||||
my %option = ();
|
||||
my ($backup,$restore,$import);
|
||||
GetOptions(
|
||||
"backup" => \$backup,
|
||||
"restore" => \$restore,
|
||||
"import=s" => \$import,
|
||||
"source=s" => \$option{source},
|
||||
"destination=s" => \$option{destination},
|
||||
"help" => \$option{help},
|
||||
"error-file=s" => \$option{error_file},
|
||||
"critical-warnings" => \$option{critical_warnings},
|
||||
"mild-warnings" => \$option{show_mild_warnings},
|
||||
"data-integrity" => \$option{data_integrity},
|
||||
"create-columns" => \$option{create_columns},
|
||||
"create-missing-categories" => \$option{create_missing_categories},
|
||||
"clear-tables" => \$option{clear_tables},
|
||||
"straight-import" => \$option{straight_import},
|
||||
"rdf-category=s" => \$option{rdf_category},
|
||||
"rdf-destination=s" => \$option{rdf_destination},
|
||||
"rdf-add-date=s" => \$option{rdf_add_date},
|
||||
"with-gzip=s" => \$option{with_gzip},
|
||||
"rdf-update" => \$option{rdf_update},
|
||||
"rdf-user=s" => \$option{rdf_user},
|
||||
"xml-parser!" => \$option{xml_parser}
|
||||
);
|
||||
$option{transfer} = $IMPORT_OPT_MAP{uc $import} || "";
|
||||
unless ($option{from} or $option{to} or $option{source} or $option{destination}) {
|
||||
return wantarray ? () : {};
|
||||
}
|
||||
if (($backup and $restore) or ($backup and $option{transfer}) or ($restore and $option{transfer})) {
|
||||
delete $option{transfer}; # Two options provided!
|
||||
}
|
||||
elsif ($backup) {
|
||||
$option{transfer} = "S2BK";
|
||||
}
|
||||
elsif ($restore) {
|
||||
$option{transfer} = "BKS2";
|
||||
}
|
||||
return wantarray ? %option : \%option;
|
||||
}
|
||||
|
||||
sub start_page {
|
||||
show_help(@_);
|
||||
}
|
||||
|
||||
sub show_help {
|
||||
my $self = shift;
|
||||
print <<HELP;
|
||||
|
||||
Links SQL 2 Importer/Exporter
|
||||
|
||||
Usage:
|
||||
|
||||
perl $0 {--backup|--restore|--import type} --source=<source>
|
||||
--destination=<destination> [any others of the following options]
|
||||
|
||||
Options are (options may be simplified to uniqueness):
|
||||
|
||||
(One of the following three is required)
|
||||
--import Links1|Links2|LinksSQL1|RDF
|
||||
Will do an import from the chosen source.
|
||||
|
||||
--backup
|
||||
This option will perform a Links SQL 2 backup.
|
||||
|
||||
--restore
|
||||
This option will return from a Links SQL 2 backup file created
|
||||
with --backup.
|
||||
|
||||
--source=<input_source> (required)
|
||||
Sets according to the following:
|
||||
--import Links1|Links2
|
||||
the path of the def and db files
|
||||
--import LinksSQL1
|
||||
the path of the def files
|
||||
--import RDF
|
||||
the path and filename of the RDF file to import from.
|
||||
Note that if the file ends in .gz, the import will attempt to run
|
||||
it through gzip decompression trying several standard locations for
|
||||
gzip. You may specify a location for gzip using the --with-gzip
|
||||
option.
|
||||
--restore
|
||||
the path and filename of the backup file created with --backup
|
||||
--backup
|
||||
the path of the Links SQL 2 def files
|
||||
|
||||
--destination=<output_dest> (required)
|
||||
Sets according to the following:
|
||||
--import Links1|Links2|LinksSQL1|RDF
|
||||
--restore
|
||||
the path of the Links SQL 2 def files
|
||||
--backup
|
||||
the path and filename of a file to use for the Links SQL 2 backup.
|
||||
|
||||
--error-file="./error/errors.txt" (not required)
|
||||
Sets a file to which all import errors will be written. If you set it
|
||||
to STDOUT, or if it is not set, it will write all errors to standard
|
||||
output (STDOUT) prepended with "IMPORT ERROR: ".
|
||||
|
||||
--critical-warnings
|
||||
Makes import warnings become fatal errors. Note that relatively minor
|
||||
warnings such as not having enough information to create a new user for
|
||||
a link (therefore setting the link to be owned by admin) are not
|
||||
promoted to fatal errors.
|
||||
|
||||
--mild-warnings
|
||||
Displays mild import warnings. Mild warnings are those that affect a
|
||||
relatively minor portion of the script. Note that mild warnings will
|
||||
NOT cause the script to abort, even if the --critical-warnings option
|
||||
has been enabled.
|
||||
|
||||
--data-integrity
|
||||
Makes the import check every category before inserting it to insure
|
||||
that there are no duplicates. Note that this option will make the
|
||||
import take much longer to complete as each and every category will
|
||||
have to be checked to see if it exists. This option is only recommended
|
||||
if you suspect that your data might contain duplicate categories. It
|
||||
only works when importing data to a Links SQL 2 database from Links
|
||||
1.x, Links 2.x, or Links SQL 1.x (NOT when backing up, restoring, or
|
||||
importing from an RDF).
|
||||
|
||||
--create-columns
|
||||
Makes the import attempt to create any columns which are in the source
|
||||
tables, but NOT in the destination tables. That is, custom tables will
|
||||
be imported into the new Links database. Without this option, existing
|
||||
tables that do not exist in the destination format will cause a
|
||||
warning. If this feature is enabled, a mild warning will occur whenever
|
||||
a table does not exist and is being created.
|
||||
|
||||
--clear-tables
|
||||
(This option is required to use --restore, but optional for --backup
|
||||
and all imports)
|
||||
Makes the current Links SQL tables be cleared (except for the admin
|
||||
user in the Users table) before doing the import. Only takes effect
|
||||
when importing to Links SQL 2. This option allows you to use the
|
||||
--straight-import option below.
|
||||
|
||||
--straight-import
|
||||
(This option can only be used with --clear-tables).
|
||||
Makes the import not recalculate Category/Link ID numbers. That is, a
|
||||
link with ID number 12 in the source will be inserted into the Links
|
||||
SQL 2 database with an ID number of 12. Note that this can leave a
|
||||
fairly large gap in the Links ID fields depending on the usage of the
|
||||
source import. This option does nothing with --backup and --restore
|
||||
|
||||
--create-missing-categories
|
||||
Used with an import. Categories are "missing" when they are required
|
||||
for the database to be complete but do not exist. For example, if
|
||||
category A/B/C existed in the database but A and A/B did not, then both
|
||||
A and A/B would be considered "missing" and would be automatically
|
||||
created if this option is enabled. For Links 1.x and 2.x imports, this
|
||||
will also make the import attempt to create categories that are
|
||||
required for links. For example, if a link exists and thinks it is in
|
||||
category A/B/C but A/B does no exist, A/B and A/B/C will be created to
|
||||
allow the link to be imported.
|
||||
|
||||
--rdf-category="Top/Category/Name"
|
||||
(This option can only be and must be used with `--import RDF')
|
||||
Specifies the RDF category to import such as "Top/Business".
|
||||
|
||||
--rdf-destination="Links SQL2/Category/Name"
|
||||
(This option can only be used with `--import RDF')
|
||||
Specifies a Links SQL 2 category to import the data to. For example,
|
||||
"My Business Links" would import the RDF category specified with
|
||||
--rdf-category into the "My Business Links" category. If this is not
|
||||
specified (or specified as "/" or "") the import will be done into the
|
||||
Links SQL category root.
|
||||
|
||||
--with-gzip="/path/to/gzip"
|
||||
(This option can only be used with `--import RDF')
|
||||
Specifies the location of gzip. This option is only needed if the RDF
|
||||
file has been compressed with gzip (the file will end with ".gz") and
|
||||
the import is unable to locate gzip on its own.
|
||||
|
||||
--rdf-update
|
||||
(This option can only be used with `--import RDF')
|
||||
Specifies that the import should check to see that categories and links
|
||||
do not already exist. For an initial RDF import, this option is not
|
||||
needed, however to update a previous RDF import you MUST use this
|
||||
option; failing to do so would result in duplicate categories and links
|
||||
appearing. It is not recommended that you use this option when
|
||||
performing an initial import from an RDF as it will increase the import
|
||||
time considerably.
|
||||
|
||||
--rdf-user="Username"
|
||||
(This option can only be used with `--import RDF')
|
||||
Specifies a user who all new links should belong to. The user MUST
|
||||
already exist in the Links SQL Users table. If not specified, all links
|
||||
will have `admin' as the LinkOwner. Note that if the --clear-tables
|
||||
option is specified, this user will also be preserved when all tables
|
||||
are wiped.
|
||||
|
||||
--rdf-add-date="2001-01-05"
|
||||
(This option can only be used with `--import RDF' and is required)
|
||||
This sets the date that new link links should have their `Add_Date' and
|
||||
`Mod_Date' fields set to. This should be in the format `YYYY-MM-DD'.
|
||||
NOTE: You should NOT set this to a very recent date as all links would
|
||||
then show up as "New" links.
|
||||
|
||||
--help
|
||||
Displays this screen
|
||||
|
||||
HELP
|
||||
|
||||
# Understood, but often fails as RDF files are commonly malformed:
|
||||
#
|
||||
# --xml-parser
|
||||
# (This option can only be used with `--import RDF')
|
||||
# Attempts to use the new XML::Parser-based code for importing the RDF
|
||||
# file. Although much faster, it requires that the XML::Parser module be
|
||||
# installed, and should be considered an experimental feature.
|
||||
}
|
||||
|
||||
sub pre_import () { }
|
||||
|
||||
sub usage ($$;$) {
|
||||
my $self = shift;
|
||||
$self->{usage_list} = [ ] unless exists $self->{usage_list};
|
||||
my $message = "";
|
||||
if (@_) {
|
||||
$message = shift() . ".";
|
||||
$message .= " See " . shift() . " and --help" if @_;
|
||||
}
|
||||
push @{$self->{usage_list}}, $message if $message;
|
||||
}
|
||||
|
||||
sub has_usage {
|
||||
my $self = shift;
|
||||
return ref($self->{usage_list}) ? scalar @{$self->{usage_list}} : undef;
|
||||
}
|
||||
|
||||
sub show_usage {
|
||||
my $self = shift;
|
||||
for (@{$self->{usage_list}}) { print <<USAGE }
|
||||
|
||||
|
||||
Incorrect usage.
|
||||
|
||||
$_
|
||||
|
||||
USAGE
|
||||
}
|
||||
|
||||
sub finished () {
|
||||
my $self = shift;
|
||||
print "\n\nImport completed successfully\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
"Apparently, I'm true";
|
||||
689
site/slowtwitch.com/cgi-bin/articles/admin/Links/Import/L1S2.pm
Normal file
689
site/slowtwitch.com/cgi-bin/articles/admin/Links/Import/L1S2.pm
Normal file
@@ -0,0 +1,689 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# Revision : $Id: L1S2.pm,v 1.25 2005/04/16 02:11:50 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
package Links::Import::L1S2;
|
||||
|
||||
use 5.004_04;
|
||||
use strict;
|
||||
use vars qw/$Warning_Code $Critical_Code $Mild_Code $Print_Out/;
|
||||
|
||||
use GT::SQL;
|
||||
|
||||
sub critical {
|
||||
$Critical_Code->(@_);
|
||||
}
|
||||
|
||||
sub warning {
|
||||
$Warning_Code->(@_);
|
||||
}
|
||||
|
||||
sub mild_warning {
|
||||
ref $Mild_Code eq 'CODE' and $Mild_Code->(@_);
|
||||
}
|
||||
|
||||
sub import_print {
|
||||
if (ref $Print_Out eq 'CODE') {
|
||||
$Print_Out->(@_);
|
||||
}
|
||||
else {
|
||||
print @_;
|
||||
}
|
||||
}
|
||||
|
||||
# Takes 3-4 arguments: hash reference, 2 or 3 code refs
|
||||
# The hash reference is the options hash for an import.
|
||||
# The first code reference will be called when a warning occurs.
|
||||
# The second code reference will be called when a critical error occurs.
|
||||
# If provided, the third code reference will be called when a mild warning occurs
|
||||
sub import {
|
||||
my $opt = shift;
|
||||
return if ref $opt ne 'HASH';
|
||||
{
|
||||
my $warning = shift;
|
||||
return if ref $warning ne 'CODE';
|
||||
$Warning_Code = $warning;
|
||||
|
||||
my $critical = shift;
|
||||
return if ref $critical ne 'CODE';
|
||||
$Critical_Code = $critical;
|
||||
|
||||
my $mild = shift;
|
||||
$Mild_Code = $mild if ref $mild eq 'CODE';
|
||||
|
||||
my $output = shift;
|
||||
$Print_Out = $output if ref $output eq 'CODE';
|
||||
}
|
||||
|
||||
my ($have_email_db,$have_validate_db);
|
||||
|
||||
my $DB = new GT::SQL(def_path => $$opt{destination}, subclass => 0);
|
||||
my $e_prefix = $DB->prefix;
|
||||
my $e_dbh;
|
||||
{
|
||||
my $table = $DB->table("Links");
|
||||
$table->connect();
|
||||
$e_dbh = $table->{driver}->connect();
|
||||
}
|
||||
|
||||
local (*LINKS,*CATS,*EMAIL,$@,$!,*VALIDATE);
|
||||
|
||||
# Check to see if this should be a Links SQL 1.x import instead of Links 1.x.
|
||||
my $error_msg = "";
|
||||
-e "$$opt{source}/links.cfg" or $error_msg .= "$$opt{source}/links.cfg does not exist.";
|
||||
-e "$$opt{source}/Links.def" and $error_msg .= " $$opt{source}/Links.def DOES exist. Perhaps you meant to import Links SQL 1.x instead of Links 1.x?";
|
||||
critical $error_msg if $error_msg;
|
||||
|
||||
my $did = do {
|
||||
package Links1::Def::Links; # Avoid namespace pollution
|
||||
do "$$opt{source}/links.cfg";
|
||||
};
|
||||
!$did and $! and critical "Cannot open $$opt{source}/links.cfg (This error may result from links.def): $!".($@ ? ", ".substr($@,0,length($@)-1) : "");
|
||||
!$did and $@ and critical "Cannot parse $$opt{source}/links.cfg (This error may result from links.def): $@";
|
||||
$Links1::Def::Links::db_file_name or critical "links.cfg did not load correctly. Import aborted.";
|
||||
|
||||
$did = do {
|
||||
package Links1::Def::Category;
|
||||
local $ENV{PATH_INFO} = "/category";
|
||||
do "$$opt{source}/links.cfg";
|
||||
};
|
||||
!$did and $! and critical "Cannot open $$opt{source}/links.cfg (This error may result from category.def): $!".($@ ? ", ".substr($@,0,length($@)-1) : "");
|
||||
!$did and $@ and critical "Cannot parse $$opt{source}/links.cfg (This error may result from category.def): $@";
|
||||
$Links1::Def::Category::db_file_name or critical "links.cfg did not load correctly. Import aborted.";
|
||||
|
||||
open CATS, "<$Links1::Def::Category::db_file_name" or critical "Unable to open $Links1::Def::Links::db_file_name: $!";
|
||||
open LINKS, "<$Links1::Def::Links::db_file_name" or critical "Unable to open $Links1::Def::Links::db_file_name: $!";
|
||||
if (open VALIDATE, "<$Links1::Def::Links::db_valid_name") {
|
||||
$have_validate_db = 1;
|
||||
}
|
||||
else {
|
||||
warning "Could not open $Links1::Def::Links::db_valid_name: $!. Non-validated links will not be imported.";
|
||||
}
|
||||
|
||||
my %e_standard_cols = (
|
||||
Category => { map { ($_ => 1) } qw/ID Name FatherID Full_Name Description Meta_Description Meta_Keywords Header Footer Category_Template Number_of_Links Has_New_Links Has_Changed_Links Newest_Link Timestmp Payment_Mode/},
|
||||
Links => { map { ($_ => 1) } qw/ID Title URL LinkOwner Add_Date Mod_Date Description Contact_Name Contact_Email Hits isNew isChanged isPopular isValidated Rating Votes Status Date_Checked Timestmp ExpiryDate ExpiryCounted ExpiryNotify/},
|
||||
);
|
||||
|
||||
my %e_non_standard_cols;
|
||||
for my $table (keys %e_standard_cols) {
|
||||
my %cols = $DB->table($table)->cols;
|
||||
for (grep !$e_standard_cols{$table}{$_}, keys %cols) {
|
||||
$e_non_standard_cols{$table}{$_} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
my %i_standard_cols = (
|
||||
Category => { map { ($_ => 1) } qw/ID Name Description Related Header Footer/,'Meta Description','Meta Keywords' },
|
||||
Links => { map { ($_ => 1) } qw/ID Title URL Date Category Description Hits isNew isPopular/,'Contact Name','Contact Email'}
|
||||
);
|
||||
|
||||
my %i_non_standard_cols;
|
||||
$i_non_standard_cols{Links} = { map { !$i_standard_cols{Links}{$_} ? ($_ => 1) : () } @Links1::Def::Links::db_cols };
|
||||
$i_non_standard_cols{Category} = { map { !$i_standard_cols{Category}{$_} ? ($_ => 1) : () } @Links1::Def::Category::db_cols };
|
||||
|
||||
my $Links_counter;
|
||||
my $Category_counter;
|
||||
if (($DB->table('Links')->{connect}->{driver} || "") eq "ODBC") {
|
||||
$e_dbh->do("SET IDENTITY_INSERT Links ON");
|
||||
$e_dbh->do("SET IDENTITY_INSERT Category ON");
|
||||
}
|
||||
|
||||
if ($$opt{clear_tables}) {
|
||||
# Delete everything from all tables, EXCEPT for the `admin' user from the Users table
|
||||
$e_dbh->do("DELETE FROM ${e_prefix}Users WHERE Username <> 'admin'") or critical "Unable to delete all existing users: ".$e_dbh->errstr;
|
||||
for (qw/Links Category CatLinks CatRelations Category_Score_List
|
||||
Category_Word_List ClickTrack Editors EmailMailings EmailTemplates
|
||||
Links_Score_List Links_Word_List MailingIndex MailingList
|
||||
MailingListIndex Sessions Verify/) {
|
||||
$e_dbh->do("DELETE FROM $e_prefix$_");
|
||||
}
|
||||
unless ($$opt{straight_import}) {
|
||||
$Links_counter = $Category_counter = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
my $sth = $e_dbh->prepare("SELECT MAX(ID) FROM ${e_prefix}Links") or critical "Unable to prepare query `SELECT MAX(ID) FROM ${e_prefix}Links': ".$e_dbh->errstr;
|
||||
$sth->execute or critical "Unable to execute query `SELECT MAX(ID) FROM ${e_prefix}Links': ".$sth->errstr;
|
||||
$Links_counter = $sth->fetchrow_array;
|
||||
$sth->finish();
|
||||
|
||||
$sth = $e_dbh->prepare("SELECT MAX(ID) FROM ${e_prefix}Category") or critical "Unable to prepare query `SELECT MAX(ID) FROM ${e_prefix}Category': ".$e_dbh->errstr;
|
||||
$sth->execute or critical "Unable to execute query `SELECT MAX(ID) FROM ${e_prefix}Category': ".$sth->errstr;
|
||||
$Category_counter = $sth->fetchrow_array;
|
||||
$sth->finish();
|
||||
}
|
||||
|
||||
|
||||
# Categories
|
||||
my %cat_map; # $cat_map{name} = new_id
|
||||
my @num_of_links; # $num_of_links[category_id] = (the number of links in that category)
|
||||
|
||||
{
|
||||
my @cat_get_cols = ('ID','Name','Related', 'Description','Meta Description','Meta Keywords','Header','Footer');
|
||||
my $cat_ins_cols = "(ID, Name, FatherID, Full_Name, Description, Meta_Description, Meta_Keywords, Header, Footer";
|
||||
my $cat_ins_vals = "(?, ?, ?, ?, ?, ?, ?, ?, ?";
|
||||
|
||||
# Build up extra fields that exist in both old and new Category tables
|
||||
for (keys %{$e_non_standard_cols{"${e_prefix}Category"}}) {
|
||||
if ($i_non_standard_cols{Category}{$_}) {
|
||||
$cat_ins_cols .= ", $_";
|
||||
$cat_ins_vals .= ", ?";
|
||||
push @cat_get_cols, $_;
|
||||
}
|
||||
else {
|
||||
mild_warning("Custom destination column `${e_prefix}Category.$_' has no equivelant import column. It will contain the default values for the column");
|
||||
}
|
||||
}
|
||||
|
||||
for (grep !$e_non_standard_cols{"${e_prefix}Category"}{$_}, keys %{$i_non_standard_cols{Category}}) {
|
||||
if ($opt->{create_columns}) {
|
||||
if (/\W/) {
|
||||
critical "Custom import column `Category.$_' cannot be imported because is is not a valid column name. You will need to rename the column name in the def file and in any relevant templates to a new name consisting only of letters, numbers, and the _ character.";
|
||||
next;
|
||||
}
|
||||
mild_warning("Custom import column `Category.$_' had no destination equivelant. A destination column will be created");
|
||||
my $editor = $DB->editor("Category");
|
||||
$editor->add_col(
|
||||
$_,
|
||||
{
|
||||
type => 'TEXT',
|
||||
size => $Links1::Def::Category::db_max_field_length,
|
||||
($Links1::Def::Category::db_not_null{$_} ? (not_null => 1) : ()),
|
||||
($Links1::Def::Category::db_defaults{$_} ? (default => $Links1::Def::Category::db_defaults{$_}) : ()),
|
||||
($Links1::Def::Category::db_valid_types{$_} ? (regex => $Links1::Def::Category::db_valid_types{$_}) : ()),
|
||||
}
|
||||
);
|
||||
$cat_ins_cols .= ", $_";
|
||||
$cat_ins_vals .= ", ?";
|
||||
push @cat_get_cols, $_;
|
||||
|
||||
$e_non_standard_cols{"${e_prefix}Category"}{$_} = 1;
|
||||
}
|
||||
else {
|
||||
warning("Custom import column `Category.$_' has no destination equivelant. It will be ignored");
|
||||
}
|
||||
}
|
||||
$cat_ins_cols .= ")";
|
||||
$cat_ins_vals .= ")";
|
||||
|
||||
my $add_cat_relation = $e_dbh->prepare("INSERT INTO ${e_prefix}CatRelations (CategoryID, RelatedID) VALUES (?, ?)") or critical "Unable to prepare query `INSERT INTO ${e_prefix}CatRelations (CategoryID, RelatedID) VALUES (?, ?)': ".$e_dbh->errstr;
|
||||
my $count_cats_sth = $e_dbh->prepare("SELECT COUNT(*) FROM ${e_prefix}Category WHERE Full_Name = ?") or critical("Unable to prepare query `SELECT COUNT(*) FROM ${e_prefix}Category WHERE Full_Name = ?': ".$e_dbh->errstr);
|
||||
my $cat_ins_sth = $e_dbh->prepare("INSERT INTO ${e_prefix}Category $cat_ins_cols VALUES $cat_ins_vals") or critical("Unable to prepare query `INSERT INTO ${e_prefix}Category $cat_ins_cols VALUES $cat_ins_vals': ".$e_dbh->errstr);
|
||||
my $cat_ins_simple_sth = $e_dbh->prepare("INSERT INTO ${e_prefix}Category (ID, Name, Full_Name, FatherID) VALUES (?, ?, ?, ?)") or critical "Unable to prepare query `INSERT INTO ${e_prefix}Category (ID, Name, Full_Name, FatherID) VALUES (?, ?, ?, ?)': ".$e_dbh->errstr;
|
||||
my $get_id_sth = $e_dbh->prepare("SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?") or critical("Unable to prepare query `SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?': ".$e_dbh->errstr);
|
||||
|
||||
my @cat_relations;
|
||||
|
||||
my $cat_imported = 0;
|
||||
import_print "\nImporting Categories ...\n";
|
||||
my @cat_data;
|
||||
while (my $row = get_rec(\*CATS,'Category',\@Links1::Def::Category::db_cols,"|",\@cat_get_cols)) {
|
||||
push @cat_data, $row if ref $row eq 'ARRAY';
|
||||
}
|
||||
|
||||
@cat_data = sort { $a->[1] cmp $b->[1] } @cat_data;
|
||||
|
||||
my @missing_cats;
|
||||
my %missing_cats;
|
||||
for my $row (@cat_data) {
|
||||
$row = [@$row];
|
||||
my $old_id = shift @$row;
|
||||
my $new_id = $$opt{straight_import} ? $old_id : ++$Category_counter;
|
||||
my ($name) = (my $full_name = shift @$row) =~ m[([^/]*)\Z];
|
||||
unless (defined $name and length $name) {
|
||||
$Category_counter-- unless $$opt{straight_import};
|
||||
warning "Cannot insert Category $full_name because it is an invalid name";
|
||||
next;
|
||||
}
|
||||
my ($father_full_name) = $full_name =~ m[\A(.*)/];
|
||||
my $father_id;
|
||||
if (not defined $father_full_name) {
|
||||
$father_id = 0;
|
||||
}
|
||||
else {
|
||||
$get_id_sth->execute($father_full_name) or critical "Unable to execute query `SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?': ".$get_id_sth->errstr;
|
||||
if (my $ar = $get_id_sth->fetchrow_arrayref()) {
|
||||
$father_id = $ar->[0] || 0;
|
||||
}
|
||||
else {
|
||||
if ($$opt{create_missing_categories}) {
|
||||
unless ($missing_cats{$father_full_name}++) {
|
||||
unshift @missing_cats, $father_full_name;
|
||||
mild_warning "$father_full_name is needed for category $full_name and does not exist. It will be created";
|
||||
my $fn = $father_full_name;
|
||||
while ($fn =~ s[/[^/]*\Z][]) {
|
||||
$count_cats_sth->execute($fn) or critical "Unable to execute query `SELECT COUNT(*) FROM ${e_prefix}Category WHERE Full_Name = ?': ".$count_cats_sth->errstr;
|
||||
if ($count_cats_sth->fetchrow_array or $missing_cats{$fn}++) { # It exists
|
||||
last;
|
||||
}
|
||||
else {
|
||||
unshift @missing_cats, $fn;
|
||||
mild_warning "$fn is needed for category $full_name and does not exist. It will be created";
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
mild_warning "$father_full_name is also needed for category $full_name and is already in the queue to be created.";
|
||||
}
|
||||
}
|
||||
else {
|
||||
warning "No father row found for $full_name! This may be a serious error as $full_name should probably have a father category";
|
||||
}
|
||||
$father_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
$cat_relations[$new_id] = shift @$row; # This has to be dealt with later.
|
||||
|
||||
if ($$opt{data_integrity}) {
|
||||
$count_cats_sth->execute($full_name) or critical "Unable to execute query `SELECT COUNT(*) FROM ${e_prefix}Category WHERE Full_Name = ?': ".$count_cats_sth->errstr;
|
||||
unless ($count_cats_sth->fetchrow_array) {
|
||||
unless ($cat_ins_sth->execute($new_id,$name,$father_id,$full_name,@$row)) {
|
||||
$Category_counter-- unless $$opt{straight_import};
|
||||
warning "Unable to insert category `$full_name' (SQL query: `INSERT INTO ${e_prefix}Category $cat_ins_cols VALUES $cat_ins_vals'): ".$cat_ins_sth->errstr;
|
||||
next;
|
||||
}
|
||||
import_print "$cat_imported\n" unless ++$cat_imported % 500;
|
||||
$cat_map{$full_name} = $new_id;
|
||||
$num_of_links[$new_id] = 0;
|
||||
}
|
||||
else {
|
||||
--$Category_counter unless $$opt{straight_import};
|
||||
mild_warning("Duplicate category found ($full_name) and skipped");
|
||||
next;
|
||||
}
|
||||
}
|
||||
else {
|
||||
unless ($cat_ins_sth->execute($new_id,$name,$father_id,$full_name,@$row)) {
|
||||
--$Category_counter unless $$opt{straight_import};
|
||||
warning("Unable to insert category `$full_name' (SQL query: `INSERT INTO ${e_prefix}Category $cat_ins_cols VALUES $cat_ins_vals'): ".$cat_ins_sth->errstr);
|
||||
next;
|
||||
}
|
||||
import_print "$cat_imported\n" unless ++$cat_imported % 500;
|
||||
$cat_map{$full_name} = $new_id;
|
||||
$num_of_links[$new_id] = 0;
|
||||
}
|
||||
}
|
||||
my $missing_cats;
|
||||
if ($$opt{create_missing_categories} and @missing_cats) {
|
||||
my $counter = $e_dbh->prepare("SELECT MAX(ID) FROM ${e_prefix}Category");
|
||||
$counter->execute();
|
||||
my $ins_id = $counter->fetchrow_array();
|
||||
my $update_sub_cats = $e_dbh->prepare("UPDATE ${e_prefix}Category SET FatherID = ? WHERE Full_Name LIKE ? AND Full_Name NOT LIKE ?") or critical "Unable to prepare query `UPDATE ${e_prefix}Category SET FatherID = ? WHERE Full_Name LIKE ? AND Full_Name NOT LIKE ?': ".$e_dbh->errstr;
|
||||
for (@missing_cats) {
|
||||
my ($name) = m[([^/]*)\Z];
|
||||
my ($father_full) = m[\A(.*)/];
|
||||
my $father_id;
|
||||
if ($father_full) {
|
||||
$get_id_sth->execute($father_full) or critical "Unable to execute query `SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?': ".$get_id_sth->errstr;
|
||||
$father_id = $get_id_sth->fetchrow_array;
|
||||
}
|
||||
else { # Must be a category of root
|
||||
$father_id = 0;
|
||||
}
|
||||
$cat_ins_simple_sth->execute(++$ins_id,$name,$_,$father_id) or critical "Unable to create missing category $_: ".$cat_ins_simple_sth->errstr;
|
||||
$cat_map{$_} = $ins_id;
|
||||
$update_sub_cats->execute($ins_id,"$_/%","$_/%/%") or critical "Unable to execute query `UPDATE ${e_prefix}Category SET FatherID = ? WHERE Full_Name LIKE ? and Full_Name NOT LIKE ?': ".$update_sub_cats->errstr;
|
||||
$missing_cats++;
|
||||
}
|
||||
}
|
||||
|
||||
import_print "$cat_imported Categories imported";
|
||||
import_print ", $missing_cats missing categories created" if $missing_cats;
|
||||
import_print ".\n";
|
||||
|
||||
# Category Relations
|
||||
import_print "\nImporting Category Relations ...\n";
|
||||
my $cat_rel_imported = 0;
|
||||
for my $cat_id (0..$#cat_relations) {
|
||||
next unless defined $cat_relations[$cat_id];
|
||||
my @cats = split /\|/, $cat_relations[$cat_id];
|
||||
for (@cats) {
|
||||
$get_id_sth->execute($_) or critical "Unable to execute query `SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?': ".$get_id_sth->errstr;
|
||||
my $rel_id = $get_id_sth->fetchrow_array;
|
||||
if (defined $rel_id) {
|
||||
unless ($add_cat_relation->execute($cat_id,$rel_id)) {
|
||||
warning "Unable to execute query `INSERT INTO ${e_prefix}CatRelations (CategoryID, RelatedID) VALUES (?, ?)': ".$add_cat_relation->errstr;
|
||||
}
|
||||
else {
|
||||
import_print "$cat_rel_imported\n" unless ++$cat_rel_imported % 500;
|
||||
}
|
||||
}
|
||||
else {
|
||||
warning "Unable to add category relation for category with ID $cat_id and `$_'. Reason: Category `$_' not found in database.";
|
||||
}
|
||||
}
|
||||
}
|
||||
import_print "$cat_rel_imported Category Relations imported.\n";
|
||||
}
|
||||
# Links
|
||||
{
|
||||
my @links_get_cols = ('ID','Category','Date','Contact Name','Contact Email', qw/Title URL Description Hits isNew isPopular/);
|
||||
my $links_ins_cols = "(ID, LinkOwner, isValidated, Add_Date, Mod_Date, Contact_Name, Contact_Email, Title, URL, Description, Hits, isNew, isPopular";
|
||||
my $links_ins_vals = "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?";
|
||||
|
||||
for (keys %{$e_non_standard_cols{"${e_prefix}Links"}}) {
|
||||
if ($i_non_standard_cols{Links}{$_}) {
|
||||
$links_ins_cols .= ", $_";
|
||||
$links_ins_vals .= ", ?";
|
||||
push @links_get_cols, $_;
|
||||
}
|
||||
else {
|
||||
mild_warning("Custom destination column `${e_prefix}Links.$_' has no equivelant import column. It will contain the default values for the column");
|
||||
}
|
||||
}
|
||||
|
||||
for (grep $e_standard_cols{Links}{$_}, keys %{$i_non_standard_cols{Links}}) {
|
||||
$links_ins_cols .= ", $_";
|
||||
$links_ins_vals .= ", ?";
|
||||
push @links_get_cols, $_;
|
||||
}
|
||||
for (grep +(!$e_standard_cols{Links} and !$e_non_standard_cols{"${e_prefix}Links"}{$_}), keys %{$i_non_standard_cols{Links}}) {
|
||||
if ($opt->{create_columns}) {
|
||||
mild_warning("Custom import column `Links.$_' had no destination equivelant. A destination column will be created");
|
||||
my $editor = $DB->editor("Links");
|
||||
$editor->add_col(
|
||||
$_,
|
||||
{
|
||||
type => 'TEXT',
|
||||
size => $Links1::Def::Links::db_max_field_length,
|
||||
($Links1::Def::Links::db_not_null{$_} ? (not_null => 1) : ()),
|
||||
($Links1::Def::Links::db_defaults{$_} ? (default => $Links1::Def::Links::db_defaults{$_}) : ()),
|
||||
($Links1::Def::Links::db_valid_types{$_} ? (regex => $Links1::Def::Links::db_valid_types{$_}) : ()),
|
||||
}
|
||||
) or critical("Unable to add column $_: $GT::SQL::error");
|
||||
$links_ins_cols .= ", $_";
|
||||
$links_ins_vals .= ", ?";
|
||||
push @links_get_cols, $_;
|
||||
|
||||
$e_non_standard_cols{"${e_prefix}Links"}{$_} = 1;
|
||||
}
|
||||
else {
|
||||
warning("Custom import column `Links.$_' has no destination equivelant. It will be ignored");
|
||||
}
|
||||
}
|
||||
$links_ins_cols .= ")";
|
||||
$links_ins_vals .= ")";
|
||||
my $user_ins_sth = $e_dbh->prepare("INSERT INTO ${e_prefix}Users (Username, Email, Name, ReceiveMail, Status) VALUES (?, ?, ?, ?, 'Registered')") or critical("Unable to prepare query `INSERT INTO ${e_prefix}Users (Username, Email, Name, ReceiveMail, Status) VALUES (?, ?, ?, ?, 'Registered')': ".$e_dbh->errstr);
|
||||
my $cat_links_sth = $e_dbh->prepare("INSERT INTO ${e_prefix}CatLinks (LinkID, CategoryID) VALUES (?, ?)") or critical("Unable to prepare query `INSERT INTO ${e_prefix}CatLinks (LinkID, CategoryID) VALUES (?, ?)': ".$e_dbh->errstr);
|
||||
my $insert_link_sth = $e_dbh->prepare("INSERT INTO ${e_prefix}Links $links_ins_cols VALUES $links_ins_vals") or critical("Unable to prepare query `INSERT INTO ${e_prefix}Links $links_ins_cols VALUES $links_ins_vals': ".$e_dbh->errstr);
|
||||
|
||||
my $user_count_sth = $e_dbh->prepare("SELECT COUNT(*) FROM ${e_prefix}Users WHERE Email = ?") or critical("Unable to prepare query `SELECT COUNT(*) FROM ${e_prefix}Users WHERE Email = ?': ".$e_dbh->errstr);
|
||||
my $username_sth = $e_dbh->prepare("SELECT Username FROM ${e_prefix}Users WHERE Email = ?") or critical("Unable to prepare query `SELECT Username FROM ${e_prefix}Users WHERE Email = ?': ".$e_dbh->errstr);
|
||||
|
||||
# What other than the Name and ReceiveMail can be updated here?
|
||||
my $user_mod_sth = $e_dbh->prepare("UPDATE ${e_prefix}Users SET Name = ?, ReceiveMail = ? WHERE Email = ?") or critical "Unable to prepare query `UPDATE ${e_prefix}Users SET Name = ?, ReceiveMail = ? WHERE Email = ?': ".$e_dbh->errstr;
|
||||
|
||||
my $num_links_sth = $e_dbh->prepare("UPDATE ${e_prefix}Category SET Number_of_Links = ? WHERE ID = ?") or critical "Unable to prepare query `UPDATE ${e_prefix}Category SET Number_of_Links = ? WHERE ID = ?': ".$e_dbh->errstr;
|
||||
|
||||
my ($count_cats_sth,$get_cat_id_sth,$cat_ins_simple_sth);
|
||||
my $ins_id;
|
||||
if ($$opt{create_missing_categories}) {
|
||||
$count_cats_sth = $e_dbh->prepare("SELECT COUNT(*) FROM ${e_prefix}Category WHERE Full_Name = ?") or critical "Unable to prepare query `SELECT COUNT(*) FROM ${e_prefix}Category WHERE Full_Name = ?': ".$e_dbh->errstr;
|
||||
$get_cat_id_sth = $e_dbh->prepare("SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?") or critical "Unable to prepare query `SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?': ".$e_dbh->errstr;
|
||||
$cat_ins_simple_sth = $e_dbh->prepare("INSERT INTO ${e_prefix}Category (ID, Name, Full_Name, FatherID) VALUES (?, ?, ?, ?)") or critical "Unable to prepare query `INSERT INTO ${e_prefix}Category (ID, Name, Full_Name, FatherID) VALUES (?, ?, ?, ?)': ".$e_dbh->errstr;
|
||||
my $counter = $e_dbh->prepare("SELECT MAX(ID) FROM ${e_prefix}Category");
|
||||
$counter->execute();
|
||||
$ins_id = $counter->fetchrow_array();
|
||||
}
|
||||
|
||||
import_print "\nImporting Links ...\n";
|
||||
my $links_imported = 0;
|
||||
my $missing_cats = 0;
|
||||
my @more_needed; # This will hold any missing categories (such as A/B in A/B/C)
|
||||
LINK: while (my $row = get_rec(\*LINKS,'Links',\@Links1::Def::Links::db_cols,"|",\@links_get_cols)) {
|
||||
$row = [@$row]; # Remove aliasing
|
||||
my ($id, $cat_name, $date, $contact_name, $contact_email) = splice @$row,0,5;
|
||||
unshift @$row, $contact_name, $contact_email;
|
||||
$date = convert_date($date) or warning("Invalid date `$date' for link with ID $id. Link skipped."),next;
|
||||
$id = ++$Links_counter unless $$opt{straight_import};
|
||||
my $cat_id = $cat_map{$cat_name};
|
||||
unless (defined $cat_id) {
|
||||
if ($$opt{create_missing_categories} and $cat_name) {
|
||||
my @needed = my $fn = $cat_name;
|
||||
while ($fn =~ s[/[^/]*\Z][]) {
|
||||
$count_cats_sth->execute($fn) or critical "Unable to execute query `SELECT COUNT(*) FROM ${e_prefix}Category WHERE Full_Name = ?': ".$count_cats_sth->errstr;
|
||||
if ($count_cats_sth->fetchrow_array) { # It exists
|
||||
last;
|
||||
}
|
||||
else {
|
||||
unshift @needed, $fn;
|
||||
}
|
||||
}
|
||||
for (@needed) {
|
||||
my ($name) = m[([^/]+)\Z];
|
||||
unless ($name) {
|
||||
warning "Unable to create category $_ because it is an invalid name. Link ID $id will be skipped as a result.";
|
||||
last;
|
||||
}
|
||||
mild_warning("Creating category $_ as it is needed by link ID $id");
|
||||
my ($father_full) = m[\A(.*)/];
|
||||
my $father_id;
|
||||
if ($father_full) {
|
||||
$get_cat_id_sth->execute($father_full) or critical "Unable to execute query `SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?': ".$get_cat_id_sth->errstr;
|
||||
$father_id = $get_cat_id_sth->fetchrow_array;
|
||||
}
|
||||
else { # Must be a root category
|
||||
$father_id = 0;
|
||||
}
|
||||
$cat_ins_simple_sth->execute(++$ins_id,$name,$_,$father_id) or critical "Unable to create missing category $_: ".$cat_ins_simple_sth->errstr;
|
||||
$cat_map{$_} = $ins_id;
|
||||
$cat_id = $ins_id;
|
||||
$missing_cats++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
warning("Invalid category `$cat_name' for link $$row[0] (ID: $id, line $.). Link skipped"),next unless defined $cat_id;
|
||||
}
|
||||
}
|
||||
next LINK unless defined $cat_id;
|
||||
my $username;
|
||||
$user_count_sth->execute($contact_email) or warning("Unable to execute query `SELECT COUNT(*) FROM ${e_prefix}Users WHERE Email = ?': ".$user_count_sth->errstr);
|
||||
if ($user_count_sth->fetchrow_arrayref()->[0]) { # This e-mail address already exists
|
||||
$user_mod_sth->execute($contact_name, $contact_email) or warning("Unable to execute query `UPDATE ${e_prefix}Users SET Name = ?, ReceiveMail = ? WHERE Email = ?': ".$user_mod_sth->errstr);
|
||||
$username_sth->execute($contact_email) or warning("Unable to execute query: ".$username_sth->errstr);
|
||||
$username = $username_sth->fetchrow_arrayref()->[0];
|
||||
}
|
||||
elsif ($contact_email) {
|
||||
$user_ins_sth->execute(($contact_email) x 2, (defined $contact_name ? $contact_name : ""), 'Yes') or warning("Unable to execute query `INSERT INTO ${e_prefix}Users (Username, Email, Name, ReceiveMail, Status) VALUES (?, ?, ?, ?, 'Registered')': ".$user_ins_sth->errstr);
|
||||
$username = $contact_email;
|
||||
}
|
||||
else {
|
||||
mild_warning("Not enough information to add a user for link `".($$row[0] or '<unknown>')." (URL: ".($$row[1] or "<none>")."). Setting link owner to `admin'");
|
||||
$username = 'admin';
|
||||
}
|
||||
if ($insert_link_sth->execute($id,$username,'Yes',$date,$date,@$row)) {
|
||||
$cat_links_sth->execute($id,$cat_id) or warning "Unable to execute query `INSERT INTO ${e_prefix}CatLinks (LinkID, CategoryID) VALUES (?, ?)': ".$cat_links_sth->errstr;
|
||||
$num_of_links[$cat_id]++;
|
||||
|
||||
import_print "$links_imported\n" unless ++$links_imported % 500;
|
||||
}
|
||||
else {
|
||||
$Links_counter-- unless $$opt{straight_import};
|
||||
warning("Unable to insert link `$$row[0]' (SQL query: `INSERT INTO ${e_prefix}Links $links_ins_cols VALUES $links_ins_vals'): ".$insert_link_sth->errstr);
|
||||
}
|
||||
}
|
||||
import_print "$links_imported records from 'Links' imported.\n";
|
||||
|
||||
if ($have_validate_db) {
|
||||
$links_imported = 0;
|
||||
import_print "Importing records from 'Validate'.\n";
|
||||
|
||||
LINK: while(my $row = get_rec(\*VALIDATE,'Links',\@Links1::Def::Links::db_cols,"|",\@links_get_cols)) {
|
||||
$row = [@$row]; # Remove aliasing
|
||||
my ($id, $cat_name, $date, $contact_name, $contact_email) = splice @$row,0,5;
|
||||
unshift @$row, $contact_name, $contact_email;
|
||||
$date = convert_date($date) or warning("Invalid date `$date' for link with ID $id. Link skipped."),next;
|
||||
$id = ++$Links_counter unless $$opt{straight_import};
|
||||
my $cat_id = $cat_map{$cat_name};
|
||||
unless (defined $cat_id) {
|
||||
if ($$opt{create_missing_categories} and $cat_name) {
|
||||
my @needed = my $fn = $cat_name;
|
||||
while ($fn =~ s[/[^/]*\Z][]) {
|
||||
$count_cats_sth->execute($fn) or critical "Unable to execute query `SELECT COUNT(*) FROM ${e_prefix}Category WHERE Full_Name = ?': ".$count_cats_sth->errstr;
|
||||
if ($count_cats_sth->fetchrow_array) { # It exists
|
||||
last;
|
||||
}
|
||||
else {
|
||||
unshift @needed, $fn;
|
||||
}
|
||||
}
|
||||
for (@needed) {
|
||||
my ($name) = m[([^/]+)\Z];
|
||||
unless ($name) {
|
||||
warning "Unable to create category $_ because it is an invalid name. Link ID $id will be skipped as a result.";
|
||||
last;
|
||||
}
|
||||
mild_warning("Creating category $_ as it is needed by link ID $id");
|
||||
my ($father_full) = m[\A(.*)/];
|
||||
my $father_id;
|
||||
if ($father_full) {
|
||||
$get_cat_id_sth->execute($father_full) or critical "Unable to execute query `SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?': ".$get_cat_id_sth->errstr;
|
||||
$father_id = $get_cat_id_sth->fetchrow_array;
|
||||
}
|
||||
else { # Must be a root category
|
||||
$father_id = 0;
|
||||
}
|
||||
$cat_ins_simple_sth->execute(++$ins_id,$name,$_,$father_id) or critical "Unable to create missing category $_: ".$cat_ins_simple_sth->errstr;
|
||||
$cat_map{$_} = $ins_id;
|
||||
$cat_id = $ins_id;
|
||||
$missing_cats++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
warning("Invalid category `$cat_name' for link $$row[0] (ID: $id, line $.). Link skipped"),next unless defined $cat_id;
|
||||
}
|
||||
}
|
||||
next LINK unless defined $cat_id;
|
||||
my $username;
|
||||
$user_count_sth->execute($contact_email) or warning("Unable to execute query `SELECT COUNT(*) FROM ${e_prefix}Users WHERE Email = ?': ".$user_count_sth->errstr);
|
||||
if ($user_count_sth->fetchrow_arrayref()->[0]) { # This e-mail address already exists
|
||||
$user_mod_sth->execute($contact_name, 'Yes', $contact_email) or warning("Unable to execute query `UPDATE ${e_prefix}Users SET Name = ?, ReceiveMail = ? WHERE Email = ?': ".$user_mod_sth->errstr);
|
||||
$username_sth->execute($contact_email) or warning("Unable to execute query: ".$username_sth->errstr);
|
||||
$username = $username_sth->fetchrow_arrayref()->[0];
|
||||
}
|
||||
elsif ($contact_email) {
|
||||
$user_ins_sth->execute(($contact_email) x 2, (defined $contact_name ? $contact_name : ""), 'Yes') or warning("Unable to execute query `INSERT INTO ${e_prefix}Users (Username, Email, Name, ReceiveMail, Status) VALUES (?, ?, ?, ?, 'Registered')': ".$user_ins_sth->errstr);
|
||||
$username = $contact_email;
|
||||
}
|
||||
else {
|
||||
mild_warning("Not enough information to add a user for link `".($$row[0] or '<unknown>')." (URL: ".($$row[1] or "<none>")."). Setting link owner to `admin'");
|
||||
$username = 'admin';
|
||||
}
|
||||
if ($insert_link_sth->execute($id,$username,'No',$date,$date,@$row)) {
|
||||
$cat_links_sth->execute($id,$cat_id) or warning "Unable to execute query `INSERT INTO ${e_prefix}CatLinks (LinkID, CategoryID) VALUES (?, ?)': ".$cat_links_sth->errstr;
|
||||
$num_of_links[$cat_id]++;
|
||||
|
||||
import_print "$links_imported\n" unless ++$links_imported % 500;
|
||||
}
|
||||
else {
|
||||
$Links_counter-- unless $$opt{straight_import};
|
||||
warning("Unable to insert link `$$row[0]' (SQL query: `INSERT INTO ${e_prefix}Links $links_ins_cols VALUES $links_ins_vals'): ".$insert_link_sth->errstr);
|
||||
}
|
||||
}
|
||||
import_print "$links_imported records from 'Validate' imported.\n";
|
||||
}
|
||||
|
||||
import_print "$missing_cats categories have been created due to missing categories for links\n" if $missing_cats;
|
||||
|
||||
for (grep $num_of_links[$_], 0..$#num_of_links) {
|
||||
$num_links_sth->execute($num_of_links[$_],$_) or warning "Unable to execute query `UPDATE ${e_prefix}Category SET Number_of_Links = ? WHERE ID = ?': ".$num_links_sth->errstr;
|
||||
}
|
||||
}
|
||||
$e_dbh->disconnect;
|
||||
import_print "\nNOTE: You must run Rebuild Cat. tree, Repair Tables, and Rebuild Search after performing an import!\n";
|
||||
}
|
||||
|
||||
# Takes 4 options: a glob ref containing an opened filehandle, a table name, a
|
||||
# hash ref, a scalar delimiter, and (optionally) an array of fields to return.
|
||||
# The table name should be 'Links', 'CatRelations', 'Category', or 'Sessions'.
|
||||
# If you give it the fields, it will come back with an array (or array ref) of
|
||||
# the values for those fields in the order specified.
|
||||
# Otherwise, it will return a hash ref (or hash in list context) of the fields
|
||||
# in column => value format.
|
||||
#
|
||||
# Call it as %rec = get_rec(\*FH, $table_name, \@db_cols, $delimiter, \@fields);
|
||||
# You can, if you prefer, also make the delimiter a scalar reference.
|
||||
# @db_cols should be the @db_cols from Links 1.x.
|
||||
sub get_rec {
|
||||
defined wantarray or return; # Don't bother doing anything in void context
|
||||
my $fh = shift;
|
||||
my $table_name = shift;
|
||||
my $db_cols = shift;
|
||||
my $delimiter = ref $_[0] eq 'SCALAR' ? ${shift()} : shift;
|
||||
my ($fields,@fields,%fields);
|
||||
if (@_) {
|
||||
$fields = 1;
|
||||
@fields = ref $_[0] eq 'ARRAY' ? @{shift()} : @_;
|
||||
%fields = map { ($_ => 1) } @fields;
|
||||
}
|
||||
defined fileno($fh) or critical "Interal error: File handle passed to get_rec() is not an opened file";
|
||||
|
||||
local $/ = "\n";
|
||||
my $line;
|
||||
until (defined $line) {
|
||||
$line = <$fh>;
|
||||
return unless defined $line; # Catch the end of the file.
|
||||
chomp $line;
|
||||
$line ||= undef; # skip blanks
|
||||
}
|
||||
my $i = 0;
|
||||
my @rec = split /\Q$delimiter/, $line, -1;
|
||||
my %rec;
|
||||
for (@rec) {
|
||||
s/``/\n/g;
|
||||
s/~~/|/g;
|
||||
$_ = undef if $_ eq 'NULL';
|
||||
}
|
||||
for (0..$#rec) {
|
||||
if (defined $db_cols->[$_] and (!$fields or $fields{$db_cols->[$_]})) { # Skip "extra" and unwanted records
|
||||
$rec{$db_cols->[$_]} = $rec[$_];
|
||||
}
|
||||
}
|
||||
if ($table_name eq 'Links') {
|
||||
$rec{Category} =~ y/_/ / if $rec{Category};
|
||||
}
|
||||
elsif ($table_name eq 'Category') {
|
||||
$rec{Name} =~ y/_/ / if $rec{Name};
|
||||
$rec{Related} =~ y/_/ / if $rec{Related};
|
||||
}
|
||||
|
||||
$fields or return wantarray ? %rec : \%rec;
|
||||
my @ret = map $rec{$_}, @fields;
|
||||
return wantarray ? @ret : \@ret;
|
||||
}
|
||||
|
||||
# Converts a date. Returns false if the date is invalid.
|
||||
sub convert_date ($) {
|
||||
my $in = shift;
|
||||
my ($day, $mon, $year) = split /-/, $in, 3;
|
||||
my %months = qw(Jan 01 Feb 02 Mar 03 Apr 04 May 05 Jun 06 Jul 07 Aug 08 Sep 09 Oct 10 Nov 11 Dec 12);
|
||||
# Any extra fields needed should be set like this:
|
||||
# $months{Okt} = "10";
|
||||
# $months{Mai} = "05";
|
||||
# $months{Dez} = "12";
|
||||
#
|
||||
$day = sprintf "%02d", $day;
|
||||
$year = sprintf "%04d", $year;
|
||||
if ($year and $months{$mon} and $day) {
|
||||
return sprintf("%04d-$months{$mon}-%02d", $year, $day);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
# Returns a random password of random length (20-25 characters).
|
||||
sub random_pass () {
|
||||
my @chars = ('a'..'z','A'..'Z',0..9,qw a _ [ ] { } ` ' " ! @ ^ * ( ) - _ = + : ; . / \ a,'#',',');
|
||||
my $pass = join '', map { $chars[rand @chars] } 0..(20+rand(5));
|
||||
}
|
||||
|
||||
"True or not true? That is the question."
|
||||
814
site/slowtwitch.com/cgi-bin/articles/admin/Links/Import/L2S2.pm
Normal file
814
site/slowtwitch.com/cgi-bin/articles/admin/Links/Import/L2S2.pm
Normal file
@@ -0,0 +1,814 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# Revision : $Id: L2S2.pm,v 1.39 2005/04/16 02:11:50 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::Import::L2S2;
|
||||
|
||||
use 5.004_04;
|
||||
use strict;
|
||||
use vars qw/$Warning_Code $Critical_Code $Mild_Code $Print_Out/;
|
||||
|
||||
use GT::SQL;
|
||||
|
||||
sub critical {
|
||||
$Critical_Code->(@_);
|
||||
}
|
||||
|
||||
sub warning {
|
||||
$Warning_Code->(@_);
|
||||
}
|
||||
|
||||
sub mild_warning {
|
||||
ref $Mild_Code eq 'CODE' and $Mild_Code->(@_);
|
||||
}
|
||||
|
||||
sub import_print {
|
||||
if (ref $Print_Out eq 'CODE') {
|
||||
$Print_Out->(@_);
|
||||
}
|
||||
else {
|
||||
print @_;
|
||||
}
|
||||
}
|
||||
|
||||
# Takes 3-4 arguments: hash reference, 2 or 3 code refs
|
||||
# The hash reference is the options hash for an import.
|
||||
# The first code reference will be called when a warning occurs.
|
||||
# The second code reference will be called when a critical error occurs.
|
||||
# If provided, the third code reference will be called when a mild warning occurs
|
||||
sub import {
|
||||
my $opt = shift;
|
||||
return if ref $opt ne 'HASH';
|
||||
{
|
||||
my $warning = shift;
|
||||
return if ref $warning ne 'CODE';
|
||||
$Warning_Code = $warning;
|
||||
|
||||
my $critical = shift;
|
||||
return if ref $critical ne 'CODE';
|
||||
$Critical_Code = $critical;
|
||||
|
||||
my $mild = shift;
|
||||
$Mild_Code = $mild if ref $mild eq 'CODE';
|
||||
|
||||
my $output = shift;
|
||||
$Print_Out = $output if ref $output eq 'CODE';
|
||||
}
|
||||
|
||||
my ($have_email_db,$have_validate_db);
|
||||
|
||||
my $DB = new GT::SQL(def_path => $$opt{destination}, subclass => 0);
|
||||
my $e_prefix = $DB->prefix;
|
||||
my $e_dbh;
|
||||
{
|
||||
my $table = $DB->table("Links");
|
||||
$table->connect();
|
||||
$e_dbh = $table->{driver}->connect();
|
||||
}
|
||||
|
||||
local (*LINKS,*CATS,*EMAIL,$@,$!,*VALIDATE);
|
||||
my $did = do {
|
||||
package Links2::Def::Category; # Avoid namespace pollution
|
||||
do "$$opt{source}/category.def";
|
||||
};
|
||||
!$did and $@ and critical "Cannot parse $$opt{source}/category.def: $@";
|
||||
!$did and $! and critical "Cannot open $$opt{source}/category.def: $!";
|
||||
open CATS, "<$$opt{source}/data/categories.db" or critical "Unable to open $$opt{source}/data/categories.db: $!";
|
||||
$did = do {
|
||||
package Links2::Def::Links;
|
||||
do "$$opt{source}/links.def";
|
||||
};
|
||||
!$did and $@ and critical "Cannot parse $$opt{source}/links.def: $@";
|
||||
!$did and $! and critical "Cannot open $$opt{source}/links.def: $!";
|
||||
open LINKS, "<$$opt{source}/data/links.db" or critical "Unable to open $$opt{source}/data/links.db: $!";
|
||||
if (open VALIDATE, "<$$opt{source}/data/validate.db") {
|
||||
$have_validate_db = 1;
|
||||
}
|
||||
else {
|
||||
warning "Could not open $$opt{source}/data/validate.db: $!. Non-validated links will not be imported.";
|
||||
}
|
||||
if (open EMAIL, "$$opt{source}/data/email.db") {
|
||||
$have_email_db = 1;
|
||||
}
|
||||
else {
|
||||
warning "Could not open $$opt{source}/data/email.db: $!. No newsletter users will be imported.";
|
||||
}
|
||||
|
||||
my %e_standard_cols = (
|
||||
Category => { map { ($_ => 1) } qw/ID Name FatherID Full_Name Description Meta_Description Meta_Keywords Header Footer Category_Template Number_of_Links Has_New_Links Has_Changed_Links Newest_Link Timestmp Payment_Mode/},
|
||||
Links => { map { ($_ => 1) } qw/ID Title URL LinkOwner Add_Date Mod_Date Description Contact_Name Contact_Email Hits isNew isChanged isPopular isValidated Rating Votes Status Date_Checked Timestmp ExpiryDate ExpiryCounted ExpiryNotify/},
|
||||
);
|
||||
|
||||
my %e_non_standard_cols;
|
||||
for my $table (keys %e_standard_cols) {
|
||||
my %cols = $DB->table($table)->cols;
|
||||
for (grep !$e_standard_cols{$table}{$_}, keys %cols) {
|
||||
$e_non_standard_cols{$table}{$_} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
my %i_standard_cols = (
|
||||
Category => { map { ($_ => 1) } qw/ID Name Description Related Header Footer/,'Meta Description','Meta Keywords' },
|
||||
Links => { map { ($_ => 1) } qw/ID Title URL Date Category Description Hits isNew isPopular Rating Votes ReceiveMail/,'Contact Name','Contact Email'}
|
||||
);
|
||||
|
||||
my %i_non_standard_cols;
|
||||
$i_non_standard_cols{Links} = { map { !$i_standard_cols{Links}{$_} ? ($_ => 1) : () } keys %Links2::Def::Links::db_def };
|
||||
$i_non_standard_cols{Category} = { map { !$i_standard_cols{Category}{$_} ? ($_ => 1) : () } keys %Links2::Def::Category::db_def };
|
||||
|
||||
my $alt_categories = delete $i_non_standard_cols{Links}{AltCategories};
|
||||
|
||||
my $Links_counter;
|
||||
my $Category_counter;
|
||||
my $odbc = 0;
|
||||
if ($DB->table('Links')->{connect}->{driver} eq 'ODBC') {
|
||||
$odbc = 1;
|
||||
}
|
||||
|
||||
if ($$opt{clear_tables}) {
|
||||
# Delete everything from all tables, EXCEPT for the `admin' user from the Users table
|
||||
$e_dbh->do("DELETE FROM ${e_prefix}Users WHERE Username <> 'admin'") or critical "Unable to delete all existing users: ".$e_dbh->errstr;
|
||||
for (qw/Links Category CatLinks CatRelations Category_Score_List
|
||||
Category_Word_List ClickTrack Editors EmailMailings EmailTemplates
|
||||
Links_Score_List Links_Word_List MailingIndex MailingList
|
||||
MailingListIndex Sessions Verify/) {
|
||||
$e_dbh->do("DELETE FROM $e_prefix$_");
|
||||
}
|
||||
unless ($$opt{straight_import}) {
|
||||
$Links_counter = $Category_counter = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
my $sth = $e_dbh->prepare("SELECT MAX(ID) FROM ${e_prefix}Links") or critical "Unable to prepare query `SELECT MAX(ID) FROM ${e_prefix}Links': ".$e_dbh->errstr;
|
||||
$sth->execute or critical "Unable to execute query `SELECT MAX(ID) FROM ${e_prefix}Links': ".$sth->errstr;
|
||||
$Links_counter = $sth->fetchrow_array;
|
||||
$sth->finish();
|
||||
|
||||
$sth = $e_dbh->prepare("SELECT MAX(ID) FROM ${e_prefix}Category") or critical "Unable to prepare query `SELECT MAX(ID) FROM ${e_prefix}Category': ".$e_dbh->errstr;
|
||||
$sth->execute or critical "Unable to execute query `SELECT MAX(ID) FROM ${e_prefix}Category': ".$sth->errstr;
|
||||
$Category_counter = $sth->fetchrow_array;
|
||||
$sth->finish();
|
||||
}
|
||||
|
||||
# Subscribe users - these users receive the newsletter.
|
||||
if ($have_email_db) {
|
||||
my $count_users = $e_dbh->prepare("SELECT COUNT(*) FROM ${e_prefix}Users WHERE Email = ?");
|
||||
my $add_user = $e_dbh->prepare("INSERT INTO ${e_prefix}Users (Name, Username, Password, Email, ReceiveMail) VALUES (?, ?, ?, ?, 'Yes')");
|
||||
my $give_newsletter = $e_dbh->prepare("UPDATE ${e_prefix}Users SET ReceiveMail = 'Yes' WHERE Email = ?");
|
||||
|
||||
my $sub_imported = 0;
|
||||
import_print "\nImporting Subscribe users (newsletter receivers) ...\n";
|
||||
while (<EMAIL>) {
|
||||
chomp;
|
||||
my ($email,$name) = split /\|/;
|
||||
$name ||= "";
|
||||
$count_users->execute($email) or warning("Unable to count users with email $email: ".$count_users->errstr), next;
|
||||
if ($count_users->fetchrow_array) {
|
||||
$give_newsletter->execute($email) or warning("Unable to set ReceiveMail = 'Yes' for user with e-mail $email: ".$give_newsletter->errstr),--$sub_imported;
|
||||
}
|
||||
else { # User doesn't already exist
|
||||
$add_user->execute($name, $email, random_pass(), $email) or warning("Unable to insert user $email: ".$add_user->errstr),--$sub_imported;
|
||||
}
|
||||
import_print "$sub_imported\n" unless ++$sub_imported % 500;
|
||||
}
|
||||
import_print "$sub_imported Subscribed users imported.\n";
|
||||
}
|
||||
|
||||
|
||||
# Categories
|
||||
my %cat_map; # $cat_map{name} = new_id
|
||||
my @num_of_links; # $num_of_links[category_id] = (the number of links in that category)
|
||||
|
||||
{
|
||||
my @cat_get_cols = ('ID','Name','Related', 'Description','Meta Description','Meta Keywords','Header','Footer');
|
||||
my $cat_ins_cols = "(ID, Name, FatherID, Full_Name, Description, Meta_Description, Meta_Keywords, Header, Footer";
|
||||
my $cat_ins_vals = "(?, ?, ?, ?, ?, ?, ?, ?, ?";
|
||||
|
||||
# Build up extra fields that exist in both old and new Category tables
|
||||
for (keys %{$e_non_standard_cols{"${e_prefix}Category"}}) {
|
||||
if ($i_non_standard_cols{Category}{$_}) {
|
||||
$cat_ins_cols .= ", $_";
|
||||
$cat_ins_vals .= ", ?";
|
||||
push @cat_get_cols, $_;
|
||||
}
|
||||
else {
|
||||
mild_warning("Custom destination column `${e_prefix}Category.$_' has no equivelant import column. It will contain the default values for the column");
|
||||
}
|
||||
}
|
||||
|
||||
for (grep !$e_non_standard_cols{"${e_prefix}Category"}{$_}, keys %{$i_non_standard_cols{Category}}) {
|
||||
if ($opt->{create_columns}) {
|
||||
if (/\W/) {
|
||||
critical "Custom import column `Category.$_' cannot be imported because is is not a valid column name. You will need to rename the column name in the def file and in any relevant templates to a new name consisting only of letters, numbers, and the _ character.";
|
||||
next;
|
||||
}
|
||||
mild_warning("Custom import column `Category.$_' had no destination equivelant. A destination column will be created");
|
||||
my $editor = $DB->editor("Category");
|
||||
my @def = @{$Links2::Def::Category::db_def{$_}};
|
||||
$editor->add_col(
|
||||
$_,
|
||||
{
|
||||
type => ((uc $def[1] eq 'ALPHA' and $def[3] > 255) ? 'TEXT' : 'CHAR'),
|
||||
($def[2] ? (form_size => ((index($def[2],"x") > -1) ? [split(/x/,$def[2],2)] : $def[2])) : ()),
|
||||
size => $def[3],
|
||||
($def[4] ? (not_null => 1) : ()),
|
||||
($def[5] ? (default => $def[5]) : ()),
|
||||
($def[6] ? (regex => $def[6]) : ()),
|
||||
}
|
||||
);
|
||||
$cat_ins_cols .= ", $_";
|
||||
$cat_ins_vals .= ", ?";
|
||||
push @cat_get_cols, $_;
|
||||
|
||||
$e_non_standard_cols{"${e_prefix}Category"}{$_} = 1;
|
||||
}
|
||||
else {
|
||||
warning("Custom import column `Category.$_' has no destination equivelant. It will be ignored");
|
||||
}
|
||||
}
|
||||
$cat_ins_cols .= ")";
|
||||
$cat_ins_vals .= ")";
|
||||
|
||||
my $add_cat_relation = $e_dbh->prepare("INSERT INTO ${e_prefix}CatRelations (CategoryID, RelatedID) VALUES (?, ?)") or critical "Unable to prepare query `INSERT INTO ${e_prefix}CatRelations (CategoryID, RelatedID) VALUES (?, ?)': ".$e_dbh->errstr;
|
||||
my $count_cats_sth = $e_dbh->prepare("SELECT COUNT(*) FROM ${e_prefix}Category WHERE Full_Name = ?") or critical("Unable to prepare query `SELECT COUNT(*) FROM ${e_prefix}Category WHERE Full_Name = ?': ".$e_dbh->errstr);
|
||||
my $cat_ins_sth = $odbc
|
||||
? ($e_dbh->prepare("SET IDENTITY_INSERT ${e_prefix}Category ON; INSERT INTO ${e_prefix}Category $cat_ins_cols VALUES $cat_ins_vals") or critical("Unable to prepare query `INSERT INTO ${e_prefix}Category $cat_ins_cols VALUES $cat_ins_vals': ".$e_dbh->errstr))
|
||||
: ($e_dbh->prepare("INSERT INTO ${e_prefix}Category $cat_ins_cols VALUES $cat_ins_vals") or critical("Unable to prepare query `INSERT INTO ${e_prefix}Category $cat_ins_cols VALUES $cat_ins_vals': ".$e_dbh->errstr));
|
||||
|
||||
my $cat_ins_simple_sth = $odbc
|
||||
? ($e_dbh->prepare("SET IDENTITY_INSERT ${e_prefix}Category ON; INSERT INTO ${e_prefix}Category (ID, Name, Full_Name, FatherID) VALUES (?, ?, ?, ?)") or critical("Unable to prepare query `INSERT INTO ${e_prefix}Category (ID, Name, Full_Name, FatherID) VALUES (?, ?, ?, ?)': ".$e_dbh->errstr))
|
||||
: ($e_dbh->prepare("INSERT INTO ${e_prefix}Category (ID, Name, Full_Name, FatherID) VALUES (?, ?, ?, ?)") or critical("Unable to prepare query `INSERT INTO ${e_prefix}Category (ID, Name, Full_Name, FatherID) VALUES (?, ?, ?, ?)': ".$e_dbh->errstr));
|
||||
|
||||
my $get_id_sth = $e_dbh->prepare("SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?") or critical("Unable to prepare query `SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?': ".$e_dbh->errstr);
|
||||
|
||||
{
|
||||
my ($no_warning) = (
|
||||
$Links2::Def::Category::db_delim,
|
||||
$Links2::Def::Links::db_delim
|
||||
)
|
||||
}
|
||||
|
||||
my @cat_relations;
|
||||
|
||||
my $cat_imported = 0;
|
||||
import_print "\nImporting Categories ...\n";
|
||||
my @cat_data;
|
||||
while (my $row = get_rec(\*CATS,'Category',\%Links2::Def::Category::db_def,\$Links2::Def::Category::db_delim,\@cat_get_cols)) {
|
||||
push @cat_data, $row if ref $row eq 'ARRAY';
|
||||
}
|
||||
|
||||
@cat_data = sort { $a->[0] cmp $b->[0] } @cat_data;
|
||||
|
||||
my @missing_cats;
|
||||
my %missing_cats;
|
||||
for my $row (@cat_data) {
|
||||
$row = [@$row];
|
||||
my $old_id = shift @$row;
|
||||
my $new_id = $$opt{straight_import} ? $old_id : ++$Category_counter;
|
||||
my ($name) = (my $full_name = shift @$row) =~ m[([^/]*)\Z];
|
||||
unless (defined $name and length $name) {
|
||||
$Category_counter-- unless $$opt{straight_import};
|
||||
warning "Cannot insert Category $full_name because it is an invalid name";
|
||||
next;
|
||||
}
|
||||
my ($father_full_name) = $full_name =~ m[\A(.*)/];
|
||||
my $father_id;
|
||||
if (not defined $father_full_name) {
|
||||
$father_id = 0;
|
||||
}
|
||||
else {
|
||||
$get_id_sth->execute($father_full_name) or critical "Unable to execute query `SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?': ".$get_id_sth->errstr;
|
||||
if (my $ar = $get_id_sth->fetchrow_arrayref()) {
|
||||
$father_id = $ar->[0] || 0;
|
||||
}
|
||||
else {
|
||||
my $ins_pos = @missing_cats;
|
||||
if ($$opt{create_missing_categories}) {
|
||||
unless ($missing_cats{$father_full_name}++) {
|
||||
splice @missing_cats, $ins_pos, 0, $father_full_name;
|
||||
mild_warning "$father_full_name is needed for category $full_name and does not exist. It will be created";
|
||||
my $fn = $father_full_name;
|
||||
while ($fn =~ s[/[^/]*\Z][]) {
|
||||
$count_cats_sth->execute($fn) or critical "Unable to execute query `SELECT COUNT(*) FROM ${e_prefix}Category WHERE Full_Name = ?': ".$count_cats_sth->errstr;
|
||||
if ($count_cats_sth->fetchrow_array or $missing_cats{$fn}++) { # It exists
|
||||
$count_cats_sth->finish;
|
||||
last;
|
||||
}
|
||||
else {
|
||||
splice @missing_cats, $ins_pos, 0, $fn;
|
||||
mild_warning "$fn is needed for category $full_name and does not exist. It will be created";
|
||||
$count_cats_sth->finish;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
mild_warning "$father_full_name is also needed for category $full_name and is already in the queue to be created.";
|
||||
}
|
||||
}
|
||||
else {
|
||||
warning "No father row found for $full_name! This may be a serious error as $full_name should probably have a father category";
|
||||
}
|
||||
$father_id = 0;
|
||||
}
|
||||
$get_id_sth->finish;
|
||||
}
|
||||
|
||||
$cat_relations[$new_id] = shift @$row; # This has to be dealt with later.
|
||||
|
||||
if ($$opt{data_integrity}) {
|
||||
$count_cats_sth->execute($full_name) or critical "Unable to execute query `SELECT COUNT(*) FROM ${e_prefix}Category WHERE Full_Name = ?': ".$count_cats_sth->errstr;
|
||||
unless ($count_cats_sth->fetchrow_array) {
|
||||
unless ($cat_ins_sth->execute($new_id,$name,$father_id,$full_name,@$row)) {
|
||||
$Category_counter-- unless $$opt{straight_import};
|
||||
warning "Unable to insert category `$full_name' (SQL query: `INSERT INTO ${e_prefix}Category $cat_ins_cols VALUES $cat_ins_vals'): ".$cat_ins_sth->errstr;
|
||||
$count_cats_sth->finish;
|
||||
next;
|
||||
}
|
||||
import_print "$cat_imported\n" unless ++$cat_imported % 500;
|
||||
$cat_map{$full_name} = $new_id;
|
||||
$num_of_links[$new_id] = 0;
|
||||
$count_cats_sth->finish;
|
||||
}
|
||||
else {
|
||||
--$Category_counter unless $$opt{straight_import};
|
||||
mild_warning("Duplicate category found ($full_name) and skipped");
|
||||
$count_cats_sth->finish;
|
||||
next;
|
||||
}
|
||||
}
|
||||
elsif (!$cat_map{$full_name}) {
|
||||
unless ($cat_ins_sth->execute($new_id,$name,$father_id,$full_name,@$row)) {
|
||||
--$Category_counter unless $$opt{straight_import};
|
||||
warning("Unable to insert category `$full_name' (SQL query: `INSERT INTO ${e_prefix}Category $cat_ins_cols VALUES $cat_ins_vals'): ".$cat_ins_sth->errstr);
|
||||
next;
|
||||
}
|
||||
import_print "$cat_imported\n" unless ++$cat_imported % 500;
|
||||
$cat_map{$full_name} = $new_id;
|
||||
$num_of_links[$new_id] = 0;
|
||||
}
|
||||
else {
|
||||
--$Category_counter unless $$opt{straight_import};
|
||||
mild_warning("Duplicate category found ($full_name) and skipped");
|
||||
next;
|
||||
}
|
||||
}
|
||||
my $missing_cats;
|
||||
if ($$opt{create_missing_categories} and @missing_cats) {
|
||||
my $counter = $e_dbh->prepare("SELECT MAX(ID) FROM ${e_prefix}Category");
|
||||
$counter->execute();
|
||||
my $ins_id = $counter->fetchrow_array();
|
||||
my $update_sub_cats = $e_dbh->prepare("UPDATE ${e_prefix}Category SET FatherID = ? WHERE Full_Name LIKE ? AND Full_Name NOT LIKE ?") or critical "Unable to prepare query `UPDATE ${e_prefix}Category SET FatherID = ? WHERE Full_Name LIKE ? AND Full_Name NOT LIKE ?': ".$e_dbh->errstr;
|
||||
for (@missing_cats) {
|
||||
if ($cat_map{$_}) { # Already exists
|
||||
$update_sub_cats->execute($cat_map{$_},"$_/%","$_/%/%") or critical "Unable to execute query `UPDATE ${e_prefix}Category SET FatherID = ? WHERE Full_Name LIKE ? and Full_Name NOT LIKE ?': ".$update_sub_cats->errstr;
|
||||
next;
|
||||
}
|
||||
my ($name) = m[([^/]*)\Z];
|
||||
my ($father_full) = m[\A(.*)/];
|
||||
my $father_id;
|
||||
if ($father_full and exists $cat_map{$father_full}) {
|
||||
$father_id = $cat_map{$father_full};
|
||||
}
|
||||
elsif ($father_full) {
|
||||
$get_id_sth->execute($father_full) or critical "Unable to execute query `SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?': ".$get_id_sth->errstr;
|
||||
$father_id = $get_id_sth->fetchrow_array;
|
||||
}
|
||||
else { # Must be a category of root
|
||||
$father_id = 0;
|
||||
}
|
||||
$cat_ins_simple_sth->execute(++$ins_id,$name,$_,$father_id) or critical "Unable to create missing category $_: ".$cat_ins_simple_sth->errstr;
|
||||
$cat_map{$_} = $ins_id;
|
||||
$update_sub_cats->execute($ins_id,"$_/%","$_/%/%") or critical "Unable to execute query `UPDATE ${e_prefix}Category SET FatherID = ? WHERE Full_Name LIKE ? and Full_Name NOT LIKE ?': ".$update_sub_cats->errstr;
|
||||
$missing_cats++;
|
||||
}
|
||||
}
|
||||
|
||||
import_print "$cat_imported Categories imported";
|
||||
import_print ", $missing_cats missing categories created" if $missing_cats;
|
||||
import_print ".\n";
|
||||
|
||||
# Category Relations
|
||||
import_print "\nImporting Category Relations ...\n";
|
||||
my $cat_rel_imported = 0;
|
||||
for my $cat_id (0..$#cat_relations) {
|
||||
next unless defined $cat_relations[$cat_id];
|
||||
my @cats = split /\Q$Links2::Def::Category::db_delim/, $cat_relations[$cat_id];
|
||||
for (@cats) {
|
||||
$get_id_sth->execute($_) or critical "Unable to execute query `SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ? <- $_': ".$get_id_sth->errstr;
|
||||
my $rel_id = $get_id_sth->fetchrow_array;
|
||||
if (defined $rel_id) {
|
||||
unless ($add_cat_relation->execute($cat_id,$rel_id)) {
|
||||
warning "Unable to execute query `INSERT INTO ${e_prefix}CatRelations (CategoryID, RelatedID) VALUES (?, ?)': ".$add_cat_relation->errstr;
|
||||
}
|
||||
else {
|
||||
import_print "$cat_rel_imported\n" unless ++$cat_rel_imported % 500;
|
||||
}
|
||||
}
|
||||
else {
|
||||
warning "Unable to add category relation for category with ID $cat_id and `$_'. Reason: Category `$_' not found in database.";
|
||||
}
|
||||
$get_id_sth->finish;
|
||||
}
|
||||
}
|
||||
import_print "$cat_rel_imported Category Relations imported.\n";
|
||||
}
|
||||
# Links
|
||||
{
|
||||
my @links_get_cols = ('ID','Category','Date','Contact Name','Contact Email','ReceiveMail', qw/Title URL Description Hits isNew isPopular Rating Votes/);
|
||||
my $links_ins_cols = "(ID, LinkOwner, isValidated, Add_Date, Mod_Date, Contact_Name, Contact_Email, Title, URL, Description, Hits, isNew, isPopular, Rating, Votes";
|
||||
my $links_ins_vals = "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?";
|
||||
|
||||
for (keys %{$e_non_standard_cols{"${e_prefix}Links"}}) {
|
||||
if ($i_non_standard_cols{Links}{$_}) {
|
||||
$links_ins_cols .= ", $_";
|
||||
$links_ins_vals .= ", ?";
|
||||
push @links_get_cols, $_;
|
||||
}
|
||||
else {
|
||||
mild_warning("Custom destination column `${e_prefix}Links.$_' has no equivelant import column. It will contain the default values for the column");
|
||||
}
|
||||
}
|
||||
|
||||
for (grep !$e_non_standard_cols{"${e_prefix}Links"}{$_}, keys %{$i_non_standard_cols{Links}}) {
|
||||
if ($opt->{create_columns}) {
|
||||
mild_warning("Custom import column `Links.$_' had no destination equivelant. A destination column will be created");
|
||||
my $editor = $DB->editor("Links");
|
||||
my @def = @{$Links2::Def::Links::db_def{$_}};
|
||||
$editor->add_col(
|
||||
$_,
|
||||
{
|
||||
type => ((uc $def[1] eq 'ALPHA' and $def[3] > 255) ? 'TEXT' : 'CHAR'),
|
||||
($def[2] ? (form_size => ((index($def[2],"x") > -1) ? [split(/x/,$def[2],2)] : $def[2])) : ()),
|
||||
size => $def[3],
|
||||
($def[4] ? (not_null => 1) : ()),
|
||||
($def[5] ? (default => $def[5]) : ()),
|
||||
($def[6] ? (regex => $def[6]) : ())
|
||||
}
|
||||
);
|
||||
$links_ins_cols .= ", $_";
|
||||
$links_ins_vals .= ", ?";
|
||||
push @links_get_cols, $_;
|
||||
|
||||
$e_non_standard_cols{"${e_prefix}Links"}{$_} = 1;
|
||||
}
|
||||
else {
|
||||
warning("Custom import column `Links.$_' has no destination equivelant. It will be ignored");
|
||||
}
|
||||
}
|
||||
$links_ins_cols .= ")";
|
||||
$links_ins_vals .= ")";
|
||||
|
||||
unshift @links_get_cols, "AltCategories" if $alt_categories;
|
||||
|
||||
my $user_ins_sth = $e_dbh->prepare("INSERT INTO ${e_prefix}Users (Password, Username, Email, Name, ReceiveMail, Status) VALUES (?, ?, ?, ?, ?, 'Registered')") or critical("Unable to prepare query `INSERT INTO ${e_prefix}Users (Username, Email, Name, ReceiveMail, Status) VALUES (?, ?, ?, ?, ?, 'Registered')': ".$e_dbh->errstr);
|
||||
my $cat_links_sth = $e_dbh->prepare("INSERT INTO ${e_prefix}CatLinks (LinkID, CategoryID) VALUES (?, ?)") or critical("Unable to prepare query `INSERT INTO ${e_prefix}CatLinks (LinkID, CategoryID) VALUES (?, ?)': ".$e_dbh->errstr);
|
||||
my $insert_link_sth = $odbc
|
||||
? ($e_dbh->prepare("SET IDENTITY_INSERT ${e_prefix}Links ON; INSERT INTO ${e_prefix}Links $links_ins_cols VALUES $links_ins_vals") or critical("Unable to prepare query `INSERT INTO ${e_prefix}Links $links_ins_cols VALUES $links_ins_vals': ".$e_dbh->errstr))
|
||||
: ($e_dbh->prepare("INSERT INTO ${e_prefix}Links $links_ins_cols VALUES $links_ins_vals") or critical("Unable to prepare query `INSERT INTO ${e_prefix}Links $links_ins_cols VALUES $links_ins_vals': ".$e_dbh->errstr));
|
||||
|
||||
|
||||
my $user_count_sth = $e_dbh->prepare("SELECT COUNT(*) FROM ${e_prefix}Users WHERE Email = ?") or critical("Unable to prepare query `SELECT COUNT(*) FROM ${e_prefix}Users WHERE Email = ?': ".$e_dbh->errstr);
|
||||
my $username_sth = $e_dbh->prepare("SELECT Username FROM ${e_prefix}Users WHERE Email = ?") or critical("Unable to prepare query `SELECT Username FROM ${e_prefix}Users WHERE Email = ?': ".$e_dbh->errstr);
|
||||
|
||||
# What other than the Name and ReceiveMail can be updated here?
|
||||
my $user_mod_sth = $e_dbh->prepare("UPDATE ${e_prefix}Users SET Name = ?, ReceiveMail = ? WHERE Email = ?") or critical "Unable to prepare query `UPDATE ${e_prefix}Users SET Name = ?, ReceiveMail = ? WHERE Email = ?': ".$e_dbh->errstr;
|
||||
|
||||
my $num_links_sth = $e_dbh->prepare("UPDATE ${e_prefix}Category SET Number_of_Links = ? WHERE ID = ?") or critical "Unable to prepare query `UPDATE ${e_prefix}Category SET Number_of_Links = ? WHERE ID = ?': ".$e_dbh->errstr;
|
||||
|
||||
my ($count_cats_sth,$get_cat_id_sth,$cat_ins_simple_sth);
|
||||
my $ins_id;
|
||||
if ($$opt{create_missing_categories}) {
|
||||
$count_cats_sth = $e_dbh->prepare("SELECT COUNT(*) FROM ${e_prefix}Category WHERE Full_Name = ?") or critical "Unable to prepare query `SELECT COUNT(*) FROM ${e_prefix}Category WHERE Full_Name = ?': ".$e_dbh->errstr;
|
||||
$get_cat_id_sth = $e_dbh->prepare("SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?") or critical "Unable to prepare query `SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?': ".$e_dbh->errstr;
|
||||
$cat_ins_simple_sth = $odbc
|
||||
? ($e_dbh->prepare("SET IDENTITY_INSERT ${e_prefix}Category ON; INSERT INTO ${e_prefix}Category (ID, Name, Full_Name, FatherID) VALUES (?, ?, ?, ?)") or critical "Unable to prepare query `INSERT INTO ${e_prefix}Category (ID, Name, Full_Name, FatherID) VALUES (?, ?, ?, ?)': ".$e_dbh->errstr)
|
||||
: ($e_dbh->prepare("INSERT INTO ${e_prefix}Category (ID, Name, Full_Name, FatherID) VALUES (?, ?, ?, ?)") or critical "Unable to prepare query `INSERT INTO ${e_prefix}Category (ID, Name, Full_Name, FatherID) VALUES (?, ?, ?, ?)': ".$e_dbh->errstr);
|
||||
my $counter = $e_dbh->prepare("SELECT MAX(ID) FROM ${e_prefix}Category");
|
||||
$counter->execute();
|
||||
$ins_id = $counter->fetchrow_array();
|
||||
}
|
||||
|
||||
import_print "\nImporting Links ...\n";
|
||||
my $links_imported = 0;
|
||||
my $missing_cats = 0;
|
||||
my @more_needed; # This will hold any missing categories (such as A/B in A/B/C)
|
||||
LINK: while (my $row = get_rec(\*LINKS,'Links',\%Links2::Def::Links::db_def,\$Links2::Def::Links::db_delim,\@links_get_cols)) {
|
||||
$row = [@$row]; # Remove aliasing
|
||||
my $alt_cats;
|
||||
$alt_cats = shift @$row if $alt_categories;
|
||||
my ($id, $cat_name, $date, $contact_name, $contact_email, $receive_mail) = splice @$row,0,6;
|
||||
unshift @$row, $contact_name, $contact_email;
|
||||
$date = convert_date($date) or warning("Invalid date for link with ID $id. Link skipped."),next;
|
||||
$id = ++$Links_counter unless $$opt{straight_import};
|
||||
my @category_alternates;
|
||||
if ($alt_categories) {
|
||||
@category_alternates = split /\Q$Links2::Def::Links::db_delim/, $alt_cats;
|
||||
for (@category_alternates) { y/_/ / }
|
||||
my %dups;
|
||||
# Get rid of duplicates
|
||||
@category_alternates = grep !$dups{$_}++, @category_alternates;
|
||||
}
|
||||
my @cats = ($cat_name,@category_alternates);
|
||||
my @cat_ids = @cat_map{@cats};
|
||||
my $bad_cats = 0;
|
||||
for my $j (0..$#cats) {
|
||||
my $cat_id = $cat_ids[$j];
|
||||
my $cat_name = $cats[$j];
|
||||
unless (defined $cat_id) {
|
||||
if ($$opt{create_missing_categories} and $cat_name) {
|
||||
my @needed = my $fn = $cat_name;
|
||||
while ($fn =~ s[/[^/]*\Z][]) {
|
||||
$count_cats_sth->execute($fn) or critical "Unable to execute query `SELECT COUNT(*) FROM ${e_prefix}Category WHERE Full_Name = ?': ".$count_cats_sth->errstr;
|
||||
if ($count_cats_sth->fetchrow_array) { # It exists
|
||||
$count_cats_sth->finish;
|
||||
last;
|
||||
}
|
||||
else {
|
||||
$count_cats_sth->finish;
|
||||
unshift @needed, $fn;
|
||||
}
|
||||
}
|
||||
for (@needed) {
|
||||
my ($name) = m[([^/]+)\Z];
|
||||
unless ($name) {
|
||||
warning "Unable to create category $_ because it is an invalid name.";
|
||||
$bad_cats++;
|
||||
last;
|
||||
}
|
||||
mild_warning("Creating category $_ as it is needed by link ID $id");
|
||||
my ($father_full) = m[\A(.*)/];
|
||||
my $father_id;
|
||||
if ($father_full) {
|
||||
$get_cat_id_sth->execute($father_full) or critical "Unable to execute query `SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?': ".$get_cat_id_sth->errstr;
|
||||
$father_id = $get_cat_id_sth->fetchrow_array;
|
||||
$get_cat_id_sth->finish;
|
||||
}
|
||||
else { # Must be a root category
|
||||
$father_id = 0;
|
||||
}
|
||||
$cat_ins_simple_sth->execute(++$ins_id,$name,$_,$father_id) or critical "Unable to create missing category $_: ".$cat_ins_simple_sth->errstr;
|
||||
$cat_map{$_} = $ins_id;
|
||||
$cat_ids[$j] = $ins_id;
|
||||
$missing_cats++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$bad_cats++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($bad_cats == @cat_ids) { # Between the category and the alternate categories, there has to be at least ONE good one.
|
||||
if (@cat_ids == 1) {
|
||||
warning "Invalid category `$cat_ids[0]' for link $$row[0] (ID: $id, line $.). Link skipped";
|
||||
next LINK;
|
||||
}
|
||||
else {
|
||||
warning "No valid categories ($cat_name @category_alternates) for link $$row[0] (ID: $id, line $.). Link skipped";
|
||||
next LINK;
|
||||
}
|
||||
}
|
||||
my $username;
|
||||
$user_count_sth->execute($contact_email) or warning("Unable to execute query `SELECT COUNT(*) FROM ${e_prefix}Users WHERE Email = ?': ".$user_count_sth->errstr);
|
||||
if ($user_count_sth->fetchrow_arrayref()->[0]) { # This e-mail address already exists
|
||||
$user_mod_sth->execute($contact_name, ($receive_mail eq 'Yes' ? 'Yes' : 'No'), $contact_email) or warning("Unable to execute query `UPDATE ${e_prefix}Users SET Name = ?, ReceiveMail = ? WHERE Email = ?': ".$user_mod_sth->errstr);
|
||||
$username_sth->execute($contact_email) or warning("Unable to execute query: ".$username_sth->errstr);
|
||||
$username = $username_sth->fetchrow_arrayref()->[0];
|
||||
$username_sth->finish;
|
||||
}
|
||||
elsif ($contact_email) {
|
||||
$user_ins_sth->execute('', ($contact_email) x 2, (defined $contact_name ? $contact_name : ""), ($receive_mail eq 'Yes' ? 'Yes' : 'No')) or warning("Unable to execute query `INSERT INTO ${e_prefix}Users (Username, Email, Name, ReceiveMail, Status) VALUES (?, ?, ?, ?, ?, 'Registered')': ".$user_ins_sth->errstr);
|
||||
$username = $contact_email;
|
||||
}
|
||||
else {
|
||||
mild_warning("Not enough information to add a user for link `".($$row[0] or '<unknown>')." (URL: ".($$row[1] or "<none>")."). Setting link owner to `admin'");
|
||||
$username = 'admin';
|
||||
}
|
||||
$user_count_sth->finish;
|
||||
if ($insert_link_sth->execute($id,$username,'Yes',$date,$date,@$row)) {
|
||||
for my $cat_id (@cat_ids) {
|
||||
$cat_links_sth->execute($id,$cat_id) or warning "Unable to execute query `INSERT INTO ${e_prefix}CatLinks (LinkID, CategoryID) VALUES (?, ?)': ".$cat_links_sth->errstr;
|
||||
$num_of_links[$cat_id]++;
|
||||
}
|
||||
import_print "$links_imported\n" unless ++$links_imported % 500;
|
||||
}
|
||||
else {
|
||||
$Links_counter-- unless $$opt{straight_import};
|
||||
warning("Unable to insert link `$$row[0]' (SQL query: `INSERT INTO ${e_prefix}Links $links_ins_cols VALUES $links_ins_vals'): ".$insert_link_sth->errstr);
|
||||
}
|
||||
}
|
||||
import_print "$links_imported records from 'Links' imported.\n";
|
||||
|
||||
if ($have_validate_db) {
|
||||
$links_imported = 0;
|
||||
import_print "Importing records from 'Validate'.\n";
|
||||
|
||||
LINK: while(my $row = get_rec(\*VALIDATE,'Links',\%Links2::Def::Links::db_def,\$Links2::Def::Links::db_delim,\@links_get_cols)) {
|
||||
$row = [@$row]; # Remove aliasing
|
||||
my $alt_cats;
|
||||
$alt_cats = shift @$row if $alt_categories;
|
||||
my ($id, $cat_name, $date, $contact_name, $contact_email, $receive_mail) = splice @$row,0,6;
|
||||
unshift @$row, $contact_name, $contact_email;
|
||||
$date = convert_date($date) or warning("Invalid date `$date' for link with ID $id. Link skipped."),next;
|
||||
$id = ++$Links_counter unless $$opt{straight_import};
|
||||
|
||||
my @category_alternates;
|
||||
if ($alt_categories) {
|
||||
@category_alternates = split /\Q$Links2::Def::Links::db_delim/, $alt_cats;
|
||||
}
|
||||
my @cats = ($cat_name,@category_alternates);
|
||||
my @cat_ids = @cat_map{@cats};
|
||||
my $bad_cats = 0;
|
||||
for (0..$#cats) {
|
||||
my $cat_id = $cat_ids[$_];
|
||||
my $cat_name = $cats[$_];
|
||||
unless (defined $cat_id) {
|
||||
if ($$opt{create_missing_categories} and $cat_name) {
|
||||
my @needed = my $fn = $cat_name;
|
||||
while ($fn =~ s[/[^/]*\Z][]) {
|
||||
$count_cats_sth->execute($fn) or critical "Unable to execute query `SELECT COUNT(*) FROM ${e_prefix}Category WHERE Full_Name = ?': ".$count_cats_sth->errstr;
|
||||
if ($count_cats_sth->fetchrow_array) { # It exists
|
||||
$count_cats_sth->finish;
|
||||
last;
|
||||
}
|
||||
else {
|
||||
$count_cats_sth->finish;
|
||||
unshift @needed, $fn;
|
||||
}
|
||||
}
|
||||
for (@needed) {
|
||||
my ($name) = m[([^/]+)\Z];
|
||||
unless ($name) {
|
||||
warning "Unable to create category $_ because it is an invalid name.";
|
||||
$bad_cats++;
|
||||
last;
|
||||
}
|
||||
mild_warning("Creating category $_ as it is needed by link ID $id");
|
||||
my ($father_full) = m[\A(.*)/];
|
||||
my $father_id;
|
||||
if ($father_full) {
|
||||
$get_cat_id_sth->execute($father_full) or critical "Unable to execute query `SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?': ".$get_cat_id_sth->errstr;
|
||||
$father_id = $get_cat_id_sth->fetchrow_array;
|
||||
$get_cat_id_sth->finish;
|
||||
}
|
||||
else { # Must be a root category
|
||||
$father_id = 0;
|
||||
}
|
||||
$cat_ins_simple_sth->execute(++$ins_id,$name,$_,$father_id) or critical "Unable to create missing category $_: ".$cat_ins_simple_sth->errstr;
|
||||
$cat_map{$_} = $ins_id;
|
||||
$cat_id = $ins_id;
|
||||
$missing_cats++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$bad_cats++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($bad_cats == @cat_ids) { # Between the category and the alternate categories, there has to be at least ONE good one.
|
||||
if (@cat_ids == 1) {
|
||||
warning "Invalid category `$cat_ids[0]' for link $$row[0] (ID: $id, line $.). Link skipped";
|
||||
next LINK;
|
||||
}
|
||||
else {
|
||||
warning "No valid categories ($cat_name @category_alternates) for link $$row[0] (ID: $id, line $.). Link skipped";
|
||||
next LINK;
|
||||
}
|
||||
}
|
||||
|
||||
my $username;
|
||||
$user_count_sth->execute($contact_email) or warning("Unable to execute query `SELECT COUNT(*) FROM ${e_prefix}Users WHERE Email = ?': ".$user_count_sth->errstr);
|
||||
if ($user_count_sth->fetchrow_arrayref()->[0]) { # This e-mail address already exists
|
||||
$user_mod_sth->execute($contact_name, ($receive_mail eq 'Yes' ? 'Yes' : 'No'), $contact_email) or warning("Unable to execute query `UPDATE ${e_prefix}Users SET Name = ?, ReceiveMail = ? WHERE Email = ?': ".$user_mod_sth->errstr);
|
||||
$username_sth->execute($contact_email) or warning("Unable to execute query: ".$username_sth->errstr);
|
||||
$username = $username_sth->fetchrow_arrayref()->[0];
|
||||
$username_sth->finish;
|
||||
}
|
||||
elsif ($contact_email) {
|
||||
$user_ins_sth->execute('', ($contact_email) x 2, (defined $contact_name ? $contact_name : ""), ($receive_mail eq 'Yes' ? 'Yes' : 'No')) or warning("Unable to execute query `INSERT INTO ${e_prefix}Users (Username, Email, Name, ReceiveMail, Status) VALUES (?, ?, ?, ?, ?, 'Registered')': ".$user_ins_sth->errstr);
|
||||
$username = $contact_email;
|
||||
}
|
||||
else {
|
||||
mild_warning("Not enough information to add a user for link `".($$row[0] or '<unknown>')." (URL: ".($$row[1] or "<none>")."). Setting link owner to `admin'");
|
||||
$username = 'admin';
|
||||
}
|
||||
$user_count_sth->finish;
|
||||
if ($insert_link_sth->execute($id,$username,'No',$date,$date,@$row)) {
|
||||
for my $cat_id (@cat_ids) {
|
||||
next if (! defined $cat_id);
|
||||
$cat_links_sth->execute($id,$cat_id) or warning "Unable to execute query `INSERT INTO ${e_prefix}CatLinks (LinkID, CategoryID) VALUES (?, ?)': ".$cat_links_sth->errstr;
|
||||
$num_of_links[$cat_id]++;
|
||||
}
|
||||
import_print "$links_imported\n" unless ++$links_imported % 500;
|
||||
}
|
||||
else {
|
||||
$Links_counter-- unless $$opt{straight_import};
|
||||
warning("Unable to insert link `$$row[0]' (SQL query: `INSERT INTO ${e_prefix}Links $links_ins_cols VALUES $links_ins_vals'): ".$insert_link_sth->errstr);
|
||||
}
|
||||
}
|
||||
import_print "$links_imported records from 'Validate' imported.\n";
|
||||
}
|
||||
|
||||
import_print "$missing_cats categories have been created due to missing categories for links\n" if $missing_cats;
|
||||
|
||||
for (grep $num_of_links[$_], 0..$#num_of_links) {
|
||||
$num_links_sth->execute($num_of_links[$_],$_) or warning "Unable to execute query `UPDATE ${e_prefix}Category SET Number_of_Links = ? WHERE ID = ?': ".$num_links_sth->errstr;
|
||||
}
|
||||
}
|
||||
$e_dbh->disconnect;
|
||||
import_print "\nNOTE: You must run Rebuild Cat. tree, Repair Tables, and Rebuild Search after performing an import!\n";
|
||||
}
|
||||
|
||||
# Takes 4 options: a glob ref containing an opened filehandle, a table name, a
|
||||
# hash ref, a scalar delimiter, and (optionally) an array of fields to return.
|
||||
# The table name should be 'Links', 'CatRelations', 'Category', or 'Sessions'.
|
||||
# If you give it the fields, it will come back with an array (or array ref) of
|
||||
# the values for those fields in the order specified.
|
||||
# Otherwise, it will return a hash ref (or hash in list context) of the fields
|
||||
# in column => value format.
|
||||
#
|
||||
# Call it as %rec = get_rec(\*FH, $table_name, \%db_def, $delimiter, \@fields);
|
||||
# You can, if you prefer, also make the delimiter a scalar reference.
|
||||
# The hash should be the %db_def used in Links 2.x.
|
||||
sub get_rec {
|
||||
defined wantarray or return; # Don't bother doing anything in void context
|
||||
my $fh = shift;
|
||||
my $table_name = shift;
|
||||
my $db_def = shift;
|
||||
my $delimiter = ref $_[0] eq 'SCALAR' ? ${shift()} : shift;
|
||||
my ($fields,@fields,%fields);
|
||||
if (@_) {
|
||||
$fields = 1;
|
||||
@fields = ref $_[0] eq 'ARRAY' ? @{shift()} : @_;
|
||||
%fields = map { ($_ => 1) } @fields;
|
||||
}
|
||||
defined fileno($fh) or critical "Interal error: File handle passed to get_rec() is not an opened file";
|
||||
|
||||
my @mapping = sort { $db_def->{$a}[0] <=> $db_def->{$b}[0] } keys %$db_def;
|
||||
local $/ = "\n";
|
||||
my $line;
|
||||
until (defined $line) {
|
||||
$line = <$fh>;
|
||||
return unless defined $line; # Catch the end of the file.
|
||||
chomp $line;
|
||||
$line ||= undef;
|
||||
}
|
||||
my $i = 0;
|
||||
my @rec = split /\Q$delimiter/, $line, -1;
|
||||
my %rec;
|
||||
for (@rec) {
|
||||
s/``/\n/g;
|
||||
s/~~/|/g;
|
||||
$_ = undef if $_ eq 'NULL';
|
||||
}
|
||||
for (0..$#rec) {
|
||||
if (defined $mapping[$_] and (!$fields or $fields{$mapping[$_]})) { # Skip "extra" and unwanted records
|
||||
$rec{$mapping[$_]} = $rec[$_];
|
||||
}
|
||||
}
|
||||
if ($table_name eq 'Links') {
|
||||
$rec{Category} =~ y/_/ / if $rec{Category};
|
||||
$rec{Hits} ||= 0 if exists $rec{Hits}; # Fix for Links 2 database having the Hits table removed
|
||||
}
|
||||
elsif ($table_name eq 'Category') {
|
||||
$rec{Name} =~ y/_/ / if $rec{Name};
|
||||
$rec{Related} =~ y/_/ / if $rec{Related};
|
||||
}
|
||||
|
||||
$fields or return wantarray ? %rec : \%rec;
|
||||
my @ret = map $rec{$_}, @fields;
|
||||
return wantarray ? @ret : \@ret;
|
||||
}
|
||||
|
||||
# Converts a date. Returns false if the date is invalid.
|
||||
sub convert_date ($) {
|
||||
my $in = shift;
|
||||
my ($day, $mon, $year) = split /-/, $in, 3;
|
||||
my %months = qw(Jan 01 Feb 02 Mar 03 Apr 04 May 05 Jun 06 Jul 07 Aug 08 Sep 09 Oct 10 Nov 11 Dec 12);
|
||||
# Any extra fields needed should be set like this:
|
||||
# $months{Okt} = "10";
|
||||
# $months{Mai} = "05";
|
||||
# $months{Dez} = "12";
|
||||
#
|
||||
if ($year and $months{$mon} and $day) {
|
||||
return sprintf("%04d-$months{$mon}-%02d", $year, $day);
|
||||
} else {
|
||||
warning "Invalid date `$in' encountered.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
# Returns a random password of random length (20-25 characters).
|
||||
sub random_pass () {
|
||||
my @chars = ('a'..'z','A'..'Z',0..9,qw a _ [ ] { } ` ' " ! @ ^ * ( ) - _ = + : ; . / \ a,'#',',');
|
||||
my $pass = join '', map { $chars[rand @chars] } 0..(20+rand(5));
|
||||
}
|
||||
|
||||
"True or not true? That is the question."
|
||||
533
site/slowtwitch.com/cgi-bin/articles/admin/Links/Import/RDFS2.pm
Normal file
533
site/slowtwitch.com/cgi-bin/articles/admin/Links/Import/RDFS2.pm
Normal file
@@ -0,0 +1,533 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: RDFS2.pm,v 1.20 2005/04/07 19:34:41 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::Import::RDFS2;
|
||||
|
||||
use 5.004_04;
|
||||
use strict;
|
||||
use vars qw/$Warning_Code $Critical_Code $Mild_Code $Print_Out/;
|
||||
|
||||
use DBI;
|
||||
use GT::SQL;
|
||||
use GT::RDF;
|
||||
|
||||
sub critical {
|
||||
$Critical_Code->(@_);
|
||||
}
|
||||
|
||||
sub warning {
|
||||
$Warning_Code->(@_);
|
||||
}
|
||||
|
||||
sub mild_warning {
|
||||
ref $Mild_Code eq 'CODE' and $Mild_Code->(@_);
|
||||
}
|
||||
|
||||
sub import_print {
|
||||
if (ref $Print_Out eq 'CODE') {
|
||||
$Print_Out->(@_);
|
||||
}
|
||||
else {
|
||||
print @_;
|
||||
}
|
||||
}
|
||||
|
||||
# Takes 3-4 arguments: hash reference, 2 or 3 code refs
|
||||
# The hash reference is the options hash for an import.
|
||||
# The first code reference will be called when a warning occurs.
|
||||
# The second code reference will be called when a critical error occurs.
|
||||
# If provided, the third code reference will be called when a mild warning occurs
|
||||
sub import {
|
||||
my $opt = shift;
|
||||
return if ref $opt ne 'HASH';
|
||||
{
|
||||
my $warning = shift;
|
||||
return if ref $warning ne 'CODE';
|
||||
$Warning_Code = $warning;
|
||||
|
||||
my $critical = shift;
|
||||
return if ref $critical ne 'CODE';
|
||||
$Critical_Code = $critical;
|
||||
|
||||
my $mild = shift;
|
||||
$Mild_Code = $mild if ref $mild eq 'CODE';
|
||||
|
||||
my $output = shift;
|
||||
$Print_Out = $output if ref $output eq 'CODE';
|
||||
}
|
||||
|
||||
my $DB = new GT::SQL(def_path => $$opt{destination}, subclass => 0);
|
||||
my $e_prefix = $DB->prefix;
|
||||
my $e_dbh;
|
||||
{
|
||||
my $table = $DB->table("Links");
|
||||
$table->connect();
|
||||
$e_dbh = $table->{driver}->connect();
|
||||
}
|
||||
|
||||
my $Links_counter;
|
||||
my $Category_counter;
|
||||
my $odbc = 0;
|
||||
if (($DB->table('Links')->{connect}{driver} || "") eq "ODBC") {
|
||||
$odbc = 1;
|
||||
# Set max read properties for DBI.
|
||||
$e_dbh->{LongReadLen} = 1000000;
|
||||
}
|
||||
|
||||
if ($$opt{clear_tables}) {
|
||||
# Delete everything from all tables, EXCEPT for the `admin' user from the Users table.
|
||||
# Also ignore --rdf-user if specified.
|
||||
if ($$opt{rdf_user}) {
|
||||
my $sth = $e_dbh->prepare("SELECT COUNT(*) FROM ${e_prefix}Users WHERE Username = ?") or critical "Unable to prepare query `SELECT COUNT(*) FROM ${e_prefix}Users WHERE Username = ?': ".$e_dbh->errstr;
|
||||
$sth->execute($$opt{rdf_user}) or critical "Unable to execute query `SELECT COUNT(*) FROM ${e_prefix}Users WHERE Username = ?': ".$sth->errstr;
|
||||
if ($sth->fetchrow_array) {
|
||||
$e_dbh->do("DELETE FROM ${e_prefix}Users WHERE Username <> 'admin' AND Username <> ".$e_dbh->quote($$opt{rdf_user}));
|
||||
}
|
||||
else {
|
||||
critical "The rdf username that you specified ($$opt{rdf_user}) does not exist. Please create the user.";
|
||||
}
|
||||
}
|
||||
else {
|
||||
$e_dbh->do("DELETE FROM ${e_prefix}Users WHERE Username <> 'admin'") or critical "Unable to delete all existing users: ".$e_dbh->errstr;
|
||||
}
|
||||
for (qw/Links Category CatLinks CatRelations Category_Score_List
|
||||
Category_Word_List ClickTrack Editors EmailMailings EmailTemplates
|
||||
Links_Score_List Links_Word_List MailingIndex MailingList
|
||||
MailingListIndex Verify/) {
|
||||
$e_dbh->do("DELETE FROM $e_prefix$_");
|
||||
}
|
||||
unless ($$opt{straight_import}) {
|
||||
$Links_counter = $Category_counter = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
my $sth = $e_dbh->prepare("SELECT MAX(ID) FROM ${e_prefix}Links") or critical "Unable to prepare query `SELECT MAX(ID) FROM ${e_prefix}Links': ".$e_dbh->errstr;
|
||||
$sth->execute() or critical "Unable to execute query `SELECT MAX(ID) FROM ${e_prefix}Links': ".$sth->errstr;
|
||||
$Links_counter = $sth->fetchrow_array;
|
||||
$sth->finish();
|
||||
|
||||
$sth = $e_dbh->prepare("SELECT MAX(ID) FROM ${e_prefix}Category") or critical "Unable to prepare query `SELECT MAX(ID) FROM ${e_prefix}Category': ".$e_dbh->errstr;
|
||||
$sth->execute() or critical "Unable to execute query `SELECT MAX(ID) FROM ${e_prefix}Category': ".$sth->errstr;
|
||||
$Category_counter = $sth->fetchrow_array;
|
||||
$sth->finish();
|
||||
}
|
||||
|
||||
my $gzip = $$opt{with_gzip};
|
||||
my $need_gz = -B $$opt{source}; # Tests if the file is binary.
|
||||
|
||||
my $is_win = $^O =~ /win/i;
|
||||
|
||||
my $gzip_filename = $is_win ? "gzip.exe" : "gzip";
|
||||
|
||||
if (not $gzip and $need_gz) {
|
||||
# Try to find gzip
|
||||
my $dir_sep = $is_win ? ";" : ":";
|
||||
my @locations = split /$dir_sep/, $ENV{PATH};
|
||||
for (@locations) {
|
||||
-x "$_/$gzip_filename" and $gzip = "$_/$gzip_filename", last;
|
||||
}
|
||||
$gzip or critical "\nUnable to locate gzip (Searched @locations). Please specify with --with-gzip=\"/path/to/gzip\"";
|
||||
}
|
||||
elsif ($gzip and -d $gzip) {
|
||||
if (-x "$gzip/$gzip_filename") {
|
||||
$gzip = "$gzip/$gzip_filename";
|
||||
}
|
||||
else {
|
||||
critical "\nThe directory $gzip does not contain a valid gzip program";
|
||||
}
|
||||
}
|
||||
if ($need_gz) {
|
||||
-x $gzip or critical "\nUnable to find an executable gzip";
|
||||
}
|
||||
|
||||
my $rdf = \do { local *FH; *FH };
|
||||
|
||||
if ($$opt{with_gzip} or $need_gz) {
|
||||
import_print "\nOpening uncompressed stream from gzip compressed file `$$opt{source}' ...\n";
|
||||
open $rdf, " $gzip -c -d $$opt{source} |" or critical "Unable to open `$gzip -c -d $$opt{source} |': $!";
|
||||
}
|
||||
else {
|
||||
import_print "\nOpening stream from non-compressed file `$$opt{source}' ...\n";
|
||||
open $rdf, "<$$opt{source}" or critical "Unable to open $$opt{source}: $!";
|
||||
}
|
||||
|
||||
import_print "Stream opened.\n";
|
||||
# Do the import
|
||||
{
|
||||
my $count_cats_sth = $e_dbh->prepare("SELECT COUNT(*) FROM ${e_prefix}Category WHERE Full_Name = ?") or critical "Unable to prepare query `SELECT COUNT(*) FROM ${e_prefix}Category WHERE Full_Name = ?': ".$e_dbh->errstr;
|
||||
my $cat_id_sth = $e_dbh->prepare("SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?") or critical "Unable to prepare query `SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?': ".$e_dbh->errstr;
|
||||
|
||||
my $cat_ins_sth = $odbc
|
||||
? $e_dbh->prepare("SET IDENTITY_INSERT ${e_prefix}Category ON; INSERT INTO ${e_prefix}Category (ID, Name, Full_Name, FatherID, Number_of_Links, Has_New_Links, Has_Changed_Links) VALUES (?, ?, ?, ?, 0, 'No', 'No')")
|
||||
: $e_dbh->prepare("INSERT INTO ${e_prefix}Category (ID, Name, Full_Name, FatherID, Number_of_Links, Has_New_Links, Has_Changed_Links) VALUES (?, ?, ?, ?, 0, 'No', 'No')");
|
||||
$cat_ins_sth or critical "Unable to prepare query `INSERT INTO ${e_prefix}Category (ID, Name, Full_Name, FatherID) VALUES (?, ?, ?, ?)': ".$e_dbh->errstr;
|
||||
|
||||
my $sub_cats_sth = $e_dbh->prepare("UPDATE ${e_prefix}Category SET FatherID = ? WHERE Full_Name LIKE ? AND Full_Name NOT LIKE ?") or critical "Unable to prepare query `UPDATE ${e_prefix}Category SET FatherID = ? WHERE Full_Name LIKE ? AND Full_Name NOT LIKE ?': ".$e_dbh->errstr;
|
||||
my $get_num_links_sth = $e_dbh->prepare("SELECT COUNT(*) FROM ${e_prefix}CatLinks WHERE CategoryID = ?") or critical "Unable to prepare query `SELECT COUNT(*) FROM ${e_prefix}CatLinks WHERE CategoryID = ?': ".$e_dbh->errstr;
|
||||
my $num_links_sth = $e_dbh->prepare("UPDATE ${e_prefix}Category SET Number_of_Links = ? WHERE ID = ?") or critical "Unable to prepare query `UPDATE ${e_prefix}Category SET Number_of_Links = ? WHERE ID = ?': ".$e_dbh->errstr;
|
||||
|
||||
my $insert_link_sth = $odbc
|
||||
? $e_dbh->prepare("SET IDENTITY_INSERT ${e_prefix}Links ON; INSERT INTO ${e_prefix}Links (ID, Title, URL, Add_Date, Mod_Date, Description, LinkOwner, Status, Votes, Rating, Hits) VALUES (?, ?, ?, ?, ?, ?, ".$e_dbh->quote($$opt{rdf_user} or "admin").",0,0,0,0)")
|
||||
: $e_dbh->prepare("INSERT INTO ${e_prefix}Links (ID, Title, URL, Add_Date, Mod_Date, Description, LinkOwner, Status, Votes, Rating, Hits) VALUES (?, ?, ?, ?, ?, ?, ".$e_dbh->quote($$opt{rdf_user} or "admin").",0,0,0,0)");
|
||||
$insert_link_sth or critical "Unable to prepare query `INSERT INTO ${e_prefix}Links (Title, URL, Add_Date, Mod_Date, Description, LinkOwner) VALUES (?, ?, ?, ?, ?, ?)': ".$e_dbh->errstr;
|
||||
|
||||
my $link_exists_sth = $e_dbh->prepare("SELECT COUNT(*) FROM ${e_prefix}Links, ${e_prefix}CatLinks WHERE URL = ? AND CategoryID = ? AND ID = LinkID") or critical "Unable to prepare query `SELECT * FROM Links, CatLinks WHERE URL = ? AND CategoryID = ? AND ID = LinkID': ".$e_dbh->errstr;
|
||||
|
||||
my $cat_links_sth = $e_dbh->prepare("INSERT INTO ${e_prefix}CatLinks (LinkID, CategoryID) VALUES (?, ?)") or critical "Unable to prepare query `INSERT INTO ${e_prefix}CatLinks (LinkID, CategoryID) VALUES (?, ?)': ".$e_dbh->errstr;
|
||||
|
||||
my $count_links_sth = $e_dbh->prepare("SELECT COUNT(*) FROM ${e_prefix}CatLinks WHERE CategoryID = ?") or critical "Unable to prepare query `SELECT COUNT(*) FROM ${e_prefix}CatLinks WHERE CategoryID = ?': ".$e_dbh->errstr;
|
||||
|
||||
import_print "\nImporting from $$opt{source}\n";
|
||||
my $links_imported = 0;
|
||||
my $cats_imported = 0;
|
||||
my %cat_needs_num; # $cat_needs_num{cat_id} = 1 if the category needs to have its Number_Of_Links updated
|
||||
|
||||
my $base_cat = $$opt{rdf_destination};
|
||||
my $base_cat_id;
|
||||
if (defined $base_cat) {
|
||||
$base_cat =~ s|//+|/|g; # Remove doubled (and more) slashes
|
||||
$base_cat =~ s|^/+||; # Remove any leading slashes
|
||||
$base_cat =~ s|/+$||; # And any trailing ones
|
||||
}
|
||||
else {
|
||||
$base_cat = "";
|
||||
}
|
||||
if (length $base_cat) {
|
||||
$count_cats_sth->execute($base_cat) or critical "Unable to execute query `SELECT COUNT(*) FROM ${e_prefix}Category WHERE Full_Name = ?': ".$count_cats_sth->errstr;
|
||||
if ($count_cats_sth->fetchrow_array) {
|
||||
$cat_id_sth->execute($base_cat) or critical "Unable to execute query `SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?': ".$cat_id_sth->errstr;
|
||||
$base_cat_id = $cat_id_sth->fetchrow_array;
|
||||
}
|
||||
else { # Category doesn't exist
|
||||
my @missing_cats = $base_cat;
|
||||
mild_warning "$base_cat does not exist and is being created";
|
||||
my $fn = $base_cat;
|
||||
while ($fn =~ s[/[^/]*\Z][]) {
|
||||
$count_cats_sth->execute($fn) or critical "Unable to execute query `SELECT COUNT(*) FROM ${e_prefix}Category WHERE Full_Name = ?': ".$count_cats_sth->errstr;
|
||||
if ($count_cats_sth->fetchrow_array) { # It exists
|
||||
last;
|
||||
}
|
||||
else {
|
||||
unshift @missing_cats, $fn;
|
||||
mild_warning "$fn is needed for base category $base_cat and does not exist. It will be created";
|
||||
}
|
||||
}
|
||||
for (@missing_cats) {
|
||||
my ($name) = m[([^/]+)\Z];
|
||||
my ($father_full) = m[\A(.*)/];
|
||||
my $father_id;
|
||||
if ($father_full) {
|
||||
$cat_id_sth->execute($father_full) or critical "Unable to execute query `SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?': ".$cat_id_sth->errstr;
|
||||
$father_id = $cat_id_sth->fetchrow_array;
|
||||
}
|
||||
else { # Must be the root category
|
||||
$father_id = 0;
|
||||
}
|
||||
$cat_ins_sth->execute(++$Category_counter,$name,$_,$father_id) or critical "Unable to create missing category $_: ".$cat_ins_sth->errstr;
|
||||
$cats_imported++;
|
||||
$sub_cats_sth->execute($Category_counter,"$_/%","$_/%/%") or critical "Unable to execute query `UPDATE ${e_prefix}Category SET FatherID = ? WHERE Full_Name LIKE ? and Full_Name NOT LIKE ?': ".$sub_cats_sth->errstr;
|
||||
}
|
||||
$base_cat_id = $Category_counter; # The last one inserted will be the category import base
|
||||
}
|
||||
}
|
||||
else {
|
||||
$base_cat_id = 0;
|
||||
}
|
||||
my $cat = $$opt{rdf_category};
|
||||
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Main code, get a parser object and start parsing!
|
||||
#
|
||||
# New for 2.2.0 - XML::Parser-based code, which should be significantly faster.
|
||||
# It should, however, still be considered experimental.
|
||||
#
|
||||
if ($$opt{xml_parser}) {
|
||||
require XML::Parser;
|
||||
my (%links, %want, %current, @in);
|
||||
my $last_status = -1;
|
||||
my $insert_cat = sub {
|
||||
my $cat_name = $current{category};
|
||||
|
||||
# If we are under ODBC we need to reset the sth handle to avoid a "Invalid Cursor State" error.
|
||||
$odbc and $count_cats_sth->finish;
|
||||
|
||||
# Check to make sure we haven't seen this category before
|
||||
# Set $found to the category ID.
|
||||
$count_cats_sth->execute($cat_name) or critical "Execute: $DBI::errstr";
|
||||
if ($count_cats_sth->fetchrow_array) {
|
||||
$cat_id_sth->finish if ($odbc); # Need to reset cursor under odbc.
|
||||
$cat_id_sth->execute($cat_name) or critical "Execute: $DBI::errstr";
|
||||
$current{cat_id} = $cat_id_sth->fetchrow_array;
|
||||
}
|
||||
else {
|
||||
my ($father_name, $short_name) = $current{category} =~ m|(?:(.*)/)?([^/]+)$|;
|
||||
my $father_id;
|
||||
if ($father_name) {
|
||||
$cat_id_sth->finish if ($odbc); # Need to reset cursor under odbc.
|
||||
$cat_id_sth->execute($father_name) or critical "Execute: $DBI::errstr";
|
||||
$father_id = $cat_id_sth->fetchrow_array;
|
||||
}
|
||||
else {
|
||||
$father_id = 0;
|
||||
}
|
||||
|
||||
$cat_ins_sth->execute(++$Category_counter, $short_name, $current{category}, $father_id) or critical "Execute: $DBI::errstr";
|
||||
$cats_imported++;
|
||||
$current{cat_id} = $Category_counter;
|
||||
}
|
||||
|
||||
for my $link (keys %links) {
|
||||
# Either append, or insert new link.
|
||||
if ($$opt{rdf_update}) {
|
||||
$link_exists_sth->execute($link, $current{cat_id}) or critical "Execute: $DBI::errstr";
|
||||
next if $link_exists_sth->fetchrow;
|
||||
}
|
||||
# Title can only be 100 characters (ODBC fatals about data that is too long).
|
||||
my $title = substr($links{$link}->{title} || $link, 0, 100);
|
||||
$insert_link_sth->execute(++$Links_counter, $title, $link, $$opt{rdf_add_date}, $$opt{rdf_add_date}, $links{$link}->{description} || "") or critical "Execute: $DBI::errstr";
|
||||
$cat_links_sth->execute($Links_counter, $current{cat_id}) or critical "Execute: $DBI::errstr";
|
||||
$cat_needs_num{$current{cat_id}} = 1;
|
||||
$links_imported++;
|
||||
}
|
||||
%links = ();
|
||||
return scalar keys %links;
|
||||
};
|
||||
|
||||
my $parser = XML::Parser->new(
|
||||
Handlers => {
|
||||
Start => sub {
|
||||
my ($parser, $tag, %attr) = @_;
|
||||
if ($tag eq 'Topic') {
|
||||
{
|
||||
my $disp_topic = $attr{'r:id'};
|
||||
substr($disp_topic, 30) = '...' if length $disp_topic > 33;
|
||||
my $padding = " " x (33 - length $disp_topic);
|
||||
$disp_topic = "(L:$links_imported, C:$cats_imported) $disp_topic$padding";
|
||||
import_print("\r$disp_topic");
|
||||
}
|
||||
if ($current{category}) {
|
||||
my $cat_count = $insert_cat->();
|
||||
import_print "$cat_count ";
|
||||
}
|
||||
my $dmoz_cat = $attr{'r:id'};
|
||||
if ($dmoz_cat =~ /^\Q$cat\E(.*)/) {
|
||||
my $topic = $base_cat . '/' . $1;
|
||||
$topic =~ s|/{2,}|/|g;
|
||||
$topic =~ s|^/||;
|
||||
$topic =~ s|/$||;
|
||||
$topic =~ y|_| |;
|
||||
$current{category} = $topic;
|
||||
}
|
||||
else {
|
||||
delete $current{category};
|
||||
import_print "skipping";
|
||||
}
|
||||
}
|
||||
elsif ($tag eq 'link' and $in[-1] eq 'Topic' and $current{category} and $attr{'r:resource'}) {
|
||||
$links{$attr{'r:resource'}} = {};
|
||||
}
|
||||
elsif ($tag eq 'ExternalPage' and $current{category} and $attr{about}) {
|
||||
$current{ExternalPage} = $attr{about};
|
||||
}
|
||||
elsif ($tag eq 'd:Title' and $in[-1] eq 'ExternalPage' and $current{ExternalPage}) {
|
||||
$want{title} = \$links{$current{ExternalPage}}->{title};
|
||||
}
|
||||
elsif ($tag eq 'd:Description' and $in[-1] eq 'ExternalPage' and $current{ExternalPage}) {
|
||||
$want{description} = \$links{$current{ExternalPage}}->{description};
|
||||
}
|
||||
|
||||
push @in, $tag;
|
||||
},
|
||||
End => sub {
|
||||
my ($parser, $tag) = @_;
|
||||
pop @in;
|
||||
if ($tag eq 'd:Description') {
|
||||
delete $want{description};
|
||||
}
|
||||
elsif ($tag eq 'd:Title') {
|
||||
delete $want{title};
|
||||
}
|
||||
},
|
||||
Char => sub {
|
||||
my ($parser, $text) = @_;
|
||||
if ($want{title}) {
|
||||
${$want{title}} = $text;
|
||||
}
|
||||
elsif ($want{description}) {
|
||||
${$want{description}} = $text;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$parser->parse($rdf, ProtocolEncoding => 'ISO-8859-1');
|
||||
}
|
||||
else {
|
||||
my $parse = GT::RDF->new (io => $rdf);
|
||||
my ($found, $was_found, %links);
|
||||
my $cat_count = 0;
|
||||
my $first = 0;
|
||||
|
||||
while ($parse->parse) {
|
||||
|
||||
# Either we have a topic, external page or unknown.
|
||||
if ($parse->{name} eq 'Topic') {
|
||||
|
||||
# Add extra links that did not have external page info.
|
||||
if (defined $found) {
|
||||
foreach my $link (keys %links) {
|
||||
if ($$opt{rdf_update}) {
|
||||
$link_exists_sth->execute($parse->{attribs}{about},$found) or critical "Execute: $DBI::errstr";
|
||||
unless ($link_exists_sth->fetchrow_array) {
|
||||
$insert_link_sth->execute(++$Links_counter, $link, $link, $$opt{rdf_add_date}, $$opt{rdf_add_date}, "") or critical "Execute: $DBI::errstr";
|
||||
$cat_links_sth->execute($Links_counter, $found) or critical "Execute: $DBI::errstr";
|
||||
$cat_needs_num{$found} = 1;
|
||||
$links_imported++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$insert_link_sth->execute(++$Links_counter, $link, $link, $$opt{rdf_add_date}, $$opt{rdf_add_date}, "") or critical "Execute: $DBI::errstr";
|
||||
$cat_links_sth->execute($Links_counter, $found) or critical "Execute: $DBI::errstr";
|
||||
$cat_needs_num{$found} = 1;
|
||||
$links_imported++;
|
||||
}
|
||||
}
|
||||
import_print "$cat_count ";
|
||||
}
|
||||
else {
|
||||
import_print "skipping" if $first++;
|
||||
}
|
||||
|
||||
# Clear out our links hash, and set found to undef.
|
||||
$cat_count = 0;
|
||||
%links = ();
|
||||
$was_found = $found;
|
||||
$found = undef;
|
||||
|
||||
# We've finished a topic, start a new one.
|
||||
my $dmoz_cat = $parse->{attribs}{'r:id'};
|
||||
if ($dmoz_cat =~ /^\Q$cat\E(.*)/) {
|
||||
my $topic = $base_cat . '/' . $1;
|
||||
$topic =~ s|//+|/|g;
|
||||
$topic =~ s|^/||;
|
||||
$topic =~ s|/$||;
|
||||
$topic =~ s|_| |g;
|
||||
if ($topic) {
|
||||
|
||||
# If we are under ODBC we need to reset the sth handle to avoid a "Invalid Cursor State" error.
|
||||
$odbc and ($count_cats_sth->finish);
|
||||
|
||||
# Check to make sure we haven't seen this category before
|
||||
# Set $found to the category ID.
|
||||
$count_cats_sth->execute($topic) or critical "Execute: $DBI::errstr";
|
||||
if ($count_cats_sth->fetchrow_array) {
|
||||
$cat_id_sth->finish if ($odbc); # Need to reset cursor under odbc.
|
||||
$cat_id_sth->execute($topic) or critical "Execute: $DBI::errstr";
|
||||
$found = $cat_id_sth->fetchrow_array;
|
||||
}
|
||||
else {
|
||||
my ($short_name) = $topic =~ m|([^/]+)$|;
|
||||
my ($father_name) = $topic =~ m|(.*)/|;
|
||||
my $father_id;
|
||||
if ($father_name) {
|
||||
$cat_id_sth->finish if ($odbc); # Need to reset cursor under odbc.
|
||||
$cat_id_sth->execute($father_name) or critical "Execute: $DBI::errstr";
|
||||
$father_id = $cat_id_sth->fetchrow_array;
|
||||
}
|
||||
else {
|
||||
$father_id = 0;
|
||||
}
|
||||
|
||||
$cat_ins_sth->execute(++$Category_counter, $short_name, $topic, $father_id) or critical "Execute: $DBI::errstr";
|
||||
$cats_imported++;
|
||||
$found = $Category_counter;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
my $disp_topic = $parse->{attribs}{'r:id'};
|
||||
substr($disp_topic, 30) = '...' if length $disp_topic > 33;
|
||||
my $padding = " " x (33 - length $disp_topic);
|
||||
$disp_topic = "(L:$links_imported, C:$cats_imported) $disp_topic$padding";
|
||||
import_print("\r$disp_topic");
|
||||
}
|
||||
if (defined $found) {
|
||||
for (@{$parse->{tags}}) {
|
||||
next unless ($_->{attribs}{'r:resource'});
|
||||
$links{$_->{attribs}{'r:resource'}} = 1;
|
||||
$cat_count++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return 1 if $was_found;
|
||||
}
|
||||
}
|
||||
elsif (defined ($found) and $parse->{name} eq 'ExternalPage') {
|
||||
|
||||
# Remove from our simple link list.
|
||||
delete $links{$parse->{attribs}{about}};
|
||||
|
||||
# Insert with description or title if it does not exist and we are not overwritting
|
||||
my ($title, $desc);
|
||||
for (@{$parse->{tags}}) {
|
||||
if ($_->{name} eq 'd:Title' and $_->{data}) { $title = $_->{data} }
|
||||
elsif ($_->{name} eq 'd:Description' and $_->{data}) { $desc = $_->{data} }
|
||||
}
|
||||
$title ||= $parse->{attribs}{about};
|
||||
$desc ||= '';
|
||||
|
||||
# Either append, or insert new link.
|
||||
if ($$opt{rdf_update}) {
|
||||
$link_exists_sth->execute ($parse->{attribs}{about},$found) or critical "Execute: $DBI::errstr";
|
||||
unless ($link_exists_sth->fetchrow_array) {
|
||||
# Title can only be 100 characters (ODBC fatals about data that is too long).
|
||||
$title = substr($title, 0, 100);
|
||||
$insert_link_sth->execute(++$Links_counter, $title, $parse->{attribs}{about}, $$opt{rdf_add_date}, $$opt{rdf_add_date}, $desc) or critical "Execute: $DBI::errstr";
|
||||
$cat_links_sth->execute($Links_counter, $found) or critical "Execute: $DBI::errstr";
|
||||
$cat_needs_num{$found} = 1;
|
||||
$links_imported++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
# Title can only be 100 characters (ODBC fatals about data that is too long).
|
||||
$title = substr($title, 0, 100);
|
||||
$insert_link_sth->execute(++$Links_counter, $title, $parse->{attribs}{about}, $$opt{rdf_add_date}, $$opt{rdf_add_date}, $desc) or critical "Execute: $DBI::errstr";
|
||||
$cat_links_sth->execute($Links_counter, $found) or critical "Execute: $DBI::errstr";
|
||||
$cat_needs_num{$found} = 1;
|
||||
$links_imported++;
|
||||
}
|
||||
}
|
||||
elsif (defined $found) {
|
||||
require GT::Dumper;
|
||||
critical "STRANGE TAG: " . GT::Dumper::Dumper($parse) . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Now we have to go through the categories to update each one's Number_Of_Links
|
||||
for (keys %cat_needs_num) {
|
||||
$count_links_sth->execute($_) or critical "Unable to count links for Category ID $_: ".$count_links_sth->errstr;
|
||||
my $links = $count_links_sth->fetchrow_array;
|
||||
$count_links_sth->finish if ($odbc);
|
||||
$num_links_sth->execute($links,$_) or critical "Unable to update number of links for Category ID $_: ".$num_links_sth->errstr;
|
||||
}
|
||||
}
|
||||
import_print "\nNOTE: You must run Rebuild Cat. tree, Repair Tables, and Rebuild Search after performing an import!\n";
|
||||
|
||||
$e_dbh->disconnect;
|
||||
1;
|
||||
}
|
||||
|
||||
|
||||
'"I am lying," said the man. Was he?';
|
||||
802
site/slowtwitch.com/cgi-bin/articles/admin/Links/Import/S1S2.pm
Normal file
802
site/slowtwitch.com/cgi-bin/articles/admin/Links/Import/S1S2.pm
Normal file
@@ -0,0 +1,802 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: S1S2.pm,v 1.32 2005/04/16 02:11:50 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::Import::S1S2;
|
||||
|
||||
use 5.004_04;
|
||||
use strict;
|
||||
use vars qw/$Warning_Code $Critical_Code $Mild_Code $Print_Out/;
|
||||
|
||||
use DBI;
|
||||
use GT::SQL;
|
||||
|
||||
sub critical {
|
||||
$Critical_Code->(@_);
|
||||
}
|
||||
|
||||
sub warning {
|
||||
$Warning_Code->(@_);
|
||||
}
|
||||
|
||||
sub mild_warning {
|
||||
ref $Mild_Code eq 'CODE' and $Mild_Code->(@_);
|
||||
}
|
||||
|
||||
sub import_print {
|
||||
if (ref $Print_Out eq 'CODE') {
|
||||
$Print_Out->(@_);
|
||||
}
|
||||
else {
|
||||
print @_;
|
||||
}
|
||||
}
|
||||
|
||||
# Takes 3-4 arguments: hash reference, 2 or 3 code refs
|
||||
# The hash reference is the options hash for an import.
|
||||
# The first code reference will be called when a warning occurs.
|
||||
# The second code reference will be called when a critical error occurs.
|
||||
# If provided, the third code reference will be called when a mild warning occurs
|
||||
sub import {
|
||||
my $opt = shift;
|
||||
return if ref $opt ne 'HASH';
|
||||
{
|
||||
my $warning = shift;
|
||||
return if ref $warning ne 'CODE';
|
||||
$Warning_Code = $warning;
|
||||
|
||||
my $critical = shift;
|
||||
return if ref $critical ne 'CODE';
|
||||
$Critical_Code = $critical;
|
||||
|
||||
my $mild = shift;
|
||||
$Mild_Code = $mild if ref $mild eq 'CODE';
|
||||
|
||||
my $output = shift;
|
||||
$Print_Out = $output if ref $output eq 'CODE';
|
||||
}
|
||||
|
||||
for (qw/Category Links CategoryRelations CategoryAlternates Validate Users Subscribe/) {
|
||||
local ($!,$@);
|
||||
my $did = do "$$opt{source}/$_.def";
|
||||
critical "Error parsing file $$opt{source}/$_: $@" if !$did and $@;
|
||||
critical "Error reading file $$opt{source}/$_: $!" if !$did and $!;
|
||||
}
|
||||
|
||||
# Check that all necessary databases have been loaded from the def files
|
||||
my $DEBUG_counter = 0;
|
||||
for ($Links::DBSQL::Category::db_driver,
|
||||
$Links::DBSQL::Category::db_user,
|
||||
$Links::DBSQL::Category::db_pass,
|
||||
$Links::DBSQL::Category::db_host,
|
||||
$Links::DBSQL::Category::db_table,
|
||||
$Links::DBSQL::Category::db_name) {
|
||||
defined $_ or critical "The source def files did not load correctly (Category)";
|
||||
}
|
||||
for ($Links::DBSQL::Links::db_driver,
|
||||
$Links::DBSQL::Links::db_user,
|
||||
$Links::DBSQL::Links::db_pass,
|
||||
$Links::DBSQL::Links::db_host,
|
||||
$Links::DBSQL::Links::db_table,
|
||||
$Links::DBSQL::Links::db_name) {
|
||||
defined $_ or critical "The source def files did not load correctly (Links)";
|
||||
}
|
||||
for ($Links::DBSQL::CategoryRelations::db_driver,
|
||||
$Links::DBSQL::CategoryRelations::db_user,
|
||||
$Links::DBSQL::CategoryRelations::db_pass,
|
||||
$Links::DBSQL::CategoryRelations::db_host,
|
||||
$Links::DBSQL::CategoryRelations::db_table,
|
||||
$Links::DBSQL::CategoryRelations::db_name) {
|
||||
defined $_ or critical "The source def files did not load correctly (CategoryRelations)";
|
||||
}
|
||||
for ($Links::DBSQL::CategoryAlternates::db_driver,
|
||||
$Links::DBSQL::CategoryAlternates::db_user,
|
||||
$Links::DBSQL::CategoryAlternates::db_pass,
|
||||
$Links::DBSQL::CategoryAlternates::db_host,
|
||||
$Links::DBSQL::CategoryAlternates::db_table,
|
||||
$Links::DBSQL::CategoryAlternates::db_name) {
|
||||
defined $_ or critical "The source def files did not load correctly (CategoryAlternates)";
|
||||
}
|
||||
for ($Links::DBSQL::Validate::db_driver,
|
||||
$Links::DBSQL::Validate::db_user,
|
||||
$Links::DBSQL::Validate::db_pass,
|
||||
$Links::DBSQL::Validate::db_host,
|
||||
$Links::DBSQL::Validate::db_table,
|
||||
$Links::DBSQL::Validate::db_name) {
|
||||
defined $_ or critical "The source def files did not load correctly (Validate)";
|
||||
}
|
||||
for ($Links::DBSQL::Users::db_driver,
|
||||
$Links::DBSQL::Users::db_user,
|
||||
$Links::DBSQL::Users::db_pass,
|
||||
$Links::DBSQL::Users::db_host,
|
||||
$Links::DBSQL::Users::db_table,
|
||||
$Links::DBSQL::Users::db_name) {
|
||||
defined $_ or critical "The source def files did not load correctly (Users)";
|
||||
}
|
||||
for ($Links::DBSQL::Subscribe::db_driver,
|
||||
$Links::DBSQL::Subscribe::db_user,
|
||||
$Links::DBSQL::Subscribe::db_pass,
|
||||
$Links::DBSQL::Subscribe::db_host,
|
||||
$Links::DBSQL::Subscribe::db_table,
|
||||
$Links::DBSQL::Subscribe::db_name) {
|
||||
defined $_ or critical "The source def files did not load correctly (Subscribe)";
|
||||
}
|
||||
|
||||
my %i_dbh;
|
||||
my $i_dbi_opts = { AutoCommit => 1, RaiseError => 0, PrintError => 0 };
|
||||
|
||||
{
|
||||
my ($no_warning) = ($Links::DBSQL::Category::db_port,
|
||||
$Links::DBSQL::Links::db_port,
|
||||
$Links::DBSQL::Validate::db_port,
|
||||
$Links::DBSQL::Users::db_port,
|
||||
$Links::DBSQL::Subscribe::db_port,
|
||||
$Links::DBSQL::CategoryRelations::db_port,
|
||||
$Links::DBSQL::CategoryAlternates::db_port);
|
||||
}
|
||||
|
||||
for ( ['Category', $Links::DBSQL::Category::db_name, $Links::DBSQL::Category::db_driver, $Links::DBSQL::Category::db_host, $Links::DBSQL::Category::db_port, $Links::DBSQL::Category::db_user, $Links::DBSQL::Category::db_pass ],
|
||||
['Links', $Links::DBSQL::Links::db_name, $Links::DBSQL::Links::db_driver, $Links::DBSQL::Links::db_host, $Links::DBSQL::Links::db_port, $Links::DBSQL::Links::db_user, $Links::DBSQL::Links::db_pass ],
|
||||
['Validate', $Links::DBSQL::Validate::db_name, $Links::DBSQL::Validate::db_driver, $Links::DBSQL::Validate::db_host, $Links::DBSQL::Validate::db_port, $Links::DBSQL::Validate::db_user, $Links::DBSQL::Validate::db_pass ],
|
||||
['Users', $Links::DBSQL::Users::db_name, $Links::DBSQL::Users::db_driver, $Links::DBSQL::Users::db_host, $Links::DBSQL::Users::db_port, $Links::DBSQL::Users::db_user, $Links::DBSQL::Users::db_pass ],
|
||||
['Subscribe',$Links::DBSQL::Subscribe::db_name,$Links::DBSQL::Subscribe::db_driver,$Links::DBSQL::Subscribe::db_host,$Links::DBSQL::Subscribe::db_port,$Links::DBSQL::Subscribe::db_user,$Links::DBSQL::Subscribe::db_pass],
|
||||
['CategoryRelations',$Links::DBSQL::CategoryRelations::db_name,$Links::DBSQL::CategoryRelations::db_driver,$Links::DBSQL::CategoryRelations::db_host,$Links::DBSQL::CategoryRelations::db_port,$Links::DBSQL::CategoryRelations::db_user,$Links::DBSQL::CategoryRelations::db_pass],
|
||||
['CategoryAlternates',$Links::DBSQL::CategoryAlternates::db_name,$Links::DBSQL::CategoryAlternates::db_driver,$Links::DBSQL::CategoryAlternates::db_host,$Links::DBSQL::CategoryAlternates::db_port,$Links::DBSQL::CategoryAlternates::db_user,$Links::DBSQL::CategoryAlternates::db_pass]) {
|
||||
my $driver = $$_[2] || "mysql";
|
||||
critical "The source def files did not load correctly (no \$db_name set for $$_[0] table)" unless $$_[1];
|
||||
next if exists $i_dbh{$$_[1]};
|
||||
my $dsn = "DBI:$driver:$$_[1]";
|
||||
if ($driver eq "mysql") {
|
||||
if ($$_[3]) {
|
||||
$dsn .= ":$$_[3]";
|
||||
if ($$_[4]) {
|
||||
$dsn .= ":$$_[4]";
|
||||
}
|
||||
}
|
||||
}
|
||||
$i_dbh{$$_[1]} = DBI->connect($dsn,@$_[5,6],$i_dbi_opts) or critical("Couldn't connect to source $$_[0] db: ".$DBI::errstr);
|
||||
}
|
||||
|
||||
my $DB = new GT::SQL(def_path => $$opt{destination}, subclass => 0);
|
||||
my $e_prefix = $DB->prefix;
|
||||
my $e_dbh;
|
||||
{
|
||||
my $table = $DB->table("Links");
|
||||
$table->connect();
|
||||
$e_dbh = $table->{driver}->connect();
|
||||
}
|
||||
|
||||
my %e_standard_cols = (
|
||||
Category => { map { ($_ => 1) } qw/ID Name FatherID Full_Name Description Meta_Description Meta_Keywords Header Footer Category_Template Number_of_Links Has_New_Links Has_Changed_Links Newest_Link Timestmp Payment_Mode/},
|
||||
Users => { map { ($_ => 1) } qw/Username Password Email Name Validation Status ReceiveMail/},
|
||||
Links => { map { ($_ => 1) } qw/ID Title URL LinkOwner Add_Date Mod_Date Description Contact_Name Contact_Email Hits isNew isChanged isPopular isValidated Rating Votes Status Date_Checked Timestmp ExpiryDate ExpiryCounted ExpiryNotify/},
|
||||
);
|
||||
|
||||
my %e_non_standard_cols;
|
||||
for my $table (keys %e_standard_cols) {
|
||||
my %cols = $DB->table($table)->cols;
|
||||
for (grep !$e_standard_cols{$table}{$_}, keys %cols) {
|
||||
$e_non_standard_cols{$table}{$_} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
my %i_standard_cols = (
|
||||
Category => { map { ($_ => 1) } qw/ID Name Description Meta_Description Meta_Keywords Header Footer Number_of_Links Has_New_Links Has_Changed_Links Newest_Link/},
|
||||
Users => { map { ($_ => 1) } qw/Username Password Email Validation Status/},
|
||||
Links => { map { ($_ => 1) } qw/ID Title URL Add_Date Mod_Date CategoryID Description Contact_Name Contact_Email Hits isNew isChanged isPopular Rating Votes ReceiveMail Status Date_Checked/},
|
||||
Validate => { map { ($_ => 1) } qw/ID Title URL Add_Date Mod_Date CategoryID Description Contact_Name Contact_Email Hits isNew isChanged isPopular Rating Votes ReceiveMail Status Date_Checked LinkID Mode/},
|
||||
);
|
||||
|
||||
my %i_non_standard_cols;
|
||||
$i_non_standard_cols{Category} = { map { !$i_standard_cols{Category}{$_} ? ($_ => 1) : () } keys %Links::DBSQL::Category::db_def } if keys %Links::DBSQL::Category::db_def;
|
||||
$i_non_standard_cols{Users} = { map { !$i_standard_cols{Users}{$_} ? ($_ => 1) : () } keys %Links::DBSQL::Users::db_def } if keys %Links::DBSQL::Users::db_def;
|
||||
$i_non_standard_cols{Links} = { map { !$i_standard_cols{Links}{$_} ? ($_ => 1) : () } keys %Links::DBSQL::Links::db_def } if keys %Links::DBSQL::Links::db_def;
|
||||
$i_non_standard_cols{Validate} = { map { !$i_standard_cols{Validate}{$_} ? ($_ => 1) : () } keys %Links::DBSQL::Validate::db_def } if keys %Links::DBSQL::Validate::db_def;
|
||||
|
||||
my $Links_counter;
|
||||
my $Category_counter;
|
||||
|
||||
my $odbc = 0;
|
||||
if ($DB->table('Links')->{connect}->{driver} eq 'ODBC') {
|
||||
$odbc = 1;
|
||||
$i_dbh{$Links::DBSQL::Links::db_name}->{LongReadLen} = 1000000;
|
||||
}
|
||||
|
||||
if ($$opt{clear_tables}) {
|
||||
# Delete everything from all tables, EXCEPT for the `admin' user from the Users table
|
||||
$e_dbh->do("DELETE FROM ${e_prefix}Users WHERE Username <> 'admin'") or critical "Unable to delete all existing users: ".$e_dbh->errstr;
|
||||
for (qw/Links Category CatLinks CatRelations Category_Score_List
|
||||
Category_Word_List ClickTrack Editors EmailMailings EmailTemplates
|
||||
Links_Score_List Links_Word_List MailingIndex MailingList
|
||||
MailingListIndex Sessions Verify/) {
|
||||
$e_dbh->do("DELETE FROM $e_prefix$_");
|
||||
}
|
||||
unless ($$opt{straight_import}) {
|
||||
$Links_counter = $Category_counter = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
my $sth = $e_dbh->prepare("SELECT MAX(ID) FROM ${e_prefix}Links") or critical "Unable to prepare query `SELECT MAX(ID) FROM ${e_prefix}Links': ".$e_dbh->errstr;
|
||||
$sth->execute or critical "Unable to execute query `SELECT MAX(ID) FROM ${e_prefix}Links': ".$sth->errstr;
|
||||
$Links_counter = $sth->fetchrow_array;
|
||||
$sth->finish();
|
||||
|
||||
$sth = $e_dbh->prepare("SELECT MAX(ID) FROM ${e_prefix}Category") or critical "Unable to prepare query `SELECT MAX(ID) FROM ${e_prefix}Category': ".$e_dbh->errstr;
|
||||
$sth->execute or critical "Unable to execute query `SELECT MAX(ID) FROM ${e_prefix}Category': ".$sth->errstr;
|
||||
$Category_counter = $sth->fetchrow_array;
|
||||
$sth->finish();
|
||||
}
|
||||
|
||||
# Users
|
||||
{
|
||||
my $get_cols = "Username, Password, Email, Validation, Status";
|
||||
my $ins_cols = "(Name, Username, Password, Email, Validation, Status";
|
||||
my $ins_vals = "(?, ?, ?, ?, ?, ?";
|
||||
for (keys %{$e_non_standard_cols{"${e_prefix}Users"}}) {
|
||||
if ($i_non_standard_cols{Users}{$_}) {
|
||||
$ins_cols .= ", $_";
|
||||
$ins_vals .= ", ?";
|
||||
$get_cols .= ", $_";
|
||||
}
|
||||
else {
|
||||
mild_warning("Custom destination column `${e_prefix}Users.$_' has no equivelant import column. It will contain the default values for the column");
|
||||
}
|
||||
}
|
||||
for (grep +(not $e_standard_cols{"${e_prefix}Users"}{$_} and not $e_non_standard_cols{"${e_prefix}Users"}{$_}), keys %{$i_non_standard_cols{Users}}) {
|
||||
next if $e_non_standard_cols{"${e_prefix}Users"}{$_};
|
||||
if ($opt->{create_columns}) {
|
||||
mild_warning("Custom import column `Users.$_' had no destination equivelant. A column will be created");
|
||||
my $editor = $DB->editor("Users");
|
||||
my @def = @{$Links::DBSQL::Users::db_def{$_}};
|
||||
$editor->add_col(
|
||||
$_,
|
||||
{
|
||||
type => ((uc $def[1] eq 'CHAR' and $def[3] > 255) ? 'TEXT' : $def[1]),
|
||||
($def[2] ? (form_size => ((index($def[2],"x") > -1) ? [split(/x/,$def[2],2)] : $def[2])) : ()),
|
||||
size => $def[3],
|
||||
($def[4] ? (not_null => 1) : ()),
|
||||
($def[5] ? (default => $def[5]) : ()),
|
||||
($def[6] ? (regex => $def[6]) : ()),
|
||||
($def[7] ? (weight => $def[7]) : ())
|
||||
}
|
||||
);
|
||||
$ins_cols .= ", $_";
|
||||
$ins_vals .= ", ?";
|
||||
$get_cols .= ", $_";
|
||||
|
||||
$e_non_standard_cols{"${e_prefix}Users"}{$_} = 1;
|
||||
}
|
||||
else {
|
||||
warning("Custom import column `Users.$_' has no destination equivelant. It will be ignored");
|
||||
}
|
||||
}
|
||||
$ins_cols .= ")";
|
||||
$ins_vals .= ")";
|
||||
my $sth = $i_dbh{$Links::DBSQL::Users::db_name}->prepare("SELECT $get_cols FROM $Links::DBSQL::Users::db_table") or critical("Unable to prepare query `SELECT $get_cols FROM $Links::DBSQL::Users::db_table': ".$i_dbh{$Links::DBSQL::Users::db_name}->errstr);
|
||||
$sth->execute() or critical("Unable to execute query `SELECT $get_cols FROM $Links::DBSQL::Users::db_table': ".$sth->errstr);
|
||||
my $ins_sth = $e_dbh->prepare("INSERT INTO ${e_prefix}Users $ins_cols VALUES $ins_vals") or critical("Unable to prepare query `INSERT INTO ${e_prefix}Users $ins_cols VALUES $ins_vals': ".$e_dbh->errstr);
|
||||
my $user_count_sth = $e_dbh->prepare("SELECT COUNT(*) FROM ${e_prefix}Users WHERE Email = ?") or critical("Unable to prepare query `SELECT COUNT(*) FROM ${e_prefix}Users WHERE Email = ?': ".$e_dbh->errstr);
|
||||
my $username_sth = $e_dbh->prepare("SELECT Username FROM ${e_prefix}Users WHERE Email = ?") or critical("Unable to prepare query `SELECT Username FROM ${e_prefix}Users WHERE Email = ?': ".$e_dbh->errstr);
|
||||
|
||||
# What other than the Name and ReceiveMail can be updated here?
|
||||
my $user_mod_sth = $e_dbh->prepare("UPDATE ${e_prefix}Users SET Name = ?, ReceiveMail = ? WHERE Email = ?") or critical "Unable to prepare query `UPDATE ${e_prefix}Users SET Name = ?, ReceiveMail = ? WHERE Email = ?': ".$e_dbh->errstr;
|
||||
|
||||
while (my $row = $sth->fetchrow_arrayref) {
|
||||
$user_count_sth->execute($$row[2]) or warning("Unable to execute query `SELECT COUNT(*) FROM ${e_prefix}Users WHERE Email = ?': ".$user_count_sth->errstr);
|
||||
if ($user_count_sth->fetchrow_array) { # This e-mail address already exists, so skip it
|
||||
next;
|
||||
}
|
||||
$ins_sth->execute(@$row[0,0],($$row[1] or random_pass()),@$row[2..$#$row]) or warning("Unable to execute query `INSERT INTO ${e_prefix}Users $ins_cols VALUES $ins_vals' ($$row[0]): ".$ins_sth->errstr),next;
|
||||
}
|
||||
}
|
||||
# Subscribe users - these users receive the newsletter.
|
||||
{
|
||||
my $get_subscribers = $i_dbh{$Links::DBSQL::Subscribe::db_name}->prepare("SELECT Name, Email FROM $Links::DBSQL::Subscribe::db_table") or warning("Unable to prepare query `SELECT Name, Email FROM $Links::DBSQL::Subscribe::db_table': ".$i_dbh{$Links::DBSQL::Subscribe::db_name}->errstr);
|
||||
$get_subscribers->execute();
|
||||
my $count_users = $e_dbh->prepare("SELECT COUNT(*) FROM ${e_prefix}Users WHERE Email = ?");
|
||||
my $add_user = $e_dbh->prepare("INSERT INTO ${e_prefix}Users (Name, Username, Password, Email, ReceiveMail, Status) VALUES (?, ?, ?, ?, 'Yes', 'Registered')");
|
||||
my $give_newsletter = $e_dbh->prepare("UPDATE ReceiveMail = 'Yes' WHERE Email = ?");
|
||||
|
||||
my $sub_imported = 0;
|
||||
import_print "\nImporting Subscribed users (users who receive the newsletter) ...\n";
|
||||
while (my $row = $get_subscribers->fetchrow_arrayref) {
|
||||
# If we are under ODBC we need to reset the sth handle to avoid a "Invalid Cursor State" error.
|
||||
$odbc and ($count_users->finish);
|
||||
$count_users->execute($$row[1]) or warning("Unable to count users with email $$row[1]: ".$count_users->errstr), next;
|
||||
if ($count_users->fetchrow_array) {
|
||||
$give_newsletter->execute($$row[1]) or warning("Unable to set Newsletter = 'Yes' for user with e-mail $$row[1]: ".$give_newsletter->errstr),--$sub_imported;
|
||||
}
|
||||
else { # User doesn't already exist
|
||||
$add_user->execute($$row[0], $$row[1], random_pass(), $$row[1]) or warning("Unable to insert user $$row[1]: ".$add_user->errstr),--$sub_imported;
|
||||
}
|
||||
import_print "$sub_imported\n" unless ++$sub_imported % 500;
|
||||
}
|
||||
import_print "$sub_imported Subscribed users imported.\n";
|
||||
}
|
||||
|
||||
# Everything else (in most cases including even more users)
|
||||
{
|
||||
# Category select statements
|
||||
my $cat_get_cols = "ID, Name, Description, Meta_Description, Meta_Keywords, " .
|
||||
"Header, Footer, Number_of_Links, Has_New_Links, Has_Changed_Links, Newest_Link";
|
||||
my $cat_ins_cols = "(ID, Name, FatherID, Full_Name, Description, Meta_Description, Meta_Keywords, " .
|
||||
"Header, Footer, Number_of_Links, Has_New_Links, Has_Changed_Links, Newest_Link";
|
||||
my $cat_ins_vals = "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?";
|
||||
|
||||
# Links select statements
|
||||
my $links_get_cols = "ID, Contact_Name, Contact_Email, ReceiveMail, CategoryID, " .
|
||||
"Title, URL, Add_Date, Mod_Date, Description, Hits, isNew, " .
|
||||
"isChanged, isPopular, Rating, Votes, Status, Date_Checked";
|
||||
my $links_ins_cols = "(ID, LinkOwner, isValidated, Contact_Name, Contact_Email, " .
|
||||
"Title, URL, Add_Date, Mod_Date, Description, Hits, isNew, " .
|
||||
"isChanged, isPopular, Rating, Votes, Status, Date_Checked";
|
||||
my $links_ins_vals = "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?";
|
||||
|
||||
# Validate select statements
|
||||
my $validate_get_cols = "ID, Contact_Name, Contact_Email, ReceiveMail, CategoryID, " .
|
||||
"Title, URL, Add_Date, Mod_Date, Description, Hits, isNew, " .
|
||||
"isChanged, isPopular, Rating, Votes, Status, Date_Checked";
|
||||
my $validate_ins_cols = "(ID, LinkOwner, isValidated, Contact_Name, Contact_Email, " .
|
||||
"Title, URL, Add_Date, Mod_Date, Description, Hits, isNew, " .
|
||||
"isChanged, isPopular, Rating, Votes, Status, Date_Checked";
|
||||
my $validate_ins_vals = "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?";
|
||||
|
||||
# Build up extra fields that exist in both old and new Category tables
|
||||
for (keys %{$e_non_standard_cols{"${e_prefix}Category"}}) {
|
||||
if ($i_non_standard_cols{Category}{$_}) {
|
||||
$cat_ins_cols .= ", $_";
|
||||
$cat_ins_vals .= ", ?";
|
||||
$cat_get_cols .= ", $_";
|
||||
}
|
||||
else {
|
||||
mild_warning("Custom destination column `${e_prefix}Category.$_' has no equivelant import column. It will contain the default values for the column");
|
||||
}
|
||||
}
|
||||
|
||||
for (grep !$e_non_standard_cols{"${e_prefix}Category"}{$_}, keys %{$i_non_standard_cols{Category}}) {
|
||||
next if $e_non_standard_cols{"${e_prefix}Category"}{$_};
|
||||
if ($opt->{create_columns}) {
|
||||
mild_warning("Custom import column `Category.$_' had no destination equivelant. A destination column will be created");
|
||||
my $editor = $DB->editor("Category");
|
||||
my @def = @{$Links::DBSQL::Category::db_def{$_}};
|
||||
$editor->add_col(
|
||||
$_,
|
||||
{
|
||||
type => ((uc $def[1] eq 'CHAR' and $def[3] > 255) ? 'TEXT' : $def[1]),
|
||||
($def[2] ? (form_size => ((index($def[2],"x") > -1) ? [split(/x/,$def[2],2)] : $def[2])) : ()),
|
||||
size => $def[3],
|
||||
($def[4] ? (not_null => 1) : ()),
|
||||
($def[5] ? (default => $def[5]) : ()),
|
||||
($def[6] ? (regex => $def[6]) : ()),
|
||||
($def[7] ? (weight => $def[7]) : ())
|
||||
}
|
||||
);
|
||||
$cat_ins_cols .= ", $_";
|
||||
$cat_ins_vals .= ", ?";
|
||||
$cat_get_cols .= ", $_";
|
||||
|
||||
$e_non_standard_cols{"${e_prefix}Category"}{$_} = 1;
|
||||
}
|
||||
else {
|
||||
warning("Custom import column `Category.$_' has no destination equivelant. It will be ignored");
|
||||
}
|
||||
}
|
||||
$cat_ins_cols .= ")";
|
||||
$cat_ins_vals .= ")";
|
||||
|
||||
|
||||
for (keys %{$e_non_standard_cols{"${e_prefix}Links"}}) {
|
||||
if ($i_non_standard_cols{Links}{$_}) {
|
||||
$links_ins_cols .= ", $_";
|
||||
$links_ins_vals .= ", ?";
|
||||
$links_get_cols .= ", $_";
|
||||
}
|
||||
else {
|
||||
mild_warning("Custom destination column `${e_prefix}Links.$_' has no equivelant import column. It will contain the default values for the column");
|
||||
}
|
||||
}
|
||||
|
||||
for (grep !$e_non_standard_cols{"${e_prefix}Links"}{$_}, keys %{$i_non_standard_cols{Links}}) {
|
||||
next if $e_non_standard_cols{"${e_prefix}Links"}{$_};
|
||||
if ($opt->{create_columns}) {
|
||||
mild_warning("Custom import column `Links.$_' had no destination equivelant. A destination column will be created");
|
||||
my $editor = $DB->editor("Links");
|
||||
my @def = @{$Links::DBSQL::Links::db_def{$_}};
|
||||
$editor->add_col(
|
||||
$_,
|
||||
{
|
||||
type => ((uc $def[1] eq 'CHAR' and $def[3] > 255) ? 'TEXT' : $def[1]),
|
||||
($def[2] ? (form_size => ((index($def[2],"x") > -1) ? [split(/x/,$def[2],2)] : $def[2])) : ()),
|
||||
size => $def[3],
|
||||
($def[4] ? (not_null => 1) : ()),
|
||||
($def[5] ? (default => $def[5]) : ()),
|
||||
($def[6] ? (regex => $def[6]) : ()),
|
||||
($def[7] ? (weight => $def[7]) : ())
|
||||
}
|
||||
);
|
||||
$links_ins_cols .= ", $_";
|
||||
$links_ins_vals .= ", ?";
|
||||
$links_get_cols .= ", $_";
|
||||
|
||||
$e_non_standard_cols{"${e_prefix}Links"}{$_} = 1;
|
||||
}
|
||||
else {
|
||||
warning("Custom import column `Links.$_' has no destination equivelant. It will be ignored");
|
||||
}
|
||||
}
|
||||
$links_ins_cols .= ")";
|
||||
$links_ins_vals .= ")";
|
||||
|
||||
|
||||
for (keys %{$e_non_standard_cols{"${e_prefix}Links"}}) {
|
||||
if ($i_non_standard_cols{Validate}{$_}) {
|
||||
$validate_ins_cols .= ", $_";
|
||||
$validate_ins_vals .= ", ?";
|
||||
$validate_get_cols .= ", $_";
|
||||
}
|
||||
else {
|
||||
mild_warning("Custom destination column `${e_prefix}Links.$_' has no equivelant Validate import column. It will contain the default values for the column");
|
||||
}
|
||||
}
|
||||
|
||||
for (grep !$e_non_standard_cols{"${e_prefix}Links"}{$_}, keys %{$i_non_standard_cols{Validate}}) {
|
||||
next if $e_non_standard_cols{"${e_prefix}Links"}{$_};
|
||||
if ($opt->{create_columns}) {
|
||||
mild_warning("Custom import column `Validate.$_' had no destination Links equivelant. A destination column will be created");
|
||||
my $editor = $DB->editor("Links");
|
||||
my @def = @{$Links::DBSQL::Validate::db_def{$_}};
|
||||
$editor->add_col(
|
||||
$_,
|
||||
{
|
||||
type => ((uc $def[1] eq 'CHAR' and $def[3] > 255) ? 'TEXT' : $def[1]),
|
||||
($def[2] ? (form_size => ((index($def[2],"x") > -1) ? [split(/x/,$def[2],2)] : $def[2])) : ()),
|
||||
size => $def[3],
|
||||
($def[4] ? (not_null => 1) : ()),
|
||||
($def[5] ? (default => $def[5]) : ()),
|
||||
($def[6] ? (regex => $def[6]) : ()),
|
||||
($def[7] ? (weight => $def[7]) : ())
|
||||
}
|
||||
);
|
||||
$validate_ins_cols .= ", $_";
|
||||
$validate_ins_vals .= ", ?";
|
||||
$validate_get_cols .= ", $_";
|
||||
|
||||
$e_non_standard_cols{"${e_prefix}Links"}{$_} = 1;
|
||||
|
||||
}
|
||||
else {
|
||||
warning("Custom import column `Validate.$_' has no destination equivelant. It will be ignored");
|
||||
}
|
||||
}
|
||||
$validate_ins_cols .= ")";
|
||||
$validate_ins_vals .= ")";
|
||||
|
||||
my $cat_sth = $i_dbh{$Links::DBSQL::Category::db_name}->prepare("SELECT $cat_get_cols FROM $Links::DBSQL::Category::db_table ORDER BY Name") or critical("Unable to prepare query `SELECT $cat_get_cols FROM $Links::DBSQL::Category::db_table ORDER BY Name': ".$i_dbh{$Links::DBSQL::Category::db_name}->errstr);
|
||||
$cat_sth->execute() or critical("Unable to execute query `SELECT $cat_get_cols FROM $Links::DBSQL::Category::db_table ORDER BY Name': ".$cat_sth->errstr);
|
||||
|
||||
my $get_cat_relations = $i_dbh{$Links::DBSQL::CategoryRelations::db_name}->prepare("SELECT CategoryID, RelatedID from $Links::DBSQL::CategoryRelations::db_table") or critical "Unable to prepare query `SELECT CategoryID, RelatedID from $Links::DBSQL::CategoryRelations::db_table': ".$i_dbh{$Links::DBSQL::CategoryRelations::db_name}->errstr;
|
||||
my $add_cat_relation = $e_dbh->prepare("INSERT INTO ${e_prefix}CatRelations (CategoryID, RelatedID) VALUES (?, ?)") or critical "Unable to prepare query `INSERT INTO ${e_prefix}CatRelations (CategoryID, RelatedID) VALUES (?, ?)': ".$e_dbh->errstr;
|
||||
my @cat_map; # $cat_map[old_id] = new_id; Don't need this with --straight-import enabled
|
||||
|
||||
my $count_cats_sth = $e_dbh->prepare("SELECT COUNT(*) FROM ${e_prefix}Category WHERE Full_Name = ?") or critical("Unable to prepare query `SELECT COUNT(*) FROM ${e_prefix}Category WHERE Full_Name = ?': ".$e_dbh->errstr);
|
||||
|
||||
my $get_cat_alts = $i_dbh{$Links::DBSQL::CategoryAlternates::db_name}->prepare("SELECT CategoryID FROM $Links::DBSQL::CategoryAlternates::db_table WHERE LinkID = ?") or critical "Unable to prepare query `SELECT * FROM $Links::DBSQL::CategoryAlternates::db_table WHERE LinkID = ?': ".$i_dbh{$Links::DBSQL::CategoryAlternates::db_name}->errstr;
|
||||
|
||||
my $cat_ins_sth = $odbc ?
|
||||
($e_dbh->prepare("SET IDENTITY_INSERT ${e_prefix}Category ON; INSERT INTO ${e_prefix}Category $cat_ins_cols VALUES $cat_ins_vals") or critical("Unable to prepare query `INSERT INTO ${e_prefix}Category $cat_ins_cols VALUES $cat_ins_vals': ".$e_dbh->errstr)) :
|
||||
($e_dbh->prepare("INSERT INTO ${e_prefix}Category $cat_ins_cols VALUES $cat_ins_vals") or critical("Unable to prepare query `INSERT INTO ${e_prefix}Category $cat_ins_cols VALUES $cat_ins_vals': ".$e_dbh->errstr));
|
||||
|
||||
my $cat_ins_simple_sth = $odbc ?
|
||||
($e_dbh->prepare("SET IDENTITY_INSERT ${e_prefix}Category ON; INSERT INTO ${e_prefix}Category (ID, Name, Full_Name, FatherID) VALUES (?, ?, ?, ?)") or critical("Unable to prepare query `INSERT INTO ${e_prefix}Category (ID, Name, Full_Name, FatherID) VALUES (?, ?, ?, ?)': ".$e_dbh->errstr)) :
|
||||
($e_dbh->prepare("INSERT INTO ${e_prefix}Category (ID, Name, Full_Name, FatherID) VALUES (?, ?, ?, ?)") or critical("Unable to prepare query `INSERT INTO ${e_prefix}Category (ID, Name, Full_Name, FatherID) VALUES (?, ?, ?, ?)': ".$e_dbh->errstr));
|
||||
|
||||
my $user_ins_sth = $e_dbh->prepare("INSERT INTO ${e_prefix}Users (Username, Email, Password, Name, ReceiveMail, Status) VALUES (?, ?, ?, ?, ?, 'Registered')") or critical("Unable to prepare query `INSERT INTO ${e_prefix}Users (Username, Email, Name, ReceiveMail, Status) VALUES (?, ?, ?, ?, ?, 'Registered')': ".$e_dbh->errstr);
|
||||
my $cat_links_sth = $e_dbh->prepare("INSERT INTO ${e_prefix}CatLinks (LinkID, CategoryID) VALUES (?, ?)") or critical("Unable to prepare query `INSERT INTO ${e_prefix}CatLinks (LinkID, CategoryID) VALUES (?, ?)': ".$e_dbh->errstr);
|
||||
my $insert_link_sth = $odbc ?
|
||||
($e_dbh->prepare("SET IDENTITY_INSERT ${e_prefix}Links ON; INSERT INTO ${e_prefix}Links $links_ins_cols VALUES $links_ins_vals") or critical("Unable to prepare query `INSERT INTO ${e_prefix}Links $links_ins_cols VALUES $links_ins_vals': ".$e_dbh->errstr)) :
|
||||
($e_dbh->prepare("INSERT INTO ${e_prefix}Links $links_ins_cols VALUES $links_ins_vals") or critical("Unable to prepare query `INSERT INTO ${e_prefix}Links $links_ins_cols VALUES $links_ins_vals': ".$e_dbh->errstr));
|
||||
|
||||
my $insert_vlink_sth = $odbc ?
|
||||
($e_dbh->prepare("SET IDENTITY_INSERT ${e_prefix}Links ON; INSERT INTO ${e_prefix}Links $validate_ins_cols VALUES $validate_ins_vals") or critical("Unable to prepare query `INSERT INTO ${e_prefix}Links $validate_ins_cols VALUES $validate_ins_vals': ".$e_dbh->errstr)) :
|
||||
($e_dbh->prepare("INSERT INTO ${e_prefix}Links $validate_ins_cols VALUES $validate_ins_vals") or critical("Unable to prepare query `INSERT INTO ${e_prefix}Links $validate_ins_cols VALUES $validate_ins_vals': ".$e_dbh->errstr));
|
||||
|
||||
my $father_sth = $e_dbh->prepare("SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?") or critical("Unable to prepare query `SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?': ".$e_dbh->errstr);
|
||||
|
||||
my $get_links_sth = $i_dbh{$Links::DBSQL::Links::db_name}->prepare("SELECT $links_get_cols FROM $Links::DBSQL::Links::db_table WHERE CategoryID = ?") or critical("Unable to prepare query `SELECT $links_get_cols FROM $Links::DBSQL::Links::db_table WHERE CategoryID = ?': ".$i_dbh{$Links::DBSQL::Links::db_name}->errstr);
|
||||
my $get_vlinks_sth = $i_dbh{$Links::DBSQL::Validate::db_name}->prepare("SELECT $validate_get_cols FROM $Links::DBSQL::Validate::db_table WHERE CategoryID = ?") or critical("Unable to prepare query `SELECT $validate_get_cols FROM $Links::DBSQL::Validate::db_table WHERE CategoryID = ?': ".$i_dbh{$Links::DBSQL::Validate::db_name}->errstr);
|
||||
|
||||
my $user_count_sth = $e_dbh->prepare("SELECT COUNT(*) FROM ${e_prefix}Users WHERE Email = ?") or critical("Unable to prepare query `SELECT COUNT(*) FROM ${e_prefix}Users WHERE Email = ?': ".$e_dbh->errstr);
|
||||
my $username_sth = $e_dbh->prepare("SELECT Username FROM ${e_prefix}Users WHERE Email = ?") or critical("Unable to prepare query `SELECT Username FROM ${e_prefix}Users WHERE Email = ?': ".$e_dbh->errstr);
|
||||
|
||||
# What other than the Name and ReceiveMail can be updated here?
|
||||
my $user_mod_sth = $e_dbh->prepare("UPDATE ${e_prefix}Users SET Name = ?, ReceiveMail = ? WHERE Email = ?") or critical "Unable to prepare query `UPDATE ${e_prefix}Users SET Name = ?, ReceiveMail = ? WHERE Email = ?': ".$e_dbh->errstr;
|
||||
my $num_links_sth = $e_dbh->prepare("UPDATE ${e_prefix}Category SET Number_of_Links = ? WHERE ID = ?") or critical "Unable to prepare query `UPDATE ${e_prefix}Category SET Number_of_Links = ? WHERE ID = ?': ".$e_dbh->errstr;
|
||||
|
||||
import_print "\nImporting Categories and Links ...\n";
|
||||
my $links_imported = 0;
|
||||
my $cats_imported = 0;
|
||||
my @missing_cats; # contains the Full_Name's of missing categories.
|
||||
my %missing_cats; # contains Full_name => true for missing categories.
|
||||
|
||||
# Have to go through hoops here as ODBC can only run one sth at a time.
|
||||
my $sub;
|
||||
if ($odbc) {
|
||||
my $results = $cat_sth->fetchall_arrayref;
|
||||
$cat_sth->finish;
|
||||
import_print "\n\tImporting ", scalar @$results, " categories ..\n";
|
||||
$sub = sub { return shift @$results; }
|
||||
}
|
||||
else {
|
||||
$sub = sub { $cat_sth->fetchrow_arrayref; }
|
||||
}
|
||||
while(my $row = $sub->()) {
|
||||
$row = [@$row];
|
||||
my $old_id = shift @$row;
|
||||
my $new_id = $$opt{straight_import} ? $old_id : ++$Category_counter;
|
||||
my ($name) = (my $full_name = shift @$row) =~ m[([^/]*)\Z];
|
||||
unless (defined $name and length $name) {
|
||||
$Category_counter-- unless $$opt{straight_import};
|
||||
warning "Cannot insert Category $full_name because it is an invalid name";
|
||||
next;
|
||||
}
|
||||
my ($father_full_name) = $full_name =~ m[\A(.*)/];
|
||||
my $father_id;
|
||||
if (not defined $father_full_name) {
|
||||
$father_id = 0;
|
||||
}
|
||||
else {
|
||||
$odbc and $father_sth->finish;
|
||||
$father_sth->execute($father_full_name) or critical "Unable to execute query `SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?': ".$father_sth->errstr;
|
||||
if (my $ar = $father_sth->fetchrow_arrayref()) {
|
||||
$father_id = $ar->[0] || 0;
|
||||
}
|
||||
else {
|
||||
if ($$opt{create_missing_categories}) {
|
||||
if ($missing_cats{$father_full_name}++) {
|
||||
mild_warning "$father_full_name is needed for category $full_name and is already in the list of categories to be created";
|
||||
}
|
||||
else {
|
||||
my $ins_pos = @missing_cats;
|
||||
splice @missing_cats, $ins_pos, 0, $father_full_name;
|
||||
mild_warning "$father_full_name is needed for category $full_name and does not exist. It will be created";
|
||||
my $fn = $father_full_name;
|
||||
while ($fn =~ s[/[^/]*\Z][]) {
|
||||
$count_cats_sth->execute($fn) or critical "Unable to execute query `SELECT COUNT(*) FROM ${e_prefix}Category WHERE Full_Name = ?': ".$count_cats_sth->errstr;
|
||||
if ($count_cats_sth->fetchrow_array) { # It exists
|
||||
last;
|
||||
}
|
||||
else {
|
||||
splice @missing_cats, $ins_pos, 0, $fn;
|
||||
mild_warning "$fn is needed for category $full_name and does not exist. It will be created";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
warning "No father row found for $full_name! This may be a serious error as $full_name should probably have a father category";
|
||||
}
|
||||
$father_id = 0;
|
||||
}
|
||||
}
|
||||
if ($$opt{data_integrity}) {
|
||||
$odbc and $count_cats_sth->finish;
|
||||
$count_cats_sth->execute($full_name) or critical "Unable to execute query `SELECT COUNT(*) FROM ${e_prefix}Category WHERE Full_Name = ?': ".$count_cats_sth->errstr;
|
||||
unless ($count_cats_sth->fetchrow_array) {
|
||||
unless ($cat_ins_sth->execute($new_id,$name,$father_id,$full_name,@$row)) {
|
||||
$Category_counter-- unless $$opt{straight_import};
|
||||
warning "Unable to insert category `$full_name' (SQL query: `INSERT INTO ${e_prefix}Category $cat_ins_cols VALUES $cat_ins_vals'): ".$cat_ins_sth->errstr;
|
||||
next;
|
||||
}
|
||||
elsif (not $$opt{straight_import}) {
|
||||
$cat_map[$old_id] = $new_id;
|
||||
}
|
||||
}
|
||||
else {
|
||||
--$Category_counter unless $$opt{straight_import};
|
||||
mild_warning("Duplicate category found ($full_name) and skipped");
|
||||
next;
|
||||
}
|
||||
}
|
||||
else {
|
||||
unless ($cat_ins_sth->execute($new_id,$name,$father_id,$full_name,@$row)) {
|
||||
--$Category_counter unless $$opt{straight_import};
|
||||
warning("Unable to insert category `$full_name' (SQL query: `INSERT INTO ${e_prefix}Category $cat_ins_cols VALUES $cat_ins_vals'): ".$cat_ins_sth->errstr);
|
||||
next;
|
||||
}
|
||||
elsif (not $$opt{straight_import}) {
|
||||
$cat_map[$old_id] = $new_id;
|
||||
}
|
||||
}
|
||||
|
||||
import_print "$cats_imported Categories imported\n" unless ++$cats_imported % 500;
|
||||
|
||||
my $num_of_links = 0;
|
||||
my $link_sub;
|
||||
$get_links_sth->execute($old_id) or critical "Unable to execute query: ".$get_links_sth->errstr;
|
||||
if ($odbc) {
|
||||
my $links_results = $get_links_sth->fetchall_arrayref;
|
||||
$get_links_sth->finish;
|
||||
$link_sub = sub { return shift @$links_results; }
|
||||
}
|
||||
else {
|
||||
$link_sub = sub { $get_links_sth->fetchrow_arrayref; }
|
||||
}
|
||||
while(my $row = $link_sub->()) {
|
||||
$row = [@$row];
|
||||
my ($id, $contact_name, $contact_email, $receive_mail, $cat_id) = splice @$row,0,5;
|
||||
|
||||
unshift @$row, $contact_name, $contact_email;
|
||||
|
||||
$get_cat_alts->execute($id) or critical "Unable to execute query `SELECT CategoryID FROM $Links::DBSQL::CategoryAlternates::db_table WHERE LinkID = ?': ".$get_cat_alts->errstr;
|
||||
my @alt_ids;
|
||||
while (my $row = $get_cat_alts->fetchrow_arrayref) {
|
||||
push @alt_ids, ($$opt{straight_import} ? $$row[0] : $cat_map[$$row[0]]);
|
||||
}
|
||||
|
||||
$id = ++$Links_counter unless $$opt{straight_import};
|
||||
my $username;
|
||||
$odbc and $user_count_sth->finish;
|
||||
$user_count_sth->execute($contact_email) or warning("Unable to execute query `SELECT COUNT(*) FROM ${e_prefix}Users WHERE Email = ?': ".$user_count_sth->errstr);
|
||||
if ($user_count_sth->fetchrow_array) { # This e-mail address already exists
|
||||
$user_mod_sth->execute($contact_name, ($receive_mail eq 'Yes' ? 'Yes' : 'No'), $contact_email) or warning("Unable to execute query `UPDATE ${e_prefix}Users SET Name = ?, ReceiveMail = ? WHERE Email = ?': ".$user_mod_sth->errstr);
|
||||
$odbc and $username_sth->finish;
|
||||
$username_sth->execute($contact_email) or warning("Unable to execute query: ".$username_sth->errstr);
|
||||
$username = $username_sth->fetchrow_arrayref()->[0];
|
||||
}
|
||||
elsif ($contact_email) {
|
||||
$user_ins_sth->execute(($contact_email) x 2, '', (defined $contact_name ? $contact_name : ""), ($receive_mail eq 'Yes' ? 'Yes' : 'No')) or warning("Unable to execute query `INSERT INTO ${e_prefix}Users (Username, Password, Email, Name, ReceiveMail, Status) VALUES (?, ?, ?, ?, 'Registered')': ".$user_ins_sth->errstr);
|
||||
$username = $contact_email;
|
||||
}
|
||||
else {
|
||||
mild_warning("Not enough information to add a user for link `".($$row[0] or '<unknown>')." (URL: ".($$row[1] or "<none>")."). Setting link owner to `admin'");
|
||||
$username = 'admin';
|
||||
}
|
||||
if ($insert_link_sth->execute($id,$username,'Yes',@$row)) {
|
||||
for ($new_id,@alt_ids) {
|
||||
if (! defined $_) { next; }
|
||||
$cat_links_sth->execute($id,$_) or warning "Unable to execute query `INSERT INTO ${e_prefix}CatLinks (LinkID, CategoryID) VALUES (?, ?)': ".$cat_links_sth->errstr;
|
||||
}
|
||||
$num_of_links++;
|
||||
import_print "$links_imported Links imported\n" unless ++$links_imported % 500;
|
||||
}
|
||||
else {
|
||||
$Links_counter-- unless $$opt{straight_import};
|
||||
warning("Unable to insert validated link `$$row[0]' (SQL query: `INSERT INTO ${e_prefix}Links $links_ins_cols VALUES $links_ins_vals'): ".$insert_link_sth->errstr);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
# Even with a straight import, Validate ID's cannot stay the same because they would conflict with link ID's.
|
||||
my $sth = $e_dbh->prepare("SELECT MAX(ID) FROM ${e_prefix}Links") or critical "Unable to prepare query `SELECT MAX(ID) FROM ${e_prefix}Links': ".$e_dbh->errstr;
|
||||
$sth->execute or critical "Unable to execute query `SELECT MAX(ID) FROM ${e_prefix}Links': ".$sth->errstr;
|
||||
$Links_counter = $sth->fetchrow_array;
|
||||
$sth->finish;
|
||||
if ($$opt{straight_import}) {
|
||||
# For a straight import, we need to make sure that the link ID's used
|
||||
# for non-validated links start after the highest old Link ID.
|
||||
$sth = $i_dbh{$Links::DBSQL::Links::db_name}->prepare("SELECT MAX(ID) FROM $Links::DBSQL::Users::db_table") or critical "Unable to prepare query `SELECT MAX(ID) FROM $Links::DBSQL::Users::db_table: ".$i_dbh{$Links::DBSQL::Links::db_name}->errstr;
|
||||
$sth->execute or critical "Unable to execute query `SELECT MAX(ID) FROM $Links::DBSQL::Users::db_table: ".$sth->errstr;
|
||||
my $old_max = $sth->fetchrow_array;
|
||||
$sth->finish;
|
||||
$Links_counter = $old_max if $old_max > $Links_counter;
|
||||
}
|
||||
}
|
||||
$get_vlinks_sth->execute($old_id) or critical "Unable to execute query: ".$get_vlinks_sth->errstr;
|
||||
if ($odbc) {
|
||||
my $links_results = $get_vlinks_sth->fetchall_arrayref;
|
||||
$get_vlinks_sth->finish;
|
||||
$link_sub = sub { return shift @$links_results }
|
||||
}
|
||||
else {
|
||||
$link_sub = sub { $get_vlinks_sth->fetchrow_arrayref }
|
||||
}
|
||||
while(my $row = $link_sub->()) {
|
||||
$row = [@$row]; # Get rid of a peculiar read-only aliasing in DBI
|
||||
my ($id, $contact_name, $contact_email, $receive_mail, $cat_id) = splice @$row,0,5;
|
||||
|
||||
unshift @$row, $contact_name, $contact_email;
|
||||
|
||||
$get_cat_alts->execute($id) or critical "Unable to execute query `SELECT CategoryID FROM $Links::DBSQL::CategoryAlternates::db_table WHERE LinkID = ?': ".$get_cat_alts->errstr;
|
||||
my @alt_ids;
|
||||
while (my $row = $get_cat_alts->fetchrow_arrayref) {
|
||||
push @alt_ids, ($$opt{straight_import} ? $$row[0] : $cat_map[$$row[0]]);
|
||||
}
|
||||
|
||||
$id = ++$Links_counter;
|
||||
my $username;
|
||||
$user_count_sth->execute($contact_email) or warning("Unable to execute query: ".$user_count_sth->errstr);
|
||||
if ($user_count_sth->fetchrow_array) { # Exists
|
||||
$user_mod_sth->execute($contact_name, ($receive_mail eq 'Yes' ? 'Yes' : 'No'), $contact_email) or warning("Unable to execute query `UPDATE ${e_prefix}Users SET Name = ?, ReceiveMail = ? WHERE Email = ?': ".$user_mod_sth->errstr);
|
||||
$username_sth->execute($contact_email) or warning("Unable to execute query: ".$username_sth->errstr);
|
||||
$username = $username_sth->fetchrow_arrayref()->[0];
|
||||
}
|
||||
elsif ($contact_email) { # Doesn't exist, but we can make the e-mail address into a username
|
||||
$user_ins_sth->execute(($contact_email) x 2, '', (defined $contact_name ? $contact_name : ""), ($receive_mail eq 'Yes' ? 'Yes' : 'No')) or warning("Unable to execute query `INSERT INTO ${e_prefix}Users (Username, Email, Name, ReceiveMail) VALUES (?, ?, ?, ?, ?)': ".$user_ins_sth->errstr);
|
||||
$username = $contact_email;
|
||||
}
|
||||
else { # Can't make a user; use the `admin' user.
|
||||
mild_warning("Not enough information to add a user for link `".($$row[0] or '<unknown>')." (URL: ".($$row[1] or "<none>")."). Setting link owner to `admin'");
|
||||
$username = 'admin';
|
||||
}
|
||||
if ($insert_vlink_sth->execute($id,$username,'No',@$row)) {
|
||||
for ($id,@alt_ids) {
|
||||
$cat_links_sth->execute($_,$new_id) or warning "Unable to execute query `INSERT INTO ${e_prefix}CatLinks (LinkID, CategoryID) VALUES (?, ?)': ".$cat_links_sth->errstr;
|
||||
}
|
||||
$num_of_links++;
|
||||
import_print "$links_imported Links imported\n" unless ++$links_imported % 500;
|
||||
}
|
||||
else {
|
||||
$Links_counter--;
|
||||
warning("Unable to insert non-validated link `$$row[0]' (SQL query: `INSERT INTO ${e_prefix}Links $validate_ins_cols VALUES $validate_ins_vals'): ".$insert_vlink_sth->errstr);
|
||||
}
|
||||
}
|
||||
$num_links_sth->execute($num_of_links,$new_id) or warning "Unable to execute query `UPDATE ${e_prefix}Category SET Number_of_Links = ? WHERE ID = ?': ".$num_links_sth->errstr;
|
||||
}
|
||||
my $missing_cats;
|
||||
if ($$opt{create_missing_categories} and @missing_cats) {
|
||||
my $counter = $e_dbh->prepare("SELECT MAX(ID) FROM ${e_prefix}Category");
|
||||
$counter->execute();
|
||||
my $count = $counter->fetchrow_array();
|
||||
my $update_sub_cats = $e_dbh->prepare("UPDATE ${e_prefix}Category SET FatherID = ? WHERE Full_Name LIKE ? AND Full_Name NOT LIKE ?") or critical "Unable to prepare query `UPDATE ${e_prefix}Category SET FatherID = ? WHERE Full_Name LIKE ? AND Full_Name NOT LIKE ?': ".$e_dbh->errstr;
|
||||
for (@missing_cats) {
|
||||
my ($name) = m[([^/]+)\Z];
|
||||
my ($father_full) = m[\A(.*)/];
|
||||
my $father_id;
|
||||
if ($father_full) {
|
||||
$father_sth->execute($father_full) or critical "Unable to execute query `SELECT ID FROM ${e_prefix}Category WHERE Full_Name = ?': ".$father_sth->errstr;
|
||||
$father_id = $father_sth->fetchrow_array;
|
||||
}
|
||||
else { # Must be a root category
|
||||
$father_id = 0;
|
||||
}
|
||||
$cat_ins_simple_sth->execute(++$count,$name,$_,$father_id) or critical "Unable to create missing category $_: ".$cat_ins_simple_sth->errstr;
|
||||
$update_sub_cats->execute($count,"$_/%","$_/%/%") or critical "Unable to execute query `UPDATE ${e_prefix}Category SET FatherID = ? WHERE Full_Name LIKE ? and Full_Name NOT LIKE ?': ".$update_sub_cats->errstr;
|
||||
$missing_cats++;
|
||||
}
|
||||
}
|
||||
|
||||
import_print "$cats_imported Categories imported";
|
||||
import_print ", $missing_cats missing categories created" if $missing_cats;
|
||||
import_print ".\n";
|
||||
import_print "$links_imported Links imported.\n";
|
||||
|
||||
# Category Relations:
|
||||
if ($$opt{straight_import}) {
|
||||
$get_cat_relations->execute or critical "Unable to execute query `SELECT CategoryID, RelatedID from $Links::DBSQL::CategoryRelations::db_table': ".$get_cat_relations->errstr;
|
||||
while (my $row = $get_cat_relations->fetchrow_arrayref) {
|
||||
$add_cat_relation->execute(@$row) or warning "Unable to add category relation for categories with ID's $$row[0] and $$row[1]. Reason: ".$add_cat_relation->errstr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$get_cat_relations->execute or critical "Unable to execute query `SELECT CategoryID, RelatedID from $Links::DBSQL::CategoryRelations::db_table': ".$get_cat_relations->errstr;
|
||||
while (my $row = $get_cat_relations->fetchrow_arrayref) {
|
||||
$add_cat_relation->execute(@cat_map[@$row]) or warning "Unable to add category relation for categories with ID's: (new: $cat_map[$$row[0]], old: $$row[0]) and (new: $cat_map[$$row[1]], old: $$row[1]). Reason: ".$add_cat_relation->errstr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (keys %i_dbh) {
|
||||
$i_dbh{$_}->disconnect;
|
||||
}
|
||||
$e_dbh->disconnect;
|
||||
import_print "\nNOTE: You must run Rebuild Cat. tree, Repair Tables, and Rebuild Search after performing an import!\n";
|
||||
|
||||
1;
|
||||
}
|
||||
|
||||
# Returns a random password of random length (20-25 characters).
|
||||
sub random_pass () {
|
||||
my @chars = ('a'..'z','A'..'Z',0..9,qw a _ [ ] { } ` ' " ! @ ^ * ( ) - _ = + : ; . / \ a,'#',',');
|
||||
my $pass = join '', map { $chars[rand @chars] } 0..(20+rand(5));
|
||||
}
|
||||
|
||||
1;
|
||||
152
site/slowtwitch.com/cgi-bin/articles/admin/Links/Import/S2BK.pm
Normal file
152
site/slowtwitch.com/cgi-bin/articles/admin/Links/Import/S2BK.pm
Normal file
@@ -0,0 +1,152 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: S2BK.pm,v 1.13 2009/05/09 06:35:25 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::Import::S2BK;
|
||||
|
||||
use 5.004_04;
|
||||
use strict;
|
||||
use vars qw/$Warning_Code $Critical_Code $Mild_Code $Print_Out/;
|
||||
|
||||
use GT::SQL;
|
||||
use Links qw/$CFG/;
|
||||
|
||||
sub critical {
|
||||
$Critical_Code->(@_);
|
||||
}
|
||||
|
||||
sub warning {
|
||||
$Warning_Code->(@_);
|
||||
}
|
||||
|
||||
sub mild_warning {
|
||||
ref $Mild_Code eq 'CODE' and $Mild_Code->(@_);
|
||||
}
|
||||
|
||||
sub import_print {
|
||||
if (ref $Print_Out eq 'CODE') {
|
||||
$Print_Out->(@_);
|
||||
}
|
||||
else {
|
||||
print @_;
|
||||
}
|
||||
}
|
||||
|
||||
# Takes 3-4 arguments: hash reference, 2 or 3 code refs
|
||||
# The hash reference is the options hash for an import.
|
||||
# The first code reference will be called when a warning occurs.
|
||||
# The second code reference will be called when a critical error occurs.
|
||||
# If provided, the third code reference will be called when a mild warning occurs
|
||||
sub import {
|
||||
my $opt = shift;
|
||||
return if ref $opt ne 'HASH';
|
||||
{
|
||||
my $warning = shift;
|
||||
return if ref $warning ne 'CODE';
|
||||
$Warning_Code = $warning;
|
||||
|
||||
my $critical = shift;
|
||||
return if ref $critical ne 'CODE';
|
||||
$Critical_Code = $critical;
|
||||
|
||||
my $mild = shift;
|
||||
$Mild_Code = $mild if ref $mild eq 'CODE';
|
||||
|
||||
my $output = shift;
|
||||
$Print_Out = $output if ref $output eq 'CODE';
|
||||
}
|
||||
|
||||
my $DB = new GT::SQL(def_path => $$opt{source}, subclass => 0);
|
||||
my $prefix = $DB->prefix || "";
|
||||
|
||||
my $delimiter = $$opt{delimiter};
|
||||
critical "Invalid delimiter `".(defined$delimiter?$delimiter:'')."' for a delimited file!"
|
||||
unless defined $delimiter and length $delimiter == 1 and $delimiter ne '\\';
|
||||
|
||||
my @tables;
|
||||
opendir (D, "$CFG->{admin_root_path}/defs") or critical "unable to opendir $CFG->{admin_root_path}/defs ($!)";
|
||||
while (defined (my $def = readdir(D))) {
|
||||
next unless $def =~ /^\Q$prefix\E(.*)\.def$/;
|
||||
push @tables, $1 if $1 !~ /_(?:Word|Score)_List$/;
|
||||
}
|
||||
|
||||
local ($,,$\,*EXPORT_FH);
|
||||
open EXPORT_FH, "> $$opt{destination}" or critical "Unable to open $$opt{destination} for writing: $!";
|
||||
binmode EXPORT_FH; # this is NOT a text file.
|
||||
print EXPORT_FH "Links SQL 2 backup. This backup was generated at " . gmtime() . " UTC. THIS FILE IS NOT A TEXT FILE. You should NOT attempt to edit this file as you will end up corrupting the data contained in it.\0";
|
||||
|
||||
=pod
|
||||
Schematic for the file:
|
||||
|
||||
- Newline delimiter is changed to \0 (hex and ascii 0).
|
||||
- Each line starting with '\\\\' starts off a new table.
|
||||
- The first line following the '\\\\' is the table name by itself (NOT prefixed).
|
||||
- The first character of the line after that is the delimiter for that table, and
|
||||
the rest of that line is the columns of the table delimited by the delimiter.
|
||||
- All subsequent lines (until another '\\\\') are individual records.
|
||||
- All fields (headers and records) are escaped where needed in '\\XX' format
|
||||
(where 'XX' is the hexadecimal representation of the character).
|
||||
- All lines until the first '\\\\' are treated as comments and are ignored.
|
||||
- Everything following '\\\\' is treated as a comment and is ignored.
|
||||
|
||||
=cut
|
||||
|
||||
for my $t (@tables) {
|
||||
$GT::SQL::error = '';
|
||||
my $table = $DB->table($t);
|
||||
my $count = $table->count;
|
||||
next if $GT::SQL::error;
|
||||
|
||||
import_print "Exporting $prefix$t ...\n";
|
||||
print EXPORT_FH "\\\\ The following is table $t".($prefix ? " (from prefixed table $prefix$t)" : "")."\0";
|
||||
print EXPORT_FH "$t\0";
|
||||
print EXPORT_FH $delimiter; # The first character on this line is the delimiter
|
||||
local ($a,$b);
|
||||
print EXPORT_FH join($delimiter, sort { $table->{schema}{cols}{$a}{pos} <=> $table->{schema}{cols}{$b}{pos} } map BK_escape($_,$delimiter), keys %{$table->cols}),"\0";
|
||||
my $sth;
|
||||
my $printed = 0;
|
||||
for my $i (0 .. $count/1000) {
|
||||
$sth = $table->prepare("SELECT * FROM $prefix$t LIMIT ".($i * 1000).", 1000") or critical "Unable to prepare query `SELECT * FROM $prefix$t LIMIT ".($i * 1000).", 1000': ".$sth->errstr;
|
||||
$sth->execute();
|
||||
while (my $row = $sth->fetchrow_arrayref) {
|
||||
print EXPORT_FH join($delimiter, map BK_escape($_,$delimiter), @$row),"\0";
|
||||
unless (++$printed % 500) {
|
||||
import_print "$printed records from $prefix$t exported ...\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
import_print "$printed records from $prefix$t exported.\n",
|
||||
"All records from $prefix$t have been exported.\n\n";
|
||||
}
|
||||
close EXPORT_FH;
|
||||
}
|
||||
|
||||
# Takes two parameters: The field to escape, and the delimiter. It will return
|
||||
# the escaped form of the field.
|
||||
sub BK_escape ($$) {
|
||||
return unless defined wantarray;
|
||||
my $field = shift;
|
||||
my $delimiter = shift;
|
||||
$delimiter = "" unless defined $delimiter;
|
||||
critical "Bad delimiter `$delimiter'" unless length $delimiter == 1 and $delimiter ne '\\';
|
||||
my $escape_chr = '\\';
|
||||
if (not defined $field) {
|
||||
return 'NULL';
|
||||
}
|
||||
elsif ($field eq 'NULL') {
|
||||
return 'NUL\4C'; # If it is the actual string 'NULL' this will keep it
|
||||
} # from being recognized as a NULL field when it is read in again.
|
||||
$field =~ s/([\Q$delimiter$escape_chr\E\x00-\x1f])/sprintf '\%02X', ord $1/ge;
|
||||
$field;
|
||||
}
|
||||
|
||||
"Once upon a time, in a galaxy far, far away . . . There was a true value";
|
||||
1214
site/slowtwitch.com/cgi-bin/articles/admin/Links/MassMailer.pm
Normal file
1214
site/slowtwitch.com/cgi-bin/articles/admin/Links/MassMailer.pm
Normal file
File diff suppressed because it is too large
Load Diff
622
site/slowtwitch.com/cgi-bin/articles/admin/Links/Newsletter.pm
Normal file
622
site/slowtwitch.com/cgi-bin/articles/admin/Links/Newsletter.pm
Normal file
@@ -0,0 +1,622 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Newsletter.pm,v 1.15 2007/09/06 01:43:45 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
# Notes about the Newsletter code:
|
||||
# ================================
|
||||
# Example category structure:
|
||||
# a
|
||||
# b
|
||||
# c
|
||||
# d
|
||||
# If a user is subscribed to a category (eg. category a), then they will
|
||||
# be automatically subscribed to all the subcategories of that category
|
||||
# (ie. b, c, d). If the user is already subscribed to a subcategory
|
||||
# (eg. b), then that subscription will be removed when they subscribe to
|
||||
# a parent category (ie. a). This keeps listing subscribed categories
|
||||
# simple.
|
||||
#
|
||||
# Remember that the root category (0) is a special category and needs to be
|
||||
# handled appropriately. It is not a real category as it does not exist in
|
||||
# the Category table.
|
||||
|
||||
package Links::Newsletter;
|
||||
|
||||
use strict;
|
||||
use Links qw/:objects/;
|
||||
use Links::SiteHTML;
|
||||
use GT::Dumper;
|
||||
|
||||
sub handle {
|
||||
# ---------------------------------------------------
|
||||
# Determine what to do.
|
||||
#
|
||||
my $res;
|
||||
my $action = lc $IN->param('action');
|
||||
|
||||
require Links::Build;
|
||||
my $mtl = Links::Build::build('title', Links::language('LINKS_NEWSLETTER'), "$CFG->{db_cgi_url}/subscribe.cgi");
|
||||
|
||||
# Custom lists
|
||||
if ($IN->param('list')) {
|
||||
my $email = $IN->param('email');
|
||||
if ($email and $action eq 'subscribe') {
|
||||
$res = $PLG->dispatch('custom_list_subscribe', \&custom_list_subscribe);
|
||||
}
|
||||
elsif ($email and $action eq 'unsubscribe') {
|
||||
$res = $PLG->dispatch('custom_list_unsubscribe', \&custom_list_unsubscribe);
|
||||
}
|
||||
else {
|
||||
$res = { error => Links::language('SUBSCRIBE_ERROR') };
|
||||
}
|
||||
$res->{main_title_loop} ||= $mtl;
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('newsletter', $res);
|
||||
}
|
||||
|
||||
# With the old Newsletter code, anyone could sign up to it. This is bad since
|
||||
# no e-mail validation is performed. The new code will only allow signed up
|
||||
# users to sign up.
|
||||
unless ($USER) {
|
||||
print $IN->redirect(Links::redirect_login_url('subscribe'));
|
||||
return;
|
||||
}
|
||||
|
||||
my $page;
|
||||
if ($CFG->{newsletter_global_subscribe}) {
|
||||
$page = 'newsletter_global';
|
||||
if ($action eq 'subscribe') {
|
||||
$res = $PLG->dispatch('newsletter_global_sub', \&global_subscribe);
|
||||
}
|
||||
elsif ($action eq 'unsubscribe') {
|
||||
$res = $PLG->dispatch('newsletter_global_unsub', \&global_unsubscribe);
|
||||
}
|
||||
}
|
||||
elsif ($action eq 'list') {
|
||||
$page = 'newsletter_list';
|
||||
}
|
||||
elsif ($action eq 'unsubscribe') {
|
||||
$res = $PLG->dispatch('newsletter_unsubscribe', \&unsubscribe);
|
||||
$page = $IN->param('page') || 'newsletter';
|
||||
}
|
||||
elsif ($action eq 'subscribe') {
|
||||
$res = $PLG->dispatch('newsletter_subscribe', \&subscribe);
|
||||
$page = 'newsletter';
|
||||
}
|
||||
elsif ($action eq 'update') {
|
||||
$res = $PLG->dispatch('newsletter_update', \&update_subscription);
|
||||
$page = 'newsletter_browse';
|
||||
}
|
||||
else {
|
||||
$page = 'newsletter_browse';
|
||||
}
|
||||
$res->{main_title_loop} ||= $mtl;
|
||||
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display($page, $res);
|
||||
}
|
||||
|
||||
sub custom_list_subscribe {
|
||||
# ---------------------------------------------------
|
||||
# Subscribe to a custom list
|
||||
#
|
||||
my $list = $IN->param('list');
|
||||
my $email = $IN->param('email');
|
||||
my $mli = $DB->table('MailingListIndex');
|
||||
my $ml = $DB->table('MailingList');
|
||||
unless ($mli->count({ Name => $list })) {
|
||||
return { error => Links::language('SUBSCRIBE_INVALIDLIST', $list) };
|
||||
}
|
||||
|
||||
my $id = $mli->select('ID', { Name => $list })->fetchrow;
|
||||
|
||||
if ($ml->count({ Email => $email, ID => $id })) {
|
||||
return { error => Links::language('SUBSCRIBE_ALREADYSUB') };
|
||||
}
|
||||
$ml->insert({ Email => $email, ID => $id });
|
||||
return { message => Links::language('SUBSCRIBE_SUCCESS') };
|
||||
}
|
||||
|
||||
sub custom_list_unsubscribe {
|
||||
# ---------------------------------------------------
|
||||
# Unsubscribe from a custom list
|
||||
#
|
||||
my $list = $IN->param('list');
|
||||
my $email = $IN->param('email');
|
||||
my $mli = $DB->table('MailingListIndex');
|
||||
my $ml = $DB->table('MailingList');
|
||||
unless ($mli->count({ Name => $list })) {
|
||||
return { error => Links::language('SUBSCRIBE_INVALIDLIST', $list) };
|
||||
}
|
||||
|
||||
my $id = $mli->select('ID', { Name => $list })->fetchrow;
|
||||
|
||||
unless ($ml->count({ Email => $email, ID => $id })) {
|
||||
return { error => Links::language('SUBSCRIBE_NOTSUB') };
|
||||
}
|
||||
$ml->delete({ Email => $email, ID => $id });
|
||||
return { message => Links::language('SUBSCRIBE_UNSUBSUCCESS') };
|
||||
}
|
||||
|
||||
sub global_subscribe {
|
||||
# ---------------------------------------------------
|
||||
# Global subscribe to the newsletter. If the admin option is enabled, then this
|
||||
# will behave like the newsletter did in 2.x, where there is only one global
|
||||
# newsletter. The only difference is that only registered users can subscribe.
|
||||
#
|
||||
my $ns = $DB->table('NewsletterSubscription');
|
||||
|
||||
if ($ns->count({ UserID => $USER->{Username}, CategoryID => 0 })) {
|
||||
return { error => Links::language('NEWSLETTERERR_ALREADYSUB') };
|
||||
}
|
||||
_subscribe(0);
|
||||
return { message => Links::language('NEWSLETTER_SUBSCRIBED') };
|
||||
}
|
||||
|
||||
sub global_unsubscribe {
|
||||
# ---------------------------------------------------
|
||||
# Unsubscribe from the newsletter.
|
||||
#
|
||||
my $ns = $DB->table('NewsletterSubscription');
|
||||
|
||||
if ($ns->count({ UserID => $USER->{Username}, CategoryID => 0 })) {
|
||||
_unsubscribe(0);
|
||||
return { message => Links::language('NEWSLETTER_UNSUBSCRIBED') };
|
||||
}
|
||||
return { error => Links::language('NEWSLETTERERR_NOTSUB') };
|
||||
}
|
||||
|
||||
sub global_subscribe_info {
|
||||
# ---------------------------------------------------
|
||||
# Returns information about the user's newsletter subscription.
|
||||
#
|
||||
return { subscribed => $DB->table('NewsletterSubscription')->count({ UserID => $USER->{Username}, CategoryID => 0 }) };
|
||||
}
|
||||
|
||||
sub list_subscribed {
|
||||
# ---------------------------------------------------
|
||||
# Returns a list of categories they are subscribed to.
|
||||
#
|
||||
my $ns = $DB->table('NewsletterSubscription');
|
||||
my $nsc = $DB->table('NewsletterSubscription', 'Category');
|
||||
|
||||
if ($ns->count({ UserID => $USER->{Username}, CategoryID => 0 })) {
|
||||
return { subscribed => [_root()] };
|
||||
}
|
||||
|
||||
$nsc->select_options("ORDER BY Full_Name");
|
||||
my $list = $nsc->select({ UserID => $USER->{Username} })->fetchall_hashref;
|
||||
return { subscribed => $list };
|
||||
}
|
||||
|
||||
sub unsubscribe {
|
||||
# ---------------------------------------------------
|
||||
# Unsubscribe from one or more categories.
|
||||
#
|
||||
my @unsub = $IN->param('ID');
|
||||
@unsub = @_ unless @unsub;
|
||||
|
||||
return { error => Links::language('NEWSLETTERERR_NOCATSUB') } unless @unsub;
|
||||
|
||||
_unsubscribe(@unsub);
|
||||
|
||||
return { message => Links::language('NEWSLETTER_CATUNSUB') };
|
||||
}
|
||||
|
||||
sub subscribe {
|
||||
# ---------------------------------------------------
|
||||
# Subscribe to one or more categories.
|
||||
#
|
||||
my @sub = $IN->param('ID');
|
||||
@sub = @_ unless @sub;
|
||||
|
||||
return { error => Links::language('NEWSLETTER_NOCATUNSUB') } unless @sub;
|
||||
|
||||
_subscribe(@sub);
|
||||
|
||||
return { message => Links::language('NEWSLETTER_CATSUB') };
|
||||
}
|
||||
|
||||
sub update_subscription {
|
||||
# ---------------------------------------------------
|
||||
# Update a User's category subscriptions from their browse selection.
|
||||
#
|
||||
|
||||
# These should be the original subscribe states of the categories. S<ID> are the
|
||||
# categories which they wish to be subscribed to.
|
||||
my @presub = $IN->param('subscribed');
|
||||
my @preunsub = $IN->param('unsubscribed');
|
||||
my (@sub, @unsub);
|
||||
|
||||
for (@presub) {
|
||||
next if $_ =~ /\D/;
|
||||
push @unsub, $_ unless defined $IN->param("S$_");
|
||||
}
|
||||
_unsubscribe(@unsub);
|
||||
|
||||
for (@preunsub) {
|
||||
next if $_ =~ /\D/;
|
||||
push @sub, $_ if defined $IN->param("S$_");
|
||||
}
|
||||
_subscribe(@sub);
|
||||
|
||||
return { message => Links::language('NEWSLETTER_CATUPDATED') };
|
||||
}
|
||||
|
||||
sub browse {
|
||||
# ---------------------------------------------------
|
||||
# Browse the categories.
|
||||
#
|
||||
my $root = $IN->param('root') || 0;
|
||||
my $cat = $DB->table('Category');
|
||||
my $ns = $DB->table('NewsletterSubscription');
|
||||
if ($root != 0 and not $cat->count({ ID => $root })) {
|
||||
$root = 0;
|
||||
}
|
||||
|
||||
my $root_cat;
|
||||
if ($root == 0) {
|
||||
$root_cat = _root();
|
||||
$root_cat->{CatDepth} = -1;
|
||||
}
|
||||
else {
|
||||
$root_cat = $cat->select({ ID => $root })->fetchrow_hashref or return { error => $GT::SQL::error };
|
||||
}
|
||||
|
||||
my $tree = $cat->tree;
|
||||
my $cats;
|
||||
# When root = 0, max_depth is kind of weird because there isn't actually a Category with ID = 0.
|
||||
# Because of this GT::SQL::Tree doesn't handle the case where max_depth = 1 and root = 0, so
|
||||
# we'll handle it ourselves.
|
||||
if ($root == 0 and $CFG->{newsletter_max_depth} == 1) {
|
||||
$cat->select_options("ORDER BY Full_Name");
|
||||
$cats = $cat->select({ FatherID => 0 })->fetchall_hashref;
|
||||
}
|
||||
else {
|
||||
$cats = $tree->children(id => $root, max_depth => ($root == 0 ? $CFG->{newsletter_max_depth} - 1 : $CFG->{newsletter_max_depth}), sort_col => 'Full_Name');
|
||||
}
|
||||
# Insert the root category as the first element
|
||||
splice @$cats, 0, 0, $root_cat;
|
||||
my @parents;
|
||||
my %catids;
|
||||
for (0 .. $#$cats) {
|
||||
my $c = $cats->[$_];
|
||||
# ID to $cats index mapping
|
||||
$catids{$c->{ID}}->{index} = $_;
|
||||
# List of children (only ones which are shown in the trimmed tree)
|
||||
$catids{$c->{ID}}->{children} = [];
|
||||
# Fix CatDepth to be relative to $root
|
||||
if ($_) {
|
||||
$c->{CatDepth} -= $root_cat->{CatDepth};
|
||||
}
|
||||
|
||||
# Keep track of categories which could have sub categories (that are past max_depth)
|
||||
if ($CFG->{newsletter_max_depth} > 0 and $c->{CatDepth} == $CFG->{newsletter_max_depth}) {
|
||||
$catids{$c->{ID}}->{check_child}++;
|
||||
}
|
||||
else {
|
||||
$c->{HasMoreChildren} = 0;
|
||||
}
|
||||
$c->{Subscribed} = 0;
|
||||
|
||||
# Find all the children
|
||||
while (@parents and @parents > $c->{CatDepth}) {
|
||||
my $p = pop @parents;
|
||||
for (@parents) {
|
||||
push @{$catids{$_}->{children}}, $p;
|
||||
}
|
||||
}
|
||||
push @parents, $c->{ID};
|
||||
}
|
||||
while (@parents) {
|
||||
my $p = pop @parents;
|
||||
for (@parents) {
|
||||
push @{$catids{$_}->{children}}, $p;
|
||||
}
|
||||
}
|
||||
$cats->[0]->{CatDepth} = 0;
|
||||
|
||||
if (%catids) {
|
||||
for (keys %catids) {
|
||||
$cats->[$catids{$_}->{index}]->{Children} = $catids{$_}->{children};
|
||||
}
|
||||
|
||||
# Figure out which categories the user has subscribed to
|
||||
my @subscribed = $ns->select('CategoryID', { UserID => $USER->{Username}, CategoryID => [keys %catids] })->fetchall_list;
|
||||
for (@subscribed) {
|
||||
$cats->[$catids{$_}->{index}]->{Subscribed}++;
|
||||
}
|
||||
|
||||
# Check to see which categories have sub categories
|
||||
my @check = grep $catids{$_}->{check_child}, keys %catids;
|
||||
if (@check) {
|
||||
my $subcats = $tree->child_ids(id => \@check);
|
||||
for (keys %$subcats) {
|
||||
$cats->[$catids{$_}->{index}]->{HasMoreChildren} = @{$subcats->{$_}};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my %previous = (PPID => '');
|
||||
my $parent_subscribed;
|
||||
if ($root != 0) {
|
||||
my @parents = @{$tree->parent_ids(id => $root)};
|
||||
splice(@parents, 0, 0, 0);
|
||||
|
||||
$parent_subscribed = $ns->count({ UserID => $USER->{Username}, CategoryID => \@parents });
|
||||
|
||||
my $parent;
|
||||
if (@parents < $CFG->{newsletter_max_depth}) {
|
||||
$parent = $parents[0];
|
||||
}
|
||||
else {
|
||||
$parent = $parents[-$CFG->{newsletter_max_depth}];
|
||||
}
|
||||
|
||||
# Get the previous parent's info
|
||||
if ($parent == 0) {
|
||||
$parent = _root();
|
||||
}
|
||||
else {
|
||||
$parent = $cat->select({ ID => $parent })->fetchrow_hashref;
|
||||
}
|
||||
%previous = map { "PP" . $_ => $parent->{$_} } keys %$parent;
|
||||
}
|
||||
|
||||
return { %previous, category => $cats, parent_subscribed => $parent_subscribed };
|
||||
}
|
||||
|
||||
sub admin_browse {
|
||||
# ---------------------------------------------------
|
||||
# Browse the categories (admin side).
|
||||
#
|
||||
my $root = $IN->param('root') || 0;
|
||||
my $cat = $DB->table('Category');
|
||||
my $ns = $DB->table('NewsletterSubscription');
|
||||
if ($root != 0 and not $cat->count({ ID => $root })) {
|
||||
$root = 0;
|
||||
}
|
||||
|
||||
my $root_cat;
|
||||
if ($root == 0) {
|
||||
$root_cat = _root();
|
||||
$root_cat->{CatDepth} = -1;
|
||||
}
|
||||
else {
|
||||
$root_cat = $cat->select({ ID => $root })->fetchrow_hashref or return { error => $GT::SQL::error };
|
||||
}
|
||||
|
||||
my $tree = $cat->tree;
|
||||
my $cats;
|
||||
# root (0) isn't a 'real' category in the tree, so we have to select it ourselves
|
||||
if ($root == 0 and $CFG->{newsletter_max_depth} == 1) {
|
||||
$cat->select_options("ORDER BY Full_Name");
|
||||
$cats = $cat->select({ FatherID => 0 })->fetchall_hashref;
|
||||
}
|
||||
else {
|
||||
$cats = $tree->children(id => $root, max_depth => ($root == 0 ? $CFG->{newsletter_max_depth} - 1 : $CFG->{newsletter_max_depth}), sort_col => 'Full_Name');
|
||||
}
|
||||
# Insert the root category as the first element of the list of categories
|
||||
splice @$cats, 0, 0, $root_cat;
|
||||
|
||||
my %catids;
|
||||
for (0 .. $#$cats) {
|
||||
my $c = $cats->[$_];
|
||||
# ID to $cats index mapping
|
||||
$catids{$c->{ID}}->{index} = $_;
|
||||
# List of children (only ones which are shown in the trimmed tree)
|
||||
$catids{$c->{ID}}->{children} = [];
|
||||
# Fix CatDepth to be relative to $root
|
||||
if ($_) {
|
||||
$c->{CatDepth} -= $root_cat->{CatDepth};
|
||||
}
|
||||
|
||||
# Keep track of categories which could have sub categories (that are past max_depth)
|
||||
if ($CFG->{newsletter_max_depth} > 0 and $c->{CatDepth} == $CFG->{newsletter_max_depth}) {
|
||||
$catids{$c->{ID}}->{check_child}++;
|
||||
}
|
||||
else {
|
||||
$c->{HasMoreChildren} = 0;
|
||||
}
|
||||
$c->{DirectSubscribers} = 0;
|
||||
}
|
||||
$cats->[0]->{CatDepth} = 0;
|
||||
|
||||
# Get a list of the root's parents (this is used twice below)
|
||||
my @root_parents = $root == 0 ? () : (0, @{$tree->parent_ids(id => $root)});
|
||||
|
||||
if (%catids) {
|
||||
# Calculate the number of direct subscribers for each category
|
||||
my %subscribers;
|
||||
$ns->select_options("GROUP BY CategoryID");
|
||||
my $sth = $ns->select('CategoryID', 'COUNT(*)', { CategoryID => [@root_parents, keys %catids] });
|
||||
while (my ($catid, $count) = $sth->fetchrow_array) {
|
||||
if (exists $catids{$catid}) {
|
||||
$cats->[$catids{$catid}->{index}]->{DirectSubscribers} = $count;
|
||||
}
|
||||
# Save the counts to calculate the total subscribers
|
||||
$subscribers{$catid} = $count;
|
||||
}
|
||||
|
||||
# Calculate the number of subscribers for each category (if a newsletter was
|
||||
# sent to this category, it would go to this many people)
|
||||
my $parents = $tree->parent_ids(id => [keys %catids]);
|
||||
for my $catid (keys %$parents) {
|
||||
for (@{$parents->{$catid}}, $catid) {
|
||||
$cats->[$catids{$catid}->{index}]->{Subscribers} += $subscribers{$_};
|
||||
}
|
||||
$cats->[$catids{$catid}->{index}]->{Subscribers} += $subscribers{0} if $catid;
|
||||
}
|
||||
|
||||
# Check to see which categories have sub categories
|
||||
my @check = grep $catids{$_}->{check_child}, keys %catids;
|
||||
if (@check) {
|
||||
my $subcats = $tree->child_ids(id => \@check);
|
||||
for (keys %$subcats) {
|
||||
$cats->[$catids{$_}->{index}]->{HasMoreChildren} = @{$subcats->{$_}};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my %previous = (PPID => '');
|
||||
if ($root != 0) {
|
||||
my $parent;
|
||||
if (@root_parents < $CFG->{newsletter_max_depth}) {
|
||||
$parent = $root_parents[0];
|
||||
}
|
||||
else {
|
||||
$parent = $root_parents[-$CFG->{newsletter_max_depth}];
|
||||
}
|
||||
|
||||
# Get the previous parent's info
|
||||
if ($parent == 0) {
|
||||
$parent = _root();
|
||||
}
|
||||
else {
|
||||
$parent = $cat->select({ ID => $parent })->fetchrow_hashref;
|
||||
}
|
||||
%previous = map { "PP" . $_ => $parent->{$_} } keys %$parent;
|
||||
}
|
||||
|
||||
return { %previous, category => $cats };
|
||||
}
|
||||
|
||||
sub subscriber_info {
|
||||
# ---------------------------------------------------
|
||||
# Returns information about the subscribers of a category.
|
||||
#
|
||||
my $catid = $IN->param('ID');
|
||||
my $direct = $IN->param('direct');
|
||||
my $cat = $DB->table('Category');
|
||||
my $nsu = $DB->table('NewsletterSubscription', 'Users');
|
||||
|
||||
if (not defined $catid or not ($catid == 0 or $cat->count({ ID => $catid }))) {
|
||||
return { error => 'Invalid ID' };
|
||||
}
|
||||
|
||||
my $tree = $cat->tree;
|
||||
my @parents = $direct || $catid == 0 ? ($catid) : (0, @{$tree->parent_ids(id => $catid)}, $catid);
|
||||
|
||||
$nsu->select_options("ORDER BY Username");
|
||||
my $subscribers = $nsu->select({ CategoryID => \@parents })->fetchall_hashref;
|
||||
|
||||
return { subscribers => $subscribers };
|
||||
}
|
||||
|
||||
sub subscription_info {
|
||||
# ---------------------------------------------------
|
||||
# Returns subscription information about a category.
|
||||
# 0 = not subscribed
|
||||
# 1 = indirectly subscribed (parent is subscribed)
|
||||
# 2 = directly subscribed
|
||||
#
|
||||
my $catid = $IN->param('ID') || shift;
|
||||
my $ns = $DB->table('NewsletterSubscription');
|
||||
my $tree = $DB->table('Category')->tree;
|
||||
|
||||
if ($ns->count({ UserID => $USER->{Username}, CategoryID => $catid })) {
|
||||
return { SubscriptionStatus => 2 };
|
||||
}
|
||||
|
||||
if ($catid == 0) {
|
||||
return { SubscriptionStatus => 0 };
|
||||
}
|
||||
|
||||
my @parents = (0, @{$tree->parent_ids(id => $catid)});
|
||||
my @pids = $ns->select('CategoryID', { UserID => $USER->{Username}, CategoryID => \@parents })->fetchall_list;
|
||||
if (@pids) {
|
||||
return { SubscriptionStatus => 1 };
|
||||
}
|
||||
|
||||
return { SubscriptionStatus => 0 };
|
||||
}
|
||||
|
||||
sub _root {
|
||||
# ---------------------------------------------------
|
||||
# Since there is no real root category, return what a select from the Category
|
||||
# table would return.
|
||||
#
|
||||
my $ns = $DB->table('NewsletterSubscription');
|
||||
return {
|
||||
ID => 0,
|
||||
Name => Links::language('NEWSLETTER_ROOTCAT'),
|
||||
CatDepth => 0,
|
||||
Full_Name => Links::language('NEWSLETTER_ROOTCAT'),
|
||||
Description => '',
|
||||
Subscribed => $USER->{Username} ? $ns->count({ UserID => $USER->{Username}, CategoryID => 0 }) : 0,
|
||||
};
|
||||
}
|
||||
|
||||
sub _subscribe {
|
||||
# ---------------------------------------------------
|
||||
# Subscribe to the categories passed in.
|
||||
#
|
||||
my @sub = @_;
|
||||
return 0 unless @sub;
|
||||
|
||||
my $cat = $DB->table('Category');
|
||||
my $ns = $DB->table('NewsletterSubscription');
|
||||
my $tree = $cat->tree;
|
||||
|
||||
# Already subscribed to root category
|
||||
if ($ns->count({ UserID => $USER->{Username}, CategoryID => 0 })) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@sub = sort { $a <=> $b } @sub;
|
||||
if ($sub[0] == 0) {
|
||||
@sub = (0);
|
||||
}
|
||||
else {
|
||||
# Filter out the invalid category ID's
|
||||
my @s = $cat->select('ID', { ID => \@sub })->fetchall_list;
|
||||
# Filter out categories which are already subscribed to by being a subcat
|
||||
@sub = ();
|
||||
my $parents = $tree->parent_ids(id => \@s);
|
||||
for (@s) {
|
||||
unless (@{$parents->{$_}} and $ns->count({ UserID => $USER->{Username}, CategoryID => $parents->{$_} })) {
|
||||
push @sub, $_;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0 unless @sub;
|
||||
|
||||
# Subscribing to the root, subscribes you to all, so remove any existing subscriptions.
|
||||
$ns->delete({ UserID => $USER->{Username} }) if $sub[0] == 0;
|
||||
|
||||
$ns->insert_multiple([qw/UserID CategoryID/], map { [$USER->{Username}, $_] } @sub);
|
||||
|
||||
# Remove any subscribed subcats of the ones we just added
|
||||
if ($sub[0] != 0) {
|
||||
my $c = $tree->child_ids(id => \@sub);
|
||||
my @subcats = map { @{$c->{$_}} } keys %$c;
|
||||
if (@subcats) {
|
||||
$ns->delete({ UserID => $USER->{Username}, CategoryID => \@subcats });
|
||||
}
|
||||
}
|
||||
|
||||
# FIXME need to take into account how many were deleted
|
||||
return scalar @sub;
|
||||
}
|
||||
|
||||
sub _unsubscribe {
|
||||
# ---------------------------------------------------
|
||||
# Unsubscribe from categories passed in. Returns the number of categories
|
||||
# unsubscribed from.
|
||||
#
|
||||
my @unsub = @_;
|
||||
return 0 unless @unsub;
|
||||
|
||||
return $DB->table('NewsletterSubscription')->delete({ UserID => $USER->{Username}, CategoryID => \@unsub });
|
||||
}
|
||||
|
||||
1;
|
||||
284
site/slowtwitch.com/cgi-bin/articles/admin/Links/Parallel.pm
Normal file
284
site/slowtwitch.com/cgi-bin/articles/admin/Links/Parallel.pm
Normal file
@@ -0,0 +1,284 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Parallel.pm,v 1.8 2005/03/05 01:29:09 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::Parallel;
|
||||
# ==================================================================
|
||||
# A way to get parallel work for ceartain tasks (not thread based).
|
||||
#
|
||||
use strict;
|
||||
|
||||
sub new {
|
||||
#------------------------------------------------------------
|
||||
# creats a new class, be sure to take a look at how it can
|
||||
# be configured
|
||||
#
|
||||
my $class = shift;
|
||||
my %p;
|
||||
|
||||
ref $_[0] ? (%p = %{$_[0]} ) : (%p = @_);
|
||||
|
||||
my $self = {};
|
||||
bless $self, $class;
|
||||
|
||||
$self->{max_workunit} = defined ( $p{max_workunit} ) ? $p{max_workunit} : 10;
|
||||
$self->{min_workunit} = defined ( $p{min_workunit} ) ? $p{min_workunit} : 3;
|
||||
$self->{max_children} = defined ( $p{max_children} ) ? $p{max_children} : 3;
|
||||
$self->{child_path} = defined ( $p{child_path} ) ? $p{child_path} : "./child.pl";
|
||||
$self->{path_to_perl} = defined ( $p{path_to_perl} ) ? $p{path_to_perl} : "/usr/local/bin/perl";
|
||||
$self->{max_children} = defined ( $p{max_children} ) ? $p{max_children} : 3;
|
||||
|
||||
$self->{spawn_delay} = defined ( $p{spawn_delay} ) ? $p{spawn_delay} : 2;
|
||||
|
||||
$self->{to_check} = defined ( $p{to_check} ) ? $p{to_check} : [];
|
||||
$self->{on_response} = defined ( $p{on_response} ) ? $p{on_response} : sub { };
|
||||
|
||||
# for statistics
|
||||
$self->{start_time} = 0;
|
||||
$self->{end_time} = 0;
|
||||
$self->{threads_spawned}= 0;
|
||||
|
||||
$self->{threads_stats} = {};
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub wait {
|
||||
#------------------------------------------------------------
|
||||
# the main loop that waits until the subset is checked.
|
||||
#
|
||||
my $self = shift;
|
||||
my $max_children = $self->{max_children};
|
||||
my @active_units = ();
|
||||
$self->{start_time} = time;
|
||||
my $temp = 0;
|
||||
|
||||
# while there's stuff to check
|
||||
while ( ( @{$self->{to_check}} ) or ( @active_units) ) {
|
||||
# create work units
|
||||
my $spawned = 0;
|
||||
while ( ( ($#active_units+1) < $max_children ) and ( @{$self->{to_check}} ) ) {
|
||||
# if we've already spawned a child, wait one so we don't
|
||||
# spike the load
|
||||
sleep $self->{spawn_delay} if $spawned;
|
||||
$spawned++;
|
||||
push @active_units, $self->new_work_unit ();
|
||||
};
|
||||
|
||||
# wait for any connections, blocking call.
|
||||
my $rin = fhbits ( \@active_units );
|
||||
select ( $rin, undef, undef, undef );
|
||||
|
||||
# find out who is has input
|
||||
my ( $i, $wild_protect );
|
||||
$i = 0; $wild_protect = 0;
|
||||
while ( $i <= $#active_units ) {
|
||||
|
||||
# if a unit requires attention, get input or kill it
|
||||
if ( $active_units[$i]->has_input () ) {
|
||||
$wild_protect++;
|
||||
my ( $id, $code, $message ) = $active_units[$i]->get_input ();
|
||||
if ( defined $id ) {
|
||||
&{$self->{on_response}}( $id, $code, $message );
|
||||
} else {
|
||||
my ( $unit_id, $start_time, $number_checked, $time_taken ) = $active_units[$i]->get_stats();
|
||||
${$self->{thread_stats}}{$unit_id} = [$start_time, $number_checked, $time_taken];
|
||||
$active_units[$i]->end_unit();
|
||||
# in case the child aborted abnormally, push the remaining
|
||||
# urls to be checked onto the stack
|
||||
push @{$self->{to_check}}, @{$active_units[$i]->{to_check}};
|
||||
splice ( @active_units, $i, 1 );
|
||||
next;
|
||||
};
|
||||
};
|
||||
$i++;
|
||||
};
|
||||
|
||||
# protect against wild looping
|
||||
$wild_protect || die "Error in verifier, looping wildly";
|
||||
};
|
||||
$self->{end_time} = time;
|
||||
}
|
||||
|
||||
sub get_stats {
|
||||
#------------------------------------------------------------
|
||||
# Return stats for the thread information.
|
||||
#
|
||||
my $self = shift;
|
||||
return [ $self->{threads_spawned}, $self->{end_time} - $self->{start_time}, $self->{thread_stats} ];
|
||||
}
|
||||
|
||||
sub new_work_unit {
|
||||
#------------------------------------------------------------
|
||||
# allocates a new work unit for the chilren
|
||||
# there are some optimization routines that should at some
|
||||
# point be implemented (for better allocation of
|
||||
# work units
|
||||
#
|
||||
my $self = shift;
|
||||
my $num_units = $#{$self->{to_check}}+1;
|
||||
my $max_children= $self->{max_children};
|
||||
my $unit_size = int ( $num_units / ( $max_children + 1 ) );
|
||||
|
||||
($unit_size > $self->{max_workunit}) and $unit_size = $self->{max_workunit};
|
||||
($unit_size < $self->{min_workunit}) and $unit_size = $self->{min_workunit};
|
||||
($unit_size > $num_units) and $unit_size = $num_units;
|
||||
|
||||
my @to_check = @{$self->{to_check}}[0..$unit_size-1];
|
||||
splice ( @{$self->{to_check}}, 0, $unit_size );
|
||||
|
||||
$self->{threads_spawned}++;
|
||||
|
||||
return Links::Parallel::WorkUnit->new ( $self->{path_to_perl}, $self->{child_path}, $self->{child_args}, \@to_check );
|
||||
}
|
||||
|
||||
sub fhbits {
|
||||
#------------------------------------------------------------
|
||||
# to set the fhandle bits for the impending select call
|
||||
#
|
||||
my ($work_units, $bits) = @_;
|
||||
defined $bits or ($bits = '');
|
||||
foreach (@$work_units) {
|
||||
vec($bits,$_->fno(),1) = 1;
|
||||
};
|
||||
return $bits;
|
||||
}
|
||||
|
||||
#####################################################
|
||||
package Links::Parallel::WorkUnit;
|
||||
|
||||
use FileHandle;
|
||||
use strict;
|
||||
my $clwork_units = 0;
|
||||
|
||||
sub new {
|
||||
#------------------------------------------------------------
|
||||
# creates a new work unit, starts up the child process
|
||||
# and encapsulats all the required data...,
|
||||
#
|
||||
my ($class, $perlpath, $child, $cmdline, $to_check, $verbosity) = @_;
|
||||
my $self = {};
|
||||
$self->{verbosity} = $verbosity || 1;
|
||||
($self->{istream}, $self->{pid}) = new_handle ( $perlpath, $child, $cmdline, $to_check );
|
||||
@{$self->{to_check}} = @$to_check;
|
||||
@{$self->{checked}} = ();
|
||||
$self->{unitid} = $clwork_units++;
|
||||
$self->{start_time} = time;
|
||||
$self->{number_checked} = 0;
|
||||
bless $self, $class;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub new_handle {
|
||||
#------------------------------------------------------------
|
||||
# the function that actually creates a new child process
|
||||
#
|
||||
my ($perlpath, $child, $cmdline, $to_check, $verbosity) = @_;
|
||||
$verbosity ||= 1;
|
||||
|
||||
my $newfh = new FileHandle;
|
||||
my $pid = 0;
|
||||
$cmdline ||= '';
|
||||
$, = "|";
|
||||
|
||||
if ($verbosity) {
|
||||
print "Launching new child ... ";
|
||||
}
|
||||
if (-e $child) {
|
||||
$pid = $newfh->open ( "$perlpath $child $cmdline @$to_check |" );
|
||||
if ((!$pid)or($?)) { die "Error launching child '$perlpath $child $cmdline'. Status: $?"; }
|
||||
} else {
|
||||
die "Child ($child) must exist";
|
||||
}
|
||||
print "ok ($pid)\n";
|
||||
return ( $newfh, $pid );
|
||||
}
|
||||
|
||||
sub fno {
|
||||
#------------------------------------------------------------
|
||||
# returns the file handle, useful when using the
|
||||
# "select" call
|
||||
#
|
||||
my $self = shift;
|
||||
return fileno ( $self->{istream} );
|
||||
}
|
||||
|
||||
sub has_input {
|
||||
#------------------------------------------------------------
|
||||
# returns whether or not this workunit has anything to
|
||||
# report to the parent
|
||||
#
|
||||
my $self = shift;
|
||||
my $rin = '';
|
||||
vec ( $rin, $self->fno(), 1 ) = 1;
|
||||
my $s = select ( $rin, undef, undef, 0 );
|
||||
return $s;
|
||||
}
|
||||
|
||||
sub get_input {
|
||||
#------------------------------------------------------------
|
||||
# process the local input.
|
||||
# this is only here because we want to make sure that
|
||||
# the work unit keeps track of it's own work pool
|
||||
# this frees the task administrator to do it's real
|
||||
# work and helps with crash recovery
|
||||
#
|
||||
my $self = shift;
|
||||
my $fh = $self->{istream};
|
||||
$fh || die "not defined!";
|
||||
my $str = <$fh>;
|
||||
if ( defined ( $str ) ) {
|
||||
chop $str;
|
||||
$str =~ /\s*([0-9]+)\t([-0-9]*)\t(.*)/;
|
||||
push @{$self->{checked}}, $1;
|
||||
splice @{$self->{to_check}}, 0, 1;
|
||||
$self->{number_checked}++;
|
||||
return ( $1, $2, $3 );
|
||||
} else {
|
||||
$self->end_unit ();
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
sub get_stats {
|
||||
#------------------------------------------------------------
|
||||
# Display statistic information.
|
||||
#
|
||||
my $self = shift;
|
||||
return ( $self->{unitid}, $self->{start_time}, $self->{number_checked}, time-$self->{start_time} );
|
||||
}
|
||||
|
||||
sub end_unit {
|
||||
#------------------------------------------------------------
|
||||
# prepares the WorkUnit for deallocation. Note how
|
||||
# there is a force -9 kill, without that, perl will wait
|
||||
# until the child finishes on it's own, which might be
|
||||
# soon, later or in a 100 years
|
||||
#
|
||||
my $self = shift;
|
||||
my $fh = $self->{istream};
|
||||
|
||||
kill 9, ( $self->{pid} );
|
||||
$self->{istream}->close ();
|
||||
}
|
||||
|
||||
sub DESTROY {
|
||||
#------------------------------------------------------------
|
||||
# deallocs the object
|
||||
# we want perl to force kill the child so we can ensure we exit
|
||||
# quickly
|
||||
my $self = shift;
|
||||
$self->end_unit;
|
||||
}
|
||||
|
||||
1;
|
||||
1663
site/slowtwitch.com/cgi-bin/articles/admin/Links/Payment.pm
Normal file
1663
site/slowtwitch.com/cgi-bin/articles/admin/Links/Payment.pm
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,165 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: AuthorizeDotNet.pm,v 1.3 2005/03/05 01:29:09 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2003 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
#
|
||||
# Glue between Gossamer Links and Authorize.Net payment interface
|
||||
|
||||
package Links::Payment::Direct::AuthorizeDotNet;
|
||||
use strict;
|
||||
|
||||
# Make sure the payment module is available
|
||||
use GT::Payment::Direct::AuthorizeDotNet;
|
||||
use Links qw/$IN $CFG $DB/;
|
||||
use vars qw/%INVALID %EMPTY/;
|
||||
|
||||
sub required {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Returns a list of required field names. Each field name will be looked for
|
||||
# in the language file, prefixed with 'PAYMENT_DIRECT_AuthorizeDotNet_', for
|
||||
# the title of the field, and 'PAYMENT_DIRECT_DESC_AuthorizeDotNet_' for a
|
||||
# description of the field's contents.
|
||||
# Note that these are just required SETUP fields, so things like credit card
|
||||
# number, billing name, etc. are NOT included.
|
||||
return
|
||||
account_username => { type => 'TEXT', valid => '^\w+$' }, # FIXME - I have no idea what this can be
|
||||
account_key => { type => 'TEXT', valid => '^\w+$' };
|
||||
|
||||
}
|
||||
|
||||
sub optional {
|
||||
# -----------------------------------------------------------------------------
|
||||
my @currencies;
|
||||
for (sort {
|
||||
$a eq 'USD' ? -1 : $b eq 'USD' ? 1 : $a eq 'CAD' ? -1 : $b eq 'CAD' ? 1 :
|
||||
$GT::Payment::Direct::AuthorizeDotNet::CURRENCY{$a} cmp
|
||||
$GT::Payment::Direct::AuthorizeDotNet::CURRENCY{$b}
|
||||
} keys %GT::Payment::Direct::AuthorizeDotNet::CURRENCY) {
|
||||
push @currencies, $_ => $GT::Payment::Direct::AuthorizeDotNet::CURRENCY{$_};
|
||||
}
|
||||
return
|
||||
currency => {
|
||||
type => 'SELECT',
|
||||
options => \@currencies
|
||||
},
|
||||
account_password => { type => 'TEXT', size => 40, valid => '.' }, # An optionally-required account password
|
||||
confirmation_merchant => { type => 'TEXT', size => 40, valid => '.@.' }, # A merchant confirmation e-mail address
|
||||
confirmation_confirm => { type => 'YESNO' }, # Whether or not to send a customer confirmation e-mail.
|
||||
test_mode => { type => 'YESNO' }
|
||||
}
|
||||
|
||||
sub payment_info {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Returns a hash of various parameters used to figure out how to display the
|
||||
# payment form for this payment method.
|
||||
return {
|
||||
no_cc_brand => 1,
|
||||
fields => [
|
||||
grep ! /^(?:account|capture|currency|test)/, keys %GT::Payment::Direct::AuthorizeDotNet::VALID
|
||||
],
|
||||
billing_phone_required => 1
|
||||
}
|
||||
}
|
||||
|
||||
sub verify {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Checks that $IN, combined with the saved admin settings, makes up all of the
|
||||
# required information. Returns 1 on success, or an array ref of invalid keys
|
||||
# on failure.
|
||||
_collect_data();
|
||||
if (keys %INVALID or keys %EMPTY) {
|
||||
my ($i, %order);
|
||||
for (@{$GT::Payment::Direct::AuthorizeDotNet::REQUIRED{AUTHORIZE}}) { $order{$_} = $i++ }
|
||||
return [ # Error
|
||||
[sort { ($order{$a} || 0x7fff_ffff) <=> ($order{$b} || 0x7fff_ffff) } keys %INVALID],
|
||||
[sort { ($order{$a} || 0x7fff_ffff) <=> ($order{$b} || 0x7fff_ffff) } keys %EMPTY]
|
||||
];
|
||||
}
|
||||
else {
|
||||
return 1; # Success
|
||||
}
|
||||
}
|
||||
|
||||
sub complete {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Checks that $IN, combined with the saved admin settings, makes up all of the
|
||||
# required information. Returns (1, $message) on success, (0, $reason) on
|
||||
# declined, or (-1, $errormsg) on error.
|
||||
|
||||
my $pay = _collect_data() or return;
|
||||
|
||||
# Set the admin-specified fields
|
||||
while (my ($k, $v) = each %{$CFG->{payment}->{direct}->{used}->{AuthorizeDotNet}}) {
|
||||
$pay->$k($v) or return (-1, "Payment configuration error (Invalid $k)");
|
||||
}
|
||||
|
||||
$pay->check('sale') or return (-1, $pay->error);
|
||||
my $ret = $pay->sale;
|
||||
if (not defined $ret) { # An error occured in the module
|
||||
return (-1, $pay->error);
|
||||
}
|
||||
else { # The request at least got through to Authorize.Net
|
||||
my $response = $pay->response;
|
||||
if ($ret == 1) { # Approved!
|
||||
my @receipt = @{$response->{receipt}};
|
||||
|
||||
my $receipt = "Transaction approved\n\n";
|
||||
while (@receipt) {
|
||||
my ($k, $v) = splice @receipt, 0, 2;
|
||||
$receipt .= "$k: $v\n";
|
||||
}
|
||||
|
||||
return (1, $response->{reason_text}, $receipt);
|
||||
}
|
||||
elsif ($ret == 0) { # Declined
|
||||
return (0, $response->{reason_text});
|
||||
}
|
||||
else { # An error was generated by Authorize.Net
|
||||
return (-1, $response->{reason_text});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub _collect_data {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Collect data from the payment data saved in the admin, and any valid columns
|
||||
# in $IN. Anything from $IN is checked for validity, and $INVALID{column} is
|
||||
# set if invalid.
|
||||
%INVALID = %EMPTY = ();
|
||||
return unless $CFG->{payment}->{direct}->{used}->{AuthorizeDotNet};
|
||||
my %data = %{$CFG->{payment}->{direct}->{used}->{AuthorizeDotNet}};
|
||||
my $pay = GT::Payment::Direct::AuthorizeDotNet->new();
|
||||
my %required = map { $_ => 1 } @{$GT::Payment::Direct::AuthorizeDotNet::REQUIRED{AUTHORIZE}};
|
||||
for my $field (keys %GT::Payment::Direct::AuthorizeDotNet::VALID) {
|
||||
# The account_*, capture_*, currency_*, etc. fields should not be user-settable.
|
||||
next if exists $data{$field} or $field =~ /^(?:account|capture|currency|test)/;
|
||||
if (my $value = $IN->param($field)) {
|
||||
if ($pay->$field($value)) {
|
||||
$data{$field} = $value;
|
||||
}
|
||||
else {
|
||||
$INVALID{$field}++;
|
||||
$data{$field} = undef;
|
||||
}
|
||||
}
|
||||
elsif ($required{$field}) {
|
||||
$EMPTY{$field}++;
|
||||
$data{$field} = undef;
|
||||
}
|
||||
}
|
||||
|
||||
$pay->billing_ip($ENV{REMOTE_ADDR}) if $ENV{REMOTE_ADDR} and $ENV{REMOTE_ADDR} ne '127.0.0.1';
|
||||
|
||||
return if keys %INVALID or keys %EMPTY;
|
||||
return $pay;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,152 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Moneris.pm,v 1.2 2005/03/05 01:29:09 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2003 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
#
|
||||
# Glue between Gossamer Links and Moneris payment interface
|
||||
|
||||
package Links::Payment::Direct::Moneris;
|
||||
use strict;
|
||||
|
||||
# Make sure the payment module is available
|
||||
use GT::Payment::Direct::Moneris 1.007; # CVS Versions < 1.7 were for the old, defunct Moneris payment system
|
||||
use Links qw/$IN $CFG $DB/;
|
||||
use vars qw/%INVALID %EMPTY/;
|
||||
|
||||
my @FIELDS = (
|
||||
keys %GT::Payment::Direct::Moneris::NAME_MAP,
|
||||
qw/ credit_card_number credit_card_expiry_month credit_card_expiry_year
|
||||
billing_country billing_email charge_total/
|
||||
);
|
||||
|
||||
sub required {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Returns a list of required field names. Each field name will be looked for
|
||||
# in the language file, prefixed with 'PAYMENT_DIRECT_Moneris_', for the title
|
||||
# of the field, and 'PAYMENT_DIRECT_DESC_Moneris_' for a description of the
|
||||
# field's contents.
|
||||
# Note that these are just required SETUP fields, so things like credit card
|
||||
# number, billing name, etc. are NOT included.
|
||||
return
|
||||
account_token => { type => 'TEXT', valid => '^\w+$' },
|
||||
account_token2 => { type => 'TEXT', valid => '^\w+$' };
|
||||
}
|
||||
|
||||
sub optional {
|
||||
return
|
||||
test_mode => { type => 'YESNO' }
|
||||
}
|
||||
|
||||
sub payment_info {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Returns a hash of various parameters used to figure out how to display the
|
||||
# payment form for this payment method.
|
||||
return {
|
||||
fields => [
|
||||
grep ! /^(?:account|capture|currency|test)/, @FIELDS
|
||||
],
|
||||
no_cc_brand => 1
|
||||
};
|
||||
}
|
||||
|
||||
sub verify {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Checks that $IN, combined with the saved admin settings, makes up all of the
|
||||
# required information. Returns 1 on success, or an array ref of invalid and
|
||||
# empty keys array references (i.e. [\@invalid, \@empty]) on failure.
|
||||
_collect_data();
|
||||
if (keys %INVALID or keys %EMPTY) {
|
||||
my ($i, %order);
|
||||
for (@{$GT::Payment::Direct::Moneris::REQUIRED{AUTHORIZE}}) { $order{$_} = $i++ }
|
||||
return [ # Error
|
||||
[sort { ($order{$a} || 0x7fff_ffff) <=> ($order{$b} || 0x7fff_ffff) } keys %INVALID],
|
||||
[sort { ($order{$a} || 0x7fff_ffff) <=> ($order{$b} || 0x7fff_ffff) } keys %EMPTY]
|
||||
];
|
||||
}
|
||||
else {
|
||||
return 1; # Success
|
||||
}
|
||||
}
|
||||
|
||||
sub complete {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Checks that $IN, combined with the saved admin settings, makes up all of the
|
||||
# required information. Returns (1, $message) on success, (0, $reason) on
|
||||
# declined, or (-1, $errormsg) on error.
|
||||
|
||||
my $pay = _collect_data() or return;
|
||||
|
||||
# Set the admin-specified fields
|
||||
while (my ($k, $v) = each %{$CFG->{payment}->{direct}->{used}->{Moneris}}) {
|
||||
$pay->$k($v) or return (-1, "Payment configuration error (Invalid $k)");
|
||||
}
|
||||
|
||||
$pay->check('sale') or return (-1, $pay->error);
|
||||
my $ret = $pay->sale;
|
||||
if (not defined $ret) { # An error occured in the module
|
||||
return (-1, $pay->error);
|
||||
}
|
||||
else { # The request at least got through to Moneris
|
||||
if ($ret == 1) { # Approved!
|
||||
my $resp_text;
|
||||
my @receipt = $pay->receipt();
|
||||
my $receipt = "Transaction approved\n\n";
|
||||
while (@receipt) {
|
||||
my ($k, $v) = splice @receipt, 0, 2;
|
||||
$receipt .= "$k: $v\n";
|
||||
$resp_text = $v if $k eq 'Status';
|
||||
}
|
||||
|
||||
return (1, $resp_text, $receipt);
|
||||
}
|
||||
elsif ($ret == 0) { # Declined
|
||||
return (0, $pay->error);
|
||||
}
|
||||
else { # An error was generated by Moneris
|
||||
return (-1, $pay->error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub _collect_data {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Collect data from the payment data saved in the admin, and any valid columns
|
||||
# in $IN. Anything from $IN is checked for validity, and $INVALID{column} is
|
||||
# set if invalid.
|
||||
%INVALID = %EMPTY = ();
|
||||
return unless $CFG->{payment}->{direct}->{used}->{Moneris};
|
||||
my %data = %{$CFG->{payment}->{direct}->{used}->{Moneris}};
|
||||
return unless keys %data;
|
||||
my $pay = GT::Payment::Direct::Moneris->new(debug_level => $CFG->{debug});
|
||||
my %required = map { $_ => 1 } @{$GT::Payment::Direct::Moneris::REQUIRED{AUTHORIZE}};
|
||||
for my $field (@FIELDS) {
|
||||
# The account_*, capture_*, currency_*, etc. fields should not be user-settable.
|
||||
next if exists $data{$field} or $field =~ /^(?:account|capture|currency|test)/;
|
||||
if (my $value = $IN->param($field)) {
|
||||
if ($pay->$field($value)) {
|
||||
$data{$field} = $value;
|
||||
}
|
||||
else {
|
||||
$INVALID{$field}++;
|
||||
$data{$field} = undef;
|
||||
}
|
||||
}
|
||||
elsif ($required{$field}) {
|
||||
$EMPTY{$field}++;
|
||||
$data{$field} = undef;
|
||||
}
|
||||
}
|
||||
|
||||
return if keys %INVALID or keys %EMPTY;
|
||||
return $pay;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,122 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: 2CheckOut.pm,v 1.13 2006/08/22 23:07:53 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2003 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
#
|
||||
# Glue between Gossamer Links and 2CheckOut payment interface
|
||||
|
||||
package Links::Payment::Remote::2CheckOut;
|
||||
use strict;
|
||||
|
||||
# Make sure the payment module is available
|
||||
use GT::Payment::Remote::2CheckOut;
|
||||
use Links qw/:objects/;
|
||||
use Links::Payment qw/:status :log/;
|
||||
use Links::SiteHTML;
|
||||
use vars qw/%INVALID %EMPTY/;
|
||||
|
||||
sub required {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Returns a list of required field names. Each field name will be looked for
|
||||
# in the language file, prefixed with 'PAYMENT_REMOTE_2CheckOut_', for the
|
||||
# title of the field, and 'PAYMENT_REMOTE_DESC_2CheckOut_' for a description of
|
||||
# the field's contents.
|
||||
# Note that these are just required SETUP fields, so things like credit card
|
||||
# number, billing name, etc. are NOT included.
|
||||
|
||||
return
|
||||
seller_id => { type => 'TEXT', valid => '^\d{1,10}$' },
|
||||
secret_word => { type => 'TEXT', valid => '^(?!tango$).+$' };
|
||||
}
|
||||
|
||||
sub optional {
|
||||
# -----------------------------------------------------------------------------
|
||||
return
|
||||
demo => { type => 'YESNO' };
|
||||
}
|
||||
|
||||
sub payment_info {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Returns a hashref of payment hints
|
||||
#
|
||||
my @fields = qw/seller_id secret_word demo/;
|
||||
my $ret = {
|
||||
fields => \@fields
|
||||
};
|
||||
if (my $info = $CFG->{payment}->{remote}->{used}->{'2CheckOut'}) {
|
||||
for (@fields) {
|
||||
$ret->{$_} = $info->{$_};
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
sub verify {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Checks that $IN, combined with the saved admin settings, makes up all of the
|
||||
# required information. Returns 1 on success, or an array ref of invalid keys
|
||||
# on failure. For Remote payment methods, this has no real effect.
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub postback {
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
my $pay = $DB->table('Payments');
|
||||
my $log = $DB->table('PaymentLogs');
|
||||
|
||||
my $unique = $IN->param('cart_order_id');
|
||||
my $payment = $pay->select({ payments_id => $unique })->fetchrow_hashref
|
||||
or return; # Whatever it is, we didn't create it.
|
||||
|
||||
GT::Payment::Remote::2CheckOut::process(
|
||||
param => $IN,
|
||||
sellerid => $CFG->{payment}->{remote}->{used}->{'2CheckOut'}->{seller_id},
|
||||
password => $CFG->{payment}->{remote}->{used}->{'2CheckOut'}->{secret_word},
|
||||
demo => $CFG->{payment}->{remote}->{used}->{'2CheckOut'}->{demo},
|
||||
on_valid => sub {
|
||||
return unless $IN->param('total') >= $payment->{payments_amount};
|
||||
|
||||
return if $payment->{payments_status} == COMPLETED;
|
||||
|
||||
my $cond = GT::SQL::Condition->new();
|
||||
$cond->add(paylogs_payments_id => '=' => $unique);
|
||||
$cond->add(paylogs_type => '=' => LOG_ACCEPTED);
|
||||
$cond->add(paylogs_text => LIKE => "%\n2CheckOut order number: " . $IN->param('order_number') . "%\n");
|
||||
my $found = $log->count($cond);
|
||||
return if $found;
|
||||
|
||||
$pay->update(
|
||||
{ payments_status => COMPLETED, payments_last => time },
|
||||
{ payments_id => $payment->{payments_id} }
|
||||
);
|
||||
|
||||
$log->insert({
|
||||
paylogs_payments_id => $payment->{payments_id},
|
||||
paylogs_type => LOG_ACCEPTED,
|
||||
paylogs_time => time,
|
||||
paylogs_text => (
|
||||
sprintf(Links::language('PAYMENT_REMOTE_APPROVED') => '2CheckOut') . "\n" .
|
||||
"2CheckOut order number: " . $IN->param('order_number') . "\n" .
|
||||
"Amount: $payment->{payments_amount}\n"
|
||||
)
|
||||
});
|
||||
|
||||
Links::Payment::process_payment($payment->{payments_linkid}, $payment->{payments_term});
|
||||
}
|
||||
);
|
||||
|
||||
print $IN->header;
|
||||
print Links::SiteHTML::display('payment_success');
|
||||
1;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,70 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Manual.pm,v 1.3 2005/03/05 01:46:06 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2003 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
#
|
||||
# Glue between Gossamer Links and Manual payment interface
|
||||
|
||||
package Links::Payment::Remote::Manual;
|
||||
use strict;
|
||||
|
||||
# Make sure the payment module is available
|
||||
use Links qw/:objects/;
|
||||
use Links::Payment qw/:status :log/;
|
||||
use Links::SiteHTML;
|
||||
use vars qw/%INVALID %EMPTY/;
|
||||
|
||||
sub required {
|
||||
# -----------------------------------------------------------------------------
|
||||
# No required parameters available
|
||||
return;
|
||||
}
|
||||
|
||||
sub optional {
|
||||
# -----------------------------------------------------------------------------
|
||||
# No optional parameters available.
|
||||
return;
|
||||
}
|
||||
|
||||
sub payment_info {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Returns a hashref of payment hints
|
||||
#
|
||||
return;
|
||||
}
|
||||
|
||||
sub insert_log {
|
||||
# -----------------------------------------------------------------------------
|
||||
#
|
||||
my $unique = shift;
|
||||
my $pay = $DB->table('Payments');
|
||||
my $log = $DB->table('PaymentLogs');
|
||||
my $payment = $pay->select({ payments_id => $unique })->fetchrow_hashref or return; # return if the payment doesn't exist.
|
||||
return if $payment->{payments_status} == COMPLETED;
|
||||
my $cond = GT::SQL::Condition->new(
|
||||
paylogs_payments_id => '=' => $unique,
|
||||
paylogs_type => '=' => LOG_ACCEPTED
|
||||
);
|
||||
my $found = $log->count($cond);
|
||||
return if $found;
|
||||
$log->insert({
|
||||
paylogs_payments_id => $payment->{payments_id},
|
||||
paylogs_type => LOG_MANUAL,
|
||||
paylogs_time => time,
|
||||
paylogs_text => (
|
||||
"This payment will be manually approved by admin.\n" .
|
||||
"Amount: $payment->{payments_amount}\n"
|
||||
)
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,296 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: PayPal.pm,v 1.16 2006/12/01 00:31:56 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2003 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
#
|
||||
# Glue between Gossamer Links and PayPal IPN payment interface
|
||||
|
||||
package Links::Payment::Remote::PayPal;
|
||||
use strict;
|
||||
|
||||
# Make sure the payment module is available
|
||||
use GT::Payment::Remote::PayPal;
|
||||
use Links qw/:objects/;
|
||||
use Links::Payment qw/:status :log/;
|
||||
use Links::SiteHTML;
|
||||
use vars qw/%INVALID %EMPTY/;
|
||||
|
||||
sub required {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Returns a list of required field names. Each field name will be looked for
|
||||
# in the language hash, prefixed with 'PAYMENT_REMOTE_PayPal_', for the title
|
||||
# of the field, and 'PAYMENT_REMOTE_DESC_PayPal_' for a description of the
|
||||
# field's contents.
|
||||
# Note that these are just required SETUP fields, so things like credit card
|
||||
# number, billing name, etc. are NOT included.
|
||||
my @currencies;
|
||||
for (qw/USD CAD AUD EUR GBP JPY NZD CHF HKD SGD SEK DKK PLN NOK HUF CZK/) {
|
||||
push @currencies, $_ => Links::language('PAYMENT_CURRENCY_' . $_);
|
||||
}
|
||||
|
||||
my @buttons;
|
||||
for (qw/23 cc 02 03 01 9 5 6/) {
|
||||
push @buttons, "x-click-but$_.gif" => qq|<img src="https://www.paypal.com/images/x-click-but$_.gif">|;
|
||||
}
|
||||
|
||||
my $custom = qq|Custom image:<br><input type="text" name="button_custom" size="60"|;
|
||||
if ($CFG->{payment}->{remote}->{used}->{PayPal} and $CFG->{payment}->{remote}->{used}->{PayPal}->{button_custom}) {
|
||||
$custom .= qq| value="$CFG->{payment}->{remote}->{used}->{PayPal}->{button_custom}"|;
|
||||
}
|
||||
$custom .= '>';
|
||||
|
||||
push @buttons, "custom" => $custom;
|
||||
|
||||
return
|
||||
business_email => { type => 'TEXT', valid => '.@[a-zA-Z0-9-]' },
|
||||
currency => {
|
||||
type => 'SELECT',
|
||||
options => \@currencies
|
||||
},
|
||||
button => {
|
||||
type => 'RADIO',
|
||||
options => \@buttons,
|
||||
custom => 1,
|
||||
valid => '^https?://[a-zA-Z0-9-]' # Only applies to the custom value
|
||||
}
|
||||
}
|
||||
|
||||
sub optional {
|
||||
# -----------------------------------------------------------------------------
|
||||
return
|
||||
image_url => { type => 'TEXT', size => 60, value => '^https?://[a-zA-Z0-9-]' },
|
||||
notify_url => { type => 'TEXT', size => '60', value => '^https?://[a-zA-Z0-9-]' },
|
||||
note => { type => 'TEXT', size => 30, value => '^.{1,30}$' },
|
||||
color => {
|
||||
type => 'SELECT',
|
||||
options => [
|
||||
white => Links::language('PAYMENT_REMOTE_PayPal_color_white'),
|
||||
black => Links::language('PAYMENT_REMOTE_PayPal_color_black')
|
||||
]
|
||||
},
|
||||
to_email => { type => 'TEXT', valid => '.@[a-zA-Z0-9-]' },
|
||||
sandbox => { type => 'YESNO' };
|
||||
}
|
||||
|
||||
sub payment_info {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Returns a hash of payment hints
|
||||
#
|
||||
my @fields = qw/business_email to_email currency button button_custom image_url notify_url note color sandbox/;
|
||||
my $ret = {
|
||||
fields => \@fields
|
||||
};
|
||||
if (my $pp = $CFG->{payment}->{remote}->{used}->{PayPal}) {
|
||||
for (@fields) {
|
||||
$ret->{$_ eq 'image_url' ? 'pp_image_url' : $_} = $pp->{$_};
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
sub verify {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Checks that $IN, combined with the saved admin settings, makes up all of the
|
||||
# required information. Returns 1 on success, or an array ref of invalid keys
|
||||
# on failure.
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub postback {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Handle PayPal postback
|
||||
my $unique = $IN->param('invoice');
|
||||
my $pay = $DB->table('Payments');
|
||||
my $log = $DB->table('PaymentLogs');
|
||||
my $payment = $pay->get($unique) or return;
|
||||
|
||||
GT::Payment::Remote::PayPal::process(
|
||||
param => $IN,
|
||||
sandbox => $CFG->{payment}->{remote}->{used}->{PayPal}->{sandbox},
|
||||
on_valid => sub {
|
||||
# If taxes or shipping was added, then mc_gross may be greater than payments_amount.
|
||||
if ($IN->param('mc_gross') < $payment->{payments_amount}) {
|
||||
$log->insert({
|
||||
paylogs_payments_id => $payment->{payments_id},
|
||||
paylogs_type => LOG_ERROR,
|
||||
paylogs_time => time,
|
||||
paylogs_text => "Invalid payment (payment amount is less than original charge): " .
|
||||
$IN->param('mc_gross') . " < " . $payment->{payments_amount}
|
||||
});
|
||||
return;
|
||||
}
|
||||
elsif ($IN->param('mc_currency') ne $CFG->{payment}->{remote}->{used}->{PayPal}->{currency}) {
|
||||
$log->insert({
|
||||
paylogs_payments_id => $payment->{payments_id},
|
||||
paylogs_type => LOG_ERROR,
|
||||
paylogs_time => time,
|
||||
paylogs_text => "Invalid payment (different currency): " .
|
||||
$IN->param('mc_currency') . " != " . $CFG->{payment}->{remote}->{used}->{PayPal}->{currency}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
return if $payment->{payments_status} == COMPLETED;
|
||||
|
||||
$pay->update(
|
||||
{ payments_status => COMPLETED, payments_last => time },
|
||||
{ payments_id => $payment->{payments_id} }
|
||||
);
|
||||
|
||||
$log->insert({
|
||||
paylogs_payments_id => $payment->{payments_id},
|
||||
paylogs_type => LOG_ACCEPTED,
|
||||
paylogs_time => time,
|
||||
paylogs_text => (
|
||||
sprintf(Links::language('PAYMENT_REMOTE_APPROVED') => 'PayPal') . "\n" .
|
||||
"Transaction ID: " . $IN->param('txn_id') . "\n" .
|
||||
"Amount: " . $IN->param('mc_currency') . " " . $IN->param('mc_gross') . " (Fee: "
|
||||
. $IN->param('mc_currency') . " " . $IN->param('mc_fee') . ")\n" .
|
||||
"Payer Email: " . $IN->param('payer_email') . "\n"
|
||||
)
|
||||
});
|
||||
|
||||
Links::Payment::process_payment($payment->{payments_linkid}, $payment->{payments_term});
|
||||
},
|
||||
on_pending => sub {
|
||||
$pay->update({ payments_last => time }, { payments_id => $unique });
|
||||
|
||||
my $match = Links::language('PAYLOG_PayPal_' . $IN->param('pending_reason'));
|
||||
my $str = $match ? Links::language('PAYLOG_PayPal_' . $IN->param('pending_reason')) : '';
|
||||
$log->insert({
|
||||
paylogs_payments_id => $payment->{payments_id},
|
||||
paylogs_type => LOG_INFO,
|
||||
paylogs_time => time,
|
||||
paylogs_text => (
|
||||
"Transaction ID: " . $IN->param('txn_id') . "\n" .
|
||||
"Pending: " . ($match ? $str : scalar $IN->param('pending_reason'))
|
||||
)
|
||||
});
|
||||
},
|
||||
on_refund => sub {
|
||||
$pay->update({ payments_last => time }, { payments_id => $unique });
|
||||
|
||||
$log->insert({
|
||||
paylogs_payments_id => $payment->{payments_id},
|
||||
paylogs_type => LOG_INFO,
|
||||
paylogs_time => time,
|
||||
paylogs_text => (
|
||||
sprintf(Links::language('PAYMENT_REMOTE_REFUND') => 'PayPal') . "\n" .
|
||||
"Transaction ID: " . $IN->param('txn_id') . "\n"
|
||||
)
|
||||
});
|
||||
},
|
||||
on_failed => sub {
|
||||
$pay->update(
|
||||
{ payments_status => DECLINED, payments_last => time },
|
||||
{ payments_id => $payment->{payments_id} }
|
||||
);
|
||||
|
||||
$log->insert({
|
||||
paylogs_payments_id => $payment->{payments_id},
|
||||
paylogs_type => LOG_DECLINED,
|
||||
paylogs_time => time,
|
||||
paylogs_text => "Transaction ID: " . $IN->param('txn_id')
|
||||
});
|
||||
},
|
||||
on_denied => sub {
|
||||
$pay->update(
|
||||
{ payments_status => DECLINED, payments_last => time },
|
||||
{ payments_id => $payment->{payments_id} }
|
||||
);
|
||||
|
||||
$log->insert({
|
||||
paylogs_payments_id => $payment->{payments_id},
|
||||
paylogs_type => LOG_DECLINED,
|
||||
paylogs_time => time,
|
||||
paylogs_text => "Transaction ID: " . $IN->param('txn_id')
|
||||
});
|
||||
},
|
||||
duplicate => sub {
|
||||
my $id = $IN->param('txn_id');
|
||||
my $cond = GT::SQL::Condition->new();
|
||||
$cond->add(paylogs_payments_id => '=' => $unique);
|
||||
$cond->add(paylogs_type => '=' => LOG_ACCEPTED);
|
||||
$cond->add(paylogs_text => LIKE => "%\nTransaction ID: $id\n%");
|
||||
my $found = $log->count($cond);
|
||||
return $found ? undef : 1; # True if everything checks out; undef if a duplicate was found
|
||||
},
|
||||
email => sub {
|
||||
my $email = shift;
|
||||
return lc $email eq lc $CFG->{payment}->{remote}->{used}->{PayPal}->{business_email}
|
||||
},
|
||||
on_error => sub {
|
||||
my $errmsg = shift;
|
||||
$pay->update(
|
||||
{ payments_status => ERROR, payments_last => time },
|
||||
{ payments_id => $payment->{payments_id} }
|
||||
);
|
||||
|
||||
$log->insert({
|
||||
paylogs_payments_id => $payment->{payments_id},
|
||||
paylogs_type => LOG_ERROR,
|
||||
paylogs_time => time,
|
||||
paylogs_text => $errmsg
|
||||
});
|
||||
},
|
||||
on_recurring => sub {
|
||||
if ($IN->param('mc_gross') < $payment->{payments_amount}) {
|
||||
$log->insert({
|
||||
paylogs_payments_id => $payment->{payments_id},
|
||||
paylogs_type => LOG_ERROR,
|
||||
paylogs_time => time,
|
||||
paylogs_text => "Invalid payment (payment amount is less than original charge): " .
|
||||
$IN->param('mc_gross') . " < " . $payment->{payments_amount}
|
||||
});
|
||||
return;
|
||||
}
|
||||
elsif ($IN->param('mc_currency') ne $CFG->{payment}->{remote}->{used}->{PayPal}->{currency}) {
|
||||
$log->insert({
|
||||
paylogs_payments_id => $payment->{payments_id},
|
||||
paylogs_type => LOG_ERROR,
|
||||
paylogs_time => time,
|
||||
paylogs_text => "Invalid payment (different currency): " .
|
||||
$IN->param('mc_currency') . " != " . $CFG->{payment}->{remote}->{used}->{PayPal}->{currency}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
$pay->update(
|
||||
{ payments_status => COMPLETED, payments_last => time },
|
||||
{ payments_id => $payment->{payments_id} }
|
||||
);
|
||||
|
||||
$log->insert({
|
||||
paylogs_payments_id => $payment->{payments_id},
|
||||
paylogs_type => LOG_ACCEPTED,
|
||||
paylogs_time => time,
|
||||
paylogs_text => (
|
||||
sprintf(Links::language('PAYMENT_REMOTE_APPROVED') => 'PayPal') . "\n" .
|
||||
"Transaction ID: " . $IN->param('txn_id') . "\n" .
|
||||
"Amount: " . $IN->param('mc_currency') . " " . $IN->param('mc_gross') . " (Fee: "
|
||||
. $IN->param('mc_currency') . " " . $IN->param('mc_fee') . ")\n" .
|
||||
"Payer Email: " . $IN->param('payer_email') . "\n" .
|
||||
"Subscription ID: " . $IN->param('subscr_id') . "\n"
|
||||
)
|
||||
});
|
||||
|
||||
Links::Payment::process_payment($payment->{payments_linkid}, $payment->{payments_term}, 1);
|
||||
}
|
||||
);
|
||||
|
||||
# There is no way to distinguish between PayPal sending the user back, and
|
||||
# PayPal posting the IPN, so we print a payment confirmation page.
|
||||
print $IN->header;
|
||||
print Links::SiteHTML::display('payment_success');
|
||||
1;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,207 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: WorldPay.pm,v 1.13 2006/08/22 23:05:13 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2003 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
#
|
||||
# Glue between Links and WorldPay payment interface
|
||||
|
||||
package Links::Payment::Remote::WorldPay;
|
||||
use strict;
|
||||
|
||||
# Make sure the payment module is available
|
||||
use GT::Payment::Remote::WorldPay;
|
||||
use Links qw/:objects/;
|
||||
use Links::Payment qw/:status :log/;
|
||||
use Links::SiteHTML;
|
||||
use vars qw/%INVALID %EMPTY/;
|
||||
|
||||
sub required {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Returns a list of required field names. Each field name will be looked for
|
||||
# in the language file, prefixed with 'PAYMENT_REMOTE_WorldPay_', for the title
|
||||
# of the field, and 'PAYMENT_REMOTE_DESC_WorldPay_' for a description of the
|
||||
# field's contents.
|
||||
# Note that these are just required SETUP fields, so things like credit card
|
||||
# number, billing name, etc. are NOT included.
|
||||
my @currencies;
|
||||
for (qw/USD CAD EUR GBP AFA ALL DZD AON ARS AWG AUD BSD BHD BDT BBD BZD BMD BOB BAD BWP BRL BND BGL XOF BIF KHR
|
||||
XAF CVE KYD CLP CNY COP KMF CRC HRK CUP CYP CZK DKK DJF XCD DOP TPE ECS EGP SVC EEK ETB FKP FJD XPF GMD GHC
|
||||
GIP GTQ GNF GWP GYD HTG HNL HKD HUF ISK INR IDR IRR IQD ILS JMD JPY JOD KZT KES KRW KPW KWD KGS LAK LVL LBP
|
||||
LSL LRD LYD LTL MOP MKD MGF MWK MYR MVR MTL MRO MUR MXN MNT MAD MZM MMK NAD NPR ANG NZD NIO NGN NOK OMR PKR
|
||||
PAB PGK PYG PEN PHP PLN QAR ROL RUR RWF WST STD SAR SCR SLL SGD SKK SIT SBD SOS ZAR LKR SHP SDP SRG SZL SEK
|
||||
CHF SYP TWD TJR TZS THB TOP TTD TND TRL UGX UAH AED UYU VUV VEB VND YER YUM ZRN ZMK ZWD/) {
|
||||
push @currencies, $_ => Links::language('PAYMENT_CURRENCY_' . $_);
|
||||
}
|
||||
|
||||
return
|
||||
installation_id => { type => 'TEXT', valid => '^\d{1,16}$' },
|
||||
callback_password => { type => 'TEXT' },
|
||||
md5_password => { type => 'TEXT' },
|
||||
currency => {
|
||||
type => 'SELECT',
|
||||
options => \@currencies
|
||||
}
|
||||
}
|
||||
|
||||
sub optional {
|
||||
# -----------------------------------------------------------------------------
|
||||
return
|
||||
test_mode => { type => 'SELECT', options => [100 => 'Test mode: Always approved', 101 => 'Test mode: Always declined'] }
|
||||
}
|
||||
|
||||
sub payment_info {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Returns a hashref of payment hints
|
||||
#
|
||||
my @fields = qw/currency installation_id md5_password test_mode/;
|
||||
my $ret = {
|
||||
fields => \@fields
|
||||
};
|
||||
if (my $pp = $CFG->{payment}->{remote}->{used}->{WorldPay}) {
|
||||
for (@fields) {
|
||||
$ret->{$_ eq 'image_url' ? 'pp_image_url' : $_} = $pp->{$_};
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
sub verify {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Checks that $IN, combined with the saved admin settings, makes up all of the
|
||||
# required information. Returns 1 on success, or an array ref of invalid keys
|
||||
# on failure. For Remote payment methods, this has no real effect.
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub postback {
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
my $pay = $DB->table('Payments');
|
||||
my $log = $DB->table('PaymentLogs');
|
||||
|
||||
my $unique = $IN->param('cartId');
|
||||
my $payment = $pay->select({ payments_id => $unique })->fetchrow_hashref
|
||||
or return; # Whatever it is, we didn't create it.
|
||||
|
||||
my $end = 1; # Returned after processing - if true, a blank page will be displayed,
|
||||
# if false, a worldpay receipt page.
|
||||
|
||||
GT::Payment::Remote::WorldPay::process(
|
||||
param => $IN,
|
||||
password => $CFG->{payment}->{remote}->{used}->{WorldPay}->{callback_password},
|
||||
test_mode => $CFG->{payment}->{remote}->{used}->{WorldPay}->{test_mode},
|
||||
on_valid => sub {
|
||||
# A one-time payment (or the initial payment, in the case of recurring payments)
|
||||
return unless $IN->param('amount') >= $payment->{payments_amount};
|
||||
|
||||
return if $payment->{payments_status} == COMPLETED;
|
||||
|
||||
$pay->update(
|
||||
{ payments_status => COMPLETED, payments_last => time },
|
||||
{ payments_id => $payment->{payments_id} }
|
||||
);
|
||||
|
||||
$log->insert({
|
||||
paylogs_payments_id => $payment->{payments_id},
|
||||
paylogs_type => LOG_ACCEPTED,
|
||||
paylogs_time => time,
|
||||
paylogs_text => (
|
||||
sprintf(Links::language('PAYMENT_REMOTE_APPROVED') => 'WorldPay') . "\n" .
|
||||
"Transaction ID: " . $IN->param('transId') . "\n" .
|
||||
"Amount: " . $IN->param('amountString') . " (" . $IN->param('authAmountString') . ")\n" .
|
||||
($IN->param('futurePayId') ? "FuturePay ID: " . $IN->param('futurePayId') . "\n" : '') .
|
||||
"Authorization Message: " . $IN->param('rawAuthMessage') . "\n"
|
||||
)
|
||||
});
|
||||
|
||||
Links::Payment::process_payment($payment->{payments_linkid}, $payment->{payments_term});
|
||||
|
||||
$end = 0;
|
||||
},
|
||||
on_cancel => sub {
|
||||
# The user clicked "cancel payment"
|
||||
$pay->update(
|
||||
{ payments_status => DECLINED, payments_last => time },
|
||||
{ payments_id => $payment->{payments_id} }
|
||||
);
|
||||
|
||||
$log->insert({
|
||||
paylogs_payments_id => $payment->{payments_id},
|
||||
paylogs_type => LOG_DECLINED,
|
||||
paylogs_time => time,
|
||||
paylogs_text => (
|
||||
sprintf(Links::language('PAYMENT_REMOTE_CANCELLED') => 'WorldPay') . "\n" .
|
||||
"Amount: " . $IN->param('amountString') . " (" . $IN->param('authAmountString') . ")\n"
|
||||
)
|
||||
});
|
||||
},
|
||||
on_invalid_password => sub {
|
||||
$pay->update(
|
||||
{ payments_status => ERROR, payments_last => time },
|
||||
{ payments_id => $payment->{payments_id} }
|
||||
);
|
||||
|
||||
$log->insert({
|
||||
paylogs_payments_id => $payment->{payments_id},
|
||||
paylogs_type => LOG_ERROR,
|
||||
paylogs_time => time,
|
||||
paylogs_text => sprintf(Links::language('PAYMENT_REMOTE_INVALIDPW') => 'WorldPay') . "\n"
|
||||
});
|
||||
},
|
||||
on_recurring => sub {
|
||||
# A recurring payment, NOT counting the original payment
|
||||
$pay->update(
|
||||
{ payments_status => COMPLETED, payments_last => time },
|
||||
{ payments_id => $payment->{payments_id} }
|
||||
);
|
||||
|
||||
$log->insert({
|
||||
paylogs_payments_id => $payment->{payments_id},
|
||||
paylogs_type => LOG_ACCEPTED,
|
||||
paylogs_time => time,
|
||||
paylogs_text => (
|
||||
sprintf(Links::language('PAYMENT_REMOTE_RECURRING_ACCEPTED') => 'WorldPay') . "\n" .
|
||||
"Transaction ID: " . $IN->param('transId') . "\n" .
|
||||
"Amount: " . $IN->param('amountString') . " (" . $IN->param('authAmountString') . ")\n" .
|
||||
"FuturePay ID: " . $IN->param('futurePayId') . "\n" .
|
||||
"Authorization Message: " . $IN->param('rawAuthMessage') . "\n"
|
||||
)
|
||||
});
|
||||
|
||||
# The "1" gives them an extra day for recurring payments.
|
||||
Links::Payment::process_payment($payment->{payments_linkid}, $payment->{payments_term}, 1);
|
||||
},
|
||||
on_recurring_failed => sub {
|
||||
$pay->update(
|
||||
{ payments_status => DECLINED, payments_last => time },
|
||||
{ payments_id => $payment->{payments_id} }
|
||||
);
|
||||
|
||||
$log->insert({
|
||||
paylogs_payments_id => $payment->{payments_id},
|
||||
paylogs_type => LOG_DECLINED,
|
||||
paylogs_time => time,
|
||||
paylogs_text => (
|
||||
sprintf(Links::language('PAYMENT_REMOTE_RECURRING_DECLINED') => 'WorldPay') . "\n" .
|
||||
"Amount: " . $IN->param('amountString') . " (" . $IN->param('authAmountString') . ")\n"
|
||||
)
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
print $IN->header;
|
||||
unless ($end) {
|
||||
print Links::SiteHTML::display('payment_success');
|
||||
}
|
||||
1;
|
||||
}
|
||||
|
||||
1;
|
||||
166
site/slowtwitch.com/cgi-bin/articles/admin/Links/Plugins.pm
Normal file
166
site/slowtwitch.com/cgi-bin/articles/admin/Links/Plugins.pm
Normal file
@@ -0,0 +1,166 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Plugins.pm,v 1.48 2005/04/14 01:08:49 jagerman Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::Plugins;
|
||||
# ==================================================================
|
||||
use strict;
|
||||
use Links qw/$IN $CFG/;
|
||||
|
||||
# ------------------------------------------------------------------------------------------------- #
|
||||
# Plugin config #
|
||||
# ------------------------------------------------------------------------------------------------- #
|
||||
|
||||
sub get_plugin_user_cfg {
|
||||
# --------------------------------------------------------------
|
||||
# Returns the user config hash for a given plugin.
|
||||
#
|
||||
my $class = ($_[0] eq 'Links::Plugins') ? shift : '';
|
||||
my $plugin_name = shift || return;
|
||||
my $cfg = GT::Plugins->load_cfg ( $CFG->{admin_root_path} . '/Plugins' );
|
||||
exists $cfg->{$plugin_name} or return {};
|
||||
(ref $cfg->{$plugin_name}->{user} eq 'ARRAY') or return {};
|
||||
|
||||
my $opts = {};
|
||||
foreach my $opt (@{$cfg->{$plugin_name}->{user}}) {
|
||||
$opts->{$opt->[0]} = $opt->[1];
|
||||
}
|
||||
return $opts;
|
||||
}
|
||||
|
||||
sub set_plugin_user_cfg {
|
||||
# --------------------------------------------------------------
|
||||
# Takes a plugin name and config hash and saves it.
|
||||
#
|
||||
my $class = ($_[0] eq 'Links::Plugins') ? shift : '';
|
||||
my $plugin_name = shift || return;
|
||||
my $hash = shift || return;
|
||||
|
||||
my $cfg = GT::Plugins->load_cfg ( $CFG->{admin_root_path} . '/Plugins' );
|
||||
exists $cfg->{$plugin_name} or return;
|
||||
(ref $cfg->{$plugin_name}->{user} eq 'ARRAY') or return {};
|
||||
|
||||
foreach my $opt (@{$cfg->{$plugin_name}->{user}}) {
|
||||
$opt->[1] = $hash->{$opt->[0]};
|
||||
}
|
||||
return GT::Plugins->save_cfg ( $CFG->{admin_root_path} . '/Plugins', $cfg );
|
||||
}
|
||||
|
||||
sub get_plugin_registry {
|
||||
# --------------------------------------------------------------
|
||||
# Returns the user config hash for a given plugin.
|
||||
#
|
||||
my $class = ($_[0] eq 'Links::Plugins') ? shift : '';
|
||||
my $plugin_name = shift || return;
|
||||
my $cfg = GT::Plugins->load_cfg ( $CFG->{admin_root_path} . '/Plugins' );
|
||||
exists $cfg->{$plugin_name} or return {};
|
||||
|
||||
return ( $cfg->{$plugin_name}->{registry} || {} );
|
||||
}
|
||||
|
||||
sub set_plugin_registry {
|
||||
# --------------------------------------------------------------
|
||||
# Takes a plugin name and config hash and saves it.
|
||||
#
|
||||
my $class = ($_[0] eq 'Links::Plugins') ? shift : '';
|
||||
my $plugin_name = shift || return;
|
||||
my $hash = shift || return;
|
||||
|
||||
my $cfg = GT::Plugins->load_cfg ( $CFG->{admin_root_path} . '/Plugins' );
|
||||
|
||||
exists $cfg->{$plugin_name} or return;
|
||||
|
||||
my $registry = ( $cfg->{$plugin_name}->{registry} ||= {} );
|
||||
foreach my $opt ( keys %{$hash} ) {
|
||||
$registry->{$opt} = $hash->{$opt};
|
||||
}
|
||||
|
||||
return GT::Plugins->save_cfg ( $CFG->{admin_root_path} . '/Plugins', $cfg );
|
||||
}
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------------------------- #
|
||||
# Displaying #
|
||||
# ------------------------------------------------------------------------------------------------- #
|
||||
|
||||
sub admin_menu {
|
||||
# -----------------------------------------------------------------
|
||||
# Displays the admin menu with the plugin options shown.
|
||||
#
|
||||
require GT::Plugins::Manager;
|
||||
my $man = new GT::Plugins::Manager (
|
||||
cgi => $IN,
|
||||
tpl_root => $CFG->{admin_root_path} . "/templates/admin",
|
||||
plugin_dir => $CFG->{admin_root_path} . "/Plugins",
|
||||
prog_name => 'lsql',
|
||||
prog_ver => $CFG->{version},
|
||||
prog_reg => $CFG->{reg_number},
|
||||
base_url => 'admin.cgi?do=page&page=plugin_manager.html',
|
||||
path_to_perl => $CFG->{path_to_perl},
|
||||
perl_args => "-cw -I$CFG->{admin_root_path}"
|
||||
);
|
||||
return $man->admin_menu;
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------------------------- #
|
||||
# Wizard #
|
||||
# ------------------------------------------------------------------------------------------------- #
|
||||
|
||||
sub wizard {
|
||||
# -------------------------------------------------------------------
|
||||
# Manages the plugin wizard, basically just creates a wizard object,
|
||||
# and returns the output. Real work is done in GT::Plugins::Wizard.
|
||||
#
|
||||
require GT::Plugins::Wizard;
|
||||
my $wiz = GT::Plugins::Wizard->new(
|
||||
cgi => $IN,
|
||||
tpl_root => $CFG->{admin_root_path} . "/templates/admin",
|
||||
plugin_dir => $CFG->{admin_root_path} . "/Plugins",
|
||||
prog_ver => $CFG->{version},
|
||||
install_header => 'use Links qw/:objects/;',
|
||||
initial_indent => '',
|
||||
dirs => {
|
||||
user_cgi => '$CFG->{admin_root_path}/..',
|
||||
admin_cgi => '$CFG->{admin_root_path}'
|
||||
},
|
||||
oo => '$PLG'
|
||||
);
|
||||
return $wiz->process;
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------------------------- #
|
||||
# Manager #
|
||||
# ------------------------------------------------------------------------------------------------- #
|
||||
|
||||
sub manager {
|
||||
# -------------------------------------------------------------------
|
||||
# Manages the plugin installer, basically just creates an installerobject,
|
||||
# and returns the output. Real work is done in GT::Plugins::Installer
|
||||
#
|
||||
|
||||
require GT::Plugins::Manager;
|
||||
my $man = GT::Plugins::Manager->new(
|
||||
cgi => $IN,
|
||||
tpl_root => $CFG->{admin_root_path} . "/templates/admin",
|
||||
plugin_dir => $CFG->{admin_root_path} . "/Plugins",
|
||||
prog_name => 'lsql',
|
||||
prog_ver => $CFG->{version},
|
||||
prog_init => $CFG->{admin_root_path},
|
||||
prog_reg => $CFG->{reg_number},
|
||||
base_url => 'admin.cgi?do=page&page=plugin_manager.html',
|
||||
path_to_perl => $CFG->{path_to_perl},
|
||||
perl_args => "-cw -I$CFG->{admin_root_path}"
|
||||
) or return "Error loading plugin manager: $GT::Plugins::error";
|
||||
return $man->process;
|
||||
}
|
||||
|
||||
1;
|
||||
586
site/slowtwitch.com/cgi-bin/articles/admin/Links/SQL.pm
Normal file
586
site/slowtwitch.com/cgi-bin/articles/admin/Links/SQL.pm
Normal file
@@ -0,0 +1,586 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: SQL.pm,v 1.141 2007/11/16 07:15:00 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
# Contains the default table structure for Gossamer Links tables.
|
||||
package Links::SQL;
|
||||
|
||||
use strict;
|
||||
use vars qw/@TABLES/;
|
||||
use Links qw/:payment $DB/;
|
||||
|
||||
@TABLES = qw(
|
||||
Users Links Changes Category CatPrice Reviews CatLinks CatRelations
|
||||
Editors Verify Sessions EmailTemplates EmailMailings MailingIndex
|
||||
MailingList MailingListIndex ClickTrack Payments PaymentLogs
|
||||
Bookmark_Folders Bookmark_Links SearchLogs NewsletterSubscription
|
||||
);
|
||||
|
||||
sub tables {
|
||||
# ------------------------------------------------------------------
|
||||
# Defines the SQL tables.
|
||||
#
|
||||
my $action = shift || 'warn';
|
||||
my $output = '';
|
||||
|
||||
my $ok = Links::language('dialog_ok');
|
||||
|
||||
# --------- Users Table ----------------
|
||||
create_table(\$output, 'Users', $action,
|
||||
cols => [
|
||||
Username => { type => 'CHAR', size => 50, not_null => 1, form_display => Links::language('prompt_Username') },
|
||||
Password => { type => 'CHAR', binary => 1, size => 25, not_null => 1, form_display => Links::language('prompt_Password') },
|
||||
Email => { type => 'CHAR', size => 75, not_null => 1, regex => '^(?:.+\@.+\..+|\s*)$', form_display => Links::language('prompt_Email') },
|
||||
Name => { type => 'CHAR', size => 75, form_display => Links::language('prompt_Name') },
|
||||
Validation => { type => 'CHAR', size => 20, , form_display => Links::language('prompt_Validation') },
|
||||
Status => { type => 'ENUM', values => ['Not Validated', 'Registered', 'Administrator'], not_null => 1, default => 'Registered', form_display => Links::language('prompt_Status') },
|
||||
ReceiveMail => { type => 'ENUM', values => ['No', 'Yes'], not_null => 1, default => 'Yes', form_display => Links::language('prompt_ReceiveMail') },
|
||||
SortField => { type => 'VARCHAR', size => 255, not_null => 1, regex => '^[\s\w]+$', default => 'Title', form_display => Links::language('prompt_SortField') },
|
||||
SortOrd => { type => 'ENUM', values => ['ASC', 'DESC'], not_null => 1, default => 'ASC', form_display => Links::language('prompt_SortOrd') },
|
||||
PerPage => { type => 'INT', not_null => 1, unsigned => 1, default => 15, form_display => Links::language('prompt_PerPage') },
|
||||
Grouping => { type => 'TINYINT', not_null => 1, unsigned => 1, default => 0, form_display => Links::language('prompt_Grouping') },
|
||||
],
|
||||
index => {
|
||||
emailndx => ['Email']
|
||||
},
|
||||
pk => 'Username',
|
||||
subclass => {
|
||||
table => { Users => 'Links::Table::Users' },
|
||||
html => { Users => 'Links::HTML::Users' }
|
||||
}
|
||||
);
|
||||
|
||||
# --------- Links Table ----------------
|
||||
create_table(\$output, 'Links', $action,
|
||||
cols => [
|
||||
ID => { type => 'INT', not_null => 1, unsigned => 1, regex => '^\d+$', form_display => Links::language('prompt_ID') },
|
||||
Title => { type => 'CHAR', size => 100, not_null => 1, weight => 3, form_display => Links::language('prompt_Title') },
|
||||
URL => { type => 'CHAR', size => 255, not_null => 1, weight => 1, default => 'http://', regex => '^\w+:', form_display => Links::language('prompt_URL') },
|
||||
LinkOwner => { type => 'CHAR', size => 50, not_null => 1, default => 'admin', form_display => Links::language('prompt_LinkOwner') },
|
||||
Add_Date => { type => 'DATE', not_null => 1, form_display => Links::language('prompt_Add_Date') },
|
||||
Mod_Date => { type => 'DATE', not_null => 1, form_display => Links::language('prompt_Mod_Date') },
|
||||
Description => { type => 'TEXT', weight => 1, form_display => Links::language('prompt_Description') },
|
||||
Contact_Name => { type => 'CHAR', size => 255, form_display => Links::language('prompt_Contact_Name') },
|
||||
Contact_Email => { type => 'CHAR', size => 255, form_display => Links::language('prompt_Contact_Email') },
|
||||
Hits => { type => 'INT', not_null => 1, default => 0, regex => '^\d+$', form_display => Links::language('prompt_Hits') },
|
||||
isNew => { type => 'ENUM', values => ['No', 'Yes'], not_null => 1, default => 'No', form_display => Links::language('prompt_isNew') },
|
||||
isChanged => { type => 'ENUM', values => ['No', 'Yes'], not_null => 1, default => 'No', form_display => Links::language('prompt_isChanged') },
|
||||
isPopular => { type => 'ENUM', values => ['No', 'Yes'], not_null => 1, default => 'No', form_display => Links::language('prompt_isPopular') },
|
||||
isValidated => { type => 'ENUM', values => ['No', 'Yes'], not_null => 1, default => 'Yes', form_display => Links::language('prompt_isValidated') },
|
||||
Rating => { type => 'DECIMAL', precision => 4, scale => 2, not_null => 1, default => 0, regex => '^(?:10(?:\.0*)?|\d(?:\.\d*)?)$', form_display => Links::language('prompt_Rating') },
|
||||
Votes => { type => 'SMALLINT', unsigned => 1, not_null => 1, default => 0, regex => '^\d+$', form_display => Links::language('prompt_Votes') },
|
||||
Status => { type => 'SMALLINT', not_null => 1, default => 0, regex => '^-?\d+$', form_display => Links::language('prompt_Status') },
|
||||
Date_Checked => { type => 'DATETIME', form_display => Links::language('prompt_Date_Checked') },
|
||||
Timestmp => { type => 'TIMESTAMP', time_check => 1, form_display => Links::language('prompt_Timestmp') },
|
||||
ExpiryDate => { type => 'INT', not_null => 1, default => FREE, form_display => Links::language('prompt_ExpiryDate'), form_size => 35 }, # See FREE, UNPAID & UNLIMITED constants in Links.pm
|
||||
ExpiryCounted => { type => 'TINYINT', not_null => 1, default => 0, form_display => Links::language('prompt_ExpiryCounted'), form_type => 'hidden' },
|
||||
ExpiryNotify => { type => 'TINYINT', not_null => 1, default => 0, form_display => Links::language('prompt_ExpiryNotify'), form_type => 'hidden' },
|
||||
LinkExpired => { type => 'INT', form_display => Links::language('prompt_LinkExpired'), form_type => 'hidden' },
|
||||
],
|
||||
pk => 'ID',
|
||||
ai => 'ID',
|
||||
fk => {
|
||||
Users => { LinkOwner => 'Username' }
|
||||
},
|
||||
index => {
|
||||
urlndx => ['URL'],
|
||||
stndx => ['Status'],
|
||||
valexpndx => [qw/isValidated ExpiryDate/],
|
||||
newndx => ['isNew'],
|
||||
popndx => ['isPopular'],
|
||||
userndx => ['LinkOwner'],
|
||||
expiryndx => [qw/ExpiryDate ExpiryNotify/],
|
||||
expcntndx => [qw/ExpiryCounted ExpiryDate/]
|
||||
},
|
||||
subclass => {
|
||||
table => { Links => 'Links::Table::Links' },
|
||||
html => { Links => 'Links::HTML::Links' }
|
||||
}
|
||||
);
|
||||
|
||||
# --------- Changes Table ----------------
|
||||
create_table(\$output, 'Changes', $action,
|
||||
cols => [
|
||||
LinkID => { type => 'INT', not_null => 1, unsigned => 1, regex => '^\d+$' },
|
||||
Username => { type => 'CHAR', size => 50, not_null => 1, default => 'admin' },
|
||||
ChgRequest => { type => 'TEXT' },
|
||||
Timestmp => { type => 'TIMESTAMP' }
|
||||
],
|
||||
fk => {
|
||||
Links => { LinkID => 'ID' },
|
||||
Users => { Username => 'Username' }
|
||||
}
|
||||
);
|
||||
|
||||
# --------- Category Table ----------------
|
||||
my $new_category = create_table(\$output, 'Category', $action,
|
||||
cols => [
|
||||
ID => { type => 'INT', not_null => 1, unsigned => 1, form_display => Links::language('prompt_ID') },
|
||||
Name => { type => 'CHAR', size => 255, not_null => 1, weight => 3, regex => '^[^/]+$', form_display => Links::language('prompt_Name') },
|
||||
FatherID => { type => 'INT', not_null => 1, unsigned => 1, default => 0, form_size => 1, form_display => Links::language('prompt_FatherID') },
|
||||
CatRoot => { type => 'INT', not_null => 1, unsigned => 1, default => 0, form_type => 'hidden' },
|
||||
CatDepth => { type => 'INT', not_null => 1, unsigned => 1, default => 0, form_type => 'hidden' },
|
||||
Full_Name => { type => 'CHAR', size => 255, form_display => Links::language('prompt_Full_Name') },
|
||||
Description => { type => 'TEXT', weight => 1, form_display => Links::language('prompt_Description') },
|
||||
Meta_Description => { type => 'TEXT', form_display => Links::language('prompt_Meta_Description') },
|
||||
Meta_Keywords => { type => 'TEXT', form_display => Links::language('prompt_Meta_Keywords') },
|
||||
Header => { type => 'TEXT', form_display => Links::language('prompt_Header') },
|
||||
Footer => { type => 'TEXT', form_display => Links::language('prompt_Footer') },
|
||||
Category_Template => { type => 'CHAR', size => 20, form_display => Links::language('prompt_Category_Template') },
|
||||
Number_of_Links => { type => 'INT', not_null => 1, default => 0, form_display => Links::language('prompt_Number_of_Links') },
|
||||
Direct_Links => { type => 'INT', not_null => 1, default => 0, form_display => Links::language('prompt_Direct_Links') },
|
||||
Has_New_Links => { type => 'ENUM', values => ['No', 'Yes'], not_null => 1, default => 'No', form_display => Links::language('prompt_Has_New_Links') },
|
||||
Has_Changed_Links => { type => 'ENUM', values => ['No', 'Yes'], not_null => 1, default => 'No', form_display => Links::language('prompt_Has_Changed_Links') },
|
||||
Newest_Link => { type => 'DATE', form_display => Links::language('prompt_Newest_Link') },
|
||||
Timestmp => { type => 'TIMESTAMP', time_check => 1, form_display => Links::language('prompt_Timestmp') },
|
||||
Payment_Mode => { type => 'TINYINT', not_null => 1, default => 0, form_size => 1, form_names => [GLOBAL,NOT_ACCEPTED,OPTIONAL,REQUIRED], form_values => ['Use global settings','Not accepted','Optional','Required'], form_type => 'SELECT', form_display => Links::language('prompt_Payment_Mode') },
|
||||
Payment_Description => { type => 'TEXT', form_display => Links::language('prompt_Payment_Description') },
|
||||
],
|
||||
subclass => {
|
||||
table => { Category => 'Links::Table::Category' },
|
||||
html => { Category => 'Links::HTML::Category' }
|
||||
},
|
||||
pk => 'ID',
|
||||
ai => 'ID',
|
||||
index => {
|
||||
catndx => ['Name'],
|
||||
namndx => ['Full_Name'],
|
||||
fthrindex => ['FatherID'],
|
||||
rootndx => ['CatRoot'],
|
||||
c_p => ['Payment_Mode'],
|
||||
}
|
||||
);
|
||||
|
||||
# --------- Category Tree -------------------------
|
||||
$output .= "Creating Category tree... ";
|
||||
my $e = $DB->editor('Category');
|
||||
if ($e->add_tree(father => "FatherID", root => "CatRoot", depth => "CatDepth", force => ($new_category ? 'force' : 'check'))) {
|
||||
$output .= "okay\n";
|
||||
}
|
||||
else {
|
||||
$output .= "failed ($GT::SQL::error)\n";
|
||||
}
|
||||
|
||||
# --------- CatPrice Table ----------------
|
||||
create_table(\$output, 'CatPrice', $action,
|
||||
cols => [
|
||||
cp_id => { type => 'INT', not_null => 1, unsigned => 1 },
|
||||
cp_cat_id_fk => { type => 'INT', not_null => 1, unsigned => 1 },
|
||||
cp_term => { type => 'CHAR', not_null => 1, size => 10 }, # e.g. 8d, 1m, 2y, 3w, unlimited, etc.
|
||||
cp_cost => { type => 'DOUBLE', not_null => 1 },
|
||||
cp_type => { type => 'TINYINT', not_null => 1, unsigned => 1 }, # 0 = signup, 1 = renewal, 2 = recurring
|
||||
cp_description => { type => 'TEXT' }
|
||||
],
|
||||
pk => 'cp_id',
|
||||
ai => 'cp_id',
|
||||
fk => {
|
||||
Category => { cp_cat_id_fk => 'ID' }
|
||||
}
|
||||
);
|
||||
|
||||
# --------- Reviews Table ----------------
|
||||
create_table(\$output, 'Reviews', $action,
|
||||
cols => [
|
||||
ReviewID => { type => 'INT', not_null => 1, unsigned => 1, form_display => Links::language('prompt_ReviewID') },
|
||||
Review_LinkID => { type => 'INT', not_null => 1, unsigned => 1, regex => '^\d+$', form_display => Links::language('prompt_Review_LinkID') },
|
||||
Review_Owner => { type => 'CHAR', size => 50, not_null => 1, form_display => Links::language('prompt_Review_Owner') },
|
||||
Review_Rating => { type => 'SMALLINT', unsigned => 1, not_null => 1, default => 0, regex => '^\d+$', form_display => Links::language('prompt_Review_Rating') },
|
||||
Review_Date => { type => 'DATETIME', not_null => 1, form_display => Links::language('prompt_Review_Date') },
|
||||
Review_ModifyDate => { type => 'DATETIME', not_null => 1, form_display => Links::language('prompt_Review_ModifyDate') },
|
||||
Review_Subject => { type => 'CHAR', size => 100, not_null => 1, form_display => Links::language('prompt_Review_Subject') },
|
||||
Review_Contents => { type => 'TEXT', not_null => 1, form_display => Links::language('prompt_Review_Contents') },
|
||||
Review_ByLine => { type => 'CHAR', size => 50, form_display => Links::language('prompt_Review_ByLine') },
|
||||
Review_WasHelpful => { type => 'INT', unsigned => 1, regex => '^\d+$', form_display => Links::language('prompt_Review_WasHelpful') },
|
||||
Review_WasNotHelpful => { type => 'INT', unsigned => 1, regex => '^\d+$', form_display => Links::language('prompt_Review_WasNotHelpful') },
|
||||
Review_Validated => { type => 'ENUM', values => ['No', 'Yes'], not_null => 1, default => 'No', form_display => Links::language('prompt_Review_Validated') },
|
||||
Review_GuestName => { type => 'CHAR', size => 75, form_display => Links::language('prompt_Review_GuestName') },
|
||||
Review_GuestEmail => { type => 'CHAR', size => 75, regex => '^(?:(?:.+\@.+\..+)|\s*)$', form_display => Links::language('prompt_Review_GuestEmail') },
|
||||
],
|
||||
pk => 'ReviewID',
|
||||
ai => 'ReviewID',
|
||||
subclass => {
|
||||
table => { Reviews => 'Links::Table::Reviews' }
|
||||
},
|
||||
index => {
|
||||
rownerndx => ['Review_Owner'],
|
||||
rdatendx => ['Review_Date'],
|
||||
rlinkndx => ['Review_LinkID']
|
||||
},
|
||||
fk => {
|
||||
Links => { Review_LinkID => 'ID' },
|
||||
Users => { Review_Owner => 'Username' }
|
||||
}
|
||||
);
|
||||
|
||||
# --------- CatLinks Table ----------------
|
||||
create_table(\$output, 'CatLinks', $action,
|
||||
cols => [
|
||||
LinkID => { type => 'INT', not_null => 1, unsigned => 1 },
|
||||
CategoryID => { type => 'INT', not_null => 1, unsigned => 1 }
|
||||
],
|
||||
subclass => {
|
||||
table => { CatLinks => 'Links::Table::CatLinks' }
|
||||
},
|
||||
index => {
|
||||
lndx => ['LinkID']
|
||||
},
|
||||
unique => {
|
||||
cl_cl_q => [qw/CategoryID LinkID/]
|
||||
},
|
||||
fk => {
|
||||
Links => { LinkID => 'ID' },
|
||||
Category => { CategoryID => 'ID' }
|
||||
}
|
||||
);
|
||||
|
||||
# --------- CatRelations Table ----------------
|
||||
create_table(\$output, 'CatRelations', $action,
|
||||
cols => [
|
||||
CategoryID => { type => 'INT', not_null => 1, unsigned => 1 },
|
||||
RelatedID => { type => 'INT', not_null => 1, unsigned => 1 },
|
||||
RelationName => { type => 'VARCHAR', size => 255 }
|
||||
],
|
||||
index => {
|
||||
catid => ['CategoryID']
|
||||
},
|
||||
fk => {
|
||||
Category => { CategoryID => 'ID', RelatedID => 'ID' }
|
||||
}
|
||||
);
|
||||
|
||||
# --------- User Editors Table ----------------
|
||||
create_table(\$output, 'Editors', $action,
|
||||
cols => [
|
||||
Username => { type => 'CHAR', size => 50, not_null => 1 },
|
||||
CategoryID => { type => 'INT', unsigned => 1, not_null => 1 },
|
||||
CanAddCat => { type => 'ENUM', values => ['No', 'Yes'], not_null => 1, default => 'No' },
|
||||
CanModCat => { type => 'ENUM', values => ['No', 'Yes'], not_null => 1, default => 'No' },
|
||||
CanDelCat => { type => 'ENUM', values => ['No', 'Yes'], not_null => 1, default => 'No' },
|
||||
CanMoveCat => { type => 'ENUM', values => ['No', 'Yes'], not_null => 1, default => 'No' },
|
||||
CanAddLink => { type => 'ENUM', values => ['No', 'Yes'], not_null => 1, default => 'No' },
|
||||
CanDelLink => { type => 'ENUM', values => ['No', 'Yes'], not_null => 1, default => 'No' },
|
||||
CanModLink => { type => 'ENUM', values => ['No', 'Yes'], not_null => 1, default => 'No' },
|
||||
CanCopyLink => { type => 'ENUM', values => ['No', 'Yes'], not_null => 1, default => 'No' },
|
||||
CanMoveLink => { type => 'ENUM', values => ['No', 'Yes'], not_null => 1, default => 'No' },
|
||||
CanValLink => { type => 'ENUM', values => ['No', 'Yes'], not_null => 1, default => 'No' },
|
||||
CanModReview => { type => 'ENUM', values => ['No', 'Yes'], not_null => 1, default => 'No' },
|
||||
CanAddRel => { type => 'ENUM', values => ['No', 'Yes'], not_null => 1, default => 'No' },
|
||||
CanAddEdit => { type => 'ENUM', values => ['No', 'Yes'], not_null => 1, default => 'No' },
|
||||
],
|
||||
unique => {
|
||||
edituserndx => ['Username', 'CategoryID']
|
||||
},
|
||||
fk => {
|
||||
Users => { Username => 'Username' },
|
||||
Category => { CategoryID => 'ID' }
|
||||
}
|
||||
);
|
||||
|
||||
# --------- Verify History Table ----------------
|
||||
create_table(\$output, 'Verify', $action,
|
||||
cols => [
|
||||
LinkID => { type => 'INT', unsigned => 1, not_null => 1 },
|
||||
Status => { type => 'SMALLINT', not_null => 1, default => 0 },
|
||||
Date_Checked => { type => 'DATE' }
|
||||
],
|
||||
index => {
|
||||
veriflndx => ['LinkID']
|
||||
},
|
||||
fk => {
|
||||
Links => { LinkID => 'ID' }
|
||||
}
|
||||
);
|
||||
|
||||
# --------- Session Table ----------------
|
||||
create_table(\$output, 'Sessions', $action,
|
||||
cols => [
|
||||
session_id => { type => 'CHAR', size => 32, not_null => 1, binary => 1 },
|
||||
session_user_id => { type => 'CHAR', size => 50, not_null => 1 },
|
||||
session_date => { type => 'INT', not_null => 1 },
|
||||
session_expires => { type => 'TINYINT', default => 1 },
|
||||
session_data => { type => 'TEXT' }
|
||||
],
|
||||
pk => 'session_id',
|
||||
fk => {
|
||||
Users => { session_user_id => 'Username' }
|
||||
}
|
||||
);
|
||||
|
||||
# --------- Email Template Table ----------------
|
||||
create_table(\$output, 'EmailTemplates', $action,
|
||||
cols => [
|
||||
Name => { type => 'CHAR', size => 50, not_null => 1, regex => '\S' },
|
||||
MsgFrom => { type => 'TEXT', not_null => 1, regex => '\A(?:\S+\@[a-zA-Z0-9][a-zA-Z0-9-]*(?:\.[a-zA-Z0-9][a-zA-Z0-9-]*)+)\Z' },
|
||||
MsgFromName => { type => 'TEXT', not_null => 1 },
|
||||
Subject => { type => 'TEXT', not_null => 1 },
|
||||
Message => { type => 'MEDIUMTEXT', not_null => 1 },
|
||||
MessageFormat => { type => 'ENUM', values => [qw[text html]], not_null => 1, default => 'text' },
|
||||
LinkTemplate => { type => 'MEDIUMTEXT' }
|
||||
],
|
||||
pk => 'Name'
|
||||
);
|
||||
|
||||
# --------- Email Mailings Table -------------
|
||||
create_table(\$output, 'EmailMailings', $action,
|
||||
cols => [
|
||||
ID => { type => 'INT', unsigned => 1, not_null => 1 },
|
||||
Mailing => { type => 'INT', unsigned => 1, not_null => 1 },
|
||||
Email => { type => 'TEXT', size => 75, not_null => 1 },
|
||||
Sent => { type => 'TINYINT', default => 0, not_null => 1 },
|
||||
LinkID => { type => 'INT', unsigned => 1 } # If this is a sending to link owners, this will hold the Link ID
|
||||
],
|
||||
pk => 'ID',
|
||||
ai => 'ID'
|
||||
);
|
||||
|
||||
# --------- Email Mailing Index Table --------
|
||||
create_table(\$output, 'MailingIndex', $action,
|
||||
cols => [
|
||||
Mailing => { type => 'INT', unsigned => 1, not_null => 1 },
|
||||
extra => { type => 'TINYTEXT', not_null => 1 },
|
||||
done => { type => 'INT' },
|
||||
mailfrom => { type => 'TEXT', not_null => 1 },
|
||||
name => { type => 'TEXT', not_null => 1 },
|
||||
subject => { type => 'TEXT', not_null => 1 },
|
||||
message => { type => 'MEDIUMTEXT', not_null => 1 },
|
||||
messageformat => { type => 'ENUM', values => [qw[text html]], not_null => 1, default => 'text' },
|
||||
],
|
||||
pk => 'Mailing',
|
||||
ai => 'Mailing'
|
||||
);
|
||||
|
||||
# --------- MailingList Table ----------------
|
||||
create_table(\$output, 'MailingList', $action,
|
||||
cols => [
|
||||
ID => { type => 'INT', not_null => 1 },
|
||||
Email => { type => 'CHAR', size => 255, not_null => 1 }
|
||||
],
|
||||
index => {
|
||||
maillistndx => ['Email']
|
||||
}
|
||||
);
|
||||
|
||||
# --------- MailingListIndex Table ----------------
|
||||
create_table(\$output, 'MailingListIndex', $action,
|
||||
cols => [
|
||||
ID => { type => 'INT', unsigned => 1, not_null => 1 },
|
||||
Name => { type => 'CHAR', size => 255, not_null => 1 },
|
||||
DateModified => { type => 'INT', not_null => 1 },
|
||||
DateCreated => { type => 'INT', not_null => 1 }
|
||||
],
|
||||
pk => 'ID',
|
||||
ai => 'ID'
|
||||
);
|
||||
|
||||
# --------- ClickTrack Table ----------------
|
||||
create_table(\$output, 'ClickTrack', $action,
|
||||
cols => [
|
||||
LinkID => { type => 'INT', not_null => 1 },
|
||||
IP => { type => 'CHAR', size => 16, not_null => 1 },
|
||||
ClickType => { type => 'ENUM', values => ['Rate', 'Hits','Review'], not_null => 1 },
|
||||
ReviewID => { type => 'INT', not_null => 1, default => 0},
|
||||
Created => { type => 'TIMESTAMP' }
|
||||
],
|
||||
subclass => {
|
||||
table => { ClickTrack => 'Links::Table::ClickTrack' }
|
||||
},
|
||||
unique => {
|
||||
ct_licr => ['LinkID', 'IP', 'ClickType','ReviewID']
|
||||
},
|
||||
index => {
|
||||
cndx => ['Created']
|
||||
}
|
||||
);
|
||||
|
||||
# --------- Payments Table ----------------
|
||||
create_table(\$output, 'Payments', $action,
|
||||
cols => [
|
||||
payments_id => { type => 'CHAR', not_null => 1, size => 16 },
|
||||
payments_linkid => { type => 'INT', unsigned => 1, not_null => 1 },
|
||||
payments_status => { type => 'INT', not_null => 1, default => 0, unsigned => 1 }, # 0 = pending, 1 = completed, 2 = declined, 3 = error
|
||||
payments_method => { type => 'CHAR', not_null => 1, size => 25 },
|
||||
payments_type => { type => 'TINYINT', not_null => 1, unsigned => 1 }, # 0 = initial payment, 1 = renewal payment, 2 = recurring payment
|
||||
payments_amount => { type => 'DOUBLE', not_null => 1 },
|
||||
payments_term => { type => 'CHAR', not_null => 1, size => 10 }, # e.g. 8d, 1m, 2y, 3w, unlimited, etc.
|
||||
payments_start => { type => 'INT', not_null => 1, unsigned => 1 },
|
||||
payments_last => { type => 'INT', not_null => 1, unsigned => 1 },
|
||||
],
|
||||
pk => 'payments_id',
|
||||
fk => {
|
||||
Links => { payments_linkid => 'ID' }
|
||||
},
|
||||
index => {
|
||||
p_sl => ['payments_status', 'payments_last'],
|
||||
p_ll => ['payments_linkid', 'payments_last'],
|
||||
p_al => ['payments_amount', 'payments_last'],
|
||||
}
|
||||
);
|
||||
|
||||
# --------- Payment Logs Table ----------------
|
||||
create_table(\$output, 'PaymentLogs', $action,
|
||||
cols => [
|
||||
paylogs_id => { type => 'INT', not_null => 1, unsigned => 1 },
|
||||
paylogs_payments_id => { type => 'CHAR', not_null => 1, size => 16 },
|
||||
paylogs_type => { type => 'INT', not_null => 1, default => 0, unsigned => 1 }, # 0 = info, 1 = accepted, 2 = declined, 3 = error
|
||||
paylogs_time => { type => 'INT', not_null => 1, unsigned => 1 },
|
||||
paylogs_viewed => { type => 'TINYINT', not_null => 1, default => 0, unsigned => 1 },
|
||||
paylogs_text => { type => 'TEXT' },
|
||||
],
|
||||
pk => 'paylogs_id',
|
||||
ai => 'paylogs_id',
|
||||
fk => {
|
||||
Payments => { paylogs_payments_id => 'payments_id' }
|
||||
},
|
||||
index => {
|
||||
pl_yt => ['paylogs_type', 'paylogs_time'],
|
||||
pl_t => ['paylogs_time']
|
||||
}
|
||||
);
|
||||
|
||||
# --------- Bookmark Folders Table ----------------
|
||||
create_table(\$output, 'Bookmark_Folders', $action,
|
||||
cols => [
|
||||
my_folder_id => { type => 'INT', not_null => 1, unsigned => 1 },
|
||||
my_folder_name => { type => 'VARCHAR', not_null => 1, size => 255 },
|
||||
my_folder_description => { type => 'VARCHAR', size => 255 },
|
||||
my_folder_user_username_fk => { type => 'VARCHAR', size => 50 },
|
||||
my_folder_default => { type => 'TINYINT', not_null => 1, default => 0, unsigned => 1 },
|
||||
my_folder_public => { type => 'TINYINT', not_null => 1, default => 0, unsigned => 1 }
|
||||
],
|
||||
pk => 'my_folder_id',
|
||||
ai => 'my_folder_id',
|
||||
fk => {
|
||||
Users => { my_folder_user_username_fk => 'Username' }
|
||||
}
|
||||
);
|
||||
|
||||
# --------- Bookmark Links Table ----------------
|
||||
create_table(\$output, 'Bookmark_Links', $action,
|
||||
cols => [
|
||||
my_id => { type => 'INT', not_null => 1, unsigned => 1 },
|
||||
my_link_id_fk => { type => 'INT', not_null => 1, unsigned => 1 },
|
||||
my_user_username_fk => { type => 'VARCHAR', size => 50 },
|
||||
my_folder_id_fk => { type => 'INT', not_null => 1, unsigned => 1 },
|
||||
my_comment => { type => 'VARCHAR', size => '255' }
|
||||
],
|
||||
pk => 'my_id',
|
||||
ai => 'my_id',
|
||||
fk => {
|
||||
Users => { my_user_username_fk => 'Username' },
|
||||
Bookmark_Folders => { my_folder_id_fk => 'my_folder_id' },
|
||||
Links => { my_link_id_fk => 'ID' },
|
||||
}
|
||||
);
|
||||
|
||||
# --------- SearchLogs Table ----------------
|
||||
create_table(\$output, 'SearchLogs', $action,
|
||||
cols => [
|
||||
slog_query => { type => 'VARCHAR', not_null => 1, size => 255 },
|
||||
slog_count => { type => 'INT', not_null => 1, default => 0 },
|
||||
slog_hits => { type => 'INT', not_null => 1, default => 0 },
|
||||
slog_time => { type => 'FLOAT' },
|
||||
slog_last => { type => 'INT', not_null => 1, default => 0 },
|
||||
],
|
||||
pk => 'slog_query'
|
||||
);
|
||||
|
||||
# --------- Newsletter Subscription Table ----------------
|
||||
create_table(\$output, 'NewsletterSubscription', $action,
|
||||
cols => [
|
||||
UserID => { type => 'CHAR', size => 50 },
|
||||
CategoryID => { type => 'INT', not_null => 1 },
|
||||
],
|
||||
unique => {
|
||||
ns_uc => ['UserID', 'CategoryID']
|
||||
},
|
||||
fk => {
|
||||
Users => { UserID => 'Username' },
|
||||
Category => { CategoryID => 'ID' }
|
||||
}
|
||||
);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
sub create_table {
|
||||
my ($output, $table, $action, @def) = @_;
|
||||
|
||||
$$output .= Links::language('dialog_create', $table);
|
||||
my $c = $DB->creator($table);
|
||||
$c->clear_schema() if $action eq 'force';
|
||||
|
||||
@def % 2 and die "Odd number of table defs passed to create_table()";
|
||||
while (@def) {
|
||||
my ($meth, $arg) = splice @def, 0, 2;
|
||||
$c->$meth($arg);
|
||||
}
|
||||
|
||||
if ($c->create($action)) {
|
||||
$$output .= Links::language('dialog_ok');
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
$$output .= Links::language($GT::SQL::errcode eq 'TBLEXISTS' ? ('error_failed_exists') : ('error_failed_other', $GT::SQL::error));
|
||||
$GT::SQL::errcode if 0; # silence "used only once" warning
|
||||
$c->set_defaults;
|
||||
$c->save_schema;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
sub load_from_sql {
|
||||
# ---------------------------------------------------------------
|
||||
# Creates def files based on existing tables.
|
||||
#
|
||||
my ($output, $return);
|
||||
foreach my $table (@TABLES) {
|
||||
$output .= "$table .. ";
|
||||
my $c = $DB->creator($table);
|
||||
$return = $c->load_table($table);
|
||||
if ($return) {
|
||||
if ($table eq 'Links' or $table eq 'Users' or $table eq 'Category') {
|
||||
$c->subclass(
|
||||
table => { $table => "Links::Table::$table" },
|
||||
html => { $table => "Links::HTML::$table" }
|
||||
);
|
||||
}
|
||||
elsif ($table eq 'CatLinks' or $table eq 'ClickTrack') {
|
||||
$c->subclass(
|
||||
table => { $table => "Links::Table::$table" }
|
||||
);
|
||||
}
|
||||
$output .= "ok!\n";
|
||||
$c->save_schema();
|
||||
}
|
||||
else {
|
||||
$output .= "failed: $GT::SQL::error\n";
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
sub load {
|
||||
# ---------------------------------------------------------------
|
||||
# Return a hash of current connection settings.
|
||||
#
|
||||
my %h = ();
|
||||
$h{prefix} = $DB->prefix();
|
||||
$h{database} = $DB->{connect}->{database};
|
||||
$h{login} = $DB->{connect}->{login};
|
||||
$h{password} = $DB->{connect}->{password};
|
||||
$h{host} = $DB->{connect}->{host};
|
||||
$h{host} .= ":" . $DB->{connect}->{port} if $DB->{connect}->{port};
|
||||
$h{driver} = $DB->{connect}->{driver};
|
||||
return \%h;
|
||||
}
|
||||
|
||||
1;
|
||||
313
site/slowtwitch.com/cgi-bin/articles/admin/Links/SiteHTML.pm
Normal file
313
site/slowtwitch.com/cgi-bin/articles/admin/Links/SiteHTML.pm
Normal file
@@ -0,0 +1,313 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: SiteHTML.pm,v 1.89 2008/04/29 04:02:34 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::SiteHTML;
|
||||
# ==================================================================
|
||||
use strict;
|
||||
use Links qw/:objects :payment/;
|
||||
|
||||
sub display {
|
||||
# -----------------------------------------------------------------
|
||||
# Returns a specified template parsed.
|
||||
#
|
||||
my ($template, $vars, $opts) = @_;
|
||||
my $code = exists $Links::SiteHTML::{"site_html_$template"} ? $Links::SiteHTML::{"site_html_$template"} : _compile("site_html_$template");
|
||||
defined $code or die "Invalid method: site_html_$template called.";
|
||||
|
||||
$PLG->dispatch("site_html_$template", $code, $vars, $opts);
|
||||
}
|
||||
|
||||
sub tags {
|
||||
# -----------------------------------------------------------------
|
||||
# Returns the tags needed to properly include a template in another template,
|
||||
# instead of returning parsed HTML like display(). Currently only supports
|
||||
# 'link' for formatted link information.
|
||||
#
|
||||
my ($sub, $vars, $opts) = @_;
|
||||
my $code = exists $Links::SiteHTML::{"site_tags_$sub"} && $Links::SiteHTML::{"site_tags_$sub"};
|
||||
defined $code or die "Invalid method: site_tags_$sub called.";
|
||||
|
||||
$PLG->dispatch("site_tags_$sub", $code, $vars, $opts);
|
||||
}
|
||||
|
||||
# All the templates are auto-loaded, except for the ones below which need
|
||||
# to do some special stuff.
|
||||
|
||||
sub site_tags_link {
|
||||
# --------------------------------------------------------
|
||||
# Format the tags for a single link.
|
||||
#
|
||||
my ($vars, $cat_id) = @_;
|
||||
|
||||
my %block = $Links::GLOBALS ? map { $_ => 1 } keys %$Links::GLOBALS : ();
|
||||
my %rec = map { exists $block{$_} ? () : ($_ => $vars->{$_}) } keys %$vars;
|
||||
|
||||
$rec{Add_Date_time} = GT::Date::timelocal(GT::Date::parse_format($rec{Add_Date}, GT::Date::FORMAT_DATE));
|
||||
$rec{Mod_Date_time} = GT::Date::timelocal(GT::Date::parse_format($rec{Mod_Date}, GT::Date::FORMAT_DATE));
|
||||
|
||||
# Convert the date formats.
|
||||
if (GT::Date::FORMAT_DATE ne $CFG->{date_user_format}) {
|
||||
Links::init_date();
|
||||
$rec{Add_Date} = GT::Date::date_transform($rec{Add_Date}, GT::Date::FORMAT_DATE, $CFG->{date_user_format}) || $rec{Add_Date};
|
||||
$rec{Mod_Date} = GT::Date::date_transform($rec{Mod_Date}, GT::Date::FORMAT_DATE, $CFG->{date_user_format}) || $rec{Mod_Date};
|
||||
}
|
||||
|
||||
# Set new and pop to either 1 or undef for templates.
|
||||
$rec{isNew} = ($rec{isNew} and ($rec{isNew} eq 'Yes' or $rec{isNew} eq '1')) ? 1 : 0;
|
||||
$rec{isChanged} = ($rec{isChanged} and ($rec{isChanged} eq 'Yes' or $rec{isChanged} eq '1')) ? 1 : 0;
|
||||
$rec{isPopular} = ($rec{isPopular} and ($rec{isPopular} eq 'Yes' or $rec{isPopular} eq '1')) ? 1 : 0;
|
||||
$rec{isLinkOwner} = ($USER and defined $USER->{Username} and $rec{LinkOwner} eq $USER->{Username}) ? 1 : 0;
|
||||
|
||||
$rec{paymentsEnabled} = 0; # The payment url is disabled by default
|
||||
|
||||
if ($CFG->{payment}->{enabled}) {
|
||||
my $catp;
|
||||
# Fetch payment information for the category the link is in (used below to determine if the payment data should be shown)
|
||||
my @cid = $DB->table('CatLinks')->select('CategoryID', { LinkID => $rec{ID} })->fetchall_list;
|
||||
require Links::Payment;
|
||||
$catp = Links::Payment::load_cat_price(\@cid);
|
||||
|
||||
# Add various extra tags regarding payment if the current user is the link owner:
|
||||
if ($rec{isLinkOwner} and $rec{ExpiryDate} != UNLIMITED and $catp->{payment_mode} != NOT_ACCEPTED) {
|
||||
my $expiry_date = $rec{ExpiryDate};
|
||||
my $notify_date = time + $CFG->{payment}->{expiry_notify} * (24*60*60);
|
||||
$rec{paymentsEnabled} = 1;
|
||||
$rec{isUnpaid} = $expiry_date == UNPAID;
|
||||
$rec{isFree} = $expiry_date == FREE;
|
||||
$rec{isExpired} = ($expiry_date > UNPAID and $expiry_date < time or $rec{isFree} and $rec{LinkExpired});
|
||||
$rec{wasPaid} = ($expiry_date > UNPAID and $expiry_date < FREE or $rec{isFree} and $rec{LinkExpired});
|
||||
$rec{ExpiryDateFormatted} = ($expiry_date > UNPAID and $expiry_date < FREE)
|
||||
? GT::Date::date_get($expiry_date, $CFG->{date_expiry_format})
|
||||
: ($rec{isFree} and $rec{LinkExpired})
|
||||
? GT::Date::date_get($rec{LinkExpired}, $CFG->{date_expiry_format})
|
||||
: '';
|
||||
$rec{isNotify} = ($expiry_date >= time and $expiry_date <= $notify_date) ? 1 : 0;
|
||||
}
|
||||
|
||||
$rec{isPaidLink} = 0;
|
||||
$rec{isFreeLink} = 0;
|
||||
if ($rec{ExpiryDate} >= time and $rec{ExpiryDate} < FREE) {
|
||||
$rec{isPaidLink} = 1;
|
||||
}
|
||||
elsif ($rec{ExpiryDate} == FREE) {
|
||||
$rec{isFreeLink} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
my $links = $DB->table('Links');
|
||||
if ($CFG->{build_detailed}) {
|
||||
my $detailed;
|
||||
# Generate the detailed url for a specific the category that we're in (a link may be in multiple categories)
|
||||
if ($cat_id) {
|
||||
$detailed = $links->category_detailed_url($cat_id, $rec{ID});
|
||||
}
|
||||
else {
|
||||
$detailed = $links->detailed_url($rec{ID});
|
||||
}
|
||||
$rec{detailed_url} = "$CFG->{build_detail_url}/$detailed";
|
||||
}
|
||||
|
||||
# Load any reviews, if not already done
|
||||
$links->add_reviews(\%rec) unless exists $rec{Review_Loop};
|
||||
|
||||
\%rec;
|
||||
}
|
||||
|
||||
sub site_html_link {
|
||||
# --------------------------------------------------------
|
||||
# Format and return the HTML for a single link.
|
||||
#
|
||||
# Note that this method is deprecated in favour of generating all the html in
|
||||
# the templates. Instead, you should be doing:
|
||||
# <%Links::Utils::load_link_info%><%include link.html%>
|
||||
#
|
||||
my $rec = tags(link => @_);
|
||||
|
||||
# Set the template set to use.
|
||||
my $opts = { dynamic => 0 };
|
||||
if ($rec->{Category_Template} and $rec->{Category_Template} =~ /^[\w-]+(\.[\w-]+)?$/ and (not $1 or ($1 ne '.html' and $1 ne '.htm'))) {
|
||||
$opts->{template} = delete $rec->{Category_Template};
|
||||
}
|
||||
|
||||
# Parse the template.
|
||||
return Links::user_page('link.html', $rec, $opts);
|
||||
}
|
||||
|
||||
sub site_html_detailed {
|
||||
# --------------------------------------------------------
|
||||
# Return parsed detailed page (one link per html page).
|
||||
#
|
||||
my $rec = shift;
|
||||
|
||||
$rec->{Add_Date_time} = GT::Date::timelocal(GT::Date::parse_format($rec->{Add_Date}, GT::Date::FORMAT_DATE));
|
||||
$rec->{Mod_Date_time} = GT::Date::timelocal(GT::Date::parse_format($rec->{Mod_Date}, GT::Date::FORMAT_DATE));
|
||||
|
||||
# Convert the date formats.
|
||||
if (GT::Date::FORMAT_DATE ne $CFG->{date_user_format}) {
|
||||
Links::init_date();
|
||||
$rec->{Add_Date_orig} = $rec->{Add_Date};
|
||||
$rec->{Add_Date} = GT::Date::date_transform($rec->{Add_Date}, GT::Date::FORMAT_DATE, $CFG->{date_user_format}) || $rec->{Add_Date};
|
||||
$rec->{Mod_Date_orig} = $rec->{Mod_Date};
|
||||
$rec->{Mod_Date} = GT::Date::date_transform($rec->{Mod_Date}, GT::Date::FORMAT_DATE, $CFG->{date_user_format}) || $rec->{Mod_Date};
|
||||
}
|
||||
|
||||
# Set new and pop to either 1 or undef for templates.
|
||||
$rec->{isNew} = ($rec->{isNew} and ($rec->{isNew} eq 'Yes' or $rec->{isNew} eq '1')) ? 1 : 0;
|
||||
$rec->{isChanged} = ($rec->{isChanged} and ($rec->{isChanged} eq 'Yes' or $rec->{isChanged} eq '1')) ? 1 : 0;
|
||||
$rec->{isPopular} = ($rec->{isPopular} and ($rec->{isPopular} eq 'Yes' or $rec->{isPopular} eq '1')) ? 1 : 0;
|
||||
$rec->{isLinkOwner} = ($USER and defined $USER->{Username} and $rec->{LinkOwner} eq $USER->{Username}) ? 1 : 0;
|
||||
|
||||
if ($CFG->{payment}->{enabled}) {
|
||||
$rec->{isPaidLink} = 0;
|
||||
$rec->{isFreeLink} = 0;
|
||||
if ($rec->{ExpiryDate} >= time and $rec->{ExpiryDate} < FREE) {
|
||||
$rec->{isPaidLink} = 1;
|
||||
}
|
||||
elsif ($rec->{ExpiryDate} == FREE) {
|
||||
$rec->{isFreeLink} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
# Set the template set to use.
|
||||
my $opts = { dynamic => 1 };
|
||||
if ($rec->{Category_Template} and $rec->{Category_Template} =~ /^[\w-]+(\.[\w-]+)?$/ and (not $1 or ($1 ne '.html' and $1 ne '.htm'))) {
|
||||
$opts->{template} = delete $rec->{Category_Template};
|
||||
}
|
||||
|
||||
my $output = Links::user_page('detailed.html', $rec, $opts);
|
||||
return $output;
|
||||
}
|
||||
|
||||
sub site_html_category {
|
||||
# --------------------------------------------------------
|
||||
# Return parsed category page.
|
||||
#
|
||||
my $tags = shift;
|
||||
|
||||
$tags->{build_links_per_page} = $CFG->{build_links_per_page};
|
||||
($tags->{category_first}) = $tags->{'category_name'} =~ m,/?([^/]+)$,;
|
||||
|
||||
my $opts = { dynamic => 1 };
|
||||
|
||||
# Find the proper template.
|
||||
my $template = 'category.html';
|
||||
|
||||
# If the Category_Template ends with .htm or .html, then use that file as a template, otherwise, use it as a template set.
|
||||
if ($tags->{Category_Template} and $tags->{Category_Template} =~ /^[\w-]+(\.[\w-]+)?$/ and (not $1 or ($1 ne '.html' and $1 ne '.htm'))) {
|
||||
$opts->{template} = delete $tags->{Category_Template};
|
||||
}
|
||||
elsif ($tags->{Category_Template}) {
|
||||
$template = $tags->{Category_Template};
|
||||
}
|
||||
my $output = Links::user_page($template, $tags, $opts);
|
||||
return $output;
|
||||
}
|
||||
|
||||
sub site_html_print_cat {
|
||||
# --------------------------------------------------------
|
||||
# This routine prints out a list of categories.
|
||||
#
|
||||
# Note that this method has been deprecated in favour of using loops and
|
||||
# performing html generation in the templates. If you need to modify
|
||||
# the category data, use the build_category_loop plugin hook.
|
||||
#
|
||||
my @subcat = @{$_[0]};
|
||||
my $parent_cat = shift @subcat;
|
||||
my $breakpoint = int(@subcat / $CFG->{build_category_columns});
|
||||
$breakpoint++ if @subcat % $CFG->{build_category_columns};
|
||||
my $table_head = $CFG->{build_category_table} || '';
|
||||
my $width = int(100 / $CFG->{build_category_columns});
|
||||
my $output = '';
|
||||
my $i = 0;
|
||||
my $cat_db = $DB->table('Category');
|
||||
my $opts = { dynamic => 0 };
|
||||
|
||||
# Print Header.
|
||||
if ($CFG->{build_category_columns}) {
|
||||
$output = qq|<div class="margin"><table $table_head><tr><td class="catlist" width="$width%" valign="top">\n|;
|
||||
}
|
||||
|
||||
# Figure out if we should use a different template.
|
||||
if ($parent_cat->{Category_Template} and $parent_cat->{Category_Template} =~ /^[\w-]+(\.[\w-]+)?$/ and (not $1 or ($1 ne '.html' and $1 ne '.htm'))) {
|
||||
$opts->{template} = delete $parent_cat->{Category_Template};
|
||||
}
|
||||
|
||||
# Go through each subcategory and print its template.
|
||||
for my $cat_r (@subcat) {
|
||||
$cat_r->{Short_Name} = $cat_r->{Name} =~ m,.*/([^/]+)$, ? $1 : $cat_r->{Name};
|
||||
$cat_r->{URL} ||= $CFG->{build_root_url} . "/" . $cat_db->as_url($cat_r->{Full_Name}) . "/" . ($CFG->{build_index_include} ? $CFG->{build_index} : '');
|
||||
|
||||
# Set the short name.
|
||||
if ($cat_r->{Related}) {
|
||||
if ($cat_r->{RelationName}) {
|
||||
$cat_r->{Short_Name} = $cat_r->{RelationName};
|
||||
}
|
||||
else {
|
||||
if (exists $parent_cat->{Name} and ($cat_r->{Short_Name} eq $parent_cat->{Name})) {
|
||||
my ($short) = $cat_r->{Full_Name} =~ m,([^/]+)/[^/]*$,;
|
||||
$short and ($cat_r->{Short_Name} = $short);
|
||||
}
|
||||
else {
|
||||
$cat_r->{Short_Name} = $cat_r->{Short_Name};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# We check to see if we are half way through, if so we stop this table cell
|
||||
# and begin a new one (this lets us have category names in two columns).
|
||||
if ($CFG->{build_category_columns}) {
|
||||
$output .= qq|</td>\n<td valign="top" width="$width%" class="catlist">\n| if $i > 0 and not $i % $breakpoint;
|
||||
$i++;
|
||||
}
|
||||
$output .= Links::user_page('subcategory.html', $cat_r, $opts);
|
||||
}
|
||||
|
||||
# Don't forget to end the table properly ..
|
||||
if ($CFG->{build_category_columns}) {
|
||||
$output .= "</td></tr></table></div>\n";
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
sub site_html_error {
|
||||
# --------------------------------------------------------
|
||||
# Print out the error page
|
||||
#
|
||||
my ($vars, $opts) = @_;
|
||||
$opts ||= { dynamic => 1 };
|
||||
unless (exists $vars->{main_title_loop}) {
|
||||
require Links::Build;
|
||||
$vars->{main_title_loop} = Links::Build::build('title', Links::language('LINKS_ERROR'), $CFG->{build_root_url} . "/" . ($CFG->{build_home} || ($CFG->{build_index_include} ? $CFG->{build_index} : '')));
|
||||
}
|
||||
return Links::user_page('error.html', $vars, $opts);
|
||||
}
|
||||
|
||||
sub _compile {
|
||||
# -------------------------------------------------------------------
|
||||
# Compile dynamically creates site_html routines if a template file
|
||||
# exists.
|
||||
#
|
||||
my $sub = shift;
|
||||
my ($file) = $sub =~ /^site_html_([\w-]+)$/;
|
||||
$file or return sub { display('error', { error => "Invalid SiteHTML method: '" . $IN->html_escape($sub) . "'." }) };
|
||||
$file .= '.html';
|
||||
my $template_set = Links::template_set();
|
||||
unless (Links::template_exists($template_set, $file)) {
|
||||
return sub { display('error', { error => "Invalid SiteHTML method: $sub ($file). The template does not exist in '$template_set'." }) };
|
||||
}
|
||||
my $code = sub { my ($vars, $opts) = @_; $opts ||= { dynamic => 1 }; return Links::user_page($file, $vars, $opts) };
|
||||
$Links::SiteHTML::{$sub} = $code;
|
||||
$code;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,95 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: CatLinks.pm,v 1.4 2006/03/25 01:13:35 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::Table::CatLinks;
|
||||
# ==================================================================
|
||||
use strict;
|
||||
use Links qw/:payment :objects/;
|
||||
use GT::SQL;
|
||||
use GT::SQL::Table;
|
||||
use vars qw /@ISA $ERROR_MESSAGE @DELETING/;
|
||||
|
||||
@ISA = qw/GT::SQL::Table/;
|
||||
$ERROR_MESSAGE = 'GT::SQL';
|
||||
@DELETING = (); # Used by Links::Table::Links
|
||||
|
||||
sub delete {
|
||||
# -----------------------------------------------------------------------------
|
||||
# We override the default CatLinks delete to delete any links that will no
|
||||
# longer be referenced as a result of the deletion.
|
||||
#
|
||||
my ($self, $cond) = @_;
|
||||
|
||||
ref $cond or return $self->fatal(BADARGS => '$catlinks->delete(condition)');
|
||||
|
||||
# Get the CatLinks rows that are about to be deleted
|
||||
my (%delete, %links);
|
||||
my $sth = $self->select($cond);
|
||||
while (my $row = $sth->fetchrow_hashref) {
|
||||
$delete{$row->{LinkID}}++;
|
||||
if (exists $links{$row->{LinkID}}) {
|
||||
push @{$links{$row->{LinkID}}}, $row->{CategoryID};
|
||||
}
|
||||
else {
|
||||
$links{$row->{LinkID}} = [$row->{CategoryID}];
|
||||
}
|
||||
}
|
||||
|
||||
# Delete the CatLinks rows
|
||||
my $ret = $self->SUPER::delete($cond) or return;
|
||||
|
||||
# Get the links that still exist in the CatLinks table after the delete (ie.
|
||||
# links that were in multiple categories). These are the links that shouldn't
|
||||
# be deleted from the Links table.
|
||||
my @remaining = keys %delete ? $self->select('LinkID', { LinkID => [keys %delete] })->fetchall_list : ();
|
||||
for (@remaining, @DELETING) {
|
||||
delete $delete{$_};
|
||||
}
|
||||
|
||||
# Non-validated links don't increment Category counts.
|
||||
my @notval = keys %links ? $DB->table('Links')->select('ID', { ID => [keys %links], isValidated => 'No' })->fetchall_list : ();
|
||||
for (@notval, @DELETING) {
|
||||
delete $links{$_};
|
||||
}
|
||||
|
||||
# Any links in %delete have no references to it from CatLinks
|
||||
if (keys %delete) {
|
||||
$DB->table('Links')->delete({ ID => [keys %delete] });
|
||||
}
|
||||
|
||||
# Build a list of categories that need their counts updated
|
||||
my %cats;
|
||||
for (keys %links) {
|
||||
for (@{$links{$_}}) {
|
||||
$cats{$_}++;
|
||||
}
|
||||
}
|
||||
|
||||
# Update the Category link counts
|
||||
if (keys %cats) {
|
||||
my $category = $DB->table('Category');
|
||||
my %change;
|
||||
while (my ($catid, $count) = each %cats) {
|
||||
push @{$change{-$count}}, $catid;
|
||||
}
|
||||
$category->link_count(\%change);
|
||||
|
||||
while (my ($change, $ids) = each %change) {
|
||||
$category->update({ Direct_Links => \("Direct_Links - " . abs $change) }, { ID => $ids });
|
||||
}
|
||||
}
|
||||
|
||||
$ret;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,638 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Category.pm,v 1.29 2009/05/11 05:57:45 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::Table::Category;
|
||||
# ==================================================================
|
||||
use strict;
|
||||
use Links qw/:payment :objects/;
|
||||
use GT::SQL;
|
||||
use GT::SQL::Table;
|
||||
use GT::Lock qw/lock unlock LOCK_TRY/;
|
||||
use vars qw /@ISA $ERRORS $ERROR_MESSAGE/;
|
||||
|
||||
@ISA = qw/GT::SQL::Table/;
|
||||
$ERROR_MESSAGE = 'GT::SQL';
|
||||
$ERRORS = {
|
||||
BADCATNAME => "Invalid category name: %s",
|
||||
BADCATID => "Invalid category id: %s",
|
||||
BADCATSUG => "There is no category with that name. Perhaps you meant: %s",
|
||||
CATEXISTS => "A category with the name '%s' already exists.",
|
||||
};
|
||||
|
||||
# We wrap new() to handle updating Number_of_Links - but only once: the first
|
||||
# time a Category table object is created.
|
||||
sub new {
|
||||
my $self = shift->SUPER::new(@_) or return;
|
||||
|
||||
return $self if $STASH{expired_links}++;
|
||||
|
||||
my $links = $DB->table('Links');
|
||||
my $cond;
|
||||
if ($CFG->{payment}->{enabled}) {
|
||||
$cond = GT::SQL::Condition->new(
|
||||
ExpiryCounted => '=' => 0,
|
||||
ExpiryDate => '<' => time,
|
||||
isValidated => '=' => 'Yes'
|
||||
);
|
||||
}
|
||||
else {
|
||||
$cond = GT::SQL::Condition->new(
|
||||
ExpiryCounted => '=' => 1,
|
||||
isValidated => '=' => 'Yes'
|
||||
);
|
||||
}
|
||||
# Don't select the ID's here because we haven't established a lock. Since
|
||||
# most requests won't catch expired links, doing a count here to avoid
|
||||
# needing the lock is going to be slightly slower occassionally, but
|
||||
# usually faster.
|
||||
return $self unless $links->count($cond);
|
||||
|
||||
# We've now determined that there _are_ links that have expired that
|
||||
# haven't been counted yet, so we establish a lock (to prevent a race
|
||||
# condition), and then update the links counts for categories that have
|
||||
# newly-expired links. If getting the lock fails, simply return - this is
|
||||
# only likely to happen when another process has the lock and is performing
|
||||
# the updates already, or when a previous process with a lock died - the
|
||||
# 120 should make sure that such a condition doesn't last longer than 2
|
||||
# minutes.
|
||||
lock cat_link_count => 1, LOCK_TRY, 120
|
||||
or return $self;
|
||||
|
||||
my @links = $links->select(ID => $cond)->fetchall_list;
|
||||
unless (@links) { # Despite the above count, there might not be links now if we had to wait for a lock
|
||||
unlock 'cat_link_count';
|
||||
return $self;
|
||||
}
|
||||
|
||||
if ($CFG->{payment}->{expired_is_free}) {
|
||||
# This gets a bit hairy - expired links need to become free but NOT in
|
||||
# required categories. On the other hand, links in non-required
|
||||
# categories don't affect the count.
|
||||
my %req_links = map { $_ => 1 } $DB->table('Category', 'CatLinks')->select(LinkID => { LinkID => \@links, Payment_Mode => $CFG->{payment}->{mode} == REQUIRED ? [GLOBAL, REQUIRED] : REQUIRED })->fetchall_list;
|
||||
my @to_free = grep !$req_links{$_}, @links;
|
||||
if (@to_free) {
|
||||
$DB->table('Links')->update({ LinkExpired => \'ExpiryDate' }, { ID => \@to_free });
|
||||
$DB->table('Links')->update({ ExpiryDate => FREE }, { ID => \@to_free });
|
||||
}
|
||||
@links = keys %req_links;
|
||||
unless (@links) {
|
||||
unlock 'cat_link_count';
|
||||
return $self;
|
||||
}
|
||||
}
|
||||
my $catlinks = $DB->table('CatLinks');
|
||||
$catlinks->select_options('GROUP BY CategoryID');
|
||||
my %cats = $catlinks->select(CategoryID => 'COUNT(*)' => { LinkID => \@links })->fetchall_list; # FIXME this query can be huge and will fail (the select() will fail and return undef)
|
||||
my %adjust;
|
||||
my %direct_adj;
|
||||
|
||||
my $parents = $self->parents([keys %cats]);
|
||||
for my $cat_id (keys %cats) {
|
||||
$adjust{$cat_id} ||= 0;
|
||||
$adjust{$cat_id} += $cats{$cat_id};
|
||||
$direct_adj{$cat_id} ||= 0;
|
||||
$direct_adj{$cat_id} += $cats{$cat_id};
|
||||
for (@{$parents->{$cat_id}}) {
|
||||
$adjust{$_} ||= 0;
|
||||
$adjust{$_} += $adjust{$cat_id};
|
||||
}
|
||||
}
|
||||
|
||||
my %change;
|
||||
while (my ($id, $change) = each %adjust) {
|
||||
push @{$change{$CFG->{payment}->{enabled} ? -$change : $change}}, $id;
|
||||
}
|
||||
my %change_direct;
|
||||
while (my ($id, $change) = each %direct_adj) {
|
||||
push @{$change_direct{$CFG->{payment}->{enabled} ? -$change : $change}}, $id;
|
||||
}
|
||||
|
||||
while (my ($adjust, $ids) = each %change) {
|
||||
$self->update({ Number_of_Links => \("Number_of_Links " . ($adjust >= 0 ? '+' : '-') . ' ' . abs $adjust) }, { ID => $ids });
|
||||
}
|
||||
while (my ($adjust, $ids) = each %change_direct) {
|
||||
$self->update({ Direct_Links => \("Direct_Links " . ($adjust >= 0 ? '+' : '-') . ' ' . abs $adjust) }, { ID => $ids });
|
||||
}
|
||||
|
||||
$links->update({ ExpiryCounted => $CFG->{payment}->{enabled} ? 1 : 0 }, { ID => \@links });
|
||||
|
||||
unlock 'cat_link_count';
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub add {
|
||||
# -------------------------------------------------------------------
|
||||
# Adds a category, but passes it through the plugin system.
|
||||
#
|
||||
my $self = shift;
|
||||
my $p = (ref $_[0] eq 'HASH') ? shift : {@_};
|
||||
|
||||
$PLG->dispatch('add_category', sub { return $self->_plg_add(@_) }, $p);
|
||||
}
|
||||
|
||||
sub _plg_add {
|
||||
# -------------------------------------------------------------------
|
||||
# Add a category.
|
||||
#
|
||||
my ($self, $p) = @_;
|
||||
|
||||
$self->can_add($p) or return;
|
||||
|
||||
# If successful, we need to update timestamps of parents to denote a change.
|
||||
if (my $id = $self->SUPER::add($p)) {
|
||||
if ($p->{FatherID}) {
|
||||
$self->update(
|
||||
{ Timestmp => \"NOW()" },
|
||||
{ ID => $self->parents($id) },
|
||||
{ GT_SQL_SKIP_CHECK => 1, GT_SQL_SKIP_INDEX => 1 }
|
||||
);
|
||||
}
|
||||
return $id;
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sub can_add {
|
||||
# -------------------------------------------------------------------
|
||||
# Confirms that a category can be added.
|
||||
#
|
||||
my $self = shift;
|
||||
my $p = $self->common_param(@_) or return $self->warn(BADARGS => 'Usage: $table->add(HASH or HASH_REF or CGI)');
|
||||
|
||||
# Checks that the FatherID exists and set the full name.
|
||||
$p->{FatherID} ||= 0;
|
||||
if ($p->{FatherID} =~ /\D/) {
|
||||
my $sth = $self->select(ID => Full_Name => { Full_Name => $p->{FatherID} });
|
||||
if (my @row = $sth->fetchrow) {
|
||||
$p->{FatherID} = $row[0];
|
||||
$p->{Full_Name} = "$row[1]/$p->{Name}";
|
||||
}
|
||||
else {
|
||||
my $names = $self->suggestions($p->{FatherID});
|
||||
return $self->warn(
|
||||
@$names
|
||||
? (BADCATSUG => '<ul>' . join('', map "<li>$_</li>", @$names) . '</ul>')
|
||||
: (BADCATNAME => $p->{FatherId})
|
||||
);
|
||||
}
|
||||
}
|
||||
elsif ($p->{FatherID} != 0) {
|
||||
my $full_name = $self->get_name_from_id($p->{FatherID}) or return $self->warn(BADCATID => $p->{FatherID});
|
||||
$p->{Full_Name} = "$full_name/$p->{Name}";
|
||||
}
|
||||
else {
|
||||
$p->{Full_Name} = $p->{Name};
|
||||
}
|
||||
|
||||
# Checks that there is no other category with the same (Name, FatherID)
|
||||
return $self->warn(CATEXISTS => $p->{Name})
|
||||
if $self->count({ Name => $p->{Name}, FatherID => $p->{FatherID} });
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub delete {
|
||||
# -------------------------------------------------------------------
|
||||
# Deletes a category, but passes through the plugin system.
|
||||
#
|
||||
my ($self, $where) = @_;
|
||||
if (not ref $where or ref $where eq 'ARRAY') {
|
||||
$where = { ID => $where };
|
||||
}
|
||||
return $self->fatal(BADARGS => 'Usage: $category->delete(condition)')
|
||||
unless ref $where eq 'HASH' or UNIVERSAL::isa($where, 'GT::SQL::Condition');
|
||||
|
||||
my $ret;
|
||||
my %cats = $self->select(qw/ID Direct_Links/ => $where)->fetchall_list;
|
||||
if ($PLG->active_plugins('delete_category')) {
|
||||
for my $id (keys %cats) {
|
||||
my $r = $PLG->dispatch('delete_category', sub { return $self->SUPER::delete(@_) }, { ID => $id });
|
||||
$ret += $r if defined $r;
|
||||
}
|
||||
$ret = '0 but true' if (defined $ret and $ret == 0) or not keys %cats;
|
||||
}
|
||||
else {
|
||||
$ret = $self->SUPER::delete($where);
|
||||
}
|
||||
|
||||
return $ret unless $ret;
|
||||
|
||||
# Clear out the cache as the hierarchy has changed.
|
||||
$self->_clear_cache;
|
||||
|
||||
$ret;
|
||||
}
|
||||
|
||||
sub modify {
|
||||
# -------------------------------------------------------------------
|
||||
# Modifies a category, but passes through the plugin system.
|
||||
#
|
||||
my ($self, $cat) = @_;
|
||||
$PLG->dispatch('modify_category', sub { return $self->_plg_modify(@_) }, $cat);
|
||||
}
|
||||
|
||||
sub _plg_modify {
|
||||
# -------------------------------------------------------------------
|
||||
# Modify a single category.
|
||||
#
|
||||
my $self = shift;
|
||||
my $set = shift or return $self->error('BADARGS', 'FATAL', "Usage: \$cat->modify( { col => value ... } ).");
|
||||
my $id = $set->{ID} or return $self->error('BADARGS', 'FATAL', "No primary key passed to modify!");
|
||||
|
||||
# Get the original info.
|
||||
my $orig = $self->select(qw/ID FatherID Full_Name Name Number_of_Links/ => { ID => $id })->fetchrow_hashref
|
||||
or return $self->warn(BADCATID => $id);
|
||||
|
||||
# Fix up the father ID.
|
||||
$set->{FatherID} ||= 0;
|
||||
if ($set->{FatherID} !~ /^\d+$/) {
|
||||
my $new_id = $self->get_id_from_name($set->{FatherID});
|
||||
if (! $new_id) {
|
||||
my $names = $self->suggestions($set->{FatherID});
|
||||
return $self->error(@$names
|
||||
? ('BADCATSUG', 'WARN', "<ul>" . join('', map "<li>$_</li>", @$names) . "</ul>")
|
||||
: ('BADCATNAME', 'WARN', $set->{FatherID})
|
||||
);
|
||||
}
|
||||
$set->{FatherID} = $new_id;
|
||||
}
|
||||
|
||||
$self->can_modify($set, $orig) or return;
|
||||
|
||||
if ($orig->{Name} eq $set->{Name} and $orig->{FatherID} == $set->{FatherID}) {
|
||||
# Name and parent haven't changed, no special modify handling needed
|
||||
return $self->SUPER::modify($set);
|
||||
}
|
||||
elsif ($orig->{FatherID} == $set->{FatherID}) {
|
||||
# Name has changed, but parent is the same: update ancestors'
|
||||
# timestamps, change the full name, and update subcategory names.
|
||||
($set->{Full_Name} = $orig->{Full_Name}) =~ s/\Q$orig->{Name}\E$/$set->{Name}/i;
|
||||
my $ret = $self->SUPER::modify($set);
|
||||
if ($ret) {
|
||||
# Update was successful, update the timestamp of old and new parents
|
||||
|
||||
# Clear the as the tree just changed
|
||||
$self->_clear_cache;
|
||||
|
||||
if ($set->{FatherID}) {
|
||||
my $parents = $self->parents($id);
|
||||
$self->update({ Timestmp => \"NOW()" }, { ID => $parents }, { GT_SQL_SKIP_CHECK => 1 })
|
||||
if @$parents;
|
||||
}
|
||||
|
||||
$self->update_full_name($id, $orig->{Full_Name}, $set->{Full_Name});
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
else {
|
||||
# The category has moved; get the new parent's full name and update
|
||||
my $fn = $self->select(Full_Name => { ID => $set->{FatherID} })->fetchrow;
|
||||
$set->{Full_Name} = ($fn ? "$fn/" : '') . $set->{Name};
|
||||
|
||||
my $ret = $self->SUPER::modify($set);
|
||||
if ($ret) {
|
||||
# Clear the cache as the tree has changed.
|
||||
$self->_clear_cache;
|
||||
|
||||
$self->update_full_name($id, $orig->{Full_Name}, $set->{Full_Name});
|
||||
|
||||
# Now update counters on the above parents.
|
||||
# Clear out the cache as otherwise we get our old parents.
|
||||
if ($orig->{Number_of_Links} != 0) {
|
||||
$self->link_count($orig->{FatherID}, -$orig->{Number_of_Links});
|
||||
$self->link_count($set->{FatherID}, $orig->{Number_of_Links});
|
||||
}
|
||||
}
|
||||
# Clear out the cache.
|
||||
$self->_clear_cache;
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
||||
sub update_full_name {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Call this after changing a category's Full_Name to change all the category's
|
||||
# children's full names. Call with the category ID, old full name, and new
|
||||
# full name.
|
||||
#
|
||||
my ($self, $id, $old, $new) = @_;
|
||||
|
||||
my @children = @{$self->children($id)};
|
||||
|
||||
my $new_escaped = $self->quote($new . '/');
|
||||
my $old_offset = length($old) + 2;
|
||||
my $set;
|
||||
if (lc $self->{connect}->{driver} eq 'mysql') {
|
||||
$set = "CONCAT($new_escaped, SUBSTRING(Full_Name, $old_offset))";
|
||||
}
|
||||
elsif (lc $self->{connect}->{driver} eq 'pg') {
|
||||
$set = "$new_escaped || SUBSTRING(Full_Name, $old_offset)";
|
||||
}
|
||||
elsif (lc $self->{connect}->{driver} eq 'odbc' or lc $self->{connect}->{driver} eq 'mssql') {
|
||||
$set = "$new_escaped + SUBSTRING(Full_Name, $old_offset, 255)";
|
||||
}
|
||||
elsif (lc $self->{connect}->{driver} eq 'oracle') {
|
||||
$set = "$new_escaped || SUBSTR(Full_Name, $old_offset)";
|
||||
}
|
||||
|
||||
if ($set) {
|
||||
$self->update(
|
||||
{ Full_Name => \$set },
|
||||
{ ID => \@children },
|
||||
{ GT_SQL_SKIP_CHECK => 1 }
|
||||
);
|
||||
}
|
||||
else {
|
||||
my $sth = $self->select(qw/ID Full_Name/ => { ID => \@children });
|
||||
while (my ($id, $full_name) = $sth->fetchrow) {
|
||||
$full_name =~ s/^\Q$old/$new/ or next;
|
||||
$self->update({ Full_Name => $full_name }, { ID => $id }, { GT_SQL_SKIP_CHECK => 1 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub can_modify {
|
||||
# -------------------------------------------------------------------
|
||||
# Returns 1 if a record can be modified, undef otherwise.
|
||||
#
|
||||
my ($self, $new, $orig) = @_;
|
||||
|
||||
# If the FatherID has changed, make sure the new father exists. If it's 0, then
|
||||
# it's the root category and we don't worry about it.
|
||||
if ($orig->{FatherID} != $new->{FatherID} or $orig->{Name} ne $new->{Name}) {
|
||||
if ($orig->{FatherID} != $new->{FatherID} and $new->{FatherID}) {
|
||||
$self->count({ ID => $new->{FatherID} }) or return $self->error('BADCATID', 'WARN', $new->{FatherID});
|
||||
}
|
||||
# Now make sure the new FatherID,Name doesn't exist as it must be unique.
|
||||
$self->count({ FatherID => $new->{FatherID}, Name => $new->{Name} }, GT::SQL::Condition->new(ID => '!=' => $orig->{ID})) and return $self->error('CATEXISTS', 'WARN', $new->{Name});
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub template_set {
|
||||
# -------------------------------------------------------------------
|
||||
# Return the value of template set to use for a given category.
|
||||
#
|
||||
my $self = shift;
|
||||
my $id = shift or return $self->error('BADARGS', 'FATAL', "Must pass category id to template_set");
|
||||
return '' unless (exists $self->{schema}->{cols}->{Category_Template});
|
||||
|
||||
return $self->{_template_cache}->{$id} if (exists $self->{_template_cache}->{$id});
|
||||
|
||||
# If this category has a template set, use it.
|
||||
my $cat_info = $self->select(Category_Template => { ID => $id })->fetchrow;
|
||||
|
||||
# Otherwise look at its parents.
|
||||
unless ($cat_info) {
|
||||
my $parents = $self->parents($id);
|
||||
for my $parent (@$parents) {
|
||||
$cat_info = $self->select(Category_Template => { ID => $parent })->fetchrow
|
||||
and last;
|
||||
}
|
||||
}
|
||||
$self->{_template_cache}->{$id} = $cat_info || '';
|
||||
return $self->{_template_cache}->{$id};
|
||||
}
|
||||
|
||||
sub parents {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Returns parent ID's given one or more category ID's. If called with a single
|
||||
# category ID, the return value is an array reference of the ID's of the
|
||||
# category's parents, from father => root. If called with an array reference
|
||||
# of category ID's, the return value is a hash reference of
|
||||
# (ID => [rootid ... parentid]) pairs, with one pair for each category.
|
||||
#
|
||||
my $self = shift;
|
||||
my $id = shift or return $self->error('BADARGS', 'FATAL', "No category id passed to parents");
|
||||
|
||||
my (%ret, @lookup);
|
||||
for (ref $id ? @$id : $id) {
|
||||
unless ($ret{$_} = $self->{_parent_cache}->{$_}) {
|
||||
push @lookup, $_;
|
||||
}
|
||||
}
|
||||
|
||||
if (@lookup) {
|
||||
my $parents = $self->tree->parent_ids(id => \@lookup, include_dist => 1);
|
||||
for (@lookup) {
|
||||
$ret{$_} = $self->{_parent_cache}->{$_} = [sort { $parents->{$_}->{$b} <=> $parents->{$_}->{$a} } keys %{$parents->{$_}}];
|
||||
}
|
||||
}
|
||||
return ref $id
|
||||
? \%ret
|
||||
: [reverse @{$ret{$id}}];
|
||||
}
|
||||
|
||||
sub children {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Exactly like parents(), except you get descendants rather than ancestors, and
|
||||
# you get them in shallowest => deepest.
|
||||
#
|
||||
my $self = shift;
|
||||
my $id = shift or return $self->error('BADARGS', 'FATAL', "No category id passed to children");
|
||||
|
||||
my (%ret, @lookup);
|
||||
for (ref $id ? @$id : $id) {
|
||||
unless ($ret{$_} = $self->{_child_cache}->{$_}) {
|
||||
push @lookup, $_;
|
||||
}
|
||||
}
|
||||
|
||||
if (@lookup) {
|
||||
my $children = $self->tree->child_ids(id => \@lookup, include_dist => 1);
|
||||
for (@lookup) {
|
||||
$ret{$_} = $self->{_child_cache}->{$_} = [sort { $children->{$_}->{$a} <=> $children->{$_}->{$b} } keys %{$children->{$_}}];
|
||||
}
|
||||
}
|
||||
return ref $id
|
||||
? \%ret
|
||||
: $ret{$id};
|
||||
}
|
||||
|
||||
sub suggestions {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Returns a list of suggested category names. Takes a name and optional limit.
|
||||
#
|
||||
my $self = shift;
|
||||
my $name = shift;
|
||||
$name =~ y/\r\n//d;
|
||||
$name =~ /\S/ or return [];
|
||||
|
||||
$self->select_options('LIMIT 10');
|
||||
return [$self->select(Full_Name => GT::SQL::Condition->new(Full_Name => LIKE => "%$name%"))->fetchall_list];
|
||||
}
|
||||
|
||||
sub link_count {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Change the Number_of_Links count by n for specified id, and all parents. You
|
||||
# can pass multiple ID's by passing an array reference for ID. You can pass
|
||||
# both multiple change values by passing a hash reference of (CHANGE => [ID,
|
||||
# ...]) pairs as the ID (the change value passed to the function will be
|
||||
# ignored). Note that Direct_Links counts are NOT changed.
|
||||
#
|
||||
my ($self, $id, $change) = @_;
|
||||
|
||||
my %id;
|
||||
if (!$id or ref $id eq 'ARRAY' and !@$id) {
|
||||
return;
|
||||
}
|
||||
elsif (ref $id eq 'HASH') {
|
||||
%id = %$id;
|
||||
}
|
||||
else {
|
||||
%id = ($change => ref $id ? $id : [$id]);
|
||||
}
|
||||
|
||||
my %final;
|
||||
while (my ($change, $id) = each %id) {
|
||||
for (@$id) {
|
||||
$final{$_} = ($final{$_} || 0) + $change;
|
||||
}
|
||||
my $parents = $self->tree->parent_ids(id => $id);
|
||||
for my $parent (keys %$parents) {
|
||||
for (@{$parents->{$parent}}) {
|
||||
$final{$_} += $change;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my %change;
|
||||
for (keys %final) {
|
||||
push @{$change{$final{$_}}}, $_;
|
||||
}
|
||||
|
||||
for (keys %change) {
|
||||
$self->update(
|
||||
{ Number_of_Links => \('Number_of_Links' . ($_ > 0 ? ' + ' : ' - ') . abs) },
|
||||
{ ID => $change{$_} },
|
||||
{ GT_SQL_SKIP_CHECK => 1, GT_SQL_SKIP_INDEX => 1 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
sub changed {
|
||||
# -------------------------------------------------------------------
|
||||
# Returns a statement handle that can be looped through to get a list
|
||||
# of changed categories.
|
||||
#
|
||||
Links::init_date();
|
||||
|
||||
my $self = shift;
|
||||
my $date = GT::Date::date_get(defined $_[0] ? $_[0] : time);
|
||||
my $sth = $self->select(GT::SQL::Condition->new(Timestmp => '>=' => $date ));
|
||||
return $sth;
|
||||
}
|
||||
|
||||
sub get_id_from_name {
|
||||
# -------------------------------------------------------------------
|
||||
# Returns the category id based on the name.
|
||||
#
|
||||
my ($self, $name) = @_;
|
||||
$name =~ y/\r\n//d;
|
||||
$name =~ /\S/ or return;
|
||||
|
||||
return $self->{_id_cache}->{$name} if exists $self->{_id_cache}->{$name};
|
||||
$self->{_id_cache}->{$name} = $self->select(ID => { Full_Name => $name })->fetchrow_array;
|
||||
return $self->{_id_cache}->{$name};
|
||||
}
|
||||
|
||||
sub get_name_from_id {
|
||||
# -------------------------------------------------------------------
|
||||
# Returns the category full name based on the id.
|
||||
#
|
||||
my ($self, $id) = @_;
|
||||
return $self->{_name_cache}->{$id} if exists $self->{_name_cache}->{$id};
|
||||
return $self->{_name_cache}->{$id} = $self->select(Full_Name => { ID => $id })->fetchrow;
|
||||
}
|
||||
|
||||
|
||||
sub as_url {
|
||||
# -------------------------------------------------------------------
|
||||
#
|
||||
my ($self, $name, $format) = @_;
|
||||
return $PLG->dispatch('category_as_url', sub { return $self->_as_url(@_) }, $name, $format);
|
||||
}
|
||||
|
||||
sub _as_url {
|
||||
# -------------------------------------------------------------------
|
||||
# Return the passed-in category name as a formatted category path, usable for
|
||||
# static templates.
|
||||
#
|
||||
my ($self, $name, $format) = @_;
|
||||
|
||||
my $cat = $self->select({ Full_Name => $name })->fetchrow_hashref
|
||||
or return $name;
|
||||
require Links::Tools;
|
||||
$format ||= $IN->param('d') ? $CFG->{build_category_dynamic} ? "%$CFG->{build_category_dynamic}%" : '' : $CFG->{build_category_format};
|
||||
$format ||= '%Full_Name%';
|
||||
if ($format eq '%Full_Name%' and ($IN->param('d') or $CFG->{build_format_compat})) {
|
||||
# Old Links SQL's (prior to configurable category naming) didn't
|
||||
# coalesce multiple _'s into a single _, and dynamic mode still depends
|
||||
# on that behaviour - so if the format is just Full_Name, mimic the old
|
||||
# behaviour.
|
||||
(my $ret = $cat->{Full_Name}) =~ y|a-zA-Z0-9_/-|_|c;
|
||||
return $ret;
|
||||
}
|
||||
if ($format =~ /%Full_ID%/) {
|
||||
$cat->{Full_ID} = join '/', (@{$self->tree->parent_ids(id => $cat->{ID})}, $cat->{ID});
|
||||
}
|
||||
return Links::Tools::parse_format(
|
||||
$format,
|
||||
%$cat,
|
||||
clean => 1
|
||||
);
|
||||
}
|
||||
|
||||
sub set_new {
|
||||
# -------------------------------------------------------------------
|
||||
# Sets the new flag for a given category id (or list).
|
||||
#
|
||||
my $self = shift;
|
||||
my @ids = ref $_[0] eq 'ARRAY' ? @{shift()} : shift;
|
||||
my $rel = $DB->table('Links', 'CatLinks', 'Category');
|
||||
for my $id (@ids) {
|
||||
my $parents = $self->parents($id);
|
||||
my @pids = reverse @$parents;
|
||||
push @pids, $id;
|
||||
|
||||
for my $pid (@pids) {
|
||||
my $children = $self->children($pid);
|
||||
$rel->select_options('GROUP BY Add_Date');
|
||||
my $sth = $rel->select(qw/MAX(Add_Date) isNew/ => GT::SQL::Condition->new(
|
||||
CategoryID => '=' => [$pid, @$children],
|
||||
VIEWABLE
|
||||
));
|
||||
my ($newest, $new) = $sth->fetchrow;
|
||||
$self->update(
|
||||
{ Has_New_Links => $new || 'No', Newest_Link => $newest },
|
||||
{ ID => $pid },
|
||||
{ GT_SQL_SKIP_CHECK => 1, GT_SQL_SKIP_INDEX => 1 }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub _clear_cache {
|
||||
# -------------------------------------------------------------------
|
||||
# Clear out cache results whenever a category is added/deleted/changed.
|
||||
#
|
||||
my $self = shift;
|
||||
delete @$self{qw{_parent_cache _child_cache _name_cache _id_cache _template_cache}};
|
||||
return 1;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,41 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: ClickTrack.pm,v 1.3 2009/05/08 19:56:50 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
#
|
||||
# ClickTrack is subclassed so that new() is wrapped to handle ClickTrack table
|
||||
# cleanups - but only the first time a ClickTrack table object is created, and
|
||||
# only once / day.
|
||||
|
||||
package Links::Table::ClickTrack;
|
||||
|
||||
use strict;
|
||||
use Links qw/$CFG %STASH/;
|
||||
use GT::SQL::Table ();
|
||||
use vars qw/@ISA/;
|
||||
@ISA = 'GT::SQL::Table';
|
||||
|
||||
sub new {
|
||||
my $self = shift->SUPER::new(@_) or return;
|
||||
|
||||
return $self if $STASH{clicktrack_cleanup}++;
|
||||
Links::init_date();
|
||||
my $cleanup_date = GT::Date::date_get(time - 2*24*60*60, '%yyyy%-%mm%-%dd%');
|
||||
return $self if $CFG->{last_clicktrack_cleanup} and $cleanup_date eq $CFG->{last_clicktrack_cleanup};
|
||||
|
||||
$self->delete(GT::SQL::Condition->new(Created => '<' => $cleanup_date));
|
||||
$CFG->{last_clicktrack_cleanup} = $cleanup_date;
|
||||
$CFG->save;
|
||||
|
||||
$self;
|
||||
}
|
||||
|
||||
1;
|
||||
630
site/slowtwitch.com/cgi-bin/articles/admin/Links/Table/Links.pm
Normal file
630
site/slowtwitch.com/cgi-bin/articles/admin/Links/Table/Links.pm
Normal file
@@ -0,0 +1,630 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Links.pm,v 1.33 2009/05/11 05:57:45 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::Table::Links;
|
||||
# ==================================================================
|
||||
use strict;
|
||||
use Links qw/:payment :objects/;
|
||||
use GT::SQL;
|
||||
use GT::SQL::Table;
|
||||
use vars qw /@ISA $DEBUG $ERRORS $ERROR_MESSAGE $CATLINK/;
|
||||
|
||||
@ISA = qw/GT::SQL::Table/;
|
||||
$DEBUG = 0;
|
||||
$ERROR_MESSAGE = 'GT::SQL';
|
||||
|
||||
$ERRORS = {
|
||||
NOCATEGORY => "You did not specify a category for this link.",
|
||||
BADCATSUG => "There is no category with that name. Perhaps you meant: %s",
|
||||
BADCATEGORY => "Invalid Category '%s', it does not exist.",
|
||||
};
|
||||
|
||||
sub _query {
|
||||
# -------------------------------------------------------------------
|
||||
# Overrides the default query to allow searching on category values.
|
||||
#
|
||||
my $self = shift;
|
||||
my $opts = $self->common_param(@_) or return $self->fatal(BADARGS => '$obj->insert(HASH or HASH_REF or CGI) only.');
|
||||
|
||||
# Parse date/time
|
||||
if ($opts->{ExpiryDate} and $opts->{ExpiryDate} !~ /^\s*-?\d+\s*$/) {
|
||||
my $converted = Links::date_to_time($opts->{ExpiryDate});
|
||||
$opts->{ExpiryDate} = $converted if defined $converted;
|
||||
}
|
||||
my $cat_id = $opts->{'CatLinks.CategoryID'} or return $self->SUPER::_query($opts);
|
||||
$cat_id = $self->clean_category_ids($cat_id) or return;
|
||||
|
||||
# Strip out values that are empty or blank (as query is generally
|
||||
# derived from cgi input).
|
||||
my %input = map { $_ => $opts->{$_} } grep { defined $opts->{$_} and $opts->{$_} !~ /^\s*$/ } keys %$opts;
|
||||
$opts = \%input;
|
||||
|
||||
# Create a CatLinks,Links table to do the search.
|
||||
my $db = $DB->table('CatLinks','Links');
|
||||
|
||||
# Now start handling the search
|
||||
my $cond = $self->build_query_cond($opts, $self->{schema}->{cols});
|
||||
if ( (ref $cond) =~ /::sth/i ) {
|
||||
return $cond;
|
||||
}
|
||||
|
||||
# Set the limit clause, defaults to 25, set to -1 for none.
|
||||
my $in = $self->_get_search_opts($opts);
|
||||
my $offset = ($in->{nh} - 1) * $in->{mh};
|
||||
$db->select_options("ORDER BY $in->{sb} $in->{so}") if $in->{sb};
|
||||
$db->select_options("LIMIT $in->{mh} OFFSET $offset") unless $in->{mh} == -1;
|
||||
|
||||
# Add to the condition the category clause.
|
||||
my $final = new GT::SQL::Condition;
|
||||
$final->add($cond) if $cond;
|
||||
$final->add('CatLinks.CategoryID', 'IN', $cat_id);
|
||||
|
||||
# Now do the select.
|
||||
my @sel;
|
||||
push @sel, $final if $final;
|
||||
push @sel, $opts->{rs} if $opts->{rs} and $final;
|
||||
my $sth = $db->select(@sel) or return;
|
||||
$self->{last_hits} = $db->hits;
|
||||
return $sth;
|
||||
}
|
||||
|
||||
sub add {
|
||||
# -------------------------------------------------------------------
|
||||
# Adds a link, but passes through Plugins::Dispatch.
|
||||
#
|
||||
my $self = shift;
|
||||
my $p = (ref $_[0] eq 'HASH') ? shift : {@_};
|
||||
|
||||
$PLG->dispatch('add_link', sub { $self->_plg_add(@_) }, $p);
|
||||
}
|
||||
|
||||
sub _plg_add {
|
||||
# -------------------------------------------------------------------
|
||||
# Add a link.
|
||||
#
|
||||
my ($self, $p) = @_;
|
||||
|
||||
# Check to see if we can add a link, all errors get cascaded back.
|
||||
$p->{'CatLinks.CategoryID'} or return $self->warn('NOCATEGORY');
|
||||
$p->{'CatLinks.CategoryID'} = $self->clean_category_ids($p->{'CatLinks.CategoryID'}) or return;
|
||||
|
||||
$self->set_date_flags($p);
|
||||
|
||||
my $counted = ($p->{isValidated} eq 'Yes' and $p->{ExpiryDate} >= time);
|
||||
if ($p->{ExpiryDate} >= time) {
|
||||
$p->{ExpiryCounted} = 0;
|
||||
}
|
||||
else {
|
||||
$p->{ExpiryCounted} = 1;
|
||||
}
|
||||
|
||||
# Add the link, and return if there was an error, the error is propogated back.
|
||||
my $id = $self->SUPER::add($p) or return;
|
||||
|
||||
# Now add all the categories that the link belongs too.
|
||||
my $cat = $DB->table('Category');
|
||||
my $cat_lnk = $DB->table('CatLinks');
|
||||
|
||||
my @cat_ids = ref $p->{'CatLinks.CategoryID'} ? @{$p->{'CatLinks.CategoryID'}} : $p->{'CatLinks.CategoryID'};
|
||||
my %parents;
|
||||
|
||||
# Get a list of all the parents that this will affect.
|
||||
foreach my $cat_id (@cat_ids) {
|
||||
$cat_lnk->insert({ LinkID => $id, CategoryID => $cat_id }) or return;
|
||||
if ($counted) {
|
||||
for (@{$cat->parents($cat_id)}) { $parents{$_}++ }
|
||||
$parents{$cat_id}++;
|
||||
}
|
||||
}
|
||||
|
||||
# Now update those categories.
|
||||
if ($counted) {
|
||||
$cat->update(
|
||||
{ Newest_Link => $p->{Add_Date}, Has_New_Links => 'Yes', Number_of_Links => \"Number_of_Links + 1" },
|
||||
{ ID => [keys %parents] },
|
||||
{ GT_SQL_SKIP_CHECK => 1, GT_SQL_SKIP_INDEX => 1 }
|
||||
);
|
||||
$cat->update({ Direct_Links => \"Direct_Links + 1" }, { ID => \@cat_ids });
|
||||
}
|
||||
return $id;
|
||||
}
|
||||
|
||||
sub delete {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Deletes one or more links; there is a 'delete_link' hook below that can be
|
||||
# used by plugins.
|
||||
#
|
||||
my ($self, $where) = @_;
|
||||
if (not ref $where or ref $where eq 'ARRAY') {
|
||||
$where = { ID => $where };
|
||||
}
|
||||
return $self->fatal(BADARGS => 'Usage: $links->delete(condition)')
|
||||
unless (ref $where eq 'HASH' and keys %$where) or (UNIVERSAL::isa($where, 'GT::SQL::Condition') and $where->sql);
|
||||
|
||||
my $CatLinks = $DB->table('CatLinks');
|
||||
|
||||
# Sometimes { ID => x, CatLinks.CategoryID => y } gets passed in; it is
|
||||
# wrong - CatLinks->delete should be used instead, which will recall this
|
||||
# subroutine if any links need to be deleted.
|
||||
if (ref $where eq 'HASH' and $where->{ID} and not ref $where->{ID}
|
||||
and $where->{'CatLinks.CategoryID'} and not ref $where->{'CatLinks.CategoryID'}) {
|
||||
return $CatLinks->delete({ LinkID => $where->{ID}, CategoryID => $where->{'CatLinks.CategoryID'} });
|
||||
}
|
||||
|
||||
# Delete called with a normal condition
|
||||
my $links = $self->select(qw/ID isValidated Add_Date ExpiryDate ExpiryCounted/ => $where)->fetchall_hashref;
|
||||
@$links or return '0 but true';
|
||||
|
||||
my $new_cutoff = GT::Date::timelocal(0, 0, 0, (localtime time - $CFG->{build_new_cutoff})[3 .. 5]);
|
||||
my (@counts, @new);
|
||||
for (@$links) {
|
||||
my $add_time = GT::Date::timelocal(GT::Date::parse_format($_->{Add_Date}, GT::Date::FORMAT_DATE));
|
||||
if ($_->{isValidated} eq 'Yes' and ($_->{ExpiryDate} >= time or not $_->{ExpiryCounted})) {
|
||||
push @counts, $_->{ID};
|
||||
push @new, $_->{ID} if $add_time >= $new_cutoff;
|
||||
}
|
||||
}
|
||||
|
||||
# Figure out how much each category needs to be decremented
|
||||
$CatLinks->select_options("GROUP BY CategoryID");
|
||||
my %cats = $CatLinks->select(CategoryID => 'COUNT(*)' => { LinkID => \@counts })->fetchall_list;
|
||||
|
||||
my %change;
|
||||
while (my ($catid, $count) = each %cats) {
|
||||
push @{$change{-$count}}, $catid;
|
||||
}
|
||||
|
||||
my $ret;
|
||||
{
|
||||
# CatLinks, which has an fk to Links.ID, needs to know what we're
|
||||
# deleting so that it doesn't try to recall Links->delete
|
||||
local @Links::Table::CatLinks::DELETING;
|
||||
if ($PLG->active_plugins('delete_link')) {
|
||||
for (@$links) {
|
||||
@Links::Table::CatLinks::DELETING = $_->{ID};
|
||||
my $r = $PLG->dispatch('delete_link', sub { return $self->_plg_delete_link(@_) }, { ID => $_->{ID} });
|
||||
$ret += $r if defined $r;
|
||||
}
|
||||
$ret = '0 but true' if defined $ret and $ret == 0;
|
||||
}
|
||||
else {
|
||||
# delete_link plugin hook isn't being used, a single delete will do it
|
||||
my @lids = map $_->{ID}, @$links;
|
||||
@Links::Table::CatLinks::DELETING = @lids;
|
||||
$ret = $self->SUPER::delete({ ID => \@lids });
|
||||
}
|
||||
}
|
||||
|
||||
my $Category = $DB->table('Category');
|
||||
$Category->link_count(\%change);
|
||||
|
||||
while (my ($change, $ids) = each %change) {
|
||||
$Category->update({ Direct_Links => \("Direct_Links - " . abs $change) }, { ID => $ids });
|
||||
}
|
||||
|
||||
$CatLinks->select_options("GROUP BY CategoryID");
|
||||
my @new_cats = $CatLinks->select(CategoryID => { LinkID => \@new })->fetchall_list;
|
||||
# Now reset new flags on categories.
|
||||
if ($ret and @new_cats) {
|
||||
$Category->set_new(\@new_cats);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
sub _plg_delete_link {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Deletes a single link ID (plugin hook 'delete_link'. The second argument,
|
||||
# $link, will, for historic reasons, always be a hash reference containing an
|
||||
# 'ID' key, the value of which is the ID of the link to be deleted.
|
||||
#
|
||||
my ($self, $link) = @_;
|
||||
my $link_id = $link->{ID};
|
||||
|
||||
return $self->SUPER::delete({ ID => $link_id });
|
||||
}
|
||||
|
||||
sub modify {
|
||||
# -------------------------------------------------------------------
|
||||
# Modifies a link, but passes through the plugin system.
|
||||
#
|
||||
my ($self, $link) = @_;
|
||||
$PLG->dispatch('modify_link', sub { return $self->_plg_modify(@_) }, $link);
|
||||
}
|
||||
|
||||
sub _plg_modify {
|
||||
# -------------------------------------------------------------------
|
||||
# Modify a single link.
|
||||
#
|
||||
my $self = shift;
|
||||
my $set = shift or return $self->fatal(BADARGS => "Usage: \$cat->modify( { col => value ... } ).");
|
||||
my $id = $set->{ID} or return $self->fatal(BADARGS => "No primary key passed to modify!");
|
||||
|
||||
# Let's set the changed date to right now.
|
||||
Links::init_date();
|
||||
$set->{Mod_Date} = GT::Date::date_get();
|
||||
|
||||
# Force it to uncounted so that category counts will be properly updated
|
||||
$set->{ExpiryCounted} = 0;
|
||||
|
||||
# Check to see if we can modify (makes sure valid category id's were set).
|
||||
$set->{'CatLinks.CategoryID'} or return $self->warn('NOCATEGORY');
|
||||
$set->{'CatLinks.CategoryID'} = $self->clean_category_ids($set->{'CatLinks.CategoryID'}) or return;
|
||||
|
||||
$self->set_date_flags($set);
|
||||
|
||||
# Check to see if we are changing from not validated => validated.
|
||||
my ($old_validated, $old_expiry) = $self->select(qw/isValidated ExpiryDate/ => { ID => $set->{ID} })->fetchrow;
|
||||
|
||||
# Check that the ExpiryDate is valid for the categories the link is in.
|
||||
require Links::Payment;
|
||||
my $expiry = (exists $set->{ExpiryDate} and $set->{ExpiryDate}) ? $set->{ExpiryDate} : $old_expiry;
|
||||
$expiry = Links::Payment::check_expiry_date({ ExpiryDate => $expiry }, $set->{'CatLinks.CategoryID'});
|
||||
$set->{ExpiryDate} = $expiry if $expiry;
|
||||
|
||||
my $new_validated = exists $set->{isValidated} ? $set->{isValidated} : $old_validated;
|
||||
my $new_expiry = exists $set->{ExpiryDate} ? $set->{ExpiryDate} : $old_expiry;
|
||||
|
||||
my $was_counted = $old_validated eq 'Yes' && $old_expiry >= time;
|
||||
my $now_counted = $new_validated eq 'Yes' && $new_expiry >= time;
|
||||
|
||||
if (exists $set->{ExpiryDate}) {
|
||||
$set->{ExpiryCounted} = $set->{ExpiryDate} >= time ? 0 : 1;
|
||||
}
|
||||
|
||||
=for comment
|
||||
Here are the various cases that the category count update code needs to handle and what to do in those cases:
|
||||
|
||||
add the link to a new category
|
||||
was counted, now_counted increment new cat
|
||||
!was counted, now counted increment new cat
|
||||
was counted, !now counted nothing
|
||||
!was counted, !now counted nothing
|
||||
|
||||
remove the link from a category
|
||||
was counted, now_counted decrement old cat (CatLinks handles correctly)
|
||||
!was counted, now counted nothing (CatLinks handles incorrectly and decrements in some cases, we fix and increment)
|
||||
was counted, !now counted decrement old cat (CatLinks handles correctly)
|
||||
!was counted, !now counted nothing (CatLinks handles correctly)
|
||||
|
||||
no category changes
|
||||
was counted, now_counted nothing
|
||||
!was counted, now counted increment cats
|
||||
was counted, !now counted decrement cats
|
||||
!was counted, !now counted nothing
|
||||
|
||||
the above combined (what the code needs to do)
|
||||
was counted, now_counted increment new cats
|
||||
!was counted, now counted increment curr cats, leave removed cats
|
||||
was counted, !now counted decrement cats except removed and new cats (ie. decrement curr cats, except new cats)
|
||||
!was counted, !now counted nothing
|
||||
=cut
|
||||
|
||||
# Do the update.
|
||||
my $ret = $self->SUPER::modify($set);
|
||||
# Check to see if the link has been moved into another category.
|
||||
if ($ret) {
|
||||
my $cat_lnk = $DB->table('CatLinks');
|
||||
my %orig_ids = map { $_ => 1 } $cat_lnk->select(CategoryID => { LinkID => $id })->fetchall_list;
|
||||
my %cat_ids = map { $_ => 1 } ref $set->{'CatLinks.CategoryID'} ? @{$set->{'CatLinks.CategoryID'}} : $set->{'CatLinks.CategoryID'};
|
||||
|
||||
# Categories that the link has just been added to
|
||||
my @new_cats = grep !$orig_ids{$_}, keys %cat_ids;
|
||||
# Categories that the link has just been removed from
|
||||
my @old_cats = grep !$cat_ids{$_}, keys %orig_ids;
|
||||
|
||||
my %link_adjustment;
|
||||
my $Category = $DB->table('Category');
|
||||
|
||||
# CatLinks doesn't update category counts on insert, so it's done further down in the code
|
||||
if (@new_cats) {
|
||||
$cat_lnk->insert_multiple([qw/LinkID CategoryID/], map [$id, $_], @new_cats);
|
||||
}
|
||||
|
||||
# However, deleting from CatLinks does result in updated category counts
|
||||
if (@old_cats) {
|
||||
$cat_lnk->delete({ LinkID => $id, CategoryID => \@old_cats });
|
||||
|
||||
# If the link has been modified from isValidated = No to Yes then the delete()
|
||||
# from CatLinks will end up incorrectly decrementing the category count. If
|
||||
# this is the case, then the count needs to increment to comphensate for this
|
||||
# bug. This isn't !$was_counted && $now_counted because CatLinks delete
|
||||
# currently does not take ExpiryDate into consideration.
|
||||
push @{$link_adjustment{1}}, @old_cats if $old_validated eq 'No' and $new_validated eq 'Yes';
|
||||
}
|
||||
|
||||
# The status hasn't changed: increment the new categories
|
||||
if ($was_counted and $now_counted) {
|
||||
push @{$link_adjustment{1}}, @new_cats if @new_cats;
|
||||
}
|
||||
# It wasn't viewable, but is now: increment all the current categories
|
||||
elsif (not $was_counted and $now_counted) {
|
||||
push @{$link_adjustment{1}}, keys %cat_ids;
|
||||
}
|
||||
# Was viewable, but now isn't: decrement all the current categories (except new ones)
|
||||
elsif ($was_counted and not $now_counted) {
|
||||
# Don't decrement counts on new categories, since the addition of the link
|
||||
# never incremented the count in the first place
|
||||
my %not_new = %cat_ids;
|
||||
for (@new_cats) {
|
||||
delete $not_new{$_};
|
||||
}
|
||||
push @{$link_adjustment{-1}}, keys %not_new;
|
||||
}
|
||||
# Otherwise, it wasn't visible and still isn't, or it was visible but now
|
||||
# isn't. In both cases, the new categories don't need to be incremented.
|
||||
|
||||
# Actually adjust the link counts:
|
||||
$Category->link_count(\%link_adjustment);
|
||||
|
||||
while (my ($change, $ids) = each %link_adjustment) {
|
||||
$Category->update({ Direct_Links => \("Direct_Links" . ($change > 0 ? ' + ' : ' - ') . abs $change) }, { ID => $ids });
|
||||
}
|
||||
|
||||
# If this link is now validated this link, let's update category counters and new flags.
|
||||
# It also needs to be updated if a link has been added to new categories.
|
||||
if ((not $was_counted and $now_counted) or @new_cats) {
|
||||
foreach my $cat (keys %cat_ids) {
|
||||
my @cats = ($cat, @{$Category->parents($cat)});
|
||||
my $cond = GT::SQL::Condition->new(ID => '=', \@cats);
|
||||
if ($set->{isNew} eq 'Yes') {
|
||||
$Category->update({ Has_New_Links => 'Yes' }, $cond, { GT_SQL_SKIP_CHECK => 1, GT_SQL_SKIP_INDEX => 1 });
|
||||
}
|
||||
$cond->add('Newest_Link', '<', $set->{Add_Date});
|
||||
$Category->update({ Newest_Link => $set->{Add_Date} }, $cond, { GT_SQL_SKIP_CHECK => 1, GT_SQL_SKIP_INDEX => 1 });
|
||||
}
|
||||
}
|
||||
|
||||
# Update the category timestamps to let people know that the page has changed.
|
||||
$Category->update({ Timestmp => \"NOW()" }, { ID => [keys %cat_ids, @old_cats] }, { GT_SQL_SKIP_CHECK => 1, GT_SQL_SKIP_INDEX => 1 });
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
sub update {
|
||||
# -------------------------------------------------------------------
|
||||
# Update a link.
|
||||
#
|
||||
my ($self, $set, $where) = @_;
|
||||
|
||||
my $ret = $self->SUPER::update($set, $where);
|
||||
|
||||
# Update the Category Timestmp of links which have certain columns updated
|
||||
for (split(/\s*,\s*/, $CFG->{links_cols_update_category})) {
|
||||
if (exists $set->{$_}) {
|
||||
my @cats = $DB->table('Links', 'CatLinks')->select('CategoryID', $where)->fetchall_list;
|
||||
$DB->table('Category')->update({ Timestmp => \'NOW()' }, { ID => \@cats });
|
||||
last;
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
sub detailed_url {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Takes one or more link ID's, returns one or more parsed detailed URL/paths in
|
||||
# the same order and position the links were passed in, NOT prefixed with
|
||||
# build_detail_url/build_detail_path. If the ID passed in is actually a
|
||||
# hashref, it is assumed that this hash ref includes a full set of Links and
|
||||
# Category values for the link.
|
||||
#
|
||||
my ($self, @ids) = @_;
|
||||
|
||||
my (@links, @sel_links, $need_select);
|
||||
for (@ids) {
|
||||
if (ref) {
|
||||
push @links, $_;
|
||||
push @sel_links, undef;
|
||||
}
|
||||
else {
|
||||
push @links, undef;
|
||||
push @sel_links, $_;
|
||||
$need_select++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($need_select) {
|
||||
my %links_cols = %{$self->cols};
|
||||
# Only select Category columns that don't conflict with Links columns.
|
||||
my @cat_cols = grep !$links_cols{$_}, keys %{$DB->table('Category')->cols};
|
||||
|
||||
my $rel = $DB->table(qw/Links CatLinks Category/);
|
||||
my %links = map { $_->{ID} => $_ } @{$rel->select(
|
||||
'Links.*', @cat_cols, 'CategoryID', { LinkID => [grep $_, @sel_links] }
|
||||
)->fetchall_hashref};
|
||||
|
||||
for my $i (0 .. $#sel_links) {
|
||||
$links[$i] = $links{$sel_links[$i]} if $sel_links[$i];
|
||||
}
|
||||
}
|
||||
|
||||
require Links::Tools;
|
||||
my $format;
|
||||
$format = $CFG->{build_detail_format} unless $IN->param('d');
|
||||
$format ||= '%ID%';
|
||||
$format .= '_%ID%' unless $format =~ /%ID%/;
|
||||
my @ret = $PLG->dispatch('detailed_url', sub {
|
||||
my ($format, @links) = @_;
|
||||
my @ret;
|
||||
for (@links) {
|
||||
my $parsed;
|
||||
if ($_) {
|
||||
# Make Full_Name act the same for both category and detailed urls. Set
|
||||
# build_format_compat = 2 if you want the < 3.3 behaviour of coalesced _'s for
|
||||
# Full_Name.
|
||||
if ($CFG->{build_format_compat} == 1) {
|
||||
(my $fn = $_->{Full_Name}) =~ y|a-zA-Z0-9_/-|_|c;
|
||||
$format =~ s/%Full_Name%/$fn/g;
|
||||
}
|
||||
|
||||
$parsed = Links::Tools::parse_format(
|
||||
$format,
|
||||
%$_,
|
||||
clean => 1
|
||||
);
|
||||
$parsed =~ s{(^|[/\\])index$}{${1}_index};
|
||||
$parsed .= $CFG->{build_extension};
|
||||
}
|
||||
push @ret, $parsed;
|
||||
}
|
||||
return @ret;
|
||||
}, $format, @links);
|
||||
return wantarray ? @ret : $ret[0];
|
||||
}
|
||||
|
||||
sub category_detailed_url {
|
||||
# -----------------------------------------------------------------------------
|
||||
# A wrapper to detailed_url which will return url's which given a category id,
|
||||
# will only return url's which take the category into consideration. The only
|
||||
# use for this is when a link is in multiple categories.
|
||||
#
|
||||
my ($self, $cat_id, @ids) = @_;
|
||||
|
||||
my %links_cols = %{$self->cols};
|
||||
# Only select Category columns that don't conflict with Links columns.
|
||||
my @cat_cols = grep !$links_cols{$_}, keys %{$DB->table('Category')->cols};
|
||||
|
||||
my @links;
|
||||
my $rel = $DB->table(qw/Links CatLinks Category/);
|
||||
for (@ids) {
|
||||
push @links, $rel->select('Links.*', @cat_cols, 'CategoryID', { LinkID => $_, CategoryID => $cat_id })->fetchrow_hashref;
|
||||
}
|
||||
my @ret = $self->detailed_url(@links);
|
||||
return wantarray ? @ret : $ret[0];
|
||||
}
|
||||
|
||||
sub clean_category_ids {
|
||||
# -------------------------------------------------------------------
|
||||
# Takes an argument which could be a list of category names or ids
|
||||
# and returns an array ref of ids.
|
||||
#
|
||||
my ($self, $arg) = @_;
|
||||
my $cat = $DB->table('Category');
|
||||
|
||||
# Fix up Category Names => Id numbers and offer suggestions
|
||||
# if name was not found.
|
||||
if (! ref $arg and $arg !~ /^\d*$/) {
|
||||
my @cat_names = split /\n\r?/, $arg;
|
||||
my @cat_ids = ();
|
||||
foreach my $name (@cat_names) {
|
||||
$name =~ s/[\r\n]//g; # Textareas have a nasty habit of putting \r's on the results.
|
||||
my $id = ($name =~ /^\d+$/) ? $name : $cat->get_id_from_name($name);
|
||||
if ($id) {
|
||||
push(@cat_ids, $id);
|
||||
}
|
||||
else {
|
||||
my $names = $cat->suggestions($name);
|
||||
return $self->error(@$names
|
||||
? ('BADCATSUG', 'WARN', "<ul>" . join('', map "<li>$_</li>", @$names) . "</ul>")
|
||||
: ('BADCATEGORY', 'WARN', $name)
|
||||
);
|
||||
}
|
||||
}
|
||||
return \@cat_ids;
|
||||
}
|
||||
# We assume that if ID numbers are passed in, that they will
|
||||
# be correct. This will get checked anyways by GT::SQL::Table,
|
||||
# so no point doing it twice.
|
||||
else {
|
||||
my @ids = ref $arg ? @$arg : ($arg);
|
||||
return \@ids;
|
||||
}
|
||||
}
|
||||
|
||||
sub get_categories {
|
||||
# -------------------------------------------------------------------
|
||||
# Takes a link id and returns a hash of category id => category name.
|
||||
#
|
||||
my $self = shift;
|
||||
my $id = shift;
|
||||
my $db = $DB->table('Category', 'CatLinks');
|
||||
my $sth = $db->select( { 'CatLinks.LinkID' => $id }, [ 'Category.ID', 'Category.Full_Name' ] );
|
||||
my %res = ();
|
||||
while (my ($id, $name) = $sth->fetchrow_array) {
|
||||
$res{$id} = $name;
|
||||
}
|
||||
return \%res;
|
||||
}
|
||||
|
||||
sub set_date_flags {
|
||||
# -------------------------------------------------------------------
|
||||
# Takes a link hash ref and sets the date flags properly.
|
||||
#
|
||||
my ($self, $p) = @_;
|
||||
|
||||
Links::init_date();
|
||||
my $today = GT::Date::date_get();
|
||||
if (GT::Date::date_diff($today, $p->{Add_Date}) <= $CFG->{build_new_cutoff}) {
|
||||
$p->{isNew} = 'Yes';
|
||||
$p->{isChanged} = 'No';
|
||||
}
|
||||
elsif (GT::Date::date_diff($today, $p->{Mod_Date}) <= $CFG->{build_new_cutoff}) {
|
||||
$p->{isChanged} = 'Yes';
|
||||
$p->{isNew} = 'No';
|
||||
}
|
||||
else {
|
||||
$p->{isNew} = 'No';
|
||||
$p->{isChanged} = 'No';
|
||||
}
|
||||
|
||||
# Possible ExpiryDate values that have to be handled here:
|
||||
# -1 (unpaid link) - leave it as is, does not need to be converted
|
||||
# \d (unixtime) - leave it as is, does not need to be converted
|
||||
# >=\d (doesn't actually occur here, but in _query) - leave it as is, does not need to be converted
|
||||
# YYYY-MM-DD
|
||||
# YYYY/MM/DD
|
||||
# YYYY/MM/DD HH::MM::SS
|
||||
# The purpose of this bit of code is to convert any human readable dates into
|
||||
# unixtime and leave everything else as is.
|
||||
if ($p->{ExpiryDate} and $p->{ExpiryDate} !~ /^\s*-?\d+\s*$/) {
|
||||
my $converted = Links::date_to_time($p->{ExpiryDate});
|
||||
$p->{ExpiryDate} = $converted if defined $converted;
|
||||
}
|
||||
}
|
||||
|
||||
sub add_reviews {
|
||||
# -------------------------------------------------------------------
|
||||
# Adds review information, but passes through the plugin system.
|
||||
#
|
||||
my ($self, $link) = @_;
|
||||
$PLG->dispatch('add_reviews', sub { return $self->_plg_add_reviews(@_) }, $link);
|
||||
}
|
||||
|
||||
sub _plg_add_reviews {
|
||||
# -------------------------------------------------------------------
|
||||
# Adds review information to an array ref of hash refs of links passed in
|
||||
# in one query.
|
||||
#
|
||||
my $self = shift;
|
||||
my $links = shift;
|
||||
if (ref $links eq 'HASH') {
|
||||
$links = [ $links ];
|
||||
}
|
||||
my $review_db = $DB->table('Reviews');
|
||||
my @ids = map { $_->{ID} } @$links;
|
||||
return unless (@ids);
|
||||
|
||||
my $sth = $review_db->select({ Review_Validated => 'Yes' }, { Review_LinkID => \@ids });
|
||||
my %reviews;
|
||||
my %review_count;
|
||||
while (my $review = $sth->fetchrow_hashref) {
|
||||
push @{$reviews{$review->{Review_LinkID}}}, $review;
|
||||
$review_count{$review->{Review_LinkID}}++;
|
||||
}
|
||||
for my $link (@$links) {
|
||||
$link->{Review_Count} = $review_count{$link->{ID}};
|
||||
$link->{Review_Loop} = $reviews{$link->{ID}};
|
||||
}
|
||||
return $links;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,93 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Reviews.pm,v 1.1 2007/11/16 07:15:00 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2007 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::Table::Reviews;
|
||||
# ==================================================================
|
||||
use strict;
|
||||
use Links qw/:objects/;
|
||||
use GT::SQL;
|
||||
use GT::SQL::Table;
|
||||
use vars qw/@ISA $ERROR_MESSAGE/;
|
||||
|
||||
@ISA = qw/GT::SQL::Table/;
|
||||
$ERROR_MESSAGE = 'GT::SQL';
|
||||
|
||||
sub add {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Add a review.
|
||||
#
|
||||
my $self = shift;
|
||||
my $rec = (ref $_[0] eq 'HASH') ? shift : { @_ };
|
||||
|
||||
my $id = $self->SUPER::add($rec) or return;
|
||||
|
||||
# Update the link/category timestamp if the review is validated.
|
||||
_update_timestamp($rec->{Review_LinkID}) if $rec->{Review_Validated} eq 'Yes';
|
||||
|
||||
$id;
|
||||
}
|
||||
|
||||
sub modify {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Modify a review.
|
||||
#
|
||||
my $self = shift;
|
||||
my $set = shift or return $self->fatal(BADARGS => 'Usage: $reviews->modify({ col => value ... }).');
|
||||
my $id = $set->{ReviewID} or return $self->fatal(BADARGS => 'No primary key passed to modify!');
|
||||
|
||||
my ($old, $link_id) = $self->select('Review_Validated', 'Review_LinkID', { ReviewID => $id })->fetchrow;
|
||||
|
||||
my $ret = $self->SUPER::modify($set) or return;
|
||||
|
||||
# Only update the timestamp if it was unvalidated and still is - this is the
|
||||
# only case where the pages shouldn't be rebuilt.
|
||||
my $new = $set->{Review_Validated} || $old;
|
||||
_update_timestamp($link_id) unless $old eq 'No' and $new eq 'No';
|
||||
|
||||
$ret;
|
||||
}
|
||||
|
||||
sub delete {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Delete one or more reviews.
|
||||
#
|
||||
my ($self, $cond) = @_;
|
||||
ref $cond or return $self->fatal(BADARGS => '$reviews->delete(condition)');
|
||||
|
||||
# Get the link ids of the reviews that are about to be deleted and are
|
||||
# validated (as only those pages need to be rebuilt).
|
||||
my @link_ids = $self->select('Review_LinkID', $cond, { Review_Validated => 'Yes' })->fetchall_list;
|
||||
|
||||
my $ret = $self->SUPER::delete($cond) or return;
|
||||
|
||||
_update_timestamp(\@link_ids) if @link_ids;
|
||||
|
||||
$ret;
|
||||
}
|
||||
|
||||
sub _update_timestamp {
|
||||
# -----------------------------------------------------------------------------
|
||||
# Given a link ID (or an array ref if you want to update more than one link),
|
||||
# update the Timestmp columns of the link as well as all the categories that
|
||||
# the link is in. This ensures that these pages will be rebuilt on "Build
|
||||
# Changed".
|
||||
#
|
||||
my $link_id = shift;
|
||||
return unless $link_id;
|
||||
|
||||
my @cats = $DB->table('Links', 'CatLinks')->select('CategoryID', { LinkID => $link_id })->fetchall_list;
|
||||
$DB->table('Category')->update({ Timestmp => \'NOW()' }, { ID => \@cats }) if @cats;
|
||||
$DB->table('Links')->update({ Timestmp => \'NOW()' }, { ID => $link_id });
|
||||
}
|
||||
|
||||
1;
|
||||
162
site/slowtwitch.com/cgi-bin/articles/admin/Links/Table/Users.pm
Normal file
162
site/slowtwitch.com/cgi-bin/articles/admin/Links/Table/Users.pm
Normal file
@@ -0,0 +1,162 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Users.pm,v 1.5 2005/05/12 20:51:24 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::Table::Users;
|
||||
# ==================================================================
|
||||
use strict;
|
||||
use GT::SQL;
|
||||
use GT::SQL::Table;
|
||||
use Links qw/$CFG $PLG/;
|
||||
use vars qw/@ISA $ERRORS $ERROR_MESSAGE $AUTH/;
|
||||
|
||||
@ISA = qw/GT::SQL::Table/;
|
||||
$ERROR_MESSAGE = 'GT::SQL';
|
||||
|
||||
$ERRORS = {
|
||||
AUTHERROR => "Authentication Error: %s",
|
||||
INVALIDFORMAT => "Invalid format for username: %s"
|
||||
};
|
||||
|
||||
sub init {
|
||||
# -------------------------------------------------------------------
|
||||
# Load the authentication module.
|
||||
#
|
||||
require Links::Authenticate;
|
||||
Links::Authenticate::auth('init', {});
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
sub add {
|
||||
# -------------------------------------------------------------------
|
||||
my ($self, @args) = @_;
|
||||
return $PLG->dispatch('add_user', sub { return $self->_plg_add(@_); }, @args );
|
||||
}
|
||||
|
||||
sub _plg_add {
|
||||
# -------------------------------------------------------------------
|
||||
init();
|
||||
my $self = shift;
|
||||
my $p = ref $_[0] eq 'HASH' ? shift : {@_};
|
||||
|
||||
if (! Links::Authenticate::auth('valid_format', { Username => $p->{Username} })) {
|
||||
$ERRORS->{INVALIDFORMAT} = Links::language('USER_INVALIDUSERNAME');
|
||||
return $self->error('INVALIDFORMAT', 'WARN', $p->{Username});
|
||||
}
|
||||
|
||||
my $h = Links::Authenticate::auth('add_user', { Username => $p->{Username}, Password => $p->{Password} });
|
||||
unless ($h) {
|
||||
$ERRORS->{AUTHERROR} = Links::language('USER_AUTHERROR');
|
||||
return $self->error('AUTHERROR', 'WARN', $Links::Authenticate::error);
|
||||
}
|
||||
|
||||
$p->{Username} = $h->{Username};
|
||||
$p->{Password} = $h->{Password};
|
||||
|
||||
return $self->SUPER::add($p);
|
||||
}
|
||||
|
||||
sub delete {
|
||||
# -------------------------------------------------------------------
|
||||
my ($self, @args) = @_;
|
||||
return $PLG->dispatch('delete_user', sub { return $self->_plg_delete(@_); }, @args );
|
||||
}
|
||||
|
||||
sub _plg_delete {
|
||||
# -------------------------------------------------------------------
|
||||
init();
|
||||
my ($self, $cond) = @_;
|
||||
if (! ref $cond) {
|
||||
$cond = { Username => $cond };
|
||||
}
|
||||
my $count = 0;
|
||||
my $link_db = $Links::DB->table('Links');
|
||||
my $sth = $self->select('Username', $cond);
|
||||
while (my ($user) = $sth->fetchrow_array) {
|
||||
my @links = $link_db->select('ID', { LinkOwner => $user })->fetchall_list;
|
||||
for my $link_id (@links) {
|
||||
$link_db->delete($link_id);
|
||||
}
|
||||
if (Links::Authenticate::auth('del_user', { Username => $user })) {
|
||||
my $ret = $self->SUPER::delete($user);
|
||||
$count++ if $ret;
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
sub modify {
|
||||
# -------------------------------------------------------------------
|
||||
my ($self, @args) = @_;
|
||||
return $PLG->dispatch('modify_user', sub { return $self->_plg_modify(@_); }, @args );
|
||||
}
|
||||
|
||||
sub _plg_modify {
|
||||
# -------------------------------------------------------------------
|
||||
init();
|
||||
my $self = shift;
|
||||
my $input = $self->common_param(@_) or return $self->error('BADARGS', 'FATAL', '$obj->insert(HASH or HASH_REF or CGI) only.');
|
||||
my $id = $input->{Username} or return $self->error("BADARGS", "FATAL", "No primary key passed to modify!");
|
||||
my $sth = $self->select('Username', 'Password', { Username => $id });
|
||||
my $rec = $sth->fetchrow_hashref;
|
||||
if ($rec) {
|
||||
if ($input->{Password} ne $rec->{Password}) {
|
||||
Links::Authenticate::auth('change_pass', { Username => $rec->{Username}, Password => $rec->{Password}, New_Password => $input->{Password} })
|
||||
or return $self->error('AUTHERROR', 'WARN', $Links::Authenticate::error);
|
||||
}
|
||||
}
|
||||
|
||||
# Connect to the database if we are not already connected
|
||||
$self->connect;
|
||||
|
||||
# Copy the data and remove anything that doesn't make sense here.
|
||||
my $c = $self->{schema}->{cols};
|
||||
my $set = {};
|
||||
for (keys %$c) {
|
||||
$set->{$_} = $input->{$_} if exists $input->{$_};
|
||||
}
|
||||
|
||||
# Remove primary keys from update clause.
|
||||
my $where;
|
||||
if ($input->{orig_username}) {
|
||||
$where->{Username} = $input->{orig_username};
|
||||
}
|
||||
else {
|
||||
foreach my $key (@{$self->{schema}->{pk}}) {
|
||||
$where->{$key} = delete $set->{$key} if exists $set->{$key};
|
||||
}
|
||||
}
|
||||
return $self->error("NOPKTOMOD", "WARN") unless keys %$where == @{$self->{schema}->{pk}};
|
||||
|
||||
# Remove timestamps - no sense updating.
|
||||
$self->_check_timestamp($where, $set) or return;
|
||||
foreach my $col (keys %$c) {
|
||||
delete $set->{$col} if $c->{$col}->{type} eq 'TIMESTAMP';
|
||||
}
|
||||
|
||||
# Execute the update
|
||||
$self->update($set, $where) or return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub random_pass {
|
||||
# -------------------------------------------------------------------
|
||||
# Returns a random password.
|
||||
#
|
||||
my $self = shift;
|
||||
my $pass = '';
|
||||
for (1 .. 8) { $pass .= chr(65 + int rand 57); }
|
||||
return $pass;
|
||||
}
|
||||
|
||||
1;
|
||||
1736
site/slowtwitch.com/cgi-bin/articles/admin/Links/Tools.pm
Normal file
1736
site/slowtwitch.com/cgi-bin/articles/admin/Links/Tools.pm
Normal file
File diff suppressed because it is too large
Load Diff
287
site/slowtwitch.com/cgi-bin/articles/admin/Links/Update.pm
Normal file
287
site/slowtwitch.com/cgi-bin/articles/admin/Links/Update.pm
Normal file
@@ -0,0 +1,287 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Update.pm,v 1.11 2009/05/08 19:56:50 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
#
|
||||
|
||||
package Links::Update;
|
||||
use strict;
|
||||
use Links qw/$CFG $IN %STASH/;
|
||||
use GT::Update qw/:severity/;
|
||||
use GT::File::Tools qw/basename/;
|
||||
use GT::Config;
|
||||
use constant CACHE_TIMEOUT => 5*60; # Only check the server at most once every 5 minutes
|
||||
|
||||
sub _updater {
|
||||
$STASH{updates} ||= GT::Config->load("$CFG->{admin_root_path}/Links/Config/Updates.pm", { debug => $CFG->{debug_level} });
|
||||
return $STASH{updater} if $STASH{updater};
|
||||
(my $cgi_path = $CFG->{admin_root_path}) =~ s{[\\/]+admin[\\/]*$}//;
|
||||
$STASH{updater} = GT::Update->new(
|
||||
product => 'Links',
|
||||
version => $CFG->{version},
|
||||
reg_number => $CFG->{reg_number},
|
||||
init_path => $CFG->{admin_root_path},
|
||||
perl_path => $CFG->{path_to_perl},
|
||||
backup_path => "$CFG->{admin_root_path}/updates",
|
||||
paths => {
|
||||
script => {
|
||||
cgi => $cgi_path,
|
||||
admin => $CFG->{admin_root_path}
|
||||
},
|
||||
library => $CFG->{admin_root_path},
|
||||
template => $CFG->{admin_root_path} . '/templates',
|
||||
static => {
|
||||
static => $CFG->{build_static_path},
|
||||
},
|
||||
fixed => {
|
||||
static => $CFG->{build_static_path},
|
||||
cool => $CFG->{build_cool_path},
|
||||
detail => $CFG->{build_detail_path},
|
||||
new => $CFG->{build_new_path},
|
||||
ratings => $CFG->{build_ratings_path},
|
||||
build => $CFG->{build_root_path},
|
||||
},
|
||||
version => $CFG->{admin_root_path}
|
||||
},
|
||||
replacements => {
|
||||
library => {
|
||||
'' => {
|
||||
'Links.pm' => {
|
||||
'<%VERSION%>' => $CFG->{version}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
installed => ($STASH{updates}->{installed} ||= {}),
|
||||
testing => $STASH{updates}->{testing}
|
||||
);
|
||||
}
|
||||
|
||||
sub check {
|
||||
my $updater = _updater;
|
||||
my ($cached, @updates);
|
||||
if (my $cache = $STASH{updates}->{cache} and !$STASH{updates}->{testing}) {
|
||||
if ($cache->{version} == $GT::Update::VERSION and $cache->{time} > time - CACHE_TIMEOUT) { # Only check at most once every 5 minutes
|
||||
@updates = @{$cache->{updates}};
|
||||
$cached = 1;
|
||||
}
|
||||
}
|
||||
unless ($cached) {
|
||||
@updates = $updater->check;
|
||||
|
||||
if (@updates == 1 and not defined $updates[0]) {
|
||||
my $error = $updater->error;
|
||||
my ($error_code, $error_message) = $error =~ /error code: (\d{3})\s*(.*)/;
|
||||
return { error => $error, update_error_code => $error_code, update_error_message => $error_message };
|
||||
}
|
||||
|
||||
$STASH{updates}->{cache} = { time => time, version => $GT::Update::VERSION, updates => \@updates };
|
||||
$STASH{updates}->save;
|
||||
}
|
||||
|
||||
my %ret;
|
||||
my %available = map { $_->id => $_ } @updates;
|
||||
for my $update (@updates) {
|
||||
my $id = $update->id;
|
||||
my $severity = $update->severity;
|
||||
my $update_type = $severity == CRITICAL ? 'critical' : $severity == RECOMMENDED ? 'recommended' : $severity == VERSION ? 'version' : 'optional';
|
||||
my $info = {
|
||||
id => $id,
|
||||
title => $update->title,
|
||||
description => \($update->description),
|
||||
severity => $severity,
|
||||
files => [$update->files],
|
||||
reversible => $update->reversible,
|
||||
unique => $update->unique,
|
||||
deps => [$update->deps],
|
||||
revdeps => [$update->revdeps],
|
||||
requires => [$update->requires],
|
||||
deps_first => $update->deps_first,
|
||||
update_type => $update_type,
|
||||
installed => $update->installed
|
||||
};
|
||||
push @{$ret{$update_type}}, $info;
|
||||
$ret{update}->{$id} = $info;
|
||||
}
|
||||
for (sort { $a <=> $b } keys %{$STASH{updates}->{installed}->{$CFG->{version}}}) {
|
||||
next if $available{$_};
|
||||
my %info = %{$STASH{updates}->{installed}->{$CFG->{version}}->{$_}};
|
||||
$info{id} = $_;
|
||||
my $severity = $info{severity};
|
||||
my $update_type = $severity == CRITICAL ? 'critical' : $severity == RECOMMENDED ? 'recommended' : $severity == VERSION ? 'version' : 'optional';
|
||||
push @{$ret{$update_type}}, \%info;
|
||||
}
|
||||
|
||||
for (qw/critical recommended optional version/) {
|
||||
$ret{"${_}_total"} = @{$ret{$_} ||= []};
|
||||
$ret{"${_}_installed"} = $ret{"${_}_installable"} = 0;
|
||||
for my $update (@{$ret{$_}}) {
|
||||
next unless $available{$update->{id}};
|
||||
if ($available{$update->{id}}->{installed}) {
|
||||
$ret{"${_}_installed"}++;
|
||||
}
|
||||
elsif (!$available{$update->{id}}->{impossible}) {
|
||||
$ret{"${_}_installable"}++;
|
||||
}
|
||||
}
|
||||
push @{$ret{update_types}}, { update_type => $_, updates => $ret{$_} };
|
||||
}
|
||||
|
||||
my @historic = sort { _numeric_version($a) <=> _numeric_version($b) } keys %{$STASH{updates}->{installed}};
|
||||
$ret{historic} = \@historic;
|
||||
|
||||
\%ret;
|
||||
}
|
||||
|
||||
sub check_historic {
|
||||
my $updater = _updater;
|
||||
my $version = shift || $CFG->{version};
|
||||
|
||||
my @updates = $updater->check($version);
|
||||
my %ret = (historic_version => $version, current_version => $CFG->{version});
|
||||
|
||||
for (@updates) {
|
||||
my @files = $_->files;
|
||||
my $severity = $_->severity;
|
||||
my $update_type = $severity == CRITICAL ? 'critical' : $severity == RECOMMENDED ? 'recommended' : $severity == VERSION ? 'version' : 'optional';
|
||||
my $id = $_->id;
|
||||
my %info = (
|
||||
id => $id,
|
||||
title => $_->title,
|
||||
description => \($_->description),
|
||||
severity => $severity,
|
||||
files => \@files,
|
||||
reversible => ($version eq $CFG->{version} ? $_->reversible : 0),
|
||||
unique => $_->unique,
|
||||
deps => [$_->deps],
|
||||
revdeps => [$_->revdeps],
|
||||
requires => [$_->requires],
|
||||
revdeps_first => $_->revdeps_first,
|
||||
update_type => $update_type,
|
||||
installed => $_->installed
|
||||
);
|
||||
push @{$ret{$update_type}}, \%info;
|
||||
$ret{update}->{$id} = \%info;
|
||||
}
|
||||
|
||||
for (qw/critical recommended optional version/) {
|
||||
push @{$ret{update_types}}, { update_type => $_, updates => $ret{$_} };
|
||||
}
|
||||
|
||||
my @historic = sort { _numeric_version($a) <=> _numeric_version($b) } grep keys %{$STASH{updates}->{installed}->{$_}}, keys %{$STASH{updates}->{installed}};
|
||||
$ret{historic} = \@historic;
|
||||
|
||||
\%ret;
|
||||
}
|
||||
|
||||
# Takes a version such as 1.3.7 and converts it to 1.0307.
|
||||
sub _numeric_version {
|
||||
my @v = split /\./, (shift =~ /^(\d+(?:\.\d+)*)/)[0];
|
||||
my $numeric = 0;
|
||||
for (0 .. $#v) { $numeric += $v[$_] * 100**-$_ }
|
||||
$numeric;
|
||||
}
|
||||
|
||||
sub browser_install {
|
||||
my @updates = $IN->param('install');
|
||||
my ($status, $errors) = install(@updates);
|
||||
if (!$status) {
|
||||
$errors->{updates_selected} = \@updates;
|
||||
return $errors;
|
||||
}
|
||||
my %ret = (update_success => 1, update_status => $status, updates_selected => []);
|
||||
|
||||
if ($status == 2) {
|
||||
my $id = $errors;
|
||||
my $path;
|
||||
for (@{$STASH{updates}->{installed}->{$CFG->{version}}->{$updates[0]}->{files}}) {
|
||||
if (basename($_->{file}) eq 'install.cgi') {
|
||||
$path = $_->{file} . "?upgrade_choice=Yes;install_dir=" . $IN->escape($CFG->{admin_root_path});
|
||||
last;
|
||||
}
|
||||
}
|
||||
$ret{continue_url} = $path;
|
||||
}
|
||||
|
||||
return \%ret;
|
||||
}
|
||||
|
||||
# Installs updates passed in. Returns (0, \%error_hash) on failure, 1 on
|
||||
# success of normal updates, (2, $id) on the success of version upgrade files.
|
||||
sub install {
|
||||
my @updates = @_;
|
||||
my $updater = _updater;
|
||||
my $v = $updater->verify(@updates);
|
||||
return 0, { %$v, update_failed => 1, verify_failed => 1 } if ref $v eq 'HASH';
|
||||
@updates = @$v;
|
||||
|
||||
my $success = $updater->install_verified(@updates);
|
||||
if (!$success) {
|
||||
my $error = $updater->error;
|
||||
return 0, { update_failed => 1, error => "Update failed: $error" };
|
||||
}
|
||||
$STASH{updates}->{installed} = { $updater->installed };
|
||||
delete $STASH{updates}->{cache};
|
||||
$STASH{updates}->save;
|
||||
|
||||
if (@updates == 1 and $STASH{updates}->{installed}->{$CFG->{version}}->{$updates[0]}->{severity} == VERSION) {
|
||||
# We just installed a version upgrade
|
||||
return (2, $updates[0]);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub browser_uninstall {
|
||||
my @updates = $IN->param('uninstall');
|
||||
my ($status, $errors) = uninstall(@updates);
|
||||
if (!$status) {
|
||||
$errors->{updates_selected} = \@updates;
|
||||
return $errors;
|
||||
}
|
||||
return { uninstall_success => 1, updates_selected => [] };
|
||||
}
|
||||
|
||||
sub uninstall {
|
||||
my @updates = @_;
|
||||
my $updater = _updater;
|
||||
my $v = $updater->verify_uninstall(@updates);
|
||||
return { %$v, update_failed => 1, verify_failed => 1 } if ref $v eq 'HASH';
|
||||
@updates = @$v;
|
||||
|
||||
my $success = $updater->uninstall_verified(@updates);
|
||||
if (!$success) {
|
||||
my $error = $updater->error;
|
||||
return 0, { uninstall_failed => 1, error => "Update uninstall failed: $error" };
|
||||
}
|
||||
$STASH{updates}->{installed} = { $updater->installed };
|
||||
delete $STASH{updates}->{cache};
|
||||
$STASH{updates}->save;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
# Takes a string, such as '/foo/bar/blah/sdffffffddddddddddddddddddddddddddddd'
|
||||
# and replaces a part of it with ...
|
||||
# The arguments are:
|
||||
# - string
|
||||
# - number of characters before the ...
|
||||
# - number of characters after the ...
|
||||
sub shorten {
|
||||
my ($string, $leading, $trailing) = @_;
|
||||
if (length($string) <= ($leading + $trailing + 3)) {
|
||||
return $string;
|
||||
}
|
||||
else {
|
||||
return substr($string, 0, $leading) . ' ... ' . substr($string, -$trailing);
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
1034
site/slowtwitch.com/cgi-bin/articles/admin/Links/Upgrade.pm
Normal file
1034
site/slowtwitch.com/cgi-bin/articles/admin/Links/Upgrade.pm
Normal file
File diff suppressed because it is too large
Load Diff
303
site/slowtwitch.com/cgi-bin/articles/admin/Links/User/Add.pm
Normal file
303
site/slowtwitch.com/cgi-bin/articles/admin/Links/User/Add.pm
Normal file
@@ -0,0 +1,303 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Add.pm,v 1.59 2007/12/20 20:31:35 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::User::Add;
|
||||
# ==================================================================
|
||||
use strict;
|
||||
use Links qw/:objects :payment/;
|
||||
use Links::Build;
|
||||
use Links::SiteHTML;
|
||||
|
||||
sub handle {
|
||||
# -------------------------------------------------------------------
|
||||
# Display either an add form or process an add request.
|
||||
#
|
||||
if ($CFG->{user_required} and !$USER) {
|
||||
print $IN->redirect(Links::redirect_login_url('add'));
|
||||
return;
|
||||
}
|
||||
|
||||
my $custom;
|
||||
if (exists $CFG->{payment}->{remote}->{used}->{PayPal} and $custom = $IN->param('custom') and substr($custom, 0, 3) eq 'do;') {
|
||||
substr($custom, 0, 3) = '';
|
||||
my @pairs = split /;/, $custom;
|
||||
for (@pairs) {
|
||||
my ($key, $val) = split /=/, $_;
|
||||
next unless $key and $val;
|
||||
$IN->param($key => $val) unless $IN->param($key);
|
||||
}
|
||||
}
|
||||
|
||||
print $IN->header;
|
||||
|
||||
# We are processing an add request.
|
||||
if ($IN->param('add')) {
|
||||
my $results = $PLG->dispatch('user_add_link', \&add_link);
|
||||
if (defined $results->{error}) {
|
||||
print Links::SiteHTML::display('add', $results);
|
||||
}
|
||||
else {
|
||||
$results = Links::SiteHTML::tags('link', $results);
|
||||
$results->{main_title_loop} = Links::Build::build('title', Links::language('LINKS_ADD_SUCCESS'), "$CFG->{db_cgi_url}/add.cgi");
|
||||
if ($CFG->{payment}->{enabled}) {
|
||||
require Links::Payment;
|
||||
my @cats = $IN->param('CatLinks.CategoryID');
|
||||
my $opt = Links::Payment::load_cat_price(\@cats);
|
||||
if (exists $opt->{error}) {
|
||||
print Links::SiteHTML::display('error', $opt);
|
||||
}
|
||||
elsif ($opt->{payment_mode} == NOT_ACCEPTED) {
|
||||
if ($CFG->{admin_email_add}) {
|
||||
Links::send_email('link_added.eml', $results, { admin_email => 1 }) or die "Unable to send mail: $GT::Mail::error";
|
||||
}
|
||||
print Links::SiteHTML::display('add_success', $results);
|
||||
}
|
||||
else {# payment option for this category is required or optional
|
||||
$results->{link_id} = $results->{ID}; # we need a different tag since both Category and Link have ID
|
||||
$opt->{CategoryID} = delete $opt->{ID}; # remove category id
|
||||
$opt->{CategoryDescription} = delete $opt->{Description};
|
||||
$results->{main_title_loop} = Links::Build::build('title', Links::language('LINKS_PAYMENT'), "$CFG->{db_cgi_url}/modify.cgi?do=payment_linked;process_payment=1;modify=1;ID=$results->{link_id}");
|
||||
print Links::SiteHTML::display('payment', { %$results, %$opt });
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ($CFG->{admin_email_add}) {
|
||||
Links::send_email('link_added.eml', $results, { admin_email => 1 }) or die "Unable to send mail: $GT::Mail::error";
|
||||
}
|
||||
print Links::SiteHTML::display('add_success', $results);
|
||||
}
|
||||
}
|
||||
}
|
||||
# We are processing a payment request.
|
||||
elsif ($IN->param('process_payment') and $CFG->{payment}->{enabled}) {
|
||||
my $payment_term = $IN->param('payment_term') || '';
|
||||
my $do = $IN->param('do');
|
||||
if ($payment_term eq 'free') {
|
||||
my $link = $DB->table('Links')->get($IN->param('link_id'));
|
||||
if (not $link or ($CFG->{user_required} and $link->{LinkOwner} ne $USER->{Username})) {
|
||||
print Links::SiteHTML::display('error', { error => !$link ? $GT::SQL::ERRORS : Links::language('PAYMENTERR_NOTOWNER') });
|
||||
return;
|
||||
};
|
||||
$link = Links::SiteHTML::tags('link', $link);
|
||||
|
||||
# Set ExpiryDate to free
|
||||
$link->{'CatLinks.CategoryID'} = $IN->param('cat_id');
|
||||
$link->{ExpiryDate} = FREE;
|
||||
$link->{ExpiryNotify}= 0;
|
||||
# Update the link
|
||||
$DB->table('Links')->update({ ExpiryDate => FREE, ExpiryNotify => 0 }, { ID => $link->{ID} });
|
||||
# Update the Timestmp for link's categories so they get rebuilt with build changed
|
||||
my @cats = $DB->table('Links', 'CatLinks')->select('CategoryID', { LinkID => $link->{ID} })->fetchall_list;
|
||||
$DB->table('Category')->update({ Timestmp => \'NOW()' }, { ID => \@cats });
|
||||
|
||||
# Add some special tags for formatting.
|
||||
@cats = $DB->table('Category', 'CatLinks')->select('Category.Full_Name', { 'CatLinks.LinkID' => $link->{ID} })->fetchall_list;
|
||||
$link->{Category} = join "\n", sort @cats;
|
||||
$link->{Category_loop} = [sort @cats];
|
||||
$link->{Host} = $ENV{REMOTE_HOST} ? "$ENV{REMOTE_HOST} ($ENV{REMOTE_ADDR})" : $ENV{REMOTE_ADDR} ? $ENV{REMOTE_ADDR} : 'none';
|
||||
$link->{Referer} = $ENV{HTTP_REFERER} ? $ENV{HTTP_REFERER} : 'none';
|
||||
$link->{AutoValidate} = $CFG->{build_auto_validate};
|
||||
if ($CFG->{admin_email_add}) {
|
||||
Links::send_email('link_added.eml', $link, { admin_email => 1 }) or die "Unable to send mail: $GT::Mail::error";
|
||||
}
|
||||
$link->{main_title_loop} = Links::Build::build('title', Links::language('LINKS_ADD_SUCCESS'), "$CFG->{db_cgi_url}/add.cgi");
|
||||
print Links::SiteHTML::display('add_success', $link);
|
||||
}
|
||||
elsif ($IN->param('payment_success')) {
|
||||
print Links::SiteHTML::display('payment_success', { main_title_loop => Links::Build::build('title', Links::language('LINKS_PAYMENT_SUCCESS'), $CFG->{build_root_url} . "/" . ($CFG->{build_home} || ($CFG->{build_index_include} ? $CFG->{build_index} : ''))) });
|
||||
}
|
||||
elsif ($do =~ /^payment_(method|form|direct)$/) {
|
||||
require Links::Payment;
|
||||
my $vars = Links::Payment->$1();
|
||||
my $page = $IN->param('page') || $IN->param('do');
|
||||
my $opt = Links::Payment::load_cat_price($IN->param('cat_id'));
|
||||
if ($opt->{payment_mode} == NOT_ACCEPTED) {
|
||||
print Links::SiteHTML::display('error', { error => Links::language('PAYMENTERR_NOTACCEPTED') });
|
||||
return;
|
||||
}
|
||||
my $link = $DB->table('Links')->get($IN->param('link_id'));
|
||||
if (not $link or ($CFG->{user_required} and $link->{LinkOwner} ne $USER->{Username})) {
|
||||
print Links::SiteHTML::display('error', { error => !$link ? $GT::SQL::ERRORS : Links::language('PAYMENTERR_NOTOWNER') });
|
||||
return;
|
||||
}
|
||||
$link = Links::SiteHTML::tags('link', $link);
|
||||
|
||||
$link->{main_title_loop} = Links::Build::build('title', Links::language('LINKS_PAYMENT'), "$CFG->{db_cgi_url}/modify.cgi?do=payment_linked;process_payment=1;modify=1;ID=$link->{ID}");
|
||||
print Links::SiteHTML::display($page, { %$vars, %$opt, %$link });
|
||||
}
|
||||
else {
|
||||
print Links::SiteHTML::display('error', { error => "Invalid action" });
|
||||
}
|
||||
}
|
||||
# We are displaying an add form.
|
||||
else {
|
||||
my @id = grep { /^\d+$/ } $IN->param('ID');
|
||||
|
||||
# If we don't have an id, and can't generate a list, let's send the user a message.
|
||||
if (!@id and !$CFG->{db_gen_category_list}) {
|
||||
print Links::SiteHTML::display('error', { error => Links::language('ADD_SELCAT') });
|
||||
}
|
||||
else {
|
||||
# Otherwise display the add form.
|
||||
if ($USER) {
|
||||
$IN->param('Contact_Name') or ($IN->param('Contact_Name', $USER->{Name} || $USER->{Username}));
|
||||
$IN->param('Contact_Email') or ($IN->param('Contact_Email', $USER->{Email}));
|
||||
}
|
||||
|
||||
if ($DB->table('Category')->count == 0) {
|
||||
print Links::SiteHTML::display('error', { error => Links::language('ADD_NOCATEGORIES') });
|
||||
}
|
||||
# If we're not generating a category list, the add form can't be shown without a valid category ID.
|
||||
elsif (!$CFG->{db_gen_category_list} and $DB->table('Category')->count({ ID => \@id }) == 0) {
|
||||
print Links::SiteHTML::display('error', { error => Links::language('ADD_INVALIDCAT', join(', ', @id)) });
|
||||
}
|
||||
else {
|
||||
my $category = {};
|
||||
if ($CFG->{db_gen_category_list} < 2) {
|
||||
require Links::Tools;
|
||||
$category = Links::Tools::category_list();
|
||||
$category->{Category} = sub { Links::Tools::category_list_html() };
|
||||
}
|
||||
print Links::SiteHTML::display('add', {
|
||||
main_title_loop => Links::Build::build('title', Links::language('LINKS_ADD'), "$CFG->{db_cgi_url}/add.cgi" . (@id ? "?ID=" . join(';ID=', @id) : '')),
|
||||
%$category
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub add_link {
|
||||
# --------------------------------------------------------
|
||||
# Add the link to the database.
|
||||
#
|
||||
my $class = shift;
|
||||
my @id = $IN->param('CatLinks.CategoryID');
|
||||
my %ret;
|
||||
if ($CFG->{db_gen_category_list} < 2) {
|
||||
require Links::Tools;
|
||||
%ret = %{Links::Tools::category_list()};
|
||||
$ret{Category} = sub { Links::Tools::category_list_html() };
|
||||
}
|
||||
$ret{main_title_loop} = Links::Build::build('title', Links::language('LINKS_ADD'), "$CFG->{db_cgi_url}/add.cgi" . (@id ? "?ID=" . join(';ID=', @id) : ''));
|
||||
|
||||
# Check the referer.
|
||||
if (@{$CFG->{db_referers}}) {
|
||||
my $found = 0;
|
||||
if ($ENV{'HTTP_REFERER'}) {
|
||||
foreach (@{$CFG->{db_referers}}) { $ENV{'HTTP_REFERER'} =~ /\Q$_\E/i and $found++ and last; }
|
||||
}
|
||||
unless ($found) {
|
||||
return { error => Links::language('ADD_BADREFER', $ENV{'HTTP_REFERER'}), %ret };
|
||||
}
|
||||
}
|
||||
|
||||
# Get our form data.
|
||||
my $input = $IN->get_hash;
|
||||
|
||||
# Check if the link is valid
|
||||
if ($CFG->{user_link_validation}) {
|
||||
require Links::Tools;
|
||||
my $status = Links::Tools::link_status($input->{URL});
|
||||
if ($status and $Links::Tools::STATUS_BAD{$status}) {
|
||||
return { error => Links::language('ADD_BADSTATUS', $Links::Tools::STATUS_BAD{$status}), %ret };
|
||||
}
|
||||
}
|
||||
|
||||
my $db = $DB->table('Links');
|
||||
my $cdb = $DB->table('Category');
|
||||
|
||||
# Columns the user should not be passing in
|
||||
for my $key (qw/ID LinkOwner Add_Date Mod_Date Timestmp Date_Checked ExpiryDate ExpiryCounted ExpiryNotify LinkExpired/) {
|
||||
delete $input->{$key};
|
||||
}
|
||||
|
||||
for my $key (keys %{$CFG->{add_system_fields}}) {
|
||||
$input->{$key} = $CFG->{add_system_fields}->{$key};
|
||||
}
|
||||
|
||||
# Set the LinkOwner
|
||||
$input->{LinkOwner} = $USER ? $USER->{Username} : 'admin';
|
||||
|
||||
# Set date variable to today's date.
|
||||
Links::init_date();
|
||||
my $today = GT::Date::date_get();
|
||||
$input->{Add_Date} = $today;
|
||||
$input->{Mod_Date} = $today;
|
||||
|
||||
# Backward compatibility
|
||||
$input->{Contact_Name} = $input->{'Contact_Name'} || $input->{'Contact Name'} || ($USER ? $USER->{Name} : '');
|
||||
$input->{Contact_Email} = $input->{'Contact_Email'} || $input->{'Contact Email'} || ($USER ? $USER->{Email} : '');
|
||||
|
||||
$input->{isValidated} = ($CFG->{build_auto_validate} == 1 and $USER or $CFG->{build_auto_validate} == 2) ? 'Yes' : 'No';
|
||||
|
||||
# Check the category
|
||||
my @cids = $IN->param('CatLinks.CategoryID');
|
||||
my @name;
|
||||
if (@cids) {
|
||||
foreach my $cid (@cids) {
|
||||
next if (! $cid);
|
||||
my $sth = $cdb->select('Full_Name', { ID => $cid });
|
||||
$sth->rows or return { error => Links::language('ADD_INVALIDCAT', $cid), %ret };
|
||||
push @name, $sth->fetchrow;
|
||||
}
|
||||
if (@name) {
|
||||
$input->{'CatLinks.CategoryID'} = \@cids;
|
||||
}
|
||||
}
|
||||
|
||||
my $take_payments = (
|
||||
$CFG->{payment}->{enabled}
|
||||
and
|
||||
(
|
||||
$cdb->count(GT::SQL::Condition->new(Payment_Mode => '>=' => OPTIONAL, ID => '=' => \@cids))
|
||||
or
|
||||
(
|
||||
$CFG->{payment}->{mode} >= OPTIONAL and
|
||||
$cdb->count(GT::SQL::Condition->new(Payment_Mode => '=' => GLOBAL, ID => '=' => \@cids))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
# Set the payment expiry
|
||||
# Set this to unlimited when payment is turned off so that if payment is turned on
|
||||
# at a later date, those users aren't forced to pay.
|
||||
$input->{ExpiryDate} = $CFG->{payment}->{enabled} && $take_payments ? UNPAID : FREE;
|
||||
|
||||
# Setup the language for GT::SQL.
|
||||
local $GT::SQL::ERRORS->{ILLEGALVAL} = Links::language('ADD_ILLEGALVAL');
|
||||
local $GT::SQL::ERRORS->{UNIQUE} = Links::language('ADD_UNIQUE');
|
||||
local $GT::SQL::ERRORS->{NOTNULL} = Links::language('ADD_NOTNULL');
|
||||
local $Links::Table::Links::ERRORS->{NOCATEGORY} = Links::language('ADD_NOCATEGORY');
|
||||
$Links::Table::Links::ERRORS if 0; # silence -w
|
||||
|
||||
# Add the record.
|
||||
my $id = $db->add($input);
|
||||
$input->{ID} = $id;
|
||||
if (! $id) {
|
||||
my $error = "<ul>" . join('', map "<li>$_</li>", $db->error) . "</ul>";
|
||||
return { error => $error, %ret };
|
||||
}
|
||||
|
||||
# Add some special tags for formatting.
|
||||
$input->{Category} = join "\n", sort @name;
|
||||
$input->{Category_loop} = [sort @name];
|
||||
$input->{Host} = $ENV{REMOTE_HOST} ? "$ENV{REMOTE_HOST} ($ENV{REMOTE_ADDR})" : $ENV{REMOTE_ADDR} ? $ENV{REMOTE_ADDR} : 'none';
|
||||
$input->{Referer} = $ENV{HTTP_REFERER} ? $ENV{HTTP_REFERER} : 'none';
|
||||
$input->{AutoValidate} = $CFG->{build_auto_validate};
|
||||
|
||||
# Send the visitor to the success page.
|
||||
return $input;
|
||||
}
|
||||
|
||||
1;
|
||||
126
site/slowtwitch.com/cgi-bin/articles/admin/Links/User/Editor.pm
Normal file
126
site/slowtwitch.com/cgi-bin/articles/admin/Links/User/Editor.pm
Normal file
@@ -0,0 +1,126 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Editor.pm,v 1.15 2009/05/09 06:40:54 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::User::Editor;
|
||||
# ==================================================================
|
||||
use strict;
|
||||
use Links qw/:objects/;
|
||||
use Links::Browser::Controller;
|
||||
use Links::Browser;
|
||||
use Links::SiteHTML;
|
||||
|
||||
sub handle {
|
||||
# ------------------------------------------------------------------
|
||||
# This script is only available to users who have logged on.
|
||||
#
|
||||
unless ($USER) {
|
||||
my $url = $IN->url(absolute => 1, query_string => 1);
|
||||
$url = $IN->escape($url);
|
||||
$url = $CFG->{db_cgi_url} . "/user.cgi?url=$url;from=browser";
|
||||
print $IN->redirect($url);
|
||||
return;
|
||||
}
|
||||
my $editors = $DB->table('Editors');
|
||||
my @nodes;
|
||||
my $perms = {};
|
||||
|
||||
# Get a controller to manage access.
|
||||
my $ctrl = Links::Browser::Controller->new(user => $USER);
|
||||
|
||||
if ($USER->{Status} eq 'Administrator') {
|
||||
$ctrl->{admin} = 1;
|
||||
}
|
||||
else {
|
||||
my $sth = $editors->select({ Username => $USER->{Username} });
|
||||
if ($sth->rows) {
|
||||
while (my $ed = $sth->fetchrow_hashref) {
|
||||
push @nodes, $ed->{CategoryID};
|
||||
$perms->{$ed->{CategoryID}} = $ed;
|
||||
}
|
||||
}
|
||||
unless (@nodes) {
|
||||
print $IN->header;
|
||||
print Links::SiteHTML::display('error', { error => Links::language('BROWSER_NOTEDITOR') });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
# Handle the special condition which related to viewing
|
||||
# and downloading files. Must remap the passed column
|
||||
# values so Jump functions properly.
|
||||
my $method = $IN->param('do');
|
||||
if ($method and $method =~ m/^(?:(v)iew|(download))_file$/) {
|
||||
$IN->param($+, $IN->param('cn'));
|
||||
$IN->param('ID', $IN->param('link_id') || $IN->param('id'));
|
||||
$IN->param('DB', $IN->param('db'));
|
||||
require Links::User::Jump;
|
||||
return Links::User::Jump::handle();
|
||||
}
|
||||
elsif ($method and $method =~ m/^(?:(v)iew|(download))_tmp_file$/) {
|
||||
my $download = $2;
|
||||
# view_tmp_file doesn't go through Jump because only editors are
|
||||
# allowed to see them - the tmp files are used for pending Changes.
|
||||
my $col = $IN->param('cn');
|
||||
my $id = $IN->param('link_id');
|
||||
my $changes = $DB->table('Changes')->select({ LinkID => $id })->fetchrow_hashref;
|
||||
|
||||
my ($linkinfo, $fh);
|
||||
if ($changes) {
|
||||
$linkinfo = eval $changes->{ChgRequest};
|
||||
if ($linkinfo and -f $linkinfo->{$col}) {
|
||||
my $colfh = \do { local *FH; *FH };
|
||||
if (open $colfh, "<$linkinfo->{$col}") {
|
||||
$fh = $colfh;
|
||||
binmode $fh;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$fh) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('FILE_UNKNOWN', $id) });
|
||||
return;
|
||||
}
|
||||
|
||||
(my $filename = $linkinfo->{"${col}_filename"} || $linkinfo->{$col}) =~ s{.*[/\\]}{};
|
||||
print $IN->header($IN->file_headers(
|
||||
filename => $filename,
|
||||
inline => $download ? 0 : 1,
|
||||
size => -s $linkinfo->{$col}
|
||||
));
|
||||
|
||||
while (read $fh, my $buffer, 64*1024) {
|
||||
print $buffer;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
# Load the tree if it is under 200 categories.
|
||||
$ctrl->{load_tree} = 1;
|
||||
$ctrl->{user_base_node} = \@nodes;
|
||||
$ctrl->{perms} = $perms;
|
||||
$ctrl->{admin_templates} = 0;
|
||||
|
||||
# Begin the script.
|
||||
print $IN->header(-charset => $CFG->{header_charset});
|
||||
$method = $ctrl->can_run;
|
||||
if ($method) {
|
||||
my $browser = Links::Browser->new(ctrl => $ctrl);
|
||||
$PLG->dispatch("browser_$method", sub { $browser->$method(); }, $browser);
|
||||
}
|
||||
else {
|
||||
print Links::SiteHTML::display('error', { error => Links::language('BROWSER_UNAUTHORIZED') });
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
186
site/slowtwitch.com/cgi-bin/articles/admin/Links/User/Jump.pm
Normal file
186
site/slowtwitch.com/cgi-bin/articles/admin/Links/User/Jump.pm
Normal file
@@ -0,0 +1,186 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Jump.pm,v 1.26 2006/02/20 22:38:31 jagerman Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::User::Jump;
|
||||
# ==================================================================
|
||||
use strict;
|
||||
use Links qw/:objects :payment/;
|
||||
use Links::SiteHTML;
|
||||
|
||||
sub handle {
|
||||
# --------------------------------------------------------------
|
||||
# Jump to a given ID.
|
||||
#
|
||||
$PLG->dispatch('jump_link', \&_plg_jump, {});
|
||||
}
|
||||
|
||||
sub _plg_jump {
|
||||
# --------------------------------------------------------------
|
||||
# Jump to a given link.
|
||||
#
|
||||
my $links = $DB->table('Links');
|
||||
my $id = $IN->param('ID') || $IN->param('Detailed');
|
||||
my $action = $IN->param('action') || '';
|
||||
my $goto = '';
|
||||
my $rec = {};
|
||||
|
||||
if ($CFG->{framed_jump} and $id and $action eq 'jump_frame') {
|
||||
my $error;
|
||||
if ($id !~ /^\d+$/) {
|
||||
$error = Links::language('JUMP_INVALIDID', $id);
|
||||
}
|
||||
else {
|
||||
$rec = $links->select({ ID => $id }, VIEWABLE)->fetchrow_hashref;
|
||||
unless ($rec) {
|
||||
$error = Links::language('JUMP_INVALIDID', $id);
|
||||
$rec = {};
|
||||
}
|
||||
elsif ($CFG->{build_detailed}) {
|
||||
$rec->{detailed_url} = "$CFG->{build_detail_url}/" . $links->detailed_url($id);
|
||||
}
|
||||
}
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('jump_frame', { error => $error, %$rec });
|
||||
return;
|
||||
}
|
||||
|
||||
# If we are chosing a random link, then get the total and go to one at random.
|
||||
if (lc $id eq "random") {
|
||||
my $offset = int rand $links->count(VIEWABLE);
|
||||
$links->select_options("LIMIT 1 OFFSET $offset");
|
||||
my $sth = $links->select(qw/ID URL/ => VIEWABLE);
|
||||
($id, $goto) = $sth->fetchrow_array;
|
||||
}
|
||||
elsif (defined $id) {
|
||||
if ($id !~ /^\d+$/) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('JUMP_INVALIDID', $id) });
|
||||
return;
|
||||
}
|
||||
|
||||
# Find out if we're going to be displaying a file
|
||||
my $col = $IN->param('v') || $IN->param('dl') || $IN->param('view') || $IN->param('download');
|
||||
|
||||
if ($col) {
|
||||
# in this case, we need to know from what table we want to load our data from.
|
||||
# It will by default pull information from the Links table, however if the
|
||||
# DB=tablename option is used, it will apply the request to that table instead
|
||||
my $table_name = $IN->param('DB') || 'Links';
|
||||
|
||||
unless ($table_name =~ m/^\w+$/) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('FILE_TABLEFORMAT' ) });
|
||||
return;
|
||||
};
|
||||
|
||||
if ($table_name ne 'Links') {
|
||||
eval { $links = $DB->table($table_name) };
|
||||
if ($@) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('FILE_TABLE', $table_name, $GT::SQL::error) });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
my $fh;
|
||||
eval { $fh = $links->file_info($col, $id); };
|
||||
if ($fh) {
|
||||
if ($IN->param('v') or $IN->param('view')) { # Viewing
|
||||
print $IN->header($IN->file_headers(
|
||||
filename => $fh->File_Name,
|
||||
mimetype => $fh->File_MimeType,
|
||||
inline => 1,
|
||||
size => $fh->File_Size
|
||||
));
|
||||
}
|
||||
else { # Downloading
|
||||
print $IN->header($IN->file_headers(
|
||||
filename => $fh->File_Name,
|
||||
mimetype => $fh->File_MimeType,
|
||||
inline => 0,
|
||||
size => $fh->File_Size
|
||||
));
|
||||
}
|
||||
binmode $fh;
|
||||
while (read($fh, my $buffer, 65536)) {
|
||||
print $buffer;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('FILE_UNKNOWN', $id) });
|
||||
return;
|
||||
}
|
||||
}
|
||||
# Jump to a URL, bump the hit counter.
|
||||
else {
|
||||
$rec = $links->select({ ID => $id }, VIEWABLE)->fetchrow_hashref;
|
||||
unless ($rec) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('JUMP_INVALIDID', $id) });
|
||||
return;
|
||||
}
|
||||
$goto = $rec->{URL};
|
||||
|
||||
my $clicktrack = $DB->table('ClickTrack');
|
||||
my $rows = $clicktrack->count({ LinkID => $id, IP => $ENV{REMOTE_ADDR}, ClickType => 'Hits' });
|
||||
unless ($rows) {
|
||||
eval {
|
||||
$clicktrack->insert({ LinkID => $id, IP => $ENV{REMOTE_ADDR}, ClickType => 'Hits', Created => \"NOW()" });
|
||||
$links->update({ Hits => \"Hits + 1" }, { ID => $id }, { GT_SQL_SKIP_INDEX => 1 });
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
# Oops, no link.
|
||||
else {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('JUMP_INVALIDID', $id) });
|
||||
return;
|
||||
}
|
||||
|
||||
unless (defined $goto) {
|
||||
my $error = ($IN->param('ID') eq 'random') ? Links::language('RANDOM_NOLINKS') : Links::language('JUMP_INVALIDID', $id);
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => $error });
|
||||
return;
|
||||
}
|
||||
|
||||
# Redirect to a detailed page if requested.
|
||||
if ($CFG->{build_detailed} and $IN->param('Detailed')) {
|
||||
$goto = Links::transform_url("$CFG->{build_detail_url}/" . $links->detailed_url($id));
|
||||
}
|
||||
($goto =~ m,^\w+://,) or ($goto = "http://$goto");
|
||||
if ($goto) {
|
||||
if ($CFG->{framed_jump} and not ($CFG->{build_detailed} and $IN->param('Detailed'))) {
|
||||
unless (keys %$rec) {
|
||||
$rec = $links->select({ ID => $id }, VIEWABLE)->fetchrow_hashref;
|
||||
}
|
||||
$rec->{detailed_url} = "$CFG->{build_detail_url}/" . $links->detailed_url($id) if $CFG->{build_detailed};
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('jump', { destination => $goto, %$rec });
|
||||
return;
|
||||
}
|
||||
else {
|
||||
print $IN->redirect($goto);
|
||||
}
|
||||
}
|
||||
else {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('JUMP_INVALIDID', $id) });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
263
site/slowtwitch.com/cgi-bin/articles/admin/Links/User/Login.pm
Normal file
263
site/slowtwitch.com/cgi-bin/articles/admin/Links/User/Login.pm
Normal file
@@ -0,0 +1,263 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Login.pm,v 1.19 2005/05/08 09:56:44 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::User::Login;
|
||||
# ==================================================================
|
||||
use strict;
|
||||
use Links qw/:objects/;
|
||||
use Links::Build;
|
||||
use Links::SiteHTML;
|
||||
|
||||
sub handle {
|
||||
# -------------------------------------------------------------------
|
||||
# Determine what to do.
|
||||
#
|
||||
my $input = $IN->get_hash;
|
||||
my $mtl = Links::Build::build('title', Links::language('LINKS_LOGIN'), "$CFG->{db_cgi_url}/user.cgi");
|
||||
if ($input->{login}) {
|
||||
$PLG->dispatch('user_login', \&login_user);
|
||||
}
|
||||
elsif ($input->{signup_user}) {
|
||||
$PLG->dispatch('user_signup', \&signup_user);
|
||||
}
|
||||
elsif ($input->{validate_user}) {
|
||||
$PLG->dispatch('user_validate', \&validate_user);
|
||||
}
|
||||
elsif ($input->{send_validate}) {
|
||||
$PLG->dispatch('user_validate_email', \&send_validate);
|
||||
}
|
||||
elsif ($input->{send_pass} and $CFG->{user_allow_pass}) {
|
||||
$PLG->dispatch('user_pass_email', \&send_pass);
|
||||
}
|
||||
elsif ($input->{signup_form}) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('signup_form', { Username => $IN->param('Username') || '', Password => '', Email => $IN->param('Email') || '', main_title_loop => Links::Build::build('title', Links::language('LINKS_SIGNUP'), "$CFG->{db_cgi_url}/user.cgi?signup_form=1") });
|
||||
}
|
||||
elsif ($input->{validate}) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('validate_form', { main_title_loop => Links::Build::build('title', Links::language('LINKS_VALIDATE'), "$CFG->{db_cgi_url}/user.cgi?validate=1") });
|
||||
}
|
||||
elsif ($input->{logout}) {
|
||||
Links::Authenticate::auth('delete_session');
|
||||
$USER = undef;
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('login', { Username => '', Password => '', Email => '', error => Links::language('USER_LOGOUT'), main_title_loop => $mtl });
|
||||
}
|
||||
elsif ($input->{email_pass} and $CFG->{user_allow_pass}) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('login_email', { main_title_loop => Links::Build::build('title', Links::language('LINKS_EMAILPASS'), "$CFG->{db_cgi_url}/user.cgi?email_pass=1") });
|
||||
}
|
||||
else {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('login', { Username => $IN->param('Username') || '', main_title_loop => $mtl });
|
||||
}
|
||||
}
|
||||
# ==============================================================
|
||||
|
||||
sub login_user {
|
||||
# --------------------------------------------------------
|
||||
# Logs a user in, and creates a session ID.
|
||||
#
|
||||
my $username = $IN->param('Username') || shift;
|
||||
my $password = $IN->param('Password') || shift;
|
||||
my $goto = shift || 'login_success';
|
||||
|
||||
my $mtl = Links::Build::build('title', Links::language('LINKS_LOGIN'), "$CFG->{db_cgi_url}/user.cgi");
|
||||
|
||||
# Make sure we have both a username and password.
|
||||
if (!$username or !$password) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('login', { error => Links::language('USER_BADLOGIN'), Username => $username, main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
|
||||
# Check that the user exists, and that the password is valid.
|
||||
my $user = Links::init_user($username, $password);
|
||||
if (!$user) {
|
||||
print $IN->header();
|
||||
require Links::Authenticate;
|
||||
if (Links::Authenticate::auth_valid_user({ Username => $username, Password => $password })) {
|
||||
print Links::SiteHTML::display('login', { error => Links::language('USER_NOTVAL', $user->{Email}), Username => $user->{Username}, main_title_loop => $mtl });
|
||||
}
|
||||
else {
|
||||
print Links::SiteHTML::display('login', { error => Links::language('USER_BADLOGIN'), main_title_loop => $mtl });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
# Store the session in either a cookie or url based.
|
||||
my $results = Links::Authenticate::auth('create_session', { Username => $user->{Username} });
|
||||
return if $results->{redirect};
|
||||
|
||||
# Get the $USER information.
|
||||
$USER = Links::Authenticate::auth('get_user', { Username => $username, Password => $password, auto_create => 1 });
|
||||
|
||||
print $IN->header(); # In case the session didn't print it.
|
||||
print Links::SiteHTML::display($goto, { %$user, main_title_loop => $mtl });
|
||||
}
|
||||
|
||||
sub signup_user {
|
||||
# --------------------------------------------------------
|
||||
# Signs a new user up.
|
||||
#
|
||||
my $username = $IN->param('Username');
|
||||
my $password = $IN->param('Password');
|
||||
my $email = $IN->param('Email');
|
||||
|
||||
my $mtl = Links::Build::build('title', Links::language('LINKS_SIGNUP'), "$CFG->{db_cgi_url}/user.cgi?signup_form=1");
|
||||
|
||||
if (!$username or !$password or !$email) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('signup_form', { error => Links::language('USER_INVALIDSIGNUP'), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
unless ($email =~ /.\@.+\../) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('signup_form', { error => Links::language('USER_INVALIDEMAIL', $email), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
|
||||
# Check that the username doesn't already exist.
|
||||
my $db = $DB->table('Users');
|
||||
my $user = $db->get($username);
|
||||
if ($user) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display( 'signup_form', { error => Links::language('USER_NAMETAKEN', $username), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
|
||||
# Check that the email address doesn't already exist.
|
||||
my $hits = $db->count({ Email => $email });
|
||||
if ($hits) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('signup_form', { error => Links::language('USER_EMAILTAKEN', $email), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
my ($code, $msg);
|
||||
|
||||
# Add the user in, set defaults for fields not specified.
|
||||
$user = $IN->get_hash();
|
||||
my $def = $db->default || {};
|
||||
foreach (keys %$def) {
|
||||
$user->{$_} = $def->{$_} unless (exists $user->{$_});
|
||||
}
|
||||
|
||||
# Send validation email if needed.
|
||||
if ($CFG->{user_validation}) {
|
||||
my $code = time . $$ . int rand 1000;
|
||||
$user->{Status} = "Not Validated";
|
||||
$user->{Validation} = $code;
|
||||
my $ret = $db->add($user);
|
||||
if (!$ret) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('signup_form', { error => $db->error, main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$user->{Status} = "Registered";
|
||||
$user->{Validation} = 0;
|
||||
my $ret = $db->add($user);
|
||||
if (!$ret) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('signup_form', { error => $db->error, main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
}
|
||||
# Print the welcome screen.
|
||||
if ($CFG->{user_validation}) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('signup_success', { %$user, main_title_loop => $mtl });
|
||||
Links::send_email('validate.eml', $user) or die "Unable to send message: $GT::Mail::error";
|
||||
}
|
||||
else {
|
||||
my $results = Links::Authenticate::auth('create_session', { Username => $user->{Username} });
|
||||
$USER = Links::Authenticate::auth('get_user', { Username => $user->{Username}, Password => $user->{Password}, auto_create => 1 });
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('signup_success', { %$user, main_title_loop => $mtl });
|
||||
}
|
||||
}
|
||||
|
||||
sub validate_user {
|
||||
# --------------------------------------------------------
|
||||
# Validates a user.
|
||||
#
|
||||
my $code = $IN->param('code');
|
||||
$code =~ s/^\s*|\s*$//g;
|
||||
|
||||
my $mtl = Links::Build::build('title', Links::language('LINKS_VALIDATE'), "$CFG->{db_cgi_url}/user.cgi?validate=1");
|
||||
|
||||
if (!$code) {
|
||||
print $IN->header;
|
||||
print Links::SiteHTML::display('validate_form', { error => Links::language('USER_INVALIDVAL'), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
my $db = $DB->table('Users');
|
||||
my $sth = $db->select({ Validation => $code });
|
||||
my $user = $sth->fetchrow_hashref;
|
||||
|
||||
if (! $user) {
|
||||
print $IN->header;
|
||||
print Links::SiteHTML::display('validate_form', { error => Links::language('USER_INVALIDVAL'), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
$db->update({ Status => 'Registered' }, { Username => $user->{Username} });
|
||||
login_user($user->{Username}, $user->{Password}, 'validate_success');
|
||||
}
|
||||
|
||||
sub send_pass {
|
||||
# -------------------------------------------------------------------
|
||||
# Sends the user a password reminder email.
|
||||
#
|
||||
my $email = $IN->param('Email');
|
||||
my $user_db = $DB->table('Users');
|
||||
my $sth = $user_db->select( { Email => $email } );
|
||||
print $IN->header();
|
||||
my $user = $sth->fetchrow_hashref;
|
||||
if ($user and $email =~ /.+\@.+\..+/) {
|
||||
Links::send_email('password.eml', { %$user, %ENV }) or die "Unable to send message: $GT::Mail::error";
|
||||
print Links::SiteHTML::display('login', { error => Links::language('USER_PASSSENT'), Username => '', Password => '', main_title_loop => Links::Build::build('title', Links::language('LINKS_LOGIN'), "$CFG->{db_cgi_url}/user.cgi") });
|
||||
}
|
||||
else {
|
||||
print Links::SiteHTML::display('login_email', { error => Links::language('USER_NOEMAIL'), main_title_loop => Links::Build::build('title', Links::language('LINKS_EMAILPASS'), "$CFG->{db_cgi_url}/user.cgi?email_pass=1") });
|
||||
}
|
||||
}
|
||||
|
||||
sub send_validate {
|
||||
# -------------------------------------------------------------------
|
||||
# Sends the validation email if the user needs another one.
|
||||
#
|
||||
my $email = $IN->param('Email');
|
||||
my $user_db = $DB->table('Users');
|
||||
my $sth = $user_db->select( { Email => $email } );
|
||||
print $IN->header();
|
||||
if ($sth->rows) {
|
||||
# Prepare the message.
|
||||
my $user = $sth->fetchrow_hashref;
|
||||
|
||||
# Make sure there is a validation code.
|
||||
if (! $user->{Validation}) {
|
||||
$user->{Validation} = (time) . ($$) . (int rand(1000));
|
||||
$user_db->modify($user);
|
||||
}
|
||||
Links::send_email('validate.eml', $user) or die "Unable to send message: $GT::Mail::error";
|
||||
|
||||
print Links::SiteHTML::display('login', { error => Links::language('USER_VALSENT'), Username => '', Password => '', main_title_loop => Links::Build::build('title', Links::language('LINKS_LOGIN'), "$CFG->{db_cgi_url}/user.cgi") });
|
||||
}
|
||||
else {
|
||||
print Links::SiteHTML::display('login_email', { error => Links::language('USER_NOEMAIL'), main_title_loop => Links::Build::build('title', Links::language('LINKS_EMAILPASS'), "$CFG->{db_cgi_url}/user.cgi?email_pass=1") });
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
571
site/slowtwitch.com/cgi-bin/articles/admin/Links/User/Modify.pm
Normal file
571
site/slowtwitch.com/cgi-bin/articles/admin/Links/User/Modify.pm
Normal file
@@ -0,0 +1,571 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Modify.pm,v 1.82 2013/02/01 04:43:56 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::User::Modify;
|
||||
# ==================================================================
|
||||
use strict;
|
||||
use Links qw/:objects :payment/;
|
||||
use Links::Build;
|
||||
use Links::SiteHTML;
|
||||
|
||||
sub handle {
|
||||
# ---------------------------------------------------
|
||||
# Determine what to do.
|
||||
#
|
||||
my $link_id = $IN->param('LinkID');
|
||||
if ($CFG->{user_required} and !$USER) {
|
||||
print $IN->redirect(Links::redirect_login_url('modify'));
|
||||
return;
|
||||
}
|
||||
|
||||
# Perform the link modification
|
||||
if ($IN->param('modify')) {
|
||||
_modify();
|
||||
}
|
||||
elsif ($USER) {
|
||||
# Display the link modify form (for a specific link)
|
||||
if ($IN->param('LinkID')) {
|
||||
_modify_passed_in();
|
||||
}
|
||||
else {
|
||||
_list_owned_links();
|
||||
}
|
||||
}
|
||||
# Display the link modify form (used when user_required is off)
|
||||
else {
|
||||
_modify_form();
|
||||
}
|
||||
}
|
||||
|
||||
# ==============================================================
|
||||
|
||||
sub _modify {
|
||||
# --------------------------------------------------------
|
||||
# Modifies a link.
|
||||
#
|
||||
# If payment is enabled and we are processing a payment
|
||||
if ($CFG->{payment}->{enabled} and $IN->param('process_payment')) {
|
||||
my $payment_term = $IN->param('payment_term') || '';
|
||||
my $do = $IN->param('do') || '';
|
||||
if ($payment_term eq 'free') {
|
||||
print $IN->header();
|
||||
my $link = $DB->table('Links')->get(scalar $IN->param('link_id'));
|
||||
my $mtl = Links::Build::build('title', Links::language('LINKS_MODIFY_SUCCESS'), "$CFG->{db_cgi_url}/modify.cgi");
|
||||
if (not $link or ($CFG->{user_required} and $link->{LinkOwner} ne $USER->{Username})) {
|
||||
print Links::SiteHTML::display('error', { error => !$link ? $GT::SQL::ERRORS : Links::language('PAYMENTERR_NOTOWNER'), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
$link = Links::SiteHTML::tags('link', $link);
|
||||
|
||||
# Add some special tags for formatting.
|
||||
$link->{Category} = $DB->table('Category', 'CatLinks')->select('Category.Full_Name', { 'CatLinks.LinkID' => $link->{ID} })->fetchrow;
|
||||
|
||||
# Set ExpiryDate to free
|
||||
$link->{'CatLinks.CategoryID'} = $IN->param('cat_id');
|
||||
$link->{ExpiryDate} = FREE;
|
||||
$link->{ExpiryNotify}= 0;
|
||||
# Update the link
|
||||
$DB->table('Links')->update({ ExpiryDate => FREE, ExpiryNotify => 0 }, { ID => $link->{ID} });
|
||||
# Update the Timestmp for link's categories so they get rebuilt with build changed
|
||||
my @cats = $DB->table('Links', 'CatLinks')->select('CategoryID', { LinkID => $link->{ID} })->fetchall_list;
|
||||
$DB->table('Category')->update({ Timestmp => \'NOW()' }, { ID => \@cats });
|
||||
|
||||
print Links::SiteHTML::display('modify_success', { %$link, main_title_loop => $mtl });
|
||||
}
|
||||
elsif ($do eq 'payment_linked') {
|
||||
print $IN->header;
|
||||
my $link = $DB->table('Links', 'CatLinks')->select({ ID => scalar $IN->param('ID') })->fetchrow_hashref;
|
||||
if (!$link) {
|
||||
print Links::SiteHTML::display('error', { error => Links::language('MODIFY_NOLINKS') });
|
||||
return;
|
||||
}
|
||||
elsif ($CFG->{user_required} and $link->{LinkOwner} ne $USER->{Username}) {
|
||||
print Links::SiteHTML::display('error', { error => Links::language('PAYMENTERR_NOTOWNER') });
|
||||
return;
|
||||
}
|
||||
$link = Links::SiteHTML::tags('link', $link);
|
||||
|
||||
require Links::Payment;
|
||||
my @cid = $DB->table('CatLinks')->select('CategoryID', { LinkID => $link->{ID} })->fetchall_list;
|
||||
my $opt = Links::Payment::load_cat_price(\@cid);
|
||||
if ($opt->{payment_mode} == NOT_ACCEPTED) {
|
||||
print Links::SiteHTML::display('error', { error => Links::language('PAYMENTERR_NOTACCEPTED') });
|
||||
return;
|
||||
}
|
||||
$link->{link_id} = $link->{ID}; # we need a different tag since both Category and Link have ID
|
||||
$opt->{CategoryID} = delete $opt->{ID}; # remove category id
|
||||
$opt->{CategoryDescription} = delete $opt->{Description};
|
||||
$link->{main_title_loop} = Links::Build::build('title', Links::language('LINKS_PAYMENT'), "$CFG->{db_cgi_url}/modify.cgi?do=payment_linked;process_payment=1;modify=1;ID=$link->{link_id}");
|
||||
print Links::SiteHTML::display('payment', { %$link, %$opt });
|
||||
}
|
||||
elsif ($do =~ /^payment_(method|form|direct)$/) {
|
||||
require Links::Payment;
|
||||
my $vars = Links::Payment->$1();
|
||||
my $page = $IN->param('page') || $IN->param('do');
|
||||
my $opt = Links::Payment::load_cat_price($IN->param('cat_id'));
|
||||
if ($opt->{payment_mode} == NOT_ACCEPTED) {
|
||||
print Links::SiteHTML::display('error', { error => Links::language('PAYMENTERR_NOTACCEPTED') });
|
||||
return;
|
||||
}
|
||||
my $link = $DB->table('Links')->get($IN->param('link_id'));
|
||||
print $IN->header();
|
||||
if (not $link or $link->{LinkOwner} ne $USER->{Username}) {
|
||||
print Links::SiteHTML::display('error', { error => !$link ? $GT::SQL::ERRORS : Links::language('PAYMENTERR_NOTOWNER') });
|
||||
return;
|
||||
}
|
||||
$link = Links::SiteHTML::tags('link', $link);
|
||||
|
||||
$link->{main_title_loop} = Links::Build::build('title', Links::language('LINKS_PAYMENT'), "$CFG->{db_cgi_url}/modify.cgi?do=payment_linked;process_payment=1;modify=1;ID=$link->{ID}");
|
||||
print Links::SiteHTML::display($page, { %$vars, %$opt, %$link });
|
||||
}
|
||||
else {
|
||||
print $IN->header;
|
||||
print Links::SiteHTML::display('error', { error => "Invalid action" });
|
||||
}
|
||||
}
|
||||
# Otherwise, modify the link
|
||||
else {
|
||||
my $results = $PLG->dispatch('user_modify_link', \&modify_link, {});
|
||||
if (defined $results->{error}) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('modify', $results);
|
||||
}
|
||||
else {
|
||||
$results->{main_title_loop} = Links::Build::build('title', Links::language('LINKS_MODIFY_SUCCESS'), "$CFG->{db_cgi_url}/modify.cgi");
|
||||
if ($CFG->{payment}->{enabled}) {
|
||||
require Links::Payment;
|
||||
my @cid = $IN->param('CatLinks.CategoryID');
|
||||
my $opt = Links::Payment::load_cat_price(\@cid);
|
||||
print $IN->header();
|
||||
if (exists $opt->{error}) {
|
||||
print Links::SiteHTML::display('error', $opt);
|
||||
}
|
||||
elsif ($opt->{payment_mode} == NOT_ACCEPTED or ($results->{ExpiryDate} >= time)) {
|
||||
print Links::SiteHTML::display('modify_success', $results);
|
||||
}
|
||||
else {# display payment form if the link is expired or payment mode for this category is required or optional
|
||||
$results->{link_id} = $results->{ID}; # we need a different tag since both Category and Link have ID
|
||||
$opt->{CategoryID} = delete $opt->{ID}; # remove category id
|
||||
$opt->{CategoryDescription} = delete $opt->{Description};
|
||||
$results->{main_title_loop} = Links::Build::build('title', Links::language('LINKS_PAYMENT'), "$CFG->{db_cgi_url}/modify.cgi?do=payment_linked;process_payment=1;modify=1;ID=$results->{link_id}");
|
||||
print Links::SiteHTML::display('payment', {%$results,%$opt});
|
||||
}
|
||||
}
|
||||
else {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('modify_success', $results);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub _modify_passed_in {
|
||||
# --------------------------------------------------------
|
||||
# Display link that was passed in.
|
||||
#
|
||||
my $lid = $IN->param('LinkID');
|
||||
my $link_db = $DB->table('Links');
|
||||
my $mtl = Links::Build::build('title', Links::language('LINKS_MODIFY'), "$CFG->{db_cgi_url}/modify.cgi?LinkID=$lid");
|
||||
my $sth = $link_db->select({ ID => $lid, LinkOwner => $USER->{Username} }, VIEWABLE);
|
||||
if ($sth->rows) {
|
||||
my $link = $sth->fetchrow_hashref;
|
||||
my @ids = $DB->table('CatLinks')->select('CategoryID', { LinkID => $link->{ID} })->fetchall_list;
|
||||
$IN->param('CatLinks.CategoryID', \@ids);
|
||||
|
||||
$link->{Contact_Name} ||= $USER->{Name} || $USER->{Username};
|
||||
$link->{Contact_Email} ||= $USER->{Email};
|
||||
|
||||
my $category = {};
|
||||
if ($CFG->{db_gen_category_list} < 2) {
|
||||
require Links::Tools;
|
||||
$category = Links::Tools::category_list();
|
||||
$category->{Category} = sub { Links::Tools::category_list_html() };
|
||||
}
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('modify', {
|
||||
main_title_loop => $mtl,
|
||||
%$link,
|
||||
%$category
|
||||
});
|
||||
}
|
||||
elsif (!$CFG->{user_required}) {
|
||||
_modify_form();
|
||||
}
|
||||
else {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('MODIFY_NOTOWNER'), LinkID => $lid, main_title_loop => $mtl });
|
||||
}
|
||||
}
|
||||
|
||||
sub _list_owned_links {
|
||||
# --------------------------------------------------------
|
||||
# Display a list of links the user owns.
|
||||
#
|
||||
my $link_db = $DB->table('Links');
|
||||
my ($limit, $offset, $nh) = Links::limit_offset();
|
||||
my $mtl = Links::Build::build('title', Links::language('LINKS_MODIFY'), "$CFG->{db_cgi_url}/modify.cgi");
|
||||
$link_db->select_options("ORDER BY Title ASC", "LIMIT $limit OFFSET $offset");
|
||||
my $sth = $link_db->select({
|
||||
LinkOwner => $USER->{Username},
|
||||
# If payment is enabled, we want to show non-validated links to allow
|
||||
# payment to occur, otherwise only show validated ones
|
||||
($CFG->{payment}->{enabled} ? () : (isValidated => 'Yes'))
|
||||
});
|
||||
my $total = $link_db->hits;
|
||||
if (! $sth->rows) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('MODIFY_NOLINKS'), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
my ($toolbar, %paging);
|
||||
my @links;
|
||||
while (my $hash = $sth->fetchrow_hashref) {
|
||||
push @links, Links::SiteHTML::tags('link', $hash);
|
||||
}
|
||||
if ($total > $limit) {
|
||||
my $url = $CFG->{db_cgi_url} . "/" . $IN->url;
|
||||
$toolbar = $DB->html(['Links'], $IN)->toolbar($nh, $limit, $total, $url);
|
||||
%paging = (
|
||||
url => $url,
|
||||
num_hits => $total,
|
||||
max_hits => $limit,
|
||||
current_page => $nh
|
||||
);
|
||||
}
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('modify_select', {
|
||||
link_results_loop => \@links,
|
||||
main_title_loop => $mtl,
|
||||
total => $total,
|
||||
next => $toolbar,
|
||||
paging => \%paging
|
||||
});
|
||||
}
|
||||
|
||||
sub _modify_form {
|
||||
# --------------------------------------------------------
|
||||
# Just display the regular form.
|
||||
#
|
||||
my @id = $IN->param('ID'); # Category ID.
|
||||
my $link = {};
|
||||
print $IN->header();
|
||||
if ($IN->param('LinkID')) {
|
||||
my $lid = $IN->param('LinkID');
|
||||
$link = $DB->table('Links')->select({ ID => $lid }, VIEWABLE)->fetchrow_hashref;
|
||||
if (!$link) {
|
||||
print Links::SiteHTML::display('error', { error => Links::language('MODIFY_INVALIDLINKID', $lid) });
|
||||
return;
|
||||
}
|
||||
if (!@id) {
|
||||
@id = $DB->table('CatLinks')->select('CategoryID', { LinkID => $lid })->fetchall_list;
|
||||
# Set ID to the categories that the link is in so Links::Tools::category_list
|
||||
# pre-selects them
|
||||
$IN->param(ID => \@id);
|
||||
}
|
||||
}
|
||||
|
||||
if (!@id and !$CFG->{db_gen_category_list}) {
|
||||
print Links::SiteHTML::display('error', { error => Links::language('MODIFY_SELCAT') });
|
||||
}
|
||||
else {
|
||||
my $category = {};
|
||||
if ($CFG->{db_gen_category_list} < 2) {
|
||||
require Links::Tools;
|
||||
$category = Links::Tools::category_list();
|
||||
$category->{Category} = sub { Links::Tools::category_list_html() };
|
||||
}
|
||||
print Links::SiteHTML::display('modify', {
|
||||
main_title_loop => Links::Build::build('title', Links::language('LINKS_MODIFY'), "$CFG->{db_cgi_url}/modify.cgi" . (@id ? "?ID=" . join(';ID=', @id) : '')),
|
||||
%$category,
|
||||
%$link
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
sub modify_link {
|
||||
# --------------------------------------------------------
|
||||
# Change the requested link.
|
||||
#
|
||||
my $args = $IN->get_hash();
|
||||
my $db = $DB->table('Links');
|
||||
my %cols = $db->cols;
|
||||
|
||||
# Make it possible to use any column to find the link we're modifying.
|
||||
# Normally, we use the LinkID to find the link, but in some conditions the URL
|
||||
# is used. Using this isn't recommended as you're not guaranteed to get the
|
||||
# same or unique results.
|
||||
my ($column, $value);
|
||||
foreach my $col (keys %cols) {
|
||||
if (exists $args->{'Current_' . $col} and $args->{'Current_' . $col}) {
|
||||
$column = $col;
|
||||
$value = $args->{'Current_' . $col};
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
my $lid = $args->{LinkID};
|
||||
my %ret;
|
||||
if ($CFG->{db_gen_category_list} < 2) {
|
||||
require Links::Tools;
|
||||
%ret = %{Links::Tools::category_list()};
|
||||
$ret{Category} = sub { Links::Tools::category_list_html() };
|
||||
}
|
||||
$ret{main_title_loop} = Links::Build::build('title', Links::language('LINKS_MODIFY'), "$CFG->{db_cgi_url}/modify.cgi" . ($lid ? "?LinkID=$lid" : ''));
|
||||
$ret{LinkID} = $lid;
|
||||
unless ($value or ($lid and $USER)) {
|
||||
return { error => Links::language('MODIFY_NOURL'), %ret };
|
||||
}
|
||||
|
||||
# Find the requested link
|
||||
my ($link, $sth);
|
||||
if ($USER and $lid) {
|
||||
#if ($CFG->{user_required}) {
|
||||
# Mod added back on April 10 by Virginia
|
||||
if ($CFG->{user_required} and $USER->{Status} ne 'Administrator') { # mod by Virginia Lo on Oct 29, 2007
|
||||
$sth = $db->select({ ID => $lid, LinkOwner => $USER->{Username} });
|
||||
}
|
||||
else {
|
||||
$sth = $db->select({ ID => $lid });
|
||||
}
|
||||
$sth->rows or return { error => Links::language('MODIFY_INVALIDLINKID', $lid), %ret };
|
||||
}
|
||||
else {
|
||||
$sth = $db->select({ $column => $value });
|
||||
$sth->rows or return { error => Links::language('MODIFY_BADURL', $value), %ret };
|
||||
}
|
||||
$link = $sth->fetchrow_hashref;
|
||||
|
||||
# Make sure to only allow modifications to validated links. We currently allow
|
||||
# the user to modify expired links.
|
||||
unless ($link->{isValidated} eq 'Yes') {
|
||||
return { error => Links::language('MODIFY_NOLINKS'), %ret };
|
||||
}
|
||||
|
||||
my $new = {%$args};
|
||||
|
||||
# Forced system fields (these aren't in the add_system_fields option)
|
||||
my @system = qw/ID LinkOwner Add_Date Mod_Date Timestmp Date_Checked ExpiryDate ExpiryCounted ExpiryNotify LinkExpired/;
|
||||
my %system = map { $_ => 1 } @system;
|
||||
|
||||
for my $key (keys %cols) {
|
||||
# Users can't modify system fields, so remove them so the columns don't get
|
||||
# modified
|
||||
if (exists $system{$key} or exists $CFG->{add_system_fields}->{$key}) {
|
||||
delete $new->{$key};
|
||||
next;
|
||||
}
|
||||
|
||||
# Use the original link value if it hasn't been passed in from cgi. This is
|
||||
# done to make sure all Links columns pass the column checks (not null, regex,
|
||||
# etc checks). It has to be done for all columns, since column definitions may
|
||||
# have changed since the record was originally inserted.
|
||||
$new->{$key} = $link->{$key} unless defined $args->{$key};
|
||||
}
|
||||
|
||||
# Check that the ExpiryDate is valid for the categories the link is in
|
||||
if ($CFG->{payment}->{enabled}) {
|
||||
require Links::Payment;
|
||||
my $expiry = Links::Payment::check_expiry_date($link);
|
||||
$new->{ExpiryDate} = $expiry if $expiry;
|
||||
}
|
||||
|
||||
# modify() needs the primary key to perform the update
|
||||
$new->{ID} = $link->{ID};
|
||||
|
||||
Links::init_date();
|
||||
$new->{Mod_Date} = GT::Date::date_get();
|
||||
|
||||
# Backwards compatibility
|
||||
$new->{Contact_Name} = $args->{Contact_Name} || $args->{'Contact Name'} || ($USER ? $USER->{Name} : '');
|
||||
$new->{Contact_Email} = $args->{Contact_Email} || $args->{'Contact Email'} || ($USER ? $USER->{Email} : '');
|
||||
|
||||
# Setup the language for GT::SQL
|
||||
local $GT::SQL::ERRORS->{ILLEGALVAL} = Links::language('ADD_ILLEGALVAL');
|
||||
local $GT::SQL::ERRORS->{UNIQUE} = Links::language('ADD_UNIQUE');
|
||||
local $GT::SQL::ERRORS->{NOTNULL} = Links::language('ADD_NOTNULL');
|
||||
local $Links::Table::Links::ERRORS->{NOCATEGORY} = Links::language('MODIFY_NOCATEGORY');
|
||||
$Links::Table::Links::ERRORS if 0; # silence -w
|
||||
|
||||
# On error, file column values need to be restored (since they need to get
|
||||
# re-uploaded). This is done so that the templates show the correct fields on
|
||||
# an error.
|
||||
my %fcols = $db->_file_cols();
|
||||
for (keys %fcols) {
|
||||
$ret{$_} = $link->{$_};
|
||||
}
|
||||
|
||||
# Because we store the change request in the Changes table and do not perform
|
||||
# the modify directly, all the column checks that modify() would normally do
|
||||
# need to be done now.
|
||||
my $fset;
|
||||
unless ($USER and $CFG->{user_direct_mod}) {
|
||||
if (keys %fcols) {
|
||||
require GT::SQL::File;
|
||||
my $file = GT::SQL::File->new({ parent_table => $DB->table('Links'), connect => $DB->{connect} });
|
||||
$fset = $file->pre_file_actions(\%fcols, $new, $args, $new->{ID}) or return { error => $GT::SQL::error, %ret };
|
||||
}
|
||||
|
||||
# The following block of code modifies $new (so that _check_update() works
|
||||
# properly), but we don't want that later on, so make a shallow copy of it.
|
||||
my $new_copy = { %$new };
|
||||
|
||||
# This block of code is pulled from GT::SQL::Table::modify (minus the comments)
|
||||
my $cols = $db->{schema}->{cols};
|
||||
for my $col (keys %$cols) {
|
||||
next unless exists $new_copy->{$col};
|
||||
|
||||
if ($cols->{$col}->{type} eq 'TIMESTAMP') {
|
||||
delete $new_copy->{$col};
|
||||
}
|
||||
elsif ($cols->{$col}->{type} =~ /^(?:.*INT|INTEGER|FLOAT|REAL|DOUBLE|DECIMAL|DATE|TIME|DATETIME)$/ and defined $new_copy->{$col} and $new_copy->{$col} eq '') {
|
||||
$new_copy->{$col} = undef;
|
||||
}
|
||||
elsif ($cols->{$col}->{not_null} and not (defined $new_copy->{$col} and length $new_copy->{$col})) {
|
||||
$new_copy->{$col} = undef;
|
||||
}
|
||||
}
|
||||
|
||||
$db->_check_update($new_copy, { ID => $new_copy->{ID} }) or return { error => $GT::SQL::error, %ret };
|
||||
}
|
||||
|
||||
# Make sure the category id's are valid
|
||||
$IN->param('CatLinks.CategoryID')
|
||||
or return { error => Links::language('MODIFY_NOCATEGORY'), %ret };
|
||||
|
||||
# Set the Category ID's
|
||||
my @c_ids = $IN->param('CatLinks.CategoryID');
|
||||
$new->{'CatLinks.CategoryID'} = $db->clean_category_ids(\@c_ids)
|
||||
or return { error => $GT::SQL::error, %ret };
|
||||
|
||||
# Check if the link is valid
|
||||
if ($CFG->{user_link_validation}) {
|
||||
require Links::Tools;
|
||||
my $status = Links::Tools::link_status($new->{URL});
|
||||
if ($status and $Links::Tools::STATUS_BAD{$status}) {
|
||||
return { error => Links::language('MODIFY_BADSTATUS', $Links::Tools::STATUS_BAD{$status}), %ret };
|
||||
}
|
||||
}
|
||||
|
||||
my $orig_cats = $db->get_categories($new->{ID});
|
||||
my $new_cats;
|
||||
|
||||
# Add the link either directly in, or into the change request table.
|
||||
if ($USER and $CFG->{user_direct_mod}) {
|
||||
if ($USER->{Status} ne 'Administrator' and $link->{LinkOwner} ne $USER->{Username}) {
|
||||
return { error => Links::language('MODIFY_NOTOWNER'), %ret };
|
||||
}
|
||||
|
||||
my $res = $db->modify($new) or return { error => $GT::SQL::error, %ret };
|
||||
$new_cats = $db->get_categories($new->{ID});
|
||||
}
|
||||
else {
|
||||
require GT::Dumper;
|
||||
my $chg_db = $DB->table('Changes');
|
||||
|
||||
# Remove any columns which haven't changed
|
||||
for my $key (keys %cols) {
|
||||
next if not exists $new->{$key} or $key eq 'ID';
|
||||
|
||||
delete $new->{$key} if $new->{$key} eq (defined $link->{$key} ? $link->{$key} : '');
|
||||
}
|
||||
|
||||
# Handle updating the expiry date later on (when the admin does the change
|
||||
# validation). It can't be done here because payments can be made to the link
|
||||
# before the change validation occurs, losing the user's updated expiry date.
|
||||
delete $new->{ExpiryDate};
|
||||
|
||||
# pre_file_actions() pulls the file columns out of the $new hash; put them back
|
||||
# in and save the uploaded file(s) in a temporary location for processing upon
|
||||
# change validation.
|
||||
foreach my $col (keys %fcols) {
|
||||
if (exists $fset->{$col}) {
|
||||
my $fh = $fset->{$col};
|
||||
my $fname = GT::CGI->escape(get_filename($fh));
|
||||
my $fpath = "$CFG->{admin_root_path}/tmp/$new->{ID}-$fname";
|
||||
|
||||
open F, ">$fpath";
|
||||
binmode F; binmode $fh;
|
||||
my $buf;
|
||||
while (read $fh, $buf, 4096) { print F $buf; };
|
||||
close F;
|
||||
|
||||
$new->{$col} = $fpath;
|
||||
$new->{"${col}_filename"} = $fset->{"${col}_filename"} || get_filename($fh);
|
||||
}
|
||||
elsif (exists $fset->{"${col}_del"}) {
|
||||
$new->{"${col}_del"} = $fset->{"${col}_del"};
|
||||
}
|
||||
}
|
||||
|
||||
my $count = $chg_db->count({ LinkID => $new->{ID} });
|
||||
if ($count) {
|
||||
my $href = $chg_db->select('ChgRequest', { LinkID => $new->{ID} })->fetchrow;
|
||||
$href = eval $href;
|
||||
foreach (keys %fcols) {
|
||||
my $fpath = $href->{$_} or next;
|
||||
$fpath ne $new->{$_} or next;
|
||||
$fpath !~ /\.\./ or next;
|
||||
$fpath =~ /^[\w\\\/\-\.%]+$/ or next;
|
||||
-e $fpath or next;
|
||||
$fpath =~ m,^\Q$CFG->{admin_root_path}\E/tmp/, or next;
|
||||
unlink $fpath;
|
||||
}
|
||||
$chg_db->update({ LinkID => $new->{ID}, Username => $link->{LinkOwner}, ChgRequest => GT::Dumper->dump({ data => $new, var => '' }) }, { LinkID => $new->{ID} })
|
||||
or return { error => $GT::SQL::error, %ret };
|
||||
}
|
||||
else {
|
||||
$chg_db->insert({ LinkID => $new->{ID}, Username => $link->{LinkOwner}, ChgRequest => GT::Dumper->dump({ data => $new, var => '' }) })
|
||||
or return { error => $GT::SQL::error, %ret };
|
||||
}
|
||||
my $cdb = $DB->table('Category');
|
||||
foreach my $id (@c_ids) {
|
||||
my $cat = $cdb->get($id, 'HASH', ['Full_Name']);
|
||||
$new_cats->{$id} = $cat->{Full_Name};
|
||||
}
|
||||
}
|
||||
|
||||
# Now email the site admin.
|
||||
if ($CFG->{admin_email_mod}) {
|
||||
my %tags;
|
||||
for my $key (keys %$link) {
|
||||
$tags{"Original_" . $key} = $link->{$key};
|
||||
$tags{"New_" . $key} = exists $new->{$key} ? $new->{$key} : $link->{$key};
|
||||
}
|
||||
# Pull in the extra fields that might be in $new (eg. extra file data)
|
||||
for my $key (keys %$new) {
|
||||
next if exists $tags{"New_" . $key};
|
||||
$tags{"New_" . $key} = $new->{$key};
|
||||
}
|
||||
$tags{Original_Category} = join "\n", sort values %$orig_cats;
|
||||
$tags{Original_Category_loop} = [sort values %$orig_cats];
|
||||
$tags{New_Category} = join "\n", sort values %$new_cats;
|
||||
$tags{New_Category_loop} = [sort values %$new_cats];
|
||||
|
||||
$GT::Mail::error ||= '';
|
||||
Links::send_email('link_modified.eml', \%tags, { admin_email => 1 }) or die "Unable to send message: $GT::Mail::error";
|
||||
}
|
||||
$new->{Category} = join("\n", sort values %$new_cats);
|
||||
$new->{Category_loop} = [sort values %$new_cats];
|
||||
|
||||
# All done!
|
||||
return { %$args, %$link, %$new };
|
||||
}
|
||||
|
||||
sub get_filename {
|
||||
# -------------------------------------------------------------------
|
||||
my $fpath = shift;
|
||||
my @path = split /[\\\/]/, $fpath;
|
||||
return pop @path;
|
||||
}
|
||||
|
||||
1;
|
||||
250
site/slowtwitch.com/cgi-bin/articles/admin/Links/User/Page.pm
Normal file
250
site/slowtwitch.com/cgi-bin/articles/admin/Links/User/Page.pm
Normal file
@@ -0,0 +1,250 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Page.pm,v 1.33 2007/12/19 06:59:12 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::User::Page;
|
||||
# ==================================================================
|
||||
use strict;
|
||||
use Links qw/:objects/;
|
||||
use Links::Build;
|
||||
use Links::SiteHTML;
|
||||
|
||||
sub handle {
|
||||
# --------------------------------------------------------------
|
||||
# Wrap in a subroutine to prevent possible mod_perl probs.
|
||||
#
|
||||
$ENV{PATH_INFO} and ($ENV{PATH_INFO} =~ s/.*page\.cgi//);
|
||||
my $page = $IN->param('g') || $ENV{PATH_INFO} || '';
|
||||
|
||||
# We can display a custom template by passing in p=template (the p is for
|
||||
# page).
|
||||
my $custom = $IN->param('p') || '';
|
||||
return generate_custom_page($custom) if $custom;
|
||||
|
||||
# Clean up page a little.
|
||||
$page =~ s|^/+||;
|
||||
$page =~ s|/+$||;
|
||||
|
||||
# Reset the grand total.
|
||||
$Links::Build::GRAND_TOTAL = 0;
|
||||
|
||||
# Figure out what to look for.
|
||||
my ($new_match) = $CFG->{build_new_url} =~ m{^\Q$CFG->{build_root_url}\E/(.+)};
|
||||
my ($cool_match) = $CFG->{build_cool_url} =~ m{^\Q$CFG->{build_root_url}\E/(.+)};
|
||||
my ($rate_match) = $CFG->{build_ratings_url} =~ m{^\Q$CFG->{build_root_url}\E/(.+)};
|
||||
|
||||
# Strip out the index.html/more*.html if it is there.
|
||||
$page =~ s{/*(?:\Q$CFG->{build_home}\E|\Q$CFG->{build_index}\E|\Q$CFG->{build_more}\E\d+\Q$CFG->{build_extension}\E)$}{};
|
||||
|
||||
if ($new_match and $page =~ m{^\Q$new_match\E(?:/|$)}) {
|
||||
$PLG->dispatch('generate_new', \&generate_new_page);
|
||||
}
|
||||
elsif ($cool_match and $page =~ m{^\Q$cool_match\E(?:/|$)}) {
|
||||
$PLG->dispatch('generate_cool', \&generate_cool_page);
|
||||
}
|
||||
elsif ($rate_match and $page =~ m{^\Q$rate_match\E/?$}) {
|
||||
$PLG->dispatch('generate_rate', \&generate_rate_page);
|
||||
}
|
||||
# By default the detailed page format in dynamic mode will be
|
||||
# "<%config.build_detailed_url%>/<%ID%>.<%build_extension%>", but other certain
|
||||
# formats can be used without breaking other URLs.
|
||||
elsif ($page =~ /\d+\Q$CFG->{build_extension}\E$/) {
|
||||
$PLG->dispatch('generate_detailed', \&generate_detailed_page);
|
||||
}
|
||||
elsif ($page !~ /\S/) {
|
||||
$PLG->dispatch('generate_home', \&generate_home_page);
|
||||
}
|
||||
elsif ($page =~ /(\w+\.cgi)/) {
|
||||
print $IN->redirect("$CFG->{db_cgi_url}/$1");
|
||||
}
|
||||
else {
|
||||
$PLG->dispatch('generate_category', \&generate_category_page);
|
||||
}
|
||||
}
|
||||
|
||||
sub generate_custom_page {
|
||||
# --------------------------------------------------------
|
||||
# Displays a custom template.
|
||||
#
|
||||
my $page = shift;
|
||||
if ($CFG->{dynamic_404_status}) {
|
||||
my $template_set = Links::template_set();
|
||||
if (! Links::template_exists($template_set, "$page.html")) {
|
||||
print "Status: 404" . $GT::CGI::EOL;
|
||||
}
|
||||
}
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display($page, {});
|
||||
}
|
||||
|
||||
sub generate_home_page {
|
||||
# --------------------------------------------------------
|
||||
# Display the home page.
|
||||
#
|
||||
print $IN->header();
|
||||
print Links::Build::build(home => {});
|
||||
}
|
||||
|
||||
sub generate_category_page {
|
||||
# --------------------------------------------------------
|
||||
# This routine will display a category, first thing we need
|
||||
# to do is figure out what category we've been asked for.
|
||||
#
|
||||
my $page_num = 1;
|
||||
my $page = $IN->param('g') || $ENV{PATH_INFO} || '';
|
||||
$page_num = $1 if $page =~ s{/\Q$CFG->{build_more}\E(\d+)\Q$CFG->{build_extension}\E$}{};
|
||||
$page =~ s/\Q$CFG->{build_index}\E$//;
|
||||
$page =~ s|^/+||;
|
||||
$page =~ s|/+$||;
|
||||
my $like = $page;
|
||||
$page =~ y/_/ /;
|
||||
|
||||
# Now we get the ID number of the category based on the URL.
|
||||
my $cat_db = $DB->table('Category');
|
||||
my $id;
|
||||
if ($CFG->{build_category_dynamic} eq 'ID' or $page =~ /^\d+$/) {
|
||||
($id) = $page =~ /(\d+)$/;
|
||||
# Make sure the ID is valid
|
||||
$id = $cat_db->select(ID => { ID => $id })->fetchrow;
|
||||
}
|
||||
else {
|
||||
$id = $cat_db->select(ID => { ($CFG->{build_category_dynamic} || 'Full_Name') => $page })->fetchrow;
|
||||
}
|
||||
|
||||
if (!$id) {
|
||||
# Oops, we may have had a escaped character '_' that wasn't a space. We need
|
||||
# to look it up manually.
|
||||
$like =~ y/'"//d;
|
||||
$id = $cat_db->select(ID => GT::SQL::Condition->new(($CFG->{build_category_dynamic} || 'Full_Name') => LIKE => $like))->fetchrow;
|
||||
}
|
||||
|
||||
# Check for valid sort order.
|
||||
my %opts;
|
||||
$opts{id} = $id;
|
||||
$opts{nh} = $page_num;
|
||||
$opts{sb} = $IN->param('sb');
|
||||
$opts{so} = $IN->param('so');
|
||||
$opts{cat_sb} = $IN->param('cat_sb');
|
||||
$opts{cat_so} = $IN->param('cat_so');
|
||||
unless ($opts{sb} and exists $DB->table('Links')->cols->{$opts{sb}} and (not $opts{so} or $opts{so} =~ /^(?:desc|asc)$/i)) {
|
||||
delete $opts{sb};
|
||||
delete $opts{so};
|
||||
}
|
||||
unless ($opts{cat_sb} and exists $DB->table('Category')->cols->{$opts{cat_sb}} and (not $opts{cat_so} or $opts{cat_so} =~ /^(?:desc|asc)$/i)) {
|
||||
delete $opts{cat_sb};
|
||||
delete $opts{cat_so};
|
||||
}
|
||||
|
||||
if ($id) {
|
||||
print $IN->header();
|
||||
print Links::Build::build('category', \%opts);
|
||||
}
|
||||
else {
|
||||
print "Status: 404" . $GT::CGI::EOL if $CFG->{dynamic_404_status};
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('PAGE_INVALIDCAT', $page) });
|
||||
}
|
||||
}
|
||||
|
||||
sub generate_new_page {
|
||||
# --------------------------------------------------------
|
||||
# Creates a "What's New" page. Set build_span_pages to 1 in setup
|
||||
# and it will create a seperate page for each date.
|
||||
#
|
||||
my ($page, $date);
|
||||
|
||||
$page = $IN->param('g') || $ENV{PATH_INFO} || '';
|
||||
if ($page =~ /\Q$CFG->{build_index}\E$/) {
|
||||
$date = '';
|
||||
}
|
||||
else {
|
||||
($date) = $page =~ m{/([^/]+)\Q$CFG->{build_extension}\E$};
|
||||
}
|
||||
|
||||
if ($date) {
|
||||
my $nh = 1;
|
||||
my $lpp = $CFG->{build_links_per_page} || 25;
|
||||
if ($date =~ s/_(\d+)//) {
|
||||
$nh = $1;
|
||||
}
|
||||
print $IN->header();
|
||||
print Links::Build::build('new_subpage', { date => $date, mh => $lpp, nh => $nh });
|
||||
}
|
||||
elsif ($CFG->{build_new_date_span_pages}) {
|
||||
print $IN->header();
|
||||
print Links::Build::build('new_index', {});
|
||||
}
|
||||
else {
|
||||
print $IN->header();
|
||||
print Links::Build::build('new', {});
|
||||
}
|
||||
}
|
||||
|
||||
sub generate_cool_page {
|
||||
# --------------------------------------------------------
|
||||
# Creates a "What's Cool" page.
|
||||
#
|
||||
my $page = $IN->param('g') || $ENV{PATH_INFO} || '';
|
||||
my $nh = 1;
|
||||
my $mh = $CFG->{build_span_pages} ? $CFG->{build_links_per_page} : 1000;
|
||||
if ($page =~ /\Q$CFG->{build_more}\E(\d+)\Q$CFG->{build_extension}\E$/) {
|
||||
$nh = $1;
|
||||
}
|
||||
print $IN->header();
|
||||
print Links::Build::build('cool', { mh => $mh, nh => $nh });
|
||||
}
|
||||
|
||||
sub generate_rate_page {
|
||||
# --------------------------------------------------------
|
||||
# Creates a Top 10 ratings page.
|
||||
#
|
||||
print $IN->header();
|
||||
print Links::Build::build('rating', {});
|
||||
}
|
||||
|
||||
sub generate_detailed_page {
|
||||
# --------------------------------------------------------
|
||||
# This routine build a single page for every link.
|
||||
#
|
||||
my ($page, $id, $link, $detail_match);
|
||||
|
||||
$page = $IN->param('g') || $ENV{PATH_INFO} || '';
|
||||
($id) = $page =~ /(\d+)\Q$CFG->{build_extension}\E$/;
|
||||
|
||||
# Fetch the category info if the link is in multiple categories and the category
|
||||
# the detailed page was accessed from was passed in. This is done so the next
|
||||
# and previous links are correct.
|
||||
# Note that due to the URL transformation (Links::clean_output), it isn't
|
||||
# possible to pass in the CategoryID unless the detailed url is self generated
|
||||
# (ie. <%detailed_url%> isn't used).
|
||||
if ($id) {
|
||||
my $cat_id = $IN->param('CategoryID');
|
||||
if ($cat_id and $DB->table('CatLinks')->count({ LinkID => $id, CategoryID => $cat_id })) {
|
||||
$link = $DB->table(qw/Links CatLinks Category/)->select({ LinkID => $id, CategoryID => $cat_id })->fetchrow_hashref;
|
||||
}
|
||||
else {
|
||||
$link = $DB->table('Links')->get($id, 'HASH');
|
||||
}
|
||||
}
|
||||
|
||||
if (!$link) {
|
||||
print "Status: 404" . $GT::CGI::EOL if $CFG->{dynamic_404_status};
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('PAGE_INVALIDDETAIL', $page) });
|
||||
return;
|
||||
}
|
||||
|
||||
print $IN->header();
|
||||
print Links::Build::build('detailed', $link);
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,96 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Rate.pm,v 1.20 2007/12/19 06:59:12 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::User::Rate;
|
||||
# ==================================================================
|
||||
use strict;
|
||||
use Links qw/:objects/;
|
||||
use Links::Build;
|
||||
use Links::SiteHTML;
|
||||
|
||||
sub handle {
|
||||
# ---------------------------------------------------
|
||||
# Determine what to do.
|
||||
#
|
||||
my $id = $IN->param('ID');
|
||||
|
||||
# Make sure we are allowed to rate it.
|
||||
if ($CFG->{user_rate_required} and not $USER) {
|
||||
print $IN->redirect(Links::redirect_login_url('rate'));
|
||||
return;
|
||||
}
|
||||
|
||||
# Now figure out what to do.
|
||||
my $mtl = Links::Build::build('title', Links::language('LINKS_RATE'), "$CFG->{db_cgi_url}/rate.cgi");
|
||||
if ($IN->param('rate')) {
|
||||
my $results = $PLG->dispatch('rate_link', \&rate_it, {});
|
||||
$results->{main_title_loop} = $mtl;
|
||||
if (defined $results->{error}) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('rate', $results);
|
||||
}
|
||||
else {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('rate_success', $results);
|
||||
}
|
||||
}
|
||||
elsif (defined $id and ($id =~ /^\d+$/)) {
|
||||
print $IN->header();
|
||||
my $rec = $DB->table('Links')->get($id);
|
||||
unless ($rec) {
|
||||
print Links::SiteHTML::display('error', { error => Links::language('RATE_INVALIDID', $id), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
$rec->{detailed_url} = $CFG->{build_detail_url} . '/' . $DB->table('Links')->detailed_url($rec->{ID}) if $CFG->{build_detailed};
|
||||
print Links::SiteHTML::display('rate', { %$rec, main_title_loop => $mtl });
|
||||
}
|
||||
else {
|
||||
print $IN->redirect($IN->param('d') ? "$CFG->{db_cgi_url}/page.cgi?d=1" : $CFG->{build_root_url} . "/" . ($CFG->{build_home} || ($CFG->{build_index_include} ? $CFG->{build_index} : '')));
|
||||
}
|
||||
}
|
||||
|
||||
sub rate_it {
|
||||
# --------------------------------------------------------
|
||||
# Give this link a rating.
|
||||
#
|
||||
my $id = $IN->param('ID');
|
||||
my $rating = $IN->param('rate');
|
||||
|
||||
# Let's get the link information.
|
||||
my $links = $DB->table('Links');
|
||||
my $rec = $links->get($id);
|
||||
$rec or return { error => Links::language('RATE_INVALIDID', $id) };
|
||||
|
||||
# Make sure we have a valid rating.
|
||||
unless ($rating =~ /^\d\d?$/ and $rating >= 1 and $rating <= 10) {
|
||||
return { error => Links::language('RATE_INVALIDRATE', $rating), %$rec };
|
||||
}
|
||||
|
||||
# Update the rating unless they have already voted.
|
||||
my $clicktrack = $DB->table('ClickTrack');
|
||||
my $rows = $clicktrack->count({ LinkID => $id, IP => $ENV{REMOTE_ADDR}, ClickType => 'Rate' });
|
||||
if ($rows) {
|
||||
return { error => Links::language('RATE_VOTED', $id), %$rec };
|
||||
}
|
||||
else {
|
||||
eval {
|
||||
$clicktrack->insert({ LinkID => $id, IP => $ENV{REMOTE_ADDR}, ClickType => 'Rate', Created => \'NOW()' });
|
||||
|
||||
$rec->{Rating} = ($rec->{Rating} * $rec->{Votes} + $rating) / ++$rec->{Votes};
|
||||
$links->update({ Rating => $rec->{Rating}, Votes => $rec->{Votes} }, { ID => $rec->{ID} });
|
||||
};
|
||||
return $rec;
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
605
site/slowtwitch.com/cgi-bin/articles/admin/Links/User/Review.pm
Normal file
605
site/slowtwitch.com/cgi-bin/articles/admin/Links/User/Review.pm
Normal file
@@ -0,0 +1,605 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Review.pm,v 1.78 2007/11/16 07:12:57 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::User::Review;
|
||||
# ==================================================================
|
||||
use strict;
|
||||
use Links qw/:objects/;
|
||||
use Links::Build;
|
||||
use Links::SiteHTML;
|
||||
|
||||
sub handle {
|
||||
# ------------------------------------------------------------------
|
||||
# Determine what to do.
|
||||
#
|
||||
my $input = $IN->get_hash;
|
||||
if ($input->{add_review}) { $PLG->dispatch('review_add', \&add_review) }
|
||||
elsif ($input->{edit_review}) { $PLG->dispatch('review_edit', \&edit_review) }
|
||||
elsif ($input->{helpful}) { $PLG->dispatch('review_helpful', \&helpful_review) }
|
||||
else { $PLG->dispatch('review_search', \&review_search_results) }
|
||||
return;
|
||||
}
|
||||
# ==================================================================
|
||||
|
||||
sub review_search_results {
|
||||
# ------------------------------------------------------------------
|
||||
# Display a list of validated reviews for a link
|
||||
#
|
||||
my $id = shift;
|
||||
|
||||
my $mtl = Links::Build::build('title', Links::language('LINKS_REVIEW'), "$CFG->{db_cgi_url}/review.cgi");
|
||||
|
||||
# Get our form data and prepare some default data.
|
||||
my $args = $IN->get_hash;
|
||||
$id ||= $args->{ID};
|
||||
$args->{username} = '\*' if $args->{username} eq '*';
|
||||
|
||||
# Return error if no action
|
||||
unless ($args->{keyword} or $args->{ReviewID} or $id) {
|
||||
if ($USER) {
|
||||
$args->{username} ||= $USER->{Username};
|
||||
$IN->param(username => $args->{username});
|
||||
}
|
||||
elsif (!$args->{username} and !$args->{helpful}) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('REVIEW_INVALID_ACTION'), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
# Reset ReviewID to null
|
||||
my $from_helpful = ($args->{helpful}) ? $args->{ReviewID} : '';
|
||||
$args->{ReviewID} = '';
|
||||
|
||||
# Review must be validated to list
|
||||
$args->{Review_Validated} = 'Yes';
|
||||
$args->{nh} = (defined $args->{nh} and $args->{nh} =~ /^(\d+)$/) ? $1 : 1;
|
||||
$args->{mh} = (defined $args->{mh} and $args->{mh} =~ /^(\d+)$/) ? $1 : $CFG->{reviews_per_page};
|
||||
$args->{so} = (defined $args->{so} and $args->{so} =~ /^(asc|desc)$/i) ? $1 : $CFG->{review_sort_order};
|
||||
($args->{sb} and ($args->{sb} =~ /^[\w\s,]+$/) or ($args->{sb} = $CFG->{review_sort_by}));
|
||||
delete $args->{ma};
|
||||
|
||||
my $rec = { noLink => 1 };
|
||||
# If we are listing reviews of a link
|
||||
if ($id) {
|
||||
$id and $args->{ID} = $id;
|
||||
|
||||
# Check if ID is valid
|
||||
$rec = $DB->table('Links')->get($args->{ID});
|
||||
$rec or do {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('REVIEW_INVALIDID', $args->{ID}), main_title_loop => $mtl });
|
||||
return;
|
||||
};
|
||||
$rec = Links::SiteHTML::tags('link', $rec);
|
||||
$args->{Review_LinkID} = $args->{ID};
|
||||
$args->{ww} = 1;
|
||||
}
|
||||
# If we have a user to list
|
||||
elsif ($args->{username}) {
|
||||
$args->{Review_LinkID} = '';
|
||||
$args->{Review_Owner} = $args->{username};
|
||||
$args->{'Review_Owner-opt'} = '=';
|
||||
}
|
||||
elsif ($IN->param('ReviewID')) {
|
||||
$args->{ReviewID} = $IN->param('ReviewID');
|
||||
$args->{'ReviewID-opt'} = '=';
|
||||
}
|
||||
|
||||
my $reviews = $DB->table('Reviews');
|
||||
my $review_sth = $reviews->query_sth($args);
|
||||
my $review_hits = $reviews->hits;
|
||||
|
||||
# Return if no results.
|
||||
unless ($review_hits) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('REVIEW_NORESULTS', $args->{ID} || $args->{username}), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
|
||||
my @review_results_loop;
|
||||
Links::init_date();
|
||||
my $today = GT::Date::date_get(time, GT::Date::FORMAT_DATETIME);
|
||||
my %review_cache;
|
||||
my $last_review = 0;
|
||||
while (my $review = $review_sth->fetchrow_hashref) {
|
||||
$review->{Review_Count} = $reviews->count({ Review_LinkID => $review->{Review_LinkID}, Review_Validated => 'Yes' });
|
||||
$review->{Review_IsNew} = (GT::Date::date_diff($today, $review->{Review_Date}) < $CFG->{review_days_old});
|
||||
if ($CFG->{review_allow_modify} and $USER->{Username} eq $review->{Review_Owner}) {
|
||||
if ($CFG->{review_modify_timeout}) {
|
||||
my $oldfmt = GT::Date::date_get_format();
|
||||
GT::Date::date_set_format(GT::Date::FORMAT_DATETIME);
|
||||
my $timeout = GT::Date::date_get(time - $CFG->{review_modify_timeout} * 60);
|
||||
my $date = $review->{Review_ModifyDate} =~ /^0000-00-00 00:00:00/ ? $review->{Review_Date} : $review->{Review_ModifyDate};
|
||||
if (GT::Date::date_is_greater($date, $timeout)) {
|
||||
$review->{Review_CanModify} = 1;
|
||||
}
|
||||
GT::Date::date_set_format($oldfmt);
|
||||
}
|
||||
else {
|
||||
$review->{Review_CanModify} = 1;
|
||||
}
|
||||
}
|
||||
if ($review->{Review_ModifyDate} ne $review->{Review_Date} and $review->{Review_ModifyDate} !~ /^0000-00-00 00:00:00/) {
|
||||
$review->{Review_ModifyDate} = GT::Date::date_transform($review->{Review_ModifyDate}, GT::Date::FORMAT_DATETIME, $CFG->{date_review_format});
|
||||
}
|
||||
else {
|
||||
delete $review->{Review_ModifyDate};
|
||||
}
|
||||
$review->{Review_Date} = GT::Date::date_transform($review->{Review_Date}, GT::Date::FORMAT_DATETIME, $CFG->{date_review_format});
|
||||
$review->{Num} = $review->{Review_WasHelpful} + $review->{Review_WasNotHelpful};
|
||||
($from_helpful eq $review->{ReviewID}) and $review->{last_helpful} = 1;
|
||||
$CFG->{review_convert_br_tags} and $review->{Review_Contents} = _translate_html($review->{Review_Contents});
|
||||
|
||||
# Add the link info to the review
|
||||
if ($args->{username} or $args->{ReviewID} or $args->{keyword}) {
|
||||
my $catlink = $DB->table('CatLinks', 'Category', 'Links');
|
||||
unless (exists $review_cache{$review->{Review_LinkID}}) {
|
||||
$review_cache{$review->{Review_LinkID}} = $catlink->get({ LinkID => $review->{Review_LinkID} });
|
||||
}
|
||||
if ($last_review != $review->{Review_LinkID}) {
|
||||
my $names = $review_cache{$review->{Review_LinkID}};
|
||||
$review->{LinkID} = $names->{ID};
|
||||
$review->{cat_linked} = sub { Links::Build::build('title_linked', { name => $names->{Full_Name}, complete => 1 }) };
|
||||
$review->{cat_loop} = Links::Build::build('title', $names->{Full_Name});
|
||||
foreach my $key (keys %$names) {
|
||||
next if ($key eq 'ID');
|
||||
exists $review->{$key} or ($review->{$key} = $names->{$key});
|
||||
}
|
||||
}
|
||||
$last_review = $review->{Review_LinkID};
|
||||
}
|
||||
push @review_results_loop, $review;
|
||||
}
|
||||
|
||||
my ($toolbar, %paging);
|
||||
if ($review_hits > $args->{mh}) {
|
||||
my $url = $CFG->{db_cgi_url} . "/" . $IN->url;
|
||||
$url =~ s/([;&?]?)nh=(\d+)/($1 and $1 eq '?') ? '?' : ''/eg;
|
||||
$url =~ s/[;&]helpful=1//eg;
|
||||
$toolbar = $DB->html($reviews, $args)->toolbar($args->{nh} || 1, $args->{mh} || 25, $review_hits, $url);
|
||||
%paging = (
|
||||
url => $url,
|
||||
num_hits => $review_hits,
|
||||
max_hits => $args->{mh} || 25,
|
||||
current_page => $args->{nh} || 1
|
||||
);
|
||||
}
|
||||
else {
|
||||
$toolbar = '';
|
||||
}
|
||||
|
||||
# Some statistics for review list
|
||||
my ($review_stats,$review_count);
|
||||
if (!defined $args->{keyword}) {
|
||||
if ($args->{username}) {
|
||||
%$review_stats = map { $_ => $reviews->count({ Review_Owner => $args->{username}, Review_Rating => $_, Review_Validated => 'Yes' }) } (1 .. 5);
|
||||
$review_count = $reviews->count({ Review_Owner => $args->{username}, Review_Validated => 'Yes'} );
|
||||
}
|
||||
else {
|
||||
%$review_stats = map { $_ => $reviews->count({ Review_LinkID => $args->{ID}, Review_Rating => $_, Review_Validated => 'Yes' }) } (1 .. 5);
|
||||
$review_count = $reviews->count({ Review_LinkID => $args->{ID}, Review_Validated => 'Yes'});
|
||||
}
|
||||
if ($review_count) {
|
||||
for (1 .. 5) {
|
||||
$review_stats->{'p' . $_} = $review_stats->{$_} * 150 / $review_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
$review_stats ||= { noStats => 1 };
|
||||
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('review_search_results', {
|
||||
%$review_stats,
|
||||
%$rec,
|
||||
show_link_info => ($args->{username} or $args->{ReviewID} or $args->{keyword}),
|
||||
main_title_loop => $mtl,
|
||||
Review_Count => $review_hits,
|
||||
Review_Loop => \@review_results_loop,
|
||||
Review_SpeedBar => $toolbar,
|
||||
paging => \%paging
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
sub add_review {
|
||||
# ------------------------------------------------------------------
|
||||
# Add a review (only logged in users can add reviews if required)
|
||||
#
|
||||
my $id = $IN->param('ID') || '';
|
||||
|
||||
my $mtl = Links::Build::build('title', Links::language('LINKS_REVIEW_ADD'), "$CFG->{db_cgi_url}/review.cgi");
|
||||
|
||||
# Check if we have a valid ID
|
||||
my $db = $DB->table('Links');
|
||||
my $rec = $db->get($id);
|
||||
unless ($id =~ /^\d+$/ and $rec) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('REVIEW_INVALIDID', $id), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
$rec = Links::SiteHTML::tags('link', $rec);
|
||||
$rec->{anonymous} = !$CFG->{user_review_required};
|
||||
|
||||
# Only logged in users can add reviews (if required) or must redirect to the login page
|
||||
if ($CFG->{user_review_required} and !$USER) {
|
||||
print $IN->redirect(Links::redirect_login_url('review'));
|
||||
return;
|
||||
}
|
||||
|
||||
my ($cat_id, $cat_name) = each %{$db->get_categories($id)};
|
||||
my %title = (
|
||||
title_loop => Links::Build::build('title', "$cat_name/$rec->{Title}"),
|
||||
title => sub { Links::Build::build('title_unlinked', "$cat_name/$rec->{Title}") },
|
||||
title_linked => sub { Links::Build::build('title_linked', "$cat_name/$rec->{Title}") }
|
||||
);
|
||||
|
||||
print $IN->header();
|
||||
# If we have a review to add from a form
|
||||
if ($IN->param('add_this_review')) {
|
||||
my $results = $PLG->dispatch('add_this_review', \&_add_this_review, $rec);
|
||||
|
||||
# If we have error
|
||||
if (defined $results->{error}) {
|
||||
print Links::SiteHTML::display('review_add', { %$results, %$rec, %title, main_title_loop => $mtl });
|
||||
}
|
||||
# Return to add success page
|
||||
else {
|
||||
print Links::SiteHTML::display('review_add_success', { %$results, %$rec, %title, main_title_loop => $mtl });
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ($USER) {
|
||||
my $reviews = $DB->table('Reviews');
|
||||
my $rc = $reviews->count({ Review_LinkID => $id, Review_Owner => $USER->{Username} });
|
||||
# Keep pre 3.2.0 behaviour of allowing the user to edit their existing review
|
||||
if ($rc == 1 and $CFG->{review_max_reviews} == 1) {
|
||||
my $review = $reviews->select({ Review_LinkID => $id, Review_Owner => $USER->{Username} })->fetchrow_hashref;
|
||||
my $oldfmt = GT::Date::date_get_format();
|
||||
GT::Date::date_set_format(GT::Date::FORMAT_DATETIME);
|
||||
my $timeout = GT::Date::date_get(time - $CFG->{review_modify_timeout} * 60);
|
||||
my $date = $review->{Review_ModifyDate} =~ /^0000-00-00 00:00:00/ ? $review->{Review_Date} : $review->{Review_ModifyDate};
|
||||
if (not $CFG->{review_allow_modify} or $review->{Review_Validated} eq 'No' or ($CFG->{review_modify_timeout} and GT::Date::date_is_smaller($date, $timeout))) {
|
||||
print Links::SiteHTML::display('error', { error => Links::language('REVIEW_MAX_REVIEWS', $CFG->{review_max_reviews}), main_title_loop => $mtl });
|
||||
}
|
||||
else {
|
||||
print Links::SiteHTML::display('review_edit', {
|
||||
%$rec, %title, confirm => 1,
|
||||
main_title_loop => Links::Build::build('title', Links::language('LINKS_REVIEW_EDIT'), "$CFG->{db_cgi_url}/review.cgi")
|
||||
});
|
||||
}
|
||||
GT::Date::date_set_format($oldfmt);
|
||||
return;
|
||||
}
|
||||
elsif ($CFG->{review_max_reviews} and $rc + 1 > $CFG->{review_max_reviews}) {
|
||||
print Links::SiteHTML::display('error', { error => Links::language('REVIEW_MAX_REVIEWS', $CFG->{review_max_reviews}), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
}
|
||||
# We are displaying an add review form
|
||||
print Links::SiteHTML::display('review_add', { %$rec, %title, main_title_loop => $mtl });
|
||||
}
|
||||
}
|
||||
|
||||
sub _add_this_review {
|
||||
# ------------------------------------------------------------------
|
||||
# Add this review
|
||||
#
|
||||
|
||||
# Get our form data and some default data.
|
||||
my $rec = shift;
|
||||
my $reviews = $DB->table('Reviews');
|
||||
my $id = $IN->param('ID');
|
||||
my $input = $IN->get_hash;
|
||||
$input->{Review_LinkID} = $id;
|
||||
$input->{Review_Validated} = ($CFG->{review_auto_validate} == 1 and $USER or $CFG->{review_auto_validate} == 2) ? 'Yes' : 'No';
|
||||
$input->{Review_WasHelpful} = 0 ;
|
||||
$input->{Review_WasNotHelpful} = 0 ;
|
||||
$input->{Host} = $ENV{REMOTE_HOST} ? "$ENV{REMOTE_HOST} ($ENV{REMOTE_ADDR})" : $ENV{REMOTE_ADDR} ? $ENV{REMOTE_ADDR} : 'none';
|
||||
$input->{Referer} = $ENV{HTTP_REFERER} ? $ENV{HTTP_REFERER} : 'none';
|
||||
|
||||
# Get the review owner
|
||||
$input->{Review_Owner} = $USER ? $USER->{Username} : 'admin';
|
||||
|
||||
if (not $CFG->{user_review_required} and not $USER) {
|
||||
$input->{Review_GuestName} or return { error => Links::language('REVIEW_GUEST_NAME_REQUIRED') };
|
||||
$input->{Review_GuestEmail} or return { error => Links::language('REVIEW_GUEST_EMAIL_REQUIRED') };
|
||||
}
|
||||
|
||||
# Make sure we have a valid rating.
|
||||
my $cols = $reviews->cols;
|
||||
if (exists $cols->{Review_Rating} and $cols->{Review_Rating}->{not_null} and ($input->{Review_Rating} !~ /^\d$/ or $input->{Review_Rating} < 1 or $input->{Review_Rating} > 5)) {
|
||||
return { error => Links::language('REVIEW_RATING', $input->{Review_Rating}) };
|
||||
}
|
||||
|
||||
# Set date review to today's date.
|
||||
Links::init_date();
|
||||
$input->{Review_Date} = GT::Date::date_get(time, GT::Date::FORMAT_DATETIME);
|
||||
$input->{Review_ModifyDate} = $input->{Review_Date};
|
||||
|
||||
# Check that the number of reviews the user owns is under the limit.
|
||||
if ($USER and $CFG->{review_max_reviews} and
|
||||
$CFG->{review_max_reviews} < $reviews->count({ Review_LinkID => $id, Review_Owner => $USER->{Username} }) + 1) {
|
||||
return { error => Links::language('REVIEW_MAX_REVIEWS', $CFG->{review_max_reviews}) };
|
||||
}
|
||||
|
||||
# Change the language.
|
||||
local $GT::SQL::ERRORS->{ILLEGALVAL} = Links::language('ADD_ILLEGALVAL');
|
||||
local $GT::SQL::ERRORS->{UNIQUE} = Links::language('ADD_UNIQUE');
|
||||
local $GT::SQL::ERRORS->{NOTNULL} = Links::language('ADD_NOTNULL');
|
||||
|
||||
# Add the review.
|
||||
# The review will be added only if Review_email_2 is blank
|
||||
my $added_id = $input->{Review_email_2} ? 1 : $reviews->add($input);
|
||||
$input->{ReviewID} = $added_id;
|
||||
unless ($added_id) {
|
||||
my $error = "<ul><li>" . join("</li><li>", $reviews->error) . "</li></ul>";
|
||||
return { error => $error };
|
||||
}
|
||||
|
||||
# Format the date for sending email
|
||||
$input->{Review_Date} = GT::Date::date_transform($input->{Review_Date}, GT::Date::FORMAT_DATETIME, $CFG->{date_review_format});
|
||||
|
||||
# Mail the email.
|
||||
if ($CFG->{admin_email_review_add}) {
|
||||
Links::send_email('review_added.eml', { %{$USER || {}}, %$input, %$rec }, { admin_email => 1 }) or die "Unable to send mail: $GT::Mail::error";
|
||||
}
|
||||
|
||||
# Review added successfully, return to review_add_success page
|
||||
$CFG->{review_convert_br_tags} and $input->{Review_Contents} = _translate_html($input->{Review_Contents});
|
||||
return $input;
|
||||
}
|
||||
|
||||
sub edit_review {
|
||||
# ------------------------------------------------------------------
|
||||
# Edit a review (only logged in users can edit their reviews)
|
||||
#
|
||||
my $id = $IN->param('ID') || '';
|
||||
my $rid = $IN->param('ReviewID');
|
||||
|
||||
my $mtl = Links::Build::build('title', Links::language('LINKS_REVIEW_EDIT'), "$CFG->{db_cgi_url}/review.cgi");
|
||||
|
||||
if (!$CFG->{review_allow_modify}) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('REVIEW_MODIFY_DENIED'), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
|
||||
# Only logged in users can update their reviews or must redirect to the login page
|
||||
if (!$USER) {
|
||||
print $IN->redirect(Links::redirect_login_url('review'));
|
||||
return;
|
||||
}
|
||||
|
||||
# Check if we have a valid ID
|
||||
my $db = $DB->table('Links');
|
||||
my $rec = $db->get($id);
|
||||
unless (($id =~ /^\d+$/) and $rec) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('REVIEW_INVALIDID', $id), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
$rec = Links::SiteHTML::tags('link', $rec);
|
||||
|
||||
# If a ReviewID isn't passed in and they have more than one review, then just edit the first review
|
||||
my $review = $DB->table('Reviews')->select({ Review_LinkID => $id, Review_Owner => $USER->{Username}, $rid ? (ReviewID => $rid) : () })->fetchrow_hashref;
|
||||
if (!$review) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('REVIEW_NOT_EXISTS', $id), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
elsif ($review->{Review_Validated} eq 'No') {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('REVIEW_ADD_WAIT', $id), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
|
||||
# Has the review modify period passed?
|
||||
if ($CFG->{review_modify_timeout}) {
|
||||
my $oldfmt = GT::Date::date_get_format();
|
||||
GT::Date::date_set_format(GT::Date::FORMAT_DATETIME);
|
||||
my $timeout = GT::Date::date_get(time - $CFG->{review_modify_timeout} * 60);
|
||||
my $date = $review->{Review_ModifyDate} =~ /^0000-00-00 00:00:00/ ? $review->{Review_Date} : $review->{Review_ModifyDate};
|
||||
my $smaller = GT::Date::date_is_smaller($date, $timeout);
|
||||
GT::Date::date_set_format($oldfmt);
|
||||
if ($smaller) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('error', { error => Links::language('REVIEW_MODIFY_TIMEOUT', $CFG->{review_modify_timeout}), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
my ($cat_id, $cat_name) = each %{$db->get_categories($id)};
|
||||
my %title = (
|
||||
title_loop => Links::Build::build('title', "$cat_name/$rec->{Title}"),
|
||||
title => sub { Links::Build::build('title_unlinked', "$cat_name/$rec->{Title}") },
|
||||
title_linked => sub { Links::Build::build('title_linked', "$cat_name/$rec->{Title}") }
|
||||
);
|
||||
|
||||
# If we have a review to update from a form
|
||||
if ($IN->param('update_this_review')) {
|
||||
my $results = $PLG->dispatch('update_this_review', \&_update_this_review, $rec);
|
||||
|
||||
# If we have error
|
||||
if (defined $results->{error}) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('review_edit', { %$results, %$rec, %title, main_title_loop => $mtl });
|
||||
}
|
||||
# Return to edit success page
|
||||
else {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('review_edit_success', { %$results, %$rec, %title, main_title_loop => $mtl });
|
||||
}
|
||||
}
|
||||
# We are displaying an edit review form
|
||||
elsif ($IN->param('confirmed')) {
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('review_edit', { %$rec, %$review, %title, main_title_loop => $mtl });
|
||||
}
|
||||
|
||||
# Else invalid action
|
||||
else {
|
||||
return review_search_results();
|
||||
}
|
||||
}
|
||||
|
||||
sub _update_this_review {
|
||||
# ------------------------------------------------------------------
|
||||
# Edit this review
|
||||
#
|
||||
# Get our link record.
|
||||
my $rec = shift;
|
||||
|
||||
# Get our form data and some default data.
|
||||
my $input = $IN->get_hash;
|
||||
my $reviews = $DB->table('Reviews');
|
||||
my $id = $IN->param('ID');
|
||||
$input->{Review_LinkID} = $id;
|
||||
$input->{Review_Validated} = ($CFG->{review_auto_validate} == 1 and $USER or $CFG->{review_auto_validate} == 2) ? 'Yes' : 'No';
|
||||
$input->{Review_WasHelpful} = 0 ;
|
||||
$input->{Review_WasNotHelpful} = 0 ;
|
||||
$input->{Host} = $ENV{REMOTE_HOST} ? "$ENV{REMOTE_HOST} ($ENV{REMOTE_ADDR})" : $ENV{REMOTE_ADDR} ? $ENV{REMOTE_ADDR} : 'none';
|
||||
$input->{Referer} = $ENV{HTTP_REFERER} ? $ENV{HTTP_REFERER} : 'none';
|
||||
|
||||
# Get the review owner
|
||||
$input->{Review_Owner} = $USER->{Username};
|
||||
|
||||
# Check if this review is valid for this user
|
||||
my $rows = $reviews->get({ Review_LinkID => $id, Review_Owner => $USER->{Username}, Review_Validated => 'Yes' });
|
||||
return { error => Links::language('REVIEW_INVALID_UPDATE') } unless $rows;
|
||||
|
||||
# Make sure we have a valid rating.
|
||||
my $cols = $reviews->cols;
|
||||
if (exists $cols->{Review_Rating} and $cols->{Review_Rating}->{not_null} and ($input->{Review_Rating} !~ /^\d$/ or $input->{Review_Rating} < 1 or $input->{Review_Rating} > 5)) {
|
||||
return { error => Links::language('REVIEW_RATING', $input->{Review_Rating}) };
|
||||
}
|
||||
|
||||
# Has the review modify period passed?
|
||||
if ($CFG->{review_modify_timeout}) {
|
||||
my $oldfmt = GT::Date::date_get_format();
|
||||
GT::Date::date_set_format(GT::Date::FORMAT_DATETIME);
|
||||
my $timeout = GT::Date::date_get(time - $CFG->{review_modify_timeout} * 60);
|
||||
my $date = $rows->{Review_ModifyDate} =~ /^0000-00-00 00:00:00/ ? $rows->{Review_Date} : $rows->{Review_ModifyDate};
|
||||
my $smaller = GT::Date::date_is_smaller($date, $timeout);
|
||||
GT::Date::date_set_format($oldfmt);
|
||||
if ($smaller) {
|
||||
return { error => Links::language('REVIEW_MODIFY_TIMEOUT', $CFG->{review_modify_timeout}) };
|
||||
}
|
||||
}
|
||||
|
||||
# Set date review to today's date.
|
||||
Links::init_date();
|
||||
delete $input->{Review_Date};
|
||||
$input->{Review_ModifyDate} = GT::Date::date_get(time, GT::Date::FORMAT_DATETIME);
|
||||
|
||||
# Change the language.
|
||||
local $GT::SQL::ERRORS->{ILLEGALVAL} = Links::language('ADD_ILLEGALVAL');
|
||||
local $GT::SQL::ERRORS->{UNIQUE} = Links::language('ADD_UNIQUE');
|
||||
local $GT::SQL::ERRORS->{NOTNULL} = Links::language('ADD_NOTNULL');
|
||||
|
||||
# Update the record.
|
||||
$reviews->modify($input, { ReviewID => $input->{ReviewID} }) or return { error => $GT::SQL::error };
|
||||
|
||||
# Delete the review track from this ReviewID
|
||||
$DB->table('ClickTrack')->delete({ ReviewID => $input->{ReviewID}, ClickType => 'Review' }) or return { error => $GT::SQL::error };
|
||||
|
||||
# Format the date for sending email
|
||||
$input->{Review_Date} = GT::Date::date_transform($input->{Review_ModifyDate}, GT::Date::FORMAT_DATETIME, $CFG->{date_review_format});
|
||||
|
||||
# Mail the email.
|
||||
if ($CFG->{admin_email_review_mod}) {
|
||||
my %tags;
|
||||
foreach my $key (keys %$rows) {
|
||||
$tags{"Original_$key"} = $rows->{$key};
|
||||
}
|
||||
foreach my $key (keys %$input) {
|
||||
$tags{"New_$key"} = $input->{$key};
|
||||
}
|
||||
|
||||
Links::send_email('review_modified.eml', { %$USER, %tags, %$rec }, { admin_email => 1 }) or die "Unable to send mail: $GT::Mail::error";
|
||||
}
|
||||
|
||||
# Review added successfully, return to review_add_success page
|
||||
$CFG->{review_convert_br_tags} and $input->{Review_Contents} = _translate_html($input->{Review_Contents});
|
||||
return $input;
|
||||
|
||||
}
|
||||
|
||||
sub helpful_review {
|
||||
# ------------------------------------------------------------------
|
||||
# Review was helpful or not
|
||||
#
|
||||
my $reviewID = $IN->param('ReviewID');
|
||||
|
||||
my $mtl = Links::Build::build('title', Links::language('LINKS_REVIEW'), "$CFG->{db_cgi_url}/review.cgi");
|
||||
|
||||
# Get our Reviews db object
|
||||
my $db = $DB->table('Reviews');
|
||||
my $rec = $db->get($reviewID);
|
||||
|
||||
if (!$rec) {
|
||||
print $IN->header;
|
||||
print Links::SiteHTML::display('error', { error => Links::language('REVIEW_INVALIDID', $rec->{Review_Subject}), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
|
||||
# Update the rating unless they have already voted.
|
||||
my $click_db = $DB->table('ClickTrack');
|
||||
my $rows = $click_db->count({ ReviewID => $rec->{ReviewID}, IP => $ENV{REMOTE_ADDR}, ClickType => 'Review' });
|
||||
if ($rows) {
|
||||
print $IN->header;
|
||||
print Links::SiteHTML::display('error', { error => Links::language('REVIEW_VOTED', $rec->{Review_Subject}), main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
else {
|
||||
eval {
|
||||
$click_db->insert({ LinkID => $rec->{Review_LinkID}, ReviewID => $rec->{ReviewID}, IP => $ENV{REMOTE_ADDR}, ClickType => 'Review', Created => \"NOW()" });
|
||||
# Update the Timestmp for the link so that the detailed page gets rebuilt with build changed
|
||||
$DB->table('Links')->update({ Timestmp => \'NOW()' }, { ID => $rec->{Review_LinkID} });
|
||||
};
|
||||
}
|
||||
|
||||
# Change the language.
|
||||
local $GT::SQL::ERRORS->{ILLEGALVAL} = Links::language('ADD_ILLEGALVAL');
|
||||
local $GT::SQL::ERRORS->{UNIQUE} = Links::language('ADD_UNIQUE');
|
||||
local $GT::SQL::ERRORS->{NOTNULL} = Links::language('ADD_NOTNULL');
|
||||
|
||||
# If this review was helpful
|
||||
if ($IN->param('yes')) {
|
||||
if (!$db->update({ Review_WasHelpful => $rec->{Review_WasHelpful} + 1 }, { ReviewID => $reviewID })) {
|
||||
print $IN->header;
|
||||
print Links::SiteHTML::display('error', { error => $db->error, main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!$db->update({ Review_WasNotHelpful => $rec->{Review_WasNotHelpful} + 1 }, { ReviewID => $reviewID })) {
|
||||
print $IN->header;
|
||||
print Links::SiteHTML::display('error', { error => $db->error, main_title_loop => $mtl });
|
||||
return;
|
||||
}
|
||||
}
|
||||
return review_search_results();
|
||||
}
|
||||
|
||||
sub _translate_html {
|
||||
# -------------------------------------------------------------------
|
||||
# Translate contents to html format
|
||||
#
|
||||
my $html = shift;
|
||||
$html = GT::CGI::html_escape($html);
|
||||
$html =~ s,\r?\n,<br />,g;
|
||||
return $html;
|
||||
}
|
||||
|
||||
1;
|
||||
359
site/slowtwitch.com/cgi-bin/articles/admin/Links/User/Search.pm
Normal file
359
site/slowtwitch.com/cgi-bin/articles/admin/Links/User/Search.pm
Normal file
@@ -0,0 +1,359 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Search.pm,v 1.48 2006/08/08 23:30:09 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::User::Search;
|
||||
# ==================================================================
|
||||
use strict;
|
||||
use Links qw/:objects/;
|
||||
use Links::SiteHTML;
|
||||
use Links::Build;
|
||||
|
||||
my $time_hires;
|
||||
|
||||
sub handle {
|
||||
#--------------------------------------------------------------------------------
|
||||
# Determine whether we are displaying the search form, or doing a
|
||||
# search.
|
||||
#
|
||||
my $db = $DB->table('Links');
|
||||
my $results = {};
|
||||
my $args = $IN->get_hash;
|
||||
|
||||
# Remove search fields we aren't allowed to search on.
|
||||
my @bad = (@{$CFG->{search_blocked}}, qw/isValidated ExpiryDate/);
|
||||
for my $col (@bad) {
|
||||
$col =~ s/^\s*|\s*$//g;
|
||||
if ($args->{$col}) {
|
||||
delete $args->{$col};
|
||||
$IN->delete($col);
|
||||
}
|
||||
for (qw(lt gt opt le ge ne)) {
|
||||
delete $args->{"$col-$_"};
|
||||
$IN->delete("$col-$_");
|
||||
}
|
||||
}
|
||||
|
||||
# If query is set we know we are searching.
|
||||
return search() if defined $args->{query} and $args->{query} =~ /\S/;
|
||||
|
||||
# Otherwise, if we pass in a field name, we can search on that too.
|
||||
foreach (keys %{$db->cols}) {
|
||||
for my $opt ('', qw/-lt -gt -le -ge -ne/) {
|
||||
return search() if defined $args->{"$_$opt"} and length $args->{"$_$opt"};
|
||||
}
|
||||
}
|
||||
|
||||
print $IN->header();
|
||||
print Links::SiteHTML::display('search', { main_title_loop => Links::Build::build('title', Links::language('LINKS_SEARCH'), "$CFG->{db_cgi_url}/search.cgi") });
|
||||
}
|
||||
|
||||
sub search {
|
||||
# ------------------------------------------------------------------
|
||||
# Do the search and print out the results.
|
||||
#
|
||||
my $results = $PLG->dispatch('search_results', \&query, {});
|
||||
if (defined $results->{error}) {
|
||||
print $IN->header();
|
||||
$results->{main_title_loop} = Links::Build::build('title', Links::language('LINKS_SEARCH'), "$CFG->{db_cgi_url}/search.cgi");
|
||||
print Links::SiteHTML::display('search', $results);
|
||||
}
|
||||
else {
|
||||
print $IN->header();
|
||||
$results->{main_title_loop} = Links::Build::build('title', Links::language('LINKS_SEARCH_RESULTS'), "$CFG->{db_cgi_url}/search.cgi");
|
||||
print Links::SiteHTML::display('search_results', $results);
|
||||
}
|
||||
if ($CFG->{debug_level} > 1) {
|
||||
print "<blockquote><pre>", GT::SQL->query_stack_disp , "</pre></blockquote>";
|
||||
}
|
||||
}
|
||||
|
||||
sub query {
|
||||
# ------------------------------------------------------------------
|
||||
# Query the database.
|
||||
#
|
||||
# First get our search options.
|
||||
my $args = $IN->get_hash;
|
||||
if ($args->{query}) {
|
||||
$args->{query} =~ s/^\s+//;
|
||||
$args->{query} =~ s/\s+$//;
|
||||
}
|
||||
$args->{bool} = (defined $args->{bool} and $args->{bool} =~ /^(and|or)$/i) ? uc $1 : $CFG->{search_bool};
|
||||
$args->{nh} = (defined $args->{nh} and $args->{nh} =~ /^(\d+)$/) ? $1 : 1;
|
||||
$args->{mh} = (defined $args->{mh} and $args->{mh} =~ /^\d+$/) ? $args->{mh} : $CFG->{search_maxhits};
|
||||
$args->{mh} = 200 if $args->{mh} > 200; # Safety limit
|
||||
$args->{substring} = defined $args->{substring} ? $args->{substring} : $CFG->{search_substring};
|
||||
$args->{so} = (defined $args->{so} and $args->{so} =~ /^(asc|desc)$/i) ? $1 : '';
|
||||
$args->{sb} and ($args->{sb} =~ /^[\w\s,]+$/ or ($args->{sb} = ''));
|
||||
delete $args->{ma};
|
||||
|
||||
# Make sure we only search on validated links.
|
||||
$args->{isValidated} = 'Yes';
|
||||
$args->{ExpiryDate} = '>=' . time if $CFG->{payment}->{enabled};
|
||||
|
||||
my $query = $args->{query} || '';
|
||||
my $term = $IN->escape($query);
|
||||
|
||||
my $links = $DB->table('Links');
|
||||
my $categories = $DB->table('Category');
|
||||
|
||||
# We don't do a category search if we only have a filters.
|
||||
my $filter = 0;
|
||||
if (!defined $query or $query eq '') {
|
||||
$filter = 1;
|
||||
}
|
||||
$args->{filter} = $filter;
|
||||
|
||||
# Note: if you use this or the search_set_link_callback, remember to $PLG->action(STOP) or your callback won't be used
|
||||
$args->{callback} = $PLG->dispatch('search_set_cat_callback', sub { return \&_cat_search_subcat if shift }, $args->{catid});
|
||||
my $orig_sb = $args->{sb};
|
||||
my $orig_so = $args->{so};
|
||||
$args->{sb} = $CFG->{build_sort_order_search_cat};
|
||||
$args->{so} = '';
|
||||
$filter and $args->{sb} =~ s/\s*,?\s*score//;
|
||||
|
||||
my $started;
|
||||
if (length $query and $CFG->{search_logging} and $args->{nh} == 1) {
|
||||
if (!defined $time_hires) {
|
||||
$time_hires = eval { require Time::HiRes } || 0;
|
||||
}
|
||||
$started = $time_hires ? Time::HiRes::time() : time;
|
||||
}
|
||||
|
||||
my $cat_sth;
|
||||
$cat_sth = $categories->query_sth($args) unless $filter;
|
||||
my $cat_count = $filter ? 0 : $categories->hits();
|
||||
|
||||
$args->{callback} = $PLG->dispatch('search_set_link_callback', sub { return \&_search_subcat if shift }, $args->{catid});
|
||||
$args->{sb} = $orig_sb ? $orig_sb : $CFG->{build_sort_order_search} || '';
|
||||
$args->{so} = (defined $orig_so and $orig_so =~ /^(asc|desc)$/i) ? $1 : 'ASC';
|
||||
$filter and $args->{sb} =~ s/\s*,?\s*score//;
|
||||
|
||||
# Don't force sorting by whether or not a link is paid, as that would make
|
||||
# searching almost useless w.r.t. unpaid links since a 1% paid match would be
|
||||
# higher than a 99% unpaid match.
|
||||
|
||||
my $link_sth = $links->query_sth($args);
|
||||
my $link_count = $links->hits;
|
||||
|
||||
# Log the search if it's a new query
|
||||
if (length $query and $CFG->{search_logging} and $args->{nh} == 1) {
|
||||
my $elapsed = ($time_hires ? Time::HiRes::time() : time) - $started;
|
||||
my $results = $link_count || 0;
|
||||
my $sl = $DB->table('SearchLogs');
|
||||
my $q = lc $query;
|
||||
substr($q, 255) = '' if length $q > 255;
|
||||
if (my $row = $sl->select({ slog_query => $q })->fetchrow_hashref) {
|
||||
my $slog_time = defined $row->{slog_time}
|
||||
? ($row->{slog_time} * $row->{slog_count} + $elapsed) / ($row->{slog_count} + 1)
|
||||
: $elapsed;
|
||||
$sl->update({
|
||||
slog_count => $row->{slog_count} + 1,
|
||||
slog_time => sprintf('%.6f', $slog_time),
|
||||
slog_last => time,
|
||||
slog_hits => $results
|
||||
}, {
|
||||
slog_query => $q
|
||||
});
|
||||
}
|
||||
else {
|
||||
$sl->insert({
|
||||
slog_query => $q,
|
||||
slog_count => 1,
|
||||
slog_time => sprintf('%.6f', $elapsed),
|
||||
slog_last => time,
|
||||
slog_hits => $results
|
||||
}) or die "$GT::SQL::error";
|
||||
}
|
||||
}
|
||||
|
||||
# Return if no results.
|
||||
unless ($link_count or $cat_count) {
|
||||
return { error => Links::language('SEARCH_NOLINKS', $term), term => $term };
|
||||
}
|
||||
|
||||
# Now format the category results.
|
||||
my $count = 0;
|
||||
my ($category_results, @category_results_loop);
|
||||
if (!$filter and $cat_count) {
|
||||
while (my $cat = $cat_sth->fetchrow_hashref) {
|
||||
last if ($count++ > $args->{mh});
|
||||
my $title = Links::Build::build('title_linked', { name => $cat->{Full_Name}, complete => 1, home => 0 });
|
||||
$category_results .= "<li>$title\n";
|
||||
$cat->{title_linked} = $title;
|
||||
$cat->{title_loop} = Links::Build::build('title', $cat->{Full_Name});
|
||||
push @category_results_loop, $cat;
|
||||
}
|
||||
}
|
||||
|
||||
# And format the link results.
|
||||
my (@link_results_loop, $link_results, %link_output);
|
||||
if ($link_count) {
|
||||
my $results = $link_sth->fetchall_hashref;
|
||||
$links->add_reviews($results);
|
||||
@link_results_loop = map Links::SiteHTML::tags('link', $_) => @$results unless $CFG->{build_search_gb};
|
||||
if ($CFG->{build_search_gb}) {
|
||||
my @ids = map { $_->{ID} } @$results;
|
||||
my $catlink = $DB->table('CatLinks','Category');
|
||||
my %names = $catlink->select('LinkID', 'Full_Name', { LinkID => \@ids })->fetchall_list;
|
||||
foreach my $link (@$results) {
|
||||
push @{$link_output{$names{$link->{ID}}}}, Links::SiteHTML::tags('link', $link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Join the link results by category if we are grouping.
|
||||
if ($CFG->{build_search_gb}) {
|
||||
foreach my $cat (sort keys %link_output) {
|
||||
$link_output{$cat}->[0]->{title_linked} = sub { Links::Build::build('title_linked', { name => $cat, complete => 1, home => 0 }) };
|
||||
$link_output{$cat}->[0]->{title_loop} = Links::Build::build('title', $cat);
|
||||
push @link_results_loop, @{$link_output{$cat}};
|
||||
}
|
||||
}
|
||||
$link_results = sub {
|
||||
my $links;
|
||||
$CFG->{build_search_gb} or return join("", map { Links::SiteHTML::display('link', $_) } @link_results_loop);
|
||||
foreach my $cat (sort keys %link_output) {
|
||||
my $title = Links::Build::build('title_linked', { name => $cat, complete => 1, home => 0 });
|
||||
$links .= "<p>$title" . join("", map { Links::SiteHTML::display('link', $_) } @{$link_output{$cat}});
|
||||
}
|
||||
return $links;
|
||||
};
|
||||
|
||||
# Generate a toolbar if requested.
|
||||
my ($toolbar, %paging);
|
||||
if ($link_count > $args->{mh} or $cat_count > $args->{mh}) {
|
||||
my $url = $CFG->{db_cgi_url} . "/" . $IN->url;
|
||||
$url =~ s/([;&?]?)nh=(\d+)/($1 and $1 eq '?') ? '?' : ''/eg;
|
||||
$toolbar = Links::Build::build(search_toolbar => {
|
||||
url => $url,
|
||||
numlinks => $link_count > $cat_count ? $link_count : $cat_count,
|
||||
nh => $args->{nh},
|
||||
mh => $args->{mh}
|
||||
});
|
||||
%paging = (
|
||||
url => $url,
|
||||
num_hits => $link_count > $cat_count ? $link_count : $cat_count,
|
||||
max_hits => $args->{mh},
|
||||
current_page => $args->{nh}
|
||||
);
|
||||
}
|
||||
else {
|
||||
$toolbar = '';
|
||||
}
|
||||
|
||||
# Print the output.
|
||||
my $results = {
|
||||
link_results => $link_results,
|
||||
link_results_loop => \@link_results_loop,
|
||||
category_results => $category_results,
|
||||
category_results_loop => \@category_results_loop,
|
||||
link_hits => $link_count,
|
||||
cat_hits => $cat_count,
|
||||
next => $toolbar,
|
||||
paging => \%paging,
|
||||
term => $term,
|
||||
highlight => $CFG->{search_highlighting}
|
||||
};
|
||||
return $results;
|
||||
}
|
||||
|
||||
sub _search_subcat {
|
||||
# -------------------------------------------------------------------
|
||||
# First argument is the query/table object, second argument is the current
|
||||
# result set (note: can be quite large). Must return a new result set.
|
||||
#
|
||||
my ($query, $results) = @_;
|
||||
return $results unless (keys %$results); # No matches.
|
||||
|
||||
my $cat_db = $DB->table('Category');
|
||||
my $catlink_db = $DB->table('CatLinks', 'Category');
|
||||
|
||||
# We need the full name of the category.
|
||||
my @cat_ids = $IN->param('catid') or return $results;
|
||||
my (@children, %seen);
|
||||
foreach my $id (@cat_ids) {
|
||||
next if ($id !~ /^\d+$/);
|
||||
my $child = $cat_db->children($id) or next;
|
||||
push @children, @$child, $id;
|
||||
}
|
||||
@children or return $results;
|
||||
@children = grep !$seen{$_}++, @children;
|
||||
|
||||
# Now do the joined query.
|
||||
my %filtered = map { $_ => $results->{$_} }
|
||||
$catlink_db->select(LinkID => { CategoryID => \@children, LinkID => [keys %$results] })->fetchall_list;
|
||||
|
||||
return \%filtered;
|
||||
}
|
||||
|
||||
sub _search_subcat_and {
|
||||
# -------------------------------------------------------------------
|
||||
# Search subcategories using AND.
|
||||
#
|
||||
my ($query, $results) = @_;
|
||||
return $results unless (keys %$results); # No matches
|
||||
|
||||
my $cat_db = $DB->table('Category');
|
||||
my $catlink_db = $DB->table('CatLinks', 'Category');
|
||||
|
||||
# We need the full name of the category.
|
||||
my @cat_ids = $IN->param('catid') or return $results;
|
||||
my %final = %$results;
|
||||
foreach my $id (@cat_ids) {
|
||||
next unless ($id =~ /^\d+$/);
|
||||
my @children;
|
||||
my $childs = $cat_db->children($id);
|
||||
push @children, @$childs, $id;
|
||||
my $cond = GT::SQL::Condition->new(
|
||||
CategoryID => 'IN' => \@children,
|
||||
LinkID => 'IN' => [ keys %final ]
|
||||
);
|
||||
%final = ();
|
||||
my $sth = $catlink_db->select($cond, ['LinkID']);
|
||||
while (my $link_id = $sth->fetchrow_array) {
|
||||
$final{$link_id} = $results->{$link_id};
|
||||
}
|
||||
last unless keys %final;
|
||||
}
|
||||
return \%final;
|
||||
}
|
||||
|
||||
sub _cat_search_subcat {
|
||||
# -------------------------------------------------------------------
|
||||
# First argument is the query/table object, second argument is the current
|
||||
# result set (note: can be quite large). Must return a new result set.
|
||||
#
|
||||
my ($query, $results) = @_;
|
||||
return $results unless (keys %$results); # No matches.
|
||||
|
||||
my $cat_db = $DB->table('Category');
|
||||
my @cat_ids = $IN->param('catid') or return $results;
|
||||
my (@children, %seen);
|
||||
foreach my $id (@cat_ids) {
|
||||
next if ($id !~ /^\d+$/);
|
||||
my $child = $cat_db->children($id) or next;
|
||||
push @children, @$child, $id;
|
||||
}
|
||||
@children or return $results;
|
||||
@children = grep { ! $seen{$_}++ } @children;
|
||||
|
||||
my %subcats = map { $_ => 1 } @children;
|
||||
my $filtered = {};
|
||||
while (my ($k, $s) = each %$results) {
|
||||
$filtered->{$k} = $s if (exists $subcats{$k});
|
||||
}
|
||||
return $filtered;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Treecats.pm,v 1.3 2006/09/12 06:07:12 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2006 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::User::Treecats;
|
||||
# ==================================================================
|
||||
use strict;
|
||||
use Links qw/:objects/;
|
||||
|
||||
sub handle {
|
||||
# Fetch these categories (and select them)
|
||||
my @cid = $IN->param('cid');
|
||||
# Fetch these links (and select them)
|
||||
my @lid = $IN->param('lid');
|
||||
# Fetch these categories
|
||||
my @id = $IN->param('id');
|
||||
# Fetch links as well as Categories
|
||||
my $fetchlinks = $IN->param('links');
|
||||
|
||||
my $category = $DB->table('Category');
|
||||
my $catlinks = $DB->table('CatLinks', 'Links');
|
||||
|
||||
# Fetching selected categories
|
||||
if (@cid) {
|
||||
@lid = ();
|
||||
@id = @cid;
|
||||
$fetchlinks = 0;
|
||||
}
|
||||
# Fetching selected links
|
||||
elsif (@lid) {
|
||||
# Get all the categories that the links are in
|
||||
@id = $catlinks->select('CategoryID', { LinkID => \@lid }, VIEWABLE)->fetchall_list;
|
||||
$fetchlinks = 1;
|
||||
}
|
||||
# Fetching categories/links
|
||||
else {
|
||||
@cid = ();
|
||||
@lid = ();
|
||||
@id = (0) unless @id;
|
||||
}
|
||||
|
||||
my %vars;
|
||||
# Only allow the use of treecats.cgi if db_gen_category_list == 2 or if
|
||||
# treecats_enabled (hidden config option) is true
|
||||
if ($CFG->{db_gen_category_list} != 2 and not $CFG->{treecats_enabled}) {
|
||||
$vars{error} = 'Permission denied - treecats is currently disabled.';
|
||||
}
|
||||
else {
|
||||
my @fetchlinks;
|
||||
my $cond;
|
||||
if (@cid or @lid) {
|
||||
my $parents = $category->parents(\@id);
|
||||
my @ids;
|
||||
my @fids = (0);
|
||||
for (keys %$parents) {
|
||||
# Fetch all the parents and their children
|
||||
push @ids, @{$parents->{$_}};
|
||||
push @fids, @{$parents->{$_}};
|
||||
# Fetch the category itself
|
||||
push @ids, $_;
|
||||
# When pre-selecting links, @id contains the category the link(s) are in. To
|
||||
# completely draw the tree, the children of those categories need to be
|
||||
# retreived as well.
|
||||
if (@lid) {
|
||||
push @fids, $_;
|
||||
push @fetchlinks, $_;
|
||||
}
|
||||
push @fetchlinks, @{$parents->{$_}};
|
||||
}
|
||||
$cond = GT::SQL::Condition->new(ID => IN => \@ids, FatherID => IN => \@fids);
|
||||
$cond->bool('OR');
|
||||
}
|
||||
else {
|
||||
push @fetchlinks, @id;
|
||||
$cond = GT::SQL::Condition->new(FatherID => IN => \@id);
|
||||
}
|
||||
$category->select_options("ORDER BY Full_Name");
|
||||
$vars{categories} = $category->select($cond)->fetchall_hashref;
|
||||
|
||||
# Find the children counts of all the categories and check if they should be selected or not
|
||||
my @cats;
|
||||
for (@{$vars{categories}}) {
|
||||
push @cats, $_->{ID};
|
||||
}
|
||||
$category->select_options("GROUP BY FatherID");
|
||||
my %children = $category->select('FatherID', 'COUNT(*)', { FatherID => \@cats })->fetchall_list;
|
||||
my %selected = map { $_ => 1 } @cid;
|
||||
for (@{$vars{categories}}) {
|
||||
$_->{children} = $children{$_->{ID}} || 0;
|
||||
$_->{selected} = $selected{$_->{ID}} || 0;
|
||||
}
|
||||
|
||||
if ($fetchlinks and @fetchlinks) {
|
||||
# Remove CategoryID = 0 (shouldn't normally happen)
|
||||
@fetchlinks = grep $_, @fetchlinks;
|
||||
$catlinks->select_options("ORDER BY CategoryID, Title");
|
||||
$vars{links} = $catlinks->select({ CategoryID => \@fetchlinks }, VIEWABLE)->fetchall_hashref;
|
||||
|
||||
%selected = map { $_ => 1 } @lid;
|
||||
for (@{$vars{links}}) {
|
||||
$_->{selected} = $selected{$_->{ID}} || 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print $IN->header('text/xml');
|
||||
print Links::user_page('treecats.xml', \%vars);
|
||||
}
|
||||
|
||||
1;
|
||||
585
site/slowtwitch.com/cgi-bin/articles/admin/Links/Utils.pm
Normal file
585
site/slowtwitch.com/cgi-bin/articles/admin/Links/Utils.pm
Normal file
@@ -0,0 +1,585 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: Utils.pm,v 1.61 2008/07/15 19:50:11 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::Utils;
|
||||
# ==================================================================
|
||||
# This package contains some builtin functions useful in your templates.
|
||||
#
|
||||
use strict;
|
||||
use Links qw/$IN $DB $CFG $USER/;
|
||||
|
||||
sub is_editor {
|
||||
# -------------------------------------------------------------------
|
||||
# Returns true if the current user is an editor.
|
||||
#
|
||||
return unless $USER and $USER->{Status} ne 'Not Validated';
|
||||
return $DB->table('Editors')->count({ Username => $USER->{Username} });
|
||||
}
|
||||
|
||||
sub load_editors {
|
||||
# -------------------------------------------------------------------
|
||||
# You call this tag by placing <%Links::Utils::load_editors%> in your
|
||||
# category.html template. It will then make available an <%editors%>
|
||||
# tag that you can use in your template. For example:
|
||||
# <%Links::Utils::load_editors%>
|
||||
# <%if editors%>
|
||||
# The following users are editors in this category: <%editors%>
|
||||
# <%endif%>
|
||||
#
|
||||
my $vars = GT::Template->vars;
|
||||
my $cat_id = $vars->{category_id} or return "No category_id tag found! This tag can only be used on category.html template";
|
||||
my $cat_db = $DB->table('Category');
|
||||
my @parents = @{$cat_db->parents($cat_id)};
|
||||
push @parents, $cat_id;
|
||||
|
||||
my $ed_db = $DB->table('Editors', 'Users');
|
||||
my $sth = $ed_db->select(GT::SQL::Condition->new('CategoryID', 'IN', \@parents));
|
||||
return {} unless ($sth->rows);
|
||||
|
||||
# Make any formatting changes you need here.
|
||||
my $output = '<ul>';
|
||||
my @editors;
|
||||
my %seen;
|
||||
while (my $user = $sth->fetchrow_hashref) {
|
||||
next if ($seen{$user->{Username}}++);
|
||||
$output .= qq|<li>$user->{Username}</li>|;
|
||||
push @editors, $user;
|
||||
}
|
||||
$output .= "</ul>";
|
||||
return { editors => $output, editors_loop => \@editors };
|
||||
}
|
||||
|
||||
sub load_user {
|
||||
# -------------------------------------------------------------------
|
||||
# You call this tag in your link.html or detailed.html template. It will
|
||||
# provide all the information about the user who owns the link, and also
|
||||
# create a Contact_Name and Contact_Email tag for backwards compatibility.
|
||||
# So you would put:
|
||||
# <%Links::Utils::load_user%>
|
||||
# This link is owned by <%Username%>, whose email is <%Email%>
|
||||
# and password is <%Password%>. They are a <%Status%> user.
|
||||
#
|
||||
my $vars = GT::Template->vars;
|
||||
my $username = $vars->{LinkOwner} or return "No LinkOwner tag found! This tag can only be used on link.html or detailed.html templates.";
|
||||
require Links::Authenticate;
|
||||
my $user_r = Links::Authenticate->auth('get_user', { Username => $username } );
|
||||
return $user_r;
|
||||
}
|
||||
|
||||
sub load_reviews {
|
||||
# -------------------------------------------------------------------
|
||||
# You call this tag in link.html or detailed.html template. It will
|
||||
# load all the reviews associated with this link.
|
||||
# So you would put:
|
||||
# <%Links::Utils::load_reviews($ID, $max_reviews)%>
|
||||
# This link has <%Review_Total%> reviews.
|
||||
# <%loop Reviews_Loop%><%Review_Subject%> - <%Review_ByLine%><%endloop%>
|
||||
# Review_Count is a deprecated backwards compatible variable
|
||||
#
|
||||
my ($id, $max) = @_;
|
||||
unless ($id) {
|
||||
my $vars = GT::Template->vars;
|
||||
$id = $vars->{ID};
|
||||
}
|
||||
my $reviews = $DB->table('Reviews');
|
||||
if ($CFG->{review_sort_by}) {
|
||||
my $order = $CFG->{review_sort_order} || 'DESC';
|
||||
$reviews->select_options("ORDER BY $CFG->{review_sort_by} $order");
|
||||
}
|
||||
if ($max and $max =~ /^\d+$/) {
|
||||
$reviews->select_options("LIMIT $max");
|
||||
}
|
||||
my $review_total = $reviews->count({ Review_LinkID => $id, Review_Validated => 'Yes' });
|
||||
my $sth = $reviews->select({ Review_LinkID => $id, Review_Validated => 'Yes' });
|
||||
my @reviews;
|
||||
Links::init_date();
|
||||
require Links::User::Review;
|
||||
my $today = GT::Date::date_get();
|
||||
while (my $rev = $sth->fetchrow_hashref) {
|
||||
$rev->{Review_IsNew} = (GT::Date::date_diff($today, $rev->{Review_Date}) < $CFG->{review_days_old});
|
||||
$rev->{Review_CanModify} = 0;
|
||||
if ($CFG->{review_allow_modify} and $USER->{Username} eq $rev->{Review_Owner}) {
|
||||
if ($CFG->{review_modify_timeout}) {
|
||||
my $oldfmt = GT::Date::date_get_format();
|
||||
GT::Date::date_set_format(GT::Date::FORMAT_DATETIME);
|
||||
my $timeout = GT::Date::date_get(time - $CFG->{review_modify_timeout} * 60);
|
||||
my $date = $rev->{Review_ModifyDate} =~ /^0000-00-00 00:00:00/ ? $rev->{Review_Date} : $rev->{Review_ModifyDate};
|
||||
if (GT::Date::date_is_greater($date, $timeout)) {
|
||||
$rev->{Review_CanModify} = 1;
|
||||
}
|
||||
GT::Date::date_set_format($oldfmt);
|
||||
}
|
||||
else {
|
||||
$rev->{Review_CanModify} = 1;
|
||||
}
|
||||
}
|
||||
if ($rev->{Review_ModifyDate} ne $rev->{Review_Date} and $rev->{Review_ModifyDate} !~ /^0000-00-00 00:00:00/) {
|
||||
$rev->{Review_ModifyDate} = GT::Date::date_transform($rev->{Review_ModifyDate}, GT::Date::FORMAT_DATETIME, $CFG->{date_review_format});
|
||||
}
|
||||
else {
|
||||
delete $rev->{Review_ModifyDate};
|
||||
}
|
||||
$rev->{Review_Date} = GT::Date::date_transform($rev->{Review_Date}, GT::Date::FORMAT_DATETIME, $CFG->{date_review_format});
|
||||
$rev->{Num} = $rev->{Review_WasHelpful} + $rev->{Review_WasNotHelpful};
|
||||
$CFG->{review_convert_br_tags} and $rev->{Review_Contents} = Links::User::Review::_translate_html($rev->{Review_Contents});
|
||||
push @reviews, $rev;
|
||||
}
|
||||
|
||||
return { Review_Total => $review_total, Review_Count => scalar @reviews, Review_Loop => \@reviews };
|
||||
}
|
||||
|
||||
sub load_link {
|
||||
# -------------------------------------------------------------------
|
||||
# This will return a fully formatted link. Deprecated in favour of
|
||||
# using load_link_info() + <%include link.html%>
|
||||
#
|
||||
my %vars = %{GT::Template->vars};
|
||||
if ($Links::GLOBALS) {
|
||||
delete @vars{keys %$Links::GLOBALS};
|
||||
}
|
||||
return Links::SiteHTML::display('link', \%vars);
|
||||
}
|
||||
|
||||
sub load_link_info {
|
||||
# -------------------------------------------------------------------
|
||||
# This will return the vars needed to display a fully formatted link (i.e. by
|
||||
# including link.html)
|
||||
#
|
||||
return Links::SiteHTML::tags(link => GT::Template->vars);
|
||||
}
|
||||
|
||||
sub paging {
|
||||
# -------------------------------------------------------------------
|
||||
# Generate the html needed for a paging toolbar
|
||||
#
|
||||
# The paging hash (retrieved from vars) should contain:
|
||||
# url
|
||||
# page
|
||||
# Only one of url or page should be included.
|
||||
# url is used when the generated url will be <%url%>;nh=<%page_number%>
|
||||
# page is used when the generated url will be <%build_root_url%>/<%page%>...
|
||||
# page_format
|
||||
# 1: <%build_root_url%>/<%page%>{index,more<%current_page%>}.html
|
||||
# Used in category, cool, new pages
|
||||
# 2: <%build_root_url%>/<%page%>{,_<%current_page%>}.html
|
||||
# Used in new page
|
||||
# num_hits
|
||||
# max_hits
|
||||
# current_page
|
||||
#
|
||||
# Options:
|
||||
# max_pages
|
||||
# The maximum number of pages to display (excluding boundary pages)
|
||||
# boundary_pages
|
||||
# When there are more pages than max_pages, this number of boundary
|
||||
# pages are added to the paging toolbar
|
||||
# style
|
||||
# 1: |< < [1 of 20] > >|
|
||||
# 2: [1 of 20] < >
|
||||
# 3: |< < 1 2 3 4 5 6 7 8 9 ... 20 > >|
|
||||
# style_next
|
||||
# style_prev
|
||||
# style_first
|
||||
# style_last
|
||||
# style_nonext
|
||||
# style_noprev
|
||||
# style_nofirst
|
||||
# style_nolast
|
||||
# These options allow you to change what's shown for the next/prev/etc
|
||||
# actions
|
||||
# lang_of
|
||||
# For styles 1 and 2, they use the format of "<page> <lang_of> <page>".
|
||||
# This option allows you to change the english text of "of".
|
||||
# lang_button
|
||||
# For styles 1 and 2, a "Go" button is used for users which do not have
|
||||
# javascript support. This option allows you to change the button's
|
||||
# label.
|
||||
# button_id
|
||||
# If you've got two paging toolbars on a page, then you will need to
|
||||
# change the button_id so that the javascript can remove the button.
|
||||
# paging_pre
|
||||
# paging_post
|
||||
# This text or html is added before and after the paging html.
|
||||
#
|
||||
# There are two ways of setting the above options:
|
||||
# 1) Pass them in as arguments
|
||||
# 2) Create a global code ref named 'paging_options' and return the options
|
||||
# as a hash reference
|
||||
# Options passed as arguments override all options passed in via other methods,
|
||||
# followed by the global options and lastly the defaults contained in this
|
||||
# function.
|
||||
#
|
||||
# Note 1: You can override this function by creating a paging_override global
|
||||
# Note 2: The arguments to paging_override are slightly different. To keep
|
||||
# duplicated code to a minimum, %paging with the paging calculations done
|
||||
# is passed as the first argument (it also contains a few helper code
|
||||
# refs), and the second argument contains the options with defaults set.
|
||||
# The left over arguments are the passed in options (shouldn't be needed
|
||||
# since they have been merged into the options already).
|
||||
#
|
||||
my $vars = GT::Template->vars;
|
||||
|
||||
return unless ref $vars->{paging} eq 'HASH';
|
||||
my %paging = %{$vars->{paging}};
|
||||
return if not $paging{num_hits} or $paging{num_hits} < $paging{max_hits};
|
||||
|
||||
%paging = (
|
||||
page_format => 1,
|
||||
current_page => 1,
|
||||
form_hidden => '',
|
||||
%paging
|
||||
);
|
||||
|
||||
# Setup the default options
|
||||
my %paging_options;
|
||||
%paging_options = %{$vars->{paging_options}->()} if ref $vars->{paging_options} eq 'CODE';
|
||||
my %options = (
|
||||
max_pages => 10,
|
||||
boundary_pages => 1,
|
||||
style => 1,
|
||||
style_next => '<img src="' . image_url('paging-next.gif') . '" alt=">" title="Next Page" />',
|
||||
style_prev => '<img src="' . image_url('paging-prev.gif') . '" alt="<" title="Previous Page" />',
|
||||
style_first => '<img src="' . image_url('paging-first.gif') . '" alt="|<" title="First Page" />',
|
||||
style_last => '<img src="' . image_url('paging-last.gif') . '" alt=">|" title="Last Page" />',
|
||||
style_nonext => '<img src="' . image_url('paging-nonext.gif') . '" alt="" />',
|
||||
style_noprev => '<img src="' . image_url('paging-noprev.gif') . '" alt="" />',
|
||||
style_nofirst => '<img src="' . image_url('paging-nofirst.gif') . '" alt="" />',
|
||||
style_nolast => '<img src="' . image_url('paging-nolast.gif') . '" alt="" />',
|
||||
lang_of => 'of',
|
||||
lang_button => 'Go',
|
||||
button_id => 'paging_button',
|
||||
paging_pre => '',
|
||||
paging_post => '',
|
||||
%paging_options,
|
||||
@_
|
||||
);
|
||||
|
||||
# Make all the page calculations
|
||||
$paging{num_pages} = int($paging{num_hits} / $paging{max_hits});
|
||||
$paging{num_pages}++ if $paging{num_hits} % $paging{max_hits};
|
||||
my ($start, $end);
|
||||
if ($paging{num_pages} <= $options{max_pages}) {
|
||||
$start = 1;
|
||||
$end = $paging{num_pages};
|
||||
}
|
||||
elsif ($paging{current_page} >= $paging{num_pages} - $options{max_pages} / 2) {
|
||||
$end = $paging{num_pages};
|
||||
$start = $end - $options{max_pages} + 1;
|
||||
}
|
||||
elsif ($paging{current_page} <= $options{max_pages} / 2) {
|
||||
$start = 1;
|
||||
$end = $options{max_pages};
|
||||
}
|
||||
else {
|
||||
$start = $paging{current_page} - int($options{max_pages} / 2) + 1;
|
||||
$start-- if $options{max_pages} % 2;
|
||||
$end = $paging{current_page} + int($options{max_pages} / 2);
|
||||
}
|
||||
|
||||
my ($left_boundary, $right_boundary);
|
||||
if ($end >= $paging{num_pages} - $options{boundary_pages} - 1) {
|
||||
$end = $paging{num_pages};
|
||||
}
|
||||
else {
|
||||
$right_boundary = 1;
|
||||
}
|
||||
|
||||
if ($start <= $options{boundary_pages} + 2) {
|
||||
$start = 1;
|
||||
}
|
||||
else {
|
||||
$left_boundary = 1;
|
||||
}
|
||||
|
||||
my @pages;
|
||||
push @pages, 1 .. $options{boundary_pages}, '...' if $left_boundary;
|
||||
push @pages, $start .. $end;
|
||||
push @pages, '...', $paging{num_pages} - $options{boundary_pages} + 1 .. $paging{num_pages} if $right_boundary;
|
||||
$paging{pages} = \@pages;
|
||||
|
||||
$paging{create_link} = sub {
|
||||
my ($page, $disp) = @_;
|
||||
my $ret = '';
|
||||
$ret .= qq|<a href="|;
|
||||
|
||||
if ($paging{url}) {
|
||||
(my $url = $paging{url}) =~ s/([;&?]?)nh=(\d+)/($1 and $1 eq '?') ? '?' : ''/eg;
|
||||
$ret .= $url;
|
||||
$ret .= index($url, '?') != -1 ? ';' : '?';
|
||||
$ret .= "nh=$page";
|
||||
}
|
||||
else {
|
||||
$ret .= "$CFG->{build_root_url}/$paging{page}";
|
||||
if ($paging{page_format} == 1) {
|
||||
$ret .= $page == 1 ? ($CFG->{build_index_include} ? $CFG->{build_index} : '') : "$CFG->{build_more}$page$CFG->{build_extension}";
|
||||
}
|
||||
elsif ($paging{page_format} == 2) {
|
||||
$ret .= "_$page" if $page > 1;
|
||||
$ret .= $CFG->{build_extension};
|
||||
}
|
||||
}
|
||||
$ret .= qq|">$disp</a>|;
|
||||
return $ret;
|
||||
};
|
||||
|
||||
$paging{select_value} = sub {
|
||||
my $page = shift;
|
||||
if ($paging{url}) {
|
||||
return $page;
|
||||
}
|
||||
else {
|
||||
my $ret = $paging{page};
|
||||
if ($paging{page_format} == 1) {
|
||||
$ret .= $page == 1 ? ($CFG->{build_index_include} ? $CFG->{build_index} : '') : "$CFG->{build_more}$page$CFG->{build_extension}";
|
||||
}
|
||||
elsif ($paging{page_format} == 2) {
|
||||
$ret .= "_$page" if $page > 1;
|
||||
$ret .= $CFG->{build_extension};
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
};
|
||||
|
||||
if ($paging{url}) {
|
||||
# Figure out what needs to be submitted with the form (it *should* have ? in it
|
||||
# since with these queries, it *will* have other arguments)
|
||||
($paging{form_action}, my $args) = $paging{url} =~ /^(.*?)\?(.*)$/;
|
||||
NV: for (split /[;&]/, $args) {
|
||||
my ($name, $val) = /([^=]+)=(.*)/ or next;
|
||||
$name = $IN->unescape($name);
|
||||
$val = $IN->unescape($val);
|
||||
|
||||
# Skip these since Links::clean_output will put them in automatically
|
||||
for (@{$CFG->{dynamic_preserve}}, 'nh') {
|
||||
next NV if $name eq $_;
|
||||
}
|
||||
$paging{form_hidden} .= qq|<input type="hidden" name="| . $IN->html_escape($name) . qq|" value="| . $IN->html_escape($val) . qq|" />|;
|
||||
}
|
||||
$paging{select_name} = 'nh';
|
||||
}
|
||||
else {
|
||||
$paging{form_action} = "$CFG->{db_cgi_url}/page.cgi";
|
||||
$paging{select_name} = 'g';
|
||||
}
|
||||
|
||||
# Override this function. Pass in the updated %paging and %options hashes so
|
||||
# the calculations don't have to be duplicated in the override.
|
||||
if (ref $vars->{paging_override} eq 'CODE') {
|
||||
return $vars->{paging_override}->(\%paging, \%options, @_);
|
||||
}
|
||||
|
||||
my $html;
|
||||
if ($options{style} == 1) {
|
||||
# |< < [1 of 20] > >|
|
||||
$html .= qq|<form action="$paging{form_action}">$paging{form_hidden}$options{paging_pre}|;
|
||||
if ($paging{current_page} != 1) {
|
||||
$html .= $paging{create_link}->(1, $options{style_first}) . ' ' . $paging{create_link}->($paging{current_page} - 1, $options{style_prev}) . ' ';
|
||||
}
|
||||
else {
|
||||
$html .= "$options{style_nofirst} $options{style_noprev} ";
|
||||
}
|
||||
|
||||
$html .= qq|<select name="$paging{select_name}" onchange="if (this.options[this.selectedIndex].innerHTML != '...' && !this.options[this.selectedIndex].defaultSelected) |;
|
||||
$html .= $IN->param('d') || $paging{url} ? qq|this.form.submit()| : qq|window.location = '$CFG->{build_root_url}/' + this.value|;
|
||||
$html .= qq|">|;
|
||||
for (@{$paging{pages}}) {
|
||||
if ($_ eq '...') {
|
||||
$html .= qq|<option value="" disabled="disabled">...</option>|;
|
||||
}
|
||||
else {
|
||||
$html .= qq|<option value="| . $paging{select_value}->($_) . '"';
|
||||
$html .= qq| selected="selected"| if $_ == $paging{current_page};
|
||||
$html .= qq|>$_ $options{lang_of} $paging{num_pages}</option>|;
|
||||
}
|
||||
}
|
||||
$html .= qq|</select><noscript><input type="submit" id="$options{button_id}" value="$options{lang_button}" class="submit" /></noscript> |;
|
||||
|
||||
if ($paging{current_page} != $paging{num_pages}) {
|
||||
$html .= $paging{create_link}->($paging{current_page} + 1, $options{style_next}) . ' ' . $paging{create_link}->($paging{num_pages}, $options{style_last});
|
||||
}
|
||||
else {
|
||||
$html .= "$options{style_nonext} $options{style_nolast}";
|
||||
}
|
||||
$html .= qq|$options{paging_post}</form>|;
|
||||
}
|
||||
elsif ($options{style} == 2) {
|
||||
# [1 of 20] < >
|
||||
$html .= qq|<form action="$paging{form_action}">$paging{form_hidden}$options{paging_pre}<select name="$paging{select_name}" onchange="if (this.options[this.selectedIndex].innerHTML != '...' && !this.options[this.selectedIndex].defaultSelected) |;
|
||||
$html .= $IN->param('d') || $paging{url} ? qq|this.form.submit()| : qq|window.location = '$CFG->{build_root_url}/' + this.value|;
|
||||
$html .= qq|">|;
|
||||
for (@{$paging{pages}}) {
|
||||
if ($_ eq '...') {
|
||||
$html .= qq|<option value="" disabled="disabled">...</option>|;
|
||||
}
|
||||
else {
|
||||
$html .= qq|<option value="| . $paging{select_value}->($_) . '"';
|
||||
$html .= qq| selected="selected"| if $_ == $paging{current_page};
|
||||
$html .= qq|>$_ $options{lang_of} $paging{num_pages}</option>|;
|
||||
}
|
||||
}
|
||||
$html .= qq|</select><noscript><input type="submit" id="$options{button_id}" value="$options{lang_button}" class="submit" /></noscript> |;
|
||||
|
||||
if ($paging{current_page} != 1) {
|
||||
$html .= $paging{create_link}->($paging{current_page} - 1, $options{style_prev}) . ' ';
|
||||
}
|
||||
else {
|
||||
$html .= "$options{style_noprev} ";
|
||||
}
|
||||
|
||||
if ($paging{current_page} != $paging{num_pages}) {
|
||||
$html .= $paging{create_link}->($paging{current_page} + 1, $options{style_next});
|
||||
}
|
||||
else {
|
||||
$html .= $options{style_nonext};
|
||||
}
|
||||
$html .= qq|$options{paging_post}</form>|;
|
||||
}
|
||||
elsif ($options{style} == 3) {
|
||||
# |< < 1 2 3 4 5 6 7 8 9 ... 20 > >|
|
||||
$html .= $options{paging_pre};
|
||||
if ($paging{current_page} != 1) {
|
||||
$html .= $paging{create_link}->(1, $options{style_first}) . ' ' . $paging{create_link}->($paging{current_page} - 1, $options{style_prev}) . ' ';
|
||||
}
|
||||
else {
|
||||
$html .= "$options{style_nofirst} $options{style_noprev} ";
|
||||
}
|
||||
|
||||
for (@{$paging{pages}}) {
|
||||
if ($_ eq '...') {
|
||||
$html .= "$_ ";
|
||||
}
|
||||
elsif ($_ == $paging{current_page}) {
|
||||
$html .= "<span>$_</span> ";
|
||||
}
|
||||
else {
|
||||
$html .= $paging{create_link}->($_, $_) . ' ';
|
||||
}
|
||||
}
|
||||
|
||||
if ($paging{current_page} != $paging{num_pages}) {
|
||||
$html .= $paging{create_link}->($paging{current_page} + 1, $options{style_next}) . ' ' . $paging{create_link}->($paging{num_pages}, $options{style_last});
|
||||
}
|
||||
else {
|
||||
$html .= "$options{style_nonext} $options{style_nolast}";
|
||||
}
|
||||
$html .= $options{paging_post};
|
||||
}
|
||||
|
||||
return \$html;
|
||||
}
|
||||
|
||||
sub format_title {
|
||||
# -------------------------------------------------------------------
|
||||
# Format a title
|
||||
#
|
||||
# Options:
|
||||
# separator (required)
|
||||
# The separator used to join the items.
|
||||
# no_escape_separator
|
||||
# Set this to a true value if you do not wish to HTML escape the separator.
|
||||
# include_home
|
||||
# Whether or not to include Home as the first entry. Default is no.
|
||||
# include_last
|
||||
# Whether or not to include the last entry. Default is yes.
|
||||
# link_type
|
||||
# How the items should be linked:
|
||||
# 0: No items linked
|
||||
# 1: All items linked separately
|
||||
# 2: All except the last item linked separately
|
||||
# 3: All items linked as one single link (using the last item's URL)
|
||||
# no_span
|
||||
# Don't add the span tags around the last portion of the title. Default is to include the span tags.
|
||||
#
|
||||
# Note: You can override this function by creating a format_title_override global
|
||||
#
|
||||
my ($title_loop, %options) = @_;
|
||||
return unless ref $title_loop eq 'ARRAY';
|
||||
|
||||
my $vars = GT::Template->vars;
|
||||
if (exists $vars->{format_title_override}) {
|
||||
return $vars->{format_title_override}->(@_);
|
||||
}
|
||||
|
||||
if (!exists $options{include_last}) {
|
||||
$options{include_last} = 1;
|
||||
}
|
||||
|
||||
if (!$options{include_last}) {
|
||||
pop @$title_loop;
|
||||
}
|
||||
|
||||
my $ret;
|
||||
$options{separator} = GT::CGI::html_escape($options{separator}) unless $options{no_escape_separator};
|
||||
for (0 .. $#$title_loop) {
|
||||
next unless $_ or $options{include_home};
|
||||
$ret .= '<span class="lasttitle">' if $_ == $#$title_loop and not $options{no_span} and $options{include_last};
|
||||
if ($options{link_type} == 1 or
|
||||
($options{link_type} == 2 and $_ != $#$title_loop)) {
|
||||
$ret .= qq|<a href="| . $IN->html_escape($title_loop->[$_]->{URL}) . qq|">$title_loop->[$_]->{Name}</a>|;
|
||||
}
|
||||
else {
|
||||
$ret .= $title_loop->[$_]->{Name};
|
||||
}
|
||||
$ret .= $options{separator} unless $_ == $#$title_loop;
|
||||
$ret .= '</span>' if $_ == $#$title_loop and not $options{no_span} and $options{include_last};
|
||||
}
|
||||
if ($options{link_type} == 3) {
|
||||
$ret = qq|<a href="| . $IN->html_escape($title_loop->[-1]->{URL}) . qq|">$ret</a>|;
|
||||
}
|
||||
return \$ret;
|
||||
}
|
||||
|
||||
sub column_split {
|
||||
# -------------------------------------------------------------------
|
||||
# Calculate where the columns should be
|
||||
#
|
||||
my ($items, $columns) = @_;
|
||||
if ($items % $columns > 0) {
|
||||
$items += ($columns - $items % $columns);
|
||||
}
|
||||
return $items / $columns;
|
||||
}
|
||||
|
||||
sub image_url {
|
||||
# -------------------------------------------------------------------
|
||||
# Takes an filename and using the current template set and theme, returns
|
||||
# the url of the image. It first checks if the file exists in the theme's
|
||||
# image directory, checks the template's image directory, and then tries
|
||||
# to check the template inheritance tree for more image directories.
|
||||
#
|
||||
my $image = shift;
|
||||
my ($template, $theme) = Links::template_set();
|
||||
|
||||
if (-e "$CFG->{build_static_path}/$template/images/$theme/$image") {
|
||||
return "$CFG->{build_static_url}/$template/images/$theme/$image";
|
||||
}
|
||||
|
||||
# Grab the inheritance tree of the template set and grab the basename of
|
||||
# each template set path (making an assumption that they won't do anything
|
||||
# crazy with their inheritance).
|
||||
require GT::File::Tools;
|
||||
require GT::Template::Inheritance;
|
||||
my @paths = GT::Template::Inheritance->tree(path => "$CFG->{admin_root_path}/templates/$template", local => 0);
|
||||
for (@paths) {
|
||||
my $tpl = GT::File::Tools::basename($_);
|
||||
next if $tpl eq 'browser';
|
||||
if (-e "$CFG->{build_static_path}/$tpl/images/$image") {
|
||||
return "$CFG->{build_static_url}/$tpl/images/$image";
|
||||
}
|
||||
}
|
||||
|
||||
# The image doesn't exist here, but return it anyway
|
||||
return "$CFG->{build_static_url}/$template/images/$image";
|
||||
}
|
||||
|
||||
1;
|
||||
113
site/slowtwitch.com/cgi-bin/articles/admin/Links/mod_perl.pm
Normal file
113
site/slowtwitch.com/cgi-bin/articles/admin/Links/mod_perl.pm
Normal file
@@ -0,0 +1,113 @@
|
||||
# ==================================================================
|
||||
# Gossamer Links - enhanced directory management system
|
||||
#
|
||||
# Website : http://gossamer-threads.com/
|
||||
# Support : http://gossamer-threads.com/scripts/support/
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# Revision : $Id: mod_perl.pm,v 1.34 2005/03/28 22:58:07 brewt Exp $
|
||||
#
|
||||
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
||||
# Redistribution in part or in whole strictly prohibited. Please
|
||||
# see LICENSE file for full details.
|
||||
# ==================================================================
|
||||
|
||||
package Links::mod_perl;
|
||||
# ==================================================================
|
||||
use strict();
|
||||
|
||||
# If under mod_perl, we use Apache::DBI to cache connections.
|
||||
use GT::Base qw/MOD_PERL/;
|
||||
|
||||
BEGIN {
|
||||
require Apache::DBI if MOD_PERL;
|
||||
print STDERR "\nPreloading Gossamer Links modules into mod_perl:\n\t";
|
||||
}
|
||||
|
||||
use Links();
|
||||
BEGIN { print STDERR " ." }
|
||||
use Links::Config();
|
||||
|
||||
# Preload commonly used GT libs.
|
||||
use constants();
|
||||
use GT::Cache();
|
||||
use GT::CGI();
|
||||
use GT::Date();
|
||||
use GT::Lock;
|
||||
BEGIN { print STDERR " ." }
|
||||
use GT::Dumper();
|
||||
use GT::FileMan();
|
||||
use GT::Mail();
|
||||
use GT::Mail::BulkMail();
|
||||
use GT::MD5();
|
||||
use GT::MD5::Crypt();
|
||||
use GT::MIMETypes();
|
||||
BEGIN { print STDERR " ." }
|
||||
use GT::SQL();
|
||||
use GT::SQL::Admin();
|
||||
use GT::SQL::File();
|
||||
use GT::SQL::Relation();
|
||||
use GT::SQL::Search();
|
||||
use GT::SQL::Display::HTML::Table();
|
||||
use GT::SQL::Display::HTML::Relation();
|
||||
use GT::SQL::Search::Base::Common();
|
||||
use GT::SQL::Search::Base::Indexer();
|
||||
use GT::SQL::Search::Base::STH();
|
||||
use GT::SQL::Search::Base::Search();
|
||||
BEGIN { print STDERR " ." }
|
||||
use GT::Socket::Client();
|
||||
use GT::TempFile();
|
||||
use GT::Plugins();
|
||||
use GT::Plugins::Author();
|
||||
use GT::Plugins::Installer();
|
||||
use GT::Plugins::Manager();
|
||||
use GT::Template();
|
||||
use GT::Template::Editor();
|
||||
use GT::Template::Parser();
|
||||
use GT::WWW();
|
||||
BEGIN { print STDERR " ." }
|
||||
|
||||
# Preload Gossamer Links modules.
|
||||
use Links::Admin();
|
||||
use Links::Authenticate();
|
||||
use Links::Bookmark();
|
||||
use Links::Browser();
|
||||
use Links::Build();
|
||||
use Links::Bookmark();
|
||||
use Links::Config();
|
||||
use Links::Newsletter();
|
||||
use Links::Parallel();
|
||||
use Links::Payment();
|
||||
use Links::Plugins();
|
||||
BEGIN { print STDERR " ." }
|
||||
use Links::SQL();
|
||||
use Links::SiteHTML();
|
||||
use Links::Tools();
|
||||
use Links::Utils();
|
||||
use Links::Browser::Controller();
|
||||
use Links::Browser::JFunction();
|
||||
use Links::Table::Category();
|
||||
use Links::Table::Links();
|
||||
use Links::Table::Users();
|
||||
use Links::HTML::Category();
|
||||
use Links::HTML::Links();
|
||||
use Links::HTML::Users();
|
||||
BEGIN { print STDERR " ." }
|
||||
use Links::User::Add();
|
||||
use Links::User::Editor();
|
||||
use Links::User::Jump();
|
||||
use Links::User::Login();
|
||||
use Links::User::Modify();
|
||||
use Links::User::Page();
|
||||
use Links::User::Rate();
|
||||
use Links::User::Review();
|
||||
use Links::User::Search();
|
||||
|
||||
BEGIN { print STDERR " .\nAll modules loaded ok!\n" }
|
||||
|
||||
print STDERR "Compiling all functions ...";
|
||||
|
||||
GT::AutoLoader::compile_all();
|
||||
|
||||
print STDERR " All modules compiled and loaded okay!\n\n";
|
||||
|
||||
1;
|
||||
23
site/slowtwitch.com/cgi-bin/articles/admin/Links/tmp.pl
Normal file
23
site/slowtwitch.com/cgi-bin/articles/admin/Links/tmp.pl
Normal file
@@ -0,0 +1,23 @@
|
||||
sub {
|
||||
my $related = shift || return;
|
||||
my @ids = split ("\n",$related);
|
||||
my @loop;
|
||||
my $db = $DB->table('Links');
|
||||
|
||||
my $tags = GT::Template->tags;
|
||||
my $id = $tags->{ID};
|
||||
my $cond = GT::SQL::Condition->new('RelatedArticles','like',$id . "\n%");
|
||||
$cond->add('RelatedArticles','like', "%\n" . $id . "\n%");
|
||||
$cond->add('RelatedArticles','like', "\n" . $id);
|
||||
use Data::Dumper;
|
||||
print Dumper($cond);
|
||||
#my $sth = $db->select($cond);
|
||||
|
||||
require Links::SiteHTML;
|
||||
foreach my $id (@ids) {
|
||||
my $link = $db->get($id);
|
||||
$link = Links::SiteHTML::tags('link',$link);
|
||||
push @loop, $link;
|
||||
}
|
||||
return { related_articles_loop => \@loop };
|
||||
}
|
||||
Reference in New Issue
Block a user