001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH & Co. KG, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.gwt.client;
029
030import org.opencms.db.CmsResourceState;
031import org.opencms.gwt.client.rpc.CmsRpcAction;
032import org.opencms.gwt.client.rpc.CmsRpcPrefetcher;
033import org.opencms.gwt.client.ui.CmsErrorDialog;
034import org.opencms.gwt.client.ui.CmsNotification;
035import org.opencms.gwt.client.ui.input.upload.CmsFileInfo;
036import org.opencms.gwt.client.util.CmsJsUtil;
037import org.opencms.gwt.client.util.CmsMediaQuery;
038import org.opencms.gwt.client.util.CmsUniqueActiveItemContainer;
039import org.opencms.gwt.client.util.I_CmsSimpleCallback;
040import org.opencms.gwt.shared.CmsCoreData;
041import org.opencms.gwt.shared.CmsGwtConstants;
042import org.opencms.gwt.shared.I_CmsAutoBeanFactory;
043import org.opencms.gwt.shared.rpc.I_CmsCoreService;
044import org.opencms.gwt.shared.rpc.I_CmsCoreServiceAsync;
045import org.opencms.gwt.shared.rpc.I_CmsVfsService;
046import org.opencms.gwt.shared.rpc.I_CmsVfsServiceAsync;
047import org.opencms.util.CmsStringUtil;
048import org.opencms.util.CmsUUID;
049
050import com.google.gwt.core.client.GWT;
051import com.google.gwt.dom.client.Document;
052import com.google.gwt.dom.client.Element;
053import com.google.gwt.dom.client.NodeList;
054import com.google.gwt.user.client.Window.Location;
055import com.google.gwt.user.client.rpc.AsyncCallback;
056import com.google.gwt.user.client.rpc.SerializationException;
057import com.google.gwt.user.client.rpc.ServiceDefTarget;
058import com.google.web.bindery.event.shared.Event;
059import com.google.web.bindery.event.shared.EventBus;
060import com.google.web.bindery.event.shared.SimpleEventBus;
061
062import elemental2.dom.DomGlobal;
063import elemental2.webstorage.WebStorageWindow;
064
065/**
066 * Client side core data provider.<p>
067 *
068 * @since 8.0.0
069 *
070 * @see org.opencms.gwt.CmsGwtActionElement
071 */
072public final class CmsCoreProvider extends CmsCoreData {
073
074    /** AutoBean factory instance. */
075    public static final I_CmsAutoBeanFactory AUTO_BEAN_FACTORY = GWT.create(I_CmsAutoBeanFactory.class);
076
077    /** Path to system folder. */
078    public static final String VFS_PATH_SYSTEM = "/system/";
079
080    /** Media query do detect device with no hover capability. */
081    private static final CmsMediaQuery TOUCH_ONLY = CmsMediaQuery.parse("(hover: none)");
082
083    /** Internal instance. */
084    private static CmsCoreProvider INSTANCE;
085
086    /** The core service instance. */
087    private static I_CmsCoreServiceAsync SERVICE;
088
089    /** The vfs-service instance. */
090    private static I_CmsVfsServiceAsync VFS_SERVICE;
091
092    /** The unique active item container for the flyout menu. */
093    private CmsUniqueActiveItemContainer m_activeFlyoutMenu = new CmsUniqueActiveItemContainer();
094
095    /** The client time when the data is loaded. */
096    private long m_clientTime;
097
098    /** Event bus for client side events. */
099    private EventBus m_eventBus = new SimpleEventBus();
100
101    /** Flag which indicates whether we are in Internet Explorer 7. */
102    private boolean m_isIe7;
103
104    /**
105     * Prevent instantiation.<p>
106     *
107     * @throws SerializationException if deserialization failed
108     */
109    protected CmsCoreProvider()
110    throws SerializationException {
111
112        super((CmsCoreData)CmsRpcPrefetcher.getSerializedObjectFromDictionary(getService(), DICT_NAME));
113        m_clientTime = System.currentTimeMillis();
114
115        I_CmsUserAgentInfo userAgentInfo = GWT.create(I_CmsUserAgentInfo.class);
116        m_isIe7 = userAgentInfo.isIE7();
117    }
118
119    /**
120     * Returns the client message instance.<p>
121     *
122     * @return the client message instance
123     */
124    public static CmsCoreProvider get() {
125
126        if (INSTANCE == null) {
127            try {
128                INSTANCE = new CmsCoreProvider();
129            } catch (SerializationException e) {
130                CmsErrorDialog.handleException(
131                    new Exception(
132                        "Deserialization of core data failed. This may be caused by expired java-script resources, please clear your browser cache and try again.",
133                        e));
134            }
135        }
136        return INSTANCE;
137    }
138
139    /**
140     * Gets the content attribute of a meta tag with a given name.<p>
141     *
142     * @param nameToFind the name of the meta tag
143     *
144     * @return the content attribute value of the found meta tag, or null if no meta tag with the given name was found
145     */
146    public static String getMetaElementContent(String nameToFind) {
147
148        NodeList<Element> metas = Document.get().getDocumentElement().getElementsByTagName("meta");
149        for (int i = 0; i < metas.getLength(); i++) {
150            Element meta = metas.getItem(i);
151            String name = meta.getAttribute("name");
152            if (nameToFind.equals(name)) {
153                return meta.getAttribute("content");
154            }
155        }
156        return null;
157    }
158
159    /**
160     * Returns the core service instance.<p>
161     *
162     * @return the core service instance
163     */
164    public static I_CmsCoreServiceAsync getService() {
165
166        if (SERVICE == null) {
167            SERVICE = GWT.create(I_CmsCoreService.class);
168            String serviceUrl = CmsCoreProvider.get().link("org.opencms.gwt.CmsCoreService.gwt");
169            ((ServiceDefTarget)SERVICE).setServiceEntryPoint(serviceUrl);
170        }
171        return SERVICE;
172    }
173
174    /**
175     * Returns the vfs service instance.<p>
176     *
177     * @return the vfs service instance
178     */
179    public static I_CmsVfsServiceAsync getVfsService() {
180
181        if (VFS_SERVICE == null) {
182            VFS_SERVICE = GWT.create(I_CmsVfsService.class);
183            String serviceUrl = CmsCoreProvider.get().link("org.opencms.gwt.CmsVfsService.gwt");
184            ((ServiceDefTarget)VFS_SERVICE).setServiceEntryPoint(serviceUrl);
185        }
186        return VFS_SERVICE;
187    }
188
189    /**
190     * Checks if the client is touch-only.
191     *
192     * <p>This uses media queries, but the touch-only status can also be forcibly turned on with the request parameter __touchOnly=1.
193     *
194     * @return true if the client is touch-only
195     */
196    public static boolean isTouchOnly() {
197
198        return TOUCH_ONLY.matches()
199            || "1".equals(Location.getParameter("__touchOnly"))
200            || "1".equals(CmsJsUtil.getLocalStorage("__touchOnly"));
201    }
202
203    /**
204     * Adds the current site root of this context to the given resource name.<p>
205     *
206     * @param sitePath the resource name
207     *
208     * @return the translated resource name including site root
209     *
210     * @see #removeSiteRoot(String)
211     */
212    public String addSiteRoot(String sitePath) {
213
214        if (sitePath == null) {
215            return null;
216        }
217        String siteRoot = getAdjustedSiteRoot(getSiteRoot(), sitePath);
218        StringBuffer result = new StringBuffer(128);
219        result.append(siteRoot);
220        if (((siteRoot.length() == 0) || (siteRoot.charAt(siteRoot.length() - 1) != '/'))
221            && ((sitePath.length() == 0) || (sitePath.charAt(0) != '/'))) {
222            // add slash between site root and resource if required
223            result.append('/');
224        }
225        result.append(sitePath);
226        return result.toString();
227    }
228
229    /**
230     * Creates a new CmsUUID.<p>
231     *
232     * @param callback the callback to execute
233     */
234    public void createUUID(final AsyncCallback<CmsUUID> callback) {
235
236        // do not stop/start since we do not want to give any feedback to the user
237        CmsRpcAction<CmsUUID> action = new CmsRpcAction<CmsUUID>() {
238
239            /**
240             * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute()
241             */
242            @Override
243            public void execute() {
244
245                getService().createUUID(this);
246            }
247
248            /**
249             * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object)
250             */
251            @Override
252            protected void onResponse(CmsUUID result) {
253
254                callback.onSuccess(result);
255            }
256        };
257        action.execute();
258    }
259
260    /**
261     * Fires a client side event.<p>
262     *
263     * @param event the event to fire
264     */
265    public void fireEvent(Event<?> event) {
266
267        m_eventBus.fireEvent(event);
268    }
269
270    /**
271     * Returns the adjusted site root for a resource using the provided site root as a base.<p>
272     *
273     * Usually, this would be the site root for the current site.
274     * However, if a resource from the <code>/system/</code> folder is requested,
275     * this will be the empty String.<p>
276     *
277     * @param siteRoot the site root of the current site
278     * @param resourcename the resource name to get the adjusted site root for
279     *
280     * @return the adjusted site root for the resource
281     */
282    public String getAdjustedSiteRoot(String siteRoot, String resourcename) {
283
284        if (resourcename.startsWith(VFS_PATH_SYSTEM) || resourcename.startsWith(getSharedFolder())) {
285            return "";
286        } else {
287            return siteRoot;
288        }
289    }
290
291    /**
292     * Returns the approximate time on the server.<p>
293     *
294     * @return the approximate server time
295     */
296    public long getEstimatedServerTime() {
297
298        return m_clientTime + (System.currentTimeMillis() - m_clientTime);
299    }
300
301    /**
302     * Gets the core event bus.<p>
303     *
304     * @return the core event bus
305     */
306    public EventBus getEventBus() {
307
308        return m_eventBus;
309    }
310
311    /**
312     * Returns the link to view the given resource in the file explorer.<p>
313     *
314     * @param sitePath the resource site path
315     *
316     * @return the link
317     */
318    public String getExplorerLink(String sitePath) {
319
320        return getFileExplorerLink() + sitePath;
321    }
322
323    /**
324     * Gets the unique active item container which holds a reference to the currently active content element flyout menu.<p>
325     *
326     * @return the unique active item container for flyout menus
327     */
328    public CmsUniqueActiveItemContainer getFlyoutMenuContainer() {
329
330        return m_activeFlyoutMenu;
331
332    }
333
334    /**
335     * Gets the page id that was last stored in sessionStorage.
336     *
337     * @return the page id that was last stored in session storage
338     */
339    public CmsUUID getLastPageId() {
340
341        WebStorageWindow window = WebStorageWindow.of(DomGlobal.window);
342        String lastPageStr = window.sessionStorage.getItem(CmsGwtConstants.LAST_CONTAINER_PAGE_ID);
343        if (lastPageStr != null) {
344            return new CmsUUID(lastPageStr);
345        } else {
346            return null;
347        }
348    }
349
350    /**
351     * Fetches the state of a resource from the server.<p>
352     *
353     * @param structureId the structure id of the resource
354     * @param callback the callback which should receive the result
355     */
356    public void getResourceState(final CmsUUID structureId, final AsyncCallback<CmsResourceState> callback) {
357
358        CmsRpcAction<CmsResourceState> action = new CmsRpcAction<CmsResourceState>() {
359
360            /**
361             * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute()
362             */
363            @Override
364            public void execute() {
365
366                start(0, false);
367                getService().getResourceState(structureId, this);
368            }
369
370            /**
371             * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object)
372             */
373            @Override
374            protected void onResponse(CmsResourceState result) {
375
376                stop(false);
377                callback.onSuccess(result);
378            }
379        };
380        action.execute();
381    }
382
383    /**
384     * Returns the resource type name for a given filename.<p>
385     *
386     * @param file the file info
387     *
388     * @return the resource type name
389     */
390    public String getResourceType(CmsFileInfo file) {
391
392        String typeName = null;
393        typeName = getExtensionMapping().get(file.getFileSuffix().toLowerCase());
394        if (typeName == null) {
395            typeName = "plain";
396        }
397        return typeName;
398    }
399
400    /**
401     * Returns the resource type name for a given filename.<p>
402     *
403     * @param file the file info
404     *
405     * @return the resource type name
406     */
407    public String getResourceTypeIcon(CmsFileInfo file) {
408
409        String typeName = null;
410        typeName = getIconMapping().get(file.getFileSuffix().toLowerCase());
411        if (typeName == null) {
412            typeName = getIconMapping().get("");
413        }
414        return typeName;
415    }
416
417    /**
418     * Returns if the current user agent is IE7.<p>
419     *
420     * @return <code>true</code> if the current user agent is IE7
421     */
422    public boolean isIe7() {
423
424        return m_isIe7;
425    }
426
427    /**
428     * Returns an absolute link given a site path.<p>
429     *
430     * @param sitePath the site path
431     *
432     * @return the absolute link
433     */
434    public String link(String sitePath) {
435
436        return CmsStringUtil.joinPaths(getVfsPrefix(), sitePath);
437    }
438
439    /**
440     * Locks the given resource with a temporary lock.<p>
441     *
442     * @param structureId the resource structure id
443     * @param callback the callback to execute
444     */
445    public void lock(final CmsUUID structureId, final I_CmsSimpleCallback<Boolean> callback) {
446
447        CmsRpcAction<String> lockAction = new CmsRpcAction<String>() {
448
449            /**
450            * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute()
451            */
452            @Override
453            public void execute() {
454
455                start(200, false);
456                getService().lockTemp(structureId, this);
457            }
458
459            /**
460            * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object)
461            */
462            @Override
463            public void onResponse(String result) {
464
465                stop(false);
466                if (result != null) {
467                    // unable to lock
468                    String text = Messages.get().key(Messages.GUI_LOCK_NOTIFICATION_2, structureId, result);
469                    CmsNotification.get().sendDeferred(CmsNotification.Type.WARNING, text);
470                }
471                callback.execute(result == null ? Boolean.TRUE : Boolean.FALSE);
472            }
473        };
474        lockAction.execute();
475    }
476
477    /**
478     * Locks the given resource with a temporary lock.<p>
479     *
480     * @param structureId the resource structure id
481     * @param loadTime the time when the requested resource was loaded
482     * @param callback the callback to execute
483     */
484    public void lock(final CmsUUID structureId, long loadTime, final I_CmsSimpleCallback<Boolean> callback) {
485
486        CmsRpcAction<String> lockAction = new CmsRpcAction<String>() {
487
488            /**
489            * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute()
490            */
491            @Override
492            public void execute() {
493
494                start(200, false);
495                getService().lockTemp(structureId, loadTime, this);
496            }
497
498            /**
499            * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object)
500            */
501            @Override
502            public void onResponse(String result) {
503
504                stop(false);
505                if (result != null) {
506                    // unable to lock
507                    String text = Messages.get().key(Messages.GUI_LOCK_NOTIFICATION_2, structureId, result);
508                    CmsNotification.get().sendDeferred(CmsNotification.Type.WARNING, text);
509                }
510                callback.execute(result == null ? Boolean.TRUE : Boolean.FALSE);
511            }
512        };
513        lockAction.execute();
514    }
515
516    /**
517     * Locks the given resource with a temporary lock.<p>
518     *
519     * @param sitePath the site path of the resource to lock
520     * @param loadTime the time when the requested resource was loaded
521     * @param callback the callback to execute
522     */
523    public void lock(final String sitePath, long loadTime, final I_CmsSimpleCallback<Boolean> callback) {
524
525        CmsRpcAction<String> lockAction = new CmsRpcAction<String>() {
526
527            /**
528            * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute()
529            */
530            @Override
531            public void execute() {
532
533                start(200, false);
534                getService().lockIfExists(sitePath, loadTime, this);
535            }
536
537            /**
538            * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object)
539            */
540            @Override
541            public void onResponse(String result) {
542
543                stop(false);
544                if (result != null) {
545                    // unable to lock
546                    String text = Messages.get().key(Messages.GUI_LOCK_NOTIFICATION_2, sitePath, result);
547                    CmsNotification.get().sendDeferred(CmsNotification.Type.WARNING, text);
548                }
549                callback.execute(result == null ? Boolean.TRUE : Boolean.FALSE);
550            }
551        };
552        lockAction.execute();
553    }
554
555    /**
556     * Tries to lock a resource with a given structure id and returns an error if the locking fails.<p>
557     *
558     * @param structureId the structure id of the resource to lock
559     * @param callback the callback to execute
560     */
561    public void lockOrReturnError(final CmsUUID structureId, final I_CmsSimpleCallback<String> callback) {
562
563        CmsRpcAction<String> lockAction = new CmsRpcAction<String>() {
564
565            /**
566            * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute()
567            */
568            @Override
569            public void execute() {
570
571                start(200, false);
572                getService().lockTemp(structureId, this);
573            }
574
575            /**
576            * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object)
577            */
578            @Override
579            public void onResponse(String result) {
580
581                stop(false);
582                if (result != null) {
583                    // unable to lock
584                    final String text = Messages.get().key(Messages.GUI_LOCK_NOTIFICATION_2, structureId, result);
585                    CmsNotification.get().sendDeferred(CmsNotification.Type.WARNING, text);
586                }
587                callback.execute(result);
588            }
589        };
590        lockAction.execute();
591    }
592
593    /**
594     * Tries to lock a resource with a given structure id and returns an error if the locking fails.<p>
595     *
596     * @param structureId the structure id of the resource to lock
597     * @param loadTime the time when the requested resource was loaded
598     * @param callback the callback to execute
599     */
600    public void lockOrReturnError(
601        final CmsUUID structureId,
602        final long loadTime,
603        final I_CmsSimpleCallback<String> callback) {
604
605        CmsRpcAction<String> lockAction = new CmsRpcAction<String>() {
606
607            /**
608            * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute()
609            */
610            @Override
611            public void execute() {
612
613                start(200, false);
614                getService().lockTemp(structureId, loadTime, this);
615            }
616
617            /**
618            * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object)
619            */
620            @Override
621            public void onResponse(String result) {
622
623                stop(false);
624                if (result != null) {
625                    // unable to lock
626                    final String text = Messages.get().key(Messages.GUI_LOCK_NOTIFICATION_2, structureId, result);
627                    CmsNotification.get().sendDeferred(CmsNotification.Type.WARNING, text);
628                }
629                callback.execute(result);
630            }
631        };
632        lockAction.execute();
633    }
634
635    /**
636     * Tries to lock a resource with a given site path and returns an error if the locking fails.<p>
637     * If the resource does not exist yet, the next existing ancestor folder will be checked if it is lockable.<p>
638     *
639     * @param sitePath the site path of the resource to lock
640     * @param callback the callback to execute
641     */
642    public void lockOrReturnError(final String sitePath, final I_CmsSimpleCallback<String> callback) {
643
644        CmsRpcAction<String> lockAction = new CmsRpcAction<String>() {
645
646            /**
647            * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute()
648            */
649            @Override
650            public void execute() {
651
652                start(200, false);
653                getService().lockIfExists(sitePath, this);
654            }
655
656            /**
657            * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object)
658            */
659            @Override
660            public void onResponse(String result) {
661
662                stop(false);
663                if (result != null) {
664                    // unable to lock
665                    final String text = Messages.get().key(Messages.GUI_LOCK_NOTIFICATION_2, sitePath, result);
666                    CmsNotification.get().sendDeferred(CmsNotification.Type.WARNING, text);
667                }
668                callback.execute(result);
669            }
670        };
671        lockAction.execute();
672    }
673
674    /**
675     * Tries to lock a resource with a given site path and returns an error if the locking fails.<p>
676     * If the resource does not exist yet, the next existing ancestor folder will be checked if it is lockable.<p>
677     *
678     * @param sitePath the site path of the resource to lock
679     * @param loadTime the time when the requested resource was loaded
680     * @param callback the callback to execute
681     */
682    public void lockOrReturnError(
683        final String sitePath,
684        final long loadTime,
685        final I_CmsSimpleCallback<String> callback) {
686
687        CmsRpcAction<String> lockAction = new CmsRpcAction<String>() {
688
689            /**
690            * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute()
691            */
692            @Override
693            public void execute() {
694
695                start(200, false);
696                getService().lockIfExists(sitePath, loadTime, this);
697            }
698
699            /**
700            * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object)
701            */
702            @Override
703            public void onResponse(String result) {
704
705                stop(false);
706                if (result != null) {
707                    // unable to lock
708                    final String text = Messages.get().key(Messages.GUI_LOCK_NOTIFICATION_2, sitePath, result);
709                    CmsNotification.get().sendDeferred(CmsNotification.Type.WARNING, text);
710                }
711                callback.execute(result);
712            }
713        };
714        lockAction.execute();
715    }
716
717    /**
718     * Removes the current site root prefix from the given root path,
719     * that is adjusts the resource name for the current site root.<p>
720     *
721     * If the resource name does not start with the current site root,
722     * it is left untouched.<p>
723     *
724     * @param rootPath the resource name
725     *
726     * @return the resource name adjusted for the current site root
727     *
728     * @see #addSiteRoot(String)
729     */
730    public String removeSiteRoot(String rootPath) {
731
732        String siteRoot = getAdjustedSiteRoot(getSiteRoot(), rootPath);
733        if ((siteRoot != null)
734            && (siteRoot.equals(getSiteRoot()))
735            && rootPath.startsWith(siteRoot)
736            && ((rootPath.length() == siteRoot.length()) || (rootPath.charAt(siteRoot.length()) == '/'))) {
737            rootPath = rootPath.substring(siteRoot.length());
738        }
739        return rootPath;
740    }
741
742    /**
743     * @see org.opencms.gwt.shared.CmsCoreData#setShowEditorHelp(boolean)
744     */
745    @Override
746    public void setShowEditorHelp(final boolean show) {
747
748        super.setShowEditorHelp(show);
749        CmsRpcAction<Void> action = new CmsRpcAction<Void>() {
750
751            /**
752             * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute()
753             */
754            @Override
755            public void execute() {
756
757                getService().setShowEditorHelp(show, this);
758            }
759
760            /**
761             * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object)
762             */
763            @Override
764            protected void onResponse(Void result) {
765
766                //nothing to do
767            }
768        };
769        action.execute();
770    }
771
772    /**
773     * Returns the absolute link to the given root path.<p>
774     *
775     * @param rootPath the root path
776     * @param callback the callback to execute
777     */
778    public void substituteLinkForRootPath(final String rootPath, final I_CmsSimpleCallback<String> callback) {
779
780        CmsRpcAction<String> action = new CmsRpcAction<String>() {
781
782            @Override
783            public void execute() {
784
785                getVfsService().substituteLinkForRootPath(getSiteRoot(), rootPath, this);
786            }
787
788            @Override
789            protected void onResponse(String result) {
790
791                callback.execute(result);
792            }
793        };
794        action.execute();
795    }
796
797    /**
798     * Unlocks the given resource, synchronously.<p>
799     *
800     * @param structureId the resource structure id
801     *
802     * @return <code>true</code> if succeeded, if not a a warning is already shown to the user
803     */
804    public boolean unlock(final CmsUUID structureId) {
805
806        // lock the sitemap
807        CmsRpcAction<String> unlockAction = new CmsRpcAction<String>() {
808
809            /**
810            * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute()
811            */
812            @Override
813            public void execute() {
814
815                start(200, false);
816                getService().unlock(structureId, this);
817            }
818
819            /**
820            * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object)
821            */
822            @Override
823            public void onResponse(String result) {
824
825                stop(false);
826                if (result == null) {
827                    // ok
828                    return;
829                }
830                // unable to lock
831                String text = Messages.get().key(Messages.GUI_UNLOCK_NOTIFICATION_2, structureId.toString(), result);
832                CmsNotification.get().send(CmsNotification.Type.WARNING, text);
833            }
834        };
835        return unlockAction.executeSync() == null;
836    }
837
838    /**
839     * Unlocks the given resource, synchronously.<p>
840     *
841     * @param sitePath the resource site path
842     *
843     * @return <code>true</code> if succeeded, if not a a warning is already shown to the user
844     */
845    public boolean unlock(final String sitePath) {
846
847        // lock the sitemap
848        CmsRpcAction<String> unlockAction = new CmsRpcAction<String>() {
849
850            /**
851            * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute()
852            */
853            @Override
854            public void execute() {
855
856                start(200, false);
857                getService().unlock(sitePath, this);
858            }
859
860            /**
861            * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object)
862            */
863            @Override
864            public void onResponse(String result) {
865
866                stop(false);
867                if (result == null) {
868                    // ok
869                    return;
870                }
871                // unable to lock
872                String text = Messages.get().key(Messages.GUI_UNLOCK_NOTIFICATION_2, sitePath, result);
873                CmsNotification.get().send(CmsNotification.Type.WARNING, text);
874            }
875        };
876        return unlockAction.executeSync() == null;
877    }
878
879}