announce.in 8.17 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
#!/usr/bin/perl -w
#
# Copyright (c)-2016 University of Utah and the Flux Group.
# 
# {{{EMULAB-LICENSE
# 
# This file is part of the Emulab network testbed software.
# 
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
# 
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
# License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with this file.  If not, see <http://www.gnu.org/licenses/>.
# 
# }}}
#
use strict;
use English;
use Getopt::Std;
use Date::Parse;

#
# Set up and clear node pre-reservations.
#
sub usage()
{
34
    print STDERR "Usage: announce -a <-p portal> [-s alert_style] [-b button_label] [-u action_url] [-m max_seen_count] <announcement_text>\n";
35
    print STDERR "       announce -l [active|retired|all]\n";
36
    print STDERR "       announce -r idx\n";
37
    print STDERR "       announce -i idx\n";
38 39 40
    print STDERR "       announce -h\n";
    print STDERR "   -h   This message\n";
    print STDERR "   -a   Create a new announcement with the given announcement text.\n";
41
    print STDERR "   -p   Set the portal to be the one given. Should be one of 'cloudlab', 'emulab', 'aptlab', 'phantomnet'.\n";
42
    print STDERR "   -s   Set the style of the overall announcement box. Should normally be one of bootstraps alert-* classes. Defaults to 'alert-info'.\n";
43
    print STDERR "        Common styles: 'alert-success' -> green, 'alert-info' -> blue, 'alert-warning' -> yellow, 'alert-danger' -> red\n\n";
44 45 46
    print STDERR "   -b   If there is an action associated with this announcement, this is the text which goes into the action button. Can include HTML. If it is unset, there is no action button.\n";
    print STDERR "   -u   URL of action associated with this announcement. {uid_idx} and {uid} can be used as URL templates to generate a different URL on a per-user basis. If it is unset, there is no action button.\n";
    print STDERR "   -m   The maximum number of times that this announcement will appear to a user. Every page view (even those in a single session) counts. A value of '0' indicates that the announcement should keep appearing indefinitely until dismissed by the user or an action is taken. Defaults to 20.\n";
47
    print STDERR "   -l   List global announcements. Defaults to listing active announcements.\n";
48
    print STDERR "   -r   Retire announcement with the given idx. A retired announcement will no longer be displayed to users.\n";
49
    print STDERR "   -i   Info about a particular announcement.\n\n";
50 51
    exit(-1);
}
52
my $optlist  = "hap:s:b:u:m:lr:i:";
53 54 55
my $add_mode = 0;
my $list_mode = 0;
my $retire_mode = 0;
56
my $info_mode = 0;
57
my $portal = undef;
58 59 60 61 62
my $style = "alert-info";
my $button = undef;
my $url = undef;
my $max_seen = 20;
my $retire_idx = undef;
63
my $info_idx = undef;
64
my $text = undef;
65 66 67
my $list_type = "active";

my $query_result;
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116

# Protos
sub fatal($);

#
# Configure variables
#
my $TB		 = "@prefix@";
my $TBOPS        = "@TBOPSEMAIL@";

#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use emdb;
use libtestbed;
use User;

#
# Turn off line buffering on output
#
$| = 1;

#
# Untaint the path
# 
$ENV{'PATH'} = "/bin:/sbin:/usr/bin:";

#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
my %options = ();
if (! getopts($optlist, \%options)) {
    usage();
}
if (defined($options{"h"})) {
    usage();
}
if (defined($options{"a"})) {
    $add_mode = 1;
}
if (defined($options{"l"})) {
    $list_mode = 1;
}
if (defined($options{"r"})) {
    $retire_mode = 1;
    $retire_idx = $options{"r"};
}
117 118 119 120
if (defined($options{"i"})) {
    $info_mode = 1;
    $info_idx = $options{"i"};
}
121 122
if (defined($options{"p"})) {
    $portal = $options{"p"};
123 124 125 126 127 128 129 130 131 132 133 134 135 136
}
if (defined($options{"s"})) {
    $style = $options{"s"};
}
if (defined($options{"b"})) {
    $button = $options{"b"};
}
if (defined($options{"u"})) {
    $url = $options{"u"};
}
if (defined($options{"m"})) {
    $max_seen = $options{"m"};
}

137 138 139 140 141 142 143 144 145 146 147
if ($add_mode)
{
    if (! defined($portal)) {
	print STDERR "Portal must be defined when adding an announcement\n\n";
	usage();
    }
    elsif ($portal ne "cloudlab" && $portal ne "emulab" &&
	$portal ne "aptlab" && $portal ne "phantomnet") {
	print STDERR "Portal $portal is invalid. Must be cloudlab, emulab, aptlab, or phantomnet.\n\n";
	usage();
    }
148 149
}

