enhancement on Gis Link
git-svn-id: http://svn.research-infrastructures.eu/public/d4science/gcube/trunk/portlets/user/speciesdiscovery@142571 82a268e6-3cf1-43bd-a215-b396298e98cf
This commit is contained in:
parent
92034592dd
commit
b1b268db1d
|
@ -89,9 +89,10 @@ public final class ConstantsSpeciesDiscovery {
|
|||
public static final String PLAIN_CSV_BY_DATA_SOURCE = "plain CSV (by Data Source)";
|
||||
public static final String PLAIN_CSV = "plain CSV";
|
||||
public static final String SAVE_OCCURRENCES = "Save Occurrences";
|
||||
public static final String CREATE_GIS_LAYER = "Create Gis Layer";
|
||||
// public static final String SAVES_IN_DARWIN_CORE_ARCHIVE_FORMAT = "Saves in Darwin Core Archive format";
|
||||
// public static final String DARWIN_CORE_ARCHIVE = "Darwin Core Archive";
|
||||
public static final String CREATE_GIS_LAYER_TITLE = "Create Gis Layer";
|
||||
public static final String CREATE_GIS_LAYER_TEXT = "Create a Gis Layer from selected occurences points";
|
||||
public static final String GIS_LAYER_EVENT_TEXT = "Gis Layer generated from SPD Portlet by gCube Framework";
|
||||
|
||||
public static final String SAVE_TAXONOMY_ITEMS = "Save Taxonomy Items";
|
||||
public final static String DETAILS = "Details";
|
||||
public final static String OCCURRENCEPOINTS = "Occurrence points";
|
||||
|
|
|
@ -2,6 +2,8 @@ package org.gcube.portlets.user.speciesdiscovery.client.gridview;
|
|||
|
||||
import org.gcube.portlets.user.speciesdiscovery.client.ConstantsSpeciesDiscovery;
|
||||
import org.gcube.portlets.user.speciesdiscovery.client.SearchController;
|
||||
import org.gcube.portlets.user.speciesdiscovery.client.SpeciesDiscovery;
|
||||
import org.gcube.portlets.user.speciesdiscovery.client.event.CreateGisLayerJobEvent;
|
||||
import org.gcube.portlets.user.speciesdiscovery.client.event.DisableFilterEvent;
|
||||
import org.gcube.portlets.user.speciesdiscovery.client.event.ShowOnlySelectedRowEvent;
|
||||
import org.gcube.portlets.user.speciesdiscovery.client.event.UpdateAllRowSelectionEvent;
|
||||
|
@ -16,6 +18,7 @@ import org.gcube.portlets.user.speciesdiscovery.shared.SaveFileFormat;
|
|||
import org.gcube.portlets.user.speciesdiscovery.shared.SearchResultType;
|
||||
import org.gcube.portlets.user.speciesdiscovery.shared.SearchType;
|
||||
|
||||
import com.allen_sauer.gwt.log.client.Log;
|
||||
import com.extjs.gxt.ui.client.Style.ButtonScale;
|
||||
import com.extjs.gxt.ui.client.Style.Scroll;
|
||||
import com.extjs.gxt.ui.client.data.ModelData;
|
||||
|
@ -24,6 +27,7 @@ import com.extjs.gxt.ui.client.event.MenuEvent;
|
|||
import com.extjs.gxt.ui.client.event.SelectionListener;
|
||||
import com.extjs.gxt.ui.client.store.ListStore;
|
||||
import com.extjs.gxt.ui.client.widget.ContentPanel;
|
||||
import com.extjs.gxt.ui.client.widget.Info;
|
||||
import com.extjs.gxt.ui.client.widget.button.Button;
|
||||
import com.extjs.gxt.ui.client.widget.button.ToggleButton;
|
||||
import com.extjs.gxt.ui.client.widget.layout.AnchorData;
|
||||
|
@ -37,6 +41,7 @@ import com.extjs.gxt.ui.client.widget.toolbar.SeparatorToolItem;
|
|||
import com.extjs.gxt.ui.client.widget.toolbar.ToolBar;
|
||||
import com.google.gwt.dom.client.Style.Unit;
|
||||
import com.google.gwt.event.shared.EventBus;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.AbstractImagePrototype;
|
||||
|
||||
public class ResultRowResultsPanel extends ContentPanel {
|
||||
|
@ -102,7 +107,7 @@ public class ResultRowResultsPanel extends ContentPanel {
|
|||
|
||||
private SearchController searchController;
|
||||
|
||||
private Button btnShowInGisViewer;
|
||||
private Button btnCreateGisLayer;
|
||||
private Button btnOccurrencesMenu;
|
||||
|
||||
public static ResultRowResultsPanel getInstance() {
|
||||
|
@ -499,7 +504,39 @@ public class ResultRowResultsPanel extends ContentPanel {
|
|||
});
|
||||
|
||||
formatSubMenu.add(darwinCoreFormatItem);
|
||||
|
||||
|
||||
btnCreateGisLayer = new Button(ConstantsSpeciesDiscovery.CREATE_GIS_LAYER_TITLE);
|
||||
btnCreateGisLayer.setMenu(formatSubMenu);
|
||||
btnCreateGisLayer.setScale(ButtonScale.MEDIUM);
|
||||
// btnOccurrencesMenu.setIconAlign(IconAlign.TOP);
|
||||
btnCreateGisLayer.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.getGisLayerLogo()));
|
||||
btnCreateGisLayer.setToolTip(new ToolTipConfig(ConstantsSpeciesDiscovery.CREATE_GIS_LAYER_TITLE, ConstantsSpeciesDiscovery.CREATE_GIS_LAYER_TEXT));
|
||||
|
||||
btnCreateGisLayer.addSelectionListener(new SelectionListener<ButtonEvent>() {
|
||||
|
||||
@Override
|
||||
public void componentSelected(ButtonEvent ce) {
|
||||
|
||||
SpeciesDiscovery.taxonomySearchService.retrieveOccurencesFromSelection(new AsyncCallback<Integer>() {
|
||||
|
||||
@Override
|
||||
public void onSuccess(Integer expectedPoints) {
|
||||
SearchController.eventBus.fireEvent(new CreateGisLayerJobEvent(searchController.getLastSearchEvent().getSearchTerm() + " occurrences layer", ConstantsSpeciesDiscovery.GIS_LAYER_EVENT_TEXT, expectedPoints));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
Info.display("Error getting occurrences", "Error getting occurrences, retry");
|
||||
Log.trace("Error getting occurrences", caught);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
viewsToolBar.add(btnOccurrencesMenu);
|
||||
viewsToolBar.add(btnCreateGisLayer);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -125,7 +125,7 @@ public class SpeciesJobPanel extends ContentPanel{
|
|||
@Override
|
||||
public void componentSelected(ButtonEvent ce) {
|
||||
|
||||
resetOccurrenceCounter();
|
||||
resetGisLayerCounter();
|
||||
gisLayerJobPanelInstance.getGisLayerJobWindow().show();
|
||||
}
|
||||
|
||||
|
@ -146,6 +146,11 @@ public class SpeciesJobPanel extends ContentPanel{
|
|||
setIconOccurrenceByCounter(0);
|
||||
}
|
||||
|
||||
private void resetGisLayerCounter(){
|
||||
jobGisLayerCount = 0;
|
||||
setIconGisLayerByCounter(0);
|
||||
}
|
||||
|
||||
public void setIconOccurrenceByCounter(int count){
|
||||
this.jobOccurrenceCount += count;
|
||||
|
||||
|
@ -182,19 +187,19 @@ public class SpeciesJobPanel extends ContentPanel{
|
|||
btnGisLayerJobs.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.getGlobe()));
|
||||
break;
|
||||
case 1:
|
||||
btnGisLayerJobs.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.getBluePlace1()));
|
||||
btnGisLayerJobs.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.getGlobe1()));
|
||||
break;
|
||||
case 2:
|
||||
btnGisLayerJobs.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.getBluePlace2()));
|
||||
btnGisLayerJobs.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.getGlobe2()));
|
||||
break;
|
||||
case 3:
|
||||
btnGisLayerJobs.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.getBluePlace3()));
|
||||
btnGisLayerJobs.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.getGlobe3()));
|
||||
break;
|
||||
case 4:
|
||||
btnGisLayerJobs.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.getBluePlace4()));
|
||||
btnGisLayerJobs.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.getGlobe4()));
|
||||
break;
|
||||
default:
|
||||
btnGisLayerJobs.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.getBluePlace4More()));
|
||||
btnGisLayerJobs.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.getGlobe4More()));
|
||||
}
|
||||
|
||||
toolbar.layout();
|
||||
|
|
|
@ -53,6 +53,7 @@ import com.extjs.gxt.ui.client.widget.toolbar.FillToolItem;
|
|||
import com.extjs.gxt.ui.client.widget.toolbar.SeparatorToolItem;
|
||||
import com.extjs.gxt.ui.client.widget.toolbar.ToolBar;
|
||||
import com.google.gwt.event.shared.EventBus;
|
||||
import com.google.gwt.user.client.Window;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.AbstractImagePrototype;
|
||||
|
||||
|
@ -69,11 +70,9 @@ public class GisLayerGridJob extends ContentPanel{
|
|||
private static final String REFRESH_LIST = "Refresh List";
|
||||
private static final String RE_SUBMIT = "Re-submit";
|
||||
private static final String SAVE = "Save";
|
||||
private static final String SAVEERROR = "Save Errors";
|
||||
private static final String CANCEL = "Cancel";
|
||||
private static final String INFO = "Info";
|
||||
// public static final String DATE_TIME_FORMAT = "yyyy.MM.dd 'at' HH:mm:ss";
|
||||
// private ListStore<FileModel> store = ListStoreModel.getInstance().getStore();
|
||||
private static final String OPEN_GIS_LAYER = "Open with Gis Viewer App";
|
||||
private ListStore<BaseModelData> store;
|
||||
private ToolBar toolBar = new ToolBar();
|
||||
private Grid<BaseModelData> grid;
|
||||
|
@ -489,20 +488,6 @@ public class GisLayerGridJob extends ContentPanel{
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
buttonSaveError = new Button(SAVEERROR);
|
||||
buttonSaveError.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.getSaveProducts()));
|
||||
buttonSaveError.setScale(ButtonScale.MEDIUM);
|
||||
buttonSaveError.addListener(Events.OnClick, new Listener<BaseEvent>() {
|
||||
|
||||
@Override
|
||||
public void handleEvent(BaseEvent be) {
|
||||
|
||||
saveErrorJob();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
buttonReSubmit = new Button(RE_SUBMIT);
|
||||
buttonReSubmit.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.getReSubmit()));
|
||||
buttonReSubmit.setScale(ButtonScale.MEDIUM);
|
||||
|
@ -516,7 +501,6 @@ public class GisLayerGridJob extends ContentPanel{
|
|||
|
||||
});
|
||||
|
||||
|
||||
buttonRefreshList = new Button(REFRESH_LIST);
|
||||
buttonRefreshList.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.getRefresh()));
|
||||
buttonRefreshList.setScale(ButtonScale.MEDIUM);
|
||||
|
@ -531,8 +515,6 @@ public class GisLayerGridJob extends ContentPanel{
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
toolBar.add(buttonInfo);
|
||||
toolBar.add(new SeparatorToolItem());
|
||||
|
||||
|
@ -591,6 +573,33 @@ public class GisLayerGridJob extends ContentPanel{
|
|||
*/
|
||||
public void createMenuItemsOnGrid(){
|
||||
|
||||
MenuItem menuOpenWithGis = new MenuItem();
|
||||
menuOpenWithGis.setText(OPEN_GIS_LAYER);
|
||||
menuOpenWithGis.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.getGlobe()));
|
||||
|
||||
menuOpenWithGis.addSelectionListener(new SelectionListener<MenuEvent>() {
|
||||
|
||||
@Override
|
||||
public void componentSelected(MenuEvent ce) {
|
||||
|
||||
BaseModelData baseModelData = grid.getSelectionModel().getSelectedItem();
|
||||
|
||||
if(baseModelData!=null){
|
||||
String url = baseModelData.get(JobGisLayerModel.GISVIEWERAPPLINK);
|
||||
if(url==null){
|
||||
Window.alert("The task '" +baseModelData.get(JobGisLayerModel.JOBNAME) +"' does not contain a valid GisViewer App link, Is it completed?");
|
||||
return;
|
||||
}
|
||||
Window.open(url, "", "");
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
menu.add(menuOpenWithGis);
|
||||
|
||||
|
||||
|
||||
MenuItem menuInfo = new MenuItem();
|
||||
menuInfo.setText(INFO);
|
||||
menuInfo.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.getInfoIcon()));
|
||||
|
@ -621,22 +630,6 @@ public class GisLayerGridJob extends ContentPanel{
|
|||
|
||||
menu.add(menuSave);
|
||||
|
||||
MenuItem menuSaveError = new MenuItem();
|
||||
menuSaveError.setText(SAVEERROR);
|
||||
menuSaveError.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.getSaveProducts()));
|
||||
|
||||
menuSaveError.addSelectionListener(new SelectionListener<MenuEvent>() {
|
||||
|
||||
@Override
|
||||
public void componentSelected(MenuEvent ce) {
|
||||
saveErrorJob();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
menu.add(menuSaveError);
|
||||
|
||||
|
||||
MenuItem menuCancel= new MenuItem();
|
||||
menuCancel.setText(CANCEL);
|
||||
menuCancel.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.getDelete()));
|
||||
|
@ -667,9 +660,7 @@ public class GisLayerGridJob extends ContentPanel{
|
|||
});
|
||||
|
||||
menu.add(menuResubmit);
|
||||
|
||||
grid.setContextMenu(menu);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -179,7 +179,25 @@ public interface Resources extends ClientBundle {
|
|||
* @return
|
||||
*/
|
||||
|
||||
@Source("globe24.png")
|
||||
ImageResource getGisLayerLogo();
|
||||
|
||||
@Source("gislayer/globe.png")
|
||||
ImageResource getGlobe();
|
||||
|
||||
@Source("gislayer/globe_1.png")
|
||||
ImageResource getGlobe1();
|
||||
|
||||
@Source("gislayer/globe_2.png")
|
||||
ImageResource getGlobe2();
|
||||
|
||||
@Source("gislayer/globe_3.png")
|
||||
ImageResource getGlobe3();
|
||||
|
||||
@Source("gislayer/globe_4.png")
|
||||
ImageResource getGlobe4();
|
||||
|
||||
@Source("gislayer/globe_4+.png")
|
||||
ImageResource getGlobe4More();
|
||||
|
||||
}
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
|
@ -332,9 +332,9 @@ public class ViewDetailsWindow extends Window {
|
|||
|
||||
ToolBar toolbar = new ToolBar();
|
||||
|
||||
Button btnShowInGisViewer = new Button(ConstantsSpeciesDiscovery.CREATE_GIS_LAYER);
|
||||
Button btnShowInGisViewer = new Button(ConstantsSpeciesDiscovery.CREATE_GIS_LAYER_TITLE);
|
||||
btnShowInGisViewer.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.getGisProducts()));
|
||||
btnShowInGisViewer.setToolTip(new ToolTipConfig(ConstantsSpeciesDiscovery.CREATE_GIS_LAYER, "Create a Gis Layer from selected occurences points."));
|
||||
btnShowInGisViewer.setToolTip(new ToolTipConfig(ConstantsSpeciesDiscovery.CREATE_GIS_LAYER_TITLE, ConstantsSpeciesDiscovery.CREATE_GIS_LAYER_TEXT));
|
||||
btnShowInGisViewer.setScale(ButtonScale.SMALL);
|
||||
btnShowInGisViewer.setIconAlign(IconAlign.TOP);
|
||||
btnShowInGisViewer.setArrowAlign(ButtonArrowAlign.BOTTOM);
|
||||
|
|
|
@ -72,6 +72,7 @@ import org.gcube.portlets.user.speciesdiscovery.shared.DatabaseServiceException;
|
|||
import org.gcube.portlets.user.speciesdiscovery.shared.DownloadState;
|
||||
import org.gcube.portlets.user.speciesdiscovery.shared.FetchingElement;
|
||||
import org.gcube.portlets.user.speciesdiscovery.shared.GisLayerJob;
|
||||
import org.gcube.portlets.user.speciesdiscovery.shared.InvalidJobIdException;
|
||||
import org.gcube.portlets.user.speciesdiscovery.shared.ItemParameter;
|
||||
import org.gcube.portlets.user.speciesdiscovery.shared.JobGisLayerModel;
|
||||
import org.gcube.portlets.user.speciesdiscovery.shared.JobOccurrencesModel;
|
||||
|
@ -1723,6 +1724,7 @@ public class TaxonomySearchServiceImpl extends RemoteServiceServlet implements T
|
|||
if(job.getId()==null || job.getId().isEmpty()){
|
||||
logger.warn("Gis job has an id null or empty, skipping");
|
||||
}else{
|
||||
|
||||
CompleteJobStatus statusResponse = taxonomyService.getGisLayerByJobId(job.getId());
|
||||
logger.info("get occurrence job "+job.getId()+ " from service");
|
||||
|
||||
|
@ -1744,9 +1746,15 @@ public class TaxonomySearchServiceImpl extends RemoteServiceServlet implements T
|
|||
}
|
||||
|
||||
}catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
logger.error("Error on getListGisLayerJob ", e);
|
||||
throw new Exception("Error on getListGisLayerJob", e);
|
||||
|
||||
if (e instanceof InvalidJobIdException){
|
||||
logger.info("The spd service unkwnowns GIS job id: "+job.getId() +" deleting it from db...");
|
||||
GisLayerJobUtil.deleteGisLayerJobById(job.getId(),gisLayerJobDao);
|
||||
}else{
|
||||
|
||||
logger.error("Error on getListGisLayerJob ", e);
|
||||
throw new Exception("Error on getListGisLayerJob", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ import org.gcube.portlets.user.speciesdiscovery.shared.Coordinate;
|
|||
import org.gcube.portlets.user.speciesdiscovery.shared.DataSourceCapability;
|
||||
import org.gcube.portlets.user.speciesdiscovery.shared.DataSourceModel;
|
||||
import org.gcube.portlets.user.speciesdiscovery.shared.DataSourceRepositoryInfo;
|
||||
import org.gcube.portlets.user.speciesdiscovery.shared.InvalidJobIdException;
|
||||
import org.gcube.portlets.user.speciesdiscovery.shared.SearchFilters;
|
||||
import org.gcube.portlets.user.speciesdiscovery.shared.SearchServiceException;
|
||||
import org.gcube.portlets.user.speciesdiscovery.shared.SearchType;
|
||||
|
@ -501,18 +502,22 @@ public class SpeciesService {
|
|||
* @param serverJobId the server job id
|
||||
* @return the gis layer by job id
|
||||
* @throws SearchServiceException the search service exception
|
||||
* @throws InvalidJobIdException
|
||||
*/
|
||||
public CompleteJobStatus getGisLayerByJobId(String serverJobId) throws SearchServiceException {
|
||||
public CompleteJobStatus getGisLayerByJobId(String serverJobId) throws InvalidJobIdException, Exception{
|
||||
|
||||
try {
|
||||
ExecutorClient creator = AbstractPlugin.executor().build();
|
||||
logger.debug("ExecutorClient is null: "+(creator==null));
|
||||
logger.debug("Get status for job Id: "+serverJobId);
|
||||
return creator.getStatus(serverJobId);
|
||||
}
|
||||
catch (InvalidIdentifierException e) {
|
||||
logger.error(
|
||||
"Error calling the Species Service: " + e.getMessage(), e);
|
||||
throw new SearchServiceException(
|
||||
"Error calling the Species Service: " + e.getMessage());
|
||||
logger.error("Error calling the Species Service: " + e.getMessage());
|
||||
throw new InvalidJobIdException(
|
||||
"Invalid job id: " + serverJobId);
|
||||
}catch (Exception e) {
|
||||
throw new Exception("Service exception: ",e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -524,7 +529,7 @@ public class SpeciesService {
|
|||
* @return the gis layer result link by job id
|
||||
* @throws SearchServiceException the search service exception
|
||||
*/
|
||||
public String getGisLayerResultLinkByJobId(String serverJobId) throws SearchServiceException {
|
||||
public String getGisLayerResultLinkByJobId(String serverJobId) throws InvalidJobIdException {
|
||||
|
||||
try {
|
||||
ExecutorClient creator = AbstractPlugin.executor().build();
|
||||
|
@ -533,8 +538,8 @@ public class SpeciesService {
|
|||
catch (InvalidIdentifierException e) {
|
||||
logger.error(
|
||||
"Error calling the Species Service: " + e.getMessage(), e);
|
||||
throw new SearchServiceException(
|
||||
"Error calling the Species Service: " + e.getMessage());
|
||||
throw new InvalidJobIdException(
|
||||
"Invalid job id: " + serverJobId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
*
|
||||
*/
|
||||
package org.gcube.portlets.user.speciesdiscovery.shared;
|
||||
|
||||
|
||||
/**
|
||||
* The Class InvalidJobIdException.
|
||||
*
|
||||
* @author Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it
|
||||
* Feb 15, 2017
|
||||
*/
|
||||
public class InvalidJobIdException extends Exception {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = -6513243962411796791L;
|
||||
|
||||
/**
|
||||
* Instantiates a new invalid job id exception.
|
||||
*/
|
||||
public InvalidJobIdException(){}
|
||||
|
||||
/**
|
||||
* Instantiates a new invalid job id exception.
|
||||
*
|
||||
* @param message the message
|
||||
*/
|
||||
public InvalidJobIdException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue