# ==================================================================
# 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;
