mirror of
https://gitee.com/jmix/cuba.git
synced 2024-12-04 20:28:00 +08:00
DateField basic port
This commit is contained in:
parent
d83f21baed
commit
0c723d63dd
@ -49,8 +49,8 @@ public class WebComponentsFactory implements ComponentsFactory {
|
|||||||
classes.put(Table.NAME, WebTable.class);
|
classes.put(Table.NAME, WebTable.class);
|
||||||
classes.put(TreeTable.NAME, WebTreeTable.class);
|
classes.put(TreeTable.NAME, WebTreeTable.class);
|
||||||
classes.put(GroupTable.NAME, WebGroupTable.class);
|
classes.put(GroupTable.NAME, WebGroupTable.class);
|
||||||
// classes.put(DateField.NAME, WebDateField.class);
|
classes.put(DateField.NAME, WebDateField.class);
|
||||||
// classes.put(TimeField.NAME, WebTimeField.class);
|
classes.put(TimeField.NAME, WebTimeField.class);
|
||||||
classes.put(LookupField.NAME, WebLookupField.class);
|
classes.put(LookupField.NAME, WebLookupField.class);
|
||||||
// classes.put(SearchField.NAME, WebSearchField.class);
|
// classes.put(SearchField.NAME, WebSearchField.class);
|
||||||
classes.put(PickerField.NAME, WebPickerField.class);
|
classes.put(PickerField.NAME, WebPickerField.class);
|
||||||
|
@ -17,6 +17,7 @@ import com.vaadin.server.FileResource;
|
|||||||
import com.vaadin.server.Resource;
|
import com.vaadin.server.Resource;
|
||||||
import com.vaadin.server.ThemeResource;
|
import com.vaadin.server.ThemeResource;
|
||||||
import com.vaadin.shared.ui.combobox.FilteringMode;
|
import com.vaadin.shared.ui.combobox.FilteringMode;
|
||||||
|
import com.vaadin.shared.ui.datefield.Resolution;
|
||||||
import com.vaadin.ui.*;
|
import com.vaadin.ui.*;
|
||||||
import com.vaadin.ui.Button;
|
import com.vaadin.ui.Button;
|
||||||
import com.vaadin.ui.Component;
|
import com.vaadin.ui.Component;
|
||||||
@ -357,31 +358,27 @@ public class WebComponentsHelper {
|
|||||||
return FieldGroupLayout.CAPTION_ALIGN_LEFT;
|
return FieldGroupLayout.CAPTION_ALIGN_LEFT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
public static int convertDateFieldResolution(com.haulmont.cuba.gui.components.DateField.Resolution resolution) {
|
public static Resolution convertDateFieldResolution(com.haulmont.cuba.gui.components.DateField.Resolution resolution) {
|
||||||
switch (resolution) {
|
switch (resolution) {
|
||||||
case MSEC: {
|
case SEC:
|
||||||
return com.vaadin.ui.DateField.RESOLUTION_MSEC;
|
return Resolution.SECOND;
|
||||||
}
|
|
||||||
case SEC: {
|
case HOUR:
|
||||||
return com.vaadin.ui.DateField.RESOLUTION_SEC;
|
return Resolution.HOUR;
|
||||||
}
|
|
||||||
case HOUR: {
|
case DAY:
|
||||||
return com.vaadin.ui.DateField.RESOLUTION_HOUR;
|
return Resolution.DAY;
|
||||||
}
|
|
||||||
case DAY: {
|
case MONTH:
|
||||||
return com.vaadin.ui.DateField.RESOLUTION_DAY;
|
return Resolution.MONTH;
|
||||||
}
|
|
||||||
case MONTH: {
|
case YEAR:
|
||||||
return com.vaadin.ui.DateField.RESOLUTION_MONTH;
|
return Resolution.YEAR;
|
||||||
}
|
|
||||||
case YEAR: {
|
|
||||||
return com.vaadin.ui.DateField.RESOLUTION_YEAR;
|
|
||||||
}
|
|
||||||
case MIN:
|
case MIN:
|
||||||
default: {
|
default:
|
||||||
return com.vaadin.ui.DateField.RESOLUTION_MIN;
|
return Resolution.MINUTE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}*/
|
|
||||||
}
|
}
|
@ -2,10 +2,6 @@
|
|||||||
* Copyright (c) 2008 Haulmont Technology Ltd. All Rights Reserved.
|
* Copyright (c) 2008 Haulmont Technology Ltd. All Rights Reserved.
|
||||||
* Haulmont Technology proprietary and confidential.
|
* Haulmont Technology proprietary and confidential.
|
||||||
* Use is subject to license terms.
|
* Use is subject to license terms.
|
||||||
|
|
||||||
* Author: Dmitry Abramov
|
|
||||||
* Created: 22.12.2008 18:12:13
|
|
||||||
* $Id$
|
|
||||||
*/
|
*/
|
||||||
package com.haulmont.cuba.web.gui.components;
|
package com.haulmont.cuba.web.gui.components;
|
||||||
|
|
||||||
@ -14,8 +10,9 @@ import com.haulmont.chile.core.model.MetaProperty;
|
|||||||
import com.haulmont.chile.core.model.MetaPropertyPath;
|
import com.haulmont.chile.core.model.MetaPropertyPath;
|
||||||
import com.haulmont.chile.core.model.utils.InstanceUtils;
|
import com.haulmont.chile.core.model.utils.InstanceUtils;
|
||||||
import com.haulmont.cuba.core.entity.Entity;
|
import com.haulmont.cuba.core.entity.Entity;
|
||||||
import com.haulmont.cuba.core.global.ConfigProvider;
|
import com.haulmont.cuba.core.global.AppBeans;
|
||||||
import com.haulmont.cuba.core.global.UserSessionProvider;
|
import com.haulmont.cuba.core.global.Configuration;
|
||||||
|
import com.haulmont.cuba.core.global.UserSessionSource;
|
||||||
import com.haulmont.cuba.gui.components.*;
|
import com.haulmont.cuba.gui.components.*;
|
||||||
import com.haulmont.cuba.gui.data.Datasource;
|
import com.haulmont.cuba.gui.data.Datasource;
|
||||||
import com.haulmont.cuba.gui.data.ValueChangingListener;
|
import com.haulmont.cuba.gui.data.ValueChangingListener;
|
||||||
@ -34,12 +31,16 @@ import java.util.Calendar;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author abramov
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
public class WebDateField
|
public class WebDateField
|
||||||
/*extends
|
extends
|
||||||
WebAbstractComponent<DateFieldWrapper>
|
WebAbstractComponent<DateFieldWrapper>
|
||||||
implements
|
implements
|
||||||
DateField, Component.Wrapper*/ {
|
DateField, Component.Wrapper {
|
||||||
/*
|
|
||||||
private Resolution resolution;
|
private Resolution resolution;
|
||||||
|
|
||||||
private Object prevValue = null;
|
private Object prevValue = null;
|
||||||
@ -50,8 +51,8 @@ public class WebDateField
|
|||||||
private com.haulmont.cuba.web.toolkit.ui.DateField dateField;
|
private com.haulmont.cuba.web.toolkit.ui.DateField dateField;
|
||||||
private WebTimeField timeField;
|
private WebTimeField timeField;
|
||||||
|
|
||||||
protected List<ValueListener> listeners = new ArrayList<ValueListener>();
|
protected List<ValueListener> listeners = new ArrayList<>();
|
||||||
protected List<Field.Validator> validators = new ArrayList<Field.Validator>();
|
protected List<Field.Validator> validators = new ArrayList<>();
|
||||||
|
|
||||||
protected HorizontalLayout composition;
|
protected HorizontalLayout composition;
|
||||||
|
|
||||||
@ -74,7 +75,7 @@ public class WebDateField
|
|||||||
|
|
||||||
composition.setSpacing(true);
|
composition.setSpacing(true);
|
||||||
dateField = new com.haulmont.cuba.web.toolkit.ui.DateField();
|
dateField = new com.haulmont.cuba.web.toolkit.ui.DateField();
|
||||||
dateField.setResolution(com.haulmont.cuba.web.toolkit.ui.DateField.RESOLUTION_DAY);
|
dateField.setResolution(com.vaadin.shared.ui.datefield.Resolution.DAY);
|
||||||
dateField.setWidth("100%");
|
dateField.setWidth("100%");
|
||||||
|
|
||||||
dateField.setImmediate(true);
|
dateField.setImmediate(true);
|
||||||
@ -82,17 +83,12 @@ public class WebDateField
|
|||||||
dateField.addValidator(new com.vaadin.data.Validator() {
|
dateField.addValidator(new com.vaadin.data.Validator() {
|
||||||
@Override
|
@Override
|
||||||
public void validate(Object value) throws InvalidValueException {
|
public void validate(Object value) throws InvalidValueException {
|
||||||
if (value instanceof Date)
|
// if (value instanceof Date)
|
||||||
return;
|
// return;
|
||||||
if (!isValid(value)) {
|
// if (!isValid(value)) {
|
||||||
dateField.requestRepaint();
|
// dateField.markAsDirty();
|
||||||
throw new InvalidValueException("Unable to parse value: " + value);
|
// throw new InvalidValueException("Unable to parse value: " + value);
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isValid(Object value) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -104,7 +100,7 @@ public class WebDateField
|
|||||||
timeField.<MaskedTextField>getComponent().setInvalidAllowed(false);
|
timeField.<MaskedTextField>getComponent().setInvalidAllowed(false);
|
||||||
timeField.<MaskedTextField>getComponent().setInvalidCommitted(true);
|
timeField.<MaskedTextField>getComponent().setInvalidCommitted(true);
|
||||||
|
|
||||||
dateField.addListener(new Property.ValueChangeListener() {
|
dateField.addValueChangeListener(new Property.ValueChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void valueChange(Property.ValueChangeEvent event) {
|
public void valueChange(Property.ValueChangeEvent event) {
|
||||||
updateInstance();
|
updateInstance();
|
||||||
@ -118,7 +114,7 @@ public class WebDateField
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
setResolution(Resolution.MIN);
|
setResolution(Resolution.MIN);
|
||||||
if (ConfigProvider.getConfig(WebConfig.class).getCloseCalendarWhenDateSelected()) {
|
if (AppBeans.get(Configuration.class).getConfig(WebConfig.class).getCloseCalendarWhenDateSelected()) {
|
||||||
setCloseWhenDateSelected(true);
|
setCloseWhenDateSelected(true);
|
||||||
}
|
}
|
||||||
component = new DateFieldWrapper(this, composition);
|
component = new DateFieldWrapper(this, composition);
|
||||||
@ -202,7 +198,8 @@ public class WebDateField
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void __setCloseWhenDateSelected(boolean autoClose) {
|
protected void __setCloseWhenDateSelected(boolean autoClose) {
|
||||||
dateField.setCloseWhenDateSelected(autoClose);
|
// vaadin7
|
||||||
|
// dateField.setCloseWhenDateSelected(autoClose);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -234,7 +231,7 @@ public class WebDateField
|
|||||||
prevValue = getValue();
|
prevValue = getValue();
|
||||||
if (!editable)
|
if (!editable)
|
||||||
return;
|
return;
|
||||||
dateField.setValue(value);
|
dateField.setValue((Date) value);
|
||||||
timeField.setValue(value);
|
timeField.setValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,11 +371,14 @@ public class WebDateField
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Date constructDate() {
|
private Date constructDate() {
|
||||||
final Date datePickerDate = (Date) dateField.getValue();
|
final Date datePickerDate = dateField.getValue();
|
||||||
if (datePickerDate == null) {
|
if (datePickerDate == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Calendar c = Calendar.getInstance(UserSessionProvider.getLocale());
|
|
||||||
|
UserSessionSource uss = AppBeans.get(UserSessionSource.class);
|
||||||
|
|
||||||
|
Calendar c = Calendar.getInstance(uss.getLocale());
|
||||||
c.setTime(datePickerDate);
|
c.setTime(datePickerDate);
|
||||||
if (timeField.getValue() == null) {
|
if (timeField.getValue() == null) {
|
||||||
c.set(Calendar.HOUR_OF_DAY, 0);
|
c.set(Calendar.HOUR_OF_DAY, 0);
|
||||||
@ -386,7 +386,7 @@ public class WebDateField
|
|||||||
c.set(Calendar.SECOND, 0);
|
c.set(Calendar.SECOND, 0);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Calendar c2 = Calendar.getInstance(UserSessionProvider.getLocale());
|
Calendar c2 = Calendar.getInstance(uss.getLocale());
|
||||||
c2.setTime(timeField.<Date>getValue());
|
c2.setTime(timeField.<Date>getValue());
|
||||||
|
|
||||||
c.set(Calendar.HOUR_OF_DAY, c2.get(Calendar.HOUR_OF_DAY));
|
c.set(Calendar.HOUR_OF_DAY, c2.get(Calendar.HOUR_OF_DAY));
|
||||||
@ -461,5 +461,5 @@ public class WebDateField
|
|||||||
for (Field.Validator validator : validators) {
|
for (Field.Validator validator : validators) {
|
||||||
validator.validate(value);
|
validator.validate(value);
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
}
|
}
|
@ -2,11 +2,6 @@
|
|||||||
* Copyright (c) 2010 Haulmont Technology Ltd. All Rights Reserved.
|
* Copyright (c) 2010 Haulmont Technology Ltd. All Rights Reserved.
|
||||||
* Haulmont Technology proprietary and confidential.
|
* Haulmont Technology proprietary and confidential.
|
||||||
* Use is subject to license terms.
|
* Use is subject to license terms.
|
||||||
|
|
||||||
* Author: Konstantin Krivopustov
|
|
||||||
* Created: 09.12.2010 17:11:28
|
|
||||||
*
|
|
||||||
* $Id$
|
|
||||||
*/
|
*/
|
||||||
package com.haulmont.cuba.web.gui.components;
|
package com.haulmont.cuba.web.gui.components;
|
||||||
|
|
||||||
@ -24,6 +19,8 @@ import com.haulmont.cuba.web.gui.data.PropertyWrapper;
|
|||||||
import com.haulmont.cuba.web.toolkit.ui.MaskedTextField;
|
import com.haulmont.cuba.web.toolkit.ui.MaskedTextField;
|
||||||
import com.vaadin.data.Property;
|
import com.vaadin.data.Property;
|
||||||
import com.vaadin.data.util.PropertyFormatter;
|
import com.vaadin.data.util.PropertyFormatter;
|
||||||
|
import com.vaadin.data.util.converter.Converter;
|
||||||
|
import com.vaadin.server.UserError;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
@ -33,8 +30,12 @@ import java.text.SimpleDateFormat;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
public class WebTimeField /*extends WebAbstractField<MaskedTextField> implements TimeField, Component.Wrapper*/ {
|
/**
|
||||||
/*private boolean showSeconds;
|
* @author krivopustov
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class WebTimeField extends WebAbstractField<MaskedTextField> implements TimeField, Component.Wrapper {
|
||||||
|
private boolean showSeconds;
|
||||||
|
|
||||||
private String mask;
|
private String mask;
|
||||||
private String placeholder;
|
private String placeholder;
|
||||||
@ -57,26 +58,23 @@ public class WebTimeField /*extends WebAbstractField<MaskedTextField> implements
|
|||||||
component.addValidator(new com.vaadin.data.Validator() {
|
component.addValidator(new com.vaadin.data.Validator() {
|
||||||
@Override
|
@Override
|
||||||
public void validate(Object value) throws InvalidValueException {
|
public void validate(Object value) throws InvalidValueException {
|
||||||
if (!isValid(value)) {
|
if (!(!(value instanceof String) || checkStringValue((String) value))) {
|
||||||
component.requestRepaint();
|
component.markAsDirty();
|
||||||
throw new InvalidValueException("Unable to parse value: " + value);
|
throw new InvalidValueException("Unable to parse value: " + value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isValid(Object value) {
|
|
||||||
|
|
||||||
return (!(value instanceof String) || checkStringValue((String) value));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
attachListener(component);
|
attachListener(component);
|
||||||
|
|
||||||
|
// vaadin7 rewrite to converter
|
||||||
final Property p = new AbstractPropertyWrapper() {
|
final Property p = new AbstractPropertyWrapper() {
|
||||||
public Class<?> getType() {
|
public Class<?> getType() {
|
||||||
return Date.class;
|
return Date.class;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// vaadin7 rewrite to converter
|
||||||
component.setPropertyDataSource(
|
component.setPropertyDataSource(
|
||||||
new PropertyFormatter(p) {
|
new PropertyFormatter(p) {
|
||||||
|
|
||||||
@ -101,7 +99,7 @@ public class WebTimeField /*extends WebAbstractField<MaskedTextField> implements
|
|||||||
return date;
|
return date;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("Unable to parse value of component " + getId() + "\n" + e.getMessage());
|
log.warn("Unable to parse value of component " + getId() + "\n" + e.getMessage());
|
||||||
component.setComponentError(new com.vaadin.data.Validator.InvalidValueException("Invalid value"));
|
component.setComponentError(new UserError("Invalid value"));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
@ -169,6 +167,7 @@ public class WebTimeField /*extends WebAbstractField<MaskedTextField> implements
|
|||||||
super.setValue(value);
|
super.setValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean getShowSeconds() {
|
public boolean getShowSeconds() {
|
||||||
return showSeconds;
|
return showSeconds;
|
||||||
}
|
}
|
||||||
@ -193,6 +192,7 @@ public class WebTimeField /*extends WebAbstractField<MaskedTextField> implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setShowSeconds(boolean showSeconds) {
|
public void setShowSeconds(boolean showSeconds) {
|
||||||
this.showSeconds = showSeconds;
|
this.showSeconds = showSeconds;
|
||||||
if (showSeconds) {
|
if (showSeconds) {
|
||||||
@ -241,7 +241,7 @@ public class WebTimeField /*extends WebAbstractField<MaskedTextField> implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object valueOf(Object newValue) throws ConversionException {
|
protected Object valueOf(Object newValue) throws Converter.ConversionException {
|
||||||
if (newValue instanceof String) {
|
if (newValue instanceof String) {
|
||||||
if (StringUtils.isNotEmpty((String) newValue) && !newValue.equals(placeholder)) {
|
if (StringUtils.isNotEmpty((String) newValue) && !newValue.equals(placeholder)) {
|
||||||
try {
|
try {
|
||||||
@ -252,7 +252,7 @@ public class WebTimeField /*extends WebAbstractField<MaskedTextField> implements
|
|||||||
return date;
|
return date;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("Unable to parse value of component " + getId() + "\n" + e.getMessage());
|
log.warn("Unable to parse value of component " + getId() + "\n" + e.getMessage());
|
||||||
component.setComponentError(new com.vaadin.data.Validator.InvalidValueException("Invalid value"));
|
component.setComponentError(new UserError("Invalid value"));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
@ -263,5 +263,5 @@ public class WebTimeField /*extends WebAbstractField<MaskedTextField> implements
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}*/
|
}
|
||||||
}
|
}
|
@ -6,14 +6,21 @@
|
|||||||
|
|
||||||
package com.haulmont.cuba.web.toolkit.ui;
|
package com.haulmont.cuba.web.toolkit.ui;
|
||||||
|
|
||||||
/**
|
import com.haulmont.cuba.web.gui.components.WebDateField;
|
||||||
* <p>$Id$</p>
|
import com.vaadin.data.Property;
|
||||||
*
|
import com.vaadin.data.util.converter.Converter;
|
||||||
* @author devyatkin
|
import com.vaadin.ui.Layout;
|
||||||
*/
|
import org.apache.commons.lang.ObjectUtils;
|
||||||
public class DateFieldWrapper /*extends CustomField*/ {
|
|
||||||
|
|
||||||
/*private WebDateField dateField;
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author devyatkin
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class DateFieldWrapper extends CustomField {
|
||||||
|
|
||||||
|
private WebDateField dateField;
|
||||||
|
|
||||||
public DateFieldWrapper(WebDateField dateField, Layout composition) {
|
public DateFieldWrapper(WebDateField dateField, Layout composition) {
|
||||||
this.dateField = dateField;
|
this.dateField = dateField;
|
||||||
@ -34,7 +41,7 @@ public class DateFieldWrapper /*extends CustomField*/ {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setValue(Object newValue) throws ReadOnlyException, ConversionException {
|
public void setValue(Object newValue) throws ReadOnlyException, Converter.ConversionException {
|
||||||
if (getPropertyDataSource() != null)
|
if (getPropertyDataSource() != null)
|
||||||
getPropertyDataSource().setValue(newValue);
|
getPropertyDataSource().setValue(newValue);
|
||||||
dateField.setValue(newValue);
|
dateField.setValue(newValue);
|
||||||
@ -81,5 +88,18 @@ public class DateFieldWrapper /*extends CustomField*/ {
|
|||||||
// support dateField in editable table
|
// support dateField in editable table
|
||||||
if (newDataSource != null && !ObjectUtils.equals(newDataSource.getValue(), getValue()))
|
if (newDataSource != null && !ObjectUtils.equals(newDataSource.getValue(), getValue()))
|
||||||
dateField.setValue(newDataSource.getValue());
|
dateField.setValue(newDataSource.getValue());
|
||||||
}*/
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBuffered(boolean buffered) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isBuffered() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAllValidators() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user