announce.in 8.63 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> [-c] [-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 [-c]\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 50
    print STDERR "   -i   Info about a particular announcement.\n";
    print STDERR "   -c   Compatibility mode. When adding in compatibility mode, the sitevar is changed, thus setting the legacy Emulab announcement. When removing in compatibility mode, the sitevar is cleared.\n";
51 52
    exit(-1);
}
53
my $optlist  = "hacp:s:b:u:m:lr:i:";
54 55 56
my $add_mode = 0;
my $list_mode = 0;
my $retire_mode = 0;
57
my $info_mode = 0;
58
my $portal = undef;
59 60 61 62 63
my $style = "alert-info";
my $button = undef;
my $url = undef;
my $max_seen = 20;
my $retire_idx = undef;
64
my $info_idx = undef;
65
my $text = undef;
66
my $list_type = "active";
67
my $compatibility = 0;
68 69

my $query_result;
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85

# Protos
sub fatal($);

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

#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use emdb;
use libtestbed;
86
use libdb;
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 117 118 119
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"};
}
120 121 122 123
if (defined($options{"i"})) {
    $info_mode = 1;
    $info_idx = $options{"i"};
}
124 125
if (defined($options{"p"})) {
    $portal = $options{"p"};
126 127 128 129 130 131 132 133 134 135 136 137 138
}
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"};
}
139 140 141
if (defined($options{"c"})) {
    $compatibility = 1;
}
142

143 144 145 146 147 148 149 150 151 152 153
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();
    }
154 155
}

156 157
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";
158 159 160 161 162 163
    usage();
}

if ($add_mode) {
    $text = join(' ', @ARGV);
}
164 165

if (! $list_mode && ! $add_mode && scalar(@ARGV) > 0) {
166 167 168
    usage();
}

169 170 171 172
if ($list_mode && scalar(@ARGV) > 0) {
    $list_type = $ARGV[0];
}

173
if ($add_mode) {
174 175 176
    #
    # Add a new announcement
    #
177 178
    my $query = "insert into apt_announcements set ";
    $query .= "created=NOW()";
179
    $query .= ", portal=" . DBQuoteSpecial($portal);
180 181 182 183 184 185 186 187
    $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);
188 189 190 191
    if ($compatibility)
    {
	TBSetSiteVar("web/banner", DBQuoteSpecial($text));
    }
192
} elsif ($list_mode) {
193 194 195 196 197 198 199 200 201 202
    #
    # List announcements
    #
    my $condition = "retired=0 and ";
    if ($list_type eq "all") {
	$condition = "";
    } elsif ($list_type eq "retired") {
	$condition = "retired=1 and ";
    }
    $query_result = 
203 204
	DBQueryFatal("select idx, portal, text from apt_announcements where ".$condition."uid_idx is NULL");
    print "idx\tPortal    Text\n";
205
    print "---\t-------    ----\n";
206
    while (my ($idx, $portal, $text) = $query_result->fetchrow_array()) {
207 208 209 210
	my $textbit = substr($text, 0, 55);
	if (length($text) > 55) {
	    $textbit = $textbit . "...";
	}
211 212
	my $portalpad = sprintf("%-10s", $portal);
	print "$idx\t$portalpad $textbit\n"
213 214
    }
} elsif ($retire_mode) {
215 216 217 218 219 220 221
    #
    # Retire an announcement
    #
    $query_result =
	DBQueryFatal("update apt_announcements ".
		     "set retired=1 where idx=".
		     DBQuoteSpecial($retire_idx));
222 223 224 225
    if ($compatibility)
    {
	TBSetSiteVar("web/banner", "");
    }
226 227 228 229 230 231 232 233 234 235 236
} 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();
237

238 239 240 241
    $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 =
242 243
	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,
244 245 246 247 248 249 250
	    $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";
251
	print "Portal:\t$portal\n\n";
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
	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";
    }
}
279 280 281 282 283 284 285 286 287

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

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