001/*
002 * (C) Copyright 2009-2016 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Radu Darlea
018 *     Catalin Baican
019 *     Florent Guillaume
020 */
021
022package org.nuxeo.ecm.platform.tag;
023
024import java.io.Serializable;
025import java.util.ArrayList;
026import java.util.Calendar;
027import java.util.Collections;
028import java.util.HashMap;
029import java.util.HashSet;
030import java.util.List;
031import java.util.Map;
032import java.util.Set;
033
034import org.nuxeo.ecm.core.api.CoreSession;
035import org.nuxeo.ecm.core.api.DocumentModel;
036import org.nuxeo.ecm.core.api.DocumentRef;
037import org.nuxeo.ecm.core.api.DocumentSecurityException;
038import org.nuxeo.ecm.core.api.IdRef;
039import org.nuxeo.ecm.core.api.IterableQueryResult;
040import org.nuxeo.ecm.core.api.NuxeoException;
041import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner;
042import org.nuxeo.ecm.core.api.event.DocumentEventTypes;
043import org.nuxeo.ecm.core.api.security.SecurityConstants;
044import org.nuxeo.ecm.core.event.Event;
045import org.nuxeo.ecm.core.event.EventService;
046import org.nuxeo.ecm.core.event.impl.DocumentEventContext;
047import org.nuxeo.ecm.core.query.sql.NXQL;
048import org.nuxeo.ecm.platform.query.api.PageProvider;
049import org.nuxeo.ecm.platform.query.api.PageProviderDefinition;
050import org.nuxeo.ecm.platform.query.api.PageProviderService;
051import org.nuxeo.ecm.platform.query.nxql.CoreQueryAndFetchPageProvider;
052import org.nuxeo.runtime.api.Framework;
053import org.nuxeo.runtime.model.DefaultComponent;
054
055/**
056 * The implementation of the tag service.
057 */
058public class TagServiceImpl extends DefaultComponent implements TagService {
059
060    public static final String NXTAG = TagQueryMaker.NXTAG;
061
062    protected enum PAGE_PROVIDERS {
063        //
064        GET_DOCUMENT_IDS_FOR_TAG,
065        //
066        GET_FIRST_TAGGING_FOR_DOC_AND_TAG_AND_USER,
067        //
068        GET_FIRST_TAGGING_FOR_DOC_AND_TAG,
069        //
070        GET_TAGS_FOR_DOCUMENT,
071        // core version: should keep on querying VCS
072        GET_TAGS_FOR_DOCUMENT_CORE,
073        //
074        GET_DOCUMENTS_FOR_TAG,
075        //
076        GET_TAGS_FOR_DOCUMENT_AND_USER,
077        // core version: should keep on querying VCS
078        GET_TAGS_FOR_DOCUMENT_AND_USER_CORE,
079        //
080        GET_DOCUMENTS_FOR_TAG_AND_USER,
081        //
082        GET_TAGS_TO_COPY_FOR_DOCUMENT,
083        //
084        GET_TAG_SUGGESTIONS,
085        //
086        GET_TAG_SUGGESTIONS_FOR_USER,
087        //
088        GET_TAGGED_DOCUMENTS_UNDER,
089        //
090        GET_ALL_TAGS,
091        //
092        GET_ALL_TAGS_FOR_USER,
093        //
094        GET_TAGS_FOR_DOCUMENTS,
095        //
096        GET_TAGS_FOR_DOCUMENTS_AND_USER,
097    }
098
099    @Override
100    public boolean isEnabled() {
101        return true;
102    }
103
104    protected static String cleanLabel(String label, boolean allowEmpty, boolean allowPercent) {
105        if (label == null) {
106            if (allowEmpty) {
107                return null;
108            }
109            throw new NuxeoException("Invalid empty tag");
110        }
111        label = label.toLowerCase(); // lowercase
112        label = label.replace(" ", ""); // no spaces
113        label = label.replace("\\", ""); // dubious char
114        label = label.replace("'", ""); // dubious char
115        if (!allowPercent) {
116            label = label.replace("%", ""); // dubious char
117        }
118        if (label.length() == 0) {
119            throw new NuxeoException("Invalid empty tag");
120        }
121        return label;
122    }
123
124    protected static String cleanUsername(String username) {
125        return username == null ? null : username.replace("'", "");
126    }
127
128    @Override
129    public void tag(CoreSession session, String docId, String label, String username) {
130        UnrestrictedAddTagging r = new UnrestrictedAddTagging(session, docId, label, username);
131        r.runUnrestricted();
132        fireUpdateEvent(session, docId);
133    }
134
135    protected void fireUpdateEvent(CoreSession session, String docId) {
136        DocumentRef documentRef = new IdRef(docId);
137        if (session.exists(documentRef)) {
138            DocumentModel documentModel = session.getDocument(documentRef);
139            DocumentEventContext ctx = new DocumentEventContext(session, session.getPrincipal(), documentModel);
140            Event event = ctx.newEvent(DocumentEventTypes.DOCUMENT_TAG_UPDATED);
141            Framework.getLocalService(EventService.class).fireEvent(event);
142        }
143    }
144
145    protected static class UnrestrictedAddTagging extends UnrestrictedSessionRunner {
146        private final String docId;
147
148        private final String label;
149
150        private final String username;
151
152        protected UnrestrictedAddTagging(CoreSession session, String docId, String label, String username) {
153            super(session);
154            this.docId = docId;
155            this.label = cleanLabel(label, false, false);
156            this.username = cleanUsername(username);
157        }
158
159        @Override
160        public void run() {
161            // Find tag
162            List<Map<String, Serializable>> res = getItems(PAGE_PROVIDERS.GET_DOCUMENT_IDS_FOR_TAG.name(), session,
163                    label);
164            String tagId = (res != null && !res.isEmpty()) ? (String) res.get(0).get(NXQL.ECM_UUID) : null;
165            Calendar date = Calendar.getInstance();
166            if (tagId == null) {
167                // no tag found, create it
168                DocumentModel tag = session.createDocumentModel(null, label, TagConstants.TAG_DOCUMENT_TYPE);
169                tag.setPropertyValue("dc:created", date);
170                tag.setPropertyValue(TagConstants.TAG_LABEL_FIELD, label);
171                tag = session.createDocument(tag);
172                tagId = tag.getId();
173            }
174            // Check if tagging already exists for user.
175            if (username != null) {
176                res = getItems(PAGE_PROVIDERS.GET_FIRST_TAGGING_FOR_DOC_AND_TAG_AND_USER.name(), session, docId, tagId,
177                        username);
178            } else {
179                res = getItems(PAGE_PROVIDERS.GET_FIRST_TAGGING_FOR_DOC_AND_TAG.name(), session, docId, tagId);
180            }
181            if (res != null && !res.isEmpty()) {
182                // tagging already exists
183                return;
184            }
185            // Add tagging to the document.
186            DocumentModel tagging = session.createDocumentModel(null, label, TagConstants.TAGGING_DOCUMENT_TYPE);
187            tagging.setPropertyValue("dc:created", date);
188            if (username != null) {
189                tagging.setPropertyValue("dc:creator", username);
190            }
191            tagging.setPropertyValue(TagConstants.TAGGING_SOURCE_FIELD, docId);
192            tagging.setPropertyValue(TagConstants.TAGGING_TARGET_FIELD, tagId);
193            session.createDocument(tagging);
194            session.save();
195        }
196
197    }
198
199    @Override
200    public void untag(CoreSession session, String docId, String label, String username)
201            throws DocumentSecurityException {
202        // There's two allowed cases here:
203        // - document doesn't exist, we're here after documentRemoved event
204        // - regular case: check if user can remove this tag on document
205        if (!session.exists(new IdRef(docId)) || canUntag(session, docId, label)) {
206            UnrestrictedRemoveTagging r = new UnrestrictedRemoveTagging(session, docId, label, username);
207            r.runUnrestricted();
208            if (label != null) {
209                fireUpdateEvent(session, docId);
210            }
211        } else {
212            String principalName = session.getPrincipal().getName();
213            throw new DocumentSecurityException("User '" + principalName + "' is not allowed to remove tag '" + label
214                    + "' on document '" + docId + "'");
215        }
216    }
217
218    protected static class UnrestrictedRemoveTagging extends UnrestrictedSessionRunner {
219
220        private final String docId;
221
222        private final String label;
223
224        private final String username;
225
226        protected UnrestrictedRemoveTagging(CoreSession session, String docId, String label, String username) {
227            super(session);
228            this.docId = docId;
229            this.label = cleanLabel(label, true, false);
230            this.username = cleanUsername(username);
231        }
232
233        @Override
234        public void run() {
235            String tagId = null;
236            if (label != null) {
237                // Find tag
238                List<Map<String, Serializable>> res = getItems(PAGE_PROVIDERS.GET_DOCUMENT_IDS_FOR_TAG.name(), session,
239                        label);
240                tagId = (res != null && !res.isEmpty()) ? (String) res.get(0).get(NXQL.ECM_UUID) : null;
241                if (tagId == null) {
242                    // tag not found
243                    return;
244                }
245            }
246            // Find taggings for user.
247            Set<String> taggingIds = new HashSet<>();
248            String query = String.format("SELECT ecm:uuid FROM Tagging WHERE relation:source = '%s'", docId);
249            if (tagId != null) {
250                query += String.format(" AND relation:target = '%s'", tagId);
251            }
252            if (username != null) {
253                query += String.format(" AND dc:creator = '%s'", username);
254            }
255            try (IterableQueryResult res = session.queryAndFetch(query, NXQL.NXQL)) {
256                for (Map<String, Serializable> map : res) {
257                    taggingIds.add((String) map.get(NXQL.ECM_UUID));
258                }
259            }
260            // Remove taggings
261            for (String taggingId : taggingIds) {
262                session.removeDocument(new IdRef(taggingId));
263            }
264            if (!taggingIds.isEmpty()) {
265                session.save();
266            }
267        }
268
269    }
270
271    /**
272     * @since 8.4
273     */
274    @Override
275    public boolean canUntag(CoreSession session, String docId, String label) {
276        if (session.hasPermission(new IdRef(docId), SecurityConstants.WRITE)) {
277            // If user has WRITE permission, user can remove any tags
278            return true;
279        }
280        // Else check if desired tag was created by current user
281        UnrestrictedCanRemoveTagging r = new UnrestrictedCanRemoveTagging(session, docId, label);
282        r.runUnrestricted();
283        return r.canUntag;
284    }
285
286    protected static class UnrestrictedCanRemoveTagging extends UnrestrictedSessionRunner {
287
288        private final String docId;
289
290        private final String label;
291
292        private boolean canUntag;
293
294        protected UnrestrictedCanRemoveTagging(CoreSession session, String docId, String label) {
295            super(session);
296            this.docId = docId;
297            this.label = cleanLabel(label, true, false);
298            this.canUntag = false;
299        }
300
301        @Override
302        public void run() {
303            String tagId = null;
304            if (label != null) {
305                // Find tag
306                List<Map<String, Serializable>> res = getItems(PAGE_PROVIDERS.GET_DOCUMENT_IDS_FOR_TAG.name(), session,
307                        label);
308                tagId = (res != null && !res.isEmpty()) ? (String) res.get(0).get(NXQL.ECM_UUID) : null;
309                if (tagId == null) {
310                    // tag not found - so user can untag
311                    canUntag = true;
312                    return;
313                }
314            }
315            // Find creators of tag(s).
316            Set<String> creators = new HashSet<>();
317            String query = String.format("SELECT DISTINCT dc:creator FROM Tagging WHERE relation:source = '%s'",
318                    docId);
319            if (tagId != null) {
320                query += String.format(" AND relation:target = '%s'", tagId);
321            }
322            try (IterableQueryResult res = session.queryAndFetch(query, NXQL.NXQL)) {
323                for (Map<String, Serializable> map : res) {
324                    creators.add((String) map.get("dc:creator"));
325                }
326            }
327            // Check if user can untag
328            // - in case of one tag, check if creators contains user
329            // - in case of all tags, check if user is the only creator
330            canUntag = creators.size() == 1 && creators.contains(originatingUsername);
331        }
332
333    }
334
335    @Override
336    public List<Tag> getDocumentTags(CoreSession session, String docId, String username) {
337        return getDocumentTags(session, docId, username, true);
338    }
339
340    @Override
341    public List<Tag> getDocumentTags(CoreSession session, String docId, String username, boolean useCore) {
342        UnrestrictedGetDocumentTags r = new UnrestrictedGetDocumentTags(session, docId, username, useCore);
343        r.runUnrestricted();
344        return r.tags;
345    }
346
347    protected static class UnrestrictedGetDocumentTags extends UnrestrictedSessionRunner {
348
349        protected final String docId;
350
351        protected final String username;
352
353        protected final List<Tag> tags;
354
355        protected final boolean useCore;
356
357        protected UnrestrictedGetDocumentTags(CoreSession session, String docId, String username, boolean useCore) {
358            super(session);
359            this.docId = docId;
360            this.username = cleanUsername(username);
361            this.useCore = useCore;
362            this.tags = new ArrayList<>();
363        }
364
365        @Override
366        public void run() {
367            List<Map<String, Serializable>> res;
368            if (username == null) {
369                String ppName = PAGE_PROVIDERS.GET_TAGS_FOR_DOCUMENT.name();
370                if (useCore) {
371                    ppName = PAGE_PROVIDERS.GET_TAGS_FOR_DOCUMENT_CORE.name();
372                }
373                res = getItems(ppName, session, docId);
374            } else {
375                String ppName = PAGE_PROVIDERS.GET_TAGS_FOR_DOCUMENT_AND_USER.name();
376                if (useCore) {
377                    ppName = PAGE_PROVIDERS.GET_TAGS_FOR_DOCUMENT_AND_USER_CORE.name();
378                }
379                res = getItems(ppName, session, docId, username);
380            }
381            if (res != null) {
382                for (Map<String, Serializable> map : res) {
383                    String label = (String) map.get(TagConstants.TAG_LABEL_FIELD);
384                    tags.add(new Tag(label, 0));
385                }
386            }
387        }
388
389    }
390
391    @Override
392    public void removeTags(CoreSession session, String docId) {
393        untag(session, docId, null, null);
394    }
395
396    @Override
397    public void copyTags(CoreSession session, String srcDocId, String dstDocId) {
398        copyTags(session, srcDocId, dstDocId, false);
399    }
400
401    protected void copyTags(CoreSession session, String srcDocId, String dstDocId, boolean removeExistingTags) {
402        if (removeExistingTags) {
403            removeTags(session, dstDocId);
404        }
405
406        UnrestrictedCopyTags r = new UnrestrictedCopyTags(session, srcDocId, dstDocId);
407        r.runUnrestricted();
408    }
409
410    protected static class UnrestrictedCopyTags extends UnrestrictedSessionRunner {
411
412        protected final String srcDocId;
413
414        protected final String dstDocId;
415
416        protected UnrestrictedCopyTags(CoreSession session, String srcDocId, String dstDocId) {
417            super(session);
418            this.srcDocId = srcDocId;
419            this.dstDocId = dstDocId;
420        }
421
422        @Override
423        public void run() {
424            Set<String> existingTags = new HashSet<>();
425            List<Map<String, Serializable>> dstTagsRes = getItems(PAGE_PROVIDERS.GET_TAGS_TO_COPY_FOR_DOCUMENT.name(),
426                    session, dstDocId);
427            if (dstTagsRes != null) {
428                for (Map<String, Serializable> map : dstTagsRes) {
429                    existingTags.add(String.format("%s/%s", map.get("tag:label"), map.get("dc:creator")));
430                }
431            }
432
433            List<Map<String, Serializable>> srcTagsRes = getItems(PAGE_PROVIDERS.GET_TAGS_TO_COPY_FOR_DOCUMENT.name(),
434                    session, srcDocId);
435            if (srcTagsRes != null) {
436                boolean docCreated = false;
437                for (Map<String, Serializable> map : srcTagsRes) {
438                    String key = String.format("%s/%s", map.get("tag:label"), map.get("dc:creator"));
439                    if (!existingTags.contains(key)) {
440                        DocumentModel tagging = session.createDocumentModel(null, (String) map.get("tag:label"),
441                                TagConstants.TAGGING_DOCUMENT_TYPE);
442                        tagging.setPropertyValue("dc:created", map.get("dc:created"));
443                        tagging.setPropertyValue("dc:creator", map.get("dc:creator"));
444                        tagging.setPropertyValue(TagConstants.TAGGING_SOURCE_FIELD, dstDocId);
445                        tagging.setPropertyValue(TagConstants.TAGGING_TARGET_FIELD, map.get("relation:target"));
446                        session.createDocument(tagging);
447                        docCreated = true;
448                    }
449                }
450                if (docCreated) {
451                    session.save();
452                }
453            }
454        }
455
456    }
457
458    @Override
459    public void replaceTags(CoreSession session, String srcDocId, String dstDocId) {
460        copyTags(session, srcDocId, dstDocId, true);
461    }
462
463    @Override
464    public List<String> getTagDocumentIds(CoreSession session, String label, String username) {
465        UnrestrictedGetTagDocumentIds r = new UnrestrictedGetTagDocumentIds(session, label, username);
466        r.runUnrestricted();
467        return r.docIds;
468    }
469
470    protected static class UnrestrictedGetTagDocumentIds extends UnrestrictedSessionRunner {
471
472        protected final String label;
473
474        protected final String username;
475
476        protected final List<String> docIds;
477
478        protected UnrestrictedGetTagDocumentIds(CoreSession session, String label, String username) {
479            super(session);
480            this.label = cleanLabel(label, false, false);
481            this.username = cleanUsername(username);
482            this.docIds = new ArrayList<>();
483        }
484
485        @Override
486        public void run() {
487            List<Map<String, Serializable>> res;
488            if (username == null) {
489                res = getItems(PAGE_PROVIDERS.GET_DOCUMENTS_FOR_TAG.name(), session, label);
490            } else {
491                res = getItems(PAGE_PROVIDERS.GET_DOCUMENTS_FOR_TAG_AND_USER.name(), session, label, username);
492            }
493            if (res != null) {
494                for (Map<String, Serializable> map : res) {
495                    docIds.add((String) map.get(TagConstants.TAGGING_SOURCE_FIELD));
496                }
497            }
498        }
499
500    }
501
502    @Override
503    public List<Tag> getTagCloud(CoreSession session, String docId, String username, Boolean normalize) {
504        UnrestrictedGetDocumentCloud r = new UnrestrictedGetDocumentCloud(session, docId, username, normalize);
505        r.runUnrestricted();
506        return r.cloud;
507    }
508
509    protected static class UnrestrictedGetDocumentCloud extends UnrestrictedSessionRunner {
510
511        protected final String docId;
512
513        protected final String username;
514
515        protected final List<Tag> cloud;
516
517        protected final Boolean normalize;
518
519        protected UnrestrictedGetDocumentCloud(CoreSession session, String docId, String username, Boolean normalize) {
520            super(session);
521            this.docId = docId;
522            this.username = cleanUsername(username);
523            this.normalize = normalize;
524            this.cloud = new ArrayList<>();
525        }
526
527        @Override
528        public void run() {
529            List<Map<String, Serializable>> res;
530            if (docId == null) {
531                if (username == null) {
532                    res = getItems(PAGE_PROVIDERS.GET_ALL_TAGS.name(), session);
533                } else {
534                    res = getItems(PAGE_PROVIDERS.GET_ALL_TAGS_FOR_USER.name(), session, username);
535                }
536            } else {
537                // find all docs under docid
538                String path = session.getDocument(new IdRef(docId)).getPathAsString();
539                path = path.replace("'", "");
540                List<String> docIds = new ArrayList<>();
541                docIds.add(docId);
542                List<Map<String, Serializable>> docRes = getItems(PAGE_PROVIDERS.GET_TAGGED_DOCUMENTS_UNDER.name(),
543                        session, path);
544                if (docRes != null) {
545                    for (Map<String, Serializable> map : docRes) {
546                        docIds.add((String) map.get(NXQL.ECM_UUID));
547                    }
548                }
549
550                if (username == null) {
551                    res = getItems(PAGE_PROVIDERS.GET_TAGS_FOR_DOCUMENTS.name(), session, docIds);
552                } else {
553                    res = getItems(PAGE_PROVIDERS.GET_TAGS_FOR_DOCUMENTS_AND_USER.name(), session, docIds, username);
554                }
555            }
556
557            int min = 999999, max = 0;
558            if (res != null) {
559                for (Map<String, Serializable> map : res) {
560                    String label = (String) map.get(TagConstants.TAG_LABEL_FIELD);
561                    int weight = ((Long) map.get(TagConstants.TAGGING_SOURCE_FIELD)).intValue();
562                    if (weight == 0) {
563                        // shouldn't happen
564                        continue;
565                    }
566                    if (weight > max) {
567                        max = weight;
568                    }
569                    if (weight < min) {
570                        min = weight;
571                    }
572                    Tag weightedTag = new Tag(label, weight);
573                    cloud.add(weightedTag);
574                }
575            }
576            if (normalize != null) {
577                normalizeCloud(cloud, min, max, !normalize.booleanValue());
578            }
579        }
580
581    }
582
583    public static void normalizeCloud(List<Tag> cloud, int min, int max, boolean linear) {
584        if (min == max) {
585            for (Tag tag : cloud) {
586                tag.setWeight(100);
587            }
588            return;
589        }
590        double nmin;
591        double diff;
592        if (linear) {
593            nmin = min;
594            diff = max - min;
595        } else {
596            nmin = Math.log(min);
597            diff = Math.log(max) - nmin;
598        }
599        for (Tag tag : cloud) {
600            long weight = tag.getWeight();
601            double norm;
602            if (linear) {
603                norm = (weight - nmin) / diff;
604            } else {
605                norm = (Math.log(weight) - nmin) / diff;
606            }
607            tag.setWeight(Math.round(100 * norm));
608        }
609    }
610
611    @Override
612    public List<Tag> getSuggestions(CoreSession session, String label, String username) {
613        UnrestrictedGetTagSuggestions r = new UnrestrictedGetTagSuggestions(session, label, username);
614        r.runUnrestricted();
615        return r.tags;
616    }
617
618    protected static class UnrestrictedGetTagSuggestions extends UnrestrictedSessionRunner {
619
620        protected final String label;
621
622        protected final String username;
623
624        protected final List<Tag> tags;
625
626        protected UnrestrictedGetTagSuggestions(CoreSession session, String label, String username) {
627            super(session);
628            label = cleanLabel(label, false, true);
629            if (!label.contains("%")) {
630                label += "%";
631            }
632            this.label = label;
633            this.username = cleanUsername(username);
634            this.tags = new ArrayList<>();
635        }
636
637        @Override
638        public void run() {
639            List<Map<String, Serializable>> res;
640            if (username == null) {
641                res = getItems(PAGE_PROVIDERS.GET_TAG_SUGGESTIONS.name(), session, label);
642            } else {
643                res = getItems(PAGE_PROVIDERS.GET_TAG_SUGGESTIONS_FOR_USER.name(), session, label, username);
644            }
645            if (res != null) {
646                for (Map<String, Serializable> map : res) {
647                    String label = (String) map.get(TagConstants.TAG_LABEL_FIELD);
648                    tags.add(new Tag(label, 0));
649                }
650            }
651            // XXX should sort on tag weight
652            Collections.sort(tags, Tag.LABEL_COMPARATOR);
653        }
654
655    }
656
657    /**
658     * Returns results from calls to {@link CoreSession#queryAndFetch(String, String, Object...)} using page providers.
659     *
660     * @since 6.0
661     */
662    @SuppressWarnings("unchecked")
663    protected static List<Map<String, Serializable>> getItems(String pageProviderName, CoreSession session,
664            Object... params) {
665        PageProviderService ppService = Framework.getService(PageProviderService.class);
666        if (ppService == null) {
667            throw new RuntimeException("Missing PageProvider service");
668        }
669        Map<String, Serializable> props = new HashMap<>();
670        // first retrieve potential props from definition
671        PageProviderDefinition def = ppService.getPageProviderDefinition(pageProviderName);
672        if (def != null) {
673            Map<String, String> defProps = def.getProperties();
674            if (defProps != null) {
675                props.putAll(defProps);
676            }
677        }
678        props.put(CoreQueryAndFetchPageProvider.CORE_SESSION_PROPERTY, (Serializable) session);
679        PageProvider<Map<String, Serializable>> pp = (PageProvider<Map<String, Serializable>>) ppService.getPageProvider(
680                pageProviderName, null, null, null, props, params);
681        if (pp == null) {
682            throw new NuxeoException("Page provider not found: " + pageProviderName);
683        }
684        return pp.getCurrentPage();
685    }
686
687}