1. Project Clover database Sat Feb 2 2019 06:45:20 CET
  2. Package com.xpn.xwiki.web

File CreateActionRequestHandler.java

 

Coverage histogram

../../../../img/srcFileCovDistChart10.png
0% of files have more coverage

Code metrics

64
168
26
1
817
405
68
0.4
6.46
26
2.62

Classes

Class Line # Actions
CreateActionRequestHandler 62 168 0% 68 22
0.914728791.5%
 

Contributing tests

This file is covered by 39 tests. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package com.xpn.xwiki.web;
21   
22    import java.lang.reflect.Type;
23    import java.util.ArrayList;
24    import java.util.Comparator;
25    import java.util.List;
26    import java.util.Map;
27    import java.util.stream.Collectors;
28   
29    import javax.script.ScriptContext;
30   
31    import org.apache.commons.lang3.StringUtils;
32    import org.apache.velocity.VelocityContext;
33    import org.slf4j.Logger;
34    import org.slf4j.LoggerFactory;
35    import org.xwiki.model.EntityType;
36    import org.xwiki.model.reference.DocumentReference;
37    import org.xwiki.model.reference.DocumentReferenceResolver;
38    import org.xwiki.model.reference.EntityReference;
39    import org.xwiki.model.reference.EntityReferenceResolver;
40    import org.xwiki.model.reference.EntityReferenceSerializer;
41    import org.xwiki.model.reference.SpaceReference;
42    import org.xwiki.model.reference.SpaceReferenceResolver;
43    import org.xwiki.query.Query;
44    import org.xwiki.query.QueryManager;
45    import org.xwiki.script.ScriptContextManager;
46    import org.xwiki.security.authorization.ContextualAuthorizationManager;
47    import org.xwiki.security.authorization.Right;
48    import org.xwiki.velocity.VelocityManager;
49   
50    import com.xpn.xwiki.XWiki;
51    import com.xpn.xwiki.XWikiContext;
52    import com.xpn.xwiki.XWikiException;
53    import com.xpn.xwiki.api.Document;
54    import com.xpn.xwiki.doc.XWikiDocument;
55    import com.xpn.xwiki.objects.BaseObject;
56   
57    /**
58    * Helper class used to handle one individual create action request.
59    *
60    * @version $Id: e24512d2e4d0a50ee44bdc9f818414e19f74b610 $
61    */
 
