Commit c402ca8c by Tuomas Riihimäki

Merge branch 'placeQueueFixes' into 'master'

made some stuff for placequeue, some fixes also



See merge request !362
2 parents fc4dfef2 fc0bf8dc
......@@ -56,6 +56,8 @@ public interface PlaceBeanLocal {
boolean releasePlace(Place place);
boolean userReleasePlace(Place place);
Place mergeChanges(Place place);
PlaceGroup buySelectedPlaces(EventUser user) throws BortalCatchableException;
......
......@@ -7,6 +7,7 @@ import javax.ejb.Local;
import fi.codecrew.moya.model.EventMap;
import fi.codecrew.moya.model.EventUser;
import fi.codecrew.moya.model.MapQueueRules;
import fi.codecrew.moya.model.map.MapReservationQueueEntry;
@Local
......@@ -26,6 +27,10 @@ public interface QueueBeanLocal {
MapQueueI getMapQueue(EventMap map);
void saveQueue(MapQueueI queue, EventMap map);
MapQueueRules findRules(EventMap map);
public interface MapQueueI {
Collection<MapReservationQueueEntry> getQueue();
......@@ -46,6 +51,10 @@ public interface QueueBeanLocal {
MapReservationQueueEntry getEntry(EventUser u);
boolean isBiggestFirst();
void setBiggestFirst(boolean biggestFirst);
}
void forceAddToReserving(EventMap map, EventUser u, Date time);
......
......@@ -417,7 +417,6 @@ public class BootstrapBean implements BootstrapBeanLocal {
});
dbUpdates.add(new String[] {
"ALTER TABLE products ADD COLUMN min_buy_count INTEGER default 0;"
});
......@@ -426,6 +425,17 @@ public class BootstrapBean implements BootstrapBeanLocal {
"ALTER TABLE discounts ADD COLUMN sort INTEGER NOT NULL default 10;"
});
dbUpdates.add(new String[] {
"CREATE TABLE map_queue_rules (id SERIAL NOT NULL, meta json, reserving_size INTEGER, default_timeout_min INTEGER, minium_slots_in_queue INTEGER, map_id INTEGER NOT NULL , PRIMARY KEY (id))",
"ALTER TABLE map_queue_rules ADD CONSTRAINT FK_map_queue_rules_map_id FOREIGN KEY (map_id) REFERENCES maps (id)",
});
dbUpdates.add(new String[] {
"ALTER TABLE map_queue_rules ADD COLUMN biggest_first BOOLEAN default false",
});
}
......
......@@ -494,6 +494,21 @@ public class PlaceBean implements PlaceBeanLocal {
return releasePlacePriv(place) != null;
}
/**
* User release place
*
* @param place The place to be released
* @return true when successfull, on any erroro false.
*/
@Override
@RolesAllowed({ MapPermission.S_BUY_PLACES, MapPermission.S_MANAGE_OTHERS })
public boolean userReleasePlace(Place place) {
place = placeFacade.reload(place);
PlaceSlot s = releasePlacePriv(place);
return (s == null || s.getUsed() == null);
}
private PlaceSlot releasePlacePriv(Place place) {
EventUser user = permbean.getCurrentUser();
if (place.getGroup() != null || place.getCurrentUser() == null
......
package fi.codecrew.moya.beans.map;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Set;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
import fi.codecrew.moya.model.MapQueueRules;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -24,9 +19,12 @@ public class MapQueue implements MapQueueI {
private int defaultTimeoutMin = 10;
private int minimumSlotsInQueue = 1;
private int reservingSize = 5;
private boolean biggestFirst = false;
// private final Set<MapReservationQueueEntry> reserving = new HashSet<>();
private final Set<EventUser> reserving = Collections.newSetFromMap(new ConcurrentHashMap<>());
private final PriorityQueue<MapReservationQueueEntry> queue = new PriorityQueue<>();
//private final PriorityQueue<MapReservationQueueEntry> queue = new PriorityQueue<>();
private final List<MapReservationQueueEntry> queue = Collections.synchronizedList(new ArrayList<>());
private final ConcurrentMap<EventUser, MapReservationQueueEntry> queEntries = new ConcurrentHashMap<>();
private final Integer mapId;
//private final Object reservingLock = new Object();
......@@ -35,10 +33,20 @@ public class MapQueue implements MapQueueI {
public MapQueue(EventMap map) {
this.mapId = map.getId();
}
public MapQueue(EventMap map, MapQueueRules rules) {
this(map);
if(rules != null) {
this.defaultTimeoutMin = rules.getDefaultTimeoutMin();
this.minimumSlotsInQueue = rules.getMinimumSlotsInQueue();
this.reservingSize = rules.getReservingSize();
this.biggestFirst = rules.getBiggestFirst();
}
}
void timeoutEntries() {
public void timeoutEntries() {
logger.info("Timeouting entries");
// give 10 seconds mercy ( and give us some time to go through all entries)
Date now = new Date(System.currentTimeMillis() + 1000 * 15);
......@@ -81,13 +89,18 @@ public class MapQueue implements MapQueueI {
private final AtomicBoolean modifyReservers = new AtomicBoolean(false);
private void checkReservingEntry() {
if (reserving.size() < getReservingSize() && modifyReservers.compareAndSet(false, true)) {
try {
if (reserving.size() < getReservingSize()) {
MapReservationQueueEntry queEntry = queue.poll();
if (reserving.size() < getReservingSize() && !queue.isEmpty()) {
MapReservationQueueEntry queEntry = queue.remove(0);
sortQueue();
if (queEntry != null) {
reserving.add(queEntry.getUser());
queEntry.setReservationTimeout(new Date(System.currentTimeMillis() + getDefaultTimeoutMin() * 60 * 1000));
......@@ -112,16 +125,25 @@ public class MapQueue implements MapQueueI {
while (queue.remove(entry)) {
}
}
sortQueue();
checkReservingEntry();
return ret;
}
public void sortQueue() {
if(this.biggestFirst)
Collections.sort(queue);
}
public MapReservationQueueEntry enter(EventUser user, List<PlaceSlot> slots) {
MapReservationQueueEntry ret = initEntry(user, slots);
if (!reserving.contains(user) && !queue.contains(ret)) {
queue.add(ret);
sortQueue();
// Check if the user can be put to reservation queue immediately
checkReservingEntry();
......@@ -151,6 +173,7 @@ public class MapQueue implements MapQueueI {
ret.setUser(user);
ret.setSeenTime(new Date());
ret.setPlaceslotcount(slots.size());
MapReservationQueueEntry old = queEntries.putIfAbsent(user, ret);
if (old != null) {
ret = old;
......@@ -190,12 +213,22 @@ public class MapQueue implements MapQueueI {
return queEntries.get(user);
}
@Override
public boolean isBiggestFirst() {
return this.biggestFirst;
}
@Override
public void setBiggestFirst(boolean biggestFirst) {
this.biggestFirst = biggestFirst;
}
public Collection<EventUser> getReserving() {
return reserving;
}
@Override
public PriorityQueue<MapReservationQueueEntry> getQueue() {
public List<MapReservationQueueEntry> getQueue() {
return queue;
}
......@@ -223,4 +256,5 @@ public class MapQueue implements MapQueueI {
this.reservingSize = reservingSize;
}
}
......@@ -5,7 +5,6 @@ import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.OverridingMethodsMustInvokeSuper;
import javax.annotation.security.DeclareRoles;
import javax.annotation.security.RolesAllowed;
import javax.ejb.Asynchronous;
......@@ -17,6 +16,8 @@ import javax.ejb.Singleton;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import fi.codecrew.moya.facade.MapQueueRulesFacade;
import fi.codecrew.moya.model.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -25,10 +26,6 @@ import fi.codecrew.moya.beans.PermissionBeanLocal;
import fi.codecrew.moya.beans.PlaceBeanLocal;
import fi.codecrew.moya.enums.apps.MapPermission;
import fi.codecrew.moya.facade.PlaceSlotFacade;
import fi.codecrew.moya.model.EventMap;
import fi.codecrew.moya.model.EventUser;
import fi.codecrew.moya.model.LanEventPropertyKey;
import fi.codecrew.moya.model.PlaceSlot;
import fi.codecrew.moya.model.map.MapReservationQueueEntry;
/**
......@@ -62,6 +59,9 @@ public class QueueBean implements QueueBeanLocal {
@EJB
private PermissionBeanLocal permbean;
@EJB
private MapQueueRulesFacade mapQueueRulesFacade;
@Lock(LockType.READ)
@Override
public boolean isReserving(EventMap map, EventUser user) {
......@@ -119,7 +119,9 @@ public class QueueBean implements QueueBeanLocal {
MapQueue ret = mapqueues.get(map.getId());
if (ret == null) {
ret = new MapQueue(map);
ret = new MapQueue(map, mapQueueRulesFacade.findByMap(map));
MapQueue nret = mapqueues.putIfAbsent(map.getId(), ret);
if (nret != null) {
ret = nret;
......@@ -231,6 +233,31 @@ public class QueueBean implements QueueBeanLocal {
}
@Override
public void saveQueue(MapQueueI queue, EventMap map) {
MapQueueRules rules = mapQueueRulesFacade.findByMap(map);
if(rules == null) {
rules = new MapQueueRules();
rules.setMap(map);
mapQueueRulesFacade.create(rules);
rules = mapQueueRulesFacade.reload(rules);
}
rules.setDefaultTimeoutMin(queue.getDefaultTimeoutMin());
rules.setMinimumSlotsInQueue(queue.getMinimumSlotsInQueue());
rules.setReservingSize(queue.getReservingSize());
rules.setBiggestFirst(queue.isBiggestFirst());
mapQueueRulesFacade.merge(rules);
}
@Override
public MapQueueRules findRules(EventMap map) {
return mapQueueRulesFacade.findByMap(map);
}
@Override
@Lock(LockType.READ)
@RolesAllowed(MapPermission.S_MANAGE_MAPS)
public void forceRemove(EventMap e, EventUser u) {
......
/*
* Copyright Codecrew Ry
*
* All rights reserved.
*
* This license applies to any software containing a notice placed by the
* copyright holder. Such software is herein referred to as the Software.
* This license covers modification, distribution and use of the Software.
*
* Any distribution and use in source and binary forms, with or without
* modification is not permitted without explicit written permission from the
* copyright owner.
*
* A non-exclusive royalty-free right is granted to the copyright owner of the
* Software to use, modify and distribute all modifications to the Software in
* future versions of the Software.
*
*/
package fi.codecrew.moya.facade;
import fi.codecrew.moya.model.EventMap;
import fi.codecrew.moya.model.MapQueueRules;
import fi.codecrew.moya.model.MapQueueRules_;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
@Stateless
@LocalBean
public class MapQueueRulesFacade extends IntegerPkGenericFacade<MapQueueRules> {
public MapQueueRulesFacade() {
super(MapQueueRules.class);
}
public MapQueueRules findByMap(EventMap map) {
if(map == null) {
throw new NullPointerException("Map must be given to MapQueueRules.findByMap");
}
CriteriaBuilder cb = getEm().getCriteriaBuilder();
CriteriaQuery<MapQueueRules> cq = cb.createQuery(MapQueueRules.class);
Root<MapQueueRules> root = cq.from(MapQueueRules.class);
cq.where(cb.equal(root.get(MapQueueRules_.map), map));
return getSingleNullableResult(getEm().createQuery(cq).setMaxResults(1));
}
}
/*
* Copyright Codecrew Ry
*
* All rights reserved.
*
* This license applies to any software containing a notice placed by the
* copyright holder. Such software is herein referred to as the Software.
* This license covers modification, distribution and use of the Software.
*
* Any distribution and use in source and binary forms, with or without
* modification is not permitted without explicit written permission from the
* copyright owner.
*
* A non-exclusive royalty-free right is granted to the copyright owner of the
* Software to use, modify and distribute all modifications to the Software in
* future versions of the Software.
*
*/
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package fi.codecrew.moya.model;
import javax.persistence.*;
import java.awt.*;
import java.util.Calendar;
/**
*
*/
@Entity
@Table(name = "map_queue_rules")
public class MapQueueRules extends GenericEntity {
private static final long serialVersionUID = 1L;
@Column(name = "reserving_size")
private Integer reservingSize = 5;
@Column(name = "default_timeout_min")
private Integer defaultTimeoutMin = 10;
@Column(name = "minium_slots_in_queue")
private Integer minimumSlotsInQueue = 1;
@Column(name = "biggest_first")
private Boolean biggestFirst = false;
@JoinColumn(name = "map_id", referencedColumnName = "id")
@ManyToOne
private EventMap map;
public MapQueueRules() {
super();
}
public Integer getReservingSize() {
return reservingSize;
}
public void setReservingSize(Integer reservingSize) {
this.reservingSize = reservingSize;
}
public Integer getDefaultTimeoutMin() {
return defaultTimeoutMin;
}
public void setDefaultTimeoutMin(Integer defaultTimeoutMin) {
this.defaultTimeoutMin = defaultTimeoutMin;
}
public Integer getMinimumSlotsInQueue() {
return minimumSlotsInQueue;
}
public void setMinimumSlotsInQueue(Integer minimumSlotsInQueue) {
this.minimumSlotsInQueue = minimumSlotsInQueue;
}
public EventMap getMap() {
return map;
}
public void setMap(EventMap map) {
this.map = map;
}
public Boolean getBiggestFirst() {
return biggestFirst;
}
public void setBiggestFirst(Boolean biggestFirst) {
this.biggestFirst = biggestFirst;
}
}
......@@ -92,9 +92,17 @@ public class MapReservationQueueEntry extends GenericEntity implements Comparabl
}
@Override
/**
* Smaller is bigger.
*
* Bigger first.
*/
public int compareTo(MapReservationQueueEntry o) {
return getPlaceslotcount().compareTo(o.getPlaceslotcount());
if(o == null || o.getPlaceslotcount() == null)
return -1;
return o.getPlaceslotcount().compareTo(getPlaceslotcount());
}
public Integer getPlaceslotcount() {
......
......@@ -13,18 +13,35 @@
<ui:define name="content">
<h2>Queue properties</h2>
<ui:fragment rendered="#{ajaxMapView.queueEnabled}">
<h:outputText value="#{i18n['queuemgmt.queueEnabled']}"/>
</ui:fragment>
<ui:fragment rendered="#{not ajaxMapView.queueEnabled}">
<h:outputText value="#{i18n['queuemgmt.queueDisabled']}: "/>
</ui:fragment>
<br />
<h:form>
<p:panelGrid columns="2">
<p:panelGrid columns="3" id="queueGrid">
<p:outputLabel for="minslots" value="#{i18n['queuemgmt.minimumSlotsInQueue']}" />
<p:inputText id="minslots" value="#{queueManageView.queue.minimumSlotsInQueue}" />
<h:outputText value="#{i18n['queuemgmt.inDatabase']}: #{queueManageView.defaultValues.minimumSlotsInQueue}" />
<p:outputLabel for="reservingsize" value="#{i18n['queuemgmt.reservingSize']}" />
<p:inputText id="reservingsize" value="#{queueManageView.queue.reservingSize}" />
<h:outputText value="#{i18n['queuemgmt.inDatabase']}: #{queueManageView.defaultValues.reservingSize}" />
<p:outputLabel for="defaultTimeout" value="#{i18n['queuemgmt.defaultTimeoutMin']}" />
<p:inputText id="defaultTimeout" value="#{queueManageView.queue.defaultTimeoutMin}" />
<h:outputText value="#{i18n['queuemgmt.inDatabase']}: #{queueManageView.defaultValues.defaultTimeoutMin}" />
<p:outputLabel for="biggestFirst" value="#{i18n['queuemgmt.biggestFirst']}" />
<p:selectBooleanCheckbox id="biggestFirst" value="#{queueManageView.queue.biggestFirst}" />
<h:outputText value="#{i18n['queuemgmt.inDatabase']}: #{queueManageView.defaultValues.biggestFirst}" />
</p:panelGrid>
<p:commandButton ajax="false" value="#{i18n['queuemgmt.saveProperties']}"></p:commandButton>
<p:commandButton value="#{i18n['queuemgmt.saveProperties']}" actionListener="#{queueManageView.saveToDatabase}" update="queueGrid" ></p:commandButton>
</h:form>
......
......@@ -70,18 +70,24 @@
</p:column>
</p:dataTable>
<br /><br />
<ui:fragment rendered="#{ajaxMapView.reserving}">
<div style="margin: 5px;">
<h:form id="placeselectform">
<p:commandButton onclick="$(window).unbind('beforeunload');" rendered="#{ajaxMapView.canUserBuy()}" value="#{i18n['mapView.buyPlaces']}" actionListener="#{ajaxMapView.buySelectedPlaces()}" />
</h:form>
</div>
<ui:fragment rendered="#{ajaxMapView.queueEnabled}">
<h:outputText value="#{i18n['mapView.reserveTimeLeft']}: "/>
<span id="reserveTimeLeft"/><br/>
</ui:fragment>
</ui:fragment>
<!-- Print queue status -->
<ui:fragment rendered="#{ajaxMapView.queueEnabled and not ajaxMapView.reserving}">
<h3><h:outputText value="#{i18n['mapView.youAreInQueue']}"/></h3>
<h4><h:outputText rendered="#{ajaxMapView.biggestFirst}" value="#{i18n['queuemgmt.biggestIsFirst']}" /> </h4>
<div style="margin: 1em;">
<h:outputText value="#{i18n['mapView.queuePosition']}: "/>
......@@ -98,6 +104,7 @@
<script type="text/javascript">
// Queue is enabled and we are reserving.
var queueReserving = #{(ajaxMapView.queueEnabled and not ajaxMapView.reserving)?"true":"false"};
var queueSelectingPlaces = #{(ajaxMapView.queueEnabled and ajaxMapView.reserving)?"true":"false"};
function updateMap() {
......@@ -114,6 +121,23 @@
}).fail(function () {
location.reload();
});
} else if(queueSelectingPlaces) {
$.getJSON("#{request.contextPath}/rest/placemap/v1/queueStillReserving/#{ajaxMapView.map.id}/#{ajaxMapView.eventuser.id}")
.done(function (data) {
if(data.value == 0) {
alert("#{i18n['reservequeue.reservingTimeIsUpAlert']}");
$(window).unbind('beforeunload');
location.href = "/";
}
var secsLeft = (#{ajaxMapView.reservingTimeslotEndtime}/1000) - ((new Date()).getTime()/1000 );
$("#reserveTimeLeft").text( Math.floor(secsLeft/60) + ":" + Math.round(secsLeft - (60 * Math.floor(secsLeft/60) ) ) );
}).fail(function () {
location.reload();
});
}
}
......@@ -130,7 +154,6 @@
location.reload();
} else {
$("#queuepos").text(data.value);
var d = new Date();
$("#queueupdated").text(new Date().toTimeString().replace(/.*(\d{2}:\d{2}:\d{2}).*/, "$1"));
}
}
......
......@@ -181,6 +181,29 @@ public class PlacemapRestViewV1 {
return root;
}
@GET
@Path("/queueStillReserving/{mapid}/{userid}")
public IntegerRoot getStillReserving(@PathParam("mapid") Integer mapid, @PathParam("userid") Integer userid) {
EventUser user = null;
if (userid != null) {
user = userbean.findByEventUserId(userid);
} else {
user = permbean.getCurrentUser();
}
if (user == null) {
return null;
}
EventMap map = placebean.findMap(mapid);
IntegerRoot root = new IntegerRoot();
root.setValue(quebean.isReserving(map, user)?1:0);
return root;
}
@POST
@Path("/place/{place}")
public Response togglePlaceReservation(@PathParam("place") Integer placeId)
......@@ -200,7 +223,7 @@ public class PlacemapRestViewV1 {
boolean success = false;
if (p.isReservedFor(user)) {
success = placebean.releasePlace(p);
success = placebean.userReleasePlace(p);
} else if (p.isBuyable() && !p.isTaken()) {
logger.info("Rest Reserving place for place {}", p);
success = placebean.reservePlace(p, user);
......@@ -209,7 +232,7 @@ public class PlacemapRestViewV1 {
ResponseBuilder resp = null;
if (success) {
p = placebean.find(placeId);
List<Place> thisplace = new ArrayList<Place>();
List<Place> thisplace = new ArrayList<>();
thisplace.add(p);
resp = Response.ok(PojoUtils.parseSimplePlaces(thisplace, user, permbean.hasPermission(UserPermission.VIEW_ALL)));
} else {
......@@ -257,7 +280,7 @@ public class PlacemapRestViewV1 {
}
private ArrayList<PlaceCodePojo> makePlaceCodePojos(List<Place> places) {
ArrayList<PlaceCodePojo> ret = new ArrayList<PlaceCodePojo>();
ArrayList<PlaceCodePojo> ret = new ArrayList<>();
for (Place p : places) {
if (p.getCode() == null || p.getCode().isEmpty()) {
String newcode = null;
......
package fi.codecrew.moya.web.cdiview.map;
import java.util.Collection;
import java.util.HashMap;
import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.Map;
import javax.ejb.EJB;
import javax.enterprise.context.RequestScoped;
......@@ -232,6 +231,16 @@ public class AjaxMapView extends GenericCDIView {
return reserving;
}
public long getReservingTimeslotEndtime() {
Date timeout = quebean.getReservationTimeout(initMap());
if(timeout == null)
return 0;
return timeout.getTime();
}
public boolean canUserBuy() {
return permbean.hasPermission(MapPermission.BUY_PLACES) && (permbean.hasPermission(MapPermission.MANAGE_OTHERS)
|| quebean.isReserving(initMap(), userview.getSelectedUser()));
......@@ -320,4 +329,8 @@ public class AjaxMapView extends GenericCDIView {
this.slotcount = slotcount;
}
public boolean isBiggestFirst() {
return isQueueEnabled() && quebean.getMapQueue(initMap()).isBiggestFirst();
}
}
......@@ -9,6 +9,7 @@ import javax.enterprise.context.ConversationScoped;
import javax.faces.model.ListDataModel;
import javax.inject.Named;
import fi.codecrew.moya.model.MapQueueRules;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -38,6 +39,8 @@ public class QueueManageView extends GenericCDIView {
private EventMap map;
private MapQueueRules rules;
private fi.codecrew.moya.beans.map.QueueBeanLocal.MapQueueI queue;
private String username;
......@@ -59,6 +62,8 @@ public class QueueManageView extends GenericCDIView {
if (super.requirePermissions(MapPermission.MANAGE_MAPS) && map == null) {
map = placebean.findMap(mapId);
rules = quebean.findRules(map);
queue = quebean.getMapQueue(map);
logger.info("Got queue {} for map {} with id {} to manage", queue, map, mapId);
super.beginConversation();
......@@ -154,4 +159,13 @@ public class QueueManageView extends GenericCDIView {
public void setTime(Date time) {
this.time = time;
}
public void saveToDatabase() {
quebean.saveQueue(queue, map);
rules = quebean.findRules(map); // refresh
}
public MapQueueRules getDefaultValues() {
return rules;
}
}
......@@ -899,7 +899,7 @@ queuemgmt.defaultTimeoutMin = Reservation time
queuemgmt.minimumSlotsInQueue = Minimum amount of places to enter queue
queuemgmt.remove = Remove user from queue
queuemgmt.reservingSize = Number of people reserving at the same time
queuemgmt.saveProperties = Save properties
queuemgmt.saveProperties = Save properties to Database
queuemgmt.time = Reservation time
queuemgmt.username = Login
......@@ -1582,3 +1582,10 @@ yes = Yes
page.product.shopClosed.header=Shop is closed!
page.product.shopClosed.notOpenYet=Shop is not opened. Try again later.
page.product.shopClosed.alreadyClosed=Shop is closed, welcome back in next event!
queuemgmt.inDatabase=Value in database
queuemgmt.biggestFirst=Biggest group first
queuemgmt.biggestIsFirst=Bigger group automaticly rises up in queue.
reservequeue.reservingTimeIsUpAlert=Timeslot for your place reservation has timeouted. Pleace go back to queue to continue.
mapView.reserveTimeLeft=Time to reserve places
queuemgmt.queueEnabled=Queue is ENABLED
queuemgmt.queueDisabled=Queue is DISABLED (enable from Edit event -page)
......@@ -1163,7 +1163,7 @@ queuemgmt.defaultTimeoutMin = Reservation time
queuemgmt.minimumSlotsInQueue = Minimum amount of places to enter queue
queuemgmt.remove = Remove user from queue
queuemgmt.reservingSize = Number of people reserving at the same time
queuemgmt.saveProperties = Save properties
queuemgmt.saveProperties = Save properties to Database
queuemgmt.time = Reservation time
queuemgmt.username = Login
......@@ -1860,3 +1860,10 @@ yes = Yes
page.product.shopClosed.header=Shop is closed!
page.product.shopClosed.notOpenYet=Shop is not opened. Try again later.
page.product.shopClosed.alreadyClosed=Shop is closed, welcome back in next event!
queuemgmt.inDatabase=Value in database
queuemgmt.biggestFirst=Biggest group first
queuemgmt.biggestIsFirst=Bigger group automaticly rises up in queue.
reservequeue.reservingTimeIsUpAlert=Timeslot for your place reservation has timeouted. Pleace go back to queue to continue.
mapView.reserveTimeLeft=Time to reserve places
queuemgmt.queueEnabled=Queue is ENABLED
queuemgmt.queueDisabled=Queue is DISABLED (enable from Edit event -page)
......@@ -1150,7 +1150,7 @@ queuemgmt.defaultTimeoutMin = Varausaika
queuemgmt.minimumSlotsInQueue = Jonoonp\u00E4\u00E4syn v\u00E4himm\u00E4ispaikkam\u00E4\u00E4r\u00E4
queuemgmt.remove = Poista k\u00E4ytt\u00E4j\u00E4 jonosta
queuemgmt.reservingSize = Samanaikaisesti varaamaan p\u00E4\u00E4sevien m\u00E4\u00E4r\u00E4
queuemgmt.saveProperties = Tallenna arvot
queuemgmt.saveProperties = Tallenna arvot tietokantaan
queuemgmt.time = Varausaika
queuemgmt.username = K\u00E4ytt\u00E4j\u00E4tunnus
......@@ -1847,3 +1847,10 @@ yes = Kyll\u00E4
page.product.shopClosed.header=Kauppa on kiinni!
page.product.shopClosed.notOpenYet=Tapahtuman lipunmyynti ei ole viel\u00E4 alkanut, Kokeile uudelleen my\u00F6hemmin.
page.product.shopClosed.alreadyClosed=Kauppa on suljettu, tervetuloa uudelleen tuleviin tapahtumiin.
queuemgmt.inDatabase=Arvo tietokannassa
queuemgmt.biggestFirst=Isoin ryhm\u00E4 ensin
queuemgmt.biggestIsFirst=Isompi ryhm\u00E4 nousee automaattisesti jonossa edemm\u00E4ksi.
reservequeue.reservingTimeIsUpAlert=Sinulle varattu varausauka on kulunut loppuun! Palaa takaisin 'varaa paikkasi' -sivulle jatkaaksesi.
mapView.reserveTimeLeft=Aikaa varata paikkasi
queuemgmt.queueEnabled=Varausjono ON k\u00E4yt\u00F6ss\u00E4
queuemgmt.queueDisabled=K\u00E4ytt\u00E4j\u00E4jono EI OLE k\u00E4yt\u00F6ss\u00E4 (ota k\u00E4yttoon tapahtuman tiedot -sivulta)
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!