150 151
if ($add_mode + $list_mode + $retire_mode + $info_mode != 1) {
    print STDERR "No mode selected. Must use one of -a, -r, -l, or -i\n\n";
152 153 154 155 156 157
    usage();
}

if ($add_mode) {
    $text = join(' ', @ARGV);
}
158 159

if (! $list_mode && ! $add_mode && scalar(@ARGV) > 0) {
160 161 162
    usage();
}

163 164 165 166
if ($list_mode && scalar(@ARGV) > 0) {
    $list_type = $ARGV[0];
}

167
if ($add_mode) {
168 169 170
    #
    # Add a new announcement
    #
171 172
    my $query = "insert into apt_announcements set ";
    $query .= "created=NOW()";
173
    $query .= ", portal=" . DBQuoteSpecial($portal);
174 175 176 177 178 179 180 181 182
    $query .= ", max_seen=" . DBQuoteSpecial($max_seen);
    $query .= ", text=" . DBQuoteSpecial($text);
    $query .= ", style=" . DBQuoteSpecial($style);
    if (defined($button) && defined($url)) {
	$query .= ", link_label=" . DBQuoteSpecial($button);
	$query .= ", link_url=" . DBQuoteSpecial($url);
    }
    DBQueryFatal($query);
} elsif ($list_mode) {
183 184 185 186 187 188 189 190 191 192
    #
    # List announcements
    #
    my $condition = "retired=0 and ";
    if ($list_type eq "all") {
	$condition = "";
    } elsif ($list_type eq "retired") {
	$condition = "retired=1 and ";
    }
    $query_result = 
193 194
	DBQueryFatal("select idx, portal, text from apt_announcements where ".$condition."uid_idx is NULL");
    print "idx\tPortal    Text\n";
195
    print "---\t-------    ----\n";
196
    while (my ($idx, $portal, $text) = $query_result->fetchrow_array()) {
197 198 199 200
	my $textbit = substr($text, 0, 55);
	if (length($text) > 55) {
	    $textbit = $textbit . "...";
	}
201 202
	my $portalpad = sprintf("%-10s", $portal);
	print "$idx\t$portalpad $textbit\n"
203 204
    }
} elsif ($retire_mode) {
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
    #
    # Retire an announcement
    #
    $query_result =
	DBQueryFatal("update apt_announcements ".
		     "set retired=1 where idx=".
		     DBQuoteSpecial($retire_idx));
} elsif ($info_mode) {
    #
    # Details about an announcement
    #
    $query_result =
	DBQueryFatal("select count(*) from apt_announcement_info where aid=".DBQuoteSpecial($info_idx));
    my ($seen) = $query_result->fetchrow_array();

    $query_result =
	DBQueryFatal("select count(*) from apt_announcement_info where clicked=1 and aid=".DBQuoteSpecial($info_idx));
    my ($clicked) = $query_result->fetchrow_array();
223

224 225 226 227
    $query_result =
	DBQueryFatal("select count(*) from apt_announcement_info where dismissed=1 and aid=".DBQuoteSpecial($info_idx));
    my ($dismissed) = $query_result->fetchrow_array();
    $query_result =
228 229
	DBQueryFatal("select idx, created, uid_idx, portal, retired, max_seen, text, style, link_label, link_url from apt_announcements where idx=".DBQuoteSpecial($info_idx));
    if (my ($idx, $created, $uid_idx, $portal, $retired,
230 231 232 233 234 235 236
	    $max_seen, $text, $style, $link_label,
	    $link_url) = $query_result->fetchrow_array())
    {
	print "-----------------------------\n";
	print "Details for announcement $idx\n";
	print "-----------------------------\n\n";
	print "Created:\t$created\n";
237
	print "Portal:\t$portal\n\n";
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
	if (defined($link_label) && defined($link_url)) {
	    print "Button Label:\t$link_label\n";
	    print "Button URL:\t$link_url\n";
	}
	print "Style:\t\t$style\n";
	if ($max_seen == 0) {
	    print "Shown until dismissed or clicked\n";
	} else {
	    print "Shown a maximum of $max_seen times\n";
	}
	print "\n";

	if ($retired) {
	    print "* Announcement is retired\n";
	} else {
	    print "* Announcement is active\n";
	}
	print "* Seen by $seen users\n";
	if (defined($link_label) && defined($link_url)) {
	    print "* Clicked by $clicked users\n";
	}
	print "* Dismissed by $dismissed users\n";

	print "\nText of announcement:\n\n";
	print "$text\n\n";
    }
}
265 266 267 268 269 270 271 272 273

sub fatal($)
{
    my ($mesg) = $_[0];

    die("*** $0:\n".
	"    $mesg\n");
}