62    public class CreateActionRequestHandler
63    {
64    /**
65    * Log used to report exceptions.
66    */
67    private static final Logger LOGGER = LoggerFactory.getLogger(CreateActionRequestHandler.class);
68   
69    /**
70    * The name of the space reference parameter.
71    */
72    private static final String SPACE_REFERENCE = "spaceReference";
73   
74    /**
75    * The name parameter.
76    */
77    private static final String NAME = "name";
78   
79    /**
80    * The name of the deprecated space parameter. <br>
81    * Note: if you change the value of this variable, change the value of {{@link #TOCREATE_SPACE} to the previous
82    * value.
83    *
84    * @deprecated Use {@value #SPACE_REFERENCE} as parameter name instead.
85    */
86    @Deprecated
87    private static final String SPACE = "space";
88   
89    /**
90    * The name of the page parameter.
91    *
92    * @deprecated Use {@value #NAME} as parameter name instead.
93    */
94    @Deprecated
95    private static final String PAGE = "page";
96   
97    /**
98    * The value of the tocreate parameter when a space is to be created. <br>
99    * TODO: find a way to give this constant the same value as the constant above without violating checkstyle.
100    */
101    private static final String TOCREATE_SPACE = SPACE;
102   
103    /**
104    * The name of the "type" parameter.
105    */
106    private static final String TYPE = "type";
107   
108    /**
109    * The value of the tocreate parameter when a terminal/regular document is to be created.
110    */
111    private static final String TOCREATE_TERMINAL = "terminal";
112   
113    /**
114    * The value of the tocreate parameter when a non-terminal document is to be created.
115    */
116    private static final String TOCREATE_NONTERMINAL = "nonterminal";
117   
118    /**
119    * The name of the template field inside the template provider, or the template parameter which can be sent
120    * directly, without passing through the template provider.
121    */
122    private static final String TEMPLATE = "template";
123   
124    /**
125    * The name of the template provider parameter.
126    */
127    private static final String TEMPLATE_PROVIDER = "templateprovider";
128   
129    /**
130    * The template provider class, to create documents from templates.
131    */
132    private static final EntityReference TEMPLATE_PROVIDER_CLASS = new EntityReference("TemplateProviderClass",
133    EntityType.DOCUMENT, new EntityReference(XWiki.SYSTEM_SPACE, EntityType.SPACE));
134   
135    /**
136    * The redirect class, used to mark pages that are redirect place-holders, i.e. hidden pages that serve only for
137    * redirecting the user to a different page (e.g. when a page has been moved).
138    */
139    private static final EntityReference REDIRECT_CLASS = new EntityReference("RedirectClass", EntityType.DOCUMENT,
140    new EntityReference(XWiki.SYSTEM_SPACE, EntityType.SPACE));
141   
142    /**
143    * The property name for the spaces in the template provider object.
144    *
145    * @deprecated since 8.3M2. Use {@link #TP_CREATION_RESTRICTIONS_PROPERTY} or
146    * {@value #TP_VISIBILITY_RESTRICTIONS_PROPERTY} instead for the explicit restriction you need to add.
147    */
148    @Deprecated
149    private static final String SPACES_PROPERTY = "spaces";
150   
151    /**
152    * The key used to add exceptions on the context, to be read by the template.
153    */
154    private static final String EXCEPTION = "createException";
155   
156    /**
157    * Current entity reference resolver hint.
158    */
159    private static final String CURRENT_RESOLVER_HINT = "current";
160   
161    /**
162    * Current entity reference resolver hint.
163    */
164    private static final String CURRENT_MIXED_RESOLVER_HINT = "currentmixed";
165   
166    /**
167    * Local entity reference serializer hint.
168    */
169    private static final String LOCAL_SERIALIZER_HINT = "local";
170   
171    private static final String TP_TERMINAL_PROPERTY = TOCREATE_TERMINAL;
172   
173    private static final String TP_TYPE_PROPERTY = TYPE;
174   
175    private static final String TP_TYPE_PROPERTY_SPACE_VALUE = SPACE;
176   
177    private static final String TP_CREATION_RESTRICTIONS_PROPERTY = "creationRestrictions";
178   
179    private static final String TP_VISIBILITY_RESTRICTIONS_PROPERTY = "visibilityRestrictions";
180   
181    private static final String TP_CREATION_RESTRICTIONS_ARE_SUGGESTIONS_PROPERTY =
182    "creationRestrictionsAreSuggestions";
183   
184    /**
185    * Space homepage document name.
186    */
187    private static final String WEBHOME = "WebHome";
188   
189    private ScriptContextManager scriptContextManager;
190   
191    private SpaceReference spaceReference;
192   
193    private String name;
194   
195    private boolean isSpace;
196   
197    private XWikiContext context;
198   
199    private XWikiDocument document;
200   
201    private XWikiRequest request;
202   
203    private BaseObject templateProvider;
204   
205    private List<Document> availableTemplateProviders;
206   
207    private List<Document> recommendedTemplateProviders;
208   
209    private String type;
210   
211    /**
212    * @param context the XWikiContext for which to handle the request.
213    */
 
214  104 toggle public CreateActionRequestHandler(XWikiContext context)
215    {
216  104 this.context = context;
217  104 this.document = context.getDoc();
218  104 this.request = context.getRequest();
219    }
220   
221    /**
222    * Process the request and extract from the given parameters the data needed to create the new document.
223    *
224    * @throws XWikiException if problems occur
225    */
 
226  104 toggle public void processRequest() throws XWikiException
227    {
228    // Get the template provider for creating this document, if any template provider is specified
229  104 DocumentReferenceResolver<EntityReference> referenceResolver =
230    Utils.getComponent(DocumentReferenceResolver.TYPE_REFERENCE, CURRENT_RESOLVER_HINT);
231  104 DocumentReference templateProviderClassReference = referenceResolver.resolve(TEMPLATE_PROVIDER_CLASS);
232  104 templateProvider = getTemplateProvider(templateProviderClassReference);
233   
234    // Get the available templates, in the current space, to check if all conditions to create a new document are
235    // met
236  104 availableTemplateProviders =
237    loadAvailableTemplateProviders(document.getDocumentReference().getLastSpaceReference(),
238    templateProviderClassReference, context);
239   
240    // Get the type of document to create
241  104 type = request.get(TYPE);
242   
243    // Since this template can be used for creating a Page or a Space, check the passed "tocreate" parameter
244    // which can be either "page" or "space". If no parameter is passed then we default to creating a Page.
245  104 String toCreate = request.getParameter("tocreate");
246   
247  104 if (document.isNew()) {
248  34 processNewDocument(toCreate);
249    } else {
250    // We are on an existing document...
251   
252  70 if (request.getParameter(SPACE) != null || request.getParameter(PAGE) != null) {
253    // We are in Backwards Compatibility mode and we are using the deprecated parameter names.
254  7 processDeprecatedParameters(toCreate);
255    } else {
256    // Determine the new document values from the request.
257   
258  63 String spaceReferenceParameter = request.getParameter(SPACE_REFERENCE);
259    // We can have an empty spaceReference parameter symbolizing that we are creating a top level space or
260    // non-terminal document.
261  63 if (StringUtils.isNotEmpty(spaceReferenceParameter)) {
262  34 EntityReferenceResolver<String> genericResolver =
263    Utils.getComponent(EntityReferenceResolver.TYPE_STRING, CURRENT_RESOLVER_HINT);
264   
265  34 EntityReference resolvedEntityReference =
266    genericResolver.resolve(spaceReferenceParameter, EntityType.SPACE);
267  34 spaceReference = new SpaceReference(resolvedEntityReference);
268    }
269   
270    // Note: We leave the spaceReference variable intentionally null to symbolize a top level space or
271    // non-terminal document.
272   
273  63 name = request.getParameter(NAME);
274   
275    // Determine the type of document we are creating (terminal vs non-terminal).
276   
277  63 if (TOCREATE_TERMINAL.equals(toCreate) || TOCREATE_NONTERMINAL.equals(toCreate)) {
278    // Look at the request to see what the user wanted to create (terminal or non-terminal).
279   
280  26 isSpace = !TOCREATE_TERMINAL.equals(toCreate);
281  37 } else if (templateProvider != null) {
282    // A template provider is specified. Use it and extract the type of document.
283   
284  7 boolean providerTerminal = getTemplateProviderTerminalValue();
285  7 isSpace = !providerTerminal;
286    } else {
287    // Default to creating non-terminal documents.
288  30 isSpace = true;
289    }
290    }
291    }
292    }
293   
294    /**
295    * @param toCreate the value of the "tocreate" request parameter
296    */
 
297  34 toggle private void processNewDocument(String toCreate)
298    {
299    // Current space and page name.
300  34 spaceReference = document.getDocumentReference().getLastSpaceReference();
301  34 name = document.getDocumentReference().getName();
302   
303    // Determine if the current document is in a top-level space.
304  34 EntityReference parentSpaceReference = spaceReference.getParent();
305  34 boolean isTopLevelSpace = parentSpaceReference.extractReference(EntityType.SPACE) == null;
306   
307    // Remember this since we might update it below.
308  34 String originalName = name;
309   
310    // Since WebHome is a convention, determine the real name and parent of our document.
311  34 if (WEBHOME.equals(name)) {
312    // Determine its name from the space name.
313  24 name = spaceReference.getName();
314   
315    // Determine its space reference by looking at the space's parent.
316  24 if (!isTopLevelSpace) {
317    // The parent reference is a space reference. Use it.
318  19 spaceReference = new SpaceReference(parentSpaceReference);
319    } else {
320    // Top level document, i.e. the parent reference is a wiki reference. Clear the spaceReference variable
321    // so that this case is properly handled later on (as if an empty value was passed as parameter in the
322    // request).
323  5 spaceReference = null;
324    }
325    }
326   
327    // Determine the type of document we are creating (terminal vs non-terminal).
328   
329  34 if (TOCREATE_TERMINAL.equals(toCreate) || TOCREATE_NONTERMINAL.equals(toCreate)) {
330    // Look at the request to see what the user wanted to create (terminal or non-terminal).
331   
332  14 isSpace = !TOCREATE_TERMINAL.equals(toCreate);
333  20 } else if (templateProvider != null) {
334    // A template provider is specified. Use it and extract the type of document.
335   
336  5 boolean providerTerminal = getTemplateProviderTerminalValue();
337  5 isSpace = !providerTerminal;
338    } else {
339    // Last option is to check the document's original name and see if it was "WebHome".
340  15 isSpace = WEBHOME.equals(originalName);
341    }
342    }
343   
344    /**
345    * @return
346    */
 
347  12 toggle private boolean getTemplateProviderTerminalValue()
348    {
349  12 boolean providerTerminal;
350  12 int providerTerminalValue = templateProvider.getIntValue(TP_TERMINAL_PROPERTY, -1);
351  12 if (providerTerminalValue == -1) {
352    // Backwards compatibility with providers that did not have the "terminal" property. We are deducing it
353    // from the value of the "type" property.
354  2 String providerType = templateProvider.getStringValue(TP_TYPE_PROPERTY);
355  2 if (TP_TYPE_PROPERTY_SPACE_VALUE.equals(providerType)) {
356  1 providerTerminal = false;
357    } else {
358    // 'page' or NULL both resolve to true, for backwards compatibility reasons.
359  1 providerTerminal = true;
360    }
361    } else {
362    // Use the "terminal" value from the template provider.
363  10 providerTerminal = (1 == providerTerminalValue);
364    }
365  12 return providerTerminal;
366    }
367   
368    /**
369    * @param toCreate the value of the "tocreate" request parameter
370    */
 
371  7 toggle private void processDeprecatedParameters(String toCreate)
372    {
373    // Note: The most important details is that the deprecated "space" parameter stores unescaped space
374    // names, not references!
375  7 String spaceParameter = request.getParameter(SPACE);
376   
377  7 isSpace = TOCREATE_SPACE.equals(toCreate);
378  7 if (isSpace) {
379    // Always creating top level spaces in this mode. Adapt to the new implementation.
380  4 spaceReference = null;
381  4 name = spaceParameter;
382    } else {
383  3 if (StringUtils.isNotEmpty(spaceParameter)) {
384    // Always creating documents in top level spaces in this mode.
385  3 spaceReference = new SpaceReference(spaceParameter, document.getDocumentReference().getWikiReference());
386    }
387   
388  3 name = request.getParameter(PAGE);
389    }
390    }
391   
392    /**
393    * @param templateProviderClass the class of the template provider object
394    * @return the object which holds the template provider to be used for creation
395    * @throws XWikiException in case anything goes wrong manipulating documents
396    */
 
397  104 toggle private BaseObject getTemplateProvider(DocumentReference templateProviderClass) throws XWikiException
398    {
399  104 BaseObject result = null;
400   
401    // set the template, from the template provider param
402  104 String templateProviderDocReferenceString = request.getParameter(TEMPLATE_PROVIDER);
403   
404  104 if (!StringUtils.isEmpty(templateProviderDocReferenceString)) {
405    // parse this document reference
406  22 DocumentReference templateProviderRef =
407    getCurrentMixedResolver().resolve(templateProviderDocReferenceString);
408    // get the document of the template provider and the object
409  22 XWikiDocument templateProviderDoc = context.getWiki().getDocument(templateProviderRef, context);
410  22 result = templateProviderDoc.getXObject(templateProviderClass);
411    }
412   
413  104 return result;
414    }
415   
416    /**
417    * @param spaceReference the space to check if there are available templates for
418    * @param context the context of the current request
419    * @param templateClassReference the reference to the template provider class
420    * @return the available template providers for the passed space, as {@link Document}s
421    */
 
422  104 toggle private List<Document> loadAvailableTemplateProviders(SpaceReference spaceReference,
423    DocumentReference templateClassReference, XWikiContext context)
424    {
425  104 List<Document> templates = new ArrayList<>();
426  104 try {
427  104 QueryManager queryManager = Utils.getComponent((Type) QueryManager.class, "secure");
428  104 Query query =
429    queryManager.createQuery("from doc.object(XWiki.TemplateProviderClass) as template "
430    + "where doc.fullName not like 'XWiki.TemplateProviderTemplate' " + "order by template.name",
431    Query.XWQL);
432   
433    // TODO: Extend the above query to include a filter on the type and allowed spaces properties so we can
434    // remove the java code below, thus improving performance by not loading all the documents, but only the
435    // documents we need.
436   
437  104 List<XWikiDocument> recommendedTemplates = new ArrayList<>();
438   
439  104 List<String> templateProviderDocNames = query.execute();
440  104 for (String templateProviderName : templateProviderDocNames) {
441    // get the document and template provider object
442  84 DocumentReference reference = getCurrentMixedResolver().resolve(templateProviderName);
443   
444    // Verify that the current user has the view right on the Template document
445  84 if (!getAuthManager().hasAccess(Right.VIEW, reference)) {
446  0 continue;
447    }
448   
449  84 XWikiDocument templateDoc = context.getWiki().getDocument(reference, context);
450  84 BaseObject templateObject = templateDoc.getXObject(templateClassReference);
451   
452    // Check the template provider's visibility restrictions.
453  84 if (isTemplateProviderAllowedInSpace(templateObject, spaceReference,
454    TP_VISIBILITY_RESTRICTIONS_PROPERTY)) {
455   
456  65 List<String> creationRestrictions =
457    getTemplateProviderRestrictions(templateObject, TP_CREATION_RESTRICTIONS_PROPERTY);
458  65 if (creationRestrictions.size() > 0 && isTemplateProviderAllowedInSpace(templateObject,
459    spaceReference, TP_CREATION_RESTRICTIONS_PROPERTY)) {
460    // Consider providers that have creations restrictions matching the current space as
461    // "recommended" and handle them separately.
462  1 recommendedTemplates.add(templateDoc);
463    } else {
464    // Other visible providers.
465  64 templates.add(new Document(templateDoc, context));
466    }
467    }
468    }
469   
470  104 EntityReferenceSerializer<String> localSerializer =
471    Utils.getComponent(EntityReferenceSerializer.TYPE_STRING, LOCAL_SERIALIZER_HINT);
472  104 String spaceStringReference = localSerializer.serialize(spaceReference);
473   
474    // Sort the recommended providers and promote the most specific ones.
475  104 recommendedTemplates.sort(Comparator.comparing((XWikiDocument templateProviderDocument) -> {
476  0 BaseObject templateProviderObject = templateProviderDocument.getXObject(templateClassReference);
477   
478    // Look at any set creation restrictions.
479  0 List<String> restrictions =
480    getTemplateProviderRestrictions(templateProviderObject, TP_CREATION_RESTRICTIONS_PROPERTY);
481   
482    // Return the longest (max) matching restriction reference size as being the most specific.
483  0 return restrictions.stream()
484    // Filter only restrictions matching the current space.
485    .filter(restriction -> matchesRestriction(spaceStringReference, restriction))
486    // Score them based on which has the most specific restriction.
487    .mapToInt(restriction -> {
488  0 SpaceReferenceResolver<String> spaceResolver =
489    Utils.getComponent(SpaceReferenceResolver.TYPE_STRING);
490  0 SpaceReference restrictionSpaceReference = spaceResolver.resolve(restriction);
491    // The specificity score.
492  0 int specificity = restrictionSpaceReference.getReversedReferenceChain().size();
493  0 return specificity;
494    }).max()
495    // Or 0 if no restrictions are set or if none match the current space.
496    .orElse(0);
497    }).reversed());
498   
499  104 this.recommendedTemplateProviders = recommendedTemplates.stream()
500    .map(recommendedTemplate -> new Document(recommendedTemplate, context)).collect(Collectors.toList());
501   
502    // Give priority to the providers that that specify creation restrictions
503  104 templates.addAll(0, recommendedTemplateProviders);
504    } catch (Exception e) {
505  0 LOGGER.warn("There was an error getting the available templates for space {0}", spaceReference, e);
506    }
507   
508  104 return templates;
509    }
510   
 
511  106 toggle private DocumentReferenceResolver<String> getCurrentMixedResolver()
512    {
513    // resolver to use to resolve references received in request parameters
514  106 return Utils.getComponent(DocumentReferenceResolver.TYPE_STRING, CURRENT_MIXED_RESOLVER_HINT);
515    }
516   
 
517  112 toggle private boolean isTemplateProviderAllowedInSpace(BaseObject templateObject, SpaceReference spaceReference,
518    String restrictionsProperty)
519    {
520    // Check the allowed spaces list.
521  112 List<String> restrictions = getTemplateProviderRestrictions(templateObject, restrictionsProperty);
522  112 if (restrictions.size() > 0) {
523  31 EntityReferenceSerializer<String> localSerializer =
524    Utils.getComponent(EntityReferenceSerializer.TYPE_STRING, LOCAL_SERIALIZER_HINT);
525  31 String spaceStringReference = localSerializer.serialize(spaceReference);
526   
527  31 for (String allowedSpace : restrictions) {
528    // Exact match or parent space (i.e. prefix) match.
529  31 if (matchesRestriction(spaceStringReference, allowedSpace)) {
530  4 return true;
531    }
532    }
533   
534    // No match, not allowed.
535  27 return false;
536    }
537   
538    // No restrictions exist, allowed by default.
539  81 return true;
540    }
541   
 
542  31 toggle private boolean matchesRestriction(String spaceStringReferenceToTest, String allowedSpaceRestriction)
543    {
544  31 return allowedSpaceRestriction.equals(spaceStringReferenceToTest)
545    || StringUtils.startsWith(spaceStringReferenceToTest, String.format("%s.", allowedSpaceRestriction));
546    }
547   
 
548  180 toggle private List<String> getTemplateProviderRestrictions(BaseObject templateObject, String restrictionsProperty)
549    {
550  180 List<String> creationRestrictions = templateObject.getListValue(restrictionsProperty);
551  180 if (creationRestrictions.size() == 0) {
552    // Backwards compatibility for template providers created before 8.3M2, where the "spaces" property handled
553    // both visibility and creation.
554  140 creationRestrictions = templateObject.getListValue(SPACES_PROPERTY);
555    }
556  180 return creationRestrictions;
557    }
558   
559    /**
560    * @return the document reference of the new document to be created, {@code null} if a no document can be created
561    * (because the conditions are not met)
562    */
 
563  104 toggle public DocumentReference getNewDocumentReference()
564    {
565  104 DocumentReference result = null;
566   
567  104 if (StringUtils.isEmpty(name)) {
568    // Can`t do anything without a name.
569  24 return null;
570    }
571   
572    // The new values, after the processing needed for ND below, to be used when creating the document reference.
573  80 SpaceReference newSpaceReference = spaceReference;
574  80 String newName = name;
575   
576    // Special handling for old spaces or new Nested Documents.
577  80 if (isSpace) {
578  56 EntityReference parentSpaceReference = spaceReference;
579  56 if (parentSpaceReference == null) {
580  13 parentSpaceReference = context.getDoc().getDocumentReference().getWikiReference();
581    }
582   
583    // The new space's reference.
584  56 newSpaceReference = new SpaceReference(name, parentSpaceReference);
585   
586    // The new document's name set to the new space's homepage. In Nested Documents, this leads to the new ND's
587    // reference name.
588  56 newName = WEBHOME;
589    }
590   
591    // Proceed with creating the document...
592   
593  80 if (newSpaceReference == null) {
594    // No space specified, nothing to do. This can be the case for terminal documents, since non-terminal
595    // documents can be top-level.
596  1 return null;
597    }
598   
599    // Check whether there is a template parameter set, be it an empty one. If a page should be created and there is
600    // no template parameter, it means the create action is not supposed to be executed, but only display the
601    // available templates and let the user choose
602    // If there's no passed template, check if there are any available templates. If none available, then the fact
603    // that there is no template is ok.
604  79 if (hasTemplate() || availableTemplateProviders.isEmpty()) {
605  76 result = new DocumentReference(newName, newSpaceReference);
606    }
607   
608  79 return result;
609    }
610   
611    /**
612    * @return if a template or a template provider have been set (it can be empty however)
613    */
 
614  94 toggle public boolean hasTemplate()
615    {
616  94 return request.getParameter(TEMPLATE_PROVIDER) != null || request.getParameter(TEMPLATE) != null;
617    }
618   
619    /**
620    * Verifies if the creation inside the specified spaceReference is allowed by the current template provider. If the
621    * creation is not allowed, an exception will be set on the context.
622    *
623    * @return {@code true} if the creation is allowed, {@code false} otherwise
624    */
 
625  76 toggle public boolean isTemplateProviderAllowedToCreateInCurrentSpace()
626    {
627    // Check that the chosen space is allowed with the given template, if not:
628    // - Cancel the redirect
629    // - Set an error on the context, to be read by the create.vm
630  76 if (templateProvider != null) {
631    // Check if the creation restrictions are enforced.
632  22 boolean creationRestrictionsEnforced =
633    templateProvider.getIntValue(TP_CREATION_RESTRICTIONS_ARE_SUGGESTIONS_PROPERTY, 0) == 0;
634   
635    // Check using the template provider's creation restrictions, only when they are enforced.
636  22 if (creationRestrictionsEnforced && !isTemplateProviderAllowedInSpace(templateProvider, spaceReference,
637    TP_CREATION_RESTRICTIONS_PROPERTY)) {
638    // put an exception on the context, for create.vm to know to display an error
639  3 Object[] args = {templateProvider.getStringValue(TEMPLATE), spaceReference, name};
640  3 XWikiException exception =
641    new XWikiException(XWikiException.MODULE_XWIKI_STORE,
642    XWikiException.ERROR_XWIKI_APP_TEMPLATE_NOT_AVAILABLE,
643    "Template {0} cannot be used in space {1} when creating page {2}", null, args);
644   
645  3 ScriptContext scontext = getCurrentScriptContext();
646  3 scontext.setAttribute(EXCEPTION, exception, ScriptContext.ENGINE_SCOPE);
647  3 scontext.setAttribute("createAllowedSpaces",
648    getTemplateProviderRestrictions(templateProvider, TP_CREATION_RESTRICTIONS_PROPERTY),
649    ScriptContext.ENGINE_SCOPE);
650   
651  3 return false;
652    }
653    }
654   
655    // For all other cases, creation is allowed.
656  73 return true;
657    }
658   
659    /**
660    * @param newDocument the new document to check if it already exists
661    * @return true if the document already exists (i.e. is not usable) and set an exception in the velocity context;
662    * false otherwise.
663    */
 
664  73 toggle public boolean isDocumentAlreadyExisting(XWikiDocument newDocument)
665    {
666    // if the document exists don't create it, put the exception on the context so that the template gets it and
667    // re-requests the page and space, else create the document and redirect to edit
668  73 if (!isEmptyDocument(newDocument)) {
669  4 ScriptContext scontext = getCurrentScriptContext();
670   
671    // Expose to the template reference of the document that already exist so that it can propose to view or
672    // edit it.
673  4 scontext.setAttribute("existingDocumentReference", newDocument.getDocumentReference(),
674    ScriptContext.ENGINE_SCOPE);
675   
676    // Throw an exception.
677  4 Object[] args = {newDocument.getDocumentReference()};
678  4 XWikiException documentAlreadyExists =
679    new XWikiException(XWikiException.MODULE_XWIKI_STORE,
680    XWikiException.ERROR_XWIKI_APP_DOCUMENT_NOT_EMPTY,
681    "Cannot create document {0} because it already has content", null, args);
682  4 scontext.setAttribute(EXCEPTION, documentAlreadyExists, ScriptContext.ENGINE_SCOPE);
683   
684  4 return true;
685    }
686   
687  69 return false;
688    }
689   
690    /**
691    * Checks if a document is empty, that is, if a document with that name could be created from a template. <br>
692    * TODO: move this function to a more accessible place, to be used by the readFromTemplate method as well, so that
693    * we have consistency.
694    *
695    * @param document the document to check
696    * @return {@code true} if the document is empty (i.e. a document with the same name can be created (from
697    * template)), {@code false} otherwise
698    */
 
699  73 toggle private boolean isEmptyDocument(XWikiDocument document)
700    {
701    // If it's a new document or a redirect placeholder, it's fine.
702  73 if (document.isNew() || document.getXObject(REDIRECT_CLASS) != null) {
703  69 return true;
704    }
705   
706    // FIXME: the code below is not really what users might expect. Overriding an existing document (even if no
707    // content or objects) is not really nice to do. Should be removed.
708   
709    // otherwise, check content and objects (only empty newline content allowed and no objects)
710  4 String content = document.getContent();
711  4 if (!content.equals("\n") && !content.equals("") && !content.equals("\\\\")) {
712  4 return false;
713    }
714   
715    // go through all the objects and when finding the first one which is not null (because of the remove gaps),
716    // return false, we cannot re-create this doc
717  0 for (Map.Entry<DocumentReference, List<BaseObject>> objList : document.getXObjects().entrySet()) {
718  0 for (BaseObject obj : objList.getValue()) {
719  0 if (obj != null) {
720  0 return false;
721    }
722    }
723    }
724   
725  0 return true;
726    }
727   
728    /**
729    * @return the {@link VelocityContext} for the context we are handling
730    * @deprecated since 8.3M1, use {@link #getCurrentScriptContext()} instead
731    */
 
732  0 toggle @Deprecated
733    public VelocityContext getVelocityContext()
734    {
735  0 return Utils.getComponent(VelocityManager.class).getVelocityContext();
736    }
737   
738    /**
739    * @return the authorization manager
740    * @since 9.11
741    */
 
742  84 toggle protected ContextualAuthorizationManager getAuthManager()
743    {
744  84 return Utils.getComponent(ContextualAuthorizationManager.class);
745    }
746   
747    /**
748    * @return the current script context
749    * @since 8.3M1
750    */
 
751  7 toggle protected ScriptContext getCurrentScriptContext()
752    {
753  7 if (this.scriptContextManager == null) {
754  7 this.scriptContextManager = Utils.getComponent(ScriptContextManager.class);
755    }
756   
757  7 return this.scriptContextManager.getCurrentScriptContext();
758    }
759   
760    /**
761    * @return the space reference where the new document will be created
762    */
 
763  104 toggle public SpaceReference getSpaceReference()
764    {
765  104 return spaceReference;
766    }
767   
768    /**
769    * @return the name of the new document. See {@link #isSpace()}
770    */
 
771  104 toggle public String getName()
772    {
773  104 return name;
774    }
775   
776    /**
777    * @return true if the new document is a space (i.e. Nested Document and the name means space name) or false if it's
778    * a terminal regular document (i.e. Nested Spaces document and the name means document name)
779    */
 
780  162 toggle public boolean isSpace()
781    {
782  162 return isSpace;
783    }
784   
785    /**
786    * @return the available template providers for the space from where we are creating the new document
787    */
 
788  104 toggle public List<Document> getAvailableTemplateProviders()
789    {
790  104 return availableTemplateProviders;
791    }
792   
793    /**
794    * @return the recommended template providers, from the list of available ones
795    */
 
796  104 toggle public List<Document> getRecommendedTemplateProviders()
797    {
798  104 return recommendedTemplateProviders;
799    }
800   
801    /**
802    * @return the currently used template provider read from the request, or {@code null} if none was set
803    */
 
804  58 toggle public BaseObject getTemplateProvider()
805    {
806  58 return templateProvider;
807    }
808   
809    /**
810    * @return the type of document to create, read from the request, or {@code null} if none was set
811    * @since 7.2
812    */
 
813  69 toggle public String getType()
814    {
815  69 return type;
816    }
817    }