1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.extension.script

File ExtensionManagerScriptService.java

 

Coverage histogram

../../../../img/srcFileCovDistChart6.png
69% of files have more coverage

Code metrics

24
185
48
1
1,043
459
74
0.4
3.85
48
1.54

Classes

Class Line # Actions
ExtensionManagerScriptService 84 185 0% 74 119
0.5369649553.7%
 

Contributing tests

This file is covered by 10 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 org.xwiki.extension.script;
21   
22    import java.util.Collection;
23    import java.util.Map;
24   
25    import javax.inject.Inject;
26    import javax.inject.Named;
27    import javax.inject.Singleton;
28   
29    import org.apache.commons.lang3.StringUtils;
30    import org.xwiki.component.annotation.Component;
31    import org.xwiki.component.namespace.NamespaceValidator;
32    import org.xwiki.extension.CoreExtension;
33    import org.xwiki.extension.Extension;
34    import org.xwiki.extension.ExtensionDependency;
35    import org.xwiki.extension.ExtensionId;
36    import org.xwiki.extension.ExtensionManager;
37    import org.xwiki.extension.InstalledExtension;
38    import org.xwiki.extension.LocalExtension;
39    import org.xwiki.extension.internal.ExtensionFactory;
40    import org.xwiki.extension.internal.validator.AbstractExtensionValidator;
41    import org.xwiki.extension.job.ExtensionRequest;
42    import org.xwiki.extension.job.InstallRequest;
43    import org.xwiki.extension.job.UninstallRequest;
44    import org.xwiki.extension.job.internal.AbstractExtensionJob;
45    import org.xwiki.extension.job.internal.InstallJob;
46    import org.xwiki.extension.job.internal.InstallPlanJob;
47    import org.xwiki.extension.job.internal.UninstallJob;
48    import org.xwiki.extension.job.internal.UninstallPlanJob;
49    import org.xwiki.extension.job.internal.UpgradePlanJob;
50    import org.xwiki.extension.repository.ExtensionRepository;
51    import org.xwiki.extension.repository.ExtensionRepositoryManager;
52    import org.xwiki.extension.repository.result.IterableResult;
53    import org.xwiki.extension.repository.search.ExtensionQuery;
54    import org.xwiki.extension.version.Version;
55    import org.xwiki.extension.version.VersionConstraint;
56    import org.xwiki.extension.version.VersionRange;
57    import org.xwiki.extension.version.internal.DefaultVersion;
58    import org.xwiki.extension.version.internal.DefaultVersionRange;
59    import org.xwiki.job.Job;
60    import org.xwiki.job.JobException;
61    import org.xwiki.job.JobGroupPath;
62    import org.xwiki.job.event.status.JobStatus;
63    import org.xwiki.model.reference.DocumentReference;
64    import org.xwiki.script.service.ScriptService;
65    import org.xwiki.script.service.ScriptServiceManager;
66    import org.xwiki.security.authorization.Right;
67   
68    import com.xpn.xwiki.XWikiContext;
69    import com.xpn.xwiki.doc.XWikiDocument;
70   
71    /**
72    * Entry point of extension manager from scripts.
73    * <p>
74    * Namespaces are ways to isolate extensions in a particular context, they are generally prefixed with the type of
75    * context. For example to install an extension in a namespace linked to a particular wiki the namespace is prefixed
76    * with <code>wiki:</code> which gives for the wiki <code>wiki1</code>: <code>wiki:wiki1</code>.
77    *
78    * @version $Id: 7213c7662802b93102261f1e42182d62aa22be86 $
79    * @since 2.5M2
80    */
81    @Component
82    @Named(ExtensionManagerScriptService.ROLEHINT)
83    @Singleton
 
