-
-
Notifications
You must be signed in to change notification settings - Fork 542
/
XWikiContext.java
809 lines (672 loc) · 22.2 KB
/
XWikiContext.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package com.xpn.xwiki;
import java.lang.reflect.ParameterizedType;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.inject.Provider;
import org.apache.commons.collections.map.LRUMap;
import org.apache.commons.lang3.StringUtils;
import org.apache.xmlrpc.server.XmlRpcServer;
import org.jfree.util.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xwiki.component.util.DefaultParameterizedType;
import org.xwiki.context.Execution;
import org.xwiki.context.ExecutionContext;
import org.xwiki.localization.LocaleUtils;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.DocumentReferenceResolver;
import org.xwiki.model.reference.EntityReferenceSerializer;
import org.xwiki.model.reference.SpaceReference;
import org.xwiki.model.reference.WikiReference;
import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.objects.classes.BaseClass;
import com.xpn.xwiki.user.api.XWikiRightService;
import com.xpn.xwiki.user.api.XWikiUser;
import com.xpn.xwiki.util.Util;
import com.xpn.xwiki.validation.XWikiValidationStatus;
import com.xpn.xwiki.web.Utils;
import com.xpn.xwiki.web.XWikiEngineContext;
import com.xpn.xwiki.web.XWikiForm;
import com.xpn.xwiki.web.XWikiMessageTool;
import com.xpn.xwiki.web.XWikiRequest;
import com.xpn.xwiki.web.XWikiResponse;
import com.xpn.xwiki.web.XWikiURLFactory;
public class XWikiContext extends Hashtable<Object, Object>
{
/**
* Type instance for Provider<XWikiContext>.
*
* @since 5.0M1
*/
public static final ParameterizedType TYPE_PROVIDER = new DefaultParameterizedType(null, Provider.class,
XWikiContext.class);
public static final int MODE_SERVLET = 0;
public static final int MODE_PORTLET = 1;
public static final int MODE_XMLRPC = 2;
public static final int MODE_ATOM = 3;
public static final int MODE_PDF = 4;
public static final int MODE_GWT = 5;
public static final int MODE_GWT_DEBUG = 6;
public static final String EXECUTIONCONTEXT_KEY = "xwikicontext";
/** Logging helper object. */
protected static final Logger LOGGER = LoggerFactory.getLogger(XWikiContext.class);
private static final String WIKI_KEY = "wiki";
private static final String ORIGINAL_WIKI_KEY = "originalWiki";
private static final String USER_KEY = "user";
private static final String USERREFERENCE_KEY = "userreference";
private boolean finished = false;
private XWiki wiki;
private XWikiEngineContext engine_context;
private XWikiRequest request;
private XWikiResponse response;
private XWikiForm form;
private String action;
private String orig_database;
private String database;
private DocumentReference userReference;
private Locale locale;
private static final String LANGUAGE_KEY = "language";
private String interfaceLanguage;
private int mode;
private URL url;
private XWikiURLFactory URLFactory;
private XmlRpcServer xmlRpcServer;
private int cacheDuration = 0;
private int classCacheSize = 20;
// Used to avoid recursive loading of documents if there are recursives usage of classes
// FIXME: why synchronized since a context is supposed to be tied to a thread ?
@SuppressWarnings("unchecked")
private Map<DocumentReference, BaseClass> classCache = Collections.synchronizedMap(new LRUMap(this.classCacheSize));
// FIXME: why synchronized since a context is supposed to be tied to a thread ?
private List<String> displayedFields = Collections.synchronizedList(new ArrayList<String>());
/**
* Used to resolve a string into a proper Document Reference using the current document's reference to fill the
* blanks, except for the page name for which the default page name is used instead and for the wiki name for which
* the current wiki is used instead of the current document reference's wiki.
*/
private DocumentReferenceResolver<String> currentMixedDocumentReferenceResolver = Utils.getComponent(
DocumentReferenceResolver.TYPE_STRING, "currentmixed");
/**
* Used to convert a proper Document Reference to a string but without the wiki name.
*/
private EntityReferenceSerializer<String> localEntityReferenceSerializer = Utils.getComponent(
EntityReferenceSerializer.TYPE_STRING, "local");
/**
* Used to convert a Document Reference to string (compact form without the wiki part if it matches the current
* wiki).
*/
private EntityReferenceSerializer<String> compactWikiEntityReferenceSerializer = Utils.getComponent(
EntityReferenceSerializer.TYPE_STRING, "compactwiki");
/** The Execution so that we can check if permissions were dropped there. */
private final Execution execution = Utils.getComponent(Execution.class);
public XWikiContext()
{
}
public XWiki getWiki()
{
return this.wiki;
}
public Util getUtil()
{
Util util = (Util) this.get("util");
if (util == null) {
util = new Util();
this.put("util", util);
}
return util;
}
public void setWiki(XWiki wiki)
{
this.wiki = wiki;
}
public XWikiEngineContext getEngineContext()
{
return this.engine_context;
}
public void setEngineContext(XWikiEngineContext engine_context)
{
this.engine_context = engine_context;
}
public XWikiRequest getRequest()
{
return this.request;
}
public void setRequest(XWikiRequest request)
{
this.request = request;
}
public String getAction()
{
return this.action;
}
public void setAction(String action)
{
this.action = action;
}
public XWikiResponse getResponse()
{
return this.response;
}
public void setResponse(XWikiResponse response)
{
this.response = response;
}
public String getDatabase()
{
return this.database;
}
public void setDatabase(String database)
{
this.database = database;
if (database == null) {
super.remove(WIKI_KEY);
} else {
super.put(WIKI_KEY, database);
}
if (this.orig_database == null) {
this.orig_database = database;
if (database == null) {
super.remove(ORIGINAL_WIKI_KEY);
} else {
super.put(ORIGINAL_WIKI_KEY, database);
}
}
}
/**
* {@inheritDoc}
* <p>
* Make sure to keep {@link #database} fields and map synchronized.
*
* @see java.util.Hashtable#put(java.lang.Object, java.lang.Object)
*/
@Override
public synchronized Object put(Object key, Object value)
{
Object previous;
if (WIKI_KEY.equals(key)) {
previous = get(WIKI_KEY);
setDatabase((String) value);
} else {
if (value != null) {
previous = super.put(key, value);
} else {
previous = super.remove(key);
}
}
return previous;
}
/**
* {@inheritDoc}
* <p>
* Make sure to keep {@link #database} field and map synchronized.
*
* @see java.util.Hashtable#remove(java.lang.Object)
*/
@Override
public synchronized Object remove(Object key)
{
Object previous;
if (WIKI_KEY.equals(key)) {
previous = get(WIKI_KEY);
setDatabase(null);
} else {
previous = super.remove(key);
}
return previous;
}
/**
* Get the "original" database name. In single wiki mode this will be "xwiki", but in virtual wiki mode this will be
* the database name for the wiki which the user requested. If the database is switched to load some piece of data,
* this will remember what it should be switched back to.
*
* @return the db name originally requested by the user.
*/
public String getOriginalDatabase()
{
return this.orig_database;
}
public void setOriginalDatabase(String database)
{
this.orig_database = database;
if (database == null) {
remove(ORIGINAL_WIKI_KEY);
} else {
put(ORIGINAL_WIKI_KEY, database);
}
}
/**
* @return true it's main wiki's context, false otherwise.
*/
public boolean isMainWiki()
{
return isMainWiki(getDatabase());
}
/**
* @param wikiName the name of the wiki.
* @return true it's main wiki's context, false otherwise.
*/
public boolean isMainWiki(String wikiName)
{
return StringUtils.equalsIgnoreCase(wikiName, getMainXWiki());
}
public XWikiDocument getDoc()
{
return (XWikiDocument) get("doc");
}
public void setDoc(XWikiDocument doc)
{
if (doc == null) {
remove("doc");
} else {
put("doc", doc);
}
}
public DocumentReference getUserReference()
{
return this.userReference;
}
public void setUserReference(DocumentReference userReference)
{
if (userReference == null) {
this.userReference = null;
remove(USER_KEY);
remove(USERREFERENCE_KEY);
} else {
this.userReference = new DocumentReference(userReference);
boolean ismain = isMainWiki(this.userReference.getWikiReference().getName());
put(USER_KEY, new XWikiUser(getUser(), ismain));
put(USERREFERENCE_KEY, this.userReference);
// Log this since it's probably a mistake so that we find who is doing bad things
if (this.userReference.getName().equals(XWikiRightService.GUEST_USER)) {
Log.warn("A reference to XWikiGuest user as been set instead of null. This is probably a mistake.",
new Exception("See stack trace"));
}
}
}
private void setUserInternal(String user, boolean main)
{
if (user == null) {
setUserReference(null);
} else if (user.endsWith(XWikiRightService.GUEST_USER_FULLNAME) || user.equals(XWikiRightService.GUEST_USER)) {
setUserReference(null);
// retro-compatibilty hack: some code does not give the same meaning to null XWikiUser and XWikiUser
// containing guest user
put(USER_KEY, new XWikiUser(user, main));
} else {
setUserReference(resolveUserReference(user));
}
}
/**
* Make sure to use "XWiki" as default space when it's not provided in user name.
*/
private DocumentReference resolveUserReference(String user)
{
return this.currentMixedDocumentReferenceResolver.resolve(user, new SpaceReference("XWiki", new WikiReference(
getDatabase() == null ? "xwiki" : getDatabase())));
}
/**
* @deprecated since 3.1M1 use {@link #setUserReference(DocumentReference)} instead
*/
@Deprecated
public void setUser(String user)
{
setUserInternal(user, false);
}
/**
* @deprecated since 3.1M1 use {@link #getUserReference()} instead
*/
@Deprecated
public String getUser()
{
if (this.userReference != null) {
if (getDatabase() == null) {
return this.localEntityReferenceSerializer.serialize(this.userReference);
} else {
return this.compactWikiEntityReferenceSerializer.serialize(this.userReference, new WikiReference(
getDatabase()));
}
} else {
return XWikiRightService.GUEST_USER_FULLNAME;
}
}
/**
* @deprecated since 3.1M1 use {@link #getUserReference()} instead
*/
@Deprecated
public String getLocalUser()
{
if (this.userReference != null) {
return this.localEntityReferenceSerializer.serialize(this.userReference);
} else {
return XWikiRightService.GUEST_USER_FULLNAME;
}
}
/**
* @deprecated since 3.1M1 use {@link #getUserReference()} instead
*/
@Deprecated
public XWikiUser getXWikiUser()
{
if (this.userReference != null) {
boolean ismain = isMainWiki(this.userReference.getWikiReference().getName());
return new XWikiUser(getUser(), ismain);
}
return (XWikiUser) get(USER_KEY);
}
/**
* @deprecated since 4.3M1 use {@link #getLocale()} instead
*/
@Deprecated
public String getLanguage()
{
return this.locale != null ? this.locale.toString() : null;
}
/**
* @deprecated since 4.3M1 use {@link #setLocale(Locale)} instead
*/
@Deprecated
public void setLanguage(String language)
{
setLocale(LocaleUtils.toLocale(Util.normalizeLanguage(language)));
}
/**
* @return the current locale
*/
public Locale getLocale()
{
return this.locale;
}
/**
* @param locale the current locale
*/
public void setLocale(Locale locale)
{
this.locale = locale;
if (locale == null) {
remove(LANGUAGE_KEY);
} else {
put(LANGUAGE_KEY, locale.toString());
}
}
public String getInterfaceLanguage()
{
return this.interfaceLanguage;
}
public void setInterfaceLanguage(String interfaceLanguage)
{
this.interfaceLanguage = interfaceLanguage;
}
public int getMode()
{
return this.mode;
}
public void setMode(int mode)
{
this.mode = mode;
}
public URL getURL()
{
return this.url;
}
public void setURL(URL url)
{
this.url = url;
}
public XWikiURLFactory getURLFactory()
{
return this.URLFactory;
}
public void setURLFactory(XWikiURLFactory URLFactory)
{
this.URLFactory = URLFactory;
}
public XWikiForm getForm()
{
return this.form;
}
public void setForm(XWikiForm form)
{
this.form = form;
}
public boolean isFinished()
{
return this.finished;
}
public void setFinished(boolean finished)
{
this.finished = finished;
}
public XmlRpcServer getXMLRPCServer()
{
return this.xmlRpcServer;
}
public void setXMLRPCServer(XmlRpcServer xmlRpcServer)
{
this.xmlRpcServer = xmlRpcServer;
}
/**
* @deprecated never made any sense since the context database can change any time
*/
@Deprecated
public void setWikiOwner(String wikiOwner)
{
// Cannot do anything
}
/**
* @deprecated use {@link XWiki#getWikiOwner(String, XWikiContext)} instead
*/
@Deprecated
public String getWikiOwner()
{
try {
return getWiki().getWikiOwner(getDatabase(), this);
} catch (XWikiException e) {
LOGGER.error("Failed to get owner for wiki [{}]", getDatabase(), e);
}
return null;
}
/**
* @deprecated never made any sense since the context database can change any time
*/
@Deprecated
public void setWikiServer(XWikiDocument doc)
{
// Cannot do anything
}
public XWikiDocument getWikiServer()
{
String currentWiki = getDatabase();
try {
setDatabase(getMainXWiki());
return getWiki().getDocument(XWiki.getServerWikiPage(currentWiki), this);
} catch (XWikiException e) {
LOGGER.error("Failed to get wiki descriptor for wiki [{}]", currentWiki, e);
} finally {
setDatabase(currentWiki);
}
return null;
}
public int getCacheDuration()
{
return this.cacheDuration;
}
public void setCacheDuration(int cacheDuration)
{
this.cacheDuration = cacheDuration;
}
public String getMainXWiki()
{
return (String) get("mainxwiki");
}
public void setMainXWiki(String str)
{
put("mainxwiki", str);
}
// Used to avoid recursive loading of documents if there are recursives usage of classes
public void addBaseClass(BaseClass bclass)
{
this.classCache.put(bclass.getDocumentReference(), bclass);
}
/**
* @since 2.2M2
*/
// Used to avoid recursive loading of documents if there are recursives usage of classes
public BaseClass getBaseClass(DocumentReference documentReference)
{
return this.classCache.get(documentReference);
}
/**
* Empty the class cache.
*/
public void flushClassCache()
{
this.classCache.clear();
}
public void setLinksAction(String action)
{
put("links_action", action);
}
public void unsetLinksAction()
{
remove("links_action");
}
public String getLinksAction()
{
return (String) get("links_action");
}
public void setLinksQueryString(String value)
{
put("links_qs", value);
}
public void unsetLinksQueryString()
{
remove("links_qs");
}
public String getLinksQueryString()
{
return (String) get("links_qs");
}
public XWikiMessageTool getMessageTool()
{
XWikiMessageTool msg = ((XWikiMessageTool) get("msg"));
if (msg == null) {
getWiki().prepareResources(this);
msg = ((XWikiMessageTool) get("msg"));
}
return msg;
}
public XWikiValidationStatus getValidationStatus()
{
return (XWikiValidationStatus) get("validation_status");
}
public void setValidationStatus(XWikiValidationStatus status)
{
put("validation_status", status);
}
public void addDisplayedField(String fieldname)
{
this.displayedFields.add(fieldname);
}
public List<String> getDisplayedFields()
{
return this.displayedFields;
}
public String getEditorWysiwyg()
{
return (String) get("editor_wysiwyg");
}
/**
* Drop permissions for the remainder of the request cycle.
* <p>
* After this is called:
* <ul>
* <li>1. {@link com.xpn.xwiki.api.Api#hasProgrammingRights()} will always return false.</li>
* <li>2. {@link com.xpn.xwiki.api.XWiki#getDocumentAsAuthor(org.xwiki.model.reference.DocumentReference)},
* {@link com.xpn.xwiki.api.XWiki#getDocumentAsAuthor(String)}, {@link com.xpn.xwiki.api.Document#saveAsAuthor()},
* {@link com.xpn.xwiki.api.Document#saveAsAuthor(String)},
* {@link com.xpn.xwiki.api.Document#saveAsAuthor(String, boolean)}, and
* {@link com.xpn.xwiki.api.Document#deleteAsAuthor()} will perform all of their actions as if the document's
* content author was the guest user (XWiki.XWikiGuest).</li>
* </ul>
* <p>
* In effect, no code requiring "programming right" will run, and if the document content author (see:
* {@link com.xpn.xwiki.api.Document#getContentAuthor()}) is a user who has "programming right", there will be no
* way for code following this call to save another document as this user, blessing it too with programming right.
* <p>
* Once dropped, permissions cannot be regained for the duration of the request.
* <p>
* If you are interested in a more flexable sandboxing method which sandboxed code only for the remainder of the
* rendering cycle, consider using {@link com.xpn.xwiki.api.Document#dropPermissions()}.
*
* @since 3.0M3
*/
public void dropPermissions()
{
this.put(XWikiConstant.DROPPED_PERMISSIONS, Boolean.TRUE);
}
/**
* @return true if {@link XWikiContext#dropPermissions()} has been called on this context, or if the
* {@link XWikiConstant#DROPPED_PERMISSIONS} key has been set in the
* {@link org.xwiki.context.ExecutionContext} for this thread. This is done by calling
* {Document#dropPermissions()}
*/
public boolean hasDroppedPermissions()
{
if (this.get(XWikiConstant.DROPPED_PERMISSIONS) != null) {
return true;
}
final Object dropped = this.execution.getContext().getProperty(XWikiConstant.DROPPED_PERMISSIONS);
if (dropped == null || !(dropped instanceof Integer)) {
return false;
}
return ((Integer) dropped) == System.identityHashCode(this.execution.getContext());
}
// Object
@Override
public synchronized XWikiContext clone()
{
XWikiContext context = (XWikiContext) super.clone();
// Make sure to have unique instances of the various caches
context.displayedFields = Collections.synchronizedList(new ArrayList<String>(this.displayedFields));
context.classCache = Collections.synchronizedMap(new LRUMap(this.classCacheSize));
return context;
}
/**
* There are several places where the XWiki context needs to be declared in the execution, so we add a common method
* here.
*
* @param executionContext The execution context.
*/
public void declareInExecutionContext(ExecutionContext executionContext)
{
if (!executionContext.hasProperty(XWikiContext.EXECUTIONCONTEXT_KEY)) {
executionContext.newProperty(XWikiContext.EXECUTIONCONTEXT_KEY).initial(this).inherited().declare();
} else {
executionContext.setProperty(XWikiContext.EXECUTIONCONTEXT_KEY, this);
}
}
}