SmartAPI
Open Source .NET RQL library for RedDot CMS / OpenText WSM Management Server
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Properties Pages
IProject.cs
Go to the documentation of this file.
1 // SmartAPI - .Net programmatic access to RedDot servers
2 //
3 // Copyright (C) 2013 erminas GbR
4 //
5 // This program is free software: you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License as published by the Free Software Foundation,
7 // either version 3 of the License, or (at your option) any later version.
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 // See the GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License along with this program.
14 // If not, see <http://www.gnu.org/licenses/>.
15 
16 using System;
17 using System.Collections.Generic;
18 using System.Globalization;
19 using System.Linq;
20 using System.Security;
21 using System.Xml;
22 using erminas.SmartAPI.CMS.Project.ContentClasses;
23 using erminas.SmartAPI.CMS.Project.ContentClasses.Elements;
24 using erminas.SmartAPI.CMS.Project.Folder;
25 using erminas.SmartAPI.CMS.Project.Keywords;
26 using erminas.SmartAPI.CMS.Project.Pages;
27 using erminas.SmartAPI.CMS.Project.Publication;
28 using erminas.SmartAPI.CMS.ServerManagement;
29 using erminas.SmartAPI.Exceptions;
30 using erminas.SmartAPI.Utils;
31 using erminas.SmartAPI.Utils.CachedCollections;
32 
33 namespace erminas.SmartAPI.CMS.Project
34 {
35  public enum ProjectLockLevel
36  {
37  None = 0,
38  All = -1,
39  Admin = 1,
40  SiteBuilder = 2,
41  Editor = 3,
42  Author = 4,
43  Visitor = 5,
44  Publisher = 16,
45  AdminAndPublisher = 17
46  };
47 
48  public interface IContentClassFolders : IIndexedRDList<string, IContentClassFolder>, IProjectObject
49  {
50  IIndexedRDList<string, IContentClassFolder> Broken { get; }
51  }
52 
53  internal class ContentClassFolders : NameIndexedRDList<IContentClassFolder>, IContentClassFolders
54  {
55  private readonly Project _project;
56 
57  internal ContentClassFolders(Project project, Caching caching) : base(caching)
58  {
59  _project = project;
60  RetrieveFunc = GetContentClassFolders;
61  Broken = new NameIndexedRDList<IContentClassFolder>(GetBrokenFolders, Caching.Enabled);
62  }
63 
64  public IIndexedRDList<string, IContentClassFolder> Broken { get; private set; }
65 
66  public IProject Project
67  {
68  get { return _project; }
69  }
70 
71  public ISession Session
72  {
73  get { return _project.Session; }
74  }
75 
76  private List<IContentClassFolder> GetBrokenFolders()
77  {
78  const string TREE =
79  @"<TREESEGMENT type=""project.4000"" action=""load"" guid=""4AF89E44535511D4BDAB004005312B7C"" descent=""app"" parentguid=""""/>";
80  var result = Project.ExecuteRQL(TREE);
81  var guids = this.Select(folder => folder.Guid)
82  .ToList();
83  return (from XmlElement element in result.GetElementsByTagName("SEGMENT")
84  let curGuid = element.GetGuid()
85  where !guids.Contains(curGuid)
86  select (IContentClassFolder) new ContentClassFolder(_project, curGuid)
87  {
88  Name = element.GetAttributeValue("value"),
89  IsBroken = true
90  }).ToList();
91  }
92 
93  private List<IContentClassFolder> GetContentClassFolders()
94  {
95  const string LIST_CC_FOLDERS_OF_PROJECT = @"<TEMPLATEGROUPS action=""load"" />";
96  //TODO project.execute
97  XmlDocument xmlDoc = Session.ExecuteRQLInProjectContext(LIST_CC_FOLDERS_OF_PROJECT, _project.Guid);
98  XmlNodeList xmlNodes = xmlDoc.GetElementsByTagName("GROUP");
99 
100  return (from XmlElement curNode in xmlNodes select (IContentClassFolder) new ContentClassFolder(_project, curNode)).ToList();
101  }
102  }
103 
105  {
106  //IClipboard Clipboard { get; }
107  IProjectGroups AssignedGroups { get; }
108 
109  //IAuthorizationPackages AuthorizationPackages { get; }
111 
113  IProjectClipboard Clipboard { get; }
114 
118  IContentClassFolders ContentClassFolders { get; }
119 
120  IContentClasses ContentClasses { get; }
121 
125  IDatabaseConnections DatabaseConnections { get; }
126 
130  IFolders Folders { get; }
131 
135  IIndexedCachedList<int, IInfoAttribute> InfoAttributes { get; }
136 
141  IIndexedRDList<string, IUser> OnlineUsers { get; }
142 
143  bool IsArchivingActive { get; }
144 
145  bool IsLockedBySystem { get; }
146 
147  bool IsVersioningActive { get; set; }
148 
152  IRDList<IKeyword> Keywords { get; }
153 
157  ILanguageVariants LanguageVariants { get; }
158 
162  ProjectLockLevel LockLevel { get; }
163 
164  IPages Pages { get; }
165 
169  IProjectVariants ProjectVariants { get; }
170 
174  IRDList<IPublicationFolder> PublicationFolders { get; }
175 
179  IRDList<IPublicationPackage> PublicationPackages { get; }
180 
184  IRDList<IPublicationTarget> PublicationTargets { get; }
185 
186  IRecycleBin RecycleBin { get; }
187 
191  ISyllables Syllables { get; }
192 
196  IProjectUsers Users { get; }
197 
198  IProjectWorkflows Workflows { get; }
199 
200  IProjectCopyJob CreateCopyJob(string newProjectName);
201 
202  IProjectExportJob CreateExportJob(string targetPath);
203 
207  void DeleteWithDatabase(string databaseUser, string password);
208 
215  XmlDocument ExecuteRQL(string query, RqlType type = RqlType.SessionKeyInIodata);
216 
217  void ClearPageCache();
218 
220  string GetTextContent(Guid textElementGuid, ILanguageVariant lang, string typeString);
221 
222  IProject Refreshed();
223 
227  void Select();
228 
238  void SetLockLevel(ProjectLockLevel level, string infoMessage);
239 
241  [Obsolete(
242  "This was not meant as public API and will be removed in the next major release. Do NOT use it, it probably won't do what you think it does!"
243  )]
244  Guid SetTextContent(Guid textElementGuid, ILanguageVariant languageVariant, string typeString, string content);
245  }
246 
259  internal class Project : PartialRedDotObject, IProject
260  {
261  private readonly IContentClasses _contentClasses;
262  private readonly IPages _pages;
263  private bool _isLockedBySystem;
264  private ProjectLockLevel _locklevel;
265  private IPage _startPage;
266  private IProjectClipboard _clipboard;
267 
268  internal Project(ISession session, XmlElement xmlElement) : base(session, xmlElement)
269  {
270  _contentClasses = new ContentClasses.ContentClasses(this);
271  _pages = new Pages.Pages(this);
272  LoadXml();
273  Init();
274  }
275 
276  internal Project(ISession session, Guid guid) : base(session, guid)
277  {
278  _contentClasses = new ContentClasses.ContentClasses(this);
279  _pages = new Pages.Pages(this);
280  Init();
281  }
282 
283  internal XmlDocument AllFoldersXmlDocument { get; set; }
284 
285  //public IClipboard Clipboard { get; private set; }
286  public IProjectGroups AssignedGroups { get; private set; }
287 
288  //public IAuthorizationPackages AuthorizationPackages { get; private set; }
289 
290  public ICategories Categories { get; private set; }
291 
292  [VersionIsGreaterThanOrEqual(10)]
293  public IProjectClipboard Clipboard
294  {
295  get
296  {
297  VersionVerifier.EnsureVersion(Session);
298  return _clipboard;
299  }
300  }
301 
302  public IIndexedRDList<string, IUser> OnlineUsers { get; private set; }
303 
307  public IContentClassFolders ContentClassFolders { get; private set; }
308 
309  public IContentClasses ContentClasses
310  {
311  get { return _contentClasses; }
312  }
313 
314  public IProjectCopyJob CreateCopyJob(string newProjectName)
315  {
316  return new ProjectCopyJob(this, newProjectName);
317  }
318 
319  public IProjectExportJob CreateExportJob(string targetPath)
320  {
321  return new ProjectExportJob(this, targetPath);
322  }
323 
327  public IDatabaseConnections DatabaseConnections { get; private set; }
328 
332  public void DeleteWithDatabase(string databaseUser, string password)
333  {
334  const string DELETE_PROJECT =
335  @"<ADMINISTRATION><PROJECT action=""delete"" guid=""{0}"" deletedb=""{1}"" user=""{2}"" password=""{3}""/></ADMINISTRATION>";
336  Session.ExecuteRQL(DELETE_PROJECT.RQLFormat(this, true, databaseUser, password));
337  //empty response on success, so errors can't be detected
338  }
339 
346  public XmlDocument ExecuteRQL(string query, RqlType type = RqlType.SessionKeyInIodata)
347  {
348  switch (type)
349  {
350  case RqlType.SessionKeyInIodata:
351  return Session.ExecuteRQLInProjectContext(query, Guid);
352  case RqlType.SessionKeyInProject:
353  return Session.ExecuteRQLInProjectContextAndEmbeddedInProjectElement(query, Guid);
354  default:
355  throw new ArgumentException(string.Format("Unknown query type: {0}", type));
356  }
357  }
358 
359  public void ClearPageCache()
360  {
361  const string CLEAR_PAGE_CACHE = @"<PBCACHE><CACHE action=""delete""/></PBCACHE>";
362  ExecuteRQL(CLEAR_PAGE_CACHE);
363  }
364 
368  public IFolders Folders { get; private set; }
369 
371  public string GetTextContent(Guid textElementGuid, ILanguageVariant lang, string typeString)
372  {
373  return Session.GetTextContent(Guid, lang, textElementGuid, typeString);
374  }
375 
379  public IIndexedCachedList<int, IInfoAttribute> InfoAttributes { get; private set; }
380 
381  public bool IsArchivingActive
382  {
383  get
384  {
385  EnsureInitialization();
386  return XmlElement.GetIntAttributeValue("archive")
387  .GetValueOrDefault() == -1;
388  }
389  }
390 
391  public bool IsLockedBySystem
392  {
393  get { return LazyLoad(ref _isLockedBySystem); }
394  }
395 
396  //TODO gets stored on server immediatly, commit? Save/Set?
397  public bool IsVersioningActive
398  {
399  get
400  {
401  EnsureInitialization();
402  return XmlElement.GetIntAttributeValue("versioning")
403  .GetValueOrDefault() == -1;
404  }
405 
406  set
407  {
408  const string SET_VERISONING = @"<ADMINISTRATION><PROJECT action=""save"" guid=""{2}"" versioning=""{3}""/></ADMINISTRATION>";
409  Session.ExecuteRQL(SET_VERISONING.RQLFormat(Session.LogonGuid, Session.CurrentUser, this, value));
410  }
411  }
412 
416  public IRDList<IKeyword> Keywords { get; private set; }
417 
421  public ILanguageVariants LanguageVariants { get; private set; }
422 
426  public ProjectLockLevel LockLevel
427  {
428  get { return LazyLoad(ref _locklevel); }
429  }
430 
431  public IPages Pages
432  {
433  get { return _pages; }
434  }
435 
439  public IProjectVariants ProjectVariants { get; private set; }
440 
444  public IRDList<IPublicationFolder> PublicationFolders { get; private set; }
445 
449  public IRDList<IPublicationPackage> PublicationPackages { get; private set; }
450 
454  public IRDList<IPublicationTarget> PublicationTargets { get; private set; }
455 
456  public IRecycleBin RecycleBin { get; private set; }
457 
458  public IProject Refreshed()
459  {
460  Refresh();
461  return this;
462  }
463 
467  public void Select()
468  {
469  Session.SelectProject(this);
470  }
471 
481  public void SetLockLevel(ProjectLockLevel level, string infoMessage)
482  {
483  if (level != ProjectLockLevel.None && string.IsNullOrEmpty(infoMessage))
484  {
485  throw new SmartAPIException(Session.ServerLogin, "Info message for project locking must not be empty");
486  }
487  const string SET_LOCKLEVEL =
488  @"<ADMINISTRATION><PROJECT action=""save"" guid=""{0}"" inhibitlevel=""{1}"" lockinfo=""{2}""/></ADMINISTRATION>";
489 
490  var xmlDoc =
491  Session.ExecuteRQL(
492  SET_LOCKLEVEL.RQLFormat(
493  this,
494  (int) level,
495  level == ProjectLockLevel.None
496  ? RQL.SESSIONKEY_PLACEHOLDER
497  : SecurityElement.Escape(infoMessage)));
498  var project = (XmlElement) xmlDoc.SelectSingleNode("//PROJECT");
499  if (!project.GetAttributeValue("inhibitlevel")
500  .Equals(((int) level).ToString(CultureInfo.InvariantCulture)))
501  {
502  throw new SmartAPIException(Session.ServerLogin, string.Format("Could not set project lock level to {0}", level));
503  }
504  }
505 
507  public Guid SetTextContent(Guid textElementGuid, ILanguageVariant languageVariant, string typeString, string content)
508  {
509  return Session.SetTextContent(Guid, languageVariant, textElementGuid, typeString, content);
510  }
511 
515  public ISyllables Syllables { get; private set; }
516 
520  public IProjectUsers Users { get; private set; }
521 
522  public IProjectWorkflows Workflows { get; private set; }
523 
524  protected override void LoadWholeObject()
525  {
526  LoadXml();
527  }
528 
529  protected override XmlElement RetrieveWholeObject()
530  {
531  if (Session.CurrentUser.ModuleAssignment.IsServerManager)
532  {
533  return ((Project) Session.ServerManager.Projects.GetByGuid(Guid)).XmlElement;
534  }
535  return ((Project) Session.ServerManager.Projects.ForCurrentUser.GetByGuid(Guid)).XmlElement;
536  }
537 
538  private List<IInfoAttribute> GetInfoAttributes()
539  {
540  const string LOAD_INFO_ELEMENTS = @"<TEMPLATE><INFOELEMENTS action=""list""></INFOELEMENTS></TEMPLATE>";
541  XmlDocument xmlDoc = ExecuteRQL(LOAD_INFO_ELEMENTS);
542  var infos = xmlDoc.GetElementsByTagName("INFOELEMENTS")[0] as XmlElement;
543  if (infos == null)
544  {
545  throw new SmartAPIException(Session.ServerLogin, "Could not load info elements");
546  }
547  return
548  (from XmlElement info in infos.GetElementsByTagName("PAGEINFO") select (IInfoAttribute) new InfoAttribute(info)).Union(
549  (from
550  XmlElement
551  info
552  in
553  infos
554  .GetElementsByTagName
555  (
556  "PROJECTINFO")
557  select
558  (
560  )
561  new InfoAttribute
562  (
563  info)))
564  .Union(
565  (from XmlElement info in infos.GetElementsByTagName("SESSIONOBJECT")
566  select (IInfoAttribute) new InfoAttribute(info)))
567  .ToList();
568  }
569 
570  private List<IKeyword> GetKeywords()
571  {
572  const string LIST_KEYWORDS = "<PROJECT><CATEGORY><KEYWORDS action=\"list\" /></CATEGORY></PROJECT>";
573  XmlDocument xmlDoc = ExecuteRQL(LIST_KEYWORDS);
574  XmlNodeList xmlNodes = xmlDoc.GetElementsByTagName("KEYWORD");
575  IEnumerable<Keyword> categoryKeywords = from curCategory in Categories
576  select new Keyword(this, curCategory.Guid)
577  {
578  Name = "[category]",
579  Category = curCategory
580  };
581  return (from XmlElement curNode in xmlNodes select (IKeyword) new Keyword(this, curNode)).Union(categoryKeywords)
582  .ToList();
583  }
584 
585  private List<IPublicationFolder> GetPublicationFolders()
586  {
587  const string LIST_PUBLICATION_FOLDERS = @"<PROJECT><EXPORTFOLDERS action=""list"" /></PROJECT>";
588 
589  XmlDocument xmlDoc = ExecuteRQL(LIST_PUBLICATION_FOLDERS);
590  if (xmlDoc.GetElementsByTagName("EXPORTFOLDERS")
591  .Count != 1)
592  {
593  throw new SmartAPIException(
594  Session.ServerLogin,
595  string.Format("Could not retrieve publication folders of project {0}", this));
596  }
597  return (from XmlElement curFolder in xmlDoc.GetElementsByTagName("EXPORTFOLDER")
598  select (IPublicationFolder) new PublicationFolder(this, curFolder.GetGuid())).ToList();
599  }
600 
601  private List<IPublicationPackage> GetPublicationPackages()
602  {
603  const string LIST_PUBLICATION_PACKAGES = @"<PROJECT><EXPORTPACKET action=""list""/></PROJECT>";
604  XmlDocument xmlDoc = ExecuteRQL(LIST_PUBLICATION_PACKAGES);
605  return (from XmlElement curPackage in xmlDoc.GetElementsByTagName("EXPORTPACKET")
606  select (IPublicationPackage) new PublicationPackage(this, curPackage.GetGuid())).ToList();
607  }
608 
609  private List<IPublicationTarget> GetPublicationTargets()
610  {
611  const string LIST_PUBLISHING_TARGETS = @"<PROJECT><EXPORTS action=""list""/></PROJECT>";
612 
613  XmlDocument xmlDoc = ExecuteRQL(LIST_PUBLISHING_TARGETS);
614  return
615  (from XmlElement curElement in xmlDoc.GetElementsByTagName("EXPORT")
616  select (IPublicationTarget) new PublicationTarget(this, curElement)).ToList();
617  }
618 
619  private void Init()
620  {
621  RecycleBin = new RecycleBin(this);
622  PublicationTargets = new RDList<IPublicationTarget>(GetPublicationTargets, Caching.Enabled);
623  PublicationFolders = new RDList<IPublicationFolder>(GetPublicationFolders, Caching.Enabled);
624  PublicationPackages = new RDList<IPublicationPackage>(GetPublicationPackages, Caching.Enabled);
625  InfoAttributes = new IndexedCachedList<int, IInfoAttribute>(GetInfoAttributes, x => x.Id, Caching.Enabled);
626 
627  ContentClassFolders = new ContentClassFolders(this, Caching.Enabled);
628  Folders = new Folders(this, Caching.Enabled);
629  ProjectVariants = new ProjectVariants(this, Caching.Enabled);
630  LanguageVariants = new LanguageVariants(this, Caching.Enabled);
631 
632  DatabaseConnections = new DatabaseConnections(this, Caching.Enabled);
633  Syllables = new Syllables(this, Caching.Enabled);
634  Users = new ProjectUsers(this, Caching.Enabled);
635 
636  Workflows = new ProjectWorkflow(this, Caching.Enabled);
637  Categories = new Categories(this);
638  Keywords = new RDList<IKeyword>(GetKeywords, Caching.Enabled);
639  AssignedGroups = new ProjectGroups(this, Caching.Enabled);
640 
641  OnlineUsers = new NameIndexedRDList<IUser>(GetOnlineUsers, Caching.Enabled);
642 
643  _clipboard = new ProjectClipboard(this);
644 
645  //AuthorizationPackages = new AuthorizationPackages(this);
646  }
647 
648  private List<IUser> GetOnlineUsers()
649  {
650  const string LIST_ONLINE_USERS = @"<ADMINISTRATION><USERS action=""connectlist"" projectguid=""{0}""/></ADMINISTRATION>";
651  var doc = ExecuteRQL(LIST_ONLINE_USERS.RQLFormat(this));
652 
653  var userElements = doc.GetElementsByTagName("USER").Cast<XmlElement>();
654  //most attributes are here, but not all (e.g. description)
655  //therefor we only init with guid and load the user on demand later.
656  //TODO a better solution would be to have a partial initialized user object,
657  //which doesn't need to be loaded again, if the available properties are accessed
658  return userElements.Select(x=>new User(Session, x.GetGuid())).Cast<IUser>().ToList();
659  }
660 
661  private void LoadXml()
662  {
663  InitIfPresent(ref _locklevel, "inhibitlevel", x => (ProjectLockLevel) int.Parse(x));
664  InitIfPresent(ref _isLockedBySystem, "lockedbysystem", BoolConvert);
665  }
666  }
667 
671  public enum RqlType
672  {
676  SessionKeyInProject,
677 
681  SessionKeyInIodata
682  };
683 
684  public enum NewProjectType
685  {
686  NormalProject = 0,
687  TestProject = 1
688  }
689 }