84    public class ExtensionManagerScriptService extends AbstractExtensionScriptService
85    {
86    /**
87    * The role hint of this component.
88    */
89    public static final String ROLEHINT = "extension";
90   
91    /**
92    * The prefix put behind all job ids.
93    *
94    * @deprecated since 8.2RC1, use {@link ExtensionRequest#JOBID_PREFIX} instead
95    */
96    @Deprecated
97    public static final String EXTENSION_JOBID_PREFIX = ROLEHINT;
98   
99    /**
100    * The prefix put behind all job ids which are actual actions.
101    *
102    * @deprecated since 8.2RC1, use {@link ExtensionRequest#JOBID_ACTION_PREFIX} instead
103    */
104    @Deprecated
105    public static final String EXTENSIONACTION_JOBID_PREFIX = "action";
106   
107    /**
108    * The prefix put behind all job ids which are information gathering.
109    *
110    * @deprecated since 8.2RC1, use {@link ExtensionRequest#JOBID_PLAN_PREFIX} instead
111    */
112    @Deprecated
113    public static final String EXTENSIONPLAN_JOBID_PREFIX = "plan";
114   
115    /**
116    * This property is set on requests to create an install or uninstall plan in order to specify which type of job
117    * generated the plan.
118    */
119    private static final String PROPERTY_JOB_TYPE = "job.type";
120   
121    /**
122    * The real extension manager bridged by this script service.
123    */
124    @Inject
125    private ExtensionManager extensionManager;
126   
127    /**
128    * Repository manager, needed for cross-repository operations.
129    */
130    @Inject
131    private ExtensionRepositoryManager repositoryManager;
132   
133    @Inject
134    private ScriptServiceManager scriptServiceManager;
135   
136    @Inject
137    private NamespaceValidator namespaceResolver;
138   
139    @Inject
140    private ExtensionFactory factory;
141   
142    /**
143    * @param <S> the type of the {@link ScriptService}
144    * @param serviceName the name of the sub {@link ScriptService}
145    * @return the {@link ScriptService} or null of none could be found
146    */
 
147  1180 toggle @SuppressWarnings("unchecked")
148    public <S extends ScriptService> S get(String serviceName)
149    {
150  1180 return (S) this.scriptServiceManager.get(ExtensionManagerScriptService.ROLEHINT + '.' + serviceName);
151    }
152   
153    // Repositories
154   
155    /**
156    * @return all the remote repositories
157    */
 
158  1 toggle public Collection<ExtensionRepository> getRepositories()
159    {
160  1 return safe(this.repositoryManager.getRepositories());
161    }
162   
163    /**
164    * @param repositoryId the identifier of the remote repository
165    * @return the repository
166    */
 
167  476 toggle public ExtensionRepository getRepository(String repositoryId)
168    {
169  476 return safe(this.extensionManager.getRepository(repositoryId));
170    }
171   
172    // Extensions
173   
174    /**
175    * Search among all remote (those listed in xwiki.properties)
176    * {@link org.xwiki.extension.repository.search.Searchable} repositories for extensions matching the search terms.
177    *
178    * @param pattern the words to search for
179    * @param offset the offset from where to start returning search results, 0-based
180    * @param nb the maximum number of search results to return. -1 indicate no limit. 0 indicate that no result will be
181    * returned but it can be used to get the total hits.
182    * @return the found extensions descriptors, empty list if nothing could be found and null if an expected error has
183    * been catched
184    * @see org.xwiki.extension.repository.search.Searchable
185    */
 
186  15 toggle public IterableResult<Extension> search(String pattern, int offset, int nb)
187    {
188  15 setError(null);
189   
190  15 IterableResult<Extension> result = null;
191   
192  15 try {
193  15 return this.repositoryManager.search(pattern, offset, nb);
194    } catch (Exception e) {
195  0 setError(e);
196    }
197   
198  0 return result;
199    }
200   
201    /**
202    * Search among all remote (those listed in xwiki.properties)
203    * {@link org.xwiki.extension.repository.search.AdvancedSearchable} repositories for extensions matching the search
204    * query.
205    *
206    * @param query the search query
207    * @return the found extensions descriptors, empty list if nothing could be found and null if an expected error has
208    * been catched
209    * @see org.xwiki.extension.repository.search.Searchable
210    * @since 7.1RC1
211    */
 
212  0 toggle public IterableResult<Extension> search(ExtensionQuery query)
213    {
214  0 setError(null);
215   
216  0 IterableResult<Extension> result = null;
217   
218  0 try {
219  0 result = this.repositoryManager.search(query);
220    } catch (Exception e) {
221  0 setError(e);
222    }
223   
224  0 return result;
225    }
226   
227    /**
228    * Create a new instance of a {@link ExtensionQuery} to be used in other APIs.
229    *
230    * @param query the query to execute
231    * @return a {@link ExtensionQuery} instance
232    * @since 7.1RC1
233    */
 
234  33 toggle public ExtensionQuery newQuery(String query)
235    {
236  33 return new ExtensionQuery(query);
237    }
238   
239    /**
240    * Get the extension handler corresponding to the given extension ID and version. The returned handler can be used
241    * to get more information about the extension, such as the authors, an extension description, its license...
242    *
243    * @param id the extension id or provided feature (virtual extension) of the extension to resolve
244    * @param version the specific version to resolve
245    * @return the read-only handler corresponding to the requested extension, or {@code null} if the extension couldn't
246    * be resolved, in which case {@link #getLastError()} contains the failure reason
247    */
 
248  243 toggle public Extension resolve(String id, String version)
249    {
250  243 setError(null);
251   
252  243 Extension extension = null;
253   
254  243 try {
255  243 extension = safe(this.extensionManager.resolveExtension(new ExtensionId(id, version)));
256    } catch (Exception e) {
257  2 setError(e);
258    }
259   
260  243 return extension;
261    }
262   
263    /**
264    * Get the extension handler corresponding to the given extension ID and version. The returned handler can be used
265    * to get more information about the extension, such as the authors, an extension description, its license...
266    *
267    * @param extensionDependency the extension dependency to resolve
268    * @return the read-only handler corresponding to the requested extension, or {@code null} if the extension couldn't
269    * be resolved, in which case {@link #getLastError()} contains the failure reason
270    * @since 3.4M1
271    * @deprecated since 5.3M1, use {@link #resolve(ExtensionDependency, String)} instead
272    */
 
273  0 toggle @Deprecated
274    public Extension resolve(ExtensionDependency extensionDependency)
275    {
276  0 setError(null);
277   
278  0 Extension extension = null;
279   
280  0 try {
281  0 extension = safe(this.extensionManager.resolveExtension(extensionDependency));
282    } catch (Exception e) {
283  0 setError(e);
284    }
285   
286  0 return extension;
287    }
288   
289    /**
290    * Search the provided extension as a dependency of another extension among all repositories including core and
291    * local repositories.
292    * <p>
293    * The search is done in the following order:
294    * <ul>
295    * <li>Is it a core extension ?</li>
296    * <li>Is it a local extension ?</li>
297    * <li>Is this feature installed in current namespace or parent ?</li>
298    * <li>Is it a remote extension in one of the configured remote repositories ?</li>
299    * </ul>
300    * The first one found is returned.
301    *
302    * @param extensionDependency the extension dependency to resolve
303    * @param namespace the namespace where to search for the dependency
304    * @return the read-only handler corresponding to the requested extension, or {@code null} if the extension couldn't
305    * be resolved, in which case {@link #getLastError()} contains the failure reason
306    * @since 5.3M1
307    */
 
308  9 toggle public Extension resolve(ExtensionDependency extensionDependency, String namespace)
309    {
310  9 setError(null);
311   
312  9 Extension extension = null;
313   
314  9 try {
315  9 extension = safe(this.extensionManager.resolveExtension(extensionDependency, namespace));
316    } catch (Exception e) {
317  4 setError(e);
318    }
319   
320  9 return extension;
321    }
322   
323    /**
324    * Return ordered (ascendent) versions for the provided extension id.
325    *
326    * @param id the id of the extensions for which to return versions
327    * @param offset the offset from where to start returning versions
328    * @param nb the maximum number of versions to return
329    * @return the versions of the provided extension id
330    */
 
331  0 toggle public IterableResult<Version> resolveVersions(String id, int offset, int nb)
332    {
333  0 setError(null);
334   
335  0 IterableResult<Version> versions = null;
336   
337  0 try {
338  0 versions = this.repositoryManager.resolveVersions(id, offset, nb);
339    } catch (Exception e) {
340  0 setError(e);
341    }
342   
343  0 return versions;
344    }
345   
346    // Actions
347   
348    /**
349    * Create an {@link InstallRequest} instance based on passed parameters.
350    *
351    * @param id the identifier of the extension to install
352    * @param version the version to install
353    * @param namespace the (optional) namespace where to install the extension; if {@code null} or empty, the extension
354    * will be installed globally
355    * @return the {@link InstallRequest}
356    */
 
357  19 toggle public InstallRequest createInstallRequest(String id, String version, String namespace)
358    {
359  19 InstallRequest installRequest = createInstallPlanRequest(id, version, namespace);
360   
361  19 installRequest.setId(ExtensionRequest.getJobId(ExtensionRequest.JOBID_ACTION_PREFIX, id, namespace));
362  19 installRequest.setInteractive(true);
363  19 installRequest.setProperty(PROPERTY_JOB_TYPE, InstallJob.JOBTYPE);
364  19 DocumentReference currentUserReference = this.documentAccessBridge.getCurrentUserReference();
365  19 if (currentUserReference != null) {
366    // We set the string value because the extension repository doesn't know how to serialize/parse an extension
367    // property whose value is a DocumentReference, and adding support for it requires considerable refactoring
368    // because ExtensionPropertySerializers are not components (they are currently hard-coded).
369  19 installRequest.setExtensionProperty(AbstractExtensionValidator.PROPERTY_USERREFERENCE,
370    currentUserReference.toString());
371    }
372   
373  19 return installRequest;
374    }
375   
376    /**
377    * Start the asynchronous installation process for an extension if the context document has programming rights.
378    *
379    * @param id the identifier of the extension to install
380    * @param version the version to install
381    * @param namespace the (optional) namespace where to install the extension; if {@code null} or empty, the extension
382    * will be installed globally
383    * @return the {@link Job} object which can be used to monitor the progress of the installation process, or
384    * {@code null} in case of failure
385    */
 
386  5 toggle public Job install(String id, String version, String namespace)
387    {
388  5 return install(createInstallRequest(id, version, namespace));
389    }
390   
391    /**
392    * Start the asynchronous installation process for an extension if the context document has programming rights.
393    *
394    * @param installRequest installation instructions
395    * @return the {@link Job} object which can be used to monitor the progress of the installation process, or
396    * {@code null} in case of failure
397    */
 
398  19 toggle public Job install(InstallRequest installRequest)
399    {
400  19 setError(null);
401   
402  19 if (!this.authorization.hasAccess(Right.PROGRAM)) {
403    // Make sure only PR user can remove the right checking or change the users
404  5 setRightsProperties(installRequest);
405    }
406   
407  19 Job job = null;
408  19 try {
409  19 job = this.jobExecutor.execute(InstallJob.JOBTYPE, installRequest);
410    } catch (JobException e) {
411  0 setError(e);
412    }
413   
414  19 return job;
415    }
416   
417    /**
418    * Create an {@link InstallRequest} instance based on given parameters, to be used to create the install plan.
419    *
420    * @param id the identifier of the extension to install
421    * @param version the version to install
422    * @param namespace the (optional) namespace where to install the extension; if {@code null} or empty, the extension
423    * will be installed globally
424    * @return the {@link InstallRequest}
425    */
 
426  27 toggle public InstallRequest createInstallPlanRequest(String id, String version, String namespace)
427    {
428  27 InstallRequest installRequest = new InstallRequest();
429  27 installRequest.setId(ExtensionRequest.getJobId(ExtensionRequest.JOBID_PLAN_PREFIX, id, namespace));
430  27 installRequest.addExtension(new ExtensionId(id, version));
431  27 if (StringUtils.isNotBlank(namespace)) {
432  25 installRequest.addNamespace(namespace);
433    }
434   
435  27 XWikiContext xcontext = this.xcontextProvider.get();
436   
437    // Indicate if it's allowed to do modification on root namespace
438  27 installRequest.setRootModificationsAllowed(namespace == null || xcontext.isMainWiki(toWikiId(namespace)));
439   
440    // Allow overwritting a few things in extensions descriptors
441  27 installRequest.setRewriter(new ScriptExtensionRewriter());
442   
443    // Provide informations on what started the job
444  27 installRequest.setProperty(PROPERTY_CONTEXT_WIKI, xcontext.getWikiId());
445  27 installRequest.setProperty(PROPERTY_CONTEXT_ACTION, xcontext.getAction());
446   
447  27 setRightsProperties(installRequest);
448   
449  27 installRequest.setProperty(PROPERTY_JOB_TYPE, InstallPlanJob.JOBTYPE);
450   
451  27 return installRequest;
452    }
453   
454    /**
455    * Start the asynchronous installation plan creation process for an extension.
456    *
457    * @param installRequest installation instructions
458    * @return the {@link Job} object which can be used to monitor the progress of the installation process, or
459    * {@code null} in case of failure
460    */
 
461  8 toggle public Job createInstallPlan(InstallRequest installRequest)
462    {
463  8 setError(null);
464   
465  8 if (!this.authorization.hasAccess(Right.PROGRAM)) {
466    // Make sure only PR user can remove the right checking or change the users
467  0 setRightsProperties(installRequest);
468    }
469   
470  8 Job job = null;
471  8 try {
472  8 job = this.jobExecutor.execute(InstallPlanJob.JOBTYPE, installRequest);
473    } catch (JobException e) {
474  0 setError(e);
475    }
476   
477  8 return job;
478    }
479   
480    /**
481    * Start the asynchronous installation plan creation process for an extension.
482    *
483    * @param id the identifier of the extension to install
484    * @param version the version to install
485    * @param namespace the (optional) namespace where to install the extension; if {@code null} or empty, the extension
486    * will be installed globally
487    * @return the {@link Job} object which can be used to monitor the progress of the installation process, or
488    * {@code null} in case of failure
489    */
 
490  0 toggle public Job createInstallPlan(String id, String version, String namespace)
491    {
492  0 return createInstallPlan(createInstallPlanRequest(id, version, namespace));
493    }
494   
495    /**
496    * Start the asynchronous uninstall process for an extension if the context document has programming rights.
497    * <p>
498    * Only uninstall from the provided namespace.
499    *
500    * @param id the identifier of the extension to remove
501    * @param namespace the (optional) namespace from where to uninstall the extension; if {@code null} or empty, the
502    * extension will be installed globally
503    * @return the {@link Job} object which can be used to monitor the progress of the uninstallation process, or
504    * {@code null} in case of failure
505    */
 
506  7 toggle public Job uninstall(String id, String namespace)
507    {
508  7 return uninstall(createUninstallRequest(id, namespace));
509    }
510   
511    /**
512    * Start the asynchronous uninstall process for an extension if the context document has programming rights.
513    * <p>
514    * Uninstall from all namespaces.
515    *
516    * @param extensionId the identifier of the extension to remove
517    * @return the {@link Job} object which can be used to monitor the progress of the uninstallation process, or
518    * {@code null} in case of failure
519    */
 
520  0 toggle public Job uninstall(ExtensionId extensionId)
521    {
522  0 return uninstall(createUninstallRequest(extensionId, null));
523    }
524   
525    /**
526    * Adds a new job to the job queue to perform the given uninstall request.
527    * <p>
528    * This method requires programming rights.
529    *
530    * @param uninstallRequest the uninstall request to perform
531    * @return the {@link Job} object which can be used to monitor the progress of the uninstall process, or
532    * {@code null} in case of failure
533    */
 
534  15 toggle public Job uninstall(UninstallRequest uninstallRequest)
535    {
536  15 setError(null);
537   
538  15 if (!this.authorization.hasAccess(Right.PROGRAM)) {
539    // Make sure only PR user can remove the right checking or change the users
540  4 setRightsProperties(uninstallRequest);
541    }
542   
543  15 Job job = null;
544  15 try {
545  15 job = this.jobExecutor.execute(UninstallJob.JOBTYPE, uninstallRequest);
546    } catch (JobException e) {
547  0 setError(e);
548    }
549   
550  15 return job;
551    }
552   
553    /**
554    * Create an {@link UninstallRequest} instance based on passed parameters.
555    *
556    * @param id the identifier of the extension to uninstall
557    * @param namespace the (optional) namespace from where to uninstall the extension; if {@code null} or empty, the
558    * extension will be uninstalled globally
559    * @return the {@link UninstallRequest}
560    */
 
561  15 toggle public UninstallRequest createUninstallRequest(String id, String namespace)
562    {
563  15 return createUninstallRequest(new ExtensionId(id, (Version) null), namespace);
564    }
565   
 
566  15 toggle private UninstallRequest createUninstallRequest(ExtensionId extensionId, String namespace)
567    {
568  15 UninstallRequest uninstallRequest = createUninstallPlanRequest(extensionId, namespace);
569   
570  15 uninstallRequest
571    .setId(ExtensionRequest.getJobId(ExtensionRequest.JOBID_ACTION_PREFIX, extensionId.getId(), namespace));
572  15 uninstallRequest.setInteractive(true);
573  15 uninstallRequest.setProperty(PROPERTY_JOB_TYPE, UninstallJob.JOBTYPE);
574   
575  15 return uninstallRequest;
576    }
577   
578    /**
579    * Create an {@link UninstallRequest} instance based on passed parameters.
580    *
581    * @param extensionId the identifier of the extension to uninstall
582    * @param namespace the (optional) namespace from where to uninstall the extension; if {@code null} or empty, the
583    * extension will be uninstalled globally
584    * @return the {@link UninstallRequest}
585    */
 
586  18 toggle private UninstallRequest createUninstallPlanRequest(ExtensionId extensionId, String namespace)
587    {
588  18 UninstallRequest uninstallRequest = new UninstallRequest();
589  18 uninstallRequest
590    .setId(ExtensionRequest.getJobId(ExtensionRequest.JOBID_PLAN_PREFIX, extensionId.getId(), namespace));
591  18 uninstallRequest.addExtension(extensionId);
592  18 if (StringUtils.isNotBlank(namespace)) {
593  16 uninstallRequest.addNamespace(namespace);
594    }
595   
596    // Indicate if it's allowed to do modification on root namespace
597  18 uninstallRequest.setRootModificationsAllowed(
598    namespace == null || this.xcontextProvider.get().isMainWiki(toWikiId(namespace)));
599   
600    // Provide informations on what started the job
601  18 uninstallRequest.setProperty(PROPERTY_CONTEXT_WIKI, this.xcontextProvider.get().getWikiId());
602  18 uninstallRequest.setProperty(PROPERTY_CONTEXT_ACTION, this.xcontextProvider.get().getAction());
603   
604  18 setRightsProperties(uninstallRequest);
605   
606  18 uninstallRequest.setProperty(PROPERTY_JOB_TYPE, UninstallPlanJob.JOBTYPE);
607   
608  18 return uninstallRequest;
609    }
610   
611    /**
612    * Start the asynchronous uninstallation plan creation process for an extension.
613    * <p>
614    * Only uninstall from the provided namespace.
615    *
616    * @param id the identifier of the extension that is going to be removed
617    * @param namespace the (optional) namespace from where to uninstall the extension; if {@code null} or empty, the
618    * extension will be removed from all namespaces
619    * @return the {@link Job} object which can be used to monitor the progress of the installation process, or
620    * {@code null} in case of failure
621    */
 
622  3 toggle public Job createUninstallPlan(String id, String namespace)
623    {
624  3 return createUninstallPlan(createUninstallPlanRequest(new ExtensionId(id, (Version) null), namespace));
625    }
626   
627    /**
628    * Start the asynchronous uninstallation plan creation process for an extension if no other job is in progress
629    * already.
630    * <p>
631    * Uninstall from all namespaces.
632    *
633    * @param extensionId the identifier of the extension that is going to be removed
634    * @return the {@link Job} object which can be used to monitor the progress of the installation process, or
635    * {@code null} in case of failure
636    */
 
637  0 toggle public Job createUninstallPlan(ExtensionId extensionId)
638    {
639  0 return createUninstallPlan(createUninstallPlanRequest(extensionId, null));
640    }
641   
642    /**
643    * Adds a new job to the job queue to perform the given uninstall plan request.
644    * <p>
645    * This method requires programming rights.
646    *
647    * @param uninstallRequest the uninstall plan request to perform
648    * @return the {@link Job} object which can be used to monitor the progress of the uninstall plan process, or
649    * {@code null} in case of failure
650    */
 
651  3 toggle private Job createUninstallPlan(UninstallRequest uninstallRequest)
652    {
653  3 setError(null);
654   
655  3 if (!this.authorization.hasAccess(Right.PROGRAM)) {
656    // Make sure only PR user can remove the right checking or change the users
657  0 setRightsProperties(uninstallRequest);
658    }
659   
660  3 Job job = null;
661  3 try {
662  3 job = this.jobExecutor.execute(UninstallPlanJob.JOBTYPE, uninstallRequest);
663    } catch (JobException e) {
664  0 setError(e);
665    }
666   
667  3 return job;
668    }
669   
670    /**
671    * Create the default request used when asking for the upgrade plan on a namespace.
672    *
673    * @param namespace the namespace to upgrade
674    * @return the request to pass t the job
675    */
 
676  0 toggle public InstallRequest createUpgradePlanRequest(String namespace)
677    {
678  0 InstallRequest installRequest = new InstallRequest();
679  0 installRequest.setId(ExtensionRequest.getJobId(ExtensionRequest.JOBID_PLAN_PREFIX, null, namespace));
680  0 installRequest.addNamespace(namespace);
681   
682    // Provide informations on what started the job
683  0 installRequest.setProperty(PROPERTY_CONTEXT_WIKI, this.xcontextProvider.get().getWikiId());
684  0 installRequest.setProperty(PROPERTY_CONTEXT_ACTION, this.xcontextProvider.get().getAction());
685   
686  0 return installRequest;
687    }
688   
 
689  0 toggle private InstallRequest createUpgradePlanRequest()
690    {
691  0 InstallRequest installRequest = new InstallRequest();
692  0 installRequest.setId(ExtensionRequest.getJobId(ExtensionRequest.JOBID_PLAN_PREFIX, null, null));
693   
694    // Provide informations on what started the job
695  0 installRequest.setProperty(PROPERTY_CONTEXT_WIKI, this.xcontextProvider.get().getWikiId());
696  0 installRequest.setProperty(PROPERTY_CONTEXT_ACTION, this.xcontextProvider.get().getAction());
697   
698  0 return installRequest;
699    }
700   
701    /**
702    * Schedule the upgrade plan creation job.
703    *
704    * @param request the request to pass to pass to the upgrade plan job
705    * @return the {@link Job} object which can be used to monitor the progress of the upgrade plan creation process, or
706    * {@code null} in case of failure
707    */
 
708  0 toggle public Job createUpgradePlan(InstallRequest request)
709    {
710  0 request.setProperty(AbstractExtensionValidator.PROPERTY_USERREFERENCE,
711    this.documentAccessBridge.getCurrentUserReference());
712  0 XWikiDocument callerDocument = getCallerDocument();
713  0 if (callerDocument != null) {
714  0 request.setProperty(AbstractExtensionValidator.PROPERTY_CALLERREFERENCE,
715    callerDocument.getContentAuthorReference());
716    }
717   
718  0 request.setProperty(AbstractExtensionValidator.PROPERTY_CHECKRIGHTS, true);
719   
720  0 Job job = null;
721  0 try {
722  0 job = safe(this.jobExecutor.execute(UpgradePlanJob.JOBTYPE, request));
723    } catch (JobException e) {
724  0 setError(e);
725    }
726   
727  0 return job;
728    }
729   
730    /**
731    * Start the asynchronous upgrade plan creation process for the provided namespace.
732    *
733    * @param namespace the namespace where to upgrade the extensions
734    * @return the {@link Job} object which can be used to monitor the progress of the plan creation process, or
735    * {@code null} in case of failure
736    */
 
737  0 toggle public Job createUpgradePlan(String namespace)
738    {
739  0 setError(null);
740   
741  0 return createUpgradePlan(createUpgradePlanRequest(namespace));
742    }
743   
744    /**
745    * Start the asynchronous upgrade plan creation process for all the namespaces.
746    *
747    * @return the {@link Job} object which can be used to monitor the progress of the plan creation process, or
748    * {@code null} in case of failure
749    */
 
750  0 toggle public Job createUpgradePlan()
751    {
752  0 setError(null);
753   
754  0 return createUpgradePlan(createUpgradePlanRequest());
755    }
756   
757    // Jobs
758   
759    /**
760    * Get a reference to the currently job executed.
761    * <p>
762    * Current here basically means the extension related job that is going to block any new job that would be
763    * associated to the current namespace.
764    *
765    * @return currently executing job, or {@code null} if no job is being executed
766    */
 
767  15 toggle public Job getCurrentJob()
768    {
769  15 setError(null);
770   
771  15 if (!this.authorization.hasAccess(Right.PROGRAM)) {
772  0 setError(new JobException("You need programming rights to get the current job."));
773  0 return null;
774    }
775   
776  15 return getCurrentJobInternal();
777    }
778   
 
779  15 toggle private Job getCurrentJobInternal()
780    {
781    // TODO: probably check current user namespace
782   
783    // Check current wiki namespace
784  15 Job job = this.jobExecutor.getCurrentJob(new JobGroupPath(
785    fromWikitoNamespace(this.xcontextProvider.get().getWikiId()), AbstractExtensionJob.ROOT_GROUP));
786   
787    // Check root namespace
788  15 if (job == null) {
789  15 job = this.jobExecutor.getCurrentJob(AbstractExtensionJob.ROOT_GROUP);
790    }
791   
792  15 return job;
793    }
794   
795    /**
796    * Return job status corresponding to the provided extension id from the current executed job or stored history.
797    *
798    * @param extensionId the extension identifier
799    * @param namespace the namespace where the job is being or has been executed
800    * @return the job status corresponding to the provided extension
801    */
 
802  2030 toggle public JobStatus getExtensionJobStatus(String extensionId, String namespace)
803    {
804  2030 return getJobStatus(ExtensionRequest.getJobId(ExtensionRequest.JOBID_ACTION_PREFIX, extensionId, namespace));
805    }
806   
807    /**
808    * Return extension plan corresponding to the provided extension id from the current executed job or stored history.
809    *
810    * @param extensionId the extension identifier
811    * @param namespace the namespace where the job is being or has been executed
812    * @return the extension plan corresponding to the provided extension
813    */
 
814  2030 toggle public JobStatus getExtensionPlanJobStatus(String extensionId, String namespace)
815    {
816  2030 return getJobStatus(ExtensionRequest.getJobId(ExtensionRequest.JOBID_PLAN_PREFIX, extensionId, namespace));
817    }
818   
819    /**
820    * Get the status of the currently executing job, if any.
821    *
822    * @return status of the currently executing job, or {@code null} if no job is being executed
823    */
 
824  0 toggle public JobStatus getCurrentJobStatus()
825    {
826  0 Job job = getCurrentJobInternal();
827   
828  0 JobStatus jobStatus;
829  0 if (job != null) {
830  0 jobStatus = job.getStatus();
831  0 if (!this.authorization.hasAccess(Right.PROGRAM)) {
832  0 jobStatus = safe(jobStatus);
833    }
834    } else {
835  0 jobStatus = null;
836    }
837   
838  0 return jobStatus;
839    }
840   
841    // Version management
842   
843    /**
844    * @param version the string to parse
845    * @return the {@link Version} instance
846    * @since 3.4M1
847    */
 
848  0 toggle public Version parseVersion(String version)
849    {
850  0 return new DefaultVersion(version);
851    }
852   
853    /**
854    * @param versionRange the string to parse
855    * @return the {@link VersionRange} instance
856    * @since 3.4M1
857    */
 
858  0 toggle public VersionRange parseVersionRange(String versionRange)
859    {
860  0 setError(null);
861   
862  0 try {
863  0 return new DefaultVersionRange(versionRange);
864    } catch (Exception e) {
865  0 setError(e);
866    }
867   
868  0 return null;
869    }
870   
871    /**
872    * @param versionConstraint the string to parse
873    * @return the {@link VersionConstraint} instance
874    * @since 3.4M1
875    */
 
876  0 toggle public VersionConstraint parseVersionConstraint(String versionConstraint)
877    {
878  0 setError(null);
879   
880  0 try {
881  0 return this.factory.getVersionConstraint(versionConstraint);
882    } catch (Exception e) {
883  0 setError(e);
884    }
885   
886  0 return null;
887    }
888   
889    /**
890    * Creates an extension dependency object.
891    *
892    * @param id the dependency identifier
893    * @param versionConstraint the dependency version constraint
894    * @return the extension dependency object
895    */
 
896  66 toggle public ExtensionDependency createExtensionDependency(String id, String versionConstraint)
897    {
898  66 setError(null);
899   
900  66 try {
901  66 return this.factory.getExtensionDependency(id, this.factory.getVersionConstraint(versionConstraint), null);
902    } catch (Exception e) {
903  0 setError(e);
904    }
905   
906  0 return null;
907    }
908   
909    /**
910    * @param allowedNamespaces the allowed dynamic (or not) namespaces, null matches any namespace
911    * @param namespace to validate against passed allowed namespaces
912    * @return the namespace(s) corresponding to the passed dynamic namespaces
913    * @since 8.0M1
914    */
 
915  0 toggle public boolean isAllowed(Collection<String> allowedNamespaces, String namespace)
916    {
917  0 return this.namespaceResolver.isAllowed(allowedNamespaces, namespace);
918    }
919   
920    /**
921    * @param extension the extension to check with the passed namespace
922    * @param namespace to validate against passed allowed namespaces
923    * @return the namespace(s) corresponding to the passed dynamic namespaces
924    * @since 8.0M1
925    */
 
926  48 toggle public boolean isAllowed(Extension extension, String namespace)
927    {
928  48 return this.namespaceResolver.isAllowed(extension.getAllowedNamespaces(), namespace);
929    }
930   
931    // Deprecated (generally moved to dedicated script services)
932   
933    /**
934    * Get a list of all currently installed extensions. This doesn't include core extensions, only custom extensions
935    * installed by the administrators.
936    *
937    * @return a list of read-only handlers corresponding to the installed extensions, an empty list if nothing is
938    * installed
939    * @deprecated since 5.3M1, use {@link InstalledExtensionScriptService#getInstalledExtensions()} instead
940    */
 
941  0 toggle @Deprecated
942    public Collection<InstalledExtension> getInstalledExtensions()
943    {
944  0 return this.<InstalledExtensionScriptService>get(InstalledExtensionScriptService.ID).getInstalledExtensions();
945    }
946   
947    /**
948    * Return all the extensions available for the provide namespace. This also include root extension since namespaces
949    * inherit from root.
950    * <p>
951    * This doesn't include core extensions, only extension installed through the API.
952    *
953    * @param namespace the target namespace for which to retrieve the list of installed extensions
954    * @return a list of read-only handlers corresponding to the installed extensions, an empty list if nothing is
955    * installed in the target namespace
956    * @deprecated since 5.3M1, use {@link InstalledExtensionScriptService#getInstalledExtensions(String)} instead
957    */
 
958  0 toggle @Deprecated
959    public Collection<InstalledExtension> getInstalledExtensions(String namespace)
960    {
961  0 return this.<InstalledExtensionScriptService>get(InstalledExtensionScriptService.ID)
962    .getInstalledExtensions(namespace);
963    }
964   
965    /**
966    * Get the extension handler corresponding to the given installed extension ID or feature (virtual ID) provided by
967    * the extension and namespace.
968    * <p>
969    * The returned handler can be used to get more information about the extension, such as the authors, an extension
970    * description, its license...
971    *
972    * @param feature the extension id or provided feature (virtual extension) of the extension to resolve
973    * @param namespace the optional namespace where the extension should be installed
974    * @return the read-only handler corresponding to the requested extension, or {@code null} if the extension isn't
975    * installed in the target namespace
976    * @deprecated since 5.3M1, use {@link InstalledExtensionScriptService#getInstalledExtension(String, String)}
977    * instead
978    */
 
979  0 toggle @Deprecated
980    public InstalledExtension getInstalledExtension(String feature, String namespace)
981    {
982  0 return this.<InstalledExtensionScriptService>get(InstalledExtensionScriptService.ID)
983    .getInstalledExtension(feature, namespace);
984    }
985   
986    /**
987    * Get all the installed extensions that depend on the specified extension. The results are grouped by namespace, so
988    * the same extension can appear multiple times, once for each namespace where it is installed.
989    *
990    * @param feature the extension id or provided feature (virtual extension) of the extension to resolve
991    * @param version the specific version to check
992    * @return a map namespace -&gt; list of dependent extensions, or {@code null} if any error occurs while computing
993    * the result, in which case {@link #getLastError()} contains the failure reason
994    * @deprecated since 5.3M1, use {@link InstalledExtensionScriptService#getBackwardDependencies(String)} instead
995    */
 
996  0 toggle @Deprecated
997    public Map<String, Collection<InstalledExtension>> getBackwardDependencies(String feature, String version)
998    {
999  0 return this.<InstalledExtensionScriptService>get(InstalledExtensionScriptService.ID)
1000    .getBackwardDependencies(feature);
1001    }
1002   
1003    /**
1004    * Get a list of core extensions provided by the current version of the platform.
1005    *
1006    * @return a list of read-only handlers corresponding to the core extensions
1007    * @deprecated since 5.3M1, use {@link CoreExtensionScriptService#getCoreExtensions()} instead
1008    */
 
1009  0 toggle @Deprecated
1010    public Collection<CoreExtension> getCoreExtensions()
1011    {
1012  0 return this.<CoreExtensionScriptService>get(CoreExtensionScriptService.ID).getCoreExtensions();
1013    }
1014   
1015    /**
1016    * Get the extension handler corresponding to the given core extension ID. The returned handler can be used to get
1017    * more information about the extension, such as the authors, an extension description, its license...
1018    *
1019    * @param feature the extension id or provided feature (virtual extension) of the extension to resolve
1020    * @return the read-only handler corresponding to the requested extension, or {@code null} if the extension isn't
1021    * provided by the platform
1022    * @deprecated since 5.3M1, use {@link CoreExtensionScriptService#getCoreExtension(String)} instead
1023    */
 
1024  0 toggle @Deprecated
1025    public CoreExtension getCoreExtension(String feature)
1026    {
1027  0 return this.<CoreExtensionScriptService>get(CoreExtensionScriptService.ID).getCoreExtension(feature);
1028    }
1029   
1030    /**
1031    * Get a list of cached extensions from the local extension repository. This doesn't include core extensions, only
1032    * custom extensions fetched or installed.
1033    *
1034    * @return a list of read-only handlers corresponding to the local extensions, an empty list if nothing is available
1035    * in the local repository
1036    * @deprecated since 5.3M1, use {@link LocalExtensionScriptService#getLocalExtensions()}
1037    */
 
1038  0 toggle @Deprecated
1039    public Collection<LocalExtension> getLocalExtensions()
1040    {
1041  0 return this.<LocalExtensionScriptService>get(LocalExtensionScriptService.ID).getLocalExtensions();
1042    }
1043    }