Statistics
| Branch: | Tag: | Revision:

root / src / gr / grnet / pithos / web / client / mysharedtree / MysharedTreeViewModel.java @ eb2ae14b

History | View | Annotate | Download (12.5 kB)

1 105b4e49 Christos Stathis
/*
2 105b4e49 Christos Stathis
 * Copyright 2011 GRNET S.A. All rights reserved.
3 105b4e49 Christos Stathis
 *
4 105b4e49 Christos Stathis
 * Redistribution and use in source and binary forms, with or
5 105b4e49 Christos Stathis
 * without modification, are permitted provided that the following
6 105b4e49 Christos Stathis
 * conditions are met:
7 105b4e49 Christos Stathis
 *
8 105b4e49 Christos Stathis
 *   1. Redistributions of source code must retain the above
9 105b4e49 Christos Stathis
 *      copyright notice, this list of conditions and the following
10 105b4e49 Christos Stathis
 *      disclaimer.
11 105b4e49 Christos Stathis
 *
12 105b4e49 Christos Stathis
 *   2. Redistributions in binary form must reproduce the above
13 105b4e49 Christos Stathis
 *      copyright notice, this list of conditions and the following
14 105b4e49 Christos Stathis
 *      disclaimer in the documentation and/or other materials
15 105b4e49 Christos Stathis
 *      provided with the distribution.
16 105b4e49 Christos Stathis
 *
17 105b4e49 Christos Stathis
 * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
18 105b4e49 Christos Stathis
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 105b4e49 Christos Stathis
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 105b4e49 Christos Stathis
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
21 105b4e49 Christos Stathis
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 105b4e49 Christos Stathis
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 105b4e49 Christos Stathis
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
24 105b4e49 Christos Stathis
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25 105b4e49 Christos Stathis
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 105b4e49 Christos Stathis
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27 105b4e49 Christos Stathis
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 105b4e49 Christos Stathis
 * POSSIBILITY OF SUCH DAMAGE.
29 105b4e49 Christos Stathis
 *
30 105b4e49 Christos Stathis
 * The views and conclusions contained in the software and
31 105b4e49 Christos Stathis
 * documentation are those of the authors and should not be
32 105b4e49 Christos Stathis
 * interpreted as representing official policies, either expressed
33 105b4e49 Christos Stathis
 * or implied, of GRNET S.A.
34 105b4e49 Christos Stathis
 */
