otsdaq_utilities  v2_03_00
ConfigurationGUISupervisor.cc
1 #include "otsdaq-utilities/ConfigurationGUI/ConfigurationGUISupervisor.h"
2 #include "otsdaq-core/CgiDataUtilities/CgiDataUtilities.h"
3 #include "otsdaq-core/Macros/CoutMacros.h"
4 #include "otsdaq-core/MessageFacility/MessageFacility.h"
5 #include "otsdaq-core/TablePluginDataFormats/IterateTable.h"
6 #include "otsdaq-core/XmlUtilities/HttpXmlDocument.h"
7 
8 #if MESSAGEFACILITY_HEX_VERSION > 0x20100
9 #include <boost/stacktrace.hpp>
10 #endif
11 
12 #include "otsdaq-core/TablePluginDataFormats/XDAQContextTable.h" //for context relaunch
13 
14 #include <xdaq/NamespaceURI.h>
15 
16 #include <iostream>
17 #include <map>
18 #include <utility>
19 
20 using namespace ots;
21 
22 #undef __MF_SUBJECT__
23 #define __MF_SUBJECT__ "CfgGUI"
24 
25 #define TABLE_INFO_PATH std::string(getenv("TABLE_INFO_PATH")) + "/"
26 #define TABLE_INFO_EXT std::string("Info.xml")
27 
30 xdaq::Application* ConfigurationGUISupervisor::instantiate(xdaq::ApplicationStub* stub)
31 {
32  return new ConfigurationGUISupervisor(stub);
33 }
34 
35 //========================================================================================================================
36 // new user gets a config mgr assigned
37 // user can fill any of the tables (fill from version or init empty), which becomes the
38 // active view for that table
39 ConfigurationGUISupervisor::ConfigurationGUISupervisor(xdaq::ApplicationStub* stub)
40  : CoreSupervisorBase(stub)
41 {
42  __SUP_COUT__ << "Constructor started." << __E__;
43 
44  INIT_MF("ConfigurationGUI");
45 
46  init();
47  __SUP_COUT__ << "Constructor complete." << __E__;
48 }
49 
50 //========================================================================================================================
51 ConfigurationGUISupervisor::~ConfigurationGUISupervisor(void) { destroy(); }
52 
53 //========================================================================================================================
54 void ConfigurationGUISupervisor::init(void)
55 {
56  __SUP_COUT__ << "Initializing..." << __E__;
57 
58  __SUP_COUT__ << "Activating saved context, which may prepare for normal mode..."
59  << __E__;
60  try
61  {
62  testXDAQContext(); // test context group activation
63  }
64  catch(...)
65  {
66  __COUT_WARN__ << "Failed test context group activation. otsdaq, in Normal mode, "
67  "will not launch when this test fails. "
68  << "Check the active context group from within Wizard Mode."
69  << __E__;
70  }
71 }
72 
73 //========================================================================================================================
74 void ConfigurationGUISupervisor::destroy(void)
75 {
76  // called by destructor
77  for(std::map<std::string, ConfigurationManagerRW*>::iterator it =
78  userConfigurationManagers_.begin();
79  it != userConfigurationManagers_.end();
80  ++it)
81  {
82  delete it->second;
83  it->second = 0;
84  }
85  userConfigurationManagers_.clear();
86 
87  // NOTE: Moved to ConfigurationGUISupervisor [FIXME is this correct?? should we use
88  // shared_ptr??]
89  if(ConfigurationInterface::getInstance(true) != 0)
90  delete ConfigurationInterface::getInstance(true);
91 }
92 
93 //========================================================================================================================
94 void ConfigurationGUISupervisor::defaultPage(xgi::Input* in, xgi::Output* out)
95 {
96  cgicc::Cgicc cgiIn(in);
97  std::string configWindowName =
98  CgiDataUtilities::getData(cgiIn, "configWindowName"); // from GET
99  if(configWindowName == "tableEditor")
100  *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
101  "src='/WebPath/html/ConfigurationTableEditor.html?urn="
102  << this->getApplicationDescriptor()->getLocalId() << "'></frameset></html>";
103  if(configWindowName == "iterate")
104  *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
105  "src='/WebPath/html/Iterate.html?urn="
106  << this->getApplicationDescriptor()->getLocalId() << "'></frameset></html>";
107  else
108  *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
109  "src='/WebPath/html/ConfigurationGUI.html?urn="
110  << this->getApplicationDescriptor()->getLocalId() << "'></frameset></html>";
111 }
112 
113 //========================================================================================================================
114 // When overriding, setup default property values here
115 // called by CoreSupervisorBase constructor
116 void ConfigurationGUISupervisor::setSupervisorPropertyDefaults(void)
117 {
118  CorePropertySupervisorBase::setSupervisorProperty(
119  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.UserPermissionsThreshold,
120  "*=10 | deleteTreeNodeRecords=255 | saveTableInfo=255 | "
121  "deleteTableInfo=255"); // experienced users to edit, admins to delete
122  CorePropertySupervisorBase::setSupervisorProperty(
123  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.RequireUserLockRequestTypes,
124  "*"); // all
125 }
126 
127 //========================================================================================================================
128 // forceSupervisorPropertyValues
129 // override to force supervisor property values (and ignore user settings)
130 void ConfigurationGUISupervisor::forceSupervisorPropertyValues()
131 {
132  CorePropertySupervisorBase::setSupervisorProperty(
133  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes,
134  ""); // none
135  CorePropertySupervisorBase::setSupervisorProperty(
136  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.CheckUserLockRequestTypes,
137  "*"); // all
138 }
139 
140 //========================================================================================================================
141 void ConfigurationGUISupervisor::request(const std::string& requestType,
142  cgicc::Cgicc& cgiIn,
143  HttpXmlDocument& xmlOut,
144  const WebUsers::RequestUserInfo& userInfo) try
145 {
146  // Commands
147 
148  // gatewayLaunchOTS -- and other StartOTS commands
149 
150  // saveTableInfo
151  // deleteTableInfo
152  // flattenToSystemAliases
153  // versionTracking
154  // getColumnTypes
155  // getGroupAliases
156  // setGroupAliasInActiveBackbone
157  // setVersionAliasInActiveBackbone
158  // setAliasOfGroupMembers
159  // getVersionAliases
160  // getTableGroups
161  // getTableGroupType
162  // getTables
163  // getContextMemberNames
164  // getBackboneMemberNames
165  // getIterateMemberNames
166  // getSpecificTableGroup
167  // saveNewTableGroup
168  // getSpecificTable
169  // saveSpecificTable
170  // clearTableTemporaryVersions
171  // clearTableCachedVersions
172  //
173  // ---- associated with JavaScript Config API
174  // getTreeView
175  // getTreeNodeCommonFields
176  // getUniqueFieldValuesForRecords
177  // getTreeNodeFieldValues
178  // setTreeNodeFieldValues
179  // addTreeNodeRecords
180  // deleteTreeNodeRecords
181  // ---- end associated with JavaScript Config API
182  //
183  // activateTableGroup
184  // getActiveTableGroups
185  // copyViewToCurrentColumns
186  // saveTreeNodeEdit
187  // getAffectedActiveGroups
188  // getLinkToChoices
189  // getLastTableGroups
190  // mergeGroups
191  //
192  // ---- associated with JavaScript Iterate App
193  // savePlanCommandSequence
194  // ---- end associated with JavaScript Iterate App
195 
196  // acquire user's configuration manager based on username& activeSessionIndex
197  std::string refresh = CgiDataUtilities::getData(cgiIn, "refresh"); // from GET
198  //__SUP_COUT__ << "refresh: " << refresh << __E__;
199 
200  // refresh to reload from info files and db (maintains temporary views!)
201  ConfigurationManagerRW* cfgMgr = refreshUserSession(
202  userInfo.username_, userInfo.activeUserSessionIndex_, (refresh == "1"));
203 
204  if(requestType == "saveTableInfo")
205  {
206  std::string tableName =
207  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
208  std::string columnCSV =
209  CgiDataUtilities::postData(cgiIn, "columnCSV"); // from POST
210  std::string allowOverwrite =
211  CgiDataUtilities::getData(cgiIn, "allowOverwrite"); // from GET
212  std::string tableDescription =
213  CgiDataUtilities::postData(cgiIn, "tableDescription"); // from POST
214  std::string columnChoicesCSV =
215  CgiDataUtilities::postData(cgiIn, "columnChoicesCSV"); // from POST
216 
217  // columnCSV = CgiDataUtilities::decodeURIComponent(columnCSV);
218  // tableDescription = CgiDataUtilities::decodeURIComponent(tableDescription);
219 
220  __SUP_COUT__ << "tableName: " << tableName << __E__;
221  __SUP_COUT__ << "columnCSV: " << columnCSV << __E__;
222  __SUP_COUT__ << "tableDescription: " << tableDescription << __E__;
223  __SUP_COUT__ << "columnChoicesCSV: " << columnChoicesCSV << __E__;
224  __SUP_COUT__ << "allowOverwrite: " << allowOverwrite << __E__;
225 
226  if(!allSupervisorInfo_.isWizardMode())
227  {
228  __SUP_SS__ << "Improper permissions for saving table info." << __E__;
229  xmlOut.addTextElementToData("Error", ss.str());
230  }
231  else
232  handleSaveTableInfoXML(xmlOut,
233  cfgMgr,
234  tableName,
235  columnCSV,
236  tableDescription,
237  columnChoicesCSV,
238  allowOverwrite == "1");
239  }
240  else if(requestType == "deleteTableInfo")
241  {
242  std::string tableName =
243  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
244  __SUP_COUT__ << "tableName: " << tableName << __E__;
245  handleDeleteTableInfoXML(xmlOut, cfgMgr, tableName);
246  }
247  else if(requestType == "gatewayLaunchOTS" || requestType == "gatewayLaunchWiz" ||
248  requestType == "flattenToSystemAliases")
249  {
250  // NOTE: similar to Supervisor version but does not keep active sessions
251  __SUP_COUT_WARN__ << requestType << " command received! " << __E__;
252  __MOUT_WARN__ << requestType << " command received! " << __E__;
253 
254  // now launch
255  __SUP_COUT_INFO__ << "Launching... " << __E__;
256 
257  __SUP_COUT__ << "Extracting target context hostnames... " << __E__;
258  std::vector<std::string> hostnames;
259  try
260  {
261  cfgMgr->init(); // completely reset to re-align with any changes
262 
263  const XDAQContextTable* contextTable =
264  cfgMgr->__GET_CONFIG__(XDAQContextTable);
265 
266  auto contexts = contextTable->getContexts();
267  unsigned int i, j;
268  for(const auto& context : contexts)
269  {
270  if(!context.status_)
271  continue;
272 
273  // find last slash
274  j = 0; // default to whole string
275  for(i = 0; i < context.address_.size(); ++i)
276  if(context.address_[i] == '/')
277  j = i + 1;
278  hostnames.push_back(context.address_.substr(j));
279  __SUP_COUT__ << "hostname = " << hostnames.back() << __E__;
280  }
281  }
282  catch(...)
283  {
284  __SUP_SS__ << "\nTransition to Configuring interrupted! "
285  << "The Configuration Manager could not be initialized." << __E__;
286 
287  __SUP_COUT_ERR__ << "\n" << ss.str();
288  return;
289  }
290 
291  for(const auto& hostname : hostnames)
292  {
293  std::string fn = (std::string(getenv("SERVICE_DATA_PATH")) +
294  "/StartOTS_action_" + hostname + ".cmd");
295  FILE* fp = fopen(fn.c_str(), "w");
296  if(fp)
297  {
298  if(requestType == "gatewayLaunchOTS")
299  fprintf(fp, "LAUNCH_OTS");
300  else if(requestType == "gatewayLaunchWiz")
301  fprintf(fp, "LAUNCH_WIZ");
302  else if(requestType == "flattenToSystemAliases")
303  {
304  fprintf(fp, "FLATTEN_TO_SYSTEM_ALIASES");
305  fclose(fp);
306  break; // only do at one host
307  }
308 
309  fclose(fp);
310  }
311  else
312  __SUP_COUT_ERR__ << "Unable to open command file: " << fn << __E__;
313  }
314  }
315  // else if(requestType == "launchOTS")
316  // {
317  // __SUP_COUT_WARN__ << "launchOTS command received! Launching... " << __E__;
318  //
319  // FILE* fp = fopen((std::string(getenv("SERVICE_DATA_PATH")) +
320  // "/StartOTS_action.cmd").c_str(),"w");
321  // if(fp)
322  // {
323  // fprintf(fp,"LAUNCH_OTS");
324  // fclose(fp);
325  // }
326  // else
327  // __SUP_COUT_ERR__ << "Unable to open command file: " <<
328  //(std::string(getenv("SERVICE_DATA_PATH")) +
329  // "/StartOTS_action.cmd") << __E__;
330  // }
331  // else if(requestType == "launchWiz")
332  // {
333  // __SUP_COUT_WARN__ << "launchWiz command received! Launching... " << __E__;
334  //
335  // FILE* fp = fopen((std::string(getenv("SERVICE_DATA_PATH")) +
336  // "/StartOTS_action.cmd").c_str(),"w");
337  // if(fp)
338  // {
339  // fprintf(fp,"LAUNCH_WIZ");
340  // fclose(fp);
341  // }
342  // else
343  // __SUP_COUT_ERR__ << "Unable to open command file: " <<
344  //(std::string(getenv("SERVICE_DATA_PATH")) +
345  // "/StartOTS_action.cmd") << __E__;
346  // }
347  // else if(requestType == "flattenToSystemAliases")
348  // {
349  // __SUP_COUT_WARN__ << "flattenToSystemAliases command received! Launching... "
350  //<<
351  //__E__;
352  //
353  // FILE* fp = fopen((std::string(getenv("SERVICE_DATA_PATH")) +
354  // "/StartOTS_action.cmd").c_str(),"w");
355  // if(fp)
356  // {
357  // fprintf(fp,"FLATTEN_TO_SYSTEM_ALIASES");
358  // fclose(fp);
359  // }
360  // else
361  // __SUP_COUT_ERR__ << "Unable to open command file: " <<
362  //(std::string(getenv("SERVICE_DATA_PATH")) +
363  // "/StartOTS_action.cmd") << __E__;
364  // }
365  else if(requestType == "versionTracking")
366  {
367  std::string type = CgiDataUtilities::getData(cgiIn, "Type"); // from GET
368  __SUP_COUT__ << "type: " << type << __E__;
369 
370  if(type == "Get")
371  xmlOut.addTextElementToData(
372  "versionTrackingStatus",
373  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
374  else if(type == "ON")
375  {
376  ConfigurationInterface::setVersionTrackingEnabled(true);
377  xmlOut.addTextElementToData(
378  "versionTrackingStatus",
379  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
380  }
381  else if(type == "OFF")
382  {
383  ConfigurationInterface::setVersionTrackingEnabled(false);
384  xmlOut.addTextElementToData(
385  "versionTrackingStatus",
386  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
387  }
388  }
389  else if(requestType == "getColumnTypes")
390  {
391  // return the possible column types and their defaults
392  std::vector<std::string> allTypes = TableViewColumnInfo::getAllTypesForGUI();
393  std::vector<std::string> allDataTypes =
394  TableViewColumnInfo::getAllDataTypesForGUI();
395  std::map<std::pair<std::string, std::string>, std::string> allDefaults =
396  TableViewColumnInfo::getAllDefaultsForGUI();
397 
398  for(const auto& type : allTypes)
399  xmlOut.addTextElementToData("columnTypeForGUI", type);
400  for(const auto& dataType : allDataTypes)
401  xmlOut.addTextElementToData("columnDataTypeForGUI", dataType);
402 
403  for(const auto& colDefault : allDefaults)
404  {
405  xmlOut.addTextElementToData("columnDefaultDataType", colDefault.first.first);
406  xmlOut.addTextElementToData("columnDefaultTypeFilter",
407  colDefault.first.second);
408  xmlOut.addTextElementToData("columnDefaultValue", colDefault.second);
409  }
410  }
411  else if(requestType == "getGroupAliases")
412  {
413  // Since this is called from setting up System View in the config GUI
414  // give option for reloading "persistent" active configurations
415  bool reloadActive =
416  1 == CgiDataUtilities::getDataAsInt(cgiIn, "reloadActiveGroups"); // from GET
417 
418  __SUP_COUT__ << "reloadActive: " << reloadActive << __E__;
419  bool wasError = false;
420  if(reloadActive)
421  {
422  try
423  {
424  cfgMgr->clearAllCachedVersions();
425  cfgMgr->restoreActiveTableGroups(true);
426  }
427  catch(std::runtime_error& e)
428  {
429  __SUP_SS__ << ("Error loading active groups!\n\n" + std::string(e.what()))
430  << __E__;
431  __SUP_COUT_ERR__ << "\n" << ss.str();
432  xmlOut.addTextElementToData("Error", ss.str());
433  wasError = true;
434  }
435  catch(...)
436  {
437  __SUP_SS__ << ("Error loading active groups!\n\n") << __E__;
438  __SUP_COUT_ERR__ << "\n" << ss.str();
439  xmlOut.addTextElementToData("Error", ss.str());
440  wasError = true;
441  }
442  }
443 
444  handleGroupAliasesXML(xmlOut, cfgMgr);
445  }
446  else if(requestType == "setGroupAliasInActiveBackbone")
447  {
448  std::string groupAlias =
449  CgiDataUtilities::getData(cgiIn, "groupAlias"); // from GET
450  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName"); // from
451  // GET
452  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey"); // from GET
453 
454  __SUP_COUT__ << "groupAlias: " << groupAlias << __E__;
455  __SUP_COUT__ << "groupName: " << groupName << __E__;
456  __SUP_COUT__ << "groupKey: " << groupKey << __E__;
457 
458  handleSetGroupAliasInBackboneXML(xmlOut,
459  cfgMgr,
460  groupAlias,
461  groupName,
462  TableGroupKey(groupKey),
463  userInfo.username_);
464  }
465  else if(requestType == "setVersionAliasInActiveBackbone")
466  {
467  std::string versionAlias =
468  CgiDataUtilities::getData(cgiIn, "versionAlias"); // from GET
469  std::string tableName =
470  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
471  std::string version = CgiDataUtilities::getData(cgiIn, "version"); // from GET
472 
473  __SUP_COUT__ << "versionAlias: " << versionAlias << __E__;
474  __SUP_COUT__ << "tableName: " << tableName << __E__;
475  __SUP_COUT__ << "version: " << version << __E__;
476 
477  handleSetVersionAliasInBackboneXML(xmlOut,
478  cfgMgr,
479  versionAlias,
480  tableName,
481  TableVersion(version),
482  userInfo.username_);
483  }
484  else if(requestType == "setAliasOfGroupMembers")
485  {
486  std::string versionAlias =
487  CgiDataUtilities::getData(cgiIn, "versionAlias"); // from GET
488  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName"); // from
489  // GET
490  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey"); // from GET
491 
492  __SUP_COUT__ << "versionAlias: " << versionAlias << __E__;
493  __SUP_COUT__ << "groupName: " << groupName << __E__;
494  __SUP_COUT__ << "groupKey: " << groupKey << __E__;
495 
496  handleAliasGroupMembersInBackboneXML(xmlOut,
497  cfgMgr,
498  versionAlias,
499  groupName,
500  TableGroupKey(groupKey),
501  userInfo.username_);
502  }
503  else if(requestType == "getVersionAliases")
504  {
505  handleVersionAliasesXML(xmlOut, cfgMgr);
506  }
507  else if(requestType == "getTableGroups")
508  {
509  bool doNotReturnMembers =
510  CgiDataUtilities::getDataAsInt(cgiIn, "doNotReturnMembers") == 1
511  ? true
512  : false; // from GET
513 
514  __SUP_COUT__ << "doNotReturnMembers: " << doNotReturnMembers << __E__;
515  handleTableGroupsXML(xmlOut, cfgMgr, !doNotReturnMembers);
516  }
517  else if(requestType == "getTableGroupType")
518  {
519  std::string tableList =
520  CgiDataUtilities::postData(cgiIn, "tableList"); // from POST
521  __SUP_COUT__ << "tableList: " << tableList << __E__;
522 
523  handleGetTableGroupTypeXML(xmlOut, cfgMgr, tableList);
524  }
525  else if(requestType == "getTables")
526  {
527  std::string allowIllegalColumns =
528  CgiDataUtilities::getData(cgiIn, "allowIllegalColumns"); // from GET
529 
530  __SUP_COUT__ << "allowIllegalColumns: " << allowIllegalColumns << __E__;
531 
532  handleTablesXML(xmlOut, cfgMgr, allowIllegalColumns == "1");
533  }
534  else if(requestType == "getContextMemberNames")
535  {
536  std::set<std::string> members = cfgMgr->getContextMemberNames();
537 
538  for(auto& member : members)
539  xmlOut.addTextElementToData("ContextMember", member);
540  }
541  else if(requestType == "getBackboneMemberNames")
542  {
543  std::set<std::string> members = cfgMgr->getBackboneMemberNames();
544 
545  for(auto& member : members)
546  xmlOut.addTextElementToData("BackboneMember", member);
547  }
548  else if(requestType == "getIterateMemberNames")
549  {
550  std::set<std::string> members = cfgMgr->getIterateMemberNames();
551 
552  for(auto& member : members)
553  xmlOut.addTextElementToData("IterateMember", member);
554  }
555  else if(requestType == "getSpecificTableGroup")
556  {
557  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName"); // from
558  // GET
559  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey"); // from GET
560 
561  __SUP_COUT__ << "groupName: " << groupName << __E__;
562  __SUP_COUT__ << "groupKey: " << groupKey << __E__;
563 
564  handleGetTableGroupXML(xmlOut, cfgMgr, groupName, TableGroupKey(groupKey));
565  }
566  else if(requestType == "saveNewTableGroup")
567  {
568  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName"); // from
569  // GET
570  bool ignoreWarnings =
571  CgiDataUtilities::getDataAsInt(cgiIn, "ignoreWarnings"); // from GET
572  bool allowDuplicates =
573  CgiDataUtilities::getDataAsInt(cgiIn, "allowDuplicates"); // from GET
574  bool lookForEquivalent =
575  CgiDataUtilities::getDataAsInt(cgiIn, "lookForEquivalent"); // from GET
576  std::string tableList =
577  CgiDataUtilities::postData(cgiIn, "tableList"); // from POST
578  std::string comment =
579  CgiDataUtilities::getData(cgiIn, "groupComment"); // from GET
580 
581  __SUP_COUT__ << "saveNewTableGroup: " << groupName << __E__;
582  __SUP_COUT__ << "tableList: " << tableList << __E__;
583  __SUP_COUT__ << "ignoreWarnings: " << ignoreWarnings << __E__;
584  __SUP_COUT__ << "allowDuplicates: " << allowDuplicates << __E__;
585  __SUP_COUT__ << "lookForEquivalent: " << lookForEquivalent << __E__;
586  __SUP_COUT__ << "comment: " << comment << __E__;
587 
588  handleCreateTableGroupXML(xmlOut,
589  cfgMgr,
590  groupName,
591  tableList,
592  allowDuplicates,
593  ignoreWarnings,
594  comment,
595  lookForEquivalent);
596  }
597  else if(requestType == "getSpecificTable")
598  {
599  std::string tableName =
600  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
601  std::string versionStr = CgiDataUtilities::getData(cgiIn, "version"); // from GET
602  int dataOffset = CgiDataUtilities::getDataAsInt(cgiIn, "dataOffset"); // from GET
603  int chunkSize = CgiDataUtilities::getDataAsInt(cgiIn, "chunkSize"); // from GET
604 
605  std::string allowIllegalColumns =
606  CgiDataUtilities::getData(cgiIn, "allowIllegalColumns"); // from GET
607  __SUP_COUT__ << "allowIllegalColumns: " << (allowIllegalColumns == "1") << __E__;
608 
609  __SUP_COUT__ << "getSpecificTable: " << tableName << " versionStr: " << versionStr
610  << " chunkSize: " << chunkSize << " dataOffset: " << dataOffset
611  << __E__;
612 
613  TableVersion version;
614  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
615  std::string versionAlias;
616 
617  if(allTableInfo.find(tableName) != allTableInfo.end())
618  {
619  if(versionStr == "" && // take latest version if no version specified
620  allTableInfo.at(tableName).versions_.size())
621  version = *(allTableInfo.at(tableName).versions_.rbegin());
622  else if(versionStr.find(ConfigurationManager::ALIAS_VERSION_PREAMBLE) == 0)
623  {
624  // convert alias to version
625  std::map<std::string /*table*/,
626  std::map<std::string /*alias*/, TableVersion>>
627  versionAliases = cfgMgr->getVersionAliases();
628 
629  versionAlias = versionStr.substr(
630  ConfigurationManager::ALIAS_VERSION_PREAMBLE.size());
631  // if(versionAlias ==
632  // ConfigurationManager::SCRATCH_VERSION_ALIAS)
634  // {
635  // version = TableVersion::SCRATCH;
636  // __SUP_COUT__ << "version alias translated to: " << version
637  //<<
638  //__E__;
639  // }
640  // else
641  if(versionAliases.find(tableName) != versionAliases.end() &&
642  versionAliases[tableName].find(versionStr.substr(
643  ConfigurationManager::ALIAS_VERSION_PREAMBLE.size())) !=
644  versionAliases[tableName].end())
645  {
646  version = versionAliases[tableName][versionStr.substr(
647  ConfigurationManager::ALIAS_VERSION_PREAMBLE.size())];
648  __SUP_COUT__ << "version alias translated to: " << version << __E__;
649  }
650  else
651  __SUP_COUT_WARN__
652  << "version alias '"
653  << versionStr.substr(
654  ConfigurationManager::ALIAS_VERSION_PREAMBLE.size())
655  << "'was not found in active version aliases!" << __E__;
656  }
657  else // else take specified version
658  version = atoi(versionStr.c_str());
659  }
660 
661  __SUP_COUT__ << "version: " << version << __E__;
662 
663  handleGetTableXML(xmlOut,
664  cfgMgr,
665  tableName,
666  TableVersion(version),
667  (allowIllegalColumns == "1"));
668  // append author column default value
669  xmlOut.addTextElementToData("DefaultRowValue", userInfo.username_);
670  }
671  else if(requestType == "saveSpecificTable")
672  {
673  std::string tableName =
674  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
675  int version = CgiDataUtilities::getDataAsInt(cgiIn, "version"); // from GET
676  int dataOffset = CgiDataUtilities::getDataAsInt(cgiIn, "dataOffset"); // from GET
677  bool sourceTableAsIs =
678  CgiDataUtilities::getDataAsInt(cgiIn, "sourceTableAsIs"); // from GET
679  bool lookForEquivalent =
680  CgiDataUtilities::getDataAsInt(cgiIn, "lookForEquivalent"); // from GET
681  int temporary = CgiDataUtilities::getDataAsInt(cgiIn, "temporary"); // from GET
682  std::string comment =
683  CgiDataUtilities::getData(cgiIn, "tableComment"); // from GET
684 
685  std::string data = CgiDataUtilities::postData(cgiIn, "data"); // from POST
686  // data format: commas and semi-colons indicate new row
687  // r0c0,r0c1,...,r0cN,;r1c0,...
688 
689  __SUP_COUT__ << "tableName: " << tableName << " version: " << version
690  << " temporary: " << temporary << " dataOffset: " << dataOffset
691  << __E__;
692  __SUP_COUT__ << "comment: " << comment << __E__;
693  __SUP_COUT__ << "data: " << data << __E__;
694  __SUP_COUT__ << "sourceTableAsIs: " << sourceTableAsIs << __E__;
695  __SUP_COUT__ << "lookForEquivalent: " << lookForEquivalent << __E__;
696 
697  handleCreateTableXML(xmlOut,
698  cfgMgr,
699  tableName,
700  TableVersion(version),
701  temporary,
702  data,
703  dataOffset,
704  userInfo.username_,
705  comment,
706  sourceTableAsIs,
707  lookForEquivalent);
708  }
709  else if(requestType == "clearTableTemporaryVersions")
710  {
711  std::string tableName =
712  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
713  __SUP_COUT__ << "tableName: " << tableName << __E__;
714 
715  try
716  {
717  cfgMgr->eraseTemporaryVersion(tableName);
718  }
719  catch(std::runtime_error& e)
720  {
721  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
722  xmlOut.addTextElementToData(
723  "Error", "Error clearing temporary views!\n " + std::string(e.what()));
724  }
725  catch(...)
726  {
727  __SUP_COUT__ << "Error detected!\n\n " << __E__;
728  xmlOut.addTextElementToData("Error", "Error clearing temporary views! ");
729  }
730  }
731  else if(requestType == "clearTableCachedVersions")
732  {
733  std::string tableName =
734  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
735  __SUP_COUT__ << "tableName: " << tableName << __E__;
736 
737  try
738  {
739  cfgMgr->clearCachedVersions(tableName);
740  }
741  catch(std::runtime_error& e)
742  {
743  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
744  xmlOut.addTextElementToData(
745  "Error", "Error clearing cached views!\n " + std::string(e.what()));
746  }
747  catch(...)
748  {
749  __SUP_COUT__ << "Error detected!\n\n " << __E__;
750  xmlOut.addTextElementToData("Error", "Error clearing cached views! ");
751  }
752  }
753  else if(requestType == "getTreeView")
754  {
755  std::string configGroup = CgiDataUtilities::getData(cgiIn, "configGroup");
756  std::string configGroupKey = CgiDataUtilities::getData(cgiIn, "configGroupKey");
757  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
758  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
759  std::string filterList = CgiDataUtilities::postData(cgiIn, "filterList");
760  int depth = CgiDataUtilities::getDataAsInt(cgiIn, "depth");
761  bool hideStatusFalse = CgiDataUtilities::getDataAsInt(cgiIn, "hideStatusFalse");
762 
763  __SUP_COUT__ << "configGroup: " << configGroup << __E__;
764  __SUP_COUT__ << "configGroupKey: " << configGroupKey << __E__;
765  __SUP_COUT__ << "startPath: " << startPath << __E__;
766  __SUP_COUT__ << "depth: " << depth << __E__;
767  __SUP_COUT__ << "hideStatusFalse: " << hideStatusFalse << __E__;
768  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
769  __SUP_COUT__ << "filterList: " << filterList << __E__;
770 
771  handleFillTreeViewXML(xmlOut,
772  cfgMgr,
773  configGroup,
774  TableGroupKey(configGroupKey),
775  startPath,
776  depth,
777  hideStatusFalse,
778  modifiedTables,
779  filterList);
780  }
781  else if(requestType == "getTreeNodeCommonFields")
782  {
783  std::string configGroup = CgiDataUtilities::getData(cgiIn, "configGroup");
784  std::string configGroupKey = CgiDataUtilities::getData(cgiIn, "configGroupKey");
785  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
786  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
787  std::string fieldList = CgiDataUtilities::postData(cgiIn, "fieldList");
788  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
789  int depth = CgiDataUtilities::getDataAsInt(cgiIn, "depth");
790 
791  __SUP_COUT__ << "configGroup: " << configGroup << __E__;
792  __SUP_COUT__ << "configGroupKey: " << configGroupKey << __E__;
793  __SUP_COUT__ << "startPath: " << startPath << __E__;
794  __SUP_COUT__ << "depth: " << depth << __E__;
795  __SUP_COUT__ << "fieldList: " << fieldList << __E__;
796  __SUP_COUT__ << "recordList: " << recordList << __E__;
797  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
798 
799  handleFillTreeNodeCommonFieldsXML(xmlOut,
800  cfgMgr,
801  configGroup,
802  TableGroupKey(configGroupKey),
803  startPath,
804  depth,
805  modifiedTables,
806  recordList,
807  fieldList);
808  }
809  else if(requestType == "getUniqueFieldValuesForRecords")
810  {
811  std::string configGroup = CgiDataUtilities::getData(cgiIn, "configGroup");
812  std::string configGroupKey = CgiDataUtilities::getData(cgiIn, "configGroupKey");
813  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
814  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
815  std::string fieldList = CgiDataUtilities::postData(cgiIn, "fieldList");
816  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
817 
818  __SUP_COUT__ << "configGroup: " << configGroup << __E__;
819  __SUP_COUT__ << "configGroupKey: " << configGroupKey << __E__;
820  __SUP_COUT__ << "startPath: " << startPath << __E__;
821  __SUP_COUT__ << "fieldList: " << fieldList << __E__;
822  __SUP_COUT__ << "recordList: " << recordList << __E__;
823  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
824 
825  handleFillUniqueFieldValuesForRecordsXML(xmlOut,
826  cfgMgr,
827  configGroup,
828  TableGroupKey(configGroupKey),
829  startPath,
830  modifiedTables,
831  recordList,
832  fieldList);
833  }
834  else if(requestType == "getTreeNodeFieldValues")
835  {
836  std::string configGroup = CgiDataUtilities::getData(cgiIn, "configGroup");
837  std::string configGroupKey = CgiDataUtilities::getData(cgiIn, "configGroupKey");
838  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
839  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
840  std::string fieldList = CgiDataUtilities::postData(cgiIn, "fieldList");
841  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
842 
843  __SUP_COUT__ << "configGroup: " << configGroup << __E__;
844  __SUP_COUT__ << "configGroupKey: " << configGroupKey << __E__;
845  __SUP_COUT__ << "startPath: " << startPath << __E__;
846  __SUP_COUT__ << "fieldList: " << fieldList << __E__;
847  __SUP_COUT__ << "recordList: " << recordList << __E__;
848  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
849 
850  handleFillGetTreeNodeFieldValuesXML(xmlOut,
851  cfgMgr,
852  configGroup,
853  TableGroupKey(configGroupKey),
854  startPath,
855  modifiedTables,
856  recordList,
857  fieldList);
858  }
859  else if(requestType == "setTreeNodeFieldValues")
860  {
861  std::string configGroup = CgiDataUtilities::getData(cgiIn, "configGroup");
862  std::string configGroupKey = CgiDataUtilities::getData(cgiIn, "configGroupKey");
863  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
864  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
865  std::string fieldList = CgiDataUtilities::postData(cgiIn, "fieldList");
866  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
867  std::string valueList = CgiDataUtilities::postData(cgiIn, "valueList");
868 
869  __SUP_COUT__ << "configGroup: " << configGroup << __E__;
870  __SUP_COUT__ << "configGroupKey: " << configGroupKey << __E__;
871  __SUP_COUT__ << "startPath: " << startPath << __E__;
872  __SUP_COUT__ << "fieldList: " << fieldList << __E__;
873  __SUP_COUT__ << "valueList: " << valueList << __E__;
874  __SUP_COUT__ << "recordList: " << recordList << __E__;
875  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
876 
877  handleFillSetTreeNodeFieldValuesXML(xmlOut,
878  cfgMgr,
879  configGroup,
880  TableGroupKey(configGroupKey),
881  startPath,
882  modifiedTables,
883  recordList,
884  fieldList,
885  valueList,
886  userInfo.username_);
887  }
888  else if(requestType == "addTreeNodeRecords")
889  {
890  std::string configGroup = CgiDataUtilities::getData(cgiIn, "configGroup");
891  std::string configGroupKey = CgiDataUtilities::getData(cgiIn, "configGroupKey");
892  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
893  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
894  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
895 
896  __SUP_COUT__ << "configGroup: " << configGroup << __E__;
897  __SUP_COUT__ << "configGroupKey: " << configGroupKey << __E__;
898  __SUP_COUT__ << "startPath: " << startPath << __E__;
899  __SUP_COUT__ << "recordList: " << recordList << __E__;
900  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
901 
902  handleFillCreateTreeNodeRecordsXML(xmlOut,
903  cfgMgr,
904  configGroup,
905  TableGroupKey(configGroupKey),
906  startPath,
907  modifiedTables,
908  recordList,
909  userInfo.username_);
910  }
911  else if(requestType == "deleteTreeNodeRecords")
912  {
913  std::string configGroup = CgiDataUtilities::getData(cgiIn, "configGroup");
914  std::string configGroupKey = CgiDataUtilities::getData(cgiIn, "configGroupKey");
915  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
916  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
917  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
918 
919  __SUP_COUT__ << "configGroup: " << configGroup << __E__;
920  __SUP_COUT__ << "configGroupKey: " << configGroupKey << __E__;
921  __SUP_COUT__ << "startPath: " << startPath << __E__;
922  __SUP_COUT__ << "recordList: " << recordList << __E__;
923  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
924 
925  handleFillDeleteTreeNodeRecordsXML(xmlOut,
926  cfgMgr,
927  configGroup,
928  TableGroupKey(configGroupKey),
929  startPath,
930  modifiedTables,
931  recordList);
932  }
933  else if(requestType == "getAffectedActiveGroups")
934  {
935  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName");
936  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey");
937  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
938  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
939  __SUP_COUT__ << "groupName: " << groupName << __E__;
940  __SUP_COUT__ << "groupKey: " << groupKey << __E__;
941 
942  handleGetAffectedGroupsXML(
943  xmlOut, cfgMgr, groupName, TableGroupKey(groupKey), modifiedTables);
944  }
945  else if(requestType == "saveTreeNodeEdit")
946  {
947  std::string editNodeType = CgiDataUtilities::getData(cgiIn, "editNodeType");
948  std::string targetTable = CgiDataUtilities::getData(cgiIn, "targetTable");
949  std::string targetTableVersion =
950  CgiDataUtilities::getData(cgiIn, "targetTableVersion");
951  std::string targetUID = CgiDataUtilities::getData(cgiIn, "targetUID");
952  std::string targetColumn = CgiDataUtilities::getData(cgiIn, "targetColumn");
953  std::string newValue = CgiDataUtilities::postData(cgiIn, "newValue");
954 
955  __SUP_COUT__ << "editNodeType: " << editNodeType << __E__;
956  __SUP_COUT__ << "targetTable: " << targetTable << __E__;
957  __SUP_COUT__ << "targetTableVersion: " << targetTableVersion << __E__;
958  __SUP_COUT__ << "targetUID: " << targetUID << __E__;
959  __SUP_COUT__ << "targetColumn: " << targetColumn << __E__;
960  __SUP_COUT__ << "newValue: " << newValue << __E__;
961 
962  handleSaveTreeNodeEditXML(xmlOut,
963  cfgMgr,
964  targetTable,
965  TableVersion(targetTableVersion),
966  editNodeType,
967  CgiDataUtilities::decodeURIComponent(targetUID),
968  CgiDataUtilities::decodeURIComponent(targetColumn),
969  newValue,
970  userInfo.username_);
971  }
972  else if(requestType == "getLinkToChoices")
973  {
974  std::string linkToTableName = CgiDataUtilities::getData(cgiIn, "linkToTableName");
975  std::string linkToTableVersion =
976  CgiDataUtilities::getData(cgiIn, "linkToTableVersion");
977  std::string linkIdType = CgiDataUtilities::getData(cgiIn, "linkIdType");
978  std::string linkIndex = CgiDataUtilities::decodeURIComponent(
979  CgiDataUtilities::getData(cgiIn, "linkIndex"));
980  std::string linkInitId = CgiDataUtilities::getData(cgiIn, "linkInitId");
981 
982  __SUP_COUT__ << "linkToTableName: " << linkToTableName << __E__;
983  __SUP_COUT__ << "linkToTableVersion: " << linkToTableVersion << __E__;
984  __SUP_COUT__ << "linkIdType: " << linkIdType << __E__;
985  __SUP_COUT__ << "linkIndex: " << linkIndex << __E__;
986  __SUP_COUT__ << "linkInitId: " << linkInitId << __E__;
987 
988  handleGetLinkToChoicesXML(xmlOut,
989  cfgMgr,
990  linkToTableName,
991  TableVersion(linkToTableVersion),
992  linkIdType,
993  linkIndex,
994  linkInitId);
995  }
996  else if(requestType == "activateTableGroup")
997  {
998  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName");
999  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey");
1000  bool ignoreWarnings = CgiDataUtilities::getDataAsInt(cgiIn, "ignoreWarnings");
1001 
1002  __SUP_COUT__ << "Activating config: " << groupName << "(" << groupKey << ")"
1003  << __E__;
1004  __SUP_COUT__ << "ignoreWarnings: " << ignoreWarnings << __E__;
1005 
1006  // add flag for GUI handling
1007  xmlOut.addTextElementToData("AttemptedGroupActivation", "1");
1008  xmlOut.addTextElementToData("AttemptedGroupActivationName", groupName);
1009  xmlOut.addTextElementToData("AttemptedGroupActivationKey", groupKey);
1010 
1011  std::string accumulatedTreeErrors;
1012  try
1013  {
1014  cfgMgr->activateTableGroup(
1015  groupName,
1016  TableGroupKey(groupKey),
1017  ignoreWarnings
1018  ? 0
1019  : &accumulatedTreeErrors); // if ignore warning then pass null
1020  }
1021  catch(std::runtime_error& e)
1022  {
1023  // NOTE it is critical for flimsy error parsing in JS GUI to leave
1024  // single quotes around the groupName and groupKey and have them be
1025  // the first single quotes encountered in the error mesage!
1026  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
1027  xmlOut.addTextElementToData(
1028  "Error",
1029  "Error activating config group '" + groupName + "(" + groupKey + ")" +
1030  ".' Please see details below:\n\n" + std::string(e.what()));
1031  __SUP_COUT_ERR__ << "Errors detected so de-activating group: " << groupName
1032  << " (" << groupKey << ")" << __E__;
1033  try // just in case any lingering pieces, lets deactivate
1034  {
1035  cfgMgr->destroyTableGroup(groupName, true);
1036  }
1037  catch(...)
1038  {
1039  }
1040  }
1041  catch(cet::exception& e)
1042  {
1043  // NOTE it is critical for flimsy error parsing in JS GUI to leave
1044  // single quotes around the groupName and groupKey and have them be
1045  // the first single quotes encountered in the error mesage!
1046 
1047  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
1048  xmlOut.addTextElementToData("Error",
1049  "Error activating config group '" + groupName +
1050  "(" + groupKey + ")" + "!'\n\n" +
1051  std::string(e.what()));
1052  __SUP_COUT_ERR__ << "Errors detected so de-activating group: " << groupName
1053  << " (" << groupKey << ")" << __E__;
1054  try // just in case any lingering pieces, lets deactivate
1055  {
1056  cfgMgr->destroyTableGroup(groupName, true);
1057  }
1058  catch(...)
1059  {
1060  }
1061  }
1062  catch(...)
1063  {
1064  __SUP_COUT__ << "Error detected!" << __E__;
1065  throw; // unexpected exception!
1066  }
1067 
1068  if(accumulatedTreeErrors != "")
1069  xmlOut.addTextElementToData("Error",
1070  "Warnings were found when activating group '" +
1071  groupName + "(" + groupKey + ")" +
1072  "'! Please see details below:\n\n" +
1073  accumulatedTreeErrors);
1074  }
1075  else if(requestType == "getActiveTableGroups")
1076  ; // do nothing, since they are always returned
1077  else if(requestType == "copyViewToCurrentColumns")
1078  {
1079  std::string tableName =
1080  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
1081  std::string sourceVersion = CgiDataUtilities::getData(cgiIn, "sourceVersion");
1082 
1083  __SUP_COUT__ << "tableName: " << tableName << __E__;
1084  __SUP_COUT__ << "sourceVersion: " << sourceVersion << __E__;
1085  __SUP_COUT__ << "userInfo.username_: " << userInfo.username_ << __E__;
1086 
1087  // copy source version to new temporary version
1088  TableVersion newTemporaryVersion;
1089  try
1090  {
1091  // force emptying of cache for this table
1092  newTemporaryVersion =
1093  cfgMgr->copyViewToCurrentColumns(tableName, TableVersion(sourceVersion));
1094  //
1095  // getTableByName(tableName)->reset();
1096  //
1097  // //make sure source version is loaded
1098  // //need to load with loose column rules!
1099  // config = cfgMgr->getVersionedTableByName(tableName,
1100  // TableVersion(sourceVersion), true);
1101  //
1102  // //copy from source version to a new temporary version
1103  // newTemporaryVersion = config->copyView(config->getView(),
1104  // TableVersion(),userName);
1105 
1106  __SUP_COUT__ << "New temporary version = " << newTemporaryVersion << __E__;
1107  }
1108  catch(std::runtime_error& e)
1109  {
1110  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
1111  xmlOut.addTextElementToData("Error",
1112  "Error copying view from '" + tableName + "_v" +
1113  sourceVersion + "'! " +
1114  std::string(e.what()));
1115  }
1116  catch(...)
1117  {
1118  __SUP_COUT__ << "Error detected!\n\n " << __E__;
1119  xmlOut.addTextElementToData(
1120  "Error",
1121  "Error copying view from '" + tableName + "_v" + sourceVersion + "'! ");
1122  }
1123 
1124  handleGetTableXML(xmlOut, cfgMgr, tableName, newTemporaryVersion);
1125  }
1126  else if(requestType == "getLastTableGroups")
1127  {
1128  XDAQ_CONST_CALL xdaq::ApplicationDescriptor* gatewaySupervisor =
1129  allSupervisorInfo_.isWizardMode() ? allSupervisorInfo_.getWizardDescriptor()
1130  : allSupervisorInfo_.getGatewayDescriptor();
1131 
1132  std::string timeString;
1133  std::pair<std::string /*group name*/, TableGroupKey> theGroup =
1134  theRemoteWebUsers_.getLastConfigGroup(
1135  gatewaySupervisor, "Configured", timeString);
1136  xmlOut.addTextElementToData("LastConfiguredGroupName", theGroup.first);
1137  xmlOut.addTextElementToData("LastConfiguredGroupKey", theGroup.second.toString());
1138  xmlOut.addTextElementToData("LastConfiguredGroupTime", timeString);
1139  theGroup = theRemoteWebUsers_.getLastConfigGroup(
1140  gatewaySupervisor, "Started", timeString);
1141  xmlOut.addTextElementToData("LastStartedGroupName", theGroup.first);
1142  xmlOut.addTextElementToData("LastStartedGroupKey", theGroup.second.toString());
1143  xmlOut.addTextElementToData("LastStartedGroupTime", timeString);
1144  }
1145  else if(requestType == "savePlanCommandSequence")
1146  {
1147  std::string planName = CgiDataUtilities::getData(cgiIn, "planName"); // from GET
1148  std::string commands = CgiDataUtilities::postData(cgiIn, "commands"); // from
1149  // POST
1150  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
1151  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName");
1152  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey");
1153 
1154  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
1155  __SUP_COUT__ << "planName: " << planName << __E__;
1156  __SUP_COUT__ << "commands: " << commands << __E__;
1157  __SUP_COUT__ << "groupName: " << groupName << __E__;
1158  __SUP_COUT__ << "groupKey: " << groupKey << __E__;
1159 
1160  handleSavePlanCommandSequenceXML(xmlOut,
1161  cfgMgr,
1162  groupName,
1163  TableGroupKey(groupKey),
1164  modifiedTables,
1165  userInfo.username_,
1166  planName,
1167  commands);
1168  }
1169  else if(requestType == "mergeGroups")
1170  {
1171  std::string groupANameContext =
1172  CgiDataUtilities::getData(cgiIn, "groupANameContext");
1173  std::string groupAKeyContext =
1174  CgiDataUtilities::getData(cgiIn, "groupAKeyContext");
1175  std::string groupBNameContext =
1176  CgiDataUtilities::getData(cgiIn, "groupBNameContext");
1177  std::string groupBKeyContext =
1178  CgiDataUtilities::getData(cgiIn, "groupBKeyContext");
1179  std::string groupANameConfig =
1180  CgiDataUtilities::getData(cgiIn, "groupANameConfig");
1181  std::string groupAKeyConfig =
1182  CgiDataUtilities::getData(cgiIn, "groupAKeyConfig");
1183  std::string groupBNameConfig =
1184  CgiDataUtilities::getData(cgiIn, "groupBNameConfig");
1185  std::string groupBKeyConfig =
1186  CgiDataUtilities::getData(cgiIn, "groupBKeyConfig");
1187  std::string mergeApproach =
1188  CgiDataUtilities::getData(cgiIn, "mergeApproach");
1189 
1190  __SUP_COUTV__(groupANameContext);
1191  __SUP_COUTV__(groupAKeyContext);
1192  __SUP_COUTV__(groupBNameContext);
1193  __SUP_COUTV__(groupBKeyContext);
1194  __SUP_COUTV__(groupANameConfig);
1195  __SUP_COUTV__(groupAKeyConfig);
1196  __SUP_COUTV__(groupBNameConfig);
1197  __SUP_COUTV__(groupBKeyConfig);
1198  __SUP_COUTV__(mergeApproach);
1199 
1200  handleMergeGroupsXML(xmlOut,
1201  cfgMgr,
1202  groupANameContext,
1203  TableGroupKey(groupAKeyContext),
1204  groupBNameContext,
1205  TableGroupKey(groupBKeyContext),
1206  groupANameConfig,
1207  TableGroupKey(groupAKeyConfig),
1208  groupBNameConfig,
1209  TableGroupKey(groupBKeyConfig),
1210  userInfo.username_,
1211  mergeApproach);
1212  }
1213  else
1214  {
1215  __SUP_SS__ << "requestType '" << requestType << "' request not recognized."
1216  << __E__;
1217  __SUP_COUT__ << "\n" << ss.str();
1218  xmlOut.addTextElementToData("Error", ss.str());
1219  }
1220 
1221  //__SUP_COUT__ << "Wrapping up..." << __E__;
1222 
1223  // always add active config groups to xml response
1224  std::map<std::string /*type*/, std::pair<std::string /*groupName*/, TableGroupKey>>
1225  activeGroupMap = cfgMgr->getActiveTableGroups();
1226 
1227  for(auto& type : activeGroupMap)
1228  {
1229  xmlOut.addTextElementToData(type.first + "-ActiveGroupName", type.second.first);
1230  xmlOut.addTextElementToData(type.first + "-ActiveGroupKey",
1231  type.second.second.toString());
1232  //__SUP_COUT__ << "ActiveGroup " << type.first << " " << type.second.first << "("
1233  //<< type.second.second << ")" << __E__;
1234  }
1235 
1236  // always add version tracking bool
1237  xmlOut.addTextElementToData(
1238  "versionTracking",
1239  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
1240  //
1241  // __SUP_COUT__ << __E__;
1242  // xmlOut.outputXmlDocument(0,true,true);
1243  // __SUP_COUT__ << __E__;
1244 
1245 } // end ::request()
1246 catch(const std::runtime_error& e)
1247 {
1248  __SS__ << "A fatal error occurred while handling the request '" << requestType
1249  << ".' Error: " << e.what() << __E__;
1250  __COUT_ERR__ << "\n" << ss.str();
1251  xmlOut.addTextElementToData("Error", ss.str());
1252 
1253  try
1254  {
1255  // always add version tracking bool
1256  xmlOut.addTextElementToData(
1257  "versionTracking",
1258  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
1259  }
1260  catch(...)
1261  {
1262  __COUT_ERR__ << "Error getting version tracking status!" << __E__;
1263  }
1264 }
1265 catch(...)
1266 {
1267  __SS__ << "An unknown fatal error occurred while handling the request '"
1268  << requestType << ".'" << __E__;
1269  __COUT_ERR__ << "\n" << ss.str();
1270  xmlOut.addTextElementToData("Error", ss.str());
1271 
1272  try
1273  {
1274  // always add version tracking bool
1275  xmlOut.addTextElementToData(
1276  "versionTracking",
1277  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
1278  }
1279  catch(...)
1280  {
1281  __COUT_ERR__ << "Error getting version tracking status!" << __E__;
1282  }
1283 }
1284 
1285 //========================================================================================================================
1286 // handleGetAffectedGroupsXML
1287 // checks which of the active groups are affected
1288 // by the tables changing in the modified tables list.
1289 //
1290 // returns for each group affected:
1291 // the group name/key affected
1292 // and modified member map
1293 void ConfigurationGUISupervisor::handleGetAffectedGroupsXML(
1294  HttpXmlDocument& xmlOut,
1295  ConfigurationManagerRW* cfgMgr,
1296  const std::string& rootGroupName,
1297  const TableGroupKey& rootGroupKey,
1298  const std::string& modifiedTables) try
1299 {
1300  // determine type of rootGroup
1301  // replace the matching type in considered groups
1302  // for each considered table group
1303  //
1304  // check if there is a modified table that is also a member of that group
1305  // if so,
1306  // make xml entry pair
1307 
1308  std::map<std::string, std::pair<std::string, TableGroupKey>> consideredGroups =
1309  cfgMgr->getActiveTableGroups();
1310 
1311  // check that there is a context and config group to consider
1312  // if there is not, then pull from failed list
1313  if(consideredGroups[ConfigurationManager::ACTIVE_GROUP_NAME_CONTEXT]
1314  .second.isInvalid())
1315  {
1316  __SUP_COUT__ << "Finding a context group to consider..." << __E__;
1317  if(cfgMgr->getFailedTableGroups().find(
1318  ConfigurationManager::ACTIVE_GROUP_NAME_CONTEXT) !=
1319  cfgMgr->getFailedTableGroups().end())
1320  {
1321  consideredGroups[ConfigurationManager::ACTIVE_GROUP_NAME_CONTEXT] =
1322  cfgMgr->getFailedTableGroups().at(
1323  ConfigurationManager::ACTIVE_GROUP_NAME_CONTEXT);
1324  }
1325  else if(cfgMgr->getFailedTableGroups().find(
1326  ConfigurationManager::ACTIVE_GROUP_NAME_UNKNOWN) !=
1327  cfgMgr->getFailedTableGroups().end())
1328  {
1329  consideredGroups[ConfigurationManager::ACTIVE_GROUP_NAME_CONTEXT] =
1330  cfgMgr->getFailedTableGroups().at(
1331  ConfigurationManager::ACTIVE_GROUP_NAME_UNKNOWN);
1332  }
1333  }
1334  if(consideredGroups[ConfigurationManager::ACTIVE_GROUP_NAME_CONFIGURATION]
1335  .second.isInvalid())
1336  {
1337  __SUP_COUT__ << "Finding a table group to consider..." << __E__;
1338  if(cfgMgr->getFailedTableGroups().find(
1339  ConfigurationManager::ACTIVE_GROUP_NAME_CONFIGURATION) !=
1340  cfgMgr->getFailedTableGroups().end())
1341  {
1342  consideredGroups[ConfigurationManager::ACTIVE_GROUP_NAME_CONFIGURATION] =
1343  cfgMgr->getFailedTableGroups().at(
1344  ConfigurationManager::ACTIVE_GROUP_NAME_CONFIGURATION);
1345  }
1346  else if(cfgMgr->getFailedTableGroups().find(
1347  ConfigurationManager::ACTIVE_GROUP_NAME_UNKNOWN) !=
1348  cfgMgr->getFailedTableGroups().end())
1349  {
1350  consideredGroups[ConfigurationManager::ACTIVE_GROUP_NAME_CONFIGURATION] =
1351  cfgMgr->getFailedTableGroups().at(
1352  ConfigurationManager::ACTIVE_GROUP_NAME_UNKNOWN);
1353  }
1354  }
1355 
1356  __SUP_COUTV__(StringMacros::mapToString(consideredGroups));
1357 
1358  // determine the type of table group
1359  try
1360  {
1361  std::map<std::string /*name*/, TableVersion /*version*/> rootGroupMemberMap;
1362 
1363  cfgMgr->loadTableGroup(rootGroupName,
1364  rootGroupKey,
1365  0,
1366  &rootGroupMemberMap,
1367  0,
1368  0,
1369  0,
1370  0,
1371  0, // defaults
1372  true); // doNotLoadMember
1373 
1374  const std::string& groupType = cfgMgr->getTypeNameOfGroup(rootGroupMemberMap);
1375 
1376  consideredGroups[groupType] =
1377  std::pair<std::string, TableGroupKey>(rootGroupName, rootGroupKey);
1378  }
1379  catch(const std::runtime_error& e)
1380  {
1381  // if actual group name was attempted re-throw
1382  if(rootGroupName.size())
1383  {
1384  __SUP_SS__ << "Failed to determine type of table group for " << rootGroupName
1385  << "(" << rootGroupKey << ")! " << e.what() << __E__;
1386  __SUP_COUT_ERR__ << "\n" << ss.str();
1387  __SS_THROW__;
1388  }
1389 
1390  // else assume it was the intention to just consider the active groups
1391  __SUP_COUT__ << "Did not modify considered active groups due to empty root group "
1392  "name - assuming this was intentional."
1393  << __E__;
1394  }
1395  catch(...)
1396  {
1397  // if actual group name was attempted re-throw
1398  if(rootGroupName.size())
1399  {
1400  __SUP_COUT_ERR__ << "Failed to determine type of table group for "
1401  << rootGroupName << "(" << rootGroupKey << ")!" << __E__;
1402  throw;
1403  }
1404 
1405  // else assume it was the intention to just consider the active groups
1406  __SUP_COUT__ << "Did not modify considered active groups due to empty root group "
1407  "name - assuming this was intentional."
1408  << __E__;
1409  }
1410 
1411  std::map<std::string /*name*/, TableVersion /*version*/> modifiedTablesMap;
1412  std::map<std::string /*name*/, TableVersion /*version*/>::iterator
1413  modifiedTablesMapIt;
1414  {
1415  std::istringstream f(modifiedTables);
1416  std::string table, version;
1417  while(getline(f, table, ','))
1418  {
1419  getline(f, version, ',');
1420  modifiedTablesMap.insert(
1421  std::pair<std::string /*name*/, TableVersion /*version*/>(
1422  table, TableVersion(version)));
1423  }
1424  __SUP_COUT__ << modifiedTables << __E__;
1425  for(auto& pair : modifiedTablesMap)
1426  __SUP_COUT__ << "modified table " << pair.first << ":" << pair.second
1427  << __E__;
1428  }
1429 
1430  bool affected;
1431  DOMElement* parentEl;
1432  std::string groupComment;
1433  for(auto group : consideredGroups)
1434  {
1435  if(group.second.second.isInvalid())
1436  continue; // skip invalid
1437 
1438  __SUP_COUT__ << "Considering " << group.first << " group " << group.second.first
1439  << " (" << group.second.second << ")" << __E__;
1440 
1441  affected = false;
1442 
1443  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
1444  cfgMgr->loadTableGroup(group.second.first,
1445  group.second.second,
1446  0,
1447  &memberMap,
1448  0,
1449  0,
1450  &groupComment,
1451  0,
1452  0, // mostly defaults
1453  true /*doNotLoadMember*/);
1454 
1455  __SUP_COUT__ << "groupComment = " << groupComment << __E__;
1456 
1457  for(auto& table : memberMap)
1458  {
1459  if((modifiedTablesMapIt = modifiedTablesMap.find(table.first)) !=
1460  modifiedTablesMap
1461  .end() && // check if version is different for member table
1462  table.second != (*modifiedTablesMapIt).second)
1463  {
1464  __SUP_COUT__ << "Affected by " << (*modifiedTablesMapIt).first << ":"
1465  << (*modifiedTablesMapIt).second << __E__;
1466  affected = true;
1467  memberMap[table.first] = (*modifiedTablesMapIt).second;
1468  }
1469  }
1470 
1471  if(affected)
1472  {
1473  parentEl = xmlOut.addTextElementToData("AffectedActiveGroup", "");
1474  xmlOut.addTextElementToParent("GroupName", group.second.first, parentEl);
1475  xmlOut.addTextElementToParent(
1476  "GroupKey", group.second.second.toString(), parentEl);
1477  xmlOut.addTextElementToParent("GroupComment", groupComment, parentEl);
1478 
1479  for(auto& table : memberMap)
1480  {
1481  xmlOut.addTextElementToParent("MemberName", table.first, parentEl);
1482  xmlOut.addTextElementToParent(
1483  "MemberVersion", table.second.toString(), parentEl);
1484  }
1485  }
1486  }
1487 }
1488 catch(std::runtime_error& e)
1489 {
1490  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
1491  xmlOut.addTextElementToData(
1492  "Error", "Error getting affected groups! " + std::string(e.what()));
1493 }
1494 catch(...)
1495 {
1496  __SUP_COUT__ << "Error detected!\n\n " << __E__;
1497  xmlOut.addTextElementToData("Error", "Error getting affected groups! ");
1498 }
1499 
1500 //========================================================================================================================
1501 // setupActiveTables
1502 // setup active tables based on input group and modified tables
1503 //
1504 // if groupName == "" || groupKey is invalid
1505 // then do for active groups
1506 // if valid, then replace appropriate active group with specified group
1507 // Also replace active versions of modified tables with the specified version
1508 void ConfigurationGUISupervisor::setupActiveTablesXML(
1509  HttpXmlDocument& xmlOut,
1510  ConfigurationManagerRW* cfgMgr,
1511  const std::string& groupName,
1512  const TableGroupKey& groupKey,
1513  const std::string& modifiedTables,
1514  bool refreshAll,
1515  bool doGetGroupInfo,
1516  std::map<std::string /*name*/, TableVersion /*version*/>* returnMemberMap,
1517  bool outputActiveTables,
1518  std::string* accumulatedErrors) try
1519 {
1520  if(accumulatedErrors)
1521  *accumulatedErrors = "";
1522 
1523  xmlOut.addTextElementToData("configGroup", groupName);
1524  xmlOut.addTextElementToData("configGroupKey", groupKey.toString());
1525 
1526  bool usingActiveGroups = (groupName == "" || groupKey.isInvalid());
1527 
1528  // reload all tables so that partially loaded tables are not allowed
1529  if(usingActiveGroups || refreshAll)
1530  cfgMgr->getAllTableInfo(true, accumulatedErrors); // do refresh
1531 
1532  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo(false);
1533 
1534  std::map<std::string /*name*/, TableVersion /*version*/> modifiedTablesMap;
1535  std::map<std::string /*name*/, TableVersion /*version*/>::iterator
1536  modifiedTablesMapIt;
1537 
1538  if(usingActiveGroups)
1539  {
1540  // no need to load a target group
1541  __SUP_COUT__ << "Using active groups." << __E__;
1542  }
1543  else
1544  {
1545  __SUP_COUT__ << "Loading group '" << groupName << "(" << groupKey << ")'"
1546  << __E__;
1547 
1548  std::string groupComment, groupAuthor, configGroupCreationTime;
1549 
1550  // only same member map if object pointer was passed
1551  cfgMgr->loadTableGroup(groupName,
1552  groupKey,
1553  false /*doActivate*/,
1554  returnMemberMap,
1555  0 /*progressBar*/,
1556  accumulatedErrors,
1557  doGetGroupInfo ? &groupComment : 0,
1558  doGetGroupInfo ? &groupAuthor : 0,
1559  doGetGroupInfo ? &configGroupCreationTime : 0);
1560 
1561  if(doGetGroupInfo)
1562  {
1563  xmlOut.addTextElementToData("configGroupComment", groupComment);
1564  xmlOut.addTextElementToData("configGroupAuthor", groupAuthor);
1565  xmlOut.addTextElementToData("configGroupCreationTime",
1566  configGroupCreationTime);
1567  }
1568 
1569  if(accumulatedErrors && *accumulatedErrors != "")
1570  __SUP_COUTV__(*accumulatedErrors);
1571 
1572  }
1573 
1574  // extract modified tables
1575  {
1576  std::istringstream f(modifiedTables);
1577  std::string table, version;
1578  while(getline(f, table, ','))
1579  {
1580  getline(f, version, ',');
1581  modifiedTablesMap.insert(
1582  std::pair<std::string /*name*/, TableVersion /*version*/>(
1583  table, TableVersion(version)));
1584  }
1585  //__SUP_COUT__ << modifiedTables << __E__;
1586  for(auto& pair : modifiedTablesMap)
1587  __SUP_COUT__ << "modified table " << pair.first << ":" << pair.second
1588  << __E__;
1589  }
1590 
1591  // add all active table pairs to xmlOut
1592  std::map<std::string, TableVersion> allActivePairs = cfgMgr->getActiveVersions();
1593  xmlOut.addTextElementToData("DefaultNoLink",
1594  TableViewColumnInfo::DATATYPE_LINK_DEFAULT);
1595  for(auto& activePair : allActivePairs)
1596  {
1597  if(outputActiveTables)
1598  xmlOut.addTextElementToData("ActiveTableName", activePair.first);
1599 
1600  // check if name is in modifiedTables
1601  // if so, activate the temporary version
1602  if((modifiedTablesMapIt = modifiedTablesMap.find(activePair.first)) !=
1603  modifiedTablesMap.end())
1604  {
1605  __SUP_COUT__ << "Found modified table " << (*modifiedTablesMapIt).first
1606  << ": trying... " << (*modifiedTablesMapIt).second << __E__;
1607 
1608  try
1609  {
1610  allTableInfo.at(activePair.first)
1611  .tablePtr_->setActiveView((*modifiedTablesMapIt).second);
1612  }
1613  catch(...)
1614  {
1615  __SUP_SS__
1616  << "Modified table version v" << (*modifiedTablesMapIt).second
1617  << " failed. Reverting to v"
1618  << allTableInfo.at(activePair.first).tablePtr_->getView().getVersion()
1619  << "." << __E__;
1620  __SUP_COUT_WARN__ << "Warning detected!\n\n " << ss.str() << __E__;
1621  xmlOut.addTextElementToData(
1622  "Warning",
1623  "Error setting up active tables!\n\n" + std::string(ss.str()));
1624  }
1625  }
1626 
1627  if(outputActiveTables)
1628  {
1629  xmlOut.addTextElementToData("ActiveTableVersion",
1630  allTableInfo.at(activePair.first)
1631  .tablePtr_->getView()
1632  .getVersion()
1633  .toString());
1634  xmlOut.addTextElementToData(
1635  "ActiveTableComment",
1636  allTableInfo.at(activePair.first).tablePtr_->getView().getComment());
1637  }
1638 
1639  //__SUP_COUT__ << "Active table = " <<
1640  // activePair.first << "-v" <<
1641  // allTableInfo.at(activePair.first).tablePtr_->getView().getVersion() <<
1642  //__E__;
1643  }
1644 }
1645 catch(std::runtime_error& e)
1646 {
1647  __SUP_SS__ << ("Error setting up active tables!\n\n" + std::string(e.what()))
1648  << __E__;
1649  __SUP_COUT_ERR__ << "\n" << ss.str();
1650  xmlOut.addTextElementToData("Error", ss.str());
1651 }
1652 catch(...)
1653 {
1654  __SUP_SS__ << ("Error setting up active tables!\n\n") << __E__;
1655  __SUP_COUT_ERR__ << "\n" << ss.str();
1656  xmlOut.addTextElementToData("Error", ss.str());
1657 }
1658 
1659 //========================================================================================================================
1660 // handleFillCreateTreeNodeRecordsXML
1661 // Creates the records in the appropriate table
1662 // and creates a temporary version.
1663 // the modified-<modified tables> list is returned in xml
1664 //
1665 // if groupName == "" || groupKey is invalid
1666 // then do for active groups
1667 //
1668 // parameters
1669 // configGroupName (full name with key)
1670 // starting node path
1671 // modifiedTables := CSV of table/version pairs
1672 // recordList := CSV list of records to create
1673 //
1674 void ConfigurationGUISupervisor::handleFillCreateTreeNodeRecordsXML(
1675  HttpXmlDocument& xmlOut,
1676  ConfigurationManagerRW* cfgMgr,
1677  const std::string& groupName,
1678  const TableGroupKey& groupKey,
1679  const std::string& startPath,
1680  const std::string& modifiedTables,
1681  const std::string& recordList,
1682  const std::string& author)
1683 {
1684  // setup active tables based on input group and modified tables
1685  setupActiveTablesXML(xmlOut,
1686  cfgMgr,
1687  groupName,
1688  groupKey,
1689  modifiedTables,
1690  true /* refresh all */,
1691  false /* getGroupInfo */,
1692  0 /* returnMemberMap */,
1693  false /* outputActiveTables */);
1694 
1695  try
1696  {
1697  ConfigurationTree targetNode = cfgMgr->getNode(startPath);
1698  TableBase* config = cfgMgr->getTableByName(targetNode.getTableName());
1699 
1700  __SUP_COUT__ << config->getTableName() << __E__;
1701  TableVersion temporaryVersion;
1702 
1703  // if current version is not temporary
1704  // create temporary
1705  // else re-modify temporary version
1706  // edit temporary version directly
1707  // then after all edits return active versions
1708  //
1709 
1710  bool firstSave = true;
1711 
1712  // save current version
1713  TableView backupView;
1714 
1715  // extract record list
1716  {
1717  std::istringstream f(recordList);
1718  std::string recordUID;
1719  unsigned int i;
1720 
1721  while(getline(f, recordUID, ',')) // for each record
1722  {
1723  recordUID = StringMacros::decodeURIComponent(recordUID);
1724 
1725  __SUP_COUT__ << "recordUID " << recordUID << __E__;
1726 
1727  if(firstSave) // handle version bookkeeping
1728  {
1729  if(!(temporaryVersion = targetNode.getTableVersion())
1730  .isTemporaryVersion())
1731  {
1732  __SUP_COUT__ << "Start version " << temporaryVersion << __E__;
1733  // create temporary version for editing
1734  temporaryVersion = config->createTemporaryView(temporaryVersion);
1735  cfgMgr->saveNewTable(targetNode.getTableName(),
1736  temporaryVersion,
1737  true); // proper bookkeeping for temporary
1738  // version with the new version
1739 
1740  __SUP_COUT__ << "Created temporary version " << temporaryVersion
1741  << __E__;
1742  }
1743  else // else table is already temporary version
1744  __SUP_COUT__ << "Using temporary version " << temporaryVersion
1745  << __E__;
1746 
1747  firstSave = false;
1748 
1749  // copy original to backup before modifying
1750  backupView.copy(config->getView(), temporaryVersion, author);
1751  }
1752 
1753  // at this point have valid temporary version to edit
1754 
1755  // copy "table-newRow" type edit from handleSaveTreeNodeEditXML()
1756  // functionality
1757 
1758  // add row
1759  unsigned int row = config->getViewP()->addRow(
1760  author, true /*incrementUniqueData*/); // increment all unique data
1761  // fields to void conflict
1762 
1763  // if TableViewColumnInfo::COL_NAME_STATUS exists, set it to true
1764  try
1765  {
1766  unsigned int col = config->getViewP()->getColStatus();
1767  config->getViewP()->setURIEncodedValue("1", row, col);
1768  }
1769  catch(...)
1770  {
1771  } // if not, ignore
1772 
1773  // set UID value
1774  config->getViewP()->setURIEncodedValue(
1775  recordUID, row, config->getViewP()->getColUID());
1776  }
1777  }
1778 
1779  if(!firstSave) // only test table if there was a change
1780  {
1781  try
1782  {
1783  config->getViewP()->init(); // verify new table (throws runtime_errors)
1784  }
1785  catch(...)
1786  {
1787  __SUP_COUT_INFO__ << "Reverting to original view." << __E__;
1788  __SUP_COUT__ << "Before:" << __E__;
1789  config->getViewP()->print();
1790  config->getViewP()->copy(backupView, temporaryVersion, author);
1791  __SUP_COUT__ << "After:" << __E__;
1792  config->getViewP()->print();
1793 
1794  throw; // rethrow
1795  }
1796  }
1797 
1798  handleFillModifiedTablesXML(xmlOut, cfgMgr);
1799  }
1800  catch(std::runtime_error& e)
1801  {
1802  __SUP_SS__ << ("Error creating new record(s)!\n\n" + std::string(e.what()))
1803  << __E__;
1804  __SUP_COUT_ERR__ << "\n" << ss.str();
1805  xmlOut.addTextElementToData("Error", ss.str());
1806  }
1807  catch(...)
1808  {
1809  __SUP_SS__ << ("Error creating new record(s)!\n\n") << __E__;
1810  __SUP_COUT_ERR__ << "\n" << ss.str();
1811  xmlOut.addTextElementToData("Error", ss.str());
1812  }
1813 }
1814 
1815 //========================================================================================================================
1816 // handleFillModifiedTablesXML
1817 // fills <modified tables> as used by ConfigurationAPI
1818 void ConfigurationGUISupervisor::handleFillModifiedTablesXML(
1819  HttpXmlDocument& xmlOut, ConfigurationManagerRW* cfgMgr) try
1820 {
1821  // return modified <modified tables>
1822  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
1823  std::map<std::string, TableVersion> allActivePairs = cfgMgr->getActiveVersions();
1824  for(auto& activePair : allActivePairs)
1825  {
1826  xmlOut.addTextElementToData("NewActiveTableName", activePair.first);
1827  xmlOut.addTextElementToData("NewActiveTableVersion",
1828  allTableInfo.at(activePair.first)
1829  .tablePtr_->getView()
1830  .getVersion()
1831  .toString());
1832  xmlOut.addTextElementToData(
1833  "NewActiveTableComment",
1834  allTableInfo.at(activePair.first).tablePtr_->getView().getComment());
1835  }
1836 }
1837 catch(std::runtime_error& e)
1838 {
1839  __SUP_SS__ << ("Error!\n\n" + std::string(e.what())) << __E__;
1840  __SUP_COUT_ERR__ << "\n" << ss.str();
1841  xmlOut.addTextElementToData("Error", ss.str());
1842 }
1843 catch(...)
1844 {
1845  __SUP_SS__ << ("Error!\n\n") << __E__;
1846  __SUP_COUT_ERR__ << "\n" << ss.str();
1847  xmlOut.addTextElementToData("Error", ss.str());
1848 }
1849 
1850 //========================================================================================================================
1851 // handleFillDeleteTreeNodeRecordsXML
1852 // Deletes the records in the appropriate table
1853 // and creates a temporary version.
1854 // the modified-<modified tables> list is returned in xml
1855 //
1856 // if groupName == "" || groupKey is invalid
1857 // then do for active groups
1858 //
1859 // parameters
1860 // configGroupName (full name with key)
1861 // starting node path
1862 // modifiedTables := CSV of table/version pairs
1863 // recordList := CSV list of records to create
1864 //
1865 void ConfigurationGUISupervisor::handleFillDeleteTreeNodeRecordsXML(
1866  HttpXmlDocument& xmlOut,
1867  ConfigurationManagerRW* cfgMgr,
1868  const std::string& groupName,
1869  const TableGroupKey& groupKey,
1870  const std::string& startPath,
1871  const std::string& modifiedTables,
1872  const std::string& recordList)
1873 {
1874  // setup active tables based on input group and modified tables
1875  setupActiveTablesXML(xmlOut,
1876  cfgMgr,
1877  groupName,
1878  groupKey,
1879  modifiedTables,
1880  true /* refresh all */,
1881  false /* getGroupInfo */,
1882  0 /* returnMemberMap */,
1883  false /* outputActiveTables */);
1884 
1885  try
1886  {
1887  ConfigurationTree targetNode = cfgMgr->getNode(startPath);
1888  TableBase* config = cfgMgr->getTableByName(targetNode.getTableName());
1889 
1890  __SUP_COUT__ << config->getTableName() << __E__;
1891  TableVersion temporaryVersion;
1892 
1893  // if current version is not temporary
1894  // create temporary
1895  // else re-modify temporary version
1896  // edit temporary version directly
1897  // then after all edits return active versions
1898  //
1899 
1900  bool firstSave = true;
1901 
1902  // extract record list
1903  {
1904  std::istringstream f(recordList);
1905  std::string recordUID;
1906  unsigned int i;
1907 
1908  while(getline(f, recordUID, ',')) // for each record
1909  {
1910  recordUID = StringMacros::decodeURIComponent(recordUID);
1911 
1912  __SUP_COUT__ << "recordUID " << recordUID << __E__;
1913 
1914  if(firstSave) // handle version bookkeeping
1915  {
1916  if(!(temporaryVersion = targetNode.getTableVersion())
1917  .isTemporaryVersion())
1918  {
1919  __SUP_COUT__ << "Start version " << temporaryVersion << __E__;
1920  // create temporary version for editing
1921  temporaryVersion = config->createTemporaryView(temporaryVersion);
1922  cfgMgr->saveNewTable(targetNode.getTableName(),
1923  temporaryVersion,
1924  true); // proper bookkeeping for temporary
1925  // version with the new version
1926 
1927  __SUP_COUT__ << "Created temporary version " << temporaryVersion
1928  << __E__;
1929  }
1930  else // else table is already temporary version
1931  __SUP_COUT__ << "Using temporary version " << temporaryVersion
1932  << __E__;
1933 
1934  firstSave = false;
1935  }
1936 
1937  // at this point have valid temporary version to edit
1938 
1939  // copy "delete-uid" type edit from handleSaveTreeNodeEditXML()
1940  // functionality
1941  unsigned int row = config->getViewP()->findRow(
1942  config->getViewP()->getColUID(), recordUID);
1943  config->getViewP()->deleteRow(row);
1944  }
1945  }
1946 
1947  if(!firstSave) // only test table if there was a change
1948  config->getViewP()->init(); // verify new table (throws runtime_errors)
1949 
1950  handleFillModifiedTablesXML(xmlOut, cfgMgr);
1951  }
1952  catch(std::runtime_error& e)
1953  {
1954  __SUP_SS__ << ("Error removing record(s)!\n\n" + std::string(e.what())) << __E__;
1955  __SUP_COUT_ERR__ << "\n" << ss.str();
1956  xmlOut.addTextElementToData("Error", ss.str());
1957  }
1958  catch(...)
1959  {
1960  __SUP_SS__ << ("Error removing record(s)!\n\n") << __E__;
1961  __SUP_COUT_ERR__ << "\n" << ss.str();
1962  xmlOut.addTextElementToData("Error", ss.str());
1963  }
1964 }
1965 
1966 //========================================================================================================================
1967 // handleFillSetTreeNodeFieldValuesXML
1968 // writes for each record, the field/value pairs to the appropriate table
1969 // and creates a temporary version.
1970 // the modified-<modified tables> list is returned in xml
1971 //
1972 // if groupName == "" || groupKey is invalid
1973 // then do for active groups
1974 //
1975 // parameters
1976 // configGroupName (full name with key)
1977 // starting node path
1978 // modifiedTables := CSV of table/version pairs
1979 // recordList := CSV list of records for which to write values for fields
1980 // fieldList := CSV of relative-to-record-path to fields to write to each record
1981 // valueList := CSV of values corresponding to fields
1982 //
1983 void ConfigurationGUISupervisor::handleFillSetTreeNodeFieldValuesXML(
1984  HttpXmlDocument& xmlOut,
1985  ConfigurationManagerRW* cfgMgr,
1986  const std::string& groupName,
1987  const TableGroupKey& groupKey,
1988  const std::string& startPath,
1989  const std::string& modifiedTables,
1990  const std::string& recordList,
1991  const std::string& fieldList,
1992  const std::string& valueList,
1993  const std::string& author)
1994 {
1995  // setup active tables based on input group and modified tables
1996  setupActiveTablesXML(xmlOut,
1997  cfgMgr,
1998  groupName,
1999  groupKey,
2000  modifiedTables,
2001  true /* refresh all */,
2002  false /* getGroupInfo */,
2003  0 /* returnMemberMap */,
2004  false /* outputActiveTables */);
2005 
2006  // for each field
2007  // return field/value pair in xml
2008 
2009  try
2010  {
2011  std::vector<std::string /*relative-path*/> fieldPaths;
2012  // extract field list
2013  {
2014  std::istringstream f(fieldList);
2015  std::string fieldPath;
2016  while(getline(f, fieldPath, ','))
2017  {
2018  fieldPaths.push_back(StringMacros::decodeURIComponent(fieldPath));
2019  }
2020  __SUP_COUT__ << fieldList << __E__;
2021  for(const auto& field : fieldPaths)
2022  __SUP_COUT__ << "fieldPath " << field << __E__;
2023  }
2024 
2025  std::vector<std::string /*relative-path*/> fieldValues;
2026  // extract value list
2027  {
2028  std::istringstream f(valueList);
2029  std::string fieldValue;
2030  while(getline(f, fieldValue, ','))
2031  {
2032  fieldValues.push_back(
2033  fieldValue); // setURIEncodedValue is expected
2034  // StringMacros::decodeURIComponent(fieldValue));
2035  }
2036 
2037  // if last value is "" then push empty value
2038  if(valueList.size() && valueList[valueList.size() - 1] == ',')
2039  fieldValues.push_back("");
2040 
2041  __SUP_COUT__ << valueList << __E__;
2042  for(const auto& value : fieldValues)
2043  __SUP_COUT__ << "fieldValue " << value << __E__;
2044  }
2045 
2046  if(fieldPaths.size() != fieldValues.size())
2047  {
2048  __SUP_SS__;
2049  __THROW__(ss.str() + "Mismatch in fields and values array size!");
2050  }
2051 
2052  // extract record list
2053  {
2054  TableBase* config;
2055  TableVersion temporaryVersion;
2056  std::istringstream f(recordList);
2057  std::string recordUID;
2058  unsigned int i;
2059 
2060  while(getline(f, recordUID, ',')) // for each record
2061  {
2062  recordUID = StringMacros::decodeURIComponent(recordUID);
2063 
2064  //__SUP_COUT__ << "recordUID " << recordUID << __E__;
2065 
2066  DOMElement* parentEl =
2067  xmlOut.addTextElementToData("fieldValues", recordUID);
2068 
2069  // for each field, set value
2070  for(i = 0; i < fieldPaths.size(); ++i)
2071  {
2072  __SUP_COUT__ << "fieldPath " << fieldPaths[i] << __E__;
2073  __SUP_COUT__ << "fieldValue " << fieldValues[i] << __E__;
2074 
2075  // doNotThrowOnBrokenUIDLinks so that link UIDs can be edited like
2076  // other fields
2077  ConfigurationTree targetNode =
2078  cfgMgr->getNode(startPath + "/" + recordUID + "/" + fieldPaths[i],
2079  true /*doNotThrowOnBrokenUIDLinks*/);
2080 
2081  // need table, uid, columnName to set a value
2082 
2083  // assume correct table version is loaded by setupActiveTablesXML()
2084  // config = cfgMgr->getTableByName(
2085  // targetNode.getTableName());
2086  //
2087  //__SUP_COUT__ << "Active version is " << config->getViewVersion() <<
2088  //__E__;
2089 
2090  // mimic handleSaveTreeNodeEditXML L 1750
2091  // Actually call it! ..
2092  // with a modifier?
2093  // or
2094  // handleSaveTreeNodeEditXML(xmlOut,
2095  // cfgMgr,
2096  // targetNode.getTableName(),
2097  // targetNode.getTableVersion(),
2098  // "value",
2099  // targetNode.getUIDAsString(),
2100  // targetNode.getValueName(), //col name
2101  // fieldValues[i]
2102  // );
2103 
2104  // or
2105  // (because problem is this would create a new temporary version each
2106  // time) if current version is not temporary
2107  // create temporary
2108  // else re-modify temporary version
2109  // edit temporary version directly
2110  // then after all edits return active versions
2111  //
2112 
2113  __SUP_COUT__ << "Getting table " << targetNode.getFieldTableName()
2114  << __E__;
2115 
2116  // if link must get parent config name
2117  config = cfgMgr->getTableByName(
2118  targetNode.getFieldTableName()); // NOT getTableName!
2119  if(!(temporaryVersion = config->getViewP()->getVersion())
2120  .isTemporaryVersion())
2121  {
2122  // create temporary version for editing
2123  temporaryVersion =
2124  config->createTemporaryView(config->getViewP()->getVersion());
2125  cfgMgr->saveNewTable(config->getTableName(),
2126  temporaryVersion,
2127  true); // proper bookkeeping for temporary
2128  // version with the new version
2129 
2130  __SUP_COUT__ << "Created temporary version "
2131  << config->getTableName() << "-v" << temporaryVersion
2132  << __E__;
2133  }
2134  // else //else table is already temporary version
2135  __SUP_COUT__ << "Using temporary version " << config->getTableName()
2136  << "-v" << temporaryVersion << __E__;
2137 
2138  // copy "value" type edit from handleSaveTreeNodeEditXML()
2139  // functionality
2140  config->getViewP()->setURIEncodedValue(fieldValues[i],
2141  targetNode.getFieldRow(),
2142  targetNode.getFieldColumn(),
2143  author);
2144 
2145  config->getViewP()
2146  ->init(); // verify new table (throws runtime_errors)
2147  }
2148  }
2149  }
2150 
2151  handleFillModifiedTablesXML(xmlOut, cfgMgr);
2152  }
2153  catch(std::runtime_error& e)
2154  {
2155  __SUP_SS__ << ("Error setting field values!\n\n" + std::string(e.what()))
2156  << __E__;
2157  __SUP_COUT_ERR__ << "\n" << ss.str();
2158  xmlOut.addTextElementToData("Error", ss.str());
2159  }
2160  catch(...)
2161  {
2162  __SUP_SS__ << ("Error setting field values!\n\n") << __E__;
2163  __SUP_COUT_ERR__ << "\n" << ss.str();
2164  xmlOut.addTextElementToData("Error", ss.str());
2165  }
2166 }
2167 
2168 //========================================================================================================================
2169 // handleFillGetTreeNodeFieldValuesXML
2170 // returns for each record, xml list of field/value pairs
2171 // field := relative-path
2172 //
2173 // if groupName == "" || groupKey is invalid
2174 // then do for active groups
2175 //
2176 // parameters
2177 // configGroupName (full name with key)
2178 // starting node path
2179 // modifiedTables := CSV of table/version pairs
2180 // recordStr := CSV list of records for which to lookup values for fields
2181 // fieldList := CSV of relative-to-record-path to filter common fields
2182 //
2183 void ConfigurationGUISupervisor::handleFillGetTreeNodeFieldValuesXML(
2184  HttpXmlDocument& xmlOut,
2185  ConfigurationManagerRW* cfgMgr,
2186  const std::string& groupName,
2187  const TableGroupKey& groupKey,
2188  const std::string& startPath,
2189  const std::string& modifiedTables,
2190  const std::string& recordList,
2191  const std::string& fieldList)
2192 {
2193  // setup active tables based on input group and modified tables
2194  setupActiveTablesXML(xmlOut, cfgMgr, groupName, groupKey, modifiedTables);
2195 
2196  // for each field
2197  // return field/value pair in xml
2198 
2199  try
2200  {
2201  std::vector<std::string /*relative-path*/> fieldPaths;
2202  // extract field list
2203  {
2204  std::istringstream f(fieldList);
2205  std::string fieldPath;
2206  while(getline(f, fieldPath, ','))
2207  {
2208  fieldPaths.push_back(StringMacros::decodeURIComponent(fieldPath));
2209  }
2210  __SUP_COUT__ << fieldList << __E__;
2211  for(auto& field : fieldPaths)
2212  __SUP_COUT__ << "fieldPath " << field << __E__;
2213  }
2214 
2215  // extract record list
2216  {
2217  std::istringstream f(recordList);
2218  std::string recordUID;
2219  while(getline(f, recordUID, ',')) // for each record
2220  {
2221  recordUID = StringMacros::decodeURIComponent(recordUID);
2222 
2223  __SUP_COUT__ << "recordUID " << recordUID << __E__;
2224 
2225  DOMElement* parentEl =
2226  xmlOut.addTextElementToData("fieldValues", recordUID);
2227 
2228  // for each field, get value
2229  for(const auto& fieldPath : fieldPaths)
2230  {
2231  __SUP_COUT__ << "fieldPath " << fieldPath << __E__;
2232 
2233  xmlOut.addTextElementToParent("FieldPath", fieldPath, parentEl);
2234  xmlOut.addTextElementToParent(
2235  "FieldValue",
2236  cfgMgr->getNode(startPath + "/" + recordUID + "/" + fieldPath)
2237  .getValueAsString(),
2238  parentEl);
2239  }
2240  }
2241  }
2242  }
2243  catch(std::runtime_error& e)
2244  {
2245  __SUP_SS__ << ("Error getting field values!\n\n" + std::string(e.what()))
2246  << __E__;
2247  __SUP_COUT_ERR__ << "\n" << ss.str();
2248  xmlOut.addTextElementToData("Error", ss.str());
2249  }
2250  catch(...)
2251  {
2252  __SUP_SS__ << ("Error getting field values!\n\n") << __E__;
2253  __SUP_COUT_ERR__ << "\n" << ss.str();
2254  xmlOut.addTextElementToData("Error", ss.str());
2255  }
2256 }
2257 
2258 //========================================================================================================================
2259 // handleFillTreeNodeCommonFieldsXML
2260 // returns xml list of common fields among records
2261 // field := relative-path
2262 //
2263 // if groupName == "" || groupKey is invalid
2264 // then do for active groups
2265 //
2266 // parameters
2267 // configGroupName (full name with key)
2268 // starting node path
2269 // depth from starting node path
2270 // modifiedTables := CSV of table/version pairs
2271 // recordList := CSV of records to search for fields
2272 // fieldList := CSV of relative-to-record-path to filter common fields
2273 // (accept or reject [use ! as first character to reject])
2274 // [use leading* to ignore relative path - note that only leading and trailing
2275 // wildcards work]
2276 //
2277 void ConfigurationGUISupervisor::handleFillTreeNodeCommonFieldsXML(
2278  HttpXmlDocument& xmlOut,
2279  ConfigurationManagerRW* cfgMgr,
2280  const std::string& groupName,
2281  const TableGroupKey& groupKey,
2282  const std::string& startPath,
2283  unsigned int depth,
2284  const std::string& modifiedTables,
2285  const std::string& recordList,
2286  const std::string& fieldList)
2287 {
2288  // setup active tables based on input group and modified tables
2289  setupActiveTablesXML(xmlOut, cfgMgr, groupName, groupKey, modifiedTables);
2290 
2291  try
2292  {
2293  DOMElement* parentEl = xmlOut.addTextElementToData("fields", startPath);
2294 
2295  if(depth == 0)
2296  {
2297  __SUP_SS__ << "Depth of search must be greater than 0." << __E__;
2298  __SUP_COUT__ << ss.str();
2299  __SS_THROW__; // done if 0 depth, no fields
2300  }
2301 
2302  // do not allow traversing for common fields from root level
2303  // the tree view should be used for such a purpose
2304  // if(startPath == "/")
2305  // return;
2306 
2307  std::vector<ConfigurationTree::RecordField> retFieldList;
2308 
2309  {
2310  ConfigurationTree startNode = cfgMgr->getNode(startPath);
2311  if(startNode.isLinkNode() && startNode.isDisconnected())
2312  {
2313  __SUP_SS__ << "Start path was a disconnected link node!" << __E__;
2314  __SUP_COUT_ERR__ << "\n" << ss.str();
2315  __SS_THROW__;
2316  return; // quietly ignore disconnected links at depth
2317  // note: at the root level they will be flagged for the user
2318  }
2319 
2320  std::vector<std::string /*relative-path*/> fieldAcceptList, fieldRejectList;
2321  if(fieldList != "")
2322  {
2323  // extract field filter list
2324  {
2325  std::istringstream f(fieldList);
2326  std::string fieldPath, decodedFieldPath;
2327  while(getline(f, fieldPath, ','))
2328  {
2329  decodedFieldPath = StringMacros::decodeURIComponent(fieldPath);
2330 
2331  if(decodedFieldPath[0] == '!') // reject field
2332  fieldRejectList.push_back(decodedFieldPath.substr(1));
2333  else
2334  fieldAcceptList.push_back(decodedFieldPath);
2335  }
2336  __SUP_COUT__ << fieldList << __E__;
2337  for(auto& field : fieldAcceptList)
2338  __SUP_COUT__ << "fieldAcceptList " << field << __E__;
2339  for(auto& field : fieldRejectList)
2340  __SUP_COUT__ << "fieldRejectList " << field << __E__;
2341  }
2342  }
2343 
2344  std::vector<std::string /*relative-path*/> records;
2345  if(recordList == "*") // handle all records case
2346  {
2347  records.clear();
2348  records = startNode.getChildrenNames();
2349  __SUP_COUT__ << "Translating wildcard..." << __E__;
2350  for(auto& record : records)
2351  __SUP_COUT__ << "recordList " << record << __E__;
2352  }
2353  else if(recordList != "")
2354  {
2355  // extract record list
2356  {
2357  std::istringstream f(recordList);
2358  std::string recordStr;
2359  while(getline(f, recordStr, ','))
2360  {
2361  records.push_back(StringMacros::decodeURIComponent(recordStr));
2362  }
2363  __SUP_COUT__ << recordList << __E__;
2364  for(auto& record : records)
2365  __SUP_COUT__ << "recordList " << record << __E__;
2366  }
2367  }
2368 
2369  retFieldList = startNode.getCommonFields(
2370  records, fieldAcceptList, fieldRejectList, depth);
2371  }
2372 
2373  DOMElement* parentTypeEl;
2374  for(const auto& fieldInfo : retFieldList)
2375  {
2376  xmlOut.addTextElementToParent(
2377  "FieldTableName", fieldInfo.tableName_, parentEl);
2378  xmlOut.addTextElementToParent(
2379  "FieldColumnName", fieldInfo.columnName_, parentEl);
2380  xmlOut.addTextElementToParent(
2381  "FieldRelativePath", fieldInfo.relativePath_, parentEl);
2382  xmlOut.addTextElementToParent(
2383  "FieldColumnType", fieldInfo.columnInfo_->getType(), parentEl);
2384  xmlOut.addTextElementToParent(
2385  "FieldColumnDataType", fieldInfo.columnInfo_->getDataType(), parentEl);
2386  xmlOut.addTextElementToParent("FieldColumnDefaultValue",
2387  fieldInfo.columnInfo_->getDefaultValue(),
2388  parentEl);
2389 
2390  parentTypeEl =
2391  xmlOut.addTextElementToParent("FieldColumnDataChoices", "", parentEl);
2392 
2393  // if there are associated data choices, send info
2394  auto dataChoices = fieldInfo.columnInfo_->getDataChoices();
2395  xmlOut.addTextElementToParent(
2396  "FieldColumnDataChoice", // add default to list to mimic tree handling
2397  fieldInfo.columnInfo_->getDefaultValue(),
2398  parentTypeEl);
2399  for(const auto& dataChoice : dataChoices)
2400  xmlOut.addTextElementToParent(
2401  "FieldColumnDataChoice", dataChoice, parentTypeEl);
2402  }
2403  }
2404  catch(std::runtime_error& e)
2405  {
2406  __SUP_SS__ << ("Error getting common fields!\n\n" + std::string(e.what()))
2407  << __E__;
2408  __SUP_COUT_ERR__ << "\n" << ss.str();
2409  xmlOut.addTextElementToData("Error", ss.str());
2410  }
2411  catch(...)
2412  {
2413  __SUP_SS__ << ("Error getting common fields!\n\n") << __E__;
2414  __SUP_COUT_ERR__ << "\n" << ss.str();
2415  xmlOut.addTextElementToData("Error", ss.str());
2416  }
2417 }
2418 
2419 //========================================================================================================================
2420 // handleFillUniqueFieldValuesForRecordsXML
2421 // returns xml list of unique values for each fields among records
2422 // field := relative-path
2423 //
2424 // return xml
2425 // <xml>
2426 // <field val=relative-path>
2427 // <unique_val val=uval0>
2428 // <unique_val val=uval1>
2429 // .. next unique value
2430 // </field>
2431 // ... next field
2432 // </xml>
2433 //
2434 // if groupName == "" || groupKey is invalid
2435 // then do for active groups
2436 //
2437 // parameters
2438 // configGroupName (full name with key)
2439 // starting node path
2440 // modifiedTables := CSV of table/version pairs
2441 // recordList := CSV of records to search for unique values
2442 // fieldList := CSV of fields relative-to-record-path for which to get list of unique
2443 // values
2444 //
2445 void ConfigurationGUISupervisor::handleFillUniqueFieldValuesForRecordsXML(
2446  HttpXmlDocument& xmlOut,
2447  ConfigurationManagerRW* cfgMgr,
2448  const std::string& groupName,
2449  const TableGroupKey& groupKey,
2450  const std::string& startPath,
2451  const std::string& modifiedTables,
2452  const std::string& recordList,
2453  const std::string& fieldList)
2454 {
2455  // setup active tables based on input group and modified tables
2456  setupActiveTablesXML(xmlOut, cfgMgr, groupName, groupKey, modifiedTables);
2457 
2458  try
2459  {
2460  // do not allow traversing for common fields from root level
2461  // the tree view should be used for such a purpose
2462  if(startPath == "/")
2463  return;
2464 
2465  std::vector<std::string /*relative-path*/> fieldsToGet;
2466  if(fieldList != "")
2467  {
2468  // extract field filter list
2469  {
2470  std::istringstream f(fieldList);
2471  std::string fieldPath;
2472  while(getline(f, fieldPath, ','))
2473  {
2474  fieldsToGet.push_back(StringMacros::decodeURIComponent(fieldPath));
2475  }
2476  __SUP_COUT__ << fieldList << __E__;
2477  for(auto& field : fieldsToGet)
2478  __SUP_COUT__ << "fieldsToGet " << field << __E__;
2479  }
2480  }
2481 
2482  ConfigurationTree startNode = cfgMgr->getNode(startPath);
2483  if(startNode.isLinkNode() && startNode.isDisconnected())
2484  {
2485  __SUP_SS__ << "Start path was a disconnected link node!" << __E__;
2486  __SUP_COUT_ERR__ << "\n" << ss.str();
2487  __SS_THROW__;
2488  }
2489 
2490  std::vector<std::string /*relative-path*/> records;
2491  if(recordList == "*") // handle all records case
2492  {
2493  records.clear();
2494  records = startNode.getChildrenNames();
2495  __SUP_COUT__ << "Translating wildcard..." << __E__;
2496  for(auto& record : records)
2497  __SUP_COUT__ << "recordList " << record << __E__;
2498  }
2499  else if(recordList != "")
2500  {
2501  // extract record list
2502  {
2503  std::istringstream f(recordList);
2504  std::string recordStr;
2505  while(getline(f, recordStr, ','))
2506  {
2507  records.push_back(StringMacros::decodeURIComponent(recordStr));
2508  }
2509  __SUP_COUT__ << recordList << __E__;
2510  for(auto& record : records)
2511  __SUP_COUT__ << "recordList " << record << __E__;
2512  }
2513  }
2514 
2515  // loop through each field and get unique values among records
2516  for(auto& field : fieldsToGet)
2517  {
2518  __SUP_COUT__ << "fieldsToGet " << field << __E__;
2519 
2520  DOMElement* parentEl = xmlOut.addTextElementToData("field", field);
2521 
2522  // use set to force sorted unique values
2523  std::set<std::string /*unique-values*/> uniqueValues;
2524 
2525  uniqueValues =
2526  cfgMgr->getNode(startPath).getUniqueValuesForField(records, field);
2527 
2528  for(auto& uniqueValue : uniqueValues)
2529  {
2530  __SUP_COUT__ << "uniqueValue " << uniqueValue << __E__;
2531 
2532  xmlOut.addTextElementToParent("uniqueValue", uniqueValue, parentEl);
2533  }
2534  }
2535  }
2536  catch(std::runtime_error& e)
2537  {
2538  __SUP_SS__ << ("Error getting common fields!\n\n" + std::string(e.what()))
2539  << __E__;
2540  __SUP_COUT_ERR__ << "\n" << ss.str();
2541  xmlOut.addTextElementToData("Error", ss.str());
2542  }
2543  catch(...)
2544  {
2545  __SUP_SS__ << ("Error getting common fields!\n\n") << __E__;
2546  __SUP_COUT_ERR__ << "\n" << ss.str();
2547  xmlOut.addTextElementToData("Error", ss.str());
2548  }
2549 }
2550 
2551 //========================================================================================================================
2552 // handleFillTreeViewXML
2553 // returns xml tree from path for given depth
2554 //
2555 // if groupName == "" || groupKey is invalid
2556 // then return tree for active groups
2557 //
2558 // parameters
2559 // configGroupName (full name with key)
2560 // starting node path
2561 // depth from starting node path
2562 // modifiedTables := CSV of table/version pairs
2563 // filterList := relative-to-record-path=value(,value,...);path=value... filtering
2564 // records with relative path not meeting all filter criteria
2565 // - can accept multiple values per field (values separated by commas) (i.e. OR)
2566 // - fields/value pairs separated by ; for AND
2567 // - Note: limitation here is there is no OR among fields/value pairs (in future,
2568 // could separate field/value pairs by : for OR) e.g.
2569 //"LinkToFETypeTable=NIMPlus,TemplateUDP;FEInterfacePluginName=NIMPlusPlugin"
2570 //
2571 void ConfigurationGUISupervisor::handleFillTreeViewXML(HttpXmlDocument& xmlOut,
2572  ConfigurationManagerRW* cfgMgr,
2573  const std::string& groupName,
2574  const TableGroupKey& groupKey,
2575  const std::string& startPath,
2576  unsigned int depth,
2577  bool hideStatusFalse,
2578  const std::string& modifiedTables,
2579  const std::string& filterList)
2580 {
2581  // return xml
2582  // <groupName="groupName"/>
2583  // <tree="path">
2584  // <node="...">
2585  // <node="...">
2586  // <node="...">
2587  // <value="...">
2588  // </node>
2589  // <node="...">
2590  // <value="...">
2591  // </node>
2592  // </node>
2593  // <node="...">
2594  // <value="..">
2595  // </node>
2596  // ...
2597  // </node>
2598  // </tree>
2599 
2600  // return the startPath as root "tree" element
2601  // and then display all children if depth > 0
2602 
2603  // Think about using this in the future to clean up the code
2604  // But may not work well since there is some special functionality used below
2605  // like getting group comments, and not reloading everything except for at root level
2607  // ....
2608  // // setup active tables based on input group and modified tables
2609 
2610  bool usingActiveGroups = (groupName == "" || groupKey.isInvalid());
2611  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
2612 
2613  std::string accumulatedErrors = "";
2614  try
2615  {
2616  setupActiveTablesXML(
2617  xmlOut,
2618  cfgMgr,
2619  groupName,
2620  groupKey,
2621  modifiedTables,
2622  (startPath == "/"), // refreshAll, if at root node, reload all
2623  // tables so that partially loaded tables are
2624  // not allowed
2625  (startPath == "/"), // get group info
2626  &memberMap, // get group member map
2627  true, // output active tables (default)
2628  &accumulatedErrors // accumulate errors
2629  );
2630  }
2631  catch(const std::runtime_error& e)
2632  {
2633  __SS__ << "Error occured setting up active tables: " << e.what() << __E__;
2634  accumulatedErrors += ss.str();
2635  }
2636  catch(...)
2637  {
2638  __SS__ << "Unknown error occured setting up active tables." << __E__;
2639  accumulatedErrors += ss.str();
2640  }
2641 
2642  if(accumulatedErrors != "")
2643  {
2644  xmlOut.addTextElementToData("Warning", accumulatedErrors);
2645 
2646  __SUP_COUT__ << "Active tables are setup. Warning string: '" << accumulatedErrors
2647  << "'" << __E__;
2648  }
2649  else
2650  __SUP_COUT__ << "Active tables are setup. No issues found." << __E__;
2651 
2652  try
2653  {
2654  DOMElement* parentEl = xmlOut.addTextElementToData("tree", startPath);
2655 
2656  if(depth == 0)
2657  return; // already returned root node in itself
2658 
2659  std::vector<std::pair<std::string, ConfigurationTree>> rootMap;
2660 
2661  if(startPath == "/")
2662  {
2663  // then consider the configurationManager the root node
2664 
2665  std::string accumulateTreeErrs;
2666 
2667  if(usingActiveGroups)
2668  rootMap = cfgMgr->getChildren(0, &accumulateTreeErrs);
2669  else
2670  rootMap = cfgMgr->getChildren(&memberMap, &accumulateTreeErrs);
2671 
2672  __SUP_COUT__ << "accumulateTreeErrs = " << accumulateTreeErrs << __E__;
2673  if(accumulateTreeErrs != "")
2674  xmlOut.addTextElementToData("TreeErrors", accumulateTreeErrs);
2675  }
2676  else
2677  {
2678  ConfigurationTree startNode =
2679  cfgMgr->getNode(startPath, true /*doNotThrowOnBrokenUIDLinks*/);
2680  if(startNode.isLinkNode() && startNode.isDisconnected())
2681  {
2682  xmlOut.addTextElementToData("DisconnectedStartNode", "1");
2683  return; // quietly ignore disconnected links at depth
2684  // note: at the root level they will be flagged for the user
2685  }
2686 
2687  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap;
2688  StringMacros::getMapFromString(
2689  filterList,
2690  filterMap,
2691  std::set<char>({';'}) /*pair delimiters*/,
2692  std::set<char>({'='}) /*name/value delimiters*/);
2693 
2694  __COUTV__(StringMacros::mapToString(filterMap));
2695 
2696  rootMap = cfgMgr->getNode(startPath).getChildren(filterMap);
2697  }
2698 
2699  for(auto& treePair : rootMap)
2700  recursiveTreeToXML(
2701  treePair.second, depth - 1, xmlOut, parentEl, hideStatusFalse);
2702  }
2703  catch(std::runtime_error& e)
2704  {
2705  __SUP_SS__ << "Error detected generating XML tree!\n\n " << e.what() << __E__;
2706  __SUP_COUT_ERR__ << "\n" << ss.str();
2707  xmlOut.addTextElementToData("Error", ss.str());
2708  }
2709  catch(...)
2710  {
2711  __SUP_SS__ << "Error detected generating XML tree!" << __E__;
2712  __SUP_COUT_ERR__ << "\n" << ss.str();
2713  xmlOut.addTextElementToData("Error", ss.str());
2714  }
2715 }
2716 
2717 //==============================================================================
2718 // recursiveToXml
2719 // output tree to XML from this node for desired depth
2720 // depth of 0 means output only this node's value
2721 // depth of 1 means include this node's children's values, etc..
2722 // depth of -1(unsigned int) effectively means output full tree
2723 void ConfigurationGUISupervisor::recursiveTreeToXML(const ConfigurationTree& t,
2724  unsigned int depth,
2725  HttpXmlDocument& xmlOut,
2726  DOMElement* parentEl,
2727  bool hideStatusFalse)
2728 {
2729  //__COUT__ << t.getValueAsString() << __E__;
2730 
2731  if(t.isValueNode())
2732  {
2733  parentEl = xmlOut.addTextElementToParent("node", t.getValueName(), parentEl);
2734  xmlOut.addTextElementToParent("value", t.getValueAsString(), parentEl);
2735  parentEl = xmlOut.addTextElementToParent("valueType", t.getValueType(), parentEl);
2736 
2737  // fixed choice and bitmap both use fixed choices strings
2738  // so output them to xml
2739  if(t.getValueType() == TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA ||
2740  t.getValueType() == TableViewColumnInfo::TYPE_BITMAP_DATA)
2741  {
2742  //__COUT__ << t.getValueType() << __E__;
2743 
2744  std::vector<std::string> choices = t.getFixedChoices();
2745  for(const auto& choice : choices)
2746  xmlOut.addTextElementToParent("fixedChoice", choice, parentEl);
2747  }
2748  }
2749  else
2750  {
2751  if(t.isLinkNode())
2752  {
2753  //__COUT__ << t.getValueName() << __E__;
2754 
2755  // Note: The order of xml fields is required by JavaScript, so do NOT change
2756  // order.
2757  parentEl = xmlOut.addTextElementToParent("node", t.getValueName(), parentEl);
2758 
2759  if(t.isDisconnected())
2760  {
2761  __COUT__ << t.getValueName() << __E__;
2762 
2763  // xmlOut.addTextElementToParent("value", t.getValueAsString(), parentEl);
2764  // xmlOut.addTextElementToParent("DisconnectedLink", t.getValueAsString(),
2765  // parentEl);
2766 
2767  xmlOut.addTextElementToParent("valueType", t.getValueType(), parentEl);
2768 
2769  // add extra fields for disconnected link
2770  xmlOut.addTextElementToParent(
2771  (t.isGroupLinkNode() ? "Group" : "U") + std::string("ID"),
2772  t.getDisconnectedLinkID(),
2773  parentEl);
2774  xmlOut.addTextElementToParent(
2775  "LinkTableName", t.getDisconnectedTableName(), parentEl);
2776  xmlOut.addTextElementToParent(
2777  "LinkIndex", t.getChildLinkIndex(), parentEl);
2778 
2779  // add fixed choices (in case link has them)
2780  DOMElement* choicesParentEl =
2781  xmlOut.addTextElementToParent("fixedChoices", "", parentEl);
2782  // try
2783  //{
2784 
2785  std::vector<std::string> choices = t.getFixedChoices();
2786  __COUT__ << "choices.size() " << choices.size() << __E__;
2787 
2788  for(const auto& choice : choices)
2789  xmlOut.addTextElementToParent("fixedChoice", choice, choicesParentEl);
2790  //}
2791  // catch(...)
2792  //{
2793  // __COUT__ << "Ignoring unknown fixed choice error"
2794  //} //ignore no fixed choices for disconnected
2795 
2796  return;
2797  }
2798 
2799  // handle connected links
2800 
2801  xmlOut.addTextElementToParent(
2802  (t.isGroupLinkNode() ? "Group" : "U") + std::string("ID"),
2803  t.getValueAsString(),
2804  parentEl);
2805 
2806  xmlOut.addTextElementToParent("LinkTableName", t.getTableName(), parentEl);
2807 
2808  xmlOut.addTextElementToParent("LinkIndex", t.getChildLinkIndex(), parentEl);
2809 
2810  // add fixed choices (in case link has them)
2811  {
2812  DOMElement* choicesParentEl =
2813  xmlOut.addTextElementToParent("fixedChoices", "", parentEl);
2814  std::vector<std::string> choices = t.getFixedChoices();
2815 
2816  //__COUT__ << "choices.size() " << choices.size() << __E__;
2817 
2818  for(const auto& choice : choices)
2819  xmlOut.addTextElementToParent("fixedChoice", choice, choicesParentEl);
2820  }
2821  }
2822  else // uid node
2823  {
2824  bool returnNode = true; // default to shown
2825 
2826  if(hideStatusFalse) // only show if status evaluates to true
2827  {
2828  try // try to get Status child as boolean..
2829  { // if Status bool doesn't exist exception will be thrown
2830  t.getNode(TableViewColumnInfo::COL_NAME_STATUS).getValue(returnNode);
2831  }
2832  catch(...)
2833  {
2834  }
2835  }
2836 
2837  if(returnNode)
2838  parentEl =
2839  xmlOut.addTextElementToParent("node", t.getValueAsString(), parentEl);
2840  else
2841  return; // done.. no further depth needed for node that is not shown
2842  }
2843 
2844  // if depth>=1 toXml all children
2845  // child.toXml(depth-1)
2846  if(depth >= 1)
2847  {
2848  auto C = t.getChildren();
2849  for(auto& c : C)
2850  recursiveTreeToXML(
2851  c.second, depth - 1, xmlOut, parentEl, hideStatusFalse);
2852  }
2853  }
2854 }
2855 
2856 //========================================================================================================================
2857 // handleGetLinkToChoicesXML
2858 // return all possible choices for link
2859 // linkIdType = "UID" or "GroupID"
2860 //
2861 // as xml:
2862 // <linkToChoice = xxx>
2863 void ConfigurationGUISupervisor::handleGetLinkToChoicesXML(
2864  HttpXmlDocument& xmlOut,
2865  ConfigurationManagerRW* cfgMgr,
2866  const std::string& linkToTableName,
2867  const TableVersion& linkToTableVersion,
2868  const std::string& linkIdType,
2869  const std::string& linkIndex,
2870  const std::string& linkInitId) try
2871 {
2872  // get table
2873  // if uid link
2874  // return all uids
2875  // if groupid link
2876  // find target column
2877  // create the set of values (unique values only)
2878  // note: insert group unions individually (i.e. groups | separated)
2879 
2880  // get table and activate target version
2881  // rename to re-use code template
2882  const std::string& tableName = linkToTableName;
2883  const TableVersion& version = linkToTableVersion;
2884  TableBase* config = cfgMgr->getTableByName(tableName);
2885  try
2886  {
2887  config->setActiveView(version);
2888  }
2889  catch(...)
2890  {
2891  __SUP_COUT__ << "Failed to find stored version, so attempting to load version: "
2892  << version << __E__;
2893  cfgMgr->getVersionedTableByName(tableName, version);
2894  }
2895 
2896  if(version != config->getViewVersion())
2897  {
2898  __SUP_SS__ << "Target table version (" << version
2899  << ") is not the currently active version ("
2900  << config->getViewVersion() << ". Try refreshing the tree." << __E__;
2901  __SUP_COUT_WARN__ << ss.str();
2902  __SS_THROW__;
2903  }
2904 
2905  __SUP_COUT__ << "Active version is " << config->getViewVersion() << __E__;
2906 
2907  if(linkIdType == "UID")
2908  {
2909  // give all UIDs
2910  unsigned int col = config->getView().getColUID();
2911  for(unsigned int row = 0; row < config->getView().getNumberOfRows(); ++row)
2912  xmlOut.addTextElementToData("linkToChoice",
2913  config->getView().getDataView()[row][col]);
2914  }
2915  else if(linkIdType == "GroupID")
2916  {
2917  // find target column
2918  // create the set of values (unique values only)
2919  // note: insert group unions individually (i.e. groups | separated)
2920 
2921  __SUP_COUTV__(linkIndex);
2922  __SUP_COUTV__(linkInitId);
2923 
2924  std::set<std::string> setOfGroupIDs =
2925  config->getView().getSetOfGroupIDs(linkIndex);
2926 
2927  // build list of groupids
2928  // always include initial link group id in choices
2929  // (even if not in set of group ids)
2930  bool foundInitId = false;
2931  for(const auto& groupID : setOfGroupIDs)
2932  {
2933  if(!foundInitId && linkInitId == groupID)
2934  foundInitId = true; // mark init id found
2935 
2936  xmlOut.addTextElementToData("linkToChoice", groupID);
2937  }
2938  // if init id was not found, add to list
2939  if(!foundInitId)
2940  xmlOut.addTextElementToData("linkToChoice", linkInitId);
2941 
2942  // give all UIDs
2943  unsigned int col = config->getView().getColUID();
2944  for(unsigned int row = 0; row < config->getView().getNumberOfRows(); ++row)
2945  {
2946  xmlOut.addTextElementToData("groupChoice",
2947  config->getView().getDataView()[row][col]);
2948  if(config->getView().isEntryInGroup(row, linkIndex, linkInitId))
2949  xmlOut.addTextElementToData("groupMember",
2950  config->getView().getDataView()[row][col]);
2951  }
2952  }
2953  else
2954  {
2955  __SUP_SS__ << "Unrecognized linkIdType '" << linkIdType << ".'" << __E__;
2956  __SS_THROW__;
2957  }
2958 }
2959 catch(std::runtime_error& e)
2960 {
2961  __SUP_SS__ << "Error detected saving tree node!\n\n " << e.what() << __E__;
2962  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
2963  xmlOut.addTextElementToData("Error", ss.str());
2964 }
2965 catch(...)
2966 {
2967  __SUP_SS__ << "Error detected saving tree node!\n\n " << __E__;
2968  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
2969  xmlOut.addTextElementToData("Error", ss.str());
2970 }
2971 
2972 //========================================================================================================================
2973 // handleMergeGroupsXML
2974 void ConfigurationGUISupervisor::handleMergeGroupsXML(
2975  HttpXmlDocument& xmlOut,
2976  ConfigurationManagerRW* cfgMgr,
2977  const std::string& groupANameContext,
2978  const TableGroupKey& groupAKeyContext,
2979  const std::string& groupBNameContext,
2980  const TableGroupKey& groupBKeyContext,
2981  const std::string& groupANameConfig,
2982  const TableGroupKey& groupAKeyConfig,
2983  const std::string& groupBNameConfig,
2984  const TableGroupKey& groupBKeyConfig,
2985  const std::string& author,
2986  const std::string& mergeApproach) try
2987 {
2988  __SUP_COUT__ << "Merging context group pair " << groupANameContext << " ("
2989  << groupAKeyContext << ") & " << groupBNameContext << " ("
2990  << groupBKeyContext << ") and config group pair " << groupANameConfig
2991  << " (" << groupAKeyConfig << ") & " << groupBNameConfig << " ("
2992  << groupBKeyConfig << ") with approach '" << mergeApproach << __E__;
2993 
2994  // Merges group A and group B
2995  // with consideration for UID conflicts
2996  // Result is a new key of group A's name
2997  //
2998  // There 3 modes:
2999  // Rename -- All records from both groups are maintained, but conflicts from B
3000  // are renamed.
3001  // Must maintain a map of UIDs that are remapped to new name for
3002  // groupB, because linkUID fields must be preserved. Replace --
3003  // Any UID conflicts for a record are replaced by the record from group B.
3004  // Skip -- Any UID conflicts for a record are skipped so that group A record
3005  // remains
3006 
3007  // check valid mode
3008  if(!(mergeApproach == "Rename" || mergeApproach == "Replace" ||
3009  mergeApproach == "Skip"))
3010  {
3011  __SS__ << "Error! Invalid merge approach '" << mergeApproach << ".'" << __E__;
3012  __SS_THROW__;
3013  }
3014 
3015  std::map<std::string /*name*/, TableVersion /*version*/> memberMapAContext,
3016  memberMapBContext, memberMapAConfig, memberMapBConfig;
3017 
3018  // check if skipping group pairs
3019  bool skippingContextPair = false;
3020  bool skippingConfigPair = false;
3021  if(groupANameContext.size() == 0 || groupANameContext[0] == ' ' ||
3022  groupBNameContext.size() == 0 || groupBNameContext[0] == ' ')
3023  {
3024  skippingContextPair = true;
3025  __SUP_COUTV__(skippingContextPair);
3026  }
3027  if(groupANameConfig.size() == 0 || groupANameConfig[0] == ' ' ||
3028  groupBNameConfig.size() == 0 || groupBNameConfig[0] == ' ')
3029  {
3030  skippingConfigPair = true;
3031  __SUP_COUTV__(skippingConfigPair);
3032  }
3033 
3034  // get context group member maps
3035  if(!skippingContextPair)
3036  {
3037  cfgMgr->loadTableGroup(groupANameContext,
3038  groupAKeyContext,
3039  false /*doActivate*/,
3040  &memberMapAContext,
3041  0 /*progressBar*/,
3042  0 /*accumulateErrors*/,
3043  0 /*groupComment*/,
3044  0 /*groupAuthor*/,
3045  0 /*groupCreationTime*/,
3046  false /*doNotLoadMember*/,
3047  0 /*groupTypeString*/
3048  );
3049  __SUP_COUTV__(StringMacros::mapToString(memberMapAContext));
3050 
3051  cfgMgr->loadTableGroup(groupBNameContext,
3052  groupBKeyContext,
3053  false /*doActivate*/,
3054  &memberMapBContext,
3055  0 /*progressBar*/,
3056  0 /*accumulateErrors*/,
3057  0 /*groupComment*/,
3058  0 /*groupAuthor*/,
3059  0 /*groupCreationTime*/,
3060  false /*doNotLoadMember*/,
3061  0 /*groupTypeString*/
3062  );
3063 
3064  __SUP_COUTV__(StringMacros::mapToString(memberMapBContext));
3065  }
3066 
3067  // get config group member maps
3068  if(!skippingConfigPair)
3069  {
3070  cfgMgr->loadTableGroup(groupANameConfig,
3071  groupAKeyConfig,
3072  false /*doActivate*/,
3073  &memberMapAConfig,
3074  0 /*progressBar*/,
3075  0 /*accumulateErrors*/,
3076  0 /*groupComment*/,
3077  0 /*groupAuthor*/,
3078  0 /*groupCreationTime*/,
3079  false /*doNotLoadMember*/,
3080  0 /*groupTypeString*/
3081  );
3082  __SUP_COUTV__(StringMacros::mapToString(memberMapAConfig));
3083 
3084  cfgMgr->loadTableGroup(groupBNameConfig,
3085  groupBKeyConfig,
3086  false /*doActivate*/,
3087  &memberMapBConfig,
3088  0 /*progressBar*/,
3089  0 /*accumulateErrors*/,
3090  0 /*groupComment*/,
3091  0 /*groupAuthor*/,
3092  0 /*groupCreationTime*/,
3093  false /*doNotLoadMember*/,
3094  0 /*groupTypeString*/
3095  );
3096 
3097  __SUP_COUTV__(StringMacros::mapToString(memberMapBConfig));
3098  }
3099 
3100  // for each member of B
3101  // if not found in A member map, add it
3102  // if found in both member maps, and versions are different, load both tables and
3103  // merge
3104 
3105  std::map<std::pair<std::string /*original table*/, std::string /*original uidB*/>,
3106  std::string /*converted uidB*/>
3107  uidConversionMap;
3108  std::map<
3109  std::pair<std::string /*original table*/,
3110  std::pair<std::string /*group linkid*/, std::string /*original gidB*/>>,
3111  std::string /*converted gidB*/>
3112  groupidConversionMap;
3113 
3114  // first loop create record conversion map, second loop implement merge (using
3115  // conversion map if Rename)
3116  for(unsigned int i = 0; i < 2; ++i)
3117  {
3118  if(i == 0 && mergeApproach != "Rename")
3119  continue; // only need to construct uidConversionMap for rename approach
3120 
3121  // loop for context and config pair types
3122  for(unsigned int j = 0; j < 2; ++j)
3123  {
3124  if(j == 0 && skippingContextPair) // context
3125  {
3126  __COUT__ << "Skipping context pair..." << __E__;
3127  continue;
3128  }
3129  else if(j == 1 && skippingConfigPair)
3130  {
3131  __COUT__ << "Skipping config pair..." << __E__;
3132  continue;
3133  }
3134 
3135  std::map<std::string /*name*/, TableVersion /*version*/>& memberMapAref =
3136  j == 0 ? memberMapAContext : memberMapAConfig;
3137 
3138  std::map<std::string /*name*/, TableVersion /*version*/>& memberMapBref =
3139  j == 0 ? memberMapBContext : memberMapBConfig;
3140 
3141  if(j == 0) // context
3142  __COUT__ << "Context pair..." << __E__;
3143  else
3144  __COUT__ << "Config pair..." << __E__;
3145 
3146  __COUT__ << "Starting member map B scan." << __E__;
3147  for(const auto bkey : memberMapBref)
3148  {
3149  __SUP_COUTV__(bkey.first);
3150 
3151  if(memberMapAref.find(bkey.first) == memberMapAref.end())
3152  {
3153  // not found, so add to A member map
3154  memberMapAref[bkey.first] = bkey.second;
3155  }
3156  else if(memberMapAref[bkey.first] != bkey.second)
3157  {
3158  // found table version confict
3159  __SUP_COUTV__(memberMapAref[bkey.first]);
3160  __SUP_COUTV__(bkey.second);
3161 
3162  // load both tables, and merge
3163  TableBase* config = cfgMgr->getTableByName(bkey.first);
3164 
3165  __SUP_COUT__ << "Got table." << __E__;
3166 
3167  TableVersion newVersion = config->mergeViews(
3168  cfgMgr
3169  ->getVersionedTableByName(bkey.first,
3170  memberMapAref[bkey.first])
3171  ->getView(),
3172  cfgMgr->getVersionedTableByName(bkey.first, bkey.second)
3173  ->getView(),
3174  TableVersion() /* destinationVersion*/,
3175  author,
3176  mergeApproach /*Rename,Replace,Skip*/,
3177  uidConversionMap,
3178  groupidConversionMap,
3179  i == 0 /* fillRecordConversionMaps */,
3180  i == 1 /* applyRecordConversionMaps */,
3181  config->getTableName() ==
3182  ConfigurationManager::
3183  XDAQ_APPLICATION_TABLE_NAME /* generateUniqueDataColumns
3184  */
3185  ); // dont make destination version the first time
3186 
3187  if(i == 1)
3188  {
3189  __SUP_COUTV__(newVersion);
3190 
3191  try
3192  {
3193  // save all temporary tables to persistent tables
3194  // finish off the version creation
3195  newVersion = saveModifiedVersionXML(
3196  xmlOut,
3197  cfgMgr,
3198  bkey.first,
3199  TableVersion() /*original source version*/,
3200  false /* makeTemporary */,
3201  config,
3202  newVersion /*temporary modified version*/,
3203  false /*ignore duplicates*/,
3204  true /*look for equivalent*/);
3205  }
3206  catch(std::runtime_error& e)
3207  {
3208  __SUP_SS__
3209  << "There was an error saving the '"
3210  << config->getTableName()
3211  << "' merge result to a persistent table version. "
3212  << "Perhaps you can modify this table in one of the "
3213  "groups to resolve this issue, and then re-merge."
3214  << __E__ << e.what();
3215  __SS_THROW__;
3216  }
3217 
3218  __SUP_COUTV__(newVersion);
3219 
3220  memberMapAref[bkey.first] = newVersion;
3221  }
3222  } // end member version conflict handling
3223  } // end B member map loop
3224  } // end context and config loop
3225  } // end top level conversion map or not loop
3226 
3227  // Now save groups
3228 
3229  if(!skippingContextPair)
3230  {
3231  __SUP_COUT__ << "New context member map complete." << __E__;
3232  __SUP_COUTV__(StringMacros::mapToString(memberMapAContext));
3233 
3234  // save the new table group
3235  TableGroupKey newKeyContext = cfgMgr->saveNewTableGroup(
3236  groupANameContext,
3237  memberMapAContext,
3238  "Merger of group " + groupANameContext + " (" + groupAKeyContext.toString() +
3239  ") and " + groupBNameContext + " (" + groupBKeyContext.toString() + ").");
3240 
3241  // return new resulting group
3242  xmlOut.addTextElementToData("ContextGroupName", groupANameContext);
3243  xmlOut.addTextElementToData("ContextGroupKey", newKeyContext.toString());
3244  }
3245  if(!skippingConfigPair)
3246  {
3247  __SUP_COUT__ << "New config member map complete." << __E__;
3248  __SUP_COUTV__(StringMacros::mapToString(memberMapAConfig));
3249 
3250  // save the new table group
3251  TableGroupKey newKeyConfig = cfgMgr->saveNewTableGroup(
3252  groupANameConfig,
3253  memberMapAConfig,
3254  "Merger of group " + groupANameConfig + " (" + groupAKeyConfig.toString() +
3255  ") and " + groupBNameConfig + " (" + groupBKeyConfig.toString() + ").");
3256 
3257  // return new resulting group
3258  xmlOut.addTextElementToData("ConfigGroupName", groupANameConfig);
3259  xmlOut.addTextElementToData("ConfigGroupKey", newKeyConfig.toString());
3260  }
3261 
3262 } // end handleMergeGroupsXML
3263 catch(std::runtime_error& e)
3264 {
3265  __SUP_SS__ << "Error merging context group pair " << groupANameContext << " ("
3266  << groupAKeyContext << ") & " << groupBNameContext << " ("
3267  << groupBKeyContext << ") and config group pair " << groupANameConfig
3268  << " (" << groupAKeyConfig << ") & " << groupBNameConfig << " ("
3269  << groupBKeyConfig << ") with approach '" << mergeApproach << "': \n\n"
3270  << e.what() << __E__;
3271  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
3272  xmlOut.addTextElementToData("Error", ss.str());
3273 }
3274 catch(...)
3275 {
3276  __SUP_SS__ << "Unknown error merging context group pair " << groupANameContext << " ("
3277  << groupAKeyContext << ") & " << groupBNameContext << " ("
3278  << groupBKeyContext << ") and config group pair " << groupANameConfig
3279  << " (" << groupAKeyConfig << ") & " << groupBNameConfig << " ("
3280  << groupBKeyConfig << ") with approach '" << mergeApproach << ".' \n\n";
3281  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
3282  xmlOut.addTextElementToData("Error", ss.str());
3283 }
3284 
3285 //========================================================================================================================
3286 // handleSavePlanCommandSequenceXML
3287 void ConfigurationGUISupervisor::handleSavePlanCommandSequenceXML(
3288  HttpXmlDocument& xmlOut,
3289  ConfigurationManagerRW* cfgMgr,
3290  const std::string& groupName,
3291  const TableGroupKey& groupKey,
3292  const std::string& modifiedTables,
3293  const std::string& author,
3294  const std::string& planName,
3295  const std::string& commandString) try
3296 {
3297  __MOUT__ << "handleSavePlanCommandSequenceXML" << __E__;
3298 
3299  // setup active tables based on input group and modified tables
3300  setupActiveTablesXML(xmlOut,
3301  cfgMgr,
3302  groupName,
3303  groupKey,
3304  modifiedTables,
3305  true /* refresh all */,
3306  false /* getGroupInfo */,
3307  0 /* returnMemberMap */,
3308  false /* outputActiveTables */);
3309 
3310  TableEditStruct planTable(IterateTable::PLAN_TABLE,
3311  cfgMgr); // Table ready for editing!
3312  TableEditStruct targetTable(IterateTable::TARGET_TABLE,
3313  cfgMgr); // Table ready for editing!
3314 
3315  // create table-edit struct for each iterate command type
3316  std::map<std::string, TableEditStruct> commandTypeToCommandTableMap;
3317  for(const auto& commandPair : IterateTable::commandToTableMap_)
3318  if(commandPair.second != "") // skip tables with no parameters
3319  commandTypeToCommandTableMap.emplace(std::pair<std::string, TableEditStruct>(
3320  commandPair.first, TableEditStruct(commandPair.second, cfgMgr)));
3321 
3322  // try to catch any errors while editing..
3323  // if errors delete temporary plan view (if created here)
3324  try
3325  {
3326  // Steps:
3327  // Reset plan commands
3328  // Remove all commands in group "<plan>-Plan"
3329  // Delete linked command parameters row (in separate table)
3330  // If no group remaining, then delete row.
3331  //
3332  // Save plan commands (if modified)
3333  // Create rows and add them to group "<plan>-Plan"
3334  // create row for command paramaters and add to proper table
3335 
3336  std::string groupName = planName + "-Plan";
3337  __SUP_COUT__ << "Handling commands for group " << groupName << __E__;
3338 
3339  unsigned int groupIdCol =
3340  planTable.tableView_->findCol(IterateTable::planTableCols_.GroupID_);
3341  unsigned int cmdTypeCol =
3342  planTable.tableView_->findCol(IterateTable::planTableCols_.CommandType_);
3343 
3344  unsigned int targetGroupIdCol =
3345  targetTable.tableView_->findCol(IterateTable::targetCols_.GroupID_);
3346  unsigned int targetTableCol =
3347  targetTable.tableView_->findCol(IterateTable::targetCols_.TargetLink_);
3348  unsigned int targetUIDCol =
3349  targetTable.tableView_->findCol(IterateTable::targetCols_.TargetLinkUID_);
3350 
3351  std::string groupLinkIndex =
3352  planTable.tableView_->getColumnInfo(groupIdCol).getChildLinkIndex();
3353  __SUP_COUT__ << "groupLinkIndex: " << groupLinkIndex << __E__;
3354 
3355  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> commandUidLink;
3356  {
3357  bool isGroup; // local because we know is uid link
3358  planTable.tableView_->getChildLink(
3359  planTable.tableView_->findCol(IterateTable::planTableCols_.CommandLink_),
3360  isGroup,
3361  commandUidLink);
3362  }
3363 
3364  unsigned int cmdRow, cmdCol;
3365  std::string targetGroupName;
3366 
3367  // Reset existing plan commands
3368  {
3369  std::string targetUID, cmdType;
3370 
3371  for(unsigned int row = 0; row < planTable.tableView_->getNumberOfRows();
3372  ++row)
3373  {
3374  targetUID = planTable.tableView_
3375  ->getDataView()[row][planTable.tableView_->getColUID()];
3376  __SUP_COUT__ << "targetUID: " << targetUID << __E__;
3377 
3378  // remove command from plan group.. if no more groups, delete
3379  if(planTable.tableView_->isEntryInGroup(row, groupLinkIndex, groupName))
3380  {
3381  __SUP_COUT__ << "Removing." << __E__;
3382 
3383  // delete linked command
3384  // find linked UID in table (mapped by type)
3385  cmdType = planTable.tableView_->getDataView()[row][cmdTypeCol];
3386  if(commandTypeToCommandTableMap.find(cmdType) !=
3387  commandTypeToCommandTableMap
3388  .end()) // skip if invalid command type
3389  {
3390  cmdRow =
3391  commandTypeToCommandTableMap[cmdType].tableView_->findRow(
3392  commandTypeToCommandTableMap[cmdType]
3393  .tableView_->getColUID(),
3394  planTable.tableView_
3395  ->getDataView()[row][commandUidLink.second]);
3396 
3397  // before deleting row...
3398  // look for target group
3399  // remove all targets in group
3400  try
3401  {
3402  cmdCol =
3403  commandTypeToCommandTableMap[cmdType].tableView_->findCol(
3404  IterateTable::commandTargetCols_.TargetsLinkGroupID_);
3405  targetGroupName =
3406  commandTypeToCommandTableMap[cmdType]
3407  .tableView_->getDataView()[cmdRow][cmdCol];
3408 
3409  for(unsigned int trow = 0;
3410  trow < targetTable.tableView_->getNumberOfRows();
3411  ++trow)
3412  {
3413  // remove command from target group..
3414  if(targetTable.tableView_->isEntryInGroup(
3415  trow,
3416  commandTypeToCommandTableMap[cmdType]
3417  .tableView_->getColumnInfo(cmdCol)
3418  .getChildLinkIndex(),
3419  targetGroupName))
3420  {
3421  __SUP_COUT__ << "Removing target." << __E__;
3422  // remove command entry in plan table
3423  if(targetTable.tableView_->removeRowFromGroup(
3424  trow,
3425  targetGroupIdCol,
3426  targetGroupName,
3427  true /*deleteRowIfNoGroup*/))
3428  --trow; // since row was deleted, go back!
3429  }
3430  }
3431  }
3432  catch(...)
3433  {
3434  __SUP_COUT__ << "No targets." << __E__;
3435  }
3436 
3437  // now no more targets, delete row
3438 
3439  commandTypeToCommandTableMap[cmdType].tableView_->deleteRow(
3440  cmdRow);
3441 
3442  commandTypeToCommandTableMap[cmdType].modified_ = true;
3443  }
3444 
3445  // remove command entry in plan table
3446  if(planTable.tableView_->removeRowFromGroup(
3447  row, groupIdCol, groupName, true /*deleteRowIfNoGroup*/))
3448  --row; // since row was deleted, go back!
3449  }
3450  }
3451  }
3452 
3453  // Done resetting existing plan
3454  // Now save new commands
3455 
3456  std::vector<IterateTable::Command> commands;
3457 
3458  // extract command sequence and add to table
3459  // into vector with type, and params
3460  {
3461  std::istringstream f(commandString);
3462  std::string commandSubString, paramSubString, paramValue;
3463  int i;
3464  while(getline(f, commandSubString, ';'))
3465  {
3466  //__SUP_COUT__ << "commandSubString " << commandSubString << __E__;
3467  std::istringstream g(commandSubString);
3468 
3469  i = 0;
3470  while(getline(g, paramSubString, ','))
3471  {
3472  //__SUP_COUT__ << "paramSubString " << paramSubString << __E__;
3473  if(i == 0) // type
3474  {
3475  if(paramSubString != "type")
3476  {
3477  __SUP_SS__ << "Invalid command sequence" << __E__;
3478  __SS_THROW__;
3479  }
3480  // create command object
3481  commands.push_back(IterateTable::Command());
3482 
3483  getline(g, paramValue, ',');
3484  ++i;
3485  //__SUP_COUT__ << "paramValue " << paramValue << __E__;
3486  commands.back().type_ = paramValue;
3487  }
3488  else // params
3489  {
3490  getline(g, paramValue, ',');
3491  ++i;
3492  //__SUP_COUT__ << "paramValue " << paramValue << __E__;
3493 
3494  commands.back().params_.emplace(
3495  std::pair<std::string /*param name*/,
3496  std::string /*param value*/>(
3497  paramSubString,
3498  StringMacros::decodeURIComponent(paramValue)));
3499  }
3500 
3501  ++i;
3502  }
3503  }
3504 
3505  } // end extract command sequence
3506 
3507  __SUP_COUT__ << "commands size " << commands.size() << __E__;
3508 
3509  // at this point, have extracted commands
3510 
3511  // now save commands to plan group
3512  // group should be "<plan>-Plan"
3513 
3514  unsigned int row, tgtRow;
3515  unsigned int targetIndex;
3516  std::string targetStr, cmdUID;
3517 
3518  for(auto& command : commands)
3519  {
3520  __SUP_COUT__ << "command " << command.type_ << __E__;
3521  __SUP_COUT__ << "table " << IterateTable::commandToTableMap_.at(command.type_)
3522  << __E__;
3523 
3524  // create command entry at plan level
3525  row = planTable.tableView_->addRow(author, "planCommand");
3526  planTable.tableView_->addRowToGroup(row, groupIdCol, groupName);
3527 
3528  // set command type
3529  planTable.tableView_->setURIEncodedValue(command.type_, row, cmdTypeCol);
3530 
3531  // set command status true
3532  planTable.tableView_->setValueAsString(
3533  "1", row, planTable.tableView_->getColStatus());
3534 
3535  // create command specifics
3536  if(commandTypeToCommandTableMap.find(command.type_) !=
3537  commandTypeToCommandTableMap.end()) // if table exists in map! (some
3538  // commands may have no parameters)
3539  {
3540  __SUP_COUT__ << "table "
3541  << commandTypeToCommandTableMap[command.type_].tableName_
3542  << __E__;
3543 
3544  // at this point have config, tempVersion, and createdFlag
3545 
3546  // create command parameter entry at command level
3547  cmdRow = commandTypeToCommandTableMap[command.type_].tableView_->addRow(
3548  author, true /*incrementUniqueData*/, command.type_ + "_COMMAND_");
3549 
3550  // parameters are linked
3551  // now set value of all parameters
3552  // find parameter column, and set value
3553  // if special target parameter, extract targets
3554  for(auto& param : command.params_)
3555  {
3556  __SUP_COUT__ << "\t param " << param.first << " : " << param.second
3557  << __E__;
3558 
3559  if(param.first == IterateTable::targetParams_.Tables_)
3560  {
3561  __SUP_COUT__ << "\t\t found target tables" << __E__;
3562  std::istringstream f(param.second);
3563 
3564  targetIndex = 0;
3565  while(getline(f, targetStr, '='))
3566  {
3567  __SUP_COUT__ << "\t\t targetStr = " << targetStr << __E__;
3568  if(!command.targets_.size() ||
3569  command.targets_.back().table_ != "")
3570  {
3571  __SUP_COUT__ << "\t\t make targetStr = " << targetStr
3572  << __E__;
3573  // make new target
3574  command.addTarget();
3575  command.targets_.back().table_ = targetStr;
3576  }
3577  else // file existing target
3578  command.targets_[targetIndex++].table_ = targetStr;
3579  }
3580 
3581  continue; // go to next parameter
3582  }
3583 
3584  if(param.first == IterateTable::targetParams_.UIDs_)
3585  {
3586  __SUP_COUT__ << "\t\t found target UIDs" << __E__;
3587  std::istringstream f(param.second);
3588 
3589  targetIndex = 0;
3590  while(getline(f, targetStr, '='))
3591  {
3592  __SUP_COUT__ << "\t\t targetStr = " << targetStr << __E__;
3593  if(!command.targets_.size() ||
3594  command.targets_.back().UID_ != "")
3595  {
3596  __SUP_COUT__ << "\t\t make targetStr = " << targetStr
3597  << __E__;
3598  // make new target
3599  command.addTarget();
3600  command.targets_.back().UID_ = targetStr;
3601  }
3602  else // file existing target
3603  command.targets_[targetIndex++].UID_ = targetStr;
3604  }
3605  continue;
3606  }
3607 
3608  cmdCol =
3609  commandTypeToCommandTableMap[command.type_].tableView_->findCol(
3610  param.first);
3611 
3612  __SUP_COUT__ << "param col " << cmdCol << __E__;
3613 
3614  commandTypeToCommandTableMap[command.type_]
3615  .tableView_->setURIEncodedValue(param.second, cmdRow, cmdCol);
3616  } // end parameter loop
3617 
3618  cmdUID =
3619  commandTypeToCommandTableMap[command.type_].tableView_->getDataView()
3620  [cmdRow][commandTypeToCommandTableMap[command.type_]
3621  .tableView_->getColUID()];
3622 
3623  if(command.targets_.size())
3624  {
3625  // if targets, create group in target table
3626 
3627  __SUP_COUT__ << "targets found for command UID=" << cmdUID << __E__;
3628 
3629  // create link from command table to target
3630  cmdCol =
3631  commandTypeToCommandTableMap[command.type_].tableView_->findCol(
3632  IterateTable::commandTargetCols_.TargetsLink_);
3633  commandTypeToCommandTableMap[command.type_]
3634  .tableView_->setValueAsString(
3635  IterateTable::TARGET_TABLE, cmdRow, cmdCol);
3636 
3637  cmdCol =
3638  commandTypeToCommandTableMap[command.type_].tableView_->findCol(
3639  IterateTable::commandTargetCols_.TargetsLinkGroupID_);
3640  commandTypeToCommandTableMap[command.type_]
3641  .tableView_->setValueAsString(
3642  cmdUID + "_Targets", cmdRow, cmdCol);
3643 
3644  // create row(s) for each target in target table with correct groupID
3645 
3646  for(const auto& target : command.targets_)
3647  {
3648  __SUP_COUT__ << target.table_ << " " << target.UID_ << __E__;
3649 
3650  // create target entry in target table in group
3651  tgtRow = targetTable.tableView_->addRow(author, "commandTarget");
3652  targetTable.tableView_->addRowToGroup(
3653  tgtRow, targetGroupIdCol, cmdUID + "_Targets");
3654 
3655  // set target table
3656  targetTable.tableView_->setValueAsString(
3657  target.table_, tgtRow, targetTableCol);
3658 
3659  // set target UID
3660  targetTable.tableView_->setValueAsString(
3661  target.UID_, tgtRow, targetUIDCol);
3662  }
3663  } // end target handling
3664 
3665  // add link at plan level to created UID
3666  planTable.tableView_->setValueAsString(
3667  commandTypeToCommandTableMap[command.type_].tableName_,
3668  row,
3669  commandUidLink.first);
3670  planTable.tableView_->setValueAsString(
3671  cmdUID, row, commandUidLink.second);
3672 
3673  __SUP_COUT__ << "linked to uid = " << cmdUID << __E__;
3674 
3675  commandTypeToCommandTableMap[command.type_].modified_ = true;
3676  } // done with command specifics
3677 
3678  } // end command loop
3679 
3680  // commands are created in the temporary tables
3681  // validate with init
3682 
3683  planTable.tableView_->print();
3684  planTable.tableView_->init(); // verify new table (throws runtime_errors)
3685 
3686  __SUP_COUT__ << "requestType tables:" << __E__;
3687 
3688  for(auto& modifiedConfig : commandTypeToCommandTableMap)
3689  {
3690  modifiedConfig.second.tableView_->print();
3691  modifiedConfig.second.tableView_->init();
3692  }
3693 
3694  targetTable.tableView_->print();
3695  targetTable.tableView_->init(); // verify new table (throws runtime_errors)
3696 
3697  } // end try for plan
3698  catch(...)
3699  {
3700  __SUP_COUT__ << "Handling command table errors while saving. Erasing all newly "
3701  "created versions."
3702  << __E__;
3703 
3704  // erase all temporary tables if created here
3705 
3706  if(planTable.createdTemporaryVersion_) // if temporary version created here
3707  {
3708  __SUP_COUT__ << "Erasing temporary version " << planTable.tableName_ << "-v"
3709  << planTable.temporaryVersion_ << __E__;
3710  // erase with proper version management
3711  cfgMgr->eraseTemporaryVersion(planTable.tableName_,
3712  planTable.temporaryVersion_);
3713  }
3714 
3715  if(targetTable.createdTemporaryVersion_) // if temporary version created here
3716  {
3717  __SUP_COUT__ << "Erasing temporary version " << targetTable.tableName_ << "-v"
3718  << targetTable.temporaryVersion_ << __E__;
3719  // erase with proper version management
3720  cfgMgr->eraseTemporaryVersion(targetTable.tableName_,
3721  targetTable.temporaryVersion_);
3722  }
3723 
3724  for(auto& modifiedConfig : commandTypeToCommandTableMap)
3725  {
3726  if(modifiedConfig.second
3727  .createdTemporaryVersion_) // if temporary version created here
3728  {
3729  __SUP_COUT__ << "Erasing temporary version "
3730  << modifiedConfig.second.tableName_ << "-v"
3731  << modifiedConfig.second.temporaryVersion_ << __E__;
3732  // erase with proper version management
3733  cfgMgr->eraseTemporaryVersion(modifiedConfig.second.tableName_,
3734  modifiedConfig.second.temporaryVersion_);
3735  }
3736  }
3737 
3738  throw; // re-throw
3739  }
3740 
3741  // all edits are complete and tables verified
3742  // need to save all edits properly
3743  // if not modified, discard
3744 
3745  TableVersion finalVersion = saveModifiedVersionXML(
3746  xmlOut,
3747  cfgMgr,
3748  planTable.tableName_,
3749  planTable.originalVersion_,
3750  true /*make temporary*/,
3751  planTable.table_,
3752  planTable.temporaryVersion_,
3753  true /*ignoreDuplicates*/); // save temporary version properly
3754 
3755  __SUP_COUT__ << "Final plan version is " << planTable.tableName_ << "-v"
3756  << finalVersion << __E__;
3757 
3758  finalVersion = saveModifiedVersionXML(
3759  xmlOut,
3760  cfgMgr,
3761  targetTable.tableName_,
3762  targetTable.originalVersion_,
3763  true /*make temporary*/,
3764  targetTable.table_,
3765  targetTable.temporaryVersion_,
3766  true /*ignoreDuplicates*/); // save temporary version properly
3767 
3768  __SUP_COUT__ << "Final target version is " << targetTable.tableName_ << "-v"
3769  << finalVersion << __E__;
3770 
3771  for(auto& modifiedConfig : commandTypeToCommandTableMap)
3772  {
3773  if(!modifiedConfig.second.modified_)
3774  {
3775  if(modifiedConfig.second
3776  .createdTemporaryVersion_) // if temporary version created here
3777  {
3778  __SUP_COUT__ << "Erasing unmodified temporary version "
3779  << modifiedConfig.second.tableName_ << "-v"
3780  << modifiedConfig.second.temporaryVersion_ << __E__;
3781  // erase with proper version management
3782  cfgMgr->eraseTemporaryVersion(modifiedConfig.second.tableName_,
3783  modifiedConfig.second.temporaryVersion_);
3784  }
3785  continue;
3786  }
3787 
3788  finalVersion = saveModifiedVersionXML(
3789  xmlOut,
3790  cfgMgr,
3791  modifiedConfig.second.tableName_,
3792  modifiedConfig.second.originalVersion_,
3793  true /*make temporary*/,
3794  modifiedConfig.second.table_,
3795  modifiedConfig.second.temporaryVersion_,
3796  true /*ignoreDuplicates*/); // save temporary version properly
3797 
3798  __SUP_COUT__ << "Final version is " << modifiedConfig.second.tableName_ << "-v"
3799  << finalVersion << __E__;
3800  }
3801 
3802  handleFillModifiedTablesXML(xmlOut, cfgMgr);
3803 }
3804 catch(std::runtime_error& e)
3805 {
3806  __SUP_SS__ << "Error detected saving Iteration Plan!\n\n " << e.what() << __E__;
3807  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
3808  xmlOut.addTextElementToData("Error", ss.str());
3809 }
3810 catch(...)
3811 {
3812  __SUP_SS__ << "Error detected saving Iteration Plan!\n\n " << __E__;
3813  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
3814  xmlOut.addTextElementToData("Error", ss.str());
3815 } // end handleSavePlanCommandSequenceXML
3816 
3817 //========================================================================================================================
3818 // handleSaveTreeNodeEditXML
3819 // Changes the value specified by UID/Column
3820 // in the specified version of the table.
3821 //
3822 // Error, if the specified version is not the active one.
3823 // If the version is not temporary make a new temporary version
3824 //
3825 // return this information on success
3826 // <resultingTargetTableVersion = xxx>
3827 void ConfigurationGUISupervisor::handleSaveTreeNodeEditXML(HttpXmlDocument& xmlOut,
3828  ConfigurationManagerRW* cfgMgr,
3829  const std::string& tableName,
3830  TableVersion version,
3831  const std::string& type,
3832  const std::string& uid,
3833  const std::string& colName,
3834  const std::string& newValue,
3835  const std::string& author) try
3836 {
3837  __SUP_COUT__ << "table " << tableName << "(" << version << ")" << __E__;
3838 
3839  // get the current table/version
3840  // check if the value is new
3841  // if new edit value (in a temporary version only)
3842 
3843  // get table and activate target version
3844  TableBase* config = cfgMgr->getTableByName(tableName);
3845  try
3846  {
3847  config->setActiveView(version);
3848  }
3849  catch(...)
3850  {
3851  if(version.isTemporaryVersion())
3852  throw; // if temporary, there is no hope to find lost version
3853 
3854  __SUP_COUT__ << "Failed to find stored version, so attempting to load version: "
3855  << version << __E__;
3856  cfgMgr->getVersionedTableByName(tableName, version);
3857  }
3858 
3859  __SUP_COUT__ << "Active version is " << config->getViewVersion() << __E__;
3860 
3861  if(version != config->getViewVersion())
3862  {
3863  __SUP_SS__ << "Target table version (" << version
3864  << ") is not the currently active version ("
3865  << config->getViewVersion() << ". Try refreshing the tree." << __E__;
3866  __SS_THROW__;
3867  }
3868 
3869  unsigned int col = -1;
3870  if(type == "uid" || type == "delete-uid")
3871  col = config->getView().getColUID();
3872  else if(type == "link-UID" || type == "link-GroupID" || type == "value" ||
3873  type == "value-groupid" || type == "value-bool" || type == "value-bitmap")
3874  col = config->getView().findCol(colName);
3875  else if(type == "table" || type == "link-comment" || type == "table-newGroupRow" ||
3876  type == "table-newUIDRow" || type == "table-newRow")
3877  ; // column N/A
3878  else
3879  {
3880  __SUP_SS__ << "Impossible! Unrecognized edit type: " << type << __E__;
3881  __SS_THROW__;
3882  }
3883 
3884  // check if the comment value is new before making temporary version
3885  if(type == "table" || type == "link-comment")
3886  {
3887  // editing comment, so check if comment is different
3888  if(config->getView().isURIEncodedCommentTheSame(newValue))
3889  {
3890  __SUP_SS__ << "Comment '" << newValue
3891  << "' is the same as the current comment. No need to save change."
3892  << __E__;
3893  __SS_THROW__;
3894  }
3895  }
3896 
3897  // version handling:
3898  // always make a new temporary-version from source-version
3899  // edit temporary-version
3900  // if edit fails
3901  // delete temporary-version
3902  // else
3903  // return new temporary-version
3904  // if source-version was temporary
3905  // then delete source-version
3906 
3907  TableVersion temporaryVersion = config->createTemporaryView(version);
3908 
3909  __SUP_COUT__ << "Created temporary version " << temporaryVersion << __E__;
3910 
3911  TableView* cfgView = config->getTemporaryView(temporaryVersion);
3912 
3913  // edit/verify new table (throws runtime_errors)
3914  try
3915  {
3916  // have view so edit it
3917  if(type == "table" || type == "link-comment")
3918  {
3919  // edit comment
3920  cfgView->setURIEncodedComment(newValue);
3921  }
3922  else if(type == "table-newRow" || type == "table-newUIDRow")
3923  {
3924  // add row
3925  unsigned int row = cfgView->addRow(author, true /*incrementUniqueData*/);
3926 
3927  // if TableViewColumnInfo::COL_NAME_STATUS exists, set it to true
3928  try
3929  {
3930  col = cfgView->getColStatus();
3931  cfgView->setValueAsString("1", row, col);
3932  }
3933  catch(...)
3934  {
3935  } // if not, ignore
3936 
3937  // set UID value
3938  cfgView->setURIEncodedValue(newValue, row, cfgView->getColUID());
3939  }
3940  else if(type == "table-newGroupRow")
3941  {
3942  // add row
3943  unsigned int row = cfgView->addRow(author, true /*incrementUniqueData*/);
3944 
3945  // get index value and group id value
3946  unsigned int csvIndex = newValue.find(',');
3947 
3948  std::string linkIndex = newValue.substr(0, csvIndex);
3949  std::string groupId = newValue.substr(csvIndex + 1);
3950 
3951  // get new row UID value from second part of string
3952  csvIndex = groupId.find(',');
3953  std::string newRowUID = groupId.substr(csvIndex + 1);
3954  groupId = groupId.substr(0, csvIndex);
3955 
3956  __SUP_COUT__ << "newValue " << linkIndex << "," << groupId << "," << newRowUID
3957  << __E__;
3958 
3959  // set UID value
3960  cfgView->setURIEncodedValue(newRowUID, row, cfgView->getColUID());
3961 
3962  // find groupId column from link index
3963  col = cfgView->getColLinkGroupID(linkIndex);
3964 
3965  // set group id
3966  cfgView->setURIEncodedValue(groupId, row, col);
3967 
3968  // if TableViewColumnInfo::COL_NAME_STATUS exists, set it to true
3969  try
3970  {
3971  col = cfgView->getColStatus();
3972  cfgView->setValueAsString("1", row, col);
3973  }
3974  catch(...)
3975  {
3976  } // if not, ignore
3977  }
3978  else if(type == "delete-uid")
3979  {
3980  // delete row
3981  unsigned int row = cfgView->findRow(col, uid);
3982  cfgView->deleteRow(row);
3983  }
3984  else if(type == "uid" || type == "value" || type == "value-groupid" ||
3985  type == "value-bool" || type == "value-bitmap")
3986  {
3987  unsigned int row = cfgView->findRow(cfgView->getColUID(), uid);
3988  if(!cfgView->setURIEncodedValue(newValue, row, col, author))
3989  {
3990  // no change! so discard
3991  __SUP_SS__ << "Value '" << newValue
3992  << "' is the same as the current value. No need to save "
3993  "change to tree node."
3994  << __E__;
3995  __SS_THROW__;
3996  }
3997  }
3998  else if(type == "link-UID" || type == "link-GroupID")
3999  {
4000  bool isGroup;
4001  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> linkPair;
4002  if(!cfgView->getChildLink(col, isGroup, linkPair))
4003  {
4004  // not a link ?!
4005  __SUP_SS__ << "Col '" << colName << "' is not a link column." << __E__;
4006  __SS_THROW__;
4007  }
4008 
4009  __SUP_COUT__ << "linkPair " << linkPair.first << "," << linkPair.second
4010  << __E__;
4011 
4012  std::string linkIndex = cfgView->getColumnInfo(col).getChildLinkIndex();
4013 
4014  __SUP_COUT__ << "linkIndex " << linkIndex << __E__;
4015 
4016  // find table value and id value
4017  unsigned int csvIndexStart = 0, csvIndex = newValue.find(',');
4018 
4019  std::string newTable = newValue.substr(csvIndexStart, csvIndex);
4020  csvIndexStart = csvIndex + 1;
4021  csvIndex = newValue.find(',', csvIndexStart);
4022  std::string newLinkId = newValue.substr(
4023  csvIndexStart,
4024  csvIndex -
4025  csvIndexStart); // if no more commas will take the rest of string
4026 
4027  __SUP_COUT__ << "newValue " << newTable << "," << newLinkId << __E__;
4028 
4029  // change target table in two parts
4030  unsigned int row = cfgView->findRow(cfgView->getColUID(), uid);
4031  bool changed = false;
4032  if(!cfgView->setURIEncodedValue(newTable, row, linkPair.first, author))
4033  {
4034  // no change
4035  __SUP_COUT__ << "Value '" << newTable
4036  << "' is the same as the current value." << __E__;
4037  }
4038  else
4039  changed = true;
4040 
4041  if(!cfgView->setURIEncodedValue(newLinkId, row, linkPair.second, author))
4042  {
4043  // no change
4044  __SUP_COUT__ << "Value '" << newLinkId
4045  << "' is the same as the current value." << __E__;
4046  }
4047  else
4048  changed = true;
4049 
4050  // handle groupID links slightly differently
4051  // have to look at changing link table too!
4052  // if group ID set all in member list to be members of group
4053  if(type == "link-GroupID")
4054  {
4055  bool secondaryChanged = false;
4056 
4057  // first close out main target table
4058  if(!changed) // if no changes throw out new version
4059  {
4060  __SUP_COUT__ << "No changes to primary view. Erasing temporary table."
4061  << __E__;
4062  config->eraseView(temporaryVersion);
4063  }
4064  else // if changes, save it
4065  {
4066  try
4067  {
4068  cfgView->init(); // verify new table (throws runtime_errors)
4069 
4070  saveModifiedVersionXML(xmlOut,
4071  cfgMgr,
4072  tableName,
4073  version,
4074  true /*make temporary*/,
4075  config,
4076  temporaryVersion,
4077  true /*ignoreDuplicates*/); // save
4078  // temporary
4079  // version
4080  // properly
4081  }
4082  catch(std::runtime_error&
4083  e) // erase temporary view before re-throwing error
4084  {
4085  __SUP_COUT__ << "Caught error while editing main table. Erasing "
4086  "temporary version."
4087  << __E__;
4088  config->eraseView(temporaryVersion);
4089  changed = false; // undo changed bool
4090 
4091  // send warning so that, secondary table can still be changed
4092  xmlOut.addTextElementToData(
4093  "Warning",
4094  "Error saving primary tree node! " + std::string(e.what()));
4095  }
4096  }
4097 
4098  // now, onto linked table
4099 
4100  // get the current linked table/version
4101  // check if the value is new
4102  // if new edit value (in a temporary version only)
4103 
4104  csvIndexStart = csvIndex + 1;
4105  csvIndex = newValue.find(',', csvIndexStart);
4106  version = TableVersion(newValue.substr(
4107  csvIndexStart, csvIndex - csvIndexStart)); // if no more commas will
4108  // take the rest of string
4109 
4110  if(newTable == TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
4111  {
4112  // done, since init was already tested
4113  // the result should be purposely DISCONNECTED link
4114  return;
4115  }
4116 
4117  // get table and activate target version
4118  config = cfgMgr->getTableByName(newTable);
4119  try
4120  {
4121  config->setActiveView(version);
4122  }
4123  catch(...)
4124  {
4125  __SUP_COUT__ << "Failed to find stored version, so attempting to "
4126  "load version: "
4127  << version << __E__;
4128  cfgMgr->getVersionedTableByName(newTable, version);
4129  }
4130 
4131  __SUP_COUT__ << "Active version is " << config->getViewVersion() << __E__;
4132 
4133  if(version != config->getViewVersion())
4134  {
4135  __SUP_SS__ << "Target table version (" << version
4136  << ") is not the currently active version ("
4137  << config->getViewVersion() << ". Try refreshing the tree."
4138  << __E__;
4139  __SS_THROW__;
4140  }
4141 
4142  // create temporary version for editing
4143  temporaryVersion = config->createTemporaryView(version);
4144 
4145  __SUP_COUT__ << "Created temporary version " << temporaryVersion << __E__;
4146 
4147  cfgView = config->getTemporaryView(temporaryVersion);
4148 
4149  col = cfgView->getColLinkGroupID(linkIndex);
4150 
4151  __SUP_COUT__ << "target col " << col << __E__;
4152 
4153  // extract vector of members to be
4154  std::vector<std::string> memberUIDs;
4155  do
4156  {
4157  csvIndexStart = csvIndex + 1;
4158  csvIndex = newValue.find(',', csvIndexStart);
4159  memberUIDs.push_back(
4160  newValue.substr(csvIndexStart, csvIndex - csvIndexStart));
4161  __SUP_COUT__ << "memberUIDs: " << memberUIDs.back() << __E__;
4162  } while(csvIndex != (unsigned int)std::string::npos); // no more commas
4163 
4164  // for each row,
4165  // check if should be in group
4166  // if should be but is not
4167  // add to group, CHANGE
4168  // if should not be but is
4169  // remove from group, CHANGE
4170  //
4171 
4172  std::string targetUID;
4173  bool shouldBeInGroup;
4174  bool defaultIsInGroup =
4175  false; // use to indicate if a recent new member was created
4176  bool isInGroup;
4177 
4178  for(unsigned int row = 0; row < cfgView->getNumberOfRows(); ++row)
4179  {
4180  targetUID = cfgView->getDataView()[row][cfgView->getColUID()];
4181  __SUP_COUT__ << "targetUID: " << targetUID << __E__;
4182 
4183  shouldBeInGroup = false;
4184  for(unsigned int i = 0; i < memberUIDs.size(); ++i)
4185  if(targetUID == memberUIDs[i])
4186  {
4187  // found in member uid list
4188  shouldBeInGroup = true;
4189  break;
4190  }
4191 
4192  isInGroup = cfgView->isEntryInGroup(row, linkIndex, newLinkId);
4193 
4194  // if should be but is not
4195  if(shouldBeInGroup && !isInGroup)
4196  {
4197  __SUP_COUT__ << "Changed YES: " << row << __E__;
4198  secondaryChanged = true;
4199 
4200  cfgView->addRowToGroup(row, col, newLinkId);
4201 
4202  } // if should not be but is
4203  else if(!shouldBeInGroup && isInGroup)
4204  {
4205  __SUP_COUT__ << "Changed NO: " << row << __E__;
4206  secondaryChanged = true;
4207 
4208  cfgView->removeRowFromGroup(row, col, newLinkId);
4209  }
4210  else if(targetUID ==
4211  cfgView->getDefaultRowValues()[cfgView->getColUID()] &&
4212  isInGroup)
4213  {
4214  // use to indicate if a recent new member was created
4215  defaultIsInGroup = true;
4216  }
4217  }
4218 
4219  // first close out main target table
4220  if(!secondaryChanged) // if no changes throw out new version
4221  {
4222  __SUP_COUT__
4223  << "No changes to secondary view. Erasing temporary table."
4224  << __E__;
4225  config->eraseView(temporaryVersion);
4226  }
4227  else // if changes, save it
4228  {
4229  try
4230  {
4231  cfgView->init(); // verify new table (throws runtime_errors)
4232 
4233  saveModifiedVersionXML(xmlOut,
4234  cfgMgr,
4235  newTable,
4236  version,
4237  true /*make temporary*/,
4238  config,
4239  temporaryVersion,
4240  true /*ignoreDuplicates*/); // save
4241  // temporary
4242  // version
4243  // properly
4244  }
4245  catch(std::runtime_error&
4246  e) // erase temporary view before re-throwing error
4247  {
4248  __SUP_COUT__ << "Caught error while editing secondary table. "
4249  "Erasing temporary version."
4250  << __E__;
4251  config->eraseView(temporaryVersion);
4252  secondaryChanged = false; // undo changed bool
4253 
4254  // send warning so that, secondary table can still be changed
4255  xmlOut.addTextElementToData(
4256  "Warning",
4257  "Error saving secondary tree node! " + std::string(e.what()));
4258  }
4259  }
4260 
4261  // block error message if default is in group, assume new member was just
4262  // created RAR: block because its hard to detect if changes were recently
4263  // made (one idea: to check if all other values are defaults, to assume it
4264  // was just created)
4265  if(0 && !changed && !secondaryChanged && !defaultIsInGroup)
4266  {
4267  __SUP_SS__ << "Link to table '" << newTable << "', linkID '"
4268  << newLinkId
4269  << "', and selected group members are the same as the "
4270  "current value. "
4271  << "No need to save changes to tree." << __E__;
4272  __SS_THROW__;
4273  }
4274 
4275  return; // exit since table inits were already tested
4276  }
4277  else if(0 && !changed) // block error message because sometimes things get
4278  // setup twice depending on the path of the user (e.g.
4279  // when editing links in tree-view)
4280  { // RAR: block also becuase versions are temporary at this point anyway,
4281  // might as well abuse temporary versions
4282  __SUP_SS__ << "Link to table '" << newTable << "' and linkID '"
4283  << newLinkId
4284  << "' are the same as the current values. No need to save "
4285  "change to tree node."
4286  << __E__;
4287  __SS_THROW__;
4288  }
4289  }
4290 
4291  cfgView->init(); // verify new table (throws runtime_errors)
4292  }
4293  catch(...) // erase temporary view before re-throwing error
4294  {
4295  __SUP_COUT__ << "Caught error while editing. Erasing temporary version." << __E__;
4296  config->eraseView(temporaryVersion);
4297  throw;
4298  }
4299 
4300  saveModifiedVersionXML(xmlOut,
4301  cfgMgr,
4302  tableName,
4303  version,
4304  true /*make temporary*/,
4305  config,
4306  temporaryVersion,
4307  true /*ignoreDuplicates*/); // save temporary version properly
4308 }
4309 catch(std::runtime_error& e)
4310 {
4311  __SUP_SS__ << "Error saving tree node! " << e.what() << __E__;
4312  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
4313  xmlOut.addTextElementToData("Error", ss.str());
4314 }
4315 catch(...)
4316 {
4317  __SUP_SS__ << "Unknown Error saving tree node! " << __E__;
4318  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
4319  xmlOut.addTextElementToData("Error", ss.str());
4320 }
4321 
4322 //========================================================================================================================
4323 // handleGetTableGroupXML
4324 //
4325 // give the detail of specific table specified
4326 // groupKey=-1 returns latest
4327 //
4328 // Find historical group keys
4329 // and figure out all member configurations versions
4330 
4331 //
4332 // return this information
4333 // <group name=xxx key=xxx>
4334 // <historical key=xxx>
4335 // <historical key=xxx>
4336 // ....
4337 // <table name=xxx version=xxx />
4338 // <historical version=xxx>
4339 // <historical version=xxx>
4340 // ...
4341 // </table>
4342 // <table name=xxx version=xxx>
4343 // ...
4344 // </table>
4345 void ConfigurationGUISupervisor::handleGetTableGroupXML(HttpXmlDocument& xmlOut,
4346  ConfigurationManagerRW* cfgMgr,
4347  const std::string& groupName,
4348  TableGroupKey groupKey) try
4349 {
4350  char tmpIntStr[100];
4351  DOMElement *parentEl, *configEl;
4352 
4353  // steps:
4354  // if invalid key, get latest key
4355  // get specific group with key
4356  // give member names and versions
4357  // get all table groups to locate historical keys
4358  // get all groups to find historical keys
4359 
4360  // std::set<std::string /*name+version*/> allGroups =
4361  // cfgMgr->getConfigurationInterface()->getAllTableGroupNames(groupName);
4362  // std::string name;
4363  // TableGroupKey key;
4364  // //put them in a set to sort them as TableGroupKey defines for operator<
4365  // std::set<TableGroupKey> sortedKeys;
4366  // for(auto& group: allGroups)
4367  // {
4368  // //now uses database filter
4369  // TableGroupKey::getGroupNameAndKey(group,name,key);
4370  // //if(name == groupName)
4371  // sortedKeys.emplace(key);
4372  // }
4373 
4374  const GroupInfo& groupInfo = cfgMgr->getGroupInfo(groupName);
4375  const std::set<TableGroupKey>& sortedKeys = groupInfo.keys_; // rename
4376 
4377  if(groupKey.isInvalid() || // if invalid or not found, get latest
4378  sortedKeys.find(groupKey) == sortedKeys.end())
4379  {
4380  if(sortedKeys.size())
4381  groupKey = *sortedKeys.rbegin();
4382  __SUP_COUT__ << "Group key requested was invalid or not found, going with latest "
4383  << groupKey << __E__;
4384  }
4385 
4386  xmlOut.addTextElementToData("TableGroupName", groupName);
4387  xmlOut.addTextElementToData("TableGroupKey", groupKey.toString());
4388 
4389  // add all other sorted keys for this groupName
4390  for(auto& keyInOrder : sortedKeys)
4391  xmlOut.addTextElementToData("HistoricalTableGroupKey", keyInOrder.toString());
4392 
4393  parentEl = xmlOut.addTextElementToData("TableGroupMembers", "");
4394 
4395  // get specific group with key
4396  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
4397  std::map<std::string /*name*/, std::string /*alias*/> groupMemberAliases;
4398 
4399  __SUP_COUT__ << "groupName=" << groupName << __E__;
4400  __SUP_COUT__ << "groupKey=" << groupKey << __E__;
4401 
4402  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
4403  std::map<std::string, TableInfo>::const_iterator it;
4404 
4405  // load group so comments can be had
4406  // and also group metadata (author, comment, createTime)
4407  try
4408  {
4409  std::string groupAuthor, groupComment, groupCreationTime, groupTypeString;
4410  std::string accumulateErrors;
4411 
4412  cfgMgr->loadTableGroup(groupName,
4413  groupKey,
4414  false /*doActivate*/,
4415  &memberMap,
4416  0 /*progressBar*/,
4417  &accumulateErrors /*accumulateErrors*/,
4418  &groupComment,
4419  &groupAuthor,
4420  &groupCreationTime,
4421  false /*doNotLoadMember*/,
4422  &groupTypeString,
4423  &groupMemberAliases);
4424 
4425  if(accumulateErrors != "")
4426  {
4427  __SUP_SS__ << accumulateErrors;
4428  __SUP_SS_THROW__;
4429  }
4430 
4431  xmlOut.addTextElementToData("TableGroupAuthor", groupAuthor);
4432  xmlOut.addTextElementToData("TableGroupComment", groupComment);
4433  xmlOut.addTextElementToData("TableGroupCreationTime", groupCreationTime);
4434  xmlOut.addTextElementToData("TableGroupType", groupTypeString);
4435  }
4436  catch(const std::runtime_error& e)
4437  {
4438  __SUP_SS__ << "Table group \"" + groupName + "(" + groupKey.toString() + ")" +
4439  "\" members can not be loaded!\n\n" + e.what();
4440  __SUP_COUT_ERR__ << ss.str();
4441  xmlOut.addTextElementToData("Error", ss.str());
4442  // return;
4443  }
4444  catch(...)
4445  {
4446  __SUP_SS__ << "Table group \"" + groupName + "(" + groupKey.toString() + ")" +
4447  "\" members can not be loaded!"
4448  << __E__;
4449  __SUP_COUT_ERR__ << ss.str();
4450  xmlOut.addTextElementToData("Error", ss.str());
4451  // return;
4452  }
4453 
4454  __COUTV__(StringMacros::mapToString(groupMemberAliases));
4455 
4456  std::map<std::string, std::map<std::string, TableVersion>> versionAliases =
4457  cfgMgr->getVersionAliases();
4458 
4459  __SUP_COUT__ << "# of table version aliases: " << versionAliases.size() << __E__;
4460 
4461  // Seperate loop to get name and version
4462  for(auto& memberPair : memberMap)
4463  {
4464  xmlOut.addTextElementToParent("MemberName", memberPair.first, parentEl);
4465 
4466  // if member is in groupMemberAliases, then alias version
4467  if(groupMemberAliases.find(memberPair.first) != groupMemberAliases.end())
4468  configEl = xmlOut.addTextElementToParent(
4469  "MemberVersion",
4470  ConfigurationManager::ALIAS_VERSION_PREAMBLE +
4471  groupMemberAliases[memberPair.first], // return the ALIAS:<alias>
4472  parentEl);
4473  else
4474  configEl = xmlOut.addTextElementToParent(
4475  "MemberVersion", memberPair.second.toString(), parentEl);
4476 
4477  it = allTableInfo.find(memberPair.first);
4478  if(it == allTableInfo.end())
4479  {
4480  xmlOut.addTextElementToData(
4481  "Error", "Table \"" + memberPair.first + "\" can not be retrieved!");
4482  continue;
4483  }
4484 
4485  if(versionAliases.find(it->first) != versionAliases.end())
4486  for(auto& aliasVersion : versionAliases[it->first])
4487  xmlOut.addTextElementToParent(
4488  "TableExistingVersion",
4489  ConfigurationManager::ALIAS_VERSION_PREAMBLE + aliasVersion.first,
4490  configEl);
4491 
4492  for(auto& version : it->second.versions_)
4493  // if(version == memberPair.second) continue; //CHANGED by RAR on 11/14/2016
4494  // (might as well show all versions in list to avoid user confusion) else
4495  xmlOut.addTextElementToParent(
4496  "TableExistingVersion", version.toString(), configEl);
4497  }
4498  // Seperate loop just for getting the Member Comment
4499  for(auto& memberPair : memberMap)
4500  {
4501  //__SUP_COUT__ << "\tMember config " << memberPair.first << ":" <<
4502  // memberPair.second << __E__;
4503 
4504  // xmlOut.addTextElementToParent("MemberName", memberPair.first, parentEl);
4505  // if(commentsLoaded)
4506  xmlOut.addTextElementToParent(
4507  "MemberComment",
4508  allTableInfo.at(memberPair.first).tablePtr_->getView().getComment(),
4509  parentEl);
4510  // else
4511  // xmlOut.addTextElementToParent("MemberComment", "", parentEl);
4512 
4513  // __SUP_COUT__ << "\tMember config " << memberPair.first << ":" <<
4514  // memberPair.second << __E__;
4515 
4516  // configEl = xmlOut.addTextElementToParent("MemberVersion",
4517  // memberPair.second.toString(), parentEl);
4518 
4519  /* it = allTableInfo.find(memberPair.first);
4520  if(it == allTableInfo.end())
4521  {
4522  xmlOut.addTextElementToData("Error","Table \"" +
4523  memberPair.first +
4524  "\" can not be retrieved!");
4525  return;
4526  }
4527  */
4528  // include aliases for this table
4529  /*if(versionAliases.find(it->first) != versionAliases.end())
4530  for (auto& aliasVersion:versionAliases[it->first])
4531  xmlOut.addTextElementToParent("TableExistingVersion",
4532  ConfigurationManager::ALIAS_VERSION_PREAMBLE + aliasVersion.first,
4533  configEl);
4534 
4535  for (auto& version:it->second.versions_)
4536  //if(version == memberPair.second) continue; //CHANGED by RAR on 11/14/2016
4537  (might as well show all versions in list to avoid user confusion)
4538  //else
4539  xmlOut.addTextElementToParent("TableExistingVersion",
4540  version.toString(), configEl);
4541  */
4542  }
4543 
4544  return;
4545 }
4546 catch(std::runtime_error& e)
4547 {
4548  __SUP_SS__ << ("Error!\n\n" + std::string(e.what())) << __E__;
4549  __SUP_COUT_ERR__ << "\n" << ss.str();
4550  xmlOut.addTextElementToData("Error", ss.str());
4551 }
4552 catch(...)
4553 {
4554  __SUP_SS__ << ("Error!\n\n") << __E__;
4555  __SUP_COUT_ERR__ << "\n" << ss.str();
4556  xmlOut.addTextElementToData("Error", ss.str());
4557 }
4558 
4559 //========================================================================================================================
4560 // handleGetTableXML
4561 //
4562 // if INVALID or version does not exists, default to mock-up
4563 //
4564 // give the detail of specific table specified
4565 // by tableName and version
4566 //
4567 // if no version selected, default to latest version
4568 // if no versions exists, default to mock-up
4569 //
4570 // return existing versions
4571 // return column headers
4572 // return number of rows
4573 // from dataOffset
4574 // first CHUNK_SIZE rows
4575 //
4576 // return this information
4577 //<table name=xxx version=xxx rowCount=xxx chunkReq=xxx chunkSz=xxx>
4578 // <existing version=xxx>
4579 // <existing version=xxx>
4580 // ....
4581 // <colhdr name=xxx>
4582 // <colhdr name=xxx>
4583 // ....
4584 // <rowdata>
4585 // <cell value=xxx>
4586 // <cell value=xxx>
4587 // ....
4588 // </rowdata>
4589 // <rowdata>
4590 // ....
4591 // </rowdata>
4592 // ....
4593 //</table>
4594 //
4595 //
4596 // Note: options.. if allowIllegalColumns then attempts to load data to current mockup
4597 // column names
4598 // if not allowIllegalColumns, then it is still ok if the source has more or less
4599 // columns: the client is notified through "TableWarnings" field in this case.
4600 void ConfigurationGUISupervisor::handleGetTableXML(HttpXmlDocument& xmlOut,
4601  ConfigurationManagerRW* cfgMgr,
4602  const std::string& tableName,
4603  TableVersion version,
4604  bool allowIllegalColumns) try
4605 {
4606  char tmpIntStr[100];
4607  DOMElement *parentEl, *subparentEl;
4608 
4609  std::string accumulatedErrors = "";
4610 
4611  const std::map<std::string, TableInfo>&
4612  allTableInfo = // if allowIllegalColumns, then also refresh
4613  cfgMgr->getAllTableInfo(allowIllegalColumns,
4614  allowIllegalColumns ? &accumulatedErrors : 0,
4615  tableName); // filter errors by tableName
4616 
4617  TableBase* table = cfgMgr->getTableByName(tableName);
4618 
4619  // send all table names along with
4620  // and check for specific version
4621  xmlOut.addTextElementToData("ExistingTableNames",
4622  TableViewColumnInfo::DATATYPE_LINK_DEFAULT);
4623  for(auto& configPair : allTableInfo)
4624  {
4625  xmlOut.addTextElementToData("ExistingTableNames", configPair.first);
4626  if(configPair.first == tableName && // check that version exists
4627  configPair.second.versions_.find(version) == configPair.second.versions_.end())
4628  {
4629  __SUP_COUT__ << "Version not found, so using mockup." << __E__;
4630  version = TableVersion(); // use INVALID
4631  }
4632  }
4633 
4634  xmlOut.addTextElementToData("TableName", tableName); // table name
4635  xmlOut.addTextElementToData("TableDescription",
4636  table->getTableDescription()); // table name
4637 
4638  // existing table versions
4639  {
4640  // get version aliases for translation
4641  std::map<
4642  std::string /*table name*/,
4643  std::map<std::string /*version alias*/, TableVersion /*aliased version*/>>
4644  versionAliases;
4645  try
4646  {
4647  // use whatever backbone is currently active
4648  versionAliases = cfgMgr->getVersionAliases();
4649  for(const auto& aliases : versionAliases)
4650  for(const auto& alias : aliases.second)
4651  __SUP_COUT__ << "ALIAS: " << aliases.first << " " << alias.first
4652  << " ==> " << alias.second << __E__;
4653  }
4654  catch(const std::runtime_error& e)
4655  {
4656  __SUP_COUT__ << "Could not get backbone information for version aliases: "
4657  << e.what() << __E__;
4658  }
4659 
4660  auto tableIterator = versionAliases.find(tableName);
4661 
4662  parentEl = xmlOut.addTextElementToData("TableVersions", "");
4663  for(const TableVersion& v : allTableInfo.at(tableName).versions_)
4664  {
4665  subparentEl =
4666  xmlOut.addTextElementToParent("Version", v.toString(), parentEl);
4667 
4668  if(tableIterator != versionAliases.end())
4669  {
4670  // check if this version has one or many aliases
4671  for(const auto& aliasPair : tableIterator->second)
4672  {
4673  // __SUP_COUT__ << "Checking " << aliasPair.second <<
4674  //"
4675  //-->
4676  //"
4677  //<< aliasPair.first << " for " << v <<
4678  //__E__;
4679  if(v == aliasPair.second)
4680  {
4681  __SUP_COUT__ << "Found Alias " << aliasPair.second << " --> "
4682  << aliasPair.first << __E__;
4683  xmlOut.addTextElementToParent(
4684  "VersionAlias", aliasPair.first, subparentEl);
4685  }
4686  }
4687  }
4688  }
4689  }
4690 
4691  // table columns and then rows (from table view)
4692 
4693  // get view pointer
4694  TableView* cfgViewPtr;
4695  if(version.isInvalid()) // use mock-up
4696  {
4697  cfgViewPtr = table->getMockupViewP();
4698  }
4699  else // use view version
4700  {
4701  try
4702  {
4703  cfgViewPtr = cfgMgr->getVersionedTableByName(tableName, version)->getViewP();
4704  }
4705  catch(std::runtime_error& e) // default to mock-up for fail-safe in GUI editor
4706  {
4707  __SUP_SS__ << "Failed to get table " << tableName << " version " << version
4708  << "... defaulting to mock-up! " << __E__;
4709  ss << "\n\n...Here is why it failed:\n\n" << e.what() << __E__;
4710 
4711  __SUP_COUT_ERR__ << "\n" << ss.str();
4712  version = TableVersion();
4713  cfgViewPtr = table->getMockupViewP();
4714 
4715  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
4716  }
4717  catch(...) // default to mock-up for fail-safe in GUI editor
4718  {
4719  __SUP_SS__ << "Failed to get table " << tableName << " version: " << version
4720  << "... defaulting to mock-up! "
4721  << "(You may want to try again to see what was partially loaded "
4722  "into cache before failure. "
4723  << "If you think, the failure is due to a column name change, "
4724  << "you can also try to Copy the failing view to the new column "
4725  "names using "
4726  << "'Copy and Move' functionality.)" << __E__;
4727 
4728  __SUP_COUT_ERR__ << "\n" << ss.str();
4729  version = TableVersion();
4730  cfgViewPtr = table->getMockupViewP();
4731 
4732  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
4733  }
4734  }
4735  xmlOut.addTextElementToData("TableVersion", version.toString()); // table version
4736 
4737  // get 'columns' of view
4738  DOMElement* choicesParentEl;
4739  parentEl = xmlOut.addTextElementToData("CurrentVersionColumnHeaders", "");
4740 
4741  std::vector<TableViewColumnInfo> colInfo = cfgViewPtr->getColumnsInfo();
4742 
4743  for(int i = 0; i < (int)colInfo.size(); ++i) // column headers and types
4744  {
4745  // __SUP_COUT__ << "\t\tCol " << i << ": " << colInfo[i].getType() << "() "
4746  //<< colInfo[i].getName() << " "
4747  // << colInfo[i].getStorageName() << " " << colInfo[i].getDataType()
4748  //<<
4749  //__E__;
4750 
4751  xmlOut.addTextElementToParent("ColumnHeader", colInfo[i].getName(), parentEl);
4752  xmlOut.addTextElementToParent("ColumnType", colInfo[i].getType(), parentEl);
4753  xmlOut.addTextElementToParent(
4754  "ColumnDataType", colInfo[i].getDataType(), parentEl);
4755 
4756  choicesParentEl = xmlOut.addTextElementToParent("ColumnChoices", "", parentEl);
4757  // add data choices if necessary
4758  if(colInfo[i].getType() == TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA ||
4759  colInfo[i].getType() == TableViewColumnInfo::TYPE_BITMAP_DATA ||
4760  colInfo[i].isChildLink())
4761  {
4762  for(auto& choice : colInfo[i].getDataChoices())
4763  xmlOut.addTextElementToParent("ColumnChoice", choice, choicesParentEl);
4764  }
4765  }
4766 
4767  // verify mockup columns after columns are posted to xmlOut
4768  try
4769  {
4770  if(version.isInvalid())
4771  cfgViewPtr->init();
4772  }
4773  catch(std::runtime_error& e)
4774  {
4775  // append accumulated errors, because they may be most useful
4776  __THROW__(e.what() + std::string("\n\n") + accumulatedErrors);
4777  }
4778  catch(...)
4779  {
4780  throw;
4781  }
4782 
4783  parentEl = xmlOut.addTextElementToData("CurrentVersionRows", "");
4784 
4785  for(int r = 0; r < (int)cfgViewPtr->getNumberOfRows(); ++r)
4786  {
4787  //__SUP_COUT__ << "\t\tRow " << r << ": " << __E__;
4788 
4789  sprintf(tmpIntStr, "%d", r);
4790  DOMElement* tmpParentEl =
4791  xmlOut.addTextElementToParent("Row", tmpIntStr, parentEl);
4792 
4793  for(int c = 0; c < (int)cfgViewPtr->getNumberOfColumns(); ++c)
4794  {
4795  if(colInfo[c].getDataType() == TableViewColumnInfo::DATATYPE_TIME)
4796  {
4797  std::string timeAsString;
4798  cfgViewPtr->getValue(timeAsString, r, c);
4799  xmlOut.addTextElementToParent("Entry", timeAsString, tmpParentEl);
4800  }
4801  else
4802  xmlOut.addTextElementToParent(
4803  "Entry", cfgViewPtr->getDataView()[r][c], tmpParentEl);
4804  }
4805  }
4806 
4807  // add "other" fields associated with configView
4808  xmlOut.addTextElementToData("TableComment", cfgViewPtr->getComment());
4809  xmlOut.addTextElementToData("TableAuthor", cfgViewPtr->getAuthor());
4810  xmlOut.addTextElementToData("TableCreationTime",
4811  std::to_string(cfgViewPtr->getCreationTime()));
4812  xmlOut.addTextElementToData("TableLastAccessTime",
4813  std::to_string(cfgViewPtr->getLastAccessTime()));
4814 
4815  // add to xml the default row values
4816  std::vector<std::string> defaultRowValues = cfgViewPtr->getDefaultRowValues();
4817  // don't give author and time.. force default author, let JS fill time
4818  for(unsigned int c = 0; c < defaultRowValues.size() - 2; ++c)
4819  {
4820  // __SUP_COUT__ << "Default for c" << c << "=" <<
4821  // cfgViewPtr->getColumnInfo(c).getName() << " is " <<
4822  // defaultRowValues[c] << __E__;
4823  xmlOut.addTextElementToData("DefaultRowValue", defaultRowValues[c]);
4824  }
4825 
4826  if(accumulatedErrors != "") // add accumulated errors to xmlOut
4827  {
4828  __SUP_SS__ << (std::string("Column errors were allowed for this request, so "
4829  "maybe you can ignore this, ") +
4830  "but please note the following errors:\n" + accumulatedErrors)
4831  << __E__;
4832  __SUP_COUT_ERR__ << ss.str();
4833  xmlOut.addTextElementToData("Error", ss.str());
4834  }
4835  else if(!version.isTemporaryVersion() && // not temporary (these are not filled from
4836  // interface source)
4837  (cfgViewPtr->getDataColumnSize() != cfgViewPtr->getNumberOfColumns() ||
4838  cfgViewPtr->getSourceColumnMismatch() !=
4839  0)) // check for column size mismatch
4840  {
4841  __SUP_SS__ << "\n\nThere were warnings found when loading the table " << tableName
4842  << ":v" << version << ". Please see the details below:\n\n"
4843  << "The source column size was found to be "
4844  << cfgViewPtr->getDataColumnSize()
4845  << ", and the current number of columns for this table is "
4846  << cfgViewPtr->getNumberOfColumns() << ". This resulted in a count of "
4847  << cfgViewPtr->getSourceColumnMismatch()
4848  << " source column mismatches, and a count of "
4849  << cfgViewPtr->getSourceColumnMissing() << " table entries missing in "
4850  << cfgViewPtr->getNumberOfRows() << " row(s) of data." << __E__;
4851 
4852  const std::set<std::string> srcColNames = cfgViewPtr->getSourceColumnNames();
4853  ss << "\n\nSource column names in ALPHABETICAL order were as follows:\n";
4854  char index = 'a';
4855  std::string preIndexStr = "";
4856  for(auto& srcColName : srcColNames)
4857  {
4858  ss << "\n\t" << preIndexStr << index << ". " << srcColName;
4859  if(index == 'z') // wrap-around
4860  {
4861  preIndexStr += 'a'; // keep adding index 'digits' for wrap-around
4862  index = 'a';
4863  }
4864  else
4865  ++index;
4866  }
4867  ss << __E__;
4868 
4869  std::set<std::string> destColNames = cfgViewPtr->getColumnStorageNames();
4870  ss << "\n\nCurrent table column names in ALPHABETICAL order are as follows:\n";
4871  index = 'a';
4872  preIndexStr = "";
4873  for(auto& destColName : destColNames)
4874  {
4875  ss << "\n\t" << preIndexStr << index << ". " << destColName;
4876  if(index == 'z') // wrap-around
4877  {
4878  preIndexStr += 'a'; // keep adding index 'digits' for wrap-around
4879  index = 'a';
4880  }
4881  else
4882  ++index;
4883  }
4884  ss << __E__;
4885 
4886  __SUP_COUT__ << "\n" << ss.str();
4887  xmlOut.addTextElementToData("TableWarnings", ss.str());
4888  }
4889 } // end handleGetTableXML()
4890 catch(std::runtime_error& e)
4891 {
4892  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
4893  xmlOut.addTextElementToData("Error", "Error getting view! " + std::string(e.what()));
4894 }
4895 catch(...)
4896 {
4897  __SUP_COUT__ << "Error detected!\n\n " << __E__;
4898  xmlOut.addTextElementToData("Error", "Error getting view! ");
4899 }
4900 
4901 //========================================================================================================================
4902 // saveModifiedVersionXML
4903 //
4904 // once source version has been modified in temporary version
4905 // this function finishes it off.
4906 TableVersion ConfigurationGUISupervisor::saveModifiedVersionXML(
4907  HttpXmlDocument& xmlOut,
4908  ConfigurationManagerRW* cfgMgr,
4909  const std::string& tableName,
4910  TableVersion originalVersion,
4911  bool makeTemporary,
4912  TableBase* config,
4913  TableVersion temporaryModifiedVersion,
4914  bool ignoreDuplicates,
4915  bool lookForEquivalent)
4916 {
4917  bool needToEraseTemporarySource =
4918  (originalVersion.isTemporaryVersion() && !makeTemporary);
4919 
4920  // check for duplicate tables already in cache
4921  if(!ignoreDuplicates)
4922  {
4923  __SUP_COUT__ << "Checking for duplicate tables..." << __E__;
4924 
4925  TableVersion duplicateVersion;
4926 
4927  {
4928  //"DEEP" checking
4929  // load into cache 'recent' versions for this table
4930  // 'recent' := those already in cache, plus highest version numbers not
4931  // in cache
4932  const std::map<std::string, TableInfo>& allTableInfo =
4933  cfgMgr->getAllTableInfo(); // do not refresh
4934 
4935  auto versionReverseIterator =
4936  allTableInfo.at(tableName).versions_.rbegin(); // get reverse iterator
4937  __SUP_COUT__ << "Filling up cached from " << config->getNumberOfStoredViews()
4938  << " to max count of " << config->MAX_VIEWS_IN_CACHE << __E__;
4939  for(; config->getNumberOfStoredViews() < config->MAX_VIEWS_IN_CACHE &&
4940  versionReverseIterator != allTableInfo.at(tableName).versions_.rend();
4941  ++versionReverseIterator)
4942  {
4943  __SUP_COUT__ << "Versions in reverse order " << *versionReverseIterator
4944  << __E__;
4945  try
4946  {
4947  cfgMgr->getVersionedTableByName(
4948  tableName, *versionReverseIterator); // load to cache
4949  }
4950  catch(const std::runtime_error& e)
4951  {
4952  __SUP_COUT__
4953  << "Error loadiing historical version, but ignoring: " << e.what()
4954  << __E__;
4955  }
4956  }
4957  }
4958 
4959  __SUP_COUT__ << "Checking duplicate..." << __E__;
4960 
4961  duplicateVersion = config->checkForDuplicate(
4962  temporaryModifiedVersion,
4963  (!originalVersion.isTemporaryVersion() && !makeTemporary)
4964  ? TableVersion()
4965  : // if from persistent to persistent, then include original version in
4966  // search
4967  originalVersion);
4968 
4969  if(lookForEquivalent && !duplicateVersion.isInvalid())
4970  {
4971  // found an equivalent!
4972  __SUP_COUT__ << "Equivalent table found in version v" << duplicateVersion
4973  << __E__;
4974 
4975  // if duplicate version was temporary, do not use
4976  if(duplicateVersion.isTemporaryVersion() && !makeTemporary)
4977  {
4978  __SUP_COUT__ << "Need persistent. Duplicate version was temporary. "
4979  "Abandoning duplicate."
4980  << __E__;
4981  duplicateVersion = TableVersion(); // set invalid
4982  }
4983  else
4984  {
4985  // erase and return equivalent version
4986 
4987  // erase modified equivalent version
4988  cfgMgr->eraseTemporaryVersion(tableName, temporaryModifiedVersion);
4989 
4990  // erase original if needed
4991  if(needToEraseTemporarySource)
4992  cfgMgr->eraseTemporaryVersion(tableName, originalVersion);
4993 
4994  xmlOut.addTextElementToData("savedName", tableName);
4995  xmlOut.addTextElementToData("savedVersion", duplicateVersion.toString());
4996  xmlOut.addTextElementToData("foundEquivalentVersion", "1");
4997 
4998  __SUP_COUT__ << "\t\t equivalent AssignedVersion: " << duplicateVersion
4999  << __E__;
5000 
5001  return duplicateVersion;
5002  }
5003  }
5004 
5005  if(!duplicateVersion.isInvalid())
5006  {
5007  __SUP_SS__
5008  << "This version of table '" << tableName <<
5009  "' is identical to another version currently cached v"
5010  << duplicateVersion << ". No reason to save a duplicate." << __E__;
5011  __SUP_COUT_ERR__ << "\n" << ss.str();
5012 
5013  // delete temporaryModifiedVersion
5014  config->eraseView(temporaryModifiedVersion);
5015  __SS_THROW__;
5016  }
5017 
5018  __SUP_COUT__ << "Check for duplicate tables complete." << __E__;
5019  }
5020 
5021  if(makeTemporary)
5022  __SUP_COUT__ << "\t\t**************************** Save as temporary table version"
5023  << __E__;
5024  else
5025  __SUP_COUT__ << "\t\t**************************** Save as new table version"
5026  << __E__;
5027 
5028  TableVersion newAssignedVersion =
5029  cfgMgr->saveNewTable(tableName, temporaryModifiedVersion, makeTemporary);
5030 
5031  if(needToEraseTemporarySource)
5032  cfgMgr->eraseTemporaryVersion(tableName, originalVersion);
5033 
5034  xmlOut.addTextElementToData("savedName", tableName);
5035  xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString());
5036 
5037  __SUP_COUT__ << "\t\t newAssignedVersion: " << newAssignedVersion << __E__;
5038  return newAssignedVersion;
5039 }
5040 
5041 //========================================================================================================================
5042 // handleCreateTableXML
5043 //
5044 // Save the detail of specific table specified
5045 // by tableName and version
5046 // ...starting from dataOffset
5047 //
5048 // Note: if starting version is -1 start from mock-up
5049 void ConfigurationGUISupervisor::handleCreateTableXML(HttpXmlDocument& xmlOut,
5050  ConfigurationManagerRW* cfgMgr,
5051  const std::string& tableName,
5052  TableVersion version,
5053  bool makeTemporary,
5054  const std::string& data,
5055  const int& dataOffset,
5056  const std::string& author,
5057  const std::string& comment,
5058  bool sourceTableAsIs,
5059  bool lookForEquivalent) try
5060 {
5061  //__SUP_COUT__ << "handleCreateTableXML: " << tableName << " version: " <<
5062  // version
5063  // << " dataOffset: " << dataOffset << __E__;
5064 
5065  //__SUP_COUT__ << "data: " << data << __E__;
5066 
5067  // create temporary version from starting version
5068  if(!version.isInvalid()) // if not using mock-up, make sure starting version is
5069  // loaded
5070  {
5071  try
5072  {
5073  cfgMgr->getVersionedTableByName(tableName, version);
5074  }
5075  catch(...)
5076  {
5077  // force to mockup
5078  version = TableVersion();
5079  }
5080  }
5081 
5082  TableBase* config = cfgMgr->getTableByName(tableName);
5083 
5084  // check that the source version has the right number of columns
5085  // if there is a mismatch, start from mockup
5086  if(!version.isInvalid()) // if not using mock-up, then the starting version is the
5087  // active one
5088  {
5089  // compare active to mockup column counts
5090  if(config->getViewP()->getDataColumnSize() !=
5091  config->getMockupViewP()->getNumberOfColumns() ||
5092  config->getViewP()->getSourceColumnMismatch() != 0)
5093  {
5094  __SUP_COUT__ << "config->getViewP()->getNumberOfColumns() "
5095  << config->getViewP()->getNumberOfColumns() << __E__;
5096  __SUP_COUT__ << "config->getMockupViewP()->getNumberOfColumns() "
5097  << config->getMockupViewP()->getNumberOfColumns() << __E__;
5098  __SUP_COUT__ << "config->getViewP()->getSourceColumnMismatch() "
5099  << config->getViewP()->getSourceColumnMismatch() << __E__;
5100  __SUP_COUT_INFO__
5101  << "Source view v" << version
5102  << " has a mismatch in the number of columns, so using mockup as source."
5103  << __E__;
5104  version = TableVersion(); // invalid = mockup
5105  }
5106  }
5107 
5108  // create a temporary version from the source version
5109  TableVersion temporaryVersion = config->createTemporaryView(version);
5110 
5111  __SUP_COUT__ << "\t\ttemporaryVersion: " << temporaryVersion << __E__;
5112 
5113  TableView* cfgView = config->getTemporaryView(temporaryVersion);
5114 
5115  int retVal;
5116  try
5117  {
5118  // returns -1 on error that data was unchanged
5119  retVal = sourceTableAsIs ? 0 : cfgView->fillFromCSV(data, dataOffset, author);
5120 
5121  if(retVal == 1) // data was same but columns are different!
5122  {
5123  __SUP_COUT__ << "Data was the same, but columns have changed!" << __E__;
5124  __SUP_COUTV__(sourceTableAsIs);
5125  lookForEquivalent = false; // clear
5126  }
5127 
5128  cfgView->setURIEncodedComment(comment);
5129  __SUP_COUT__ << "Table comment was set to:\n\t" << cfgView->getComment() << __E__;
5130  }
5131  catch(...) // erase temporary view before re-throwing error
5132  {
5133  __SUP_COUT__ << "Caught error while editing. Erasing temporary version." << __E__;
5134  config->eraseView(temporaryVersion);
5135  throw;
5136  }
5137 
5138  // Note: be careful with any further table operations at this point..
5139  // must catch errors and erase temporary version on failure.
5140 
5141  // only consider it an error if source version was persistent version
5142  // allow it if source version is temporary and we are making a persistent version now
5143  // also, allow it if version tracking is off.
5144  if(retVal < 0 && (!version.isTemporaryVersion() || makeTemporary) &&
5145  ConfigurationInterface::isVersionTrackingEnabled())
5146  {
5147  if(!version.isInvalid() && // if source version was mockup, then consider it
5148  // attempt to create a blank table
5149  !version.isScratchVersion()) // if source version was scratch, then consider
5150  // it attempt to make it persistent
5151  {
5152  __SUP_SS__
5153  << "No rows were modified! No reason to fill a view with same content."
5154  << __E__;
5155  __SUP_COUT_ERR__ << "\n" << ss.str();
5156  // delete temporaryVersion
5157  config->eraseView(temporaryVersion);
5158  __SS_THROW__;
5159  }
5160  else if(version.isInvalid())
5161  __SUP_COUT__ << "This was interpreted as an attempt to create a blank table."
5162  << __E__;
5163  else if(version.isScratchVersion())
5164  __SUP_COUT__ << "This was interpreted as an attempt to make a persistent "
5165  "version of the scratch table."
5166  << __E__;
5167  else
5168  {
5169  __SUP_SS__;
5170  __THROW__(ss.str() + "impossible!");
5171  }
5172  }
5173  else if(retVal < 0 && (version.isTemporaryVersion() && !makeTemporary))
5174  {
5175  __SUP_COUT__ << "Allowing the static data because this is converting from "
5176  "temporary to persistent version."
5177  << __E__;
5178  }
5179  else if(retVal < 0 && !ConfigurationInterface::isVersionTrackingEnabled())
5180  {
5181  __SUP_COUT__ << "Allowing the static data because version tracking is OFF."
5182  << __E__;
5183  }
5184  else if(retVal < 0)
5185  {
5186  __SUP_SS__ << "This should not be possible! Fatal error." << __E__;
5187  // delete temporaryVersion
5188  config->eraseView(temporaryVersion);
5189  __SS_THROW__;
5190  }
5191 
5192  // note: if sourceTableAsIs, accept equivalent versions
5193  saveModifiedVersionXML(xmlOut,
5194  cfgMgr,
5195  tableName,
5196  version,
5197  makeTemporary,
5198  config,
5199  temporaryVersion,
5200  false /*ignoreDuplicates*/,
5201  lookForEquivalent || sourceTableAsIs /*lookForEquivalent*/);
5202 } // end handleCreateTableXML()
5203 catch(std::runtime_error& e)
5204 {
5205  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
5206  xmlOut.addTextElementToData("Error",
5207  "Error saving new view!\n " + std::string(e.what()));
5208 }
5209 catch(...)
5210 {
5211  __SUP_COUT__ << "Error detected!\n\n " << __E__;
5212  xmlOut.addTextElementToData("Error", "Error saving new view! ");
5213 }
5214 
5215 //========================================================================================================================
5216 // refreshUserSession
5217 // Finds/creates the active user session based on username& actionSessionIndex
5218 //
5219 // Returns a configurationMangager instance dedictated to the user.
5220 // This configurationManager will have at least empty instances of all base
5221 // configurations (no null pointers) and will load the backbone configurations to
5222 // specified backboneVersion
5223 //
5224 // If backboneVersion is -1, then latest, and backboneVersion passed by reference
5225 // will be updated
5226 ConfigurationManagerRW* ConfigurationGUISupervisor::refreshUserSession(
5227  std::string username, uint64_t activeSessionIndex, bool refresh)
5228 //, TableVersion& backboneVersion)
5229 {
5230  activeSessionIndex =
5231  0; // make session by username for now! (may never want to change back)
5232 
5233  std::stringstream ssMapKey;
5234  ssMapKey << username << ":" << activeSessionIndex;
5235  std::string mapKey = ssMapKey.str();
5236  __SUP_COUT__ << "Config Session: " << mapKey
5237  << " ... out of size: " << userConfigurationManagers_.size() << __E__;
5238 
5239  time_t now = time(0);
5240 
5241  // create new config mgr if not one for active session index
5242  if(userConfigurationManagers_.find(mapKey) == userConfigurationManagers_.end())
5243  {
5244  __SUP_COUT_INFO__ << "Creating new Configuration Manager." << __E__;
5245  userConfigurationManagers_[mapKey] = new ConfigurationManagerRW(username);
5246 
5247  // update table info for each new configuration manager
5248  // IMPORTANTLY this also fills all configuration manager pointers with instances,
5249  // so we are not dealing with changing pointers later on
5250  userConfigurationManagers_[mapKey]->getAllTableInfo(
5251  true); // load empty instance of everything important
5252  }
5253  else if(userLastUseTime_.find(mapKey) == userLastUseTime_.end())
5254  {
5255  __SUP_SS__ << "Fatal error managing userLastUseTime_!" << __E__;
5256  __SUP_COUT_ERR__ << "\n" << ss.str();
5257  __SS_THROW__;
5258  }
5259  else if(refresh || (now - userLastUseTime_[mapKey]) >
5260  CONFIGURATION_MANAGER_REFRESH_THRESHOLD) // check if should
5261  // refresh all config
5262  // info
5263  {
5264  __SUP_COUT_INFO__ << "Refreshing all table info." << __E__;
5265  userConfigurationManagers_[mapKey]->getAllTableInfo(true);
5266  }
5267 
5268  // load backbone configurations always based on backboneVersion
5269  // if backboneVersion is -1, then latest
5270  // backboneVersion = 0;//
5271  // userConfigurationManagers_[mapKey]->loadConfigurationBackbone(backboneVersion);
5272 
5273  // update active sessionIndex last use time
5274  userLastUseTime_[mapKey] = now;
5275 
5276  // check for stale sessions and remove them (so config user maps do not grow forever)
5277  for(std::map<std::string, time_t>::iterator it = userLastUseTime_.begin();
5278  it != userLastUseTime_.end();
5279  ++it)
5280  if(now - it->second > CONFIGURATION_MANAGER_EXPIRATION_TIME) // expired!
5281  {
5282  __SUP_COUT__ << now << ":" << it->second << " = " << now - it->second
5283  << __E__;
5284  delete userConfigurationManagers_[it->first]; // call destructor
5285  if(!(userConfigurationManagers_.erase(it->first))) // erase by key
5286  {
5287  __SUP_SS__ << "Fatal error erasing configuration manager by key!"
5288  << __E__;
5289  __SUP_COUT_ERR__ << "\n" << ss.str();
5290  __SS_THROW__;
5291  }
5292  userLastUseTime_.erase(it); // erase by iterator
5293 
5294  it = userLastUseTime_.begin(); // fail safe.. reset it, to avoid trying to
5295  // understand what happens with the next
5296  // iterator
5297  }
5298 
5299  return userConfigurationManagers_[mapKey];
5300 }
5301 
5302 //========================================================================================================================
5303 // handleCreateTableGroupXML
5304 //
5305 // Save a new TableGroup:
5306 // Search for existing TableGroupKeys for this TableGroup
5307 // Append a "bumped" system key to name
5308 // Save based on list of tableName/TableVersion
5309 //
5310 // tableList parameter is comma separated table name and version
5311 //
5312 // Note: if version of -1 (INVALID/MOCKUP) is given and there are no other existing
5313 // table versions... a new table version is generated using the mockup table.
5314 //
5315 // Table Version Alias Handling:
5316 // Allow table versions to be specified as an alias with ALIAS: preamble. Aliased
5317 // versions will be translated according to the active backbone at activation
5318 // time.
5319 //
5320 //
5321 void ConfigurationGUISupervisor::handleCreateTableGroupXML(
5322  HttpXmlDocument& xmlOut,
5323  ConfigurationManagerRW* cfgMgr,
5324  const std::string& groupName,
5325  const std::string& tableList,
5326  bool allowDuplicates,
5327  bool ignoreWarnings,
5328  const std::string& groupComment,
5329  bool lookForEquivalent) try
5330 {
5331  __SUP_COUT__ << "handleCreateTableGroupXML \n";
5332 
5333  xmlOut.addTextElementToData("AttemptedNewGroupName", groupName);
5334 
5335  // make sure not using partial tables or anything weird when creating the group
5336  // so start from scratch and load backbone
5337  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo(true);
5338  cfgMgr->loadConfigurationBackbone();
5339 
5340  std::map<std::string /*tableName*/,
5341  std::map<std::string /*aliasName*/, TableVersion /*version*/>>
5342  versionAliases = cfgMgr->getVersionAliases();
5343  for(const auto& aliases : versionAliases)
5344  for(const auto& alias : aliases.second)
5345  __SUP_COUT__ << aliases.first << " " << alias.first << " " << alias.second
5346  << __E__;
5347 
5348  std::map<std::string /*name*/, TableVersion /*version*/> groupMembers;
5349  std::map<std::string /*name*/, std::string /*alias*/> groupAliases;
5350 
5351  std::string name, versionStr, alias;
5352  TableVersion version;
5353  auto c = tableList.find(',', 0);
5354  auto i = c;
5355  i = 0; // auto used to get proper index/length type
5356  while(c < tableList.length())
5357  {
5358  // add the table and version pair to the map
5359  name = tableList.substr(i, c - i);
5360  i = c + 1;
5361  c = tableList.find(',', i);
5362  if(c == std::string::npos) // missing version list entry?!
5363  {
5364  __SUP_SS__ << "Incomplete Table Name-Version pair!" << __E__;
5365  __SUP_COUT_ERR__ << "\n" << ss.str();
5366  xmlOut.addTextElementToData("Error", ss.str());
5367  return;
5368  }
5369 
5370  versionStr = tableList.substr(i, c - i);
5371  i = c + 1;
5372  c = tableList.find(',', i);
5373 
5374  //__SUP_COUT__ << "name: " << name << __E__;
5375  //__SUP_COUT__ << "versionStr: " << versionStr << __E__;
5376 
5377  // check if version is an alias and convert
5378  if(versionStr.find(ConfigurationManager::ALIAS_VERSION_PREAMBLE) == 0)
5379  {
5380  alias =
5381  versionStr.substr(ConfigurationManager::ALIAS_VERSION_PREAMBLE.size());
5382 
5383  __SUP_COUT__ << "Found alias " << name << " " << versionStr << __E__;
5384 
5385  // convert alias to version
5386  if(versionAliases.find(name) != versionAliases.end() &&
5387  versionAliases[name].find(alias) != versionAliases[name].end())
5388  {
5389  version = versionAliases[name][alias];
5390  __SUP_COUT__ << "version alias '" << alias
5391  << "'translated to: " << version << __E__;
5392 
5393  groupAliases[name] = alias;
5394  }
5395  else
5396  {
5397  __SUP_SS__ << "version alias '"
5398  << versionStr.substr(
5399  ConfigurationManager::ALIAS_VERSION_PREAMBLE.size())
5400  << "' was not found in active version aliases!" << __E__;
5401  __SUP_COUT_ERR__ << "\n" << ss.str();
5402  xmlOut.addTextElementToData("Error", ss.str());
5403  return;
5404  }
5405  }
5406  else
5407  version = TableVersion(versionStr);
5408 
5409  if(version.isTemporaryVersion())
5410  {
5411  __SUP_SS__ << "Groups can not be created using temporary member tables. "
5412  << "Table member '" << name << "' with temporary version '"
5413  << version << "' is illegal." << __E__;
5414  xmlOut.addTextElementToData("Error", ss.str());
5415  return;
5416  }
5417 
5418  // enforce that table exists
5419  if(allTableInfo.find(name) == allTableInfo.end())
5420  {
5421  __SUP_SS__ << "Groups can not be created using mock-up member tables of "
5422  "undefined tables. "
5423  << "Table member '" << name << "' is not defined." << __E__;
5424  xmlOut.addTextElementToData("Error", ss.str());
5425  return;
5426  }
5427 
5428  if(version.isMockupVersion())
5429  {
5430  // if mockup, then generate a new persistent version to use based on mockup
5431  TableBase* config = cfgMgr->getTableByName(name);
5432  // create a temporary version from the mockup as source version
5433  TableVersion temporaryVersion = config->createTemporaryView();
5434  __SUP_COUT__ << "\t\ttemporaryVersion: " << temporaryVersion << __E__;
5435 
5436  // if other versions exist check for another mockup, and use that instead
5437  __SUP_COUT__ << "Creating version from mock-up for name: " << name
5438  << " inputVersionStr: " << versionStr << __E__;
5439 
5440  // set table comment
5441  config->getTemporaryView(temporaryVersion)
5442  ->setComment("Auto-generated from mock-up.");
5443 
5444  // finish off the version creation
5445  version =
5446  saveModifiedVersionXML(xmlOut,
5447  cfgMgr,
5448  name,
5449  TableVersion() /*original source is mockup*/,
5450  false /* makeTemporary */,
5451  config,
5452  temporaryVersion /*temporary modified version*/,
5453  false /*ignore duplicates*/,
5454  true /*look for equivalent*/);
5455 
5456  __SUP_COUT__ << "Using mockup version: " << version << __E__;
5457  }
5458 
5459  //__SUP_COUT__ << "version: " << version << __E__;
5460  groupMembers[name] = version;
5461  } // end member verification loop
5462 
5463  __COUTV__(StringMacros::mapToString(groupAliases));
5464 
5465  if(!allowDuplicates)
5466  {
5467  __SUP_COUT__ << "Checking for duplicate groups..." << __E__;
5468  TableGroupKey foundKey =
5469  cfgMgr->findTableGroup(groupName, groupMembers, groupAliases);
5470 
5471  if(!foundKey.isInvalid())
5472  {
5473  // return found equivalent key
5474  xmlOut.addTextElementToData("TableGroupName", groupName);
5475  xmlOut.addTextElementToData("TableGroupKey", foundKey.toString());
5476 
5477  if(lookForEquivalent)
5478  {
5479  __SUP_COUT__ << "Found equivalent group key (" << foundKey << ") for "
5480  << groupName << "." << __E__;
5481  // allow this equivalent group to be the response without an error
5482  xmlOut.addTextElementToData("foundEquivalentKey", "1"); // indicator
5483 
5484  // insert get table info
5485  handleGetTableGroupXML(xmlOut, cfgMgr, groupName, foundKey);
5486  return;
5487  }
5488  else // treat as error, if not looking for equivalent
5489  {
5490  __SUP_COUT__ << "Treating duplicate group as error." << __E__;
5491  __SUP_SS__ << ("Failed to create table group: " + groupName +
5492  ". It is a duplicate of an existing group key (" +
5493  foundKey.toString() + ")");
5494  __SUP_COUT_ERR__ << ss.str() << __E__;
5495  xmlOut.addTextElementToData("Error", ss.str());
5496  return;
5497  }
5498  }
5499 
5500  __SUP_COUT__ << "Check for duplicate groups complete." << __E__;
5501  }
5502 
5503  // check the group for errors before creating group
5504  try
5505  {
5506  cfgMgr->loadMemberMap(groupMembers);
5507 
5508  std::string accumulateErrors = "";
5509  for(auto& groupMemberPair : groupMembers)
5510  {
5511  TableView* cfgViewPtr =
5512  cfgMgr->getTableByName(groupMemberPair.first)->getViewP();
5513  if(cfgViewPtr->getDataColumnSize() != cfgViewPtr->getNumberOfColumns() ||
5514  cfgViewPtr->getSourceColumnMismatch() !=
5515  0) // check for column size mismatch
5516  {
5517  __SUP_SS__ << "\n\nThere were errors found in loading a member table "
5518  << groupMemberPair.first << ":v" << cfgViewPtr->getVersion()
5519  << ". Please see the details below:\n\n"
5520  << "The source column size was found to be "
5521  << cfgViewPtr->getDataColumnSize()
5522  << ", and the current number of columns for this table is "
5523  << cfgViewPtr->getNumberOfColumns()
5524  << ". This resulted in a count of "
5525  << cfgViewPtr->getSourceColumnMismatch()
5526  << " source column mismatches, and a count of "
5527  << cfgViewPtr->getSourceColumnMissing()
5528  << " table entries missing in "
5529  << cfgViewPtr->getNumberOfRows() << " row(s) of data."
5530  << __E__;
5531 
5532  const std::set<std::string> srcColNames =
5533  cfgViewPtr->getSourceColumnNames();
5534  ss << "\n\nSource column names were as follows:\n";
5535  char index = 'a';
5536  for(auto& srcColName : srcColNames)
5537  ss << "\n\t" << index++ << ". " << srcColName;
5538  ss << __E__;
5539 
5540  std::set<std::string> destColNames = cfgViewPtr->getColumnStorageNames();
5541  ss << "\n\nCurrent table column names are as follows:\n";
5542  index = 'a';
5543  for(auto& destColName : destColNames)
5544  ss << "\n\t" << index++ << ". " << destColName;
5545  ss << __E__;
5546 
5547  __SUP_COUT_ERR__ << "\n" << ss.str();
5548  xmlOut.addTextElementToData("Error", ss.str());
5549  return;
5550  }
5551  }
5552  }
5553  catch(std::runtime_error& e)
5554  {
5555  __SUP_SS__ << "Failed to create config group: " << groupName
5556  << ".\nThere were problems loading the chosen members:\n\n"
5557  << e.what() << __E__;
5558  __SUP_COUT_ERR__ << "\n" << ss.str();
5559  xmlOut.addTextElementToData("Error", ss.str());
5560  return;
5561  }
5562  catch(...)
5563  {
5564  __SUP_SS__ << "Failed to create config group: " << groupName << __E__;
5565  __SUP_COUT_ERR__ << "\n" << ss.str();
5566  xmlOut.addTextElementToData("Error", ss.str());
5567  return;
5568  }
5569 
5570  // check the tree for warnings before creating group
5571  std::string accumulateTreeErrs;
5572  cfgMgr->getChildren(&groupMembers, &accumulateTreeErrs);
5573  if(accumulateTreeErrs != "")
5574  {
5575  __SUP_COUT_WARN__ << "\n" << accumulateTreeErrs << __E__;
5576  if(!ignoreWarnings)
5577  {
5578  xmlOut.addTextElementToData("TreeErrors", accumulateTreeErrs);
5579  return;
5580  }
5581  }
5582 
5583  TableGroupKey newKey;
5584  try
5585  {
5586  __COUT__ << "Saving new group..." << __E__;
5587  newKey = cfgMgr->saveNewTableGroup(
5588  groupName, groupMembers, groupComment, &groupAliases);
5589  }
5590  catch(std::runtime_error& e)
5591  {
5592  __SUP_COUT_ERR__ << "Failed to create config group: " << groupName << __E__;
5593  __SUP_COUT_ERR__ << "\n\n" << e.what() << __E__;
5594  xmlOut.addTextElementToData(
5595  "Error", "Failed to create table group: " + groupName + ".\n\n" + e.what());
5596  return;
5597  }
5598  catch(...)
5599  {
5600  __SUP_COUT_ERR__ << "Failed to create table group: " << groupName << __E__;
5601  xmlOut.addTextElementToData("Error",
5602  "Failed to create table group: " + groupName);
5603  return;
5604  }
5605 
5606  // insert get table info
5607  __COUT__ << "Loading new table group..." << __E__;
5608  handleGetTableGroupXML(xmlOut, cfgMgr, groupName, newKey);
5609 }
5610 catch(std::runtime_error& e)
5611 {
5612  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
5613  xmlOut.addTextElementToData("Error",
5614  "Error saving table group! " + std::string(e.what()));
5615 }
5616 catch(...)
5617 {
5618  __SUP_COUT__ << "Unknown Error detected!\n\n " << __E__;
5619  xmlOut.addTextElementToData("Error", "Error saving table group! ");
5620 }
5621 
5622 //========================================================================================================================
5623 // handleDeleteTableInfoXML
5624 //
5625 // return nothing except Error in xmlOut
5626 //
5627 void ConfigurationGUISupervisor::handleDeleteTableInfoXML(HttpXmlDocument& xmlOut,
5628  ConfigurationManagerRW* cfgMgr,
5629  std::string& tableName)
5630 {
5631  if(0 == rename((TABLE_INFO_PATH + tableName + TABLE_INFO_EXT).c_str(),
5632  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused").c_str()))
5633  __SUP_COUT_INFO__ << ("Table Info File successfully renamed: " +
5634  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused"))
5635  << __E__;
5636  else
5637  {
5638  __SUP_COUT_ERR__ << ("Error renaming file to " +
5639  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused"))
5640  << __E__;
5641 
5642  xmlOut.addTextElementToData(
5643  "Error",
5644  ("Error renaming Table Info File to " +
5645  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused")));
5646  return;
5647  }
5648 
5649  // reload all with refresh to remove new table
5650  cfgMgr->getAllTableInfo(true);
5651 } // end handleDeleteTableInfoXML()
5652 
5653 //========================================================================================================================
5654 // handleSaveTableInfoXML
5655 //
5656 // write new info file for tableName based CSV column info
5657 // data="type,name,dataType;type,name,dataType;..."
5658 // return resulting handleGetTableXML mock-up view
5659 //
5660 void ConfigurationGUISupervisor::handleSaveTableInfoXML(
5661  HttpXmlDocument& xmlOut,
5662  ConfigurationManagerRW* cfgMgr,
5663  std::string& tableName,
5664  const std::string& data,
5665  const std::string& tableDescription,
5666  const std::string& columnChoicesCSV,
5667  bool allowOverwrite)
5668 {
5669  // create all caps name and validate
5670  // only allow alpha-numeric names with "Table" at end
5671  std::string capsName;
5672  try
5673  {
5674  capsName = TableBase::convertToCaps(tableName, true);
5675  }
5676  catch(std::runtime_error& e)
5677  { // error! non-alpha
5678  xmlOut.addTextElementToData("Error", e.what());
5679  return;
5680  }
5681 
5682  if(!allowOverwrite)
5683  {
5684  FILE* fp = fopen((TABLE_INFO_PATH + tableName + TABLE_INFO_EXT).c_str(), "r");
5685  if(fp)
5686  {
5687  fclose(fp);
5688  xmlOut.addTextElementToData("TableName", tableName);
5689  xmlOut.addTextElementToData("OverwriteError", "1");
5690  xmlOut.addTextElementToData(
5691  "Error",
5692  "File already exists! ('" +
5693  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT) + "')");
5694  return;
5695  }
5696  }
5697 
5698  __SUP_COUT__ << "capsName=" << capsName << __E__;
5699  __SUP_COUT__ << "tableName=" << tableName << __E__;
5700  __SUP_COUT__ << "tableDescription=" << tableDescription << __E__;
5701  __SUP_COUT__ << "columnChoicesCSV=" << columnChoicesCSV << __E__;
5702 
5703  // create preview string to validate column info before write to file
5704  std::stringstream outss;
5705 
5706  outss << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n";
5707  outss << "\t<ROOT xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
5708  "xsi:noNamespaceSchemaLocation=\"TableInfo.xsd\">\n";
5709  outss << "\t\t<TABLE Name=\"" << tableName << "\">\n";
5710  outss << "\t\t\t<VIEW Name=\"" << capsName
5711  << "\" Type=\"File,Database,DatabaseTest\" Description=\"" << tableDescription
5712  << "\">\n";
5713 
5714  // each column is represented by 3 fields
5715  // - type, name, dataType
5716  int i = 0; // use to parse data std::string
5717  int j = data.find(',', i); // find next field delimiter
5718  int k = data.find(';', i); // find next col delimiter
5719 
5720  std::istringstream columnChoicesISS(columnChoicesCSV);
5721  std::string columnChoicesString;
5722  std::string columnType;
5723 
5724  while(k != (int)(std::string::npos))
5725  {
5726  // type
5727  columnType = data.substr(i, j - i);
5728  outss << "\t\t\t\t<COLUMN Type=\"";
5729  outss << columnType;
5730 
5731  i = j + 1;
5732  j = data.find(',', i); // find next field delimiter
5733 
5734  // name and storage name
5735  outss << "\" \t Name=\"";
5736  capsName = data.substr(i, j - i); // not caps yet
5737  outss << capsName;
5738  outss << "\" \t StorageName=\"";
5739 
5740  try
5741  {
5742  outss << TableBase::convertToCaps(capsName); // now caps
5743  }
5744  catch(std::runtime_error& e)
5745  { // error! non-alpha
5746  xmlOut.addTextElementToData("Error",
5747  std::string("For column name '") +
5748  data.substr(i, j - i) + "' - " + e.what());
5749  return;
5750  }
5751 
5752  i = j + 1;
5753  j = data.find(',', i); // find next field delimiter
5754 
5755  // data type
5756  outss << "\" \t DataType=\"";
5757  outss << data.substr(i, k - i);
5758 
5759  // fixed data choices for TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA
5760  getline(columnChoicesISS, columnChoicesString, ';');
5761  //__SUP_COUT__ << "columnChoicesString = " << columnChoicesString << __E__;
5762  outss << "\" \t DataChoices=\"";
5763  outss << columnChoicesString;
5764 
5765  // end column info
5766  outss << "\"/>\n";
5767 
5768  i = k + 1;
5769  j = data.find(',', i); // find next field delimiter
5770  k = data.find(';', i); // find new col delimiter
5771  }
5772 
5773  outss << "\t\t\t</VIEW>\n";
5774  outss << "\t\t</TABLE>\n";
5775  outss << "\t</ROOT>\n";
5776 
5777  __SUP_COUT__ << outss.str() << __E__;
5778 
5779  FILE* fp = fopen((TABLE_INFO_PATH + tableName + TABLE_INFO_EXT).c_str(), "w");
5780  if(!fp)
5781  {
5782  xmlOut.addTextElementToData("Error",
5783  "Failed to open destination Table Info file:" +
5784  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT));
5785  return;
5786  }
5787 
5788  fprintf(fp, "%s", outss.str().c_str());
5789  fclose(fp);
5790 
5791  // reload all config info with refresh AND reset to pick up possibly new config
5792  // check for errors related to this tableName
5793  std::string accumulatedErrors = "";
5794  cfgMgr->getAllTableInfo(true, &accumulatedErrors, tableName);
5795 
5796  // if errors associated with this config name stop and report
5797  if(accumulatedErrors != "")
5798  {
5799  __SUP_SS__ << ("The new version of the '" + tableName +
5800  "' table column info was saved, however errors were detected "
5801  "reading back the table '" +
5802  tableName + "' after the save attempt:\n\n" + accumulatedErrors)
5803  << __E__;
5804 
5805  __SUP_COUT_ERR__ << ss.str() << __E__;
5806  xmlOut.addTextElementToData("Error", ss.str());
5807 
5808  // if error detected reading back then move the saved table info to
5809  // .unused
5810  // // This was disabled by RAR on 11/4/2016.. (just keep broken info files)
5811  // // ... especially since now there is a Delete button
5812  if(0)
5813  {
5814  // table info is illegal so report error, and disable file
5815 
5816  // if error detected //move file to ".unused"
5817  if(0 ==
5818  rename((TABLE_INFO_PATH + tableName + TABLE_INFO_EXT).c_str(),
5819  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused").c_str()))
5820  __SUP_COUT_INFO__
5821  << ("File successfully renamed: " +
5822  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused"))
5823  << __E__;
5824  else
5825 
5826  __SUP_COUT_ERR__
5827  << ("Error renaming file to " +
5828  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused"))
5829  << __E__;
5830 
5831  // reload all with refresh to remove new table
5832  cfgMgr->getAllTableInfo(true);
5833  }
5834  return;
5835  }
5836 
5837  // return the new table info
5838  handleGetTableXML(xmlOut, cfgMgr, tableName, TableVersion());
5839 
5840  // debug all table column info
5841  // FIXME -- possibly remove this debug feature in future
5842  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
5843 
5844  // give a print out of currently illegal table column info
5845  __SUP_COUT_INFO__ << "Looking for errors in all table column info..." << __E__;
5846  for(const auto& cfgInfo : allTableInfo)
5847  {
5848  try
5849  {
5850  cfgMgr->getTableByName(cfgInfo.first)->getMockupViewP()->init();
5851  }
5852  catch(std::runtime_error& e)
5853  {
5854  __SUP_COUT_WARN__ << "\n\n##############################################\n"
5855  << "Error identified in column info of table '"
5856  << cfgInfo.first << "':\n\n"
5857  << e.what() << "\n\n"
5858  << __E__;
5859  }
5860  }
5861 } // end handleSaveTableInfoXML()
5862 
5863 //========================================================================================================================
5864 // handleSetGroupAliasInBackboneXML
5865 // open current backbone
5866 // modify GroupAliases
5867 // save as new version of groupAliases
5868 // return new version of groupAliases
5869 //
5870 // Note: very similar to ConfigurationGUISupervisor::handleSetVersionAliasInBackboneXML
5871 void ConfigurationGUISupervisor::handleSetGroupAliasInBackboneXML(
5872  HttpXmlDocument& xmlOut,
5873  ConfigurationManagerRW* cfgMgr,
5874  const std::string& groupAlias,
5875  const std::string& groupName,
5876  TableGroupKey groupKey,
5877  const std::string& author) try
5878 {
5879  cfgMgr->loadConfigurationBackbone();
5880  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
5881 
5882  const std::string groupAliasesTableName =
5883  ConfigurationManager::GROUP_ALIASES_TABLE_NAME;
5884  if(activeVersions.find(groupAliasesTableName) == activeVersions.end())
5885  {
5886  __SUP_SS__ << "Active version of " << groupAliasesTableName << " missing!"
5887  << __E__;
5888  xmlOut.addTextElementToData("Error", ss.str());
5889  return;
5890  }
5891 
5892  // put all old backbone versions in xmlOut
5893  const std::set<std::string> backboneMembers = cfgMgr->getBackboneMemberNames();
5894  for(auto& memberName : backboneMembers)
5895  {
5896  __SUP_COUT__ << "activeVersions[\"" << memberName
5897  << "\"]=" << activeVersions[memberName] << __E__;
5898 
5899  xmlOut.addTextElementToData("oldBackboneName", memberName);
5900  xmlOut.addTextElementToData("oldBackboneVersion",
5901  activeVersions[memberName].toString());
5902  }
5903 
5904  // make a temporary version from active view
5905  // modify the chosen groupAlias row
5906  // save as new version
5907 
5908  TableBase* config = cfgMgr->getTableByName(groupAliasesTableName);
5909  TableVersion originalVersion = activeVersions[groupAliasesTableName];
5910  TableVersion temporaryVersion = config->createTemporaryView(originalVersion);
5911 
5912  __SUP_COUT__ << "\t\t temporaryVersion: " << temporaryVersion << __E__;
5913  bool isDifferent = false;
5914 
5915  try
5916  {
5917  TableView* configView = config->getTemporaryView(temporaryVersion);
5918 
5919  unsigned int col = configView->findCol("GroupKeyAlias");
5920 
5921  // only make a new version if we are changing compared to active backbone
5922 
5923  unsigned int row = -1;
5924  // find groupAlias row
5925  try
5926  {
5927  row = configView->findRow(col, groupAlias);
5928  }
5929  catch(...)
5930  {
5931  }
5932  if(row == (unsigned int)-1) // if row not found then add a row
5933  {
5934  isDifferent = true;
5935  row = configView->addRow();
5936 
5937  // set all columns in new row
5938  col = configView->findCol("CommentDescription");
5939  configView->setValue(
5940  "This Group Alias was automatically setup by the server.", row, col);
5941  col = configView->findCol("GroupKeyAlias");
5942  configView->setValue(groupAlias, row, col);
5943  }
5944 
5945  __SUP_COUT__ << "\t\t row: " << row << __E__;
5946 
5947  col = configView->findCol("GroupName");
5948 
5949  __SUP_COUT__ << "\t\t groupName: " << groupName << " vs "
5950  << configView->getDataView()[row][col] << __E__;
5951  if(groupName != configView->getDataView()[row][col])
5952  {
5953  configView->setValue(groupName, row, col);
5954  isDifferent = true;
5955  }
5956 
5957  col = configView->findCol("GroupKey");
5958  __SUP_COUT__ << "\t\t groupKey: " << groupKey << " vs "
5959  << configView->getDataView()[row][col] << __E__;
5960  if(groupKey.toString() != configView->getDataView()[row][col])
5961  {
5962  configView->setValue(groupKey.toString(), row, col);
5963  isDifferent = true;
5964  }
5965 
5966  if(isDifferent) // set author/time of new version if different
5967  {
5968  configView->setValue(author, row, configView->findCol("Author"));
5969  configView->setValue(
5970  time(0), row, configView->findCol("RecordInsertionTime"));
5971  }
5972  }
5973  catch(...)
5974  {
5975  __SUP_COUT_ERR__ << "Error editing Group Alias view!" << __E__;
5976 
5977  // delete temporaryVersion
5978  config->eraseView(temporaryVersion);
5979  throw;
5980  }
5981 
5982  TableVersion newAssignedVersion;
5983  if(isDifferent) // make new version if different
5984  {
5985  __SUP_COUT__ << "\t\t**************************** Save as new table version"
5986  << __E__;
5987 
5988  // newAssignedVersion =
5989  // cfgMgr->saveNewTable(groupAliasesTableName,temporaryVersion);
5990 
5991  // save or find equivalent
5992 
5993  newAssignedVersion = saveModifiedVersionXML(xmlOut,
5994  cfgMgr,
5995  config->getTableName(),
5996  originalVersion,
5997  false /*makeTemporary*/,
5998  config,
5999  temporaryVersion,
6000  false /*ignoreDuplicates*/,
6001  true /*lookForEquivalent*/);
6002  }
6003  else // use existing version
6004  {
6005  __SUP_COUT__
6006  << "\t\t**************************** Using the existing table version"
6007  << __E__;
6008 
6009  // delete temporaryVersion
6010  config->eraseView(temporaryVersion);
6011  newAssignedVersion = activeVersions[groupAliasesTableName];
6012 
6013  xmlOut.addTextElementToData("savedName", groupAliasesTableName);
6014  xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString());
6015  }
6016 
6017  __SUP_COUT__ << "\t\t newAssignedVersion: " << newAssignedVersion << __E__;
6018 }
6019 catch(std::runtime_error& e)
6020 {
6021  __SUP_COUT_ERR__ << "Error detected!\n\n " << e.what() << __E__;
6022  xmlOut.addTextElementToData(
6023  "Error", "Error saving new Group Alias view!\n " + std::string(e.what()));
6024 }
6025 catch(...)
6026 {
6027  __SUP_COUT_ERR__ << "Error detected!\n\n " << __E__;
6028  xmlOut.addTextElementToData("Error", "Error saving new Group Alias view! ");
6029 }
6030 
6031 //========================================================================================================================
6032 // handleSetVersionAliasInBackboneXML
6033 // open current backbone
6034 // modify VersionAliases
6035 // save as new version of VersionAliases
6036 // return new version of VersionAliases
6037 //
6038 // Note: very similar to ConfigurationGUISupervisor::handleSetGroupAliasInBackboneXML
6039 void ConfigurationGUISupervisor::handleSetVersionAliasInBackboneXML(
6040  HttpXmlDocument& xmlOut,
6041  ConfigurationManagerRW* cfgMgr,
6042  const std::string& versionAlias,
6043  const std::string& tableName,
6044  TableVersion version,
6045  const std::string& author) try
6046 {
6047  cfgMgr->loadConfigurationBackbone();
6048  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
6049 
6050  const std::string versionAliasesTableName =
6051  ConfigurationManager::VERSION_ALIASES_TABLE_NAME;
6052  if(activeVersions.find(versionAliasesTableName) == activeVersions.end())
6053  {
6054  __SUP_SS__ << "Active version of " << versionAliasesTableName << " missing!"
6055  << __E__;
6056  xmlOut.addTextElementToData("Error", ss.str());
6057  return;
6058  }
6059 
6060  // put all old backbone versions in xmlOut
6061  const std::set<std::string> backboneMembers = cfgMgr->getBackboneMemberNames();
6062  for(auto& memberName : backboneMembers)
6063  {
6064  __SUP_COUT__ << "activeVersions[\"" << memberName
6065  << "\"]=" << activeVersions[memberName] << __E__;
6066 
6067  xmlOut.addTextElementToData("oldBackboneName", memberName);
6068  xmlOut.addTextElementToData("oldBackboneVersion",
6069  activeVersions[memberName].toString());
6070  }
6071 
6072  // make a temporary version from active view
6073  // modify the chosen versionAlias row
6074  // save as new version
6075 
6076  TableBase* config = cfgMgr->getTableByName(versionAliasesTableName);
6077  TableVersion originalVersion = activeVersions[versionAliasesTableName];
6078  TableVersion temporaryVersion = config->createTemporaryView(originalVersion);
6079 
6080  __SUP_COUT__ << "\t\t temporaryVersion: " << temporaryVersion << __E__;
6081 
6082  bool isDifferent = false;
6083 
6084  try
6085  {
6086  TableView* configView = config->getTemporaryView(temporaryVersion);
6087 
6088  unsigned int col;
6089  unsigned int col2 = configView->findCol("VersionAlias");
6090  unsigned int col3 = configView->findCol("TableName");
6091 
6092  // only make a new version if we are changing compared to active backbone
6093 
6094  unsigned int row = -1;
6095  // find tableName, versionAlias pair
6096  // NOTE: only accept the first pair, repeats are ignored.
6097  try
6098  {
6099  unsigned int tmpRow = -1;
6100  do
6101  { // start looking from beyond last find
6102  tmpRow = configView->findRow(col3, tableName, tmpRow + 1);
6103  } while(configView->getDataView()[tmpRow][col2] != versionAlias);
6104  // at this point the first pair was found! (else exception was thrown)
6105  row = tmpRow;
6106  }
6107  catch(...)
6108  {
6109  }
6110  if(row == (unsigned int)-1) // if row not found then add a row
6111  {
6112  isDifferent = true;
6113  row = configView->addRow();
6114 
6115  // set all columns in new row
6116  col = configView->findCol("CommentDescription");
6117  configView->setValue(
6118  std::string("Entry was added by server in ") +
6119  "ConfigurationGUISupervisor::setVersionAliasInActiveBackbone().",
6120  row,
6121  col);
6122 
6123  col = configView->findCol("VersionAliasUID");
6124  configView->setValue(
6125  tableName.substr(0, tableName.rfind("Table")) + versionAlias, row, col);
6126 
6127  configView->setValue(versionAlias, row, col2);
6128  configView->setValue(tableName, row, col3);
6129  }
6130 
6131  __SUP_COUT__ << "\t\t row: " << row << __E__;
6132 
6133  col = configView->findCol("Version");
6134  __SUP_COUT__ << "\t\t version: " << version << " vs "
6135  << configView->getDataView()[row][col] << __E__;
6136  if(version.toString() != configView->getDataView()[row][col])
6137  {
6138  configView->setValue(version.toString(), row, col);
6139  isDifferent = true;
6140  }
6141 
6142  if(isDifferent) // set author/time of new version if different
6143  {
6144  configView->setValue(author, row, configView->findCol("Author"));
6145  configView->setValue(
6146  time(0), row, configView->findCol("RecordInsertionTime"));
6147  }
6148  }
6149  catch(...)
6150  {
6151  __SUP_COUT_ERR__ << "Error editing Version Alias view!" << __E__;
6152 
6153  // delete temporaryVersion
6154  config->eraseView(temporaryVersion);
6155  throw;
6156  }
6157 
6158  TableVersion newAssignedVersion;
6159  if(isDifferent) // make new version if different
6160  {
6161  __SUP_COUT__ << "\t\t**************************** Save as new table version"
6162  << __E__;
6163 
6164  // newAssignedVersion =
6165  // cfgMgr->saveNewTable(versionAliasesTableName,temporaryVersion);
6166 
6167  newAssignedVersion = saveModifiedVersionXML(xmlOut,
6168  cfgMgr,
6169  config->getTableName(),
6170  originalVersion,
6171  false /*makeTemporary*/,
6172  config,
6173  temporaryVersion,
6174  false /*ignoreDuplicates*/,
6175  true /*lookForEquivalent*/);
6176  }
6177  else // use existing version
6178  {
6179  __SUP_COUT__ << "\t\t**************************** Using existing table version"
6180  << __E__;
6181 
6182  // delete temporaryVersion
6183  config->eraseView(temporaryVersion);
6184  newAssignedVersion = activeVersions[versionAliasesTableName];
6185 
6186  xmlOut.addTextElementToData("savedAlias", versionAliasesTableName);
6187  xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString());
6188  }
6189 
6190  __SUP_COUT__ << "\t\t newAssignedVersion: " << newAssignedVersion << __E__;
6191 }
6192 catch(std::runtime_error& e)
6193 {
6194  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
6195  xmlOut.addTextElementToData(
6196  "Error", "Error saving new Version Alias view!\n " + std::string(e.what()));
6197 }
6198 catch(...)
6199 {
6200  __SUP_COUT__ << "Error detected!\n\n " << __E__;
6201  xmlOut.addTextElementToData("Error", "Error saving new Version Alias view! ");
6202 }
6203 
6204 //========================================================================================================================
6205 // handleAliasGroupMembersInBackboneXML
6206 // open current backbone
6207 // modify VersionAliases
6208 // save as new version of VersionAliases
6209 // return new version of VersionAliases
6210 void ConfigurationGUISupervisor::handleAliasGroupMembersInBackboneXML(
6211  HttpXmlDocument& xmlOut,
6212  ConfigurationManagerRW* cfgMgr,
6213  const std::string& versionAlias,
6214  const std::string& groupName,
6215  TableGroupKey groupKey,
6216  const std::string& author) try
6217 {
6218  cfgMgr->loadConfigurationBackbone();
6219  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
6220 
6221  const std::string versionAliasesTableName =
6222  ConfigurationManager::VERSION_ALIASES_TABLE_NAME;
6223  if(activeVersions.find(versionAliasesTableName) == activeVersions.end())
6224  {
6225  __SUP_SS__ << "Active version of " << versionAliasesTableName << " missing!"
6226  << __E__;
6227  xmlOut.addTextElementToData("Error", ss.str());
6228  return;
6229  }
6230 
6231  // put all old backbone versions in xmlOut
6232  const std::set<std::string> backboneMembers = cfgMgr->getBackboneMemberNames();
6233  for(auto& memberName : backboneMembers)
6234  {
6235  __SUP_COUT__ << "activeVersions[\"" << memberName
6236  << "\"]=" << activeVersions[memberName] << __E__;
6237 
6238  xmlOut.addTextElementToData("oldBackboneName", memberName);
6239  xmlOut.addTextElementToData("oldBackboneVersion",
6240  activeVersions[memberName].toString());
6241  }
6242 
6243  // make a temporary version from active view
6244  // modify the chosen versionAlias row
6245  // save as new version
6246 
6247  TableBase* config = cfgMgr->getTableByName(versionAliasesTableName);
6248  TableVersion temporaryVersion =
6249  config->createTemporaryView(activeVersions[versionAliasesTableName]);
6250 
6251  __SUP_COUT__ << "\t\t temporaryVersion: " << temporaryVersion << __E__;
6252 
6253  TableView* configView = config->getTemporaryView(temporaryVersion);
6254 
6255  // only make a new version if we are changing compared to active backbone
6256  bool isDifferent = false;
6257 
6258  // get member names and versions
6259  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
6260  try
6261  {
6262  cfgMgr->loadTableGroup(groupName,
6263  groupKey,
6264  false /*doActivate*/,
6265  &memberMap,
6266  0,
6267  0,
6268  0,
6269  0,
6270  0, // defaults
6271  true /*doNotLoadMember*/);
6272  }
6273  catch(...)
6274  {
6275  xmlOut.addTextElementToData(
6276  "Error",
6277  "Table group \"" + TableGroupKey::getFullGroupString(groupName, groupKey) +
6278  "\" can not be retrieved!");
6279  return;
6280  }
6281 
6282  unsigned int col;
6283  unsigned int col2 = configView->findCol("VersionAlias");
6284  unsigned int col3 = configView->findCol("TableName");
6285 
6286  for(auto& memberPair : memberMap)
6287  {
6288  bool thisMemberIsDifferent = false;
6289  unsigned int row = -1;
6290 
6291  __SUP_COUT__ << "Adding alias for " << memberPair.first << "_v"
6292  << memberPair.second << __E__;
6293 
6294  // find tableName, versionAlias pair
6295  // NOTE: only accept the first pair, repeats are ignored.
6296  try
6297  {
6298  unsigned int tmpRow = -1;
6299  do
6300  { // start looking from beyond last find
6301  tmpRow = configView->findRow(col3, memberPair.first, tmpRow + 1);
6302  __SUP_COUT__ << configView->getDataView()[tmpRow][col2] << __E__;
6303  } while(configView->getDataView()[tmpRow][col2] != versionAlias);
6304  // at this point the first pair was found! (else exception was thrown)
6305  row = tmpRow;
6306  }
6307  catch(...)
6308  {
6309  }
6310  if(row == (unsigned int)-1) // if row not found then add a row
6311  {
6312  thisMemberIsDifferent = true;
6313  row = configView->addRow();
6314 
6315  // set all columns in new row
6316  col = configView->findCol("CommentDescription");
6317  configView->setValue(
6318  std::string("Entry was added by server in ") +
6319  "ConfigurationGUISupervisor::setVersionAliasInActiveBackbone().",
6320  row,
6321  col);
6322 
6323  col = configView->getColUID();
6324  configView->setValue(
6325  memberPair.first.substr(0, memberPair.first.rfind("Table")) +
6326  versionAlias,
6327  row,
6328  col);
6329 
6330  configView->setValue(versionAlias, row, col2);
6331  configView->setValue(memberPair.first, row, col3);
6332  }
6333 
6334  __SUP_COUT__ << "\t\t row: " << row << __E__;
6335 
6336  col = configView->findCol("Version");
6337  __SUP_COUT__ << "\t\t col: " << col << __E__;
6338  __SUP_COUT__ << "\t\t version: " << memberPair.second << " vs "
6339  << configView->getDataView()[row][col] << __E__;
6340  if(memberPair.second.toString() != configView->getDataView()[row][col])
6341  {
6342  configView->setValue(memberPair.second.toString(), row, col);
6343  thisMemberIsDifferent = true;
6344  }
6345 
6346  if(thisMemberIsDifferent) // change author and time if row is different
6347  {
6348  configView->setValue(author, row, configView->findCol("Author"));
6349  configView->setValue(
6350  time(0), row, configView->findCol("RecordInsertionTime"));
6351  }
6352 
6353  if(thisMemberIsDifferent)
6354  isDifferent = true;
6355  }
6356 
6357  // configView->print();
6358 
6359  TableVersion newAssignedVersion;
6360  if(isDifferent) // make new version if different
6361  {
6362  __SUP_COUT__ << "\t\t**************************** Save as new table version"
6363  << __E__;
6364 
6365  newAssignedVersion =
6366  cfgMgr->saveNewTable(versionAliasesTableName, temporaryVersion);
6367  }
6368  else // use existing version
6369  {
6370  __SUP_COUT__ << "\t\t**************************** Using existing table version"
6371  << __E__;
6372 
6373  // delete temporaryVersion
6374  config->eraseView(temporaryVersion);
6375  newAssignedVersion = activeVersions[versionAliasesTableName];
6376  }
6377 
6378  xmlOut.addTextElementToData("savedAlias", versionAliasesTableName);
6379  xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString());
6380  __SUP_COUT__ << "\t\t newAssignedVersion: " << newAssignedVersion << __E__;
6381 }
6382 catch(std::runtime_error& e)
6383 {
6384  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
6385  xmlOut.addTextElementToData(
6386  "Error", "Error saving new Version Alias view!\n " + std::string(e.what()));
6387 }
6388 catch(...)
6389 {
6390  __SUP_COUT__ << "Error detected!\n\n " << __E__;
6391  xmlOut.addTextElementToData("Error", "Error saving new Version Alias view! ");
6392 }
6393 
6394 //========================================================================================================================
6395 // handleGroupAliasesXML
6396 //
6397 // return aliases and backbone groupAlias table version
6398 //
6399 // return this information:
6400 // <backbone groupTableName=xxx version=xxx>
6401 // <group alias=xxx name=xxx key=xxx comment=xxx>
6402 // <group alias=xxx name=xxx key=xxx comment=xxx>
6403 // ...
6404 //
6405 void ConfigurationGUISupervisor::handleGroupAliasesXML(HttpXmlDocument& xmlOut,
6406  ConfigurationManagerRW* cfgMgr)
6407 {
6408  cfgMgr->loadConfigurationBackbone();
6409  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
6410 
6411  std::string groupAliasesTableName = ConfigurationManager::GROUP_ALIASES_TABLE_NAME;
6412  if(activeVersions.find(groupAliasesTableName) == activeVersions.end())
6413  {
6414  __SUP_SS__ << "\nActive version of " << groupAliasesTableName << " missing! "
6415  << groupAliasesTableName
6416  << " is a required member of the Backbone table group."
6417  << "\n\nLikely you need to activate a valid Backbone table group."
6418  << __E__;
6419  xmlOut.addTextElementToData("Error", ss.str());
6420  return;
6421  }
6422  __SUP_COUT__ << "activeVersions[\"" << groupAliasesTableName
6423  << "\"]=" << activeVersions[groupAliasesTableName] << __E__;
6424  xmlOut.addTextElementToData("GroupAliasesTableName", groupAliasesTableName);
6425  xmlOut.addTextElementToData("GroupAliasesTableVersion",
6426  activeVersions[groupAliasesTableName].toString());
6427 
6428  std::vector<std::pair<std::string, ConfigurationTree>> aliasNodePairs =
6429  cfgMgr->getNode(groupAliasesTableName).getChildren();
6430 
6431  std::string groupName, groupKey, groupComment, groupType;
6432  for(auto& aliasNodePair : aliasNodePairs)
6433  {
6434  groupName = aliasNodePair.second.getNode("GroupName").getValueAsString();
6435  groupKey = aliasNodePair.second.getNode("GroupKey").getValueAsString();
6436 
6437  xmlOut.addTextElementToData("GroupAlias", aliasNodePair.first);
6438  xmlOut.addTextElementToData("GroupName", groupName);
6439  xmlOut.addTextElementToData("GroupKey", groupKey);
6440  xmlOut.addTextElementToData(
6441  "AliasComment",
6442  aliasNodePair.second.getNode("CommentDescription").getValueAsString());
6443 
6444  // get group comment
6445  groupComment = ""; // clear just in case failure
6446  groupType = "Invalid";
6447  try
6448  {
6449  cfgMgr->loadTableGroup(groupName,
6450  TableGroupKey(groupKey),
6451  0,
6452  0,
6453  0,
6454  0,
6455  &groupComment,
6456  0,
6457  0, // mostly defaults
6458  true /*doNotLoadMembers*/,
6459  &groupType);
6460  }
6461  catch(...)
6462  {
6463  __SUP_COUT_WARN__ << "Failed to load group '" << groupName << "(" << groupKey
6464  << ")' to extract group comment and type." << __E__;
6465  }
6466  xmlOut.addTextElementToData("GroupComment", groupComment);
6467  xmlOut.addTextElementToData("GroupType", groupType);
6468  }
6469 }
6470 
6471 //========================================================================================================================
6472 // handleTableVersionAliasesXML
6473 //
6474 // return version aliases and backbone versionAliases table version
6475 //
6476 // return this information:
6477 // <backbone aliasTableName=xxx version=xxx>
6478 // <version alias=xxx name=xxx version=xxx comment=xxx>
6479 // <version alias=xxx name=xxx version=xxx comment=xxx>
6480 // ...
6481 //
6482 void ConfigurationGUISupervisor::handleVersionAliasesXML(HttpXmlDocument& xmlOut,
6483  ConfigurationManagerRW* cfgMgr)
6484 {
6485  cfgMgr->loadConfigurationBackbone();
6486  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
6487 
6488  std::string versionAliasesTableName =
6489  ConfigurationManager::VERSION_ALIASES_TABLE_NAME;
6490  if(activeVersions.find(versionAliasesTableName) == activeVersions.end())
6491  {
6492  __SUP_SS__ << "Active version of VersionAliases missing!"
6493  << "Make sure you have a valid active Backbone Group." << __E__;
6494  xmlOut.addTextElementToData("Error", ss.str());
6495  return;
6496  }
6497  __SUP_COUT__ << "activeVersions[\"" << versionAliasesTableName
6498  << "\"]=" << activeVersions[versionAliasesTableName] << __E__;
6499  xmlOut.addTextElementToData("VersionAliasesVersion",
6500  activeVersions[versionAliasesTableName].toString());
6501 
6502  std::vector<std::pair<std::string, ConfigurationTree>> aliasNodePairs =
6503  cfgMgr->getNode(versionAliasesTableName).getChildren();
6504 
6505  for(auto& aliasNodePair : aliasNodePairs)
6506  {
6507  // note : these are column names in the versionAliasesTableName table
6508  // VersionAlias, TableName, Version, CommentDescription
6509  xmlOut.addTextElementToData(
6510  "VersionAlias",
6511  aliasNodePair.second.getNode("VersionAlias").getValueAsString());
6512  xmlOut.addTextElementToData(
6513  "TableName", aliasNodePair.second.getNode("TableName").getValueAsString());
6514  xmlOut.addTextElementToData(
6515  "Version", aliasNodePair.second.getNode("Version").getValueAsString());
6516  xmlOut.addTextElementToData(
6517  "Comment",
6518  aliasNodePair.second.getNode("CommentDescription").getValueAsString());
6519  }
6520 }
6521 
6522 //========================================================================================================================
6523 // handleGetTableGroupTypeXML
6524 //
6525 // return this information based on member table list
6526 // <TableGroupType value=xxx>
6527 //
6528 void ConfigurationGUISupervisor::handleGetTableGroupTypeXML(
6529  HttpXmlDocument& xmlOut,
6530  ConfigurationManagerRW* cfgMgr,
6531  const std::string& tableList)
6532 {
6533  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
6534  std::string name, versionStr;
6535  auto c = tableList.find(',', 0);
6536  auto i = c;
6537  i = 0; // auto used to get proper index/length type
6538  while(c < tableList.length())
6539  {
6540  // add the table name and version pair to the map
6541  name = tableList.substr(i, c - i);
6542  i = c + 1;
6543  c = tableList.find(',', i);
6544  if(c == std::string::npos) // missing version list entry?!
6545  {
6546  __SUP_SS__ << "Incomplete Table Name-Version pair!" << __E__;
6547  __SUP_COUT_ERR__ << "\n" << ss.str();
6548  xmlOut.addTextElementToData("Error", ss.str());
6549  return;
6550  }
6551 
6552  versionStr = tableList.substr(i, c - i);
6553  i = c + 1;
6554  c = tableList.find(',', i);
6555 
6556  memberMap[name] = TableVersion(versionStr);
6557  }
6558 
6559  std::string groupTypeString = "";
6560  // try to determine type, dont report errors, just mark "Invalid"
6561  try
6562  {
6563  // determine the type of the table group
6564  groupTypeString = cfgMgr->getTypeNameOfGroup(memberMap);
6565  xmlOut.addTextElementToData("TableGroupType", groupTypeString);
6566  }
6567  catch(std::runtime_error& e)
6568  {
6569  __SUP_SS__ << "Table group has invalid type! " << e.what() << __E__;
6570  __SUP_COUT__ << "\n" << ss.str();
6571  groupTypeString = "Invalid";
6572  xmlOut.addTextElementToData("TableGroupType", groupTypeString);
6573  }
6574  catch(...)
6575  {
6576  __SUP_SS__ << "Table group has invalid type! " << __E__;
6577  __SUP_COUT__ << "\n" << ss.str();
6578  groupTypeString = "Invalid";
6579  xmlOut.addTextElementToData("TableGroupType", groupTypeString);
6580  }
6581 }
6582 
6583 //========================================================================================================================
6584 // handleTableGroupsXML
6585 //
6586 // if returnMembers then
6587 // return type, comment and members
6588 // else just name and key
6589 //
6590 // return this information
6591 // <group name=xxx key=xxx>
6592 // <config name=xxx version=xxx />
6593 // <config name=xxx version=xxx />
6594 // ...
6595 // </group>
6596 // <group name=xxx key=xxx>...</group>
6597 // ...
6598 //
6599 void ConfigurationGUISupervisor::handleTableGroupsXML(HttpXmlDocument& xmlOut,
6600  ConfigurationManagerRW* cfgMgr,
6601  bool returnMembers)
6602 {
6603  DOMElement* parentEl;
6604 
6605  // get all group info from cache (if no cache, get from interface)
6606 
6607  if(!cfgMgr->getAllGroupInfo()
6608  .size()) // empty cache is strange, attempt to get from interface
6609  {
6610  __SUP_COUT__ << "Cache is empty? Attempting to regenerate." << __E__;
6611  cfgMgr->getAllTableInfo(true /*refresh*/);
6612  }
6613 
6614  const std::map<std::string, GroupInfo>& allGroupInfo = cfgMgr->getAllGroupInfo();
6615 
6616  // ConfigurationInterface* theInterface = cfgMgr->getConfigurationInterface();
6617  // std::set<std::string /*name*/> configGroups =
6618  // theInterface->getAllTableGroupNames();
6619  // __SUP_COUT__ << "Number of Config groups: " << configGroups.size() << __E__;
6620  //
6621  // TableGroupKey groupKey;
6622  // std::string groupName;
6623  //
6624  // std::map<std::string /*groupName*/,std::set<TableGroupKey> > allGroupsWithKeys;
6625  // for(auto& groupString:configGroups)
6626  // {
6627  // TableGroupKey::getGroupNameAndKey(groupString,groupName,groupKey);
6628  // allGroupsWithKeys[groupName].emplace(groupKey);
6629  //
6630  // //__SUP_COUT__ << "Config group " << groupString << " := " << groupName <<
6631  // //"(" << groupKey << ")" << __E__;
6632  // }
6633 
6634  TableGroupKey groupKey;
6635  std::string groupName;
6636  std::string groupString, groupTypeString, groupComment, groupCreationTime,
6637  groupAuthor;
6638  for(auto& groupInfo : allGroupInfo)
6639  {
6640  groupName = groupInfo.first;
6641  if(groupInfo.second.keys_.size() == 0)
6642  {
6643  __SUP_COUT__ << "Group name '" << groupName
6644  << "' found, but no keys so ignoring." << __E__;
6645  continue;
6646  }
6647 
6648  groupKey = *(groupInfo.second.keys_.rbegin());
6649 
6650  xmlOut.addTextElementToData("TableGroupName", groupName);
6651  xmlOut.addTextElementToData("TableGroupKey", groupKey.toString());
6652 
6653  // trusting the cache!
6654  xmlOut.addTextElementToData("TableGroupType",
6655  groupInfo.second.latestKeyGroupTypeString_);
6656  xmlOut.addTextElementToData("TableGroupComment",
6657  groupInfo.second.latestKeyGroupComment_);
6658  xmlOut.addTextElementToData("TableGroupAuthor",
6659  groupInfo.second.latestKeyGroupAuthor_);
6660  xmlOut.addTextElementToData("TableGroupCreationTime",
6661  groupInfo.second.latestKeyGroupCreationTime_);
6662 
6663  if(returnMembers)
6664  {
6665  // groupTypeString = "Invalid";
6666  // groupComment = ""; //clear just in case failure
6667 
6668  // groupString = TableGroupKey::getFullGroupString(groupName,groupKey);
6669 
6670  //__SUP_COUT__ << "Latest Config group " << groupString << " := " << groupName
6671  //<<
6672  // "(" << groupKey << ")" << __E__;
6673 
6674  parentEl = xmlOut.addTextElementToData("TableGroupMembers", "");
6675  //
6676  // std::map<std::string /*name*/, TableVersion /*version*/>
6677  // memberMap;
6678  // //try to determine type, dont report errors, just mark "Invalid"
6679  // try
6680  // {
6681  // //determine the type of the table group
6682  // memberMap = cfgMgr->loadTableGroup(groupName,groupKey,
6683  // 0,0,0,&groupComment,0,0, //mostly defaults
6684  // true /*doNotLoadMembers*/,&groupTypeString);
6685  // //groupTypeString = cfgMgr->getTypeNameOfGroup(memberMap);
6686  // xmlOut.addTextElementToData("TableGroupType",
6687  // groupTypeString);
6688  // xmlOut.addTextElementToData("TableGroupComment",
6689  // groupComment);
6690  // }
6691  // catch(std::runtime_error& e)
6692  // {
6693  // __SUP_SS__ << "Table group \"" + groupString +
6694  // "\" has invalid type! " + e.what() << __E__;
6695  // __SUP_COUT__ << "\n" << ss.str();
6696  // groupTypeString = "Invalid";
6697  // xmlOut.addTextElementToData("TableGroupType",
6698  // groupTypeString);
6699  // xmlOut.addTextElementToData("TableGroupComment",
6700  // groupComment); continue;
6701  // }
6702  // catch(...)
6703  // {
6704  // __SUP_SS__ << "Table group \"" + groupString +
6705  // "\" has invalid type! " << __E__;
6706  // __SUP_COUT__ << "\n" << ss.str();
6707  // groupTypeString = "Invalid";
6708  // xmlOut.addTextElementToData("TableGroupType",
6709  // groupTypeString);
6710  // xmlOut.addTextElementToData("TableGroupComment",
6711  // groupComment); continue;
6712  // }
6713 
6714  for(auto& memberPair : groupInfo.second.latestKeyMemberMap_)
6715  {
6716  //__SUP_COUT__ << "\tMember config " << memberPair.first << ":" <<
6717  // memberPair.second << __E__;
6718  xmlOut.addTextElementToParent("MemberName", memberPair.first, parentEl);
6719  xmlOut.addTextElementToParent(
6720  "MemberVersion", memberPair.second.toString(), parentEl);
6721  }
6722  } // end if returnMembers
6723 
6724  // add other group keys to xml for this group name
6725  // but just empty members (not displayed anyway)
6726  for(auto& keyInSet : groupInfo.second.keys_)
6727  {
6728  if(keyInSet == groupKey)
6729  continue; // skip the lastest
6730  xmlOut.addTextElementToData("TableGroupName", groupName);
6731  xmlOut.addTextElementToData("TableGroupKey", keyInSet.toString());
6732 
6733  // assume latest in cache reflects others (for speed)
6734  xmlOut.addTextElementToData("TableGroupType",
6735  groupInfo.second.latestKeyGroupTypeString_);
6736  xmlOut.addTextElementToData("TableGroupComment",
6737  groupInfo.second.latestKeyGroupComment_);
6738  xmlOut.addTextElementToData("TableGroupAuthor",
6739  groupInfo.second.latestKeyGroupAuthor_);
6740  xmlOut.addTextElementToData("TableGroupCreationTime",
6741  groupInfo.second.latestKeyGroupCreationTime_);
6742 
6743  if(returnMembers)
6744  {
6745  xmlOut.addTextElementToData("TableGroupMembers", "");
6746 
6747  // TODO -- make loadingHistoricalInfo an input parameter
6748  bool loadingHistoricalInfo = false;
6749  if(loadingHistoricalInfo)
6750  {
6751  groupComment = ""; // clear just in case failure
6752  try
6753  {
6754  cfgMgr->loadTableGroup(groupName,
6755  keyInSet,
6756  0,
6757  0,
6758  0,
6759  0,
6760  &groupComment,
6761  0,
6762  0, // mostly defaults
6763  true /*doNotLoadMembers*/,
6764  &groupTypeString);
6765  }
6766  catch(...)
6767  {
6768  groupTypeString = "Invalid";
6769  __SUP_COUT_WARN__
6770  << "Failed to load group '" << groupName << "(" << keyInSet
6771  << ")' to extract group comment and type." << __E__;
6772  }
6773 
6774  xmlOut.addTextElementToData("TableGroupType", groupTypeString);
6775  xmlOut.addTextElementToData("TableGroupComment", groupComment);
6776  xmlOut.addTextElementToData("TableGroupAuthor", groupAuthor);
6777  xmlOut.addTextElementToData("TableGroupCreationTime",
6778  groupCreationTime);
6779  }
6780  }
6781 
6782  } // end other key loop
6783  } // end primary group loop
6784 }
6785 
6786 //========================================================================================================================
6787 // handleTablesXML
6788 //
6789 // return this information
6790 // <table name=xxx>
6791 // <version key=xxx />
6792 // <version key=xxx />
6793 // ...
6794 // </table>
6795 // <table name=xxx>...</table>
6796 // ...
6797 //
6798 void ConfigurationGUISupervisor::handleTablesXML(HttpXmlDocument& xmlOut,
6799  ConfigurationManagerRW* cfgMgr,
6800  bool allowIllegalColumns)
6801 {
6802  DOMElement* parentEl;
6803 
6804  std::string accumulatedErrors = "";
6805  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo(
6806  allowIllegalColumns,
6807  allowIllegalColumns ? &accumulatedErrors
6808  : 0); // if allowIllegalColumns, then also refresh
6809  std::map<std::string, TableInfo>::const_iterator it = allTableInfo.begin();
6810 
6811  __SUP_COUT__ << "# of tables found: " << allTableInfo.size() << __E__;
6812 
6813  std::map<std::string, std::map<std::string, TableVersion>> versionAliases =
6814  cfgMgr->getVersionAliases();
6815 
6816  __SUP_COUT__ << "# of tables w/aliases: " << versionAliases.size() << __E__;
6817 
6818  while(it != allTableInfo.end())
6819  {
6820  // for each table name
6821  // get existing version keys
6822 
6823  //__SUP_COUT__ << "Name: " << it->first << " - #ofVersions: " <<
6824  // it->second.versions_.size() << __E__;
6825 
6826  // add system table name
6827  xmlOut.addTextElementToData("TableName", it->first);
6828  parentEl = xmlOut.addTextElementToData("TableVersions", "");
6829 
6830  // include aliases for this table (if the versions exist)
6831  if(versionAliases.find(it->first) != versionAliases.end())
6832  for(auto& aliasVersion : versionAliases[it->first])
6833  if(it->second.versions_.find(aliasVersion.second) !=
6834  it->second.versions_.end())
6835  // if(aliasVersion.first !=
6836  // ConfigurationManager::SCRATCH_VERSION_ALIAS) //NOT NEEDED IF
6837  // SCRATCH IS ALWAYS ALIAS
6838  xmlOut.addTextElementToParent(
6839  "Version",
6840  ConfigurationManager::ALIAS_VERSION_PREAMBLE + aliasVersion.first,
6841  parentEl);
6842  // else //NOT NEEDED IF SCRATCH IS ALWAYS ALIAS
6843  // __SUP_COUT_ERR__ << "Alias for table " << it->first << " is a
6844  // reserved alias '" <<
6845  // ConfigurationManager::SCRATCH_VERSION_ALIAS << "' - this
6846  // is illegal." << __E__;
6847 
6848  // //if scratch version exists, add an alias for it /NOT NEEDED IF SCRATCH IS
6849  // ALWAYS ALIAS
6850  // if(it->second.versions_.find(TableVersion(TableVersion::SCRATCH)) !=
6851  // it->second.versions_.end())
6852  // xmlOut.addTextElementToParent("Version",
6853  // ConfigurationManager::ALIAS_VERSION_PREAMBLE +
6854  // ConfigurationManager::SCRATCH_VERSION_ALIAS, parentEl);
6855 
6856  // get all table versions for the current table
6857  // except skip scratch version
6858  for(auto& version : it->second.versions_)
6859  if(!version.isScratchVersion())
6860  xmlOut.addTextElementToParent("Version", version.toString(), parentEl);
6861 
6862  ++it;
6863  }
6864 
6865  if(accumulatedErrors != "")
6866  xmlOut.addTextElementToData(
6867  "Error",
6868  std::string("Column errors were allowed for this request, ") +
6869  "but please note the following errors:\n" + accumulatedErrors);
6870 }
6871 
6872 //========================================================================================================================
6873 // testXDAQContext
6874 // test activation of context group
6875 void ConfigurationGUISupervisor::testXDAQContext()
6876 {
6877  try
6878  {
6879  __SUP_COUT__ << "Attempting test activation of the context group." << __E__;
6880  ConfigurationManager cfgMgr; // create instance to activate saved groups
6881  }
6882  catch(const std::runtime_error& e)
6883  {
6884  __SUP_COUT_WARN__
6885  << "The test activation of the context group failed. Ignoring error: \n"
6886  << e.what() << __E__;
6887  }
6888  catch(...)
6889  {
6890  __SUP_COUT_WARN__ << "The test activation of the context group failed. Ignoring."
6891  << __E__;
6892  }
6893  return;
6894 
6896  // below has been used for debugging.
6897 
6898  // behave like a user
6899  // start with top level xdaq context
6900  // then add and delete rows proof-of-concept
6901  // export xml xdaq config file
6902 
6905  // behave like a new user
6906  //
6907  // ConfigurationManagerRW cfgMgrInst("ExampleUser");
6908  //
6909  // ConfigurationManagerRW* cfgMgr =& cfgMgrInst;
6910 
6911  // std::map<std::string, TableVersion> groupMembers;
6912  // groupMembers["DesktopIcon"] = TableVersion(2);
6913  // cfgMgr->saveNewTableGroup("test",
6914  // groupMembers, "test comment");
6915 
6916  // //
6917  // const std::map<std::string, TableInfo>& allTableInfo =
6918  // cfgMgr->getAllTableInfo(true);
6919  // __SUP_COUT__ << "allTableInfo.size() = " << allTableInfo.size() << __E__;
6920  // for(auto& mapIt : allTableInfo)
6921  // {
6922  // __SUP_COUT__ << "Config Name: " << mapIt.first << __E__;
6923  // __SUP_COUT__ << "\t\tExisting Versions: " << mapIt.second.versions_.size() <<
6924  //__E__;
6925  //
6926  // //get version key for the current system table key
6927  // for (auto& v:mapIt.second.versions_)
6928  // {
6929  // __SUP_COUT__ << "\t\t" << v << __E__;
6930  // }
6931  // }
6932 
6933  // testXDAQContext just a test bed for navigating the new config tree
6934  // cfgMgr->testXDAQContext();
6935 
6938 }
static xdaq::Application * instantiate(xdaq::ApplicationStub *s)