Separate owner password and persistence

Now, persistent rooms will be really persistent (never deleted), but can only be set by an admin
Rooms with an owner password set are now called "reserved" room, and works as before
master
Daniel Berteaud 10 years ago
parent a2b19cbeb8
commit a289803632
  1. 7
      conf/vroom.conf.sample
  2. 33
      lib/Vroom/I18N/en.pm
  3. 30
      lib/Vroom/I18N/fr.pm
  4. 45
      public/js/vroom.js
  5. 63
      public/vroom.pl
  6. 4
      templates/default/help.html.ep
  7. 6
      templates/default/join.html.ep
  8. 12
      templates/default/manage_room.html.ep
  9. 18
      templates/default/owner_password_modal.html.ep

@ -23,10 +23,11 @@ secret => 'ChangeMe!',
# App
# Rooms without any activity for that long (in seconds) will be destroyed
inactivityTimeout => 3600,
# Inactivity timeout (in seconds) for persistent rooms
# Inactivity timeout (in seconds) for rooms which have an owner password
# 0 means they are not deleted. You can use a high number
# so that persistent rooms are kept long enough, but deleted when not used
persistentInactivityTimeout => 0,
# so that those rooms are kept long enough, but deleted when really not used
# The admin interface lets you flag some room as persistent, meaning they will never be deleted
reservedInactivityTimeout => 0,
# A list of room names which are valid but too common to allow reservation
# with an owner password
commonRoomNames => [

@ -122,16 +122,16 @@ our %Lexicon = (
"PASSWORD_PROTECT" => "Password protect",
"PASSWORD_PROTECT_SET" => "A password will be needed to join this room",
"PASSWORD_PROTECT_UNSET" => "No password will be asked to join this room",
"ROOM_NOW_PERSISTENT" => "This room is now persistent",
"ROOM_NO_MORE_PERSISTENT" => "This room isn't persistent anymore",
"ROOM_NOW_RESERVED" => "This room is now reserved",
"ROOM_NO_MORE_RESERVED" => "This room isn't reserved anymore",
"PASSWORDS_DO_NOT_MATCH" => "Passwords do not match",
"MAKE_THIS_ROOM_PERSISTENT" => "Make this room persistent",
"SET_OWNER_PASS_PERSISTENT" => "To make this room persistent, you must set a manager password. Keep it carefully, " .
"RESERVE_THIS_ROOM" => "Reserve this room",
"SET_OWNER_PASS" => "To reserve this room, you must set an owner password. Keep it carefully, " .
"it'll grant you access to the configuration menus next time you connect.",
"A_STANDARD_ROOM_EXPIRES_AFTER_d" => "A standard room will be deleted after %d hour(s) without activity",
"A_PERSISTENT_ROOM" => "A persistant room",
"A_RESERVED_ROOM" => "A reserved room",
"EXPIRE_AFTER_d" => "will be deleted after %d day(s) without activity",
"NEVER_EXPIRE" => "will be kept forever",
"WILL_NEVER_EXPIRE" => "will be kept forever",
"CONFIRM_PASSWORD" => "Confirm password",
"PROTECT_ROOM_WITH_PASSWORD" => "If this password is set, participants will have to type it before the system let them in",
"ERROR_COMMON_ROOM_NAME" => "Sorry, this room name is too comon to be reserved",
@ -167,8 +167,11 @@ our %Lexicon = (
"NUMBER_OF_PARTICIPANTS" => "Number of participants",
"LOCKED" => "Locked",
"ASK_FOR_NAME" => "Require to enter a name",
"PASSWORD_PROTECTED" => "Password protection",
"JOIN_PASSWORD" => "Password to join the room",
"OWNER_PASSWORD" => "Password to manage the room",
"PERSISTENT" => "Persistent",
"ROOM_NOW_PERSISTENT" => "This room is now persistent",
"ROOM_NO_MORE_PERSISTENT" => "This rooms isn't persistent anymore",
"EMAIL_INVITE" => "Email invitation",
"DELETE_THIS_ROOM" => "Delete this room",
"CONFIRM_DELETE" => "Confirm delation",
@ -194,7 +197,7 @@ our %Lexicon = (
"HELP_PASSWORD_BUTTON" => "This button will protect access to this room with a password. Note that this password " .
"isn't asked it you join the room through an email invitation (in which case the " .
"authentication is done with a uniq token valid for two hours)",
"HELP_PERSISTENT_BUTTON" => "Make this room persistent, you'll be able to leave, reconnecte, and get configuration menus back. " .
"HELP_RESERVE_BUTTON" => "Reserve this room, you'll be able to leave, reconnect, and get configuration menus back. " .
"The room will also be kept much longer.",
"HELP_ASK_FOR_NAME_BUTTON" => "This will enforce participants to set their name before joining the room.",
"HELP_WIPE_DATA_BUTTON" => "This will wipe room data (chat history and collaborative pad content)",
@ -321,15 +324,15 @@ our %Lexicon = (
"later, which are simple participants). For example, he can protect access with a password " .
"which will be required before you can join the room. He also can set the manager's password " .
"which will allow him, if he leaves the room, to recover its privileges when he connects again.",
"PERSISTENT_ROOMS" => "Persistant rooms",
"HELP_PERSISTENT_ROOMS" => "By default, rooms are ephemeral, which means they are automatically deleted if they " .
"have no activity for a long time. The room's creator can define a manager's password, " .
"which will make the room persistent. Note that a persistent room can still be deleted " .
"if it's not used for a very long period of time.",
"RESERVED_ROOMS" => "Reserved rooms",
"HELP_RESERVED_ROOMS" => "By default, rooms are ephemeral, which means they are automatically deleted if they " .
"have no activity for some time. The room's creator can define an owner's password, " .
"which will make the room reserved. A reserved room can still be deleted " .
"if it's not used for a very long period of time, but will last longuer on the system",
"RESERVE_YOUR_ROOM" => "Reserve your room",
"HELP_RESERVE_YOUR_ROOM" => "Want to reserve your room name so it's always available for you (company name, ongoing project " .
"etc.) ? Just set both a join password and the manager password. The room will be kept " .
"as long as the manager password is set (and as long as you use it from time to time)",
"etc.) ? Just set both a join password and the owner password. The room will be kept " .
"as long as the owner password is set (and as long as you use it from time to time)",
"BE_NOTIFIED" => "Notifications",
"HELP_BE_NOTIFIED" => "You can be notified by email as soon as someone joins one of your rooms. For example, " .
"create a room, add a password to make it persistent and add the link in your email signature. " .

@ -128,17 +128,17 @@ our %Lexicon = (
"PASSWORD_PROTECT" => "Protéger par mot de passe",
"PASSWORD_PROTECT_SET" => "Un mot de passe sera demandé pour rejoindre ce salon",
"PASSWORD_PROTECT_UNSET" => "Aucun mot de passe ne sera demandé pour rejoindre ce salon",
"ROOM_NOW_PERSISTENT" => "Ce salon est maintenant persistant",
"ROOM_NO_MORE_PERSISTENT" => "Ce salon n'est plus persistant",
"ROOM_NOW_RESERVED" => "Ce salon est maintenant réservé",
"ROOM_NO_MORE_RESERVED" => "Ce salon ne vous est plus réservé",
"PASSWORDS_DO_NOT_MATCH" => "Les mots de passe ne correspondent pas",
"MAKE_THIS_ROOM_PERSISTENT" => "Rendre ce salon persistant",
"SET_OWNER_PASS_PERSISTENT" => "Pour rendre ce salon persistant, vous devez saisir un mot de passe. " .
"RESERVE_THIS_ROOM" => "Réserver ce salon",
"SET_OWNER_PASS" => "Pour réserver ce salon, vous devez saisir un mot de passe. " .
"Conservez le soigneusement, il vous permettra de retrouver " .
"l'accès aux menus de configuration quand vous vous reconnecterez.",
"A_STANDARD_ROOM_EXPIRES_AFTER_d" => "Un salon classique sera détruit après %d heure(s) sans activité",
"A_PERSISTENT_ROOM" => "Un salon persistant",
"A_RESERVED_ROOM" => "Un salon réservé",
"EXPIRE_AFTER_d" => "sera détruit après %d jour(s) sans activité",
"NEVER_EXPIRE" => "sera conservé indéfiniement",
"WILL_NEVER_EXPIRE" => "sera conservé indéfiniement",
"CONFIRM_PASSWORD" => "Confirmation du mot de passe",
"PROTECT_ROOM_WITH_PASSWORD" => "Si ce mot de passe est configuré, les participants devront le saisir avant de pouvoir " .
"rejoindre le salon",
@ -174,8 +174,11 @@ our %Lexicon = (
"NUMBER_OF_PARTICIPANTS" => "Nombre de participants",
"LOCKED" => "Verrouillé",
"ASK_FOR_NAME" => "Exige de saisir un nom",
"PASSWORD_PROTECTED" => "Protection par mot de passe",
"JOIN_PASSWORD" => "Mot de passe d'accès au salon",
"OWNER_PASSWORD" => "Mot de passe de gestionnaire",
"PERSISTENT" => "Persistant",
"ROOM_NOW_PERSISTENT" => "Ce salon est maintenant persistant",
"ROOM_NO_MORE_PERSISTENT" => "Ce salon n'est plus persistant",
"EMAIL_INVITE" => "Invitation par email",
"DELETE_THIS_ROOM" => "Supprimer ce salon",
"CONFIRM_DELETE" => "Confirmer la suppression",
@ -213,7 +216,7 @@ our %Lexicon = (
"pas demandé lorsque l'on " .
"rejoint un salon suite à une invitation par email (l'authentification se fait " .
"par un jeton unique valide pendant deux heures",
"HELP_PERSISTENT_BUTTON" => "Permet de rendre le salon persistant. Vous pourrez donc vous reconnecter et " .
"HELP_RESERVE_BUTTON" => "Permet de réserver le salon. Vous pourrez donc vous reconnecter et " .
"récupérer l'accès aux menus de configuration. Le salon sera également conservé " .
"bien plus longtemps sur le système",
"HELP_ASK_FOR_NAME_BUTTON" => "Permet d'imposer la saisie du nom avant de pouvoir rejoindre le salon",
@ -356,12 +359,13 @@ our %Lexicon = (
"de passe du gestionnaire ce qui lui permettra, s'il quitte le salon, de retrouver " .
"ses privilèges lorsqu'il se connecte à nouveau. Ces privilèges peuvent aussi être " .
"donnés à d'autres participants",
"PERSISTENT_ROOMS" => "Salons persistants",
"HELP_PERSISTENT_ROOMS" => "Par défaut, les salons sont éphémères, c'est à dire qu'ils sont automatiquement " .
"supprimés si ils ne présentent aucune activité pendant une durée prolongée. " .
"RESERVED_ROOMS" => "Salons réservés",
"HELP_RESERVED_ROOMS" => "Par défaut, les salons sont éphémères, c'est à dire qu'ils sont automatiquement " .
"supprimés si ils ne présentent aucune activité pendant un certains temps. " .
"Le créateur du salon peut définir un mot de passe de gestionaire, ce qui " .
"rendra le salon persistant. Notez qu'un salon persistant peut tout de même " .
"être supprimé si il n'est pas utilisé pendant une très longue période.",
"rendra réservera le salon. Un salon réservé peut tout de même " .
"être supprimé si il n'est pas utilisé pendant une très longue période, mais le " .
"délais sera bien plus long.",
"RESERVE_YOUR_ROOM" => "Réservez votre salon",
"HELP_RESERVE_YOUR_ROOM" => "Vous souhaitez réserver le nom de votre salon pour qu'il soit toujours disponible " .
"pour vous (nom de votre entreprise, nom d'un projet en cours etc.) ? Configurez simplement " .

@ -400,7 +400,7 @@ function initManage(){
}
else if (param === 'ownerPassSwitch'){
if (state){
$('#persistentModal').modal('show');
$('#ownerPassModal').modal('show');
sw.bootstrapSwitch('toggleState', true);
}
else{
@ -409,6 +409,11 @@ function initManage(){
sendAction(data,sw);
}
}
else if (param === 'persistentSwitch'){
data.action = 'setPersistent';
data.type = (state) ? 'set' : 'unset';
sendAction(data,sw);
}
// Something isn't implemented yet ?
else{
$.notify(locale.ERROR_OCCURRED, 'error');
@ -438,7 +443,7 @@ function initManage(){
}
});
$('#persistentForm').submit(function(event) {
$('#ownerPassForm').submit(function(event) {
event.preventDefault();
var pass = $('#ownerPass').val();
var pass2 = $('#ownerPassConfirm').val();
@ -451,7 +456,7 @@ function initManage(){
data.password = pass
sendAction(data, $('#ownerPassSwitch'));
$('#ownerPassSwitch').bootstrapSwitch('toggleState', true);
$('#persistentModal').modal('hide');
$('#ownerPassModal').modal('hide');
}
else{
$('#ownerPassConfirm').notify(locale.PASSWORDS_DO_NOT_MATCH, 'error');
@ -599,8 +604,8 @@ function initVroom(room) {
$('#joinPassButton').prop('checked', true);
}
if (data.owner_auth == 'yes'){
$('#persistentLabel').addClass('btn-danger active');
$('#persistentButton').prop('checked', true);
$('#ownerPassLabel').addClass('btn-danger active');
$('#ownerPassButton').prop('checked', true);
}
}
});
@ -1430,13 +1435,13 @@ function initVroom(room) {
var who = (peers[data.id].hasName) ? peers[data.id].displayName : locale.A_ROOM_ADMIN;
if (data.payload.action == 'set'){
$.notify(sprintf(locale.OWNER_PASSWORD_CHANGED_BY_s, stringEscape(who)), 'info');
$('#persistentLabel').addClass('btn-danger active');
$('#persistentButton').prop('checked', true);
$('#ownerPassLabel').addClass('btn-danger active');
$('#ownerPassButton').prop('checked', true);
}
else{
$.notify(sprintf(locale.OWNER_PASSWORD_REMOVED_BY_s, stringEscape(who)), 'info');
$('#persistentLabel').removeClass('btn-danger active');
$('#persistentButton').prop('checked', false);
$('#ownerPassLabel').removeClass('btn-danger active');
$('#ownerPassButton').prop('checked', false);
}
}
else{
@ -1983,15 +1988,15 @@ function initVroom(room) {
}
});
$('#persistentButton').change(function(){
$('#ownerPassButton').change(function(){
var action = ($(this).is(':checked')) ? 'set':'unset';
if (action == 'set'){
$('#persistentModal').modal('show');
$('#ownerPassModal').modal('show');
// Uncheck the button now
// so it's not inconsistent if we just close the modal dialog
// submitting the form will recheck it
$('#persistentButton').prop('checked', false);
$('#persistentLabel').removeClass('active');
$('#ownerPassButton').prop('checked', false);
$('#ownerPassLabel').removeClass('active');
}
else{
$.ajax({
@ -2008,7 +2013,7 @@ function initVroom(room) {
if (data.status == 'success'){
$.notify(data.msg, 'info');
webrtc.sendToAll('owner_password', {action: 'remove'});
$('#persistentLabel').removeClass('btn-danger active');
$('#ownerPassLabel').removeClass('btn-danger active');
}
else{
$.notify(data.msg, 'error');
@ -2019,7 +2024,7 @@ function initVroom(room) {
}
});
$('#persistentForm').submit(function(event) {
$('#ownerPassForm').submit(function(event) {
event.preventDefault();
var pass = $('#ownerPass').val();
var pass2 = $('#ownerPassConfirm').val();
@ -2034,15 +2039,15 @@ function initVroom(room) {
},
error: function() {
$.notify(locale.ERROR_OCCURRED, 'error');
$('#persistentLabel').removeClass('btn-danger active');
$('#ownerPassLabel').removeClass('btn-danger active');
},
success: function(data) {
$('#ownerPass').val('');
$('#ownerPassConfirm').val('');
if (data.status == 'success'){
$('#persistentModal').modal('hide');
$('#persistentLabel').addClass('btn-danger active');
$('#persistentButton').prop('checked', true);
$('#ownerPassModal').modal('hide');
$('#ownerPassLabel').addClass('btn-danger active');
$('#ownerPassButton').prop('checked', true);
$.notify(data.msg, 'info');
webrtc.sendToAll('owner_password', {action: 'set'});
}
@ -2224,7 +2229,7 @@ function initVroom(room) {
});
// Empty password fields on modal dismiss
$('#joinPassModal,#persistentModal').on('hide.bs.modal',function(){
$('#joinPassModal,#ownerPassModal').on('hide.bs.modal',function(){
$(this).find(':input').val('');
});

@ -122,7 +122,7 @@ our $config = plugin Config => {
poweredBy => '<a href="http://www.firewall-services.com" target="_blank">Firewall Services</a>',
template => 'default',
inactivityTimeout => 3600,
persistentInactivityTimeout => 0,
reservedInactivityTimeout => 5184000,
commonRoomNames => [ qw() ],
logLevel => 'info',
chromeExtensionId => 'ecicdpoejfllflombfanbhfpgcimjddn',
@ -380,7 +380,7 @@ helper delete_rooms => sub {
$self->app->log->debug('Removing unused rooms');
my $timeout = time()-$config->{inactivityTimeout};
my $sth = eval {
$self->db->prepare("SELECT `name` FROM `rooms` WHERE `activity_timestamp` < $timeout AND `persistent`='0';")
$self->db->prepare("SELECT `name` FROM `rooms` WHERE `activity_timestamp` < $timeout AND `persistent`='0' AND `owner_password` IS NULL;")
} || return undef;
$sth->execute();
my @toDeleteName = ();
@ -388,10 +388,10 @@ helper delete_rooms => sub {
push @toDeleteName, $room;
}
my @toDeleteId = ();
if ($config->{persistentInactivityTimeout} > 0){
$timeout = time()-$config->{persistentInactivityTimeout};
if ($config->{reservedInactivityTimeout} > 0){
$timeout = time()-$config->{reservedInactivityTimeout};
$sth = eval {
$self->db->prepare("SELECT `name` FROM `rooms` WHERE `activity_timestamp` < $timeout AND `persistent`='1';")
$self->db->prepare("SELECT `name` FROM `rooms` WHERE `activity_timestamp` < $timeout AND `persistent`='0' AND `owner_password` IS NOT NULL;")
} || return undef;
$sth->execute();
while (my $room = $sth->fetchrow_array){
@ -560,21 +560,40 @@ helper set_owner_pass => sub {
# Might be separated in the future
if ($pass){
my $sth = eval {
$self->db->prepare("UPDATE `rooms` SET `owner_password`=?,`persistent`='1' WHERE `name`=?;")
$self->db->prepare("UPDATE `rooms` SET `owner_password`=? WHERE `name`=?;")
} || return undef;
my $pass = Crypt::SaltedHash->new(algorithm => 'SHA-256')->add($pass)->generate;
$sth->execute($pass,$room) || return undef;
$self->app->log->debug($self->session('name') . " has set an owner password on room $room, which is now persistent");
$self->app->log->debug($self->session('name') . " has set an owner password on room $room");
}
else{
my $sth = eval {
$self->db->prepare("UPDATE `rooms` SET `owner_password`=?,`persistent`='0' WHERE `name`=?;")
$self->db->prepare("UPDATE `rooms` SET `owner_password`=? WHERE `name`=?;")
} || return undef;
$sth->execute(undef,$room) || return undef;
$self->app->log->debug($self->session('name') . " has removed the owner password on room $room, which is not persistent anymore");
$self->app->log->debug($self->session('name') . " has removed the owner password on room $room");
}
};
# Make the room persistent
helper set_persistent => sub {
my $self = shift;
my ($room,$set) = @_;
my $data = $self->get_room($room);
return undef unless ($data);
my $sth = eval {
$self->db->prepare("UPDATE `rooms` SET `persistent`=? WHERE `name`=?")
} || return undef;
$sth->execute($set,$room) || return undef;
if ($set eq '1'){
$self->app->log->debug("Room $room is now persistent");
}
else{
$self->app->log->debug("Room $room isn't persistent anymore");
}
return 1;
};
# Add an email address to the list of notifications
helper add_notification => sub {
my $self = shift;
@ -1238,7 +1257,7 @@ post '/*action' => [action => [qw/action admin\/action/]] => sub {
$msg = $self->l('ERROR_COMMON_ROOM_NAME');
}
elsif ($self->set_owner_pass($room,$pass)){
$msg = ($pass) ? $self->l('ROOM_NOW_PERSISTENT') : $self->l('ROOM_NO_MORE_PERSISTENT');
$msg = ($pass) ? $self->l('ROOM_NOW_RESERVED') : $self->l('ROOM_NO_MORE_RESERVED');
$status = 'success';
}
}
@ -1258,6 +1277,30 @@ post '/*action' => [action => [qw/action admin\/action/]] => sub {
}
);
}
# Handle persistence
elsif ($action eq 'setPersistent'){
my $type = $self->param('type');
my $status = 'error';
my $msg = $self->l('ERROR_OCCURRED');
# Only possible through /admin/action
if ($prefix ne 'admin'){
$msg = $self->l('NOT_ALLOWED');
}
elsif($type eq 'set' && $self->set_persistent($room,'1')){
$status = 'success';
$msg = $self->l('ROOM_NOW_PERSISTENT');
}
elsif($type eq 'unset' && $self->set_persistent($room,'0')){
$status = 'success';
$msg = $self->l('ROOM_NO_MORE_PERSISTENT');
}
return $self->render(
json => {
msg => $msg,
status => $status
}
);
}
# A participant is trying to auth as an owner, lets check that
elsif ($action eq 'authenticate'){
my $pass = $self->param('password');

@ -64,13 +64,13 @@
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapsePersistentRooms">
<%=l 'PERSISTENT_ROOMS' %>
<%=l 'RESERVED_ROOMS' %>
</a>
</h4>
</div>
<div id="collapsePersistentRooms" class="panel-collapse collapse">
<div class="panel-body">
<%==l 'HELP_PERSISTENT_ROOMS' %>
<%==l 'HELP_RESERVED_ROOMS' %>
</div>
</div>
</div>

@ -159,8 +159,8 @@
<span class="glyphicon glyphicon-lock">
</span>
</label>
<label class="btn btn-default help" id="persistentLabel" data-toggle="tooltip" data-placement="bottom" title="<%=l 'MAKE_THIS_ROOM_PERSISTENT' %>">
<input type="checkbox" id="persistentButton">
<label class="btn btn-default help" id="ownerPassLabel" data-toggle="tooltip" data-placement="bottom" title="<%=l 'RESERVE_THIS_ROOM' %>">
<input type="checkbox" id="ownerPassButton">
<span class="glyphicon glyphicon-pushpin">
</span>
</label>
@ -472,7 +472,7 @@
</div>
</div>
<div class="col-md-10">
<%=l 'HELP_PERSISTENT_BUTTON' %>
<%=l 'HELP_RESERVE_BUTTON' %>
</div>
</div>
<div class="list-group-item row ownerEl">

@ -90,7 +90,7 @@
</tr>
<tr>
<th>
<%=l 'PASSWORD_PROTECTED' %>
<%=l 'JOIN_PASSWORD' %>
</th>
<th>
<input class="bs-switch" type="checkbox" id="joinPassSwitch" data-room="<%= $room %>" <%= ($data->{join_password}) ? 'checked':''%>>
@ -98,7 +98,7 @@
</tr>
<tr>
<th>
<%=l 'PERSISTENT' %>
<%=l 'OWNER_PASSWORD' %>
</th>
<th>
<input class="bs-switch" type="checkbox" id="ownerPassSwitch" data-room="<%= $room %>" <%= ($data->{owner_password}) ? 'checked':''%>>
@ -106,6 +106,14 @@
</tr>
<tr>
<th>
<%=l 'PERSISTENT' %>
</th>
<th>
<input class="bs-switch" type="checkbox" id="persistentSwitch" data-room="<%= $room %>" <%= ($data->{persistent}) ? 'checked':''%>>
</th>
</tr>
<tr>
<th>
<%=l 'EMAIL_INVITE' %>
</th>
<th>

@ -1,28 +1,28 @@
<div class="modal fade" role="dialog" id="persistentModal" aria-labelledby="persistentModal" aria-hidden="true">
<div class="modal fade" role="dialog" id="ownerPassModal" aria-labelledby="ownerPassModal" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
&times;
</button>
<h4 class="modal-title" id="persistentTitle">
<%=l 'MAKE_THIS_ROOM_PERSISTENT' %>
<h4 class="modal-title">
<%=l 'RESERVE_THIS_ROOM' %>
</h4>
</div>
<div class="modal-body">
<form class="form-horizontal" role="form" id="persistentForm">
<form class="form-horizontal" role="form" id="ownerPassForm">
<p>
<%=l 'SET_OWNER_PASS_PERSISTENT' %>
<%=l 'SET_OWNER_PASS' %>
<ul>
<li>
<%= sprintf($self->l('A_STANDARD_ROOM_EXPIRES_AFTER_d'), int($config->{inactivityTimeout}/3600)) %>
</li>
<li>
<%=l 'A_PERSISTENT_ROOM' %>
<% if ($config->{persistentInactivityTimeout} > 0){ %>
<%= sprintf($self->l('EXPIRE_AFTER_d'), int($config->{persistentInactivityTimeout}/(3600*24))) %>
<%=l 'A_RESERVED_ROOM' %>
<% if ($config->{reservedInactivityTimeout} > 0){ %>
<%= sprintf($self->l('EXPIRE_AFTER_d'), int($config->{reservedInactivityTimeout}/(3600*24))) %>
<% } else{ %>
<%=l 'WILL NEVER_EXPIRE' %>
<%=l 'WILL_NEVER_EXPIRE' %>
<% } %>
</li>
</ul>

Loading…
Cancel
Save