35 105b4e49 Christos Stathis
36 105b4e49 Christos Stathis
package gr.grnet.pithos.web.client.mysharedtree;
37 105b4e49 Christos Stathis
38 105b4e49 Christos Stathis
import gr.grnet.pithos.web.client.FolderContextMenu;
39 105b4e49 Christos Stathis
import gr.grnet.pithos.web.client.Pithos;
40 ff06bcd5 Christos Stathis
import gr.grnet.pithos.web.client.foldertree.File;
41 105b4e49 Christos Stathis
import gr.grnet.pithos.web.client.foldertree.Folder;
42 105b4e49 Christos Stathis
import gr.grnet.pithos.web.client.mysharedtree.MysharedTreeView.Templates;
43 105b4e49 Christos Stathis
import gr.grnet.pithos.web.client.rest.GetRequest;
44 105b4e49 Christos Stathis
import gr.grnet.pithos.web.client.rest.RestException;
45 105b4e49 Christos Stathis
46 105b4e49 Christos Stathis
import java.util.HashMap;
47 ff06bcd5 Christos Stathis
import java.util.HashSet;
48 105b4e49 Christos Stathis
import java.util.Iterator;
49 105b4e49 Christos Stathis
import java.util.List;
50 105b4e49 Christos Stathis
import java.util.Map;
51 ff06bcd5 Christos Stathis
import java.util.Set;
52 105b4e49 Christos Stathis
53 105b4e49 Christos Stathis
import com.google.gwt.cell.client.AbstractCell;
54 105b4e49 Christos Stathis
import com.google.gwt.cell.client.Cell;
55 105b4e49 Christos Stathis
import com.google.gwt.cell.client.TextCell;
56 105b4e49 Christos Stathis
import com.google.gwt.cell.client.ValueUpdater;
57 105b4e49 Christos Stathis
import com.google.gwt.core.client.GWT;
58 105b4e49 Christos Stathis
import com.google.gwt.core.client.Scheduler;
59 105b4e49 Christos Stathis
import com.google.gwt.event.dom.client.ContextMenuEvent;
60 9539e23d Christos Stathis
import com.google.gwt.http.client.Response;
61 f5023f13 Christos Stathis
import com.google.gwt.http.client.URL;
62 105b4e49 Christos Stathis
import com.google.gwt.safehtml.shared.SafeHtml;
63 105b4e49 Christos Stathis
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
64 105b4e49 Christos Stathis
import com.google.gwt.text.shared.SafeHtmlRenderer;
65 105b4e49 Christos Stathis
import com.google.gwt.user.client.Command;
66 105b4e49 Christos Stathis
import com.google.gwt.user.client.ui.AbstractImagePrototype;
67 105b4e49 Christos Stathis
import com.google.gwt.view.client.ListDataProvider;
68 105b4e49 Christos Stathis
import com.google.gwt.view.client.SelectionChangeEvent;
69 105b4e49 Christos Stathis
import com.google.gwt.view.client.SelectionChangeEvent.Handler;
70 105b4e49 Christos Stathis
import com.google.gwt.view.client.SingleSelectionModel;
71 105b4e49 Christos Stathis
import com.google.gwt.view.client.TreeViewModel;
72 105b4e49 Christos Stathis
73 105b4e49 Christos Stathis
public class MysharedTreeViewModel implements TreeViewModel {
74 105b4e49 Christos Stathis
75 105b4e49 Christos Stathis
    protected Pithos app;
76 105b4e49 Christos Stathis
77 f1b24b82 Christos Stathis
    private Cell<Folder> folderCell = new AbstractCell<Folder>() {
78 105b4e49 Christos Stathis
79 105b4e49 Christos Stathis
       @Override
80 7811b9d1 Christos Stathis
        public void render(@SuppressWarnings("unused") Context context, Folder folder, SafeHtmlBuilder safeHtmlBuilder) {
81 105b4e49 Christos Stathis
            String html = AbstractImagePrototype.create(MysharedTreeView.images.folderYellow()).getHTML();
82 a7b6e464 Christos Stathis
            safeHtmlBuilder.appendHtmlConstant(html).appendHtmlConstant("&nbsp;");
83 105b4e49 Christos Stathis
            safeHtmlBuilder.append(Templates.INSTANCE.nameSpan(folder.getName()));
84 105b4e49 Christos Stathis
        }
85 105b4e49 Christos Stathis
86 105b4e49 Christos Stathis
        @Override
87 7811b9d1 Christos Stathis
        public void onBrowserEvent(@SuppressWarnings("unused") Context context, @SuppressWarnings("unused") com.google.gwt.dom.client.Element parent, Folder folder, com.google.gwt.dom.client.NativeEvent event, @SuppressWarnings("unused") ValueUpdater<Folder> valueUpdater) {
88 105b4e49 Christos Stathis
            if (event.getType().equals(ContextMenuEvent.getType().getName())) {
89 105b4e49 Christos Stathis
                MysharedTreeViewModel.this.selectionModel.setSelected(folder, true);
90 02d3a335 Christos Stathis
                FolderContextMenu menu = new FolderContextMenu(app, MysharedTreeView.images, app.getSelectedTree(), folder);
91 105b4e49 Christos Stathis
                menu.setPopupPosition(event.getClientX(), event.getClientY());
92 105b4e49 Christos Stathis
                menu.show();
93 105b4e49 Christos Stathis
            }
94 105b4e49 Christos Stathis
        }
95 105b4e49 Christos Stathis
    };
96 105b4e49 Christos Stathis
97 cf49bbc9 Christos Stathis
    protected ListDataProvider<Folder> firstLevelDataProvider = new ListDataProvider<Folder>();
98 105b4e49 Christos Stathis
99 cf49bbc9 Christos Stathis
    private Map<Folder, ListDataProvider<Folder>> dataProviderMap = new HashMap<Folder, ListDataProvider<Folder>>();
100 ff06bcd5 Christos Stathis
    
101 ff06bcd5 Christos Stathis
    protected Set<File> sharedFiles = new HashSet<File>();
102 105b4e49 Christos Stathis
103 220831bd Christos Stathis
    protected SingleSelectionModel<Folder> selectionModel;
104 105b4e49 Christos Stathis
105 105b4e49 Christos Stathis
    public MysharedTreeViewModel(Pithos _app, SingleSelectionModel<Folder> selectionModel) {
106 105b4e49 Christos Stathis
        app = _app;
107 105b4e49 Christos Stathis
        this.selectionModel = selectionModel;
108 105b4e49 Christos Stathis
    }
109 105b4e49 Christos Stathis
110 105b4e49 Christos Stathis
    @Override
111 105b4e49 Christos Stathis
    public <T> NodeInfo<?> getNodeInfo(T value) {
112 105b4e49 Christos Stathis
        if (value == null) {
113 ff06bcd5 Christos Stathis
            ListDataProvider<String> rootDataProvider = new ListDataProvider<String>();
114 105b4e49 Christos Stathis
            rootDataProvider.getList().add("My Shared");
115 105b4e49 Christos Stathis
            final SingleSelectionModel<String> selectionModel2 = new SingleSelectionModel<String>();
116 105b4e49 Christos Stathis
            selectionModel2.addSelectionChangeHandler(new Handler() {
117 105b4e49 Christos Stathis
118 105b4e49 Christos Stathis
                @Override
119 7811b9d1 Christos Stathis
                public void onSelectionChange(@SuppressWarnings("unused") SelectionChangeEvent event) {
120 105b4e49 Christos Stathis
                    if (selectionModel2.getSelectedObject() != null) {
121 f1b24b82 Christos Stathis
                            app.deselectOthers(app.getMySharedTreeView(), selectionModel2);
122 3ebb88f4 Christos Stathis
                            app.applyPermissions(null);
123 3601b114 Christos Stathis
                            fetchSharedFiles(true);
124 105b4e49 Christos Stathis
                    }
125 105b4e49 Christos Stathis
                }
126 105b4e49 Christos Stathis
            });
127 105b4e49 Christos Stathis
            app.addSelectionModel(selectionModel2);
128 105b4e49 Christos Stathis
            return new DefaultNodeInfo<String>(rootDataProvider, new TextCell(new SafeHtmlRenderer<String>() {
129 105b4e49 Christos Stathis
                @Override
130 105b4e49 Christos Stathis
                public SafeHtml render(String object) {
131 105b4e49 Christos Stathis
                    SafeHtmlBuilder builder = new SafeHtmlBuilder();
132 105b4e49 Christos Stathis
                    render(object, builder);
133 105b4e49 Christos Stathis
                    return builder.toSafeHtml();
134 105b4e49 Christos Stathis
                }
135 105b4e49 Christos Stathis
136 105b4e49 Christos Stathis
                @Override
137 105b4e49 Christos Stathis
                public void render(String object, SafeHtmlBuilder builder) {
138 105b4e49 Christos Stathis
                    String html = AbstractImagePrototype.create(MysharedTreeView.images.myShared()).getHTML();
139 a7b6e464 Christos Stathis
                    builder.appendHtmlConstant(html).appendHtmlConstant("&nbsp;");
140 105b4e49 Christos Stathis
                    builder.append(MysharedTreeView.Templates.INSTANCE.nameSpan(object));
141 105b4e49 Christos Stathis
                }
142 105b4e49 Christos Stathis
            }),  selectionModel2, null);
143 105b4e49 Christos Stathis
        }
144 105b4e49 Christos Stathis
        else if (value instanceof String) {
145 cf49bbc9 Christos Stathis
                fetchSharedContainers(null);
146 105b4e49 Christos Stathis
            return new DefaultNodeInfo<Folder>(firstLevelDataProvider, folderCell, selectionModel, null);
147 105b4e49 Christos Stathis
        }
148 105b4e49 Christos Stathis
        else {
149 105b4e49 Christos Stathis
            final Folder f = (Folder) value;
150 105b4e49 Christos Stathis
            if (dataProviderMap.get(f) == null) {
151 cf49bbc9 Christos Stathis
                dataProviderMap.put(f, new ListDataProvider<Folder>());
152 105b4e49 Christos Stathis
            }
153 cf49bbc9 Christos Stathis
            final ListDataProvider<Folder> dataProvider = dataProviderMap.get(f);
154 cf49bbc9 Christos Stathis
            fetchFolder(f, dataProvider, false);
155 105b4e49 Christos Stathis
            return new DefaultNodeInfo<Folder>(dataProvider, folderCell, selectionModel, null);
156 105b4e49 Christos Stathis
        }
157 105b4e49 Christos Stathis
    }
158 105b4e49 Christos Stathis
159 3601b114 Christos Stathis
        protected void fetchSharedFiles(final boolean showFiles) {
160 3601b114 Christos Stathis
                sharedFiles.clear();
161 ff06bcd5 Christos Stathis
            final List<Folder> containers = app.getAccount().getContainers();
162 cf49bbc9 Christos Stathis
            final ListDataProvider<Folder> tempProvider = new ListDataProvider<Folder>();
163 ff06bcd5 Christos Stathis
            Iterator<Folder> iter = containers.iterator();
164 ff06bcd5 Christos Stathis
            fetchFolder(iter, tempProvider, new Command() {
165 ff06bcd5 Christos Stathis
                        
166 ff06bcd5 Christos Stathis
                        @Override
167 ff06bcd5 Christos Stathis
                        public void execute() {
168 cf49bbc9 Christos Stathis
                                firstLevelDataProvider.getList().clear();
169 cf49bbc9 Christos Stathis
                                firstLevelDataProvider.getList().addAll(tempProvider.getList());
170 3601b114 Christos Stathis
                                if (showFiles)
171 3601b114 Christos Stathis
                                        app.showFiles(sharedFiles);
172 ff06bcd5 Christos Stathis
                        }
173 ff06bcd5 Christos Stathis
                });
174 ff06bcd5 Christos Stathis
        }
175 105b4e49 Christos Stathis
176 7c54d867 Christos Stathis
        private void fetchSharedContainers(final Command callback) {
177 ff06bcd5 Christos Stathis
            final List<Folder> containers = app.getAccount().getContainers();
178 cf49bbc9 Christos Stathis
            final ListDataProvider<Folder> tempProvider = new ListDataProvider<Folder>();
179 ff06bcd5 Christos Stathis
            Iterator<Folder> iter = containers.iterator();
180 ff06bcd5 Christos Stathis
            fetchFolder(iter, tempProvider, new Command() {
181 ff06bcd5 Christos Stathis
                        
182 ff06bcd5 Christos Stathis
                        @Override
183 ff06bcd5 Christos Stathis
                        public void execute() {
184 cf49bbc9 Christos Stathis
                                firstLevelDataProvider.getList().clear();
185 cf49bbc9 Christos Stathis
                                firstLevelDataProvider.getList().addAll(tempProvider.getList());
186 7c54d867 Christos Stathis
                                if (callback != null)
187 7c54d867 Christos Stathis
                                        callback.execute();
188 ff06bcd5 Christos Stathis
                        }
189 ff06bcd5 Christos Stathis
                });
190 105b4e49 Christos Stathis
        }
191 105b4e49 Christos Stathis
192 105b4e49 Christos Stathis
        @Override
193 105b4e49 Christos Stathis
    public boolean isLeaf(Object o) {
194 7c54d867 Christos Stathis
                if (o == null)
195 7c54d867 Christos Stathis
                        return false;
196 7c54d867 Christos Stathis
                else if (o instanceof Folder) {
197 105b4e49 Christos Stathis
            Folder f = (Folder) o;
198 105b4e49 Christos Stathis
            return f.getSubfolders().isEmpty();
199 105b4e49 Christos Stathis
        }
200 7c54d867 Christos Stathis
                else {
201 cf49bbc9 Christos Stathis
                        return firstLevelDataProvider.getList().isEmpty();
202 7c54d867 Christos Stathis
                }
203 105b4e49 Christos Stathis
    }
204 105b4e49 Christos Stathis
205 cf49bbc9 Christos Stathis
    protected void fetchFolder(final Iterator<Folder> iter, final ListDataProvider<Folder> dataProvider, final Command callback) {
206 105b4e49 Christos Stathis
        if (iter.hasNext()) {
207 105b4e49 Christos Stathis
            final Folder f = iter.next();
208 105b4e49 Christos Stathis
209 f5023f13 Christos Stathis
            String path = "/" + f.getContainer() + "?format=json&delimiter=/&prefix=" + URL.encodeQueryString(f.getPrefix());
210 63ee965c Christos Stathis
            GetRequest<Folder> getFolder = new GetRequest<Folder>(Folder.class, app.getApiPath(), f.getOwner(), path, f) {
211 105b4e49 Christos Stathis
                @Override
212 7811b9d1 Christos Stathis
                public void onSuccess(Folder _result) {
213 7811b9d1 Christos Stathis
                        if (!_result.isShared()) {
214 7811b9d1 Christos Stathis
                                for (File file : _result.getFiles()) {
215 ff06bcd5 Christos Stathis
                                        if (file.isShared())
216 ff06bcd5 Christos Stathis
                                                sharedFiles.add(file);
217 ff06bcd5 Christos Stathis
                                }
218 7811b9d1 Christos Stathis
                                Iterator<Folder> iter2 = _result.getSubfolders().iterator();
219 ff06bcd5 Christos Stathis
                                fetchFolder(iter2, dataProvider, new Command() {
220 ff06bcd5 Christos Stathis
                                                        
221 ff06bcd5 Christos Stathis
                                                        @Override
222 ff06bcd5 Christos Stathis
                                                        public void execute() {
223 ff06bcd5 Christos Stathis
                                            fetchFolder(iter, dataProvider, callback);
224 ff06bcd5 Christos Stathis
                                                        }
225 ff06bcd5 Christos Stathis
                                                });
226 ff06bcd5 Christos Stathis
                        }
227 ff06bcd5 Christos Stathis
                        else {
228 cf49bbc9 Christos Stathis
                                dataProvider.getList().add(_result);
229 ff06bcd5 Christos Stathis
                            fetchFolder(iter, dataProvider, callback);
230 ff06bcd5 Christos Stathis
                        }
231 105b4e49 Christos Stathis
                }
232 105b4e49 Christos Stathis
233 105b4e49 Christos Stathis
                @Override
234 105b4e49 Christos Stathis
                public void onError(Throwable t) {
235 105b4e49 Christos Stathis
                    GWT.log("Error getting folder", t);
236 3f8622d4 Christos Stathis
                                        app.setError(t);
237 105b4e49 Christos Stathis
                    if (t instanceof RestException)
238 105b4e49 Christos Stathis
                        app.displayError("Error getting folder: " + ((RestException) t).getHttpStatusText());
239 105b4e49 Christos Stathis
                    else
240 105b4e49 Christos Stathis
                        app.displayError("System error fetching folder: " + t.getMessage());
241 105b4e49 Christos Stathis
                }
242 9539e23d Christos Stathis
243 9539e23d Christos Stathis
                                @Override
244 9539e23d Christos Stathis
                                protected void onUnauthorized(Response response) {
245 9539e23d Christos Stathis
                                        app.sessionExpired();
246 9539e23d Christos Stathis
                                }
247 105b4e49 Christos Stathis
            };
248 105b4e49 Christos Stathis
            getFolder.setHeader("X-Auth-Token", app.getToken());
249 105b4e49 Christos Stathis
            Scheduler.get().scheduleDeferred(getFolder);
250 105b4e49 Christos Stathis
        }
251 105b4e49 Christos Stathis
        else if (callback != null)
252 105b4e49 Christos Stathis
            callback.execute();
253 105b4e49 Christos Stathis
    }
254 105b4e49 Christos Stathis
255 105b4e49 Christos Stathis
    public Folder getSelection() {
256 105b4e49 Christos Stathis
        return selectionModel.getSelectedObject();
257 105b4e49 Christos Stathis
    }
258 105b4e49 Christos Stathis
259 cf49bbc9 Christos Stathis
    public void updateFolder(Folder folder, boolean showfiles) {
260 105b4e49 Christos Stathis
        if (dataProviderMap.get(folder) == null) {
261 cf49bbc9 Christos Stathis
            dataProviderMap.put(folder, new ListDataProvider<Folder>());
262 105b4e49 Christos Stathis
        }
263 cf49bbc9 Christos Stathis
        final ListDataProvider<Folder> dataProvider = dataProviderMap.get(folder);
264 105b4e49 Christos Stathis
        fetchFolder(folder, dataProvider, showfiles);
265 105b4e49 Christos Stathis
    }
266 105b4e49 Christos Stathis
267 cf49bbc9 Christos Stathis
    public void fetchFolder(final Folder f, final ListDataProvider<Folder> dataProvider, final boolean showfiles) {
268 f5023f13 Christos Stathis
        String path = "/" + f.getContainer() + "?format=json&delimiter=/&prefix=" + URL.encodeQueryString(f.getPrefix());
269 63ee965c Christos Stathis
        GetRequest<Folder> getFolder = new GetRequest<Folder>(Folder.class, app.getApiPath(), f.getOwner(), path, f) {
270 105b4e49 Christos Stathis
            @Override
271 7811b9d1 Christos Stathis
            public void onSuccess(final Folder _result) {
272 105b4e49 Christos Stathis
                if (showfiles)
273 7811b9d1 Christos Stathis
                    app.showFiles(_result);
274 7811b9d1 Christos Stathis
                Iterator<Folder> iter = _result.getSubfolders().iterator();
275 cf49bbc9 Christos Stathis
                fetchFolder(iter, dataProvider, new Command() {
276 105b4e49 Christos Stathis
                    @Override
277 105b4e49 Christos Stathis
                    public void execute() {
278 cf49bbc9 Christos Stathis
                        dataProvider.getList().clear();
279 cf49bbc9 Christos Stathis
                                   dataProvider.getList().addAll(_result.getSubfolders());
280 105b4e49 Christos Stathis
                        app.getMySharedTreeView().updateChildren(f);
281 105b4e49 Christos Stathis
                    }
282 105b4e49 Christos Stathis
                });
283 105b4e49 Christos Stathis
            }
284 105b4e49 Christos Stathis
285 105b4e49 Christos Stathis
            @Override
286 105b4e49 Christos Stathis
            public void onError(Throwable t) {
287 105b4e49 Christos Stathis
                GWT.log("Error getting folder", t);
288 3f8622d4 Christos Stathis
                                app.setError(t);
289 105b4e49 Christos Stathis
                if (t instanceof RestException)
290 105b4e49 Christos Stathis
                    app.displayError("Error getting folder: " + ((RestException) t).getHttpStatusText());
291 105b4e49 Christos Stathis
                else
292 105b4e49 Christos Stathis
                    app.displayError("System error fetching folder: " + t.getMessage());
293 105b4e49 Christos Stathis
            }
294 9539e23d Christos Stathis
295 9539e23d Christos Stathis
                        @Override
296 9539e23d Christos Stathis
                        protected void onUnauthorized(Response response) {
297 9539e23d Christos Stathis
                                app.sessionExpired();
298 9539e23d Christos Stathis
                        }
299 105b4e49 Christos Stathis
        };
300 105b4e49 Christos Stathis
        getFolder.setHeader("X-Auth-Token", app.getToken());
301 105b4e49 Christos Stathis
        Scheduler.get().scheduleDeferred(getFolder);
302 105b4e49 Christos Stathis
    }
303 7c54d867 Christos Stathis
304 7c54d867 Christos Stathis
        public void initialize(Command callback) {
305 7c54d867 Christos Stathis
                fetchSharedContainers(callback);
306 7c54d867 Christos Stathis
        }
307 105b4e49 Christos Stathis
}