otsdaq-utilities  3.06.00
ConfigurationGUISupervisor.cc
1 #include "otsdaq-utilities/ConfigurationGUI/ConfigurationGUISupervisor.h"
2 
3 #include "otsdaq/CgiDataUtilities/CgiDataUtilities.h"
4 #include "otsdaq/Macros/CoutMacros.h"
5 #include "otsdaq/MessageFacility/MessageFacility.h"
6 #include "otsdaq/TablePlugins/IterateTable.h"
7 #include "otsdaq/XmlUtilities/HttpXmlDocument.h"
8 
9 #include <boost/stacktrace.hpp>
10 
11 #include "otsdaq/GatewaySupervisor/GatewaySupervisor.h" //for saveModifiedVersionXML()
12 #include "otsdaq/TablePlugins/ARTDAQTableBase/ARTDAQTableBase.h" //for artdaq extraction
13 #include "otsdaq/TablePlugins/XDAQContextTable/XDAQContextTable.h" //for context relaunch
14 
15 #include <xdaq/NamespaceURI.h>
16 
17 #include <iostream>
18 #include <map>
19 #include <utility>
20 
21 using namespace ots;
22 
23 #undef __MF_SUBJECT__
24 #define __MF_SUBJECT__ "CfgGUI"
25 
26 #define TABLE_INFO_PATH std::string(__ENV__("TABLE_INFO_PATH")) + "/"
27 #define TABLE_INFO_EXT std::string("Info.xml")
28 
31 xdaq::Application* ConfigurationGUISupervisor::instantiate(xdaq::ApplicationStub* stub)
32 {
33  return new ConfigurationGUISupervisor(stub);
34 }
35 
36 //==============================================================================
41  : CoreSupervisorBase(stub)
42 {
43  __SUP_COUT__ << "Constructor started." << __E__;
44 
45  INIT_MF("." /*directory used is USER_DATA/LOG/.*/);
46 
47  // make macro directories in case they don't exist
48  mkdir(((std::string)ARTDAQTableBase::ARTDAQ_CONFIG_LAYOUTS_PATH).c_str(), 0755);
49 
50  init();
51  __SUP_COUT__ << "Constructor complete." << __E__;
52 } // end constructor()
53 
54 //==============================================================================
55 ConfigurationGUISupervisor::~ConfigurationGUISupervisor(void) { destroy(); }
56 
57 //==============================================================================
58 void ConfigurationGUISupervisor::init(void)
59 {
60  __SUP_COUT__ << "Initializing..." << __E__;
61 
62  try
63  {
64  __SUP_COUT__ << "Activating saved context, which may prepare for normal mode..."
65  << __E__;
66 
67  testXDAQContext(); // test context group activation
68 
69  __SUP_COUT__ << "Done with test context." << __E__;
70  }
71  catch(...)
72  {
73  __COUT_WARN__ << "Failed test context group activation. otsdaq, in Normal mode, "
74  "will not launch when this test fails. "
75  << "Check the active context group from within Wizard Mode."
76  << __E__;
77  }
78 
79  //initialize first config manager (pre-load for first user)
80  refreshUserSession("" /* userInfo.username_ */, 1); // (refresh == "1"));
81  //after this call, empty username : index=0 is in map userConfigurationManagers_[:0]
82 
83  if(CorePropertySupervisorBase::allSupervisorInfo_.isWizardMode())
84  {
85  __SUP_COUT_INFO__
86  << "After successful Config GUI init, marking alive for wiz mode!" << __E__;
87  CorePropertySupervisorBase::
88  indicateOtsAlive(); // no parameters for wiz mode indication
89  }
90 } // end init()
91 
92 //==============================================================================
93 void ConfigurationGUISupervisor::destroy(void)
94 {
95  __SUP_COUT__ << "Destructing..." << __E__;
96 
97  // called by destructor
98  for(std::map<std::string, ConfigurationManagerRW*>::iterator it =
99  userConfigurationManagers_.begin();
100  it != userConfigurationManagers_.end();
101  ++it)
102  {
103  delete it->second;
104  it->second = 0;
105  }
106  userConfigurationManagers_.clear();
107 
108  if(ConfigurationInterface::getInstance() != nullptr)
109  delete ConfigurationInterface::getInstance();
110 
111 } // end destroy()
112 
113 //==============================================================================
114 void ConfigurationGUISupervisor::defaultPage(xgi::Input* in, xgi::Output* out)
115 {
116  cgicc::Cgicc cgiIn(in);
117  std::string configWindowName =
118  CgiDataUtilities::getData(cgiIn, "configWindowName"); // from GET
119  if(configWindowName == "tableEditor")
120  *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
121  "src='/WebPath/html/ConfigurationTableEditor.html?urn="
122  << this->getApplicationDescriptor()->getLocalId() << "'></frameset></html>";
123  if(configWindowName == "iterate")
124  *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
125  "src='/WebPath/html/Iterate.html?urn="
126  << this->getApplicationDescriptor()->getLocalId() << "'></frameset></html>";
127  else
128  *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
129  "src='/WebPath/html/ConfigurationGUI.html?urn="
130  << this->getApplicationDescriptor()->getLocalId() << "'></frameset></html>";
131 } // end defaultPage()
132 
133 //==============================================================================
137 {
138  CorePropertySupervisorBase::setSupervisorProperty(
139  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.UserPermissionsThreshold,
140  "*=10 | deleteTreeNodeRecords=255 | saveTableInfo=255 | "
141  "deleteTableInfo=255"); // experienced users to edit, admins to delete
142 
143  CorePropertySupervisorBase::setSupervisorProperty(
144  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.RequireUserLockRequestTypes,
145  "*"); // all
146 
147  //Allow get* AutomatedRequestTypes to enable read-only access to the Configuration Tree:
148  CorePropertySupervisorBase::setSupervisorProperty(
149  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes, "get*");
150 } // end setSupervisorPropertyDefaults()
151 
152 //==============================================================================
156 {
157  CorePropertySupervisorBase::addSupervisorProperty(
158  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes,
159  "getActiveTableGroups");
160 
161  //Allow get* to not require lock to enable read-only access to the Configuration Tree:
162  CorePropertySupervisorBase::setSupervisorProperty(
163  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.CheckUserLockRequestTypes,
164  "!get*"); // all except read-only requests
165 } // end forceSupervisorPropertyValues()
166 
167 //==============================================================================
168 void ConfigurationGUISupervisor::request(const std::string& requestType,
169  cgicc::Cgicc& cgiIn,
170  HttpXmlDocument& xmlOut,
171  const WebUsers::RequestUserInfo& userInfo)
172 try
173 {
174  // Commands
175 
176  __COUTTV__(requestType);
177 
178  // gatewayLaunchOTS -- and other StartOTS commands
179 
180  // saveTableInfo
181  // deleteTableInfo
182  // flattenToSystemAliases
183  // versionTracking
184  // getColumnTypes
185  // getGroupAliases
186  // setGroupAliasInActiveBackbone
187  // setTableAliasInActiveBackbone
188  // setAliasOfGroupMembers
189  // getVersionAliases
190  // getTableGroups
191  // getTableGroupType
192  // getTables
193  // getContextMemberNames
194  // getBackboneMemberNames
195  // getIterateMemberNames
196  // getSpecificTableGroup
197  // saveNewTableGroup
198  // getSpecificTable
199  // saveSpecificTable
200  // clearTableTemporaryVersions
201  // clearTableCachedVersions
202  // getGroupHistory
203  //
204  // ---- associated with JavaScript Table API
205  // getTreeView
206  // getTreeNodeCommonFields
207  // getUniqueFieldValuesForRecords
208  // getTreeNodeFieldValues
209  // setTreeNodeFieldValues
210  // addTreeNodeRecords
211  // deleteTreeNodeRecords
212  // renameTreeNodeRecords
213  // copyTreeNodeRecords
214  // getTableStructureStatusAsJSON
215  // ---- end associated with JavaScript Table API
216  //
217  // ---- associated with JavaScript artdaq API
218  // getArtdaqNodes
219  // saveArtdaqNodes
220  // getArtdaqNodeLayout
221  // saveArtdaqNodeLayout
222  // ---- end associated with JavaScript artdaq API
223  //
224  // activateTableGroup
225  // getActiveTableGroups
226  // copyViewToCurrentColumns
227  // saveTreeNodeEdit
228  // checkAffectedActiveGroups (formerly 'getAffectedActiveGroups' but then was considered automated req, when it is more correctly associated with write tasks)
229  // getLinkToChoices
230  // getLastTableGroups
231  // getSubsytemTableGroups
232  // diffWithActiveGroup
233  // diffWithGroupKey
234  // diffTableVersions
235  // findGroupsWithTable
236  // mergeGroups
237  //
238  // ---- associated with JavaScript Iterate App
239  // savePlanCommandSequence
240  // ---- end associated with JavaScript Iterate App
241 
242  // acquire user's configuration manager based on username& activeSessionIndex
243  std::string refresh = CgiDataUtilities::getData(cgiIn, "refresh"); // from GET
244 
245  // refresh to reload from info files and db (maintains temporary views!)
246  ConfigurationManagerRW* cfgMgr =
247  refreshUserSession(userInfo.username_, (refresh == "1"));
248  __COUTTV__(userInfo.username_);
249  __COUTTV__(cfgMgr->getUsername());
250 
251  if(0) //for debugging/optimizing cache resets!
252  {
253  const GroupInfo& groupInfo = cfgMgr->getGroupInfo("MC2TriggerContext");
254  const std::set<TableGroupKey>& sortedKeys = groupInfo.getKeys(); // rename
255  __COUTTV__(sortedKeys.size());
256  }
257 
258  if(requestType == "saveTableInfo")
259  {
260  std::string tableName =
261  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
262  std::string columnCSV =
263  CgiDataUtilities::postData(cgiIn, "columnCSV"); // from POST
264  std::string allowOverwrite =
265  CgiDataUtilities::getData(cgiIn, "allowOverwrite"); // from GET
266  std::string tableDescription =
267  CgiDataUtilities::postData(cgiIn, "tableDescription"); // from POST
268  std::string columnChoicesCSV =
269  CgiDataUtilities::postData(cgiIn, "columnChoicesCSV"); // from POST
270 
271  __SUP_COUT__ << "tableName: " << tableName << __E__;
272  __SUP_COUT__ << "columnCSV: " << columnCSV << __E__;
273  __SUP_COUT__ << "tableDescription: " << tableDescription << __E__;
274  __SUP_COUT__ << "columnChoicesCSV: " << columnChoicesCSV << __E__;
275  __SUP_COUT__ << "allowOverwrite: " << allowOverwrite << __E__;
276 
277  if(!allSupervisorInfo_.isWizardMode())
278  {
279  __SUP_SS__ << "Improper permissions for saving table info." << __E__;
280  xmlOut.addTextElementToData("Error", ss.str());
281  }
282  else
283  handleSaveTableInfoXML(xmlOut,
284  cfgMgr,
285  tableName,
286  columnCSV,
287  tableDescription,
288  columnChoicesCSV,
289  allowOverwrite == "1");
290  }
291  else if(requestType == "deleteTableInfo")
292  {
293  std::string tableName =
294  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
295  __SUP_COUT__ << "tableName: " << tableName << __E__;
296  handleDeleteTableInfoXML(xmlOut, cfgMgr, tableName);
297  }
298  else if(requestType == "gatewayLaunchOTS" || requestType == "gatewayLaunchWiz" ||
299  requestType == "flattenToSystemAliases")
300  {
301  // NOTE: similar to Supervisor version but does not keep active sessions
302  __SUP_COUT_WARN__ << requestType << " command received! " << __E__;
303  __COUT_WARN__ << requestType << " command received! " << __E__;
304 
305  // now launch
306  __SUP_COUT_INFO__ << "Launching " << requestType << "... " << __E__;
307 
308  __SUP_COUT__ << "Extracting target context hostnames... " << __E__;
309  std::vector<std::string> hostnames;
310 
311  // flattenToSystemAliases should always work in wiz mode!
312  if(requestType == "flattenToSystemAliases" &&
313  CorePropertySupervisorBase::allSupervisorInfo_.isWizardMode())
314  {
315  hostnames.push_back(__ENV__("OTS_CONFIGURATION_WIZARD_SUPERVISOR_SERVER"));
316  __SUP_COUT__ << "hostname = " << hostnames.back() << __E__;
317  }
318  else
319  {
320  try
321  {
322  cfgMgr->init(); // completely reset to re-align with any changes
323 
324  const XDAQContextTable* contextTable =
325  cfgMgr->__GET_CONFIG__(XDAQContextTable);
326 
327  auto contexts = contextTable->getContexts();
328  unsigned int i, j;
329  for(const auto& context : contexts)
330  {
331  if(!context.status_)
332  continue;
333 
334  // find last slash
335  j = 0; // default to whole string
336  for(i = 0; i < context.address_.size(); ++i)
337  if(context.address_[i] == '/')
338  j = i + 1;
339  hostnames.push_back(context.address_.substr(j));
340  __SUP_COUT__ << "hostname = " << hostnames.back() << __E__;
341  }
342  }
343  catch(...)
344  {
345  __SUP_SS__ << "The Configuration Manager could not be initialized to "
346  "extract contexts."
347  << __E__;
348  try
349  {
350  throw;
351  } //one more try to printout extra info
352  catch(const std::exception& e)
353  {
354  ss << "Exception message: " << e.what();
355  }
356  catch(...)
357  {
358  }
359 
360  __SUP_COUT_ERR__ << "\n" << ss.str();
361  return;
362  }
363  }
364 
365  if(hostnames.size() == 0)
366  {
367  __SUP_SS__ << "No hostnames found to launch command '" + requestType +
368  "'... Is there a valid Context group activated?"
369  << __E__;
370  __SUP_COUT_ERR__ << "\n" << ss.str();
371 
372  xmlOut.addTextElementToData("Error", ss.str());
373  }
374 
375  for(const auto& hostname : hostnames)
376  {
377  std::string fn = (std::string(__ENV__("SERVICE_DATA_PATH")) +
378  "/StartOTS_action_" + hostname + ".cmd");
379  FILE* fp = fopen(fn.c_str(), "w");
380  if(fp)
381  {
382  if(requestType == "gatewayLaunchOTS")
383  fprintf(fp, "LAUNCH_OTS");
384  else if(requestType == "gatewayLaunchWiz")
385  fprintf(fp, "LAUNCH_WIZ");
386  else if(requestType == "flattenToSystemAliases")
387  {
388  fprintf(fp, "FLATTEN_TO_SYSTEM_ALIASES");
389  fclose(fp);
390  break; // only do at one host
391  }
392 
393  fclose(fp);
394  }
395  else
396  __SUP_COUT_ERR__ << "Unable to open command file: " << fn << __E__;
397  }
398  }
399  else if(requestType == "versionTracking" || requestType == "getVersionTracking")
400  {
401  std::string type;
402  if(requestType == "getVersionTracking")
403  type = "Get";
404  else
405  type = CgiDataUtilities::getData(cgiIn, "Type"); // from GET
406  __SUP_COUT__ << "type: " << type << __E__;
407 
408  if(type == "Get")
409  xmlOut.addTextElementToData(
410  "versionTrackingStatus",
411  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
412  else if(type == "ON")
413  {
414  ConfigurationInterface::setVersionTrackingEnabled(true);
415  xmlOut.addTextElementToData(
416  "versionTrackingStatus",
417  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
418  }
419  else if(type == "OFF")
420  {
421  ConfigurationInterface::setVersionTrackingEnabled(false);
422  xmlOut.addTextElementToData(
423  "versionTrackingStatus",
424  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
425  }
426  }
427  else if(requestType == "getColumnTypes")
428  {
429  // return the possible column types and their defaults
430  std::vector<std::string> allTypes = TableViewColumnInfo::getAllTypesForGUI();
431  std::vector<std::string> allDataTypes =
432  TableViewColumnInfo::getAllDataTypesForGUI();
433  std::map<std::pair<std::string, std::string>, std::string> allDefaults =
435  // TODO maybe here a new function will be needed to get allminmaxforGUI
436  for(const auto& type : allTypes)
437  xmlOut.addTextElementToData("columnTypeForGUI", type);
438  for(const auto& dataType : allDataTypes)
439  xmlOut.addTextElementToData("columnDataTypeForGUI", dataType);
440 
441  for(const auto& colDefault : allDefaults)
442  {
443  xmlOut.addTextElementToData("columnDefaultDataType", colDefault.first.first);
444  xmlOut.addTextElementToData("columnDefaultTypeFilter",
445  colDefault.first.second);
446  xmlOut.addTextElementToData("columnDefaultValue", colDefault.second);
447  }
448  // TODO add min and max responses.
449  }
450  else if(requestType == "getGroupAliases")
451  {
452  // Since this is called from setting up System View in the table GUI
453  // give option for reloading "persistent" active configurations
454  bool reloadActive =
455  1 == CgiDataUtilities::getDataAsInt(cgiIn, "reloadActiveGroups"); // from GET
456 
457  __SUP_COUT__ << "reloadActive: " << reloadActive << __E__;
458  if(reloadActive)
459  {
460  try
461  {
462  cfgMgr->clearAllCachedVersions();
463  cfgMgr->restoreActiveTableGroups(true);
464  }
465  catch(std::runtime_error& e)
466  {
467  __SUP_SS__ << ("Error loading active groups!\n\n" + std::string(e.what()))
468  << __E__;
469  __SUP_COUT_ERR__ << "\n" << ss.str();
470  xmlOut.addTextElementToData("Error", ss.str());
471  }
472  catch(...)
473  {
474  __SUP_SS__ << ("Error loading active groups!\n\n") << __E__;
475  try
476  {
477  throw;
478  } //one more try to printout extra info
479  catch(const std::exception& e)
480  {
481  ss << "Exception message: " << e.what();
482  }
483  catch(...)
484  {
485  }
486  __SUP_COUT_ERR__ << "\n" << ss.str();
487  xmlOut.addTextElementToData("Error", ss.str());
488  }
489  }
490 
491  handleGroupAliasesXML(xmlOut, cfgMgr);
492  }
493  else if(requestType == "setGroupAliasInActiveBackbone")
494  {
495  std::string groupAliasCSV =
496  CgiDataUtilities::getData(cgiIn, "groupAlias"); // from GET
497  std::string groupNameCSV =
498  CgiDataUtilities::getData(cgiIn, "groupName"); // from GET
499  std::string groupKeyCSV =
500  CgiDataUtilities::getData(cgiIn, "groupKey"); // from GET
501 
502  __SUP_COUTV__(groupAliasCSV);
503  __SUP_COUTV__(groupNameCSV);
504  __SUP_COUTV__(groupKeyCSV);
505 
506  handleSetGroupAliasInBackboneXML(
507  xmlOut, cfgMgr, groupAliasCSV, groupNameCSV, groupKeyCSV, userInfo.username_);
508  }
509  else if(requestType == "setTableAliasInActiveBackbone")
510  {
511  std::string tableAlias =
512  CgiDataUtilities::getData(cgiIn, "tableAlias"); // from GET
513  std::string tableName =
514  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
515  std::string version = CgiDataUtilities::getData(cgiIn, "version"); // from GET
516 
517  __SUP_COUT__ << "tableAlias: " << tableAlias << __E__;
518  __SUP_COUT__ << "tableName: " << tableName << __E__;
519  __SUP_COUT__ << "version: " << version << __E__;
520 
521  handleSetTableAliasInBackboneXML(xmlOut,
522  cfgMgr,
523  tableAlias,
524  tableName,
525  TableVersion(version),
526  userInfo.username_);
527  }
528  else if(requestType == "setAliasOfGroupMembers")
529  {
530  std::string versionAlias =
531  CgiDataUtilities::getData(cgiIn, "versionAlias"); // from GET
532  std::string groupName =
533  CgiDataUtilities::getData(cgiIn, "groupName"); // from GET
534  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey"); // from GET
535 
536  __SUP_COUT__ << "versionAlias: " << versionAlias << __E__;
537  __SUP_COUT__ << "groupName: " << groupName << __E__;
538  __SUP_COUT__ << "groupKey: " << groupKey << __E__;
539 
540  handleAliasGroupMembersInBackboneXML(xmlOut,
541  cfgMgr,
542  versionAlias,
543  groupName,
544  TableGroupKey(groupKey),
545  userInfo.username_);
546  }
547  else if(requestType == "getVersionAliases")
548  {
549  handleVersionAliasesXML(xmlOut, cfgMgr);
550  }
551  else if(requestType == "getTableGroups")
552  {
553  bool doNotReturnMembers =
554  CgiDataUtilities::getDataAsInt(cgiIn, "doNotReturnMembers") == 1
555  ? true
556  : false; // from GET
557 
558  __SUP_COUT__ << "doNotReturnMembers: " << doNotReturnMembers << __E__;
559  handleTableGroupsXML(xmlOut, cfgMgr, !doNotReturnMembers);
560  }
561  else if(requestType == "getTableGroupType")
562  {
563  std::string tableList =
564  CgiDataUtilities::postData(cgiIn, "tableList"); // from POST
565  __SUP_COUT__ << "tableList: " << tableList << __E__;
566 
567  handleGetTableGroupTypeXML(xmlOut, cfgMgr, tableList);
568  }
569  else if(requestType == "getTables")
570  {
571  handleTablesXML(xmlOut, cfgMgr);
572  }
573  else if(requestType == "getContextMemberNames")
574  {
575  std::set<std::string> members = cfgMgr->getFixedContextMemberNames();
576 
577  for(auto& member : members)
578  xmlOut.addTextElementToData("ContextMember", member);
579  }
580  else if(requestType == "getBackboneMemberNames")
581  {
582  std::set<std::string> members = cfgMgr->getBackboneMemberNames();
583 
584  for(auto& member : members)
585  xmlOut.addTextElementToData("BackboneMember", member);
586  }
587  else if(requestType == "getIterateMemberNames")
588  {
589  std::set<std::string> members = cfgMgr->getIterateMemberNames();
590 
591  for(auto& member : members)
592  xmlOut.addTextElementToData("IterateMember", member);
593  }
594  else if(requestType == "getSpecificTableGroup")
595  {
596  std::string groupName =
597  CgiDataUtilities::getData(cgiIn, "groupName"); // from GET
598  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey"); // from GET
599 
600  __SUP_COUT__ << "groupName: " << groupName << __E__;
601  __SUP_COUT__ << "groupKey: " << groupKey << __E__;
602 
604  xmlOut, cfgMgr, groupName, TableGroupKey(groupKey));
605  }
606  else if(requestType == "saveNewTableGroup")
607  {
608  std::string groupName =
609  CgiDataUtilities::getData(cgiIn, "groupName"); // from GET
610  bool ignoreWarnings =
611  CgiDataUtilities::getDataAsInt(cgiIn, "ignoreWarnings"); // from GET
612  bool allowDuplicates =
613  CgiDataUtilities::getDataAsInt(cgiIn, "allowDuplicates"); // from GET
614  bool lookForEquivalent =
615  CgiDataUtilities::getDataAsInt(cgiIn, "lookForEquivalent"); // from GET
616  std::string tableList =
617  CgiDataUtilities::postData(cgiIn, "tableList"); // from POST
618  std::string comment =
619  CgiDataUtilities::getData(cgiIn, "groupComment"); // from GET
620 
621  __SUP_COUT__ << "saveNewTableGroup: " << groupName << __E__;
622  __SUP_COUT__ << "tableList: " << tableList << __E__;
623  __SUP_COUT__ << "ignoreWarnings: " << ignoreWarnings << __E__;
624  __SUP_COUT__ << "allowDuplicates: " << allowDuplicates << __E__;
625  __SUP_COUT__ << "lookForEquivalent: " << lookForEquivalent << __E__;
626  __SUP_COUT__ << "comment: " << comment << __E__;
627 
629  cfgMgr,
630  groupName,
631  tableList,
632  allowDuplicates,
633  ignoreWarnings,
634  comment,
635  lookForEquivalent);
636  }
637  else if(requestType == "getSpecificTable")
638  {
639  std::string tableName =
640  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
641  std::string versionStr = CgiDataUtilities::getData(cgiIn, "version"); // from GET
642  int dataOffset = CgiDataUtilities::getDataAsInt(cgiIn, "dataOffset"); // from GET
643  int chunkSize = CgiDataUtilities::getDataAsInt(
644  cgiIn,
645  "chunkSize"); // from GET (chunkSize is currently ignored, could use to get a few rows at a time)
646  bool descriptionOnly = CgiDataUtilities::getDataAsInt(
647  cgiIn, "descriptionOnly"); // from GET (used to get tooltip info)
648  bool allowIllegalColumns =
649  CgiDataUtilities::getDataAsInt(cgiIn, "allowIllegalColumns"); // from GET
650  bool rawData = CgiDataUtilities::getDataAsInt(cgiIn, "rawData"); // from GET
651 
652  __SUP_COUT__ << "getSpecificTable: " << tableName << " versionStr: " << versionStr
653  << " chunkSize: " << chunkSize << " dataOffset: " << dataOffset
654  << " descriptionOnly: " << descriptionOnly
655  << " allowIllegalColumns: " << allowIllegalColumns
656  << " rawData: " << rawData << __E__;
657 
658  TableVersion version;
659  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
660 
661  if(allTableInfo.find(tableName) != allTableInfo.end())
662  {
663  if(versionStr == "" && // take latest version if no version specified
664  allTableInfo.at(tableName).versions_.size())
665  {
666  // Start from the last element
667  auto it = allTableInfo.at(tableName).versions_.rbegin();
668  if(it->isScratchVersion()) //do not allow SCRATCH_VERSION as default selection
669  ++it; // Move to the second-to-last element
670  version = *it;
671  }
672  else if(versionStr.find(ConfigurationManager::ALIAS_VERSION_PREAMBLE) == 0)
673  {
674  // convert alias to version
675  std::map<std::string /*table*/,
676  std::map<std::string /*alias*/, TableVersion>>
677  versionAliases = cfgMgr->getVersionAliases();
678 
679  std::string versionAlias;
680  versionAlias = versionStr.substr(
681  ConfigurationManager::ALIAS_VERSION_PREAMBLE.size());
682  // if(versionAlias ==
683  // ConfigurationManager::SCRATCH_VERSION_ALIAS)
685  // {
686  // version = TableVersion::SCRATCH;
687  // __SUP_COUT__ << "version alias translated to: " << version
688  //<<
689  //__E__;
690  // }
691  // else
692  if(versionAliases.find(tableName) != versionAliases.end() &&
693  versionAliases[tableName].find(versionAlias) !=
694  versionAliases[tableName].end())
695  {
696  version = versionAliases[tableName][versionAlias];
697  __SUP_COUT__ << "version alias translated to: " << version << __E__;
698  }
699  else
700  __SUP_COUT_WARN__
701  << "version alias '"
702  << versionStr.substr(
703  ConfigurationManager::ALIAS_VERSION_PREAMBLE.size())
704  << "'was not found in active version aliases!" << __E__;
705  }
706  else // else take specified version
707  version = atoi(versionStr.c_str());
708  }
709 
710  __SUP_COUT__ << "version: " << version << __E__;
711 
712  handleGetTableXML(xmlOut,
713  cfgMgr,
714  tableName,
715  TableVersion(version),
716  allowIllegalColumns,
717  rawData,
718  descriptionOnly);
719  // append author column default value
720  xmlOut.addTextElementToData("DefaultRowValue", userInfo.username_);
721  }
722  else if(requestType == "saveSpecificTable")
723  {
724  std::string tableName =
725  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
726  int version = CgiDataUtilities::getDataAsInt(cgiIn, "version"); // from GET
727  int dataOffset = CgiDataUtilities::getDataAsInt(cgiIn, "dataOffset"); // from GET
728  bool sourceTableAsIs =
729  CgiDataUtilities::getDataAsInt(cgiIn, "sourceTableAsIs"); // from GET
730  bool lookForEquivalent =
731  CgiDataUtilities::getDataAsInt(cgiIn, "lookForEquivalent"); // from GET
732  int temporary = CgiDataUtilities::getDataAsInt(cgiIn, "temporary"); // from GET
733  std::string comment =
734  CgiDataUtilities::getData(cgiIn, "tableComment"); // from GET
735 
736  std::string data = CgiDataUtilities::postData(cgiIn, "data"); // from POST
737  // data format: commas and semi-colons indicate new row
738  // r0c0,r0c1,...,r0cN,;r1c0,...
739 
740  __SUP_COUT__ << "tableName: " << tableName << " version: " << version
741  << " temporary: " << temporary << " dataOffset: " << dataOffset
742  << __E__;
743  __SUP_COUT__ << "comment: " << comment << __E__;
744  __SUP_COUT__ << "data: " << data << __E__;
745  __SUP_COUT__ << "sourceTableAsIs: " << sourceTableAsIs << __E__;
746  __SUP_COUT__ << "lookForEquivalent: " << lookForEquivalent << __E__;
747 
749  cfgMgr,
750  tableName,
751  TableVersion(version),
752  temporary,
753  data,
754  dataOffset,
755  userInfo.username_,
756  comment,
757  sourceTableAsIs,
758  lookForEquivalent);
759  }
760  else if(requestType == "clearTableTemporaryVersions")
761  {
762  std::string tableName =
763  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
764  __SUP_COUT__ << "tableName: " << tableName << __E__;
765 
766  try
767  {
768  cfgMgr->eraseTemporaryVersion(tableName);
769  }
770  catch(std::runtime_error& e)
771  {
772  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
773  xmlOut.addTextElementToData(
774  "Error", "Error clearing temporary views!\n " + std::string(e.what()));
775  }
776  catch(...)
777  {
778  __SUP_COUT__ << "Error detected!\n\n " << __E__;
779  xmlOut.addTextElementToData("Error", "Error clearing temporary views! ");
780  }
781  }
782  else if(requestType == "clearTableCachedVersions")
783  {
784  std::string tableName =
785  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
786  __SUP_COUT__ << "tableName: " << tableName << __E__;
787 
788  try
789  {
790  if(tableName == "*")
791  cfgMgr->clearAllCachedVersions();
792  else
793  cfgMgr->clearCachedVersions(tableName);
794 
795  // Force manual reload... not cfgMgr->getAllTableInfo(true /*refresh*/);
796  }
797  catch(std::runtime_error& e)
798  {
799  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
800  xmlOut.addTextElementToData(
801  "Error", "Error clearing cached views!\n " + std::string(e.what()));
802  }
803  catch(...)
804  {
805  __SUP_COUT__ << "Error detected!\n\n " << __E__;
806  xmlOut.addTextElementToData("Error", "Error clearing cached views! ");
807  }
808  }
809  else if(requestType == "getGroupHistory")
810  {
811  std::string groupAction = StringMacros::decodeURIComponent(
812  CgiDataUtilities::getData(cgiIn, "groupAction")); // from GET
813  std::string groupType = StringMacros::decodeURIComponent(
814  CgiDataUtilities::getData(cgiIn, "groupType")); // from GET
815 
816  __SUP_COUTV__(groupAction);
817  __SUP_COUTV__(groupType);
818 
819  std::vector<std::map<std::string /* group field key */,
820  std::string /* group field value */>>
822  groupAction, groupType, true /* formatTime */);
823 
824  for(const auto& group : groups)
825  {
826  auto parentEl = xmlOut.addTextElementToData(
827  "GroupHistoryEntry", ""); // create parent element for each entry
828  for(const auto& field : group)
829  xmlOut.addTextElementToParent(field.first, field.second, parentEl);
830  }
831  }
832  else if(requestType == "getTreeView")
833  {
834  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
835  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
836  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
837  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
838  std::string filterList = CgiDataUtilities::postData(cgiIn, "filterList");
839  int depth = CgiDataUtilities::getDataAsInt(cgiIn, "depth");
840  bool hideStatusFalse = CgiDataUtilities::getDataAsInt(cgiIn, "hideStatusFalse");
841  std::string diffGroup = CgiDataUtilities::getData(cgiIn, "diffGroup");
842  std::string diffGroupKey = CgiDataUtilities::getData(cgiIn, "diffGroupKey");
843 
844  __SUP_COUTT__ << "tableGroup: " << tableGroup << __E__;
845  __SUP_COUTT__ << "tableGroupKey: " << tableGroupKey << __E__;
846  __SUP_COUTT__ << "startPath: " << startPath << __E__;
847  __SUP_COUTT__ << "depth: " << depth << __E__;
848  __SUP_COUTT__ << "hideStatusFalse: " << hideStatusFalse << __E__;
849  __SUP_COUTT__ << "modifiedTables: " << modifiedTables << __E__;
850  __SUP_COUTT__ << "filterList: " << filterList << __E__;
851 
852  handleFillTreeViewXML(xmlOut,
853  cfgMgr,
854  tableGroup,
855  TableGroupKey(tableGroupKey),
856  startPath,
857  depth,
858  hideStatusFalse,
859  modifiedTables,
860  filterList,
861  diffGroup,
862  TableGroupKey(diffGroupKey));
863  }
864  else if(requestType == "getTreeNodeCommonFields")
865  {
866  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
867  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
868  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
869  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
870  std::string fieldList = CgiDataUtilities::postData(cgiIn, "fieldList");
871  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
872  int depth = CgiDataUtilities::getDataAsInt(cgiIn, "depth");
873 
874  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
875  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
876  __SUP_COUT__ << "startPath: " << startPath << __E__;
877  __SUP_COUT__ << "depth: " << depth << __E__;
878  if(depth == -1)
879  depth = 10; // protect users who probably do not actually mean -1
880  __SUP_COUT__ << "fieldList: " << fieldList << __E__;
881  __SUP_COUT__ << "recordList: " << recordList << __E__;
882  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
883 
884  handleFillTreeNodeCommonFieldsXML(xmlOut,
885  cfgMgr,
886  tableGroup,
887  TableGroupKey(tableGroupKey),
888  startPath,
889  depth,
890  modifiedTables,
891  recordList,
892  fieldList);
893  }
894  else if(requestType == "getUniqueFieldValuesForRecords")
895  {
896  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
897  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
898  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
899  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
900  std::string fieldList = CgiDataUtilities::postData(cgiIn, "fieldList");
901  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
902 
903  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
904  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
905  __SUP_COUT__ << "startPath: " << startPath << __E__;
906  __SUP_COUT__ << "fieldList: " << fieldList << __E__;
907  __SUP_COUT__ << "recordList: " << recordList << __E__;
908  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
909 
910  handleFillUniqueFieldValuesForRecordsXML(xmlOut,
911  cfgMgr,
912  tableGroup,
913  TableGroupKey(tableGroupKey),
914  startPath,
915  modifiedTables,
916  recordList,
917  fieldList);
918  }
919  else if(requestType == "getTreeNodeFieldValues")
920  {
921  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
922  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
923  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
924  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
925  std::string fieldList = CgiDataUtilities::postData(cgiIn, "fieldList");
926  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
927 
928  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
929  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
930  __SUP_COUT__ << "startPath: " << startPath << __E__;
931  __SUP_COUT__ << "fieldList: " << fieldList << __E__;
932  __SUP_COUT__ << "recordList: " << recordList << __E__;
933  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
934 
935  handleFillGetTreeNodeFieldValuesXML(xmlOut,
936  cfgMgr,
937  tableGroup,
938  TableGroupKey(tableGroupKey),
939  startPath,
940  modifiedTables,
941  recordList,
942  fieldList);
943  }
944  else if(requestType == "setTreeNodeFieldValues")
945  {
946  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
947  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
948  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
949  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
950  std::string fieldList = CgiDataUtilities::postData(cgiIn, "fieldList");
951  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
952  std::string valueList = CgiDataUtilities::postData(cgiIn, "valueList");
953 
954  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
955  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
956  __SUP_COUT__ << "startPath: " << startPath << __E__;
957  __SUP_COUT__ << "fieldList: " << fieldList << __E__;
958  __SUP_COUT__ << "valueList: " << valueList << __E__;
959  __SUP_COUT__ << "recordList: " << recordList << __E__;
960  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
961 
962  handleFillSetTreeNodeFieldValuesXML(xmlOut,
963  cfgMgr,
964  tableGroup,
965  TableGroupKey(tableGroupKey),
966  startPath,
967  modifiedTables,
968  recordList,
969  fieldList,
970  valueList,
971  userInfo.username_);
972  }
973  else if(requestType == "addTreeNodeRecords")
974  {
975  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
976  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
977  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
978  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
979  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
980 
981  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
982  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
983  __SUP_COUT__ << "startPath: " << startPath << __E__;
984  __SUP_COUT__ << "recordList: " << recordList << __E__;
985  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
986 
987  handleFillCreateTreeNodeRecordsXML(xmlOut,
988  cfgMgr,
989  tableGroup,
990  TableGroupKey(tableGroupKey),
991  startPath,
992  modifiedTables,
993  recordList,
994  userInfo.username_);
995  }
996  else if(requestType == "deleteTreeNodeRecords")
997  {
998  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
999  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
1000  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
1001  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
1002  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
1003 
1004  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
1005  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
1006  __SUP_COUT__ << "startPath: " << startPath << __E__;
1007  __SUP_COUT__ << "recordList: " << recordList << __E__;
1008  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
1009 
1010  handleFillDeleteTreeNodeRecordsXML(xmlOut,
1011  cfgMgr,
1012  tableGroup,
1013  TableGroupKey(tableGroupKey),
1014  startPath,
1015  modifiedTables,
1016  recordList);
1017  }
1018  else if(requestType == "renameTreeNodeRecords")
1019  {
1020  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
1021  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
1022  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
1023  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
1024  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
1025  std::string newRecordList = CgiDataUtilities::postData(cgiIn, "newRecordList");
1026 
1027  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
1028  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
1029  __SUP_COUT__ << "startPath: " << startPath << __E__;
1030  __SUP_COUT__ << "recordList: " << recordList << __E__;
1031  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
1032  __SUP_COUTV__(newRecordList);
1033 
1034  handleFillRenameTreeNodeRecordsXML(xmlOut,
1035  cfgMgr,
1036  tableGroup,
1037  TableGroupKey(tableGroupKey),
1038  startPath,
1039  modifiedTables,
1040  recordList,
1041  newRecordList);
1042  }
1043  else if(requestType == "copyTreeNodeRecords")
1044  {
1045  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
1046  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
1047  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
1048  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
1049  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
1050  unsigned int numberOfCopies =
1051  CgiDataUtilities::getDataAsInt(cgiIn, "numberOfCopies");
1052  if(!numberOfCopies)
1053  numberOfCopies = 1; // default to 1
1054 
1055  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
1056  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
1057  __SUP_COUT__ << "startPath: " << startPath << __E__;
1058  __SUP_COUT__ << "recordList: " << recordList << __E__;
1059  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
1060  __SUP_COUTV__(numberOfCopies);
1061 
1062  handleFillCopyTreeNodeRecordsXML(xmlOut,
1063  cfgMgr,
1064  tableGroup,
1065  TableGroupKey(tableGroupKey),
1066  startPath,
1067  modifiedTables,
1068  recordList,
1069  numberOfCopies);
1070  }
1071  else if(requestType == "getTableStructureStatusAsJSON")
1072  {
1073  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
1074  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
1075  std::string tableName = CgiDataUtilities::getData(cgiIn, "tableName");
1076  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
1077 
1078  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
1079  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
1080  __SUP_COUT__ << "tableName: " << tableName << __E__;
1081  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
1082 
1083  // setup active tables based on active groups and modified tables
1084  setupActiveTablesXML(xmlOut,
1085  cfgMgr,
1086  tableGroup,
1087  TableGroupKey(tableGroupKey),
1088  modifiedTables,
1089  false /* refreshAll */);
1090 
1091  try
1092  {
1093  xmlOut.addTextElementToData(
1094  "StructureStatusAsJSON",
1095  cfgMgr->getTableByName(tableName)->getStructureAsJSON(cfgMgr));
1096  }
1097  catch(const std::runtime_error& e)
1098  {
1099  __SUP_SS__ << "The table plugin feature getStructureStatusAsJSON(), does not "
1100  "seem to be supported for the table '"
1101  << tableName
1102  << ".' Make sure you have the expected table plugin in your path, "
1103  "or contact system admins."
1104  << __E__;
1105  ss << "Here is the error: " << e.what() << __E__;
1106  __SUP_SS_THROW__;
1107  }
1108  }
1109  else if(requestType == "getArtdaqNodes")
1110  {
1111  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
1112 
1113  __SUP_COUTV__(modifiedTables);
1114 
1115  handleGetArtdaqNodeRecordsXML(xmlOut, cfgMgr, modifiedTables);
1116  }
1117  else if(requestType == "saveArtdaqNodes")
1118  {
1119  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
1120  std::string nodeString = CgiDataUtilities::postData(cgiIn, "nodeString");
1121  std::string subsystemString =
1122  CgiDataUtilities::postData(cgiIn, "subsystemString");
1123 
1124  __SUP_COUTV__(modifiedTables);
1125  __SUP_COUTV__(nodeString);
1126  __SUP_COUTV__(subsystemString);
1127 
1128  handleSaveArtdaqNodeRecordsXML(
1129  nodeString, subsystemString, xmlOut, cfgMgr, modifiedTables);
1130  }
1131  else if(requestType == "getArtdaqNodeLayout")
1132  {
1133  std::string contextGroupName =
1134  CgiDataUtilities::getData(cgiIn, "contextGroupName");
1135  std::string contextGroupKey = CgiDataUtilities::getData(cgiIn, "contextGroupKey");
1136 
1137  __SUP_COUTV__(contextGroupName);
1138  __SUP_COUTV__(contextGroupKey);
1139 
1140  handleLoadArtdaqNodeLayoutXML(
1141  xmlOut, cfgMgr, contextGroupName, TableGroupKey(contextGroupKey));
1142  }
1143  else if(requestType == "saveArtdaqNodeLayout")
1144  {
1145  std::string layout = CgiDataUtilities::postData(cgiIn, "layout");
1146  std::string contextGroupName =
1147  CgiDataUtilities::getData(cgiIn, "contextGroupName");
1148  std::string contextGroupKey = CgiDataUtilities::getData(cgiIn, "contextGroupKey");
1149 
1150  __SUP_COUTV__(layout);
1151  __SUP_COUTV__(contextGroupName);
1152  __SUP_COUTV__(contextGroupKey);
1153 
1154  handleSaveArtdaqNodeLayoutXML(
1155  xmlOut, cfgMgr, layout, contextGroupName, TableGroupKey(contextGroupKey));
1156  }
1157  else if(requestType == "checkAffectedActiveGroups")
1158  {
1159  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName");
1160  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey");
1161  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
1162  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
1163  __SUP_COUT__ << "groupName: " << groupName << __E__;
1164  __SUP_COUT__ << "groupKey: " << groupKey << __E__;
1165 
1166  handleGetAffectedGroupsXML(
1167  xmlOut, cfgMgr, groupName, TableGroupKey(groupKey), modifiedTables);
1168  }
1169  else if(requestType == "saveTreeNodeEdit")
1170  {
1171  std::string editNodeType = CgiDataUtilities::getData(cgiIn, "editNodeType");
1172  std::string targetTable = CgiDataUtilities::getData(cgiIn, "targetTable");
1173  std::string targetTableVersion =
1174  CgiDataUtilities::getData(cgiIn, "targetTableVersion");
1175  std::string targetUID = CgiDataUtilities::getData(cgiIn, "targetUID");
1176  std::string targetColumn = CgiDataUtilities::getData(cgiIn, "targetColumn");
1177  std::string newValue = CgiDataUtilities::postData(cgiIn, "newValue");
1178 
1179  __SUP_COUT__ << "editNodeType: " << editNodeType << __E__;
1180  __SUP_COUT__ << "targetTable: " << targetTable << __E__;
1181  __SUP_COUT__ << "targetTableVersion: " << targetTableVersion << __E__;
1182  __SUP_COUT__ << "targetUID: " << targetUID << __E__;
1183  __SUP_COUT__ << "targetColumn: " << targetColumn << __E__;
1184  __SUP_COUT__ << "newValue: " << newValue << __E__;
1185 
1186  handleSaveTreeNodeEditXML(xmlOut,
1187  cfgMgr,
1188  targetTable,
1189  TableVersion(targetTableVersion),
1190  editNodeType,
1192  StringMacros::decodeURIComponent(targetColumn),
1193  newValue,
1194  userInfo.username_);
1195  }
1196  else if(requestType == "getLinkToChoices")
1197  {
1198  std::string linkToTableName = CgiDataUtilities::getData(cgiIn, "linkToTableName");
1199  std::string linkToTableVersion =
1200  CgiDataUtilities::getData(cgiIn, "linkToTableVersion");
1201  std::string linkIdType = CgiDataUtilities::getData(cgiIn, "linkIdType");
1202  std::string linkIndex = StringMacros::decodeURIComponent(
1203  CgiDataUtilities::getData(cgiIn, "linkIndex"));
1204  std::string linkInitId = CgiDataUtilities::getData(cgiIn, "linkInitId");
1205 
1206  __SUP_COUT__ << "linkToTableName: " << linkToTableName << __E__;
1207  __SUP_COUT__ << "linkToTableVersion: " << linkToTableVersion << __E__;
1208  __SUP_COUT__ << "linkIdType: " << linkIdType << __E__;
1209  __SUP_COUT__ << "linkIndex: " << linkIndex << __E__;
1210  __SUP_COUT__ << "linkInitId: " << linkInitId << __E__;
1211 
1212  handleGetLinkToChoicesXML(xmlOut,
1213  cfgMgr,
1214  linkToTableName,
1215  TableVersion(linkToTableVersion),
1216  linkIdType,
1217  linkIndex,
1218  linkInitId);
1219  }
1220  else if(requestType == "activateTableGroup")
1221  {
1222  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName");
1223  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey");
1224  bool ignoreWarnings = CgiDataUtilities::getDataAsInt(cgiIn, "ignoreWarnings");
1225 
1226  __SUP_COUT__ << "Activating group: " << groupName << "(" << groupKey << ")"
1227  << __E__;
1228  __SUP_COUTV__(ignoreWarnings);
1229 
1230  // add flag for GUI handling
1231  xmlOut.addTextElementToData("AttemptedGroupActivation", "1");
1232  xmlOut.addTextElementToData("AttemptedGroupActivationName", groupName);
1233  xmlOut.addTextElementToData("AttemptedGroupActivationKey", groupKey);
1234 
1235  try
1236  {
1237  std::string accumulatedErrors, groupTypeString;
1238 
1239  // if ignore warnings,
1240  // then only print errors, do not add to xml
1241 
1242  __COUTTV__(StringMacros::mapToString(cfgMgr->getActiveVersions()));
1243 
1244  cfgMgr->activateTableGroup(
1245  groupName, TableGroupKey(groupKey), &accumulatedErrors, &groupTypeString);
1246 
1247  if(accumulatedErrors != "")
1248  {
1249  if(!ignoreWarnings)
1250  {
1251  __SS__ << "Throwing exception on accumulated errors: "
1252  << accumulatedErrors << __E__;
1253  __SS_ONLY_THROW__;
1254  }
1255  // else just print
1256  __COUT_WARN__ << "Ignoring warnings so ignoring this error:"
1257  << accumulatedErrors << __E__;
1258  __COUT_WARN__ << "Done ignoring the above error(s)." << __E__;
1259  }
1260  xmlOut.addTextElementToData("AttemptedGroupActivationType", groupTypeString);
1261  }
1262  catch(std::runtime_error& e)
1263  {
1264  // NOTE it is critical for flimsy error parsing in JS GUI to leave
1265  // single quotes around the groupName and groupKey and have them be
1266  // the first single quotes encountered in the error mesage!
1267  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
1268  xmlOut.addTextElementToData(
1269  "Error",
1270  "Error activating table group '" + groupName + "(" + groupKey + ")" +
1271  ".' Please see details below:\n\n" + std::string(e.what()));
1272  __SUP_COUT_ERR__ << "Errors detected so de-activating group: " << groupName
1273  << " (" << groupKey << ")" << __E__;
1274  try // just in case any lingering pieces, lets deactivate
1275  {
1276  cfgMgr->destroyTableGroup(groupName, true);
1277  }
1278  catch(...)
1279  {
1280  }
1281  }
1282  catch(cet::exception& e)
1283  {
1284  // NOTE it is critical for flimsy error parsing in JS GUI to leave
1285  // single quotes around the groupName and groupKey and have them be
1286  // the first single quotes encountered in the error mesage!
1287 
1288  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
1289  xmlOut.addTextElementToData("Error",
1290  "Error activating table group '" + groupName +
1291  "(" + groupKey + ")" + "!'\n\n" +
1292  std::string(e.what()));
1293  __SUP_COUT_ERR__ << "Errors detected so de-activating group: " << groupName
1294  << " (" << groupKey << ")" << __E__;
1295  try // just in case any lingering pieces, lets deactivate
1296  {
1297  cfgMgr->destroyTableGroup(groupName, true);
1298  }
1299  catch(...)
1300  {
1301  }
1302  }
1303  catch(...)
1304  {
1305  __SUP_COUT__ << "Unknown error detected!" << __E__;
1306  try // just in case any lingering pieces, lets deactivate
1307  {
1308  cfgMgr->destroyTableGroup(groupName, true);
1309  }
1310  catch(...)
1311  {
1312  }
1313 
1314  throw; // unexpected exception!
1315  }
1316  }
1317  else if(requestType == "getActiveTableGroups")
1318  ; // do nothing, since they are always returned
1319  else if(requestType == "copyViewToCurrentColumns")
1320  {
1321  std::string tableName =
1322  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
1323  std::string sourceVersion = CgiDataUtilities::getData(cgiIn, "sourceVersion");
1324 
1325  __SUP_COUT__ << "tableName: " << tableName << __E__;
1326  __SUP_COUT__ << "sourceVersion: " << sourceVersion << __E__;
1327  __SUP_COUT__ << "userInfo.username_: " << userInfo.username_ << __E__;
1328 
1329  // copy source version to new temporary version
1330  TableVersion newTemporaryVersion;
1331  try
1332  {
1333  newTemporaryVersion =
1334  cfgMgr->copyViewToCurrentColumns(tableName, TableVersion(sourceVersion));
1335 
1336  __SUP_COUT__ << "New temporary version = " << newTemporaryVersion << __E__;
1337  }
1338  catch(std::runtime_error& e)
1339  {
1340  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
1341  xmlOut.addTextElementToData("Error",
1342  "Error copying view from '" + tableName + "_v" +
1343  sourceVersion + "'! " +
1344  std::string(e.what()));
1345  }
1346  catch(...)
1347  {
1348  __SUP_COUT__ << "Error detected!\n\n " << __E__;
1349  xmlOut.addTextElementToData(
1350  "Error",
1351  "Error copying view from '" + tableName + "_v" + sourceVersion + "'! ");
1352  }
1353 
1354  handleGetTableXML(xmlOut, cfgMgr, tableName, newTemporaryVersion);
1355  }
1356  else if(requestType == "getLastTableGroups")
1357  {
1358  // std::string timeString;
1359  std::map<std::string /* group type */,
1360  std::tuple<std::string /*group name*/,
1361  TableGroupKey,
1362  std::string /* time string*/>>
1363  theGroups;
1364 
1365  theRemoteWebUsers_.getLastTableGroups(theGroups);
1366 
1367  for(const auto& theGroup : theGroups)
1368  {
1369  xmlOut.addTextElementToData("Last" + theGroup.first + "GroupName",
1370  std::get<0>(theGroup.second));
1371  xmlOut.addTextElementToData("Last" + theGroup.first + "GroupKey",
1372  std::get<1>(theGroup.second).toString());
1373  xmlOut.addTextElementToData("Last" + theGroup.first + "GroupTime",
1374  std::get<2>(theGroup.second));
1375  }
1376 
1377  // theGroup = theRemoteWebUsers_.getLastTableGroup("Configured", timeString);
1378  // xmlOut.addTextElementToData("LastConfiguredGroupName", theGroup.first);
1379  // xmlOut.addTextElementToData("LastConfiguredGroupKey", theGroup.second.toString());
1380  // xmlOut.addTextElementToData("LastConfiguredGroupTime", timeString);
1381  // theGroup = theRemoteWebUsers_.getLastTableGroup("Started", timeString);
1382  // xmlOut.addTextElementToData("LastStartedGroupName", theGroup.first);
1383  // xmlOut.addTextElementToData("LastStartedGroupKey", theGroup.second.toString());
1384  // xmlOut.addTextElementToData("LastStartedGroupTime", timeString);
1385  // theGroup = theRemoteWebUsers_.getLastTableGroup("ActivatedConfig", timeString);
1386  // xmlOut.addTextElementToData("LastActivatedConfigGroupName", theGroup.first);
1387  // xmlOut.addTextElementToData("LastActivatedConfigGroupKey",
1388  // theGroup.second.toString());
1389  // xmlOut.addTextElementToData("LastActivatedConfigGroupTime", timeString);
1390  // theGroup = theRemoteWebUsers_.getLastTableGroup("ActivatedContext", timeString);
1391  // xmlOut.addTextElementToData("LastActivatedContextGroupName", theGroup.first);
1392  // xmlOut.addTextElementToData("LastActivatedContextGroupKey",
1393  // theGroup.second.toString());
1394  // xmlOut.addTextElementToData("LastActivatedContextGroupTime", timeString);
1395  // theGroup = theRemoteWebUsers_.getLastTableGroup("ActivatedBackbone", timeString);
1396  // xmlOut.addTextElementToData("LastActivatedBackboneGroupName", theGroup.first);
1397  // xmlOut.addTextElementToData("LastActivatedBackboneGroupKey",
1398  // theGroup.second.toString());
1399  // xmlOut.addTextElementToData("LastActivatedBackboneGroupTime", timeString);
1400  // theGroup = theRemoteWebUsers_.getLastTableGroup("ActivatedIterator", timeString);
1401  // xmlOut.addTextElementToData("LastActivatedIteratorGroupName", theGroup.first);
1402  // xmlOut.addTextElementToData("LastActivatedIteratorGroupKey",
1403  // theGroup.second.toString());
1404  // xmlOut.addTextElementToData("LastActivatedIteratorGroupTime", timeString);
1405 
1406  //check other subsystems active groups
1407  handleOtherSubsystemActiveGroups(xmlOut, cfgMgr, false /* getFullList */);
1408  }
1409  else if(requestType == "getSubsytemTableGroups")
1410  {
1411  std::string subsystem =
1412  CgiDataUtilities::getData(cgiIn, "subsystem"); // from GET
1413  __SUP_COUTV__(subsystem);
1414  handleOtherSubsystemActiveGroups(
1415  xmlOut, cfgMgr, true /* getFullList */, subsystem);
1416  } //end getSubsytemTableGroups
1417  else if(requestType == "diffWithActiveGroup")
1418  {
1419  std::string groupName =
1420  CgiDataUtilities::getData(cgiIn, "groupName"); // from GET
1421  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey"); // from GET
1422  __SUP_COUTV__(groupName);
1423  __SUP_COUTV__(groupKey);
1424 
1425  handleGroupDiff(
1426  xmlOut, cfgMgr, groupName, TableGroupKey(groupKey)); //diff with active group
1427  } //end diffWithActiveGroup
1428  else if(requestType == "diffWithGroupKey")
1429  {
1430  std::string groupName =
1431  CgiDataUtilities::getData(cgiIn, "groupName"); // from GET
1432  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey"); // from GET
1433  std::string diffKey = CgiDataUtilities::getData(cgiIn, "diffKey"); // from GET
1434  std::string diffGroupName =
1435  CgiDataUtilities::getData(cgiIn, "diffGroupName"); // from GET
1436  __SUP_COUTV__(groupName);
1437  __SUP_COUTV__(groupKey);
1438  __SUP_COUTV__(diffKey);
1439  __SUP_COUTV__(diffGroupName);
1440 
1441  handleGroupDiff(xmlOut,
1442  cfgMgr,
1443  groupName,
1444  TableGroupKey(groupKey),
1445  TableGroupKey(diffKey),
1446  diffGroupName);
1447  } //end diffWithGroupKey
1448  else if(requestType == "diffTableVersions")
1449  {
1450  std::string tableName =
1451  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
1452  std::string vA = CgiDataUtilities::getData(cgiIn, "vA"); // from GET
1453  std::string vB = CgiDataUtilities::getData(cgiIn, "vB"); // from GET
1454  __SUP_COUTV__(tableName);
1455  __SUP_COUTV__(vA);
1456  __SUP_COUTV__(vB);
1457 
1458  TableVersion versionA, versionB;
1459  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
1460 
1461  //convert aliases if specified
1462  if(allTableInfo.find(tableName) != allTableInfo.end())
1463  {
1464  if(vA.find(ConfigurationManager::ALIAS_VERSION_PREAMBLE) == 0)
1465  {
1466  // convert alias to version
1467  std::map<std::string /*table*/,
1468  std::map<std::string /*alias*/, TableVersion>>
1469  versionAliases = cfgMgr->getVersionAliases();
1470 
1471  std::string versionAlias;
1472  versionAlias =
1473  vA.substr(ConfigurationManager::ALIAS_VERSION_PREAMBLE.size());
1474 
1475  if(versionAliases.find(tableName) != versionAliases.end() &&
1476  versionAliases[tableName].find(versionAlias) !=
1477  versionAliases[tableName].end())
1478  {
1479  versionA = versionAliases[tableName][versionAlias];
1480  __SUP_COUT__ << "version alias translated to: " << versionA << __E__;
1481  }
1482  else
1483  __SUP_COUT_WARN__ << "version alias '" << versionAlias
1484  << "'was not found in active version aliases!"
1485  << __E__;
1486  }
1487  else // else take specified version
1488  versionA = atoi(vA.c_str());
1489 
1490  if(vB.find(ConfigurationManager::ALIAS_VERSION_PREAMBLE) == 0)
1491  {
1492  // convert alias to version
1493  std::map<std::string /*table*/,
1494  std::map<std::string /*alias*/, TableVersion>>
1495  versionAliases = cfgMgr->getVersionAliases();
1496 
1497  std::string versionAlias;
1498  versionAlias =
1499  vB.substr(ConfigurationManager::ALIAS_VERSION_PREAMBLE.size());
1500 
1501  if(versionAliases.find(tableName) != versionAliases.end() &&
1502  versionAliases[tableName].find(versionAlias) !=
1503  versionAliases[tableName].end())
1504  {
1505  versionB = versionAliases[tableName][versionAlias];
1506  __SUP_COUT__ << "version alias translated to: " << versionB << __E__;
1507  }
1508  else
1509  __SUP_COUT_WARN__ << "version alias '" << versionAlias
1510  << "'was not found in active version aliases!"
1511  << __E__;
1512  }
1513  else // else take specified version
1514  versionB = atoi(vB.c_str());
1515  }
1516  else
1517  {
1518  versionA = atoi(vA.c_str());
1519  versionB = atoi(vB.c_str());
1520  }
1521 
1522  __SUP_COUTV__(versionA);
1523  __SUP_COUTV__(versionB);
1524 
1525  handleTableDiff(xmlOut, cfgMgr, tableName, versionA, versionB);
1526  } //end diffTableVersions
1527  else if(requestType == "findGroupsWithTable")
1528  {
1529  std::string tableName =
1530  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
1531  std::string tableVersion =
1532  CgiDataUtilities::getData(cgiIn, "tableVersion"); // from GET
1533 
1534  __SUP_COUTV__(tableName);
1535  __SUP_COUTV__(tableVersion);
1536 
1537  std::set<std::string> groupsContainingTable =
1538  cfgMgr->getConfigurationInterface()->findGroupsWithTable(
1539  tableName, TableVersion(tableVersion));
1540  __SUP_COUT__ << "Groups containing " << tableName << "-v" << tableVersion
1541  << " count: " << groupsContainingTable.size() << __E__;
1542  std::string groupsContainingTableString =
1543  StringMacros::setToString(groupsContainingTable);
1544  __SUP_COUTTV__(groupsContainingTableString);
1545 
1546  xmlOut.addNumberElementToData("GroupsContainingCount",
1547  groupsContainingTable.size());
1548  xmlOut.addTextElementToData("GroupsContainingCSV", groupsContainingTableString);
1549 
1550  } //end findGroupsWithTable
1551  else if(requestType == "savePlanCommandSequence")
1552  {
1553  std::string planName = CgiDataUtilities::getData(cgiIn, "planName"); // from GET
1554  std::string commands =
1555  CgiDataUtilities::postData(cgiIn, "commands"); // from POST
1556  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
1557  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName");
1558  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey");
1559 
1560  __SUP_COUTV__(modifiedTables);
1561  __SUP_COUTV__(planName);
1562  __SUP_COUTV__(commands);
1563  __SUP_COUTV__(groupName);
1564  __SUP_COUTV__(groupKey);
1565 
1566  handleSavePlanCommandSequenceXML(xmlOut,
1567  cfgMgr,
1568  groupName,
1569  TableGroupKey(groupKey),
1570  modifiedTables,
1571  userInfo.username_,
1572  planName,
1573  commands);
1574  }
1575  else if(requestType == "mergeGroups")
1576  {
1577  std::string groupANameContext =
1578  CgiDataUtilities::getData(cgiIn, "groupANameContext");
1579  std::string groupAKeyContext =
1580  CgiDataUtilities::getData(cgiIn, "groupAKeyContext");
1581  std::string groupBNameContext =
1582  CgiDataUtilities::getData(cgiIn, "groupBNameContext");
1583  std::string groupBKeyContext =
1584  CgiDataUtilities::getData(cgiIn, "groupBKeyContext");
1585  std::string groupANameConfig =
1586  CgiDataUtilities::getData(cgiIn, "groupANameConfig");
1587  std::string groupAKeyConfig = CgiDataUtilities::getData(cgiIn, "groupAKeyConfig");
1588  std::string groupBNameConfig =
1589  CgiDataUtilities::getData(cgiIn, "groupBNameConfig");
1590  std::string groupBKeyConfig = CgiDataUtilities::getData(cgiIn, "groupBKeyConfig");
1591  std::string mergeApproach = CgiDataUtilities::getData(cgiIn, "mergeApproach");
1592 
1593  __SUP_COUTV__(groupANameContext);
1594  __SUP_COUTV__(groupAKeyContext);
1595  __SUP_COUTV__(groupBNameContext);
1596  __SUP_COUTV__(groupBKeyContext);
1597  __SUP_COUTV__(groupANameConfig);
1598  __SUP_COUTV__(groupAKeyConfig);
1599  __SUP_COUTV__(groupBNameConfig);
1600  __SUP_COUTV__(groupBKeyConfig);
1601  __SUP_COUTV__(mergeApproach);
1602 
1603  handleMergeGroupsXML(xmlOut,
1604  cfgMgr,
1605  groupANameContext,
1606  TableGroupKey(groupAKeyContext),
1607  groupBNameContext,
1608  TableGroupKey(groupBKeyContext),
1609  groupANameConfig,
1610  TableGroupKey(groupAKeyConfig),
1611  groupBNameConfig,
1612  TableGroupKey(groupBKeyConfig),
1613  userInfo.username_,
1614  mergeApproach);
1615  }
1616  else
1617  {
1618  __SUP_SS__ << "requestType Request, " << requestType
1619  << ", not recognized by the Configuration GUI Supervisor (was it "
1620  "intended for another Supervisor?)."
1621  << __E__;
1622  __SUP_COUT__ << "\n" << ss.str();
1623  xmlOut.addTextElementToData("Error", ss.str());
1624  }
1625 
1626  __SUP_COUTT__ << "cfgMgr runtime=" << cfgMgr->runTimeSeconds() << __E__;
1627  // always add active table groups to xml response
1629  xmlOut, cfgMgr, userInfo.username_);
1630  __SUP_COUTT__ << "cfgMgr runtime=" << cfgMgr->runTimeSeconds() << __E__;
1631 
1632 } // end ::request()
1633 catch(const std::runtime_error& e)
1634 {
1635  __SS__ << "A fatal error occurred while handling the request '" << requestType
1636  << ".' Error: " << e.what() << __E__;
1637  __COUT_ERR__ << "\n" << ss.str();
1638  xmlOut.addTextElementToData("Error", ss.str());
1639 
1640  try
1641  {
1642  // always add version tracking bool
1643  xmlOut.addTextElementToData(
1644  "versionTracking",
1645  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
1646  }
1647  catch(...)
1648  {
1649  __COUT_ERR__ << "Error getting version tracking status!" << __E__;
1650  }
1651 } // end ::request() catch
1652 catch(...)
1653 {
1654  __SS__ << "An unknown fatal error occurred while handling the request '"
1655  << requestType << ".'" << __E__;
1656  try
1657  {
1658  throw;
1659  } //one more try to printout extra info
1660  catch(const std::exception& e)
1661  {
1662  ss << "Exception message: " << e.what();
1663  }
1664  catch(...)
1665  {
1666  }
1667  __COUT_ERR__ << "\n" << ss.str();
1668  xmlOut.addTextElementToData("Error", ss.str());
1669 
1670  try
1671  {
1672  // always add version tracking bool
1673  xmlOut.addTextElementToData(
1674  "versionTracking",
1675  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
1676  }
1677  catch(...)
1678  {
1679  __COUT_ERR__ << "Error getting version tracking status!" << __E__;
1680  }
1681 
1682 } // end ::request() catch
1683 
1684 //==============================================================================
1699 void ConfigurationGUISupervisor::handleGetAffectedGroupsXML(
1700  HttpXmlDocument& xmlOut,
1701  ConfigurationManagerRW* cfgMgr,
1702  const std::string& rootGroupName,
1703  const TableGroupKey& rootGroupKey,
1704  const std::string& modifiedTables)
1705 try
1706 {
1707  __SUP_COUT__ << "rootGroupName " << rootGroupName << "(" << rootGroupKey
1708  << "). modifiedTables = " << modifiedTables << __E__;
1709 
1710  std::map<std::string, std::pair<std::string, TableGroupKey>> consideredGroups =
1711  cfgMgr->getActiveTableGroups();
1712 
1713  // check that there is a context and table group to consider
1714  // if there is not, then pull from failed list
1715  if(consideredGroups[ConfigurationManager::GROUP_TYPE_NAME_CONTEXT].second.isInvalid())
1716  {
1717  __SUP_COUT__ << "Finding a context group to consider..." << __E__;
1718  if(cfgMgr->getFailedTableGroups().find(
1719  ConfigurationManager::GROUP_TYPE_NAME_CONTEXT) !=
1720  cfgMgr->getFailedTableGroups().end())
1721  {
1722  consideredGroups[ConfigurationManager::GROUP_TYPE_NAME_CONTEXT] =
1723  cfgMgr->getFailedTableGroups().at(
1724  ConfigurationManager::GROUP_TYPE_NAME_CONTEXT);
1725  }
1726  else if(cfgMgr->getFailedTableGroups().find(
1727  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN) !=
1728  cfgMgr->getFailedTableGroups().end())
1729  {
1730  consideredGroups[ConfigurationManager::GROUP_TYPE_NAME_CONTEXT] =
1731  cfgMgr->getFailedTableGroups().at(
1732  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN);
1733  }
1734  }
1735  if(consideredGroups[ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION]
1736  .second.isInvalid())
1737  {
1738  __SUP_COUT__ << "Finding a table group to consider..." << __E__;
1739  if(cfgMgr->getFailedTableGroups().find(
1740  ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION) !=
1741  cfgMgr->getFailedTableGroups().end())
1742  {
1743  consideredGroups[ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION] =
1744  cfgMgr->getFailedTableGroups().at(
1745  ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION);
1746  }
1747  else if(cfgMgr->getFailedTableGroups().find(
1748  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN) !=
1749  cfgMgr->getFailedTableGroups().end())
1750  {
1751  consideredGroups[ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION] =
1752  cfgMgr->getFailedTableGroups().at(
1753  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN);
1754  }
1755  }
1756 
1757  __SUP_COUTV__(StringMacros::mapToString(consideredGroups));
1758 
1759  // determine the type of table group
1760  try
1761  {
1762  std::map<std::string /*name*/, TableVersion /*version*/> rootGroupMemberMap;
1763 
1764  cfgMgr->loadTableGroup(rootGroupName,
1765  rootGroupKey,
1766  0,
1767  &rootGroupMemberMap,
1768  0,
1769  0,
1770  0,
1771  0,
1772  0, // defaults
1773  true); // doNotLoadMember
1774 
1775  const std::string& groupType = cfgMgr->getTypeNameOfGroup(rootGroupMemberMap);
1776 
1777  consideredGroups[groupType] =
1778  std::pair<std::string, TableGroupKey>(rootGroupName, rootGroupKey);
1779  }
1780  catch(const std::runtime_error& e)
1781  {
1782  // if actual group name was attempted re-throw
1783  if(rootGroupName.size())
1784  {
1785  __SUP_SS__ << "Failed to determine type of table group for " << rootGroupName
1786  << "(" << rootGroupKey << ")! " << e.what() << __E__;
1787  __SUP_COUT_ERR__ << "\n" << ss.str();
1788  //__SS_THROW__;
1789  }
1790 
1791  // else assume it was the intention to just consider the active groups
1792  __SUP_COUT__ << "Did not modify considered active groups due to empty root group "
1793  "name - assuming this was intentional."
1794  << __E__;
1795  }
1796  catch(...)
1797  {
1798  // if actual group name was attempted re-throw
1799  if(rootGroupName.size())
1800  {
1801  __SUP_COUT_ERR__ << "Failed to determine type of table group for "
1802  << rootGroupName << "(" << rootGroupKey << ")!" << __E__;
1803  // throw;
1804  }
1805 
1806  // else assume it was the intention to just consider the active groups
1807  __SUP_COUT__ << "Did not modify considered active groups due to empty root group "
1808  "name - assuming this was intentional."
1809  << __E__;
1810  }
1811 
1812  std::map<std::string /*name*/,
1813  std::pair<bool /*foundAffectedGroup*/, TableVersion /*version*/>>
1814  modifiedTablesMap;
1815  std::map<std::string /*name*/,
1816  std::pair<bool /*foundAffectedGroup*/, TableVersion /*version*/>>::iterator
1817  modifiedTablesMapIt;
1818  {
1819  std::istringstream f(modifiedTables);
1820  std::string table, version;
1821  while(getline(f, table, ','))
1822  {
1823  getline(f, version, ',');
1824  modifiedTablesMap.insert(
1825  std::pair<
1826  std::string /*name*/,
1827  std::pair<bool /*foundAffectedGroup*/, TableVersion /*version*/>>(
1828  table,
1829  std::make_pair(false /*foundAffectedGroup*/, TableVersion(version))));
1830  }
1831  __SUP_COUT__ << modifiedTables << __E__;
1832  for(auto& pair : modifiedTablesMap)
1833  __SUP_COUT__ << "modified table " << pair.first << ":" << pair.second.second
1834  << __E__;
1835  }
1836 
1837  bool affected;
1838  xercesc::DOMElement* parentEl = nullptr;
1839  std::string groupComment;
1840  std::vector<std::string> orderedGroupTypes(
1841  {ConfigurationManager::GROUP_TYPE_NAME_CONTEXT,
1842  ConfigurationManager::GROUP_TYPE_NAME_BACKBONE,
1843  ConfigurationManager::GROUP_TYPE_NAME_ITERATE,
1844  ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION});
1845  for(auto groupType : orderedGroupTypes)
1846  {
1847  if(consideredGroups.find(groupType) == consideredGroups.end())
1848  continue; // skip missing
1849 
1850  const std::pair<std::string, TableGroupKey>& group = consideredGroups[groupType];
1851 
1852  if(group.second.isInvalid())
1853  continue; // skip invalid
1854 
1855  __SUP_COUT__ << "Considering " << groupType << " group " << group.first << " ("
1856  << group.second << ")" << __E__;
1857 
1858  affected = false;
1859  parentEl = nullptr;
1860 
1861  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
1862  cfgMgr->loadTableGroup(group.first,
1863  group.second,
1864  0,
1865  &memberMap,
1866  0,
1867  0,
1868  &groupComment,
1869  0,
1870  0, // mostly defaults
1871  true /*doNotLoadMember*/);
1872 
1873  __SUP_COUTV__(StringMacros::mapToString(memberMap));
1874  __SUP_COUT__ << "groupComment = " << groupComment << __E__;
1875 
1876  for(auto& table : memberMap)
1877  {
1878  if((modifiedTablesMapIt = modifiedTablesMap.find(table.first)) !=
1879  modifiedTablesMap
1880  .end() && // check if version is different for member table
1881  table.second != (*modifiedTablesMapIt).second.second)
1882  {
1883  __SUP_COUT__ << "Affected by " << (*modifiedTablesMapIt).first << ":"
1884  << (*modifiedTablesMapIt).second.second << __E__;
1885 
1886  memberMap[table.first] = (*modifiedTablesMapIt).second.second;
1887  (*modifiedTablesMapIt).second.first = true; // found affected group
1888 
1889  affected = true;
1890  if(!parentEl)
1891  parentEl = xmlOut.addTextElementToData("AffectedActiveGroup", "");
1892  }
1893  }
1894 
1895  if(groupType == ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION)
1896  {
1897  __SUP_COUT__ << "Considering mockup tables for Configuration Group..."
1898  << __E__;
1899  for(auto& table : modifiedTablesMap)
1900  {
1901  if(table.second.first) // already found affected group
1902  continue;
1903 
1904  if(table.second.second.isMockupVersion() &&
1905  memberMap.find(table.first) == memberMap.end())
1906  {
1907  __SUP_COUT__ << "Found mockup table '" << table.first
1908  << "' for Configuration Group." << __E__;
1909  memberMap[table.first] = table.second.second;
1910 
1911  if(!parentEl)
1912  parentEl = xmlOut.addTextElementToData("AffectedActiveGroup", "");
1913  //indicate to client this table needs to be added to group!
1914  xmlOut.addTextElementToParent("AddMemberName", table.first, parentEl);
1915  xmlOut.addTextElementToParent(
1916  "AddMemberVersion", table.second.second.toString(), parentEl);
1917 
1918  affected = true;
1919  }
1920  }
1921  }
1922 
1923  __SUP_COUTV__(affected);
1924  if(affected)
1925  {
1926  if(!parentEl)
1927  parentEl = xmlOut.addTextElementToData("AffectedActiveGroup", "");
1928  xmlOut.addTextElementToParent("GroupName", group.first, parentEl);
1929  xmlOut.addTextElementToParent("GroupKey", group.second.toString(), parentEl);
1930  xmlOut.addTextElementToParent("GroupComment", groupComment, parentEl);
1931 
1932  for(auto& table : memberMap)
1933  {
1934  xmlOut.addTextElementToParent("MemberName", table.first, parentEl);
1935  xmlOut.addTextElementToParent(
1936  "MemberVersion", table.second.toString(), parentEl);
1937  }
1938  }
1939  } // end affected group loop
1940 }
1941 catch(std::runtime_error& e)
1942 {
1943  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
1944  xmlOut.addTextElementToData(
1945  "Error", "Error getting affected groups! " + std::string(e.what()));
1946 }
1947 catch(...)
1948 {
1949  __SUP_COUT__ << "Error detected!\n\n " << __E__;
1950  xmlOut.addTextElementToData("Error", "Error getting affected groups! ");
1951 }
1952 
1953 //==============================================================================
1961 void ConfigurationGUISupervisor::setupActiveTablesXML(
1962  HttpXmlDocument& xmlOut,
1963  ConfigurationManagerRW* cfgMgr,
1964  const std::string& groupName,
1965  const TableGroupKey& groupKey,
1966  const std::string& modifiedTables,
1967  bool refreshAll,
1968  bool doGetGroupInfo,
1969  std::map<std::string /*name*/, TableVersion /*version*/>* returnMemberMap,
1970  bool outputActiveTables,
1971  std::string* accumulatedErrors)
1972 try
1973 {
1974  xmlOut.addTextElementToData("tableGroup", groupName);
1975  xmlOut.addTextElementToData("tableGroupKey", groupKey.toString());
1976 
1977  bool usingActiveGroups = (groupName == "" || groupKey.isInvalid());
1978 
1979  __SUP_COUTTV__(StringMacros::mapToString(cfgMgr->getActiveVersions()));
1980 
1981  // reload all tables so that partially loaded tables are not allowed
1982  if( //usingActiveGroups ||
1983  refreshAll)
1984  {
1985  __SUP_COUT__ << "Refreshing all table info, ignoring warnings..." << __E__;
1986  std::string accumulatedWarnings = "";
1987  cfgMgr->getAllTableInfo(true /* refresh */,
1988  &accumulatedWarnings,
1989  "" /* errorFilterName */,
1990  false /* getGroupKeys */,
1991  false /* getGroupInfo */,
1992  true /* initializeActiveGroups */);
1993  }
1994  else //make sure expected active tables are setup, for standard starting point
1995  {
1996  //This is needed, for example, when on Context group which could point into the
1997  // Configure group; need common starting point (which is the Active group tables)
1998  // Then bring the modified tables over top.
1999  // Context --> Configure is simplest example
2000  // ...but Configure can also point at Context, or Iterator, or Backbone.
2001 
2002  __SUP_COUT__ << "Restoring active table group tables..." << __E__;
2003  auto activeGroups = cfgMgr->getActiveTableGroups();
2004  for(const auto& activeGroup : activeGroups)
2005  {
2006  if(activeGroup.second.first == groupName &&
2007  activeGroup.second.second == groupKey)
2008  {
2009  __SUP_COUTT__ << "Skipping target active group." << __E__;
2010  continue;
2011  }
2012  __SUP_COUTT__ << "Loading " << activeGroup.first << " "
2013  << activeGroup.second.first << "(" << activeGroup.second.second
2014  << ")..." << __E__;
2015  try
2016  {
2017  cfgMgr->loadTableGroup(activeGroup.second.first,
2018  activeGroup.second.second,
2019  false /*doActivate*/
2020  );
2021  }
2022  catch(...) //ignore errors
2023  {
2024  __SUP_COUT__ << "Ignoring errors while setting up active tables for "
2025  << activeGroup.second.first << "("
2026  << activeGroup.second.second << ")..." << __E__;
2027  }
2028  } //end load tables as active, but do not activate groups
2029  }
2030  __SUP_COUTTV__(StringMacros::mapToString(cfgMgr->getActiveVersions()));
2031 
2032  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
2033 
2034  std::map<std::string /*name*/, TableVersion /*version*/> modifiedTablesMap;
2035  std::map<std::string /*name*/, TableVersion /*version*/>::iterator
2036  modifiedTablesMapIt;
2037 
2038  if(usingActiveGroups)
2039  {
2040  // no need to load a target group
2041  __SUP_COUT__ << "Using active groups." << __E__;
2042  }
2043  else
2044  {
2045  __SUP_COUT__ << "Loading group '" << groupName << "(" << groupKey << ")'"
2046  << __E__;
2047 
2048  std::string groupComment, groupAuthor, tableGroupCreationTime, groupType;
2049 
2050  // only same member map if object pointer was passed
2051  cfgMgr->loadTableGroup(groupName,
2052  groupKey,
2053  false /*doActivate*/,
2054  returnMemberMap,
2055  0 /*progressBar*/,
2056  accumulatedErrors,
2057  doGetGroupInfo ? &groupComment : 0,
2058  doGetGroupInfo ? &groupAuthor : 0,
2059  doGetGroupInfo ? &tableGroupCreationTime : 0,
2060  false /*doNotLoadMembers*/,
2061  doGetGroupInfo ? &groupType : 0);
2062 
2063  if(doGetGroupInfo)
2064  {
2065  xmlOut.addTextElementToData("tableGroupComment", groupComment);
2066  xmlOut.addTextElementToData("tableGroupAuthor", groupAuthor);
2067  xmlOut.addTextElementToData("tableGroupCreationTime", tableGroupCreationTime);
2068  xmlOut.addTextElementToData("tableGroupType", groupType);
2069  }
2070 
2071  if(accumulatedErrors && *accumulatedErrors != "")
2072  __SUP_COUTV__(*accumulatedErrors);
2073  }
2074  __SUP_COUTTV__(StringMacros::mapToString(cfgMgr->getActiveVersions()));
2075 
2076  // extract modified tables
2077  {
2078  std::istringstream f(modifiedTables);
2079  std::string table, version;
2080  while(getline(f, table, ','))
2081  {
2082  getline(f, version, ',');
2083  modifiedTablesMap.insert(
2084  std::pair<std::string /*name*/, TableVersion /*version*/>(
2085  table, TableVersion(version)));
2086  }
2087  //__SUP_COUT__ << modifiedTables << __E__;
2088  for(auto& pair : modifiedTablesMap)
2089  __SUP_COUT__ << "modified table " << pair.first << ":" << pair.second
2090  << __E__;
2091  }
2092 
2093  // add all active table pairs to xmlOut
2094  std::map<std::string, TableVersion> allActivePairs = cfgMgr->getActiveVersions();
2095  xmlOut.addTextElementToData("DefaultNoLink",
2096  TableViewColumnInfo::DATATYPE_LINK_DEFAULT);
2097 
2098  // construct specially ordered table name set
2099  std::set<std::string, StringMacros::IgnoreCaseCompareStruct> orderedTableSet;
2100  for(const auto& tablePair : allActivePairs)
2101  orderedTableSet.emplace(tablePair.first);
2102 
2103  std::map<std::string, TableInfo>::const_iterator tableInfoIt;
2104  for(auto& orderedTableName : orderedTableSet)
2105  {
2106  tableInfoIt = allTableInfo.find(orderedTableName);
2107  if(tableInfoIt == allTableInfo.end())
2108  {
2109  __SS__ << "Impossible missing table in map '" << orderedTableName << "'"
2110  << __E__;
2111  __SS_THROW__;
2112  }
2113 
2114  if(outputActiveTables)
2115  xmlOut.addTextElementToData("ActiveTableName", orderedTableName);
2116 
2117  // check if name is in modifiedTables
2118  // if so, activate the temporary version
2119  if((modifiedTablesMapIt = modifiedTablesMap.find(orderedTableName)) !=
2120  modifiedTablesMap.end())
2121  {
2122  __SUP_COUT__ << "Found modified table " << (*modifiedTablesMapIt).first
2123  << ": trying... " << (*modifiedTablesMapIt).second << __E__;
2124 
2125  try
2126  {
2127  tableInfoIt->second.tablePtr_->setActiveView(
2128  (*modifiedTablesMapIt).second);
2129  }
2130  catch(...)
2131  {
2132  __SUP_SS__ << "Modified table version v" << (*modifiedTablesMapIt).second
2133  << " failed. Reverting to v"
2134  << tableInfoIt->second.tablePtr_->getView().getVersion() << "."
2135  << __E__;
2136  __SUP_COUT_WARN__ << "Warning detected!\n\n " << ss.str() << __E__;
2137  xmlOut.addTextElementToData(
2138  "Warning",
2139  "Error setting up active tables!\n\n" + std::string(ss.str()));
2140  }
2141  }
2142 
2143  if(outputActiveTables)
2144  {
2145  xmlOut.addTextElementToData(
2146  "ActiveTableVersion",
2147  tableInfoIt->second.tablePtr_->getView().getVersion().toString());
2148  xmlOut.addTextElementToData(
2149  "ActiveTableComment",
2150  tableInfoIt->second.tablePtr_->getView().getAuthor() + ": " +
2151  tableInfoIt->second.tablePtr_->getView().getComment());
2152  }
2153 
2154  } // end ordered table loop
2155 
2156  __SUP_COUTTV__(StringMacros::mapToString(cfgMgr->getActiveVersions()));
2157 
2158 } // end setupActiveTablesXML()
2159 catch(std::runtime_error& e)
2160 {
2161  __SUP_SS__ << ("Error setting up active tables!\n\n" + std::string(e.what()))
2162  << __E__;
2163  __SUP_COUT_ERR__ << "\n" << ss.str();
2164  xmlOut.addTextElementToData("Error", ss.str());
2165  throw; // throw to get info from special errors at a parent level
2166 }
2167 catch(...)
2168 {
2169  __SUP_SS__ << ("Error setting up active tables!\n\n") << __E__;
2170  try
2171  {
2172  throw;
2173  } //one more try to printout extra info
2174  catch(const std::exception& e)
2175  {
2176  ss << "Exception message: " << e.what();
2177  }
2178  catch(...)
2179  {
2180  }
2181  __SUP_COUT_ERR__ << "\n" << ss.str();
2182  xmlOut.addTextElementToData("Error", ss.str());
2183  throw; // throw to get info from special errors at a parent level
2184 } // end setupActiveTablesXML() throw
2185 
2186 //==============================================================================
2201 void ConfigurationGUISupervisor::handleFillCreateTreeNodeRecordsXML(
2202  HttpXmlDocument& xmlOut,
2203  ConfigurationManagerRW* cfgMgr,
2204  const std::string& groupName,
2205  const TableGroupKey& groupKey,
2206  const std::string& startPath,
2207  const std::string& modifiedTables,
2208  const std::string& recordList,
2209  const std::string& author)
2210 {
2211  // setup active tables based on input group and modified tables
2212  setupActiveTablesXML(xmlOut,
2213  cfgMgr,
2214  groupName,
2215  groupKey,
2216  modifiedTables,
2217  true /* refresh all */,
2218  false /* getGroupInfo */,
2219  0 /* returnMemberMap */,
2220  false /* outputActiveTables */);
2221 
2222  try
2223  {
2224  ConfigurationTree targetNode = cfgMgr->getNode(startPath);
2225  TableBase* table = cfgMgr->getTableByName(targetNode.getTableName());
2226 
2227  __SUP_COUT__ << table->getTableName() << __E__;
2228  TableVersion temporaryVersion;
2229 
2230  // if current version is not temporary
2231  // create temporary
2232  // else re-modify temporary version
2233  // edit temporary version directly
2234  // then after all edits return active versions
2235  //
2236 
2237  bool firstSave = true;
2238 
2239  // save current version
2240  TableView backupView(targetNode.getTableName());
2241 
2242  // extract record list
2243  {
2244  std::istringstream f(recordList);
2245  std::string recordUID;
2246 
2247  while(getline(f, recordUID, ',')) // for each record
2248  {
2249  recordUID = StringMacros::decodeURIComponent(recordUID);
2250 
2251  __SUP_COUT__ << "recordUID " << recordUID << __E__;
2252 
2253  if(firstSave) // handle version bookkeeping
2254  {
2255  if(!(temporaryVersion = targetNode.getTableVersion())
2256  .isTemporaryVersion())
2257  {
2258  __SUP_COUT__ << "Start version " << temporaryVersion << __E__;
2259  // create temporary version for editing
2260  temporaryVersion = table->createTemporaryView(temporaryVersion);
2261  cfgMgr->saveNewTable(
2262  targetNode.getTableName(),
2263  temporaryVersion,
2264  true); // proper bookkeeping for temporary version with the new version
2265 
2266  __SUP_COUT__ << "Created temporary version " << temporaryVersion
2267  << __E__;
2268  }
2269  else // else table is already temporary version
2270  __SUP_COUT__ << "Using temporary version " << temporaryVersion
2271  << __E__;
2272 
2273  firstSave = false;
2274 
2275  // copy original to backup before modifying
2276  backupView.copy(table->getView(), temporaryVersion, author);
2277  }
2278 
2279  // at this point have valid temporary version to edit
2280 
2281  // copy "table-newRow" type edit from handleSaveTreeNodeEditXML()
2282  // functionality
2283 
2284  // add row
2285  unsigned int row = table->getViewP()->addRow(
2286  author,
2287  true /*incrementUniqueData*/); // increment all unique data fields to void conflict
2288 
2289  // if TableViewColumnInfo::COL_NAME_STATUS exists, set it to true
2290  try
2291  {
2292  unsigned int col = table->getViewP()->getColStatus();
2293  table->getViewP()->setURIEncodedValue("1", row, col);
2294  }
2295  catch(...)
2296  {
2297  } // if not, ignore
2298 
2299  // set UID value
2300  table->getViewP()->setURIEncodedValue(
2301  recordUID, row, table->getViewP()->getColUID());
2302  }
2303  }
2304 
2305  if(!firstSave) // only test table if there was a change
2306  {
2307  try
2308  {
2309  table->getViewP()->init(); // verify new table (throws runtime_errors)
2310  }
2311  catch(...)
2312  {
2313  __SUP_COUT_INFO__ << "Reverting to original view." << __E__;
2314  __SUP_COUT__ << "Before:" << __E__;
2315  table->getViewP()->print();
2316  table->getViewP()->copy(backupView, temporaryVersion, author);
2317  __SUP_COUT__ << "After:" << __E__;
2318  table->getViewP()->print();
2319 
2320  throw; // rethrow
2321  }
2322  }
2323 
2324  handleFillModifiedTablesXML(xmlOut, cfgMgr);
2325  }
2326  catch(std::runtime_error& e)
2327  {
2328  __SUP_SS__ << ("Error creating new record(s)!\n\n" + std::string(e.what()))
2329  << __E__;
2330  __SUP_COUT_ERR__ << "\n" << ss.str();
2331  xmlOut.addTextElementToData("Error", ss.str());
2332  }
2333  catch(...)
2334  {
2335  __SUP_SS__ << ("Error creating new record(s)!\n\n") << __E__;
2336  try
2337  {
2338  throw;
2339  } //one more try to printout extra info
2340  catch(const std::exception& e)
2341  {
2342  ss << "Exception message: " << e.what();
2343  }
2344  catch(...)
2345  {
2346  }
2347  __SUP_COUT_ERR__ << "\n" << ss.str();
2348  xmlOut.addTextElementToData("Error", ss.str());
2349  }
2350 } //end handleFillCreateTreeNodeRecordsXML()
2351 
2352 //==============================================================================
2355 void ConfigurationGUISupervisor::handleFillModifiedTablesXML(
2356  HttpXmlDocument& xmlOut, ConfigurationManagerRW* cfgMgr)
2357 try
2358 {
2359  // return modified <modified tables>
2360  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
2361  std::map<std::string, TableVersion> allActivePairs = cfgMgr->getActiveVersions();
2362  for(auto& activePair : allActivePairs)
2363  {
2364  xmlOut.addTextElementToData("NewActiveTableName", activePair.first);
2365  xmlOut.addTextElementToData("NewActiveTableVersion",
2366  allTableInfo.at(activePair.first)
2367  .tablePtr_->getView()
2368  .getVersion()
2369  .toString());
2370  xmlOut.addTextElementToData(
2371  "NewActiveTableComment",
2372  allTableInfo.at(activePair.first).tablePtr_->getView().getAuthor() + ": " +
2373  allTableInfo.at(activePair.first).tablePtr_->getView().getComment());
2374  }
2375 } //end handleFillModifiedTablesXML()
2376 catch(std::runtime_error& e)
2377 {
2378  __SUP_SS__ << ("Error!\n\n" + std::string(e.what())) << __E__;
2379  __SUP_COUT_ERR__ << "\n" << ss.str();
2380  xmlOut.addTextElementToData("Error", ss.str());
2381 }
2382 catch(...)
2383 {
2384  __SUP_SS__ << ("Error!\n\n") << __E__;
2385  try
2386  {
2387  throw;
2388  } //one more try to printout extra info
2389  catch(const std::exception& e)
2390  {
2391  ss << "Exception message: " << e.what();
2392  }
2393  catch(...)
2394  {
2395  }
2396  __SUP_COUT_ERR__ << "\n" << ss.str();
2397  xmlOut.addTextElementToData("Error", ss.str());
2398 } //end handleFillModifiedTablesXML() catch
2399 
2400 //==============================================================================
2415 void ConfigurationGUISupervisor::handleFillDeleteTreeNodeRecordsXML(
2416  HttpXmlDocument& xmlOut,
2417  ConfigurationManagerRW* cfgMgr,
2418  const std::string& groupName,
2419  const TableGroupKey& groupKey,
2420  const std::string& startPath,
2421  const std::string& modifiedTables,
2422  const std::string& recordList)
2423 {
2424  // setup active tables based on input group and modified tables
2425  setupActiveTablesXML(xmlOut,
2426  cfgMgr,
2427  groupName,
2428  groupKey,
2429  modifiedTables,
2430  true /* refresh all */,
2431  false /* getGroupInfo */,
2432  0 /* returnMemberMap */,
2433  false /* outputActiveTables */);
2434 
2435  try
2436  {
2437  ConfigurationTree targetNode = cfgMgr->getNode(startPath);
2438  TableBase* table = cfgMgr->getTableByName(targetNode.getTableName());
2439 
2440  __SUP_COUT__ << table->getTableName() << __E__;
2441  TableVersion temporaryVersion;
2442 
2443  // if current version is not temporary
2444  // create temporary
2445  // else re-modify temporary version
2446  // edit temporary version directly
2447  // then after all edits return active versions
2448  //
2449 
2450  bool firstSave = true;
2451 
2452  // extract record list
2453  {
2454  std::istringstream f(recordList);
2455  std::string recordUID;
2456  // unsigned int i;
2457 
2458  while(getline(f, recordUID, ',')) // for each record
2459  {
2460  recordUID = StringMacros::decodeURIComponent(recordUID);
2461 
2462  __SUP_COUT__ << "recordUID " << recordUID << __E__;
2463 
2464  if(firstSave) // handle version bookkeeping
2465  {
2466  if(!(temporaryVersion = targetNode.getTableVersion())
2467  .isTemporaryVersion())
2468  {
2469  __SUP_COUT__ << "Start version " << temporaryVersion << __E__;
2470  // create temporary version for editing
2471  temporaryVersion = table->createTemporaryView(temporaryVersion);
2472  cfgMgr->saveNewTable(
2473  targetNode.getTableName(),
2474  temporaryVersion,
2475  true); // proper bookkeeping for temporary version with the new version
2476 
2477  __SUP_COUT__ << "Created temporary version " << temporaryVersion
2478  << __E__;
2479  }
2480  else // else table is already temporary version
2481  __SUP_COUT__ << "Using temporary version " << temporaryVersion
2482  << __E__;
2483 
2484  firstSave = false;
2485  }
2486 
2487  // at this point have valid temporary version to edit
2488 
2489  // copy "delete-uid" type edit from handleSaveTreeNodeEditXML()
2490  // functionality
2491  unsigned int row =
2492  table->getViewP()->findRow(table->getViewP()->getColUID(), recordUID);
2493  table->getViewP()->deleteRow(row);
2494  }
2495  }
2496 
2497  if(!firstSave) // only test table if there was a change
2498  table->getViewP()->init(); // verify new table (throws runtime_errors)
2499 
2500  handleFillModifiedTablesXML(xmlOut, cfgMgr);
2501  }
2502  catch(std::runtime_error& e)
2503  {
2504  __SUP_SS__ << ("Error removing record(s)!\n\n" + std::string(e.what())) << __E__;
2505  __SUP_COUT_ERR__ << "\n" << ss.str();
2506  xmlOut.addTextElementToData("Error", ss.str());
2507  }
2508  catch(...)
2509  {
2510  __SUP_SS__ << ("Error removing record(s)!\n\n") << __E__;
2511  try
2512  {
2513  throw;
2514  } //one more try to printout extra info
2515  catch(const std::exception& e)
2516  {
2517  ss << "Exception message: " << e.what();
2518  }
2519  catch(...)
2520  {
2521  }
2522  __SUP_COUT_ERR__ << "\n" << ss.str();
2523  xmlOut.addTextElementToData("Error", ss.str());
2524  }
2525 } // end handleFillDeleteTreeNodeRecordsXML()
2526 
2527 //==============================================================================
2543 void ConfigurationGUISupervisor::handleFillRenameTreeNodeRecordsXML(
2544  HttpXmlDocument& xmlOut,
2545  ConfigurationManagerRW* cfgMgr,
2546  const std::string& groupName,
2547  const TableGroupKey& groupKey,
2548  const std::string& startPath,
2549  const std::string& modifiedTables,
2550  const std::string& recordList,
2551  const std::string& newRecordList)
2552 {
2553  // setup active tables based on input group and modified tables
2554  setupActiveTablesXML(xmlOut,
2555  cfgMgr,
2556  groupName,
2557  groupKey,
2558  modifiedTables,
2559  true /* refresh all */,
2560  false /* getGroupInfo */,
2561  0 /* returnMemberMap */,
2562  false /* outputActiveTables */);
2563 
2564  try
2565  {
2566  ConfigurationTree targetNode = cfgMgr->getNode(startPath);
2567  TableBase* table = cfgMgr->getTableByName(targetNode.getTableName());
2568 
2569  __SUP_COUT__ << table->getTableName() << __E__;
2570  TableVersion temporaryVersion;
2571 
2572  // if current version is not temporary
2573  // create temporary
2574  // else re-modify temporary version
2575  // edit temporary version directly
2576  // then after all edits return active versions
2577  //
2578 
2579  // extract record list
2580  std::vector<std::string> recordArray =
2582  std::vector<std::string> newRecordArray =
2583  StringMacros::getVectorFromString(newRecordList);
2584 
2585  __SUP_COUTV__(StringMacros::vectorToString(recordArray));
2586  __SUP_COUTV__(StringMacros::vectorToString(newRecordArray));
2587 
2588  if(recordArray.size() == 0 || recordArray.size() != newRecordArray.size())
2589  {
2590  __SUP_SS__
2591  << "Invalid record size vs new record name size, they must be the same: "
2592  << recordArray.size() << " vs " << newRecordArray.size() << __E__;
2593  __SUP_SS_THROW__;
2594  }
2595 
2596  // handle version bookkeeping
2597  {
2598  if(!(temporaryVersion = targetNode.getTableVersion()).isTemporaryVersion())
2599  {
2600  __SUP_COUT__ << "Start version " << temporaryVersion << __E__;
2601  // create temporary version for editing
2602  temporaryVersion = table->createTemporaryView(temporaryVersion);
2603  cfgMgr->saveNewTable(
2604  targetNode.getTableName(),
2605  temporaryVersion,
2606  true); // proper bookkeeping for temporary version with the new version
2607 
2608  __SUP_COUT__ << "Created temporary version " << temporaryVersion << __E__;
2609  }
2610  else // else table is already temporary version
2611  __SUP_COUT__ << "Using temporary version " << temporaryVersion << __E__;
2612  }
2613 
2614  // at this point have valid temporary version to edit
2615 
2616  // for every record, change name
2617  unsigned int row;
2618  for(unsigned int i = 0; i < recordArray.size(); ++i)
2619  {
2620  row = table->getViewP()->findRow(
2621  table->getViewP()->getColUID(),
2622  StringMacros::decodeURIComponent(recordArray[i]));
2623 
2624  table->getViewP()->setValueAsString(
2625  newRecordArray[i], row, table->getViewP()->getColUID());
2626  }
2627 
2628  table->getViewP()->init(); // verify new table (throws runtime_errors)
2629 
2630  handleFillModifiedTablesXML(xmlOut, cfgMgr);
2631  }
2632  catch(std::runtime_error& e)
2633  {
2634  __SUP_SS__ << ("Error renaming record(s)!\n\n" + std::string(e.what())) << __E__;
2635  __SUP_COUT_ERR__ << "\n" << ss.str();
2636  xmlOut.addTextElementToData("Error", ss.str());
2637  }
2638  catch(...)
2639  {
2640  __SUP_SS__ << ("Error renaming record(s)!\n\n") << __E__;
2641  try
2642  {
2643  throw;
2644  } //one more try to printout extra info
2645  catch(const std::exception& e)
2646  {
2647  ss << "Exception message: " << e.what();
2648  }
2649  catch(...)
2650  {
2651  }
2652  __SUP_COUT_ERR__ << "\n" << ss.str();
2653  xmlOut.addTextElementToData("Error", ss.str());
2654  }
2655 } // end handleFillRenameTreeNodeRecordsXML()
2656 
2657 //==============================================================================
2674 void ConfigurationGUISupervisor::handleFillCopyTreeNodeRecordsXML(
2675  HttpXmlDocument& xmlOut,
2676  ConfigurationManagerRW* cfgMgr,
2677  const std::string& groupName,
2678  const TableGroupKey& groupKey,
2679  const std::string& startPath,
2680  const std::string& modifiedTables,
2681  const std::string& recordList,
2682  unsigned int numberOfCopies /* = 1 */)
2683 {
2684  if(!numberOfCopies)
2685  numberOfCopies = 1; // force 0 to 1, assuming user meant to get one copy
2686 
2687  // setup active tables based on input group and modified tables
2688  setupActiveTablesXML(xmlOut,
2689  cfgMgr,
2690  groupName,
2691  groupKey,
2692  modifiedTables,
2693  true /* refresh all */,
2694  false /* getGroupInfo */,
2695  0 /* returnMemberMap */,
2696  false /* outputActiveTables */);
2697 
2698  try
2699  {
2700  ConfigurationTree targetNode = cfgMgr->getNode(startPath);
2701  TableBase* table = cfgMgr->getTableByName(targetNode.getTableName());
2702 
2703  __SUP_COUT__ << table->getTableName() << __E__;
2704  TableVersion temporaryVersion;
2705 
2706  // if current version is not temporary
2707  // create temporary
2708  // else re-modify temporary version
2709  // edit temporary version directly
2710  // then after all edits return active versions
2711  //
2712 
2713  // extract record list
2714  std::vector<std::string> recordArray =
2716  __SUP_COUTV__(StringMacros::vectorToString(recordArray));
2717 
2718  // handle version bookkeeping
2719  {
2720  if(!(temporaryVersion = targetNode.getTableVersion()).isTemporaryVersion())
2721  {
2722  __SUP_COUT__ << "Start version " << temporaryVersion << __E__;
2723  // create temporary version for editing
2724  temporaryVersion = table->createTemporaryView(temporaryVersion);
2725  cfgMgr->saveNewTable(
2726  targetNode.getTableName(),
2727  temporaryVersion,
2728  true); // proper bookkeeping for temporary version with the new version
2729 
2730  __SUP_COUT__ << "Created temporary version " << temporaryVersion << __E__;
2731  }
2732  else // else table is already temporary version
2733  __SUP_COUT__ << "Using temporary version " << temporaryVersion << __E__;
2734  }
2735 
2736  // at this point have valid temporary version to edit
2737 
2738  // for every record, copy spec'd number of times
2739  unsigned int row;
2740  for(const auto& recordUID : recordArray)
2741  {
2742  row = table->getViewP()->findRow(table->getViewP()->getColUID(),
2744  for(unsigned int i = 0; i < numberOfCopies; ++i)
2745  table->getViewP()->copyRows(
2746  cfgMgr->getUsername(),
2747  table->getView(),
2748  row,
2749  1 /*srcRowsToCopy*/,
2750  -1 /*destOffsetRow*/,
2751  true /*generateUniqueDataColumns*/,
2752  recordUID /*baseNameAutoUID*/); // make the name similar end record loop
2753  }
2754 
2755  table->getViewP()->init(); // verify new table (throws runtime_errors)
2756 
2757  handleFillModifiedTablesXML(xmlOut, cfgMgr);
2758  }
2759  catch(std::runtime_error& e)
2760  {
2761  __SUP_SS__ << ("Error copying record(s)!\n\n" + std::string(e.what())) << __E__;
2762  __SUP_COUT_ERR__ << "\n" << ss.str();
2763  xmlOut.addTextElementToData("Error", ss.str());
2764  }
2765  catch(...)
2766  {
2767  __SUP_SS__ << ("Error copying record(s)!\n\n") << __E__;
2768  try
2769  {
2770  throw;
2771  } //one more try to printout extra info
2772  catch(const std::exception& e)
2773  {
2774  ss << "Exception message: " << e.what();
2775  }
2776  catch(...)
2777  {
2778  }
2779  __SUP_COUT_ERR__ << "\n" << ss.str();
2780  xmlOut.addTextElementToData("Error", ss.str());
2781  }
2782 } // end handleFillCopyTreeNodeRecordsXML()
2783 
2784 //==============================================================================
2801 void ConfigurationGUISupervisor::handleFillSetTreeNodeFieldValuesXML(
2802  HttpXmlDocument& xmlOut,
2803  ConfigurationManagerRW* cfgMgr,
2804  const std::string& groupName,
2805  const TableGroupKey& groupKey,
2806  const std::string& startPath,
2807  const std::string& modifiedTables,
2808  const std::string& recordList,
2809  const std::string& fieldList,
2810  const std::string& valueList,
2811  const std::string& author)
2812 {
2813  // setup active tables based on input group and modified tables
2814  setupActiveTablesXML(xmlOut,
2815  cfgMgr,
2816  groupName,
2817  groupKey,
2818  modifiedTables,
2819  true /* refresh all */,
2820  false /* getGroupInfo */,
2821  0 /* returnMemberMap */,
2822  false /* outputActiveTables */);
2823 
2824  // for each field
2825  // return field/value pair in xml
2826 
2827  try
2828  {
2829  std::vector<std::string /*relative-path*/> fieldPaths;
2830  // extract field list
2831  {
2832  std::istringstream f(fieldList);
2833  std::string fieldPath;
2834  while(getline(f, fieldPath, ','))
2835  {
2836  fieldPaths.push_back(StringMacros::decodeURIComponent(fieldPath));
2837  }
2838  __SUP_COUT__ << fieldList << __E__;
2839  for(const auto& field : fieldPaths)
2840  __SUP_COUT__ << "fieldPath " << field << __E__;
2841  }
2842 
2843  std::vector<std::string /*relative-path*/> fieldValues;
2844  // extract value list
2845  {
2846  std::istringstream f(valueList);
2847  std::string fieldValue;
2848  while(getline(f, fieldValue, ','))
2849  {
2850  fieldValues.push_back(fieldValue); // setURIEncodedValue is expected
2851  // StringMacros::decodeURIComponent(fieldValue));
2852  }
2853 
2854  // if last value is "" then push empty value
2855  if(valueList.size() && valueList[valueList.size() - 1] == ',')
2856  fieldValues.push_back("");
2857 
2858  __SUP_COUT__ << valueList << __E__;
2859  for(const auto& value : fieldValues)
2860  __SUP_COUT__ << "fieldValue " << value << __E__;
2861  }
2862 
2863  if(fieldPaths.size() != fieldValues.size())
2864  {
2865  __SUP_SS__;
2866  __THROW__(ss.str() + "Mismatch in fields and values array size!");
2867  }
2868 
2869  // extract record list
2870  {
2871  TableBase* table;
2872  TableVersion temporaryVersion;
2873  std::istringstream f(recordList);
2874  std::string recordUID;
2875  unsigned int i;
2876 
2877  while(getline(f, recordUID, ',')) // for each record
2878  {
2879  recordUID = StringMacros::decodeURIComponent(recordUID);
2880 
2881  /*xercesc::DOMElement* parentEl =*/
2882  xmlOut.addTextElementToData("fieldValues", recordUID);
2883 
2884  // for each field, set value
2885  for(i = 0; i < fieldPaths.size(); ++i)
2886  {
2887  __SUP_COUT__ << "fieldPath " << fieldPaths[i] << __E__;
2888  __SUP_COUT__ << "fieldValue " << fieldValues[i] << __E__;
2889 
2890  // doNotThrowOnBrokenUIDLinks so that link UIDs can be edited like
2891  // other fields
2892  ConfigurationTree targetNode =
2893  cfgMgr->getNode(startPath + "/" + recordUID + "/" + fieldPaths[i],
2894  true /*doNotThrowOnBrokenUIDLinks*/);
2895 
2896  // need table, uid, columnName to set a value
2897 
2898  // assume correct table version is loaded by setupActiveTablesXML()
2899  // table = cfgMgr->getTableByName(
2900  // targetNode.getTableName());
2901  //
2902  //__SUP_COUT__ << "Active version is " << table->getViewVersion() <<
2903  //__E__;
2904 
2905  // mimic handleSaveTreeNodeEditXML L 1750
2906  // Actually call it! ..
2907  // with a modifier?
2908  // or
2909  // handleSaveTreeNodeEditXML(xmlOut,
2910  // cfgMgr,
2911  // targetNode.getTableName(),
2912  // targetNode.getTableVersion(),
2913  // "value",
2914  // targetNode.getUIDAsString(),
2915  // targetNode.getValueName(), //col name
2916  // fieldValues[i]
2917  // );
2918 
2919  // or
2920  // (because problem is this would create a new temporary version each
2921  // time) if current version is not temporary
2922  // create temporary
2923  // else re-modify temporary version
2924  // edit temporary version directly
2925  // then after all edits return active versions
2926  //
2927 
2928  __SUP_COUT__ << "Getting table " << targetNode.getFieldTableName()
2929  << __E__;
2930 
2931  // if link must get parent table name
2932  table = cfgMgr->getTableByName(
2933  targetNode.getFieldTableName()); // NOT getTableName!
2934  if(!(temporaryVersion = table->getViewP()->getVersion())
2935  .isTemporaryVersion())
2936  {
2937  // create temporary version for editing
2938  temporaryVersion =
2939  table->createTemporaryView(table->getViewP()->getVersion());
2940  cfgMgr->saveNewTable(table->getTableName(),
2941  temporaryVersion,
2942  true); // proper bookkeeping for temporary
2943  // version with the new version
2944 
2945  __SUP_COUT__ << "Created temporary version "
2946  << table->getTableName() << "-v" << temporaryVersion
2947  << __E__;
2948  }
2949  // else //else table is already temporary version
2950  __SUP_COUT__ << "Using temporary version " << table->getTableName()
2951  << "-v" << temporaryVersion << __E__;
2952 
2953  // copy "value" type edit from handleSaveTreeNodeEditXML()
2954  // functionality
2955  table->getViewP()->setURIEncodedValue(fieldValues[i],
2956  targetNode.getFieldRow(),
2957  targetNode.getFieldColumn(),
2958  author);
2959 
2960  table->getViewP()
2961  ->init(); // verify new table (throws runtime_errors)
2962  }
2963  }
2964  }
2965 
2966  handleFillModifiedTablesXML(xmlOut, cfgMgr);
2967  }
2968  catch(std::runtime_error& e)
2969  {
2970  __SUP_SS__ << ("Error setting field values!\n\n" + std::string(e.what()))
2971  << __E__;
2972  __SUP_COUT_ERR__ << "\n" << ss.str();
2973  xmlOut.addTextElementToData("Error", ss.str());
2974  }
2975  catch(...)
2976  {
2977  __SUP_SS__ << ("Error setting field values!\n\n") << __E__;
2978  try
2979  {
2980  throw;
2981  } //one more try to printout extra info
2982  catch(const std::exception& e)
2983  {
2984  ss << "Exception message: " << e.what();
2985  }
2986  catch(...)
2987  {
2988  }
2989  __SUP_COUT_ERR__ << "\n" << ss.str();
2990  xmlOut.addTextElementToData("Error", ss.str());
2991  }
2992 } //end handleFillSetTreeNodeFieldValuesXML()
2993 
2994 //==============================================================================
3009 void ConfigurationGUISupervisor::handleFillGetTreeNodeFieldValuesXML(
3010  HttpXmlDocument& xmlOut,
3011  ConfigurationManagerRW* cfgMgr,
3012  const std::string& groupName,
3013  const TableGroupKey& groupKey,
3014  const std::string& startPath,
3015  const std::string& modifiedTables,
3016  const std::string& recordList,
3017  const std::string& fieldList)
3018 {
3019  // setup active tables based on input group and modified tables
3020  setupActiveTablesXML(
3021  xmlOut, cfgMgr, groupName, groupKey, modifiedTables, false /* refreshAll */);
3022 
3023  // for each field
3024  // return field/value pair in xml
3025 
3026  try
3027  {
3028  std::vector<std::string /*relative-path*/> fieldPaths;
3029  // extract field list
3030  {
3031  std::istringstream f(fieldList);
3032  std::string fieldPath;
3033  while(getline(f, fieldPath, ','))
3034  {
3035  fieldPaths.push_back(StringMacros::decodeURIComponent(fieldPath));
3036  }
3037  __SUP_COUT__ << fieldList << __E__;
3038  }
3039 
3040  // extract record list
3041  {
3042  std::istringstream f(recordList);
3043  std::string recordUID;
3044  while(getline(f, recordUID, ',')) // for each record
3045  {
3046  recordUID = StringMacros::decodeURIComponent(recordUID);
3047 
3048  __SUP_COUT__ << "recordUID " << recordUID << __E__;
3049 
3050  xercesc::DOMElement* parentEl =
3051  xmlOut.addTextElementToData("fieldValues", recordUID);
3052 
3053  // for each field, get value
3054  for(const auto& fieldPath : fieldPaths)
3055  {
3056  // __SUP_COUT__ << "fieldPath " << fieldPath << __E__;
3057  // __SUP_COUT__ << "fullPath " << (startPath + "/" + recordUID + "/" + fieldPath) << __E__;
3058 
3059  ConfigurationTree node =
3060  cfgMgr->getNode(startPath + "/" + recordUID + "/" + fieldPath);
3061 
3062  xmlOut.addTextElementToParent("FieldPath", fieldPath, parentEl);
3063 
3064  xmlOut.addTextElementToParent(
3065  "FieldValue",
3066  node.getValueAsString(true /*returnLinkTableValue*/),
3067  parentEl);
3068  }
3069  }
3070  }
3071  }
3072  catch(std::runtime_error& e)
3073  {
3074  __SUP_SS__ << ("Error getting field values!\n\n" + std::string(e.what()))
3075  << __E__;
3076  __SUP_COUT_ERR__ << "\n" << ss.str();
3077  xmlOut.addTextElementToData("Error", ss.str());
3078  }
3079  catch(...)
3080  {
3081  __SUP_SS__ << ("Error getting field values!\n\n") << __E__;
3082  try
3083  {
3084  throw;
3085  } //one more try to printout extra info
3086  catch(const std::exception& e)
3087  {
3088  ss << "Exception message: " << e.what();
3089  }
3090  catch(...)
3091  {
3092  }
3093  __SUP_COUT_ERR__ << "\n" << ss.str();
3094  xmlOut.addTextElementToData("Error", ss.str());
3095  }
3096 } //end handleFillGetTreeNodeFieldValuesXML()
3097 
3098 //==============================================================================
3117 void ConfigurationGUISupervisor::handleFillTreeNodeCommonFieldsXML(
3118  HttpXmlDocument& xmlOut,
3119  ConfigurationManagerRW* cfgMgr,
3120  const std::string& groupName,
3121  const TableGroupKey& groupKey,
3122  const std::string& startPath,
3123  unsigned int depth,
3124  const std::string& modifiedTables,
3125  const std::string& recordList,
3126  const std::string& fieldList)
3127 {
3128  // setup active tables based on input group and modified tables
3129  setupActiveTablesXML(
3130  xmlOut, cfgMgr, groupName, groupKey, modifiedTables, false /* refreshAll */);
3131 
3132  try
3133  {
3134  xercesc::DOMElement* parentEl = xmlOut.addTextElementToData("fields", startPath);
3135 
3136  if(depth == 0)
3137  {
3138  __SUP_SS__ << "Depth of search must be greater than 0." << __E__;
3139  __SUP_COUT__ << ss.str();
3140  __SS_THROW__; // done if 0 depth, no fields
3141  }
3142 
3143  // do not allow traversing for common fields from root level
3144  // the tree view should be used for such a purpose
3145  // if(startPath == "/")
3146  // return;
3147 
3148  std::vector<ConfigurationTree::RecordField> retFieldList;
3149 
3150  {
3151  ConfigurationTree startNode = cfgMgr->getNode(startPath);
3152  if(startNode.isLinkNode() && startNode.isDisconnected())
3153  {
3154  __SUP_SS__ << "Start path was a disconnected link node!" << __E__;
3155  __SUP_SS_THROW__;
3156  return; // quietly ignore disconnected links at depth
3157  // note: at the root level they will be flagged for the user
3158  }
3159 
3160  std::vector<std::string /*relative-path*/> fieldAcceptList, fieldRejectList;
3161  if(fieldList != "")
3162  {
3163  // extract field filter list
3164  {
3165  std::istringstream f(fieldList);
3166  std::string fieldPath, decodedFieldPath;
3167  while(getline(f, fieldPath, ','))
3168  {
3169  decodedFieldPath = StringMacros::decodeURIComponent(fieldPath);
3170 
3171  if(decodedFieldPath[0] == '!') // reject field
3172  fieldRejectList.push_back(decodedFieldPath.substr(1));
3173  else
3174  fieldAcceptList.push_back(decodedFieldPath);
3175  }
3176  __SUP_COUT__ << fieldList << __E__;
3177  for(auto& field : fieldAcceptList)
3178  __SUP_COUT__ << "fieldAcceptList " << field << __E__;
3179  for(auto& field : fieldRejectList)
3180  __SUP_COUT__ << "fieldRejectList " << field << __E__;
3181  }
3182  }
3183 
3184  std::vector<std::string /*relative-path*/> records;
3185  if(recordList == "*") // handle all records case
3186  {
3187  records.clear();
3188  records = startNode.getChildrenNames();
3189  __SUP_COUT__ << "Translating wildcard..." << __E__;
3190  for(auto& record : records)
3191  __SUP_COUT__ << "recordList " << record << __E__;
3192  }
3193  else if(recordList != "")
3194  {
3195  // extract record list
3196  {
3197  std::istringstream f(recordList);
3198  std::string recordStr;
3199  while(getline(f, recordStr, ','))
3200  {
3201  records.push_back(StringMacros::decodeURIComponent(recordStr));
3202  }
3203  __SUP_COUT__ << recordList << __E__;
3204  for(auto& record : records)
3205  __SUP_COUT__ << "recordList " << record << __E__;
3206  }
3207  }
3208 
3209  //=== get common fields call!
3210  retFieldList = startNode.getCommonFields(
3211  records, fieldAcceptList, fieldRejectList, depth);
3212  //=== end get common fields call!
3213  }
3214 
3215  xercesc::DOMElement* parentTypeEl;
3216  for(const auto& fieldInfo : retFieldList)
3217  {
3218  xmlOut.addTextElementToParent(
3219  "FieldTableName", fieldInfo.tableName_, parentEl);
3220  xmlOut.addTextElementToParent(
3221  "FieldColumnName", fieldInfo.columnName_, parentEl);
3222  xmlOut.addTextElementToParent(
3223  "FieldRelativePath", fieldInfo.relativePath_, parentEl);
3224  xmlOut.addTextElementToParent(
3225  "FieldColumnType", fieldInfo.columnInfo_->getType(), parentEl);
3226  xmlOut.addTextElementToParent(
3227  "FieldColumnDataType", fieldInfo.columnInfo_->getDataType(), parentEl);
3228  xmlOut.addTextElementToParent("FieldColumnDefaultValue",
3229  fieldInfo.columnInfo_->getDefaultValue(),
3230  parentEl);
3231  // again, should min and max be included here?
3232  parentTypeEl =
3233  xmlOut.addTextElementToParent("FieldColumnDataChoices", "", parentEl);
3234 
3235  // if there are associated data choices, send info
3236  auto dataChoices = fieldInfo.columnInfo_->getDataChoices();
3237  xmlOut.addTextElementToParent(
3238  "FieldColumnDataChoice", // add default to list to mimic tree handling
3239  fieldInfo.columnInfo_->getDefaultValue(),
3240  parentTypeEl);
3241  for(const auto& dataChoice : dataChoices)
3242  xmlOut.addTextElementToParent(
3243  "FieldColumnDataChoice", dataChoice, parentTypeEl);
3244  }
3245  }
3246  catch(std::runtime_error& e)
3247  {
3248  __SUP_SS__ << ("Error getting common fields!\n\n" + std::string(e.what()))
3249  << __E__;
3250  __SUP_COUT_ERR__ << "\n" << ss.str();
3251  xmlOut.addTextElementToData("Error", ss.str());
3252  }
3253  catch(...)
3254  {
3255  __SUP_SS__ << ("Error getting common fields!\n\n") << __E__;
3256  try
3257  {
3258  throw;
3259  } //one more try to printout extra info
3260  catch(const std::exception& e)
3261  {
3262  ss << "Exception message: " << e.what();
3263  }
3264  catch(...)
3265  {
3266  }
3267  __SUP_COUT_ERR__ << "\n" << ss.str();
3268  xmlOut.addTextElementToData("Error", ss.str());
3269  }
3270 } //end handleFillTreeNodeCommonFieldsXML()
3271 
3272 //==============================================================================
3300 void ConfigurationGUISupervisor::handleFillUniqueFieldValuesForRecordsXML(
3301  HttpXmlDocument& xmlOut,
3302  ConfigurationManagerRW* cfgMgr,
3303  const std::string& groupName,
3304  const TableGroupKey& groupKey,
3305  const std::string& startPath,
3306  const std::string& modifiedTables,
3307  const std::string& recordList,
3308  const std::string& fieldList)
3309 {
3310  // setup active tables based on input group and modified tables
3311  setupActiveTablesXML(
3312  xmlOut, cfgMgr, groupName, groupKey, modifiedTables, false /* refreshAll */);
3313 
3314  try
3315  {
3316  // do not allow traversing for common fields from root level
3317  // the tree view should be used for such a purpose
3318  if(startPath == "/")
3319  return;
3320 
3321  ConfigurationTree startNode = cfgMgr->getNode(startPath);
3322  if(startNode.isLinkNode() && startNode.isDisconnected())
3323  {
3324  __SUP_SS__ << "Start path was a disconnected link node!" << __E__;
3325  __SUP_COUT_ERR__ << "\n" << ss.str();
3326  __SS_THROW__;
3327  }
3328 
3329  // extract records list
3330  std::vector<std::string /*relative-path*/> records;
3331  if(recordList == "*") // handle all records case
3332  {
3333  records.clear();
3334  records = startNode.getChildrenNames();
3335  __SUP_COUT__ << "Translating wildcard..." << __E__;
3336  for(auto& record : records)
3337  __SUP_COUT__ << "recordList " << record << __E__;
3338  }
3339  else if(recordList != "")
3340  {
3341  // extract record list
3342  {
3343  std::istringstream f(recordList);
3344  std::string recordStr;
3345  while(getline(f, recordStr, ','))
3346  {
3347  records.push_back(StringMacros::decodeURIComponent(recordStr));
3348  }
3349  __SUP_COUT__ << recordList << __E__;
3350  for(auto& record : records)
3351  __SUP_COUT__ << "recordList " << record << __E__;
3352  }
3353  } // end records extraction
3354 
3355  // extract fields to get
3356  std::vector<std::string /*relative-path*/> fieldsToGet;
3357  if(fieldList != "")
3358  {
3359  // extract field filter list
3360 
3361  if(fieldList == "AUTO")
3362  {
3363  // automatically choose 3 fields, with preference
3364  // for GroupID, On/Off, and FixedChoice fields.
3365 
3366  __SUP_COUT__ << "Getting AUTO filter fields!" << __E__;
3367 
3368  std::vector<ConfigurationTree::RecordField> retFieldList;
3369  std::vector<std::string /*relative-path*/> fieldAcceptList,
3370  fieldRejectList;
3371  fieldRejectList.push_back("*" + TableViewColumnInfo::COL_NAME_COMMENT);
3372  retFieldList = startNode.getCommonFields(
3373  records, fieldAcceptList, fieldRejectList, 5, true /*auto*/);
3374 
3375  for(const auto& retField : retFieldList)
3376  fieldsToGet.push_back(retField.relativePath_ + retField.columnName_);
3377  }
3378  else
3379  {
3380  std::istringstream f(fieldList);
3381  std::string fieldPath;
3382  while(getline(f, fieldPath, ','))
3383  {
3384  fieldsToGet.push_back(StringMacros::decodeURIComponent(fieldPath));
3385  }
3386  __SUP_COUTV__(fieldList);
3387  }
3388  } // end fields extraction
3389 
3390  __SUP_COUTV__(StringMacros::vectorToString(fieldsToGet));
3391 
3392  // loop through each field and get unique values among records
3393  {
3394  ConfigurationTree startNode = cfgMgr->getNode(startPath);
3395  std::string fieldGroupIDChildLinkIndex;
3396  for(auto& field : fieldsToGet)
3397  {
3398  __SUP_COUTV__(field);
3399 
3400  xercesc::DOMElement* parentEl =
3401  xmlOut.addTextElementToData("field", field);
3402 
3403  // if groupID field, give child link index
3404  // this can be used to pre-select particular group(s)
3405 
3406  // use set to force sorted unique values
3407  std::set<std::string /*unique-values*/> uniqueValues =
3408  startNode.getUniqueValuesForField(
3409  records, field, &fieldGroupIDChildLinkIndex);
3410 
3411  if(fieldGroupIDChildLinkIndex != "")
3412  xmlOut.addTextElementToParent(
3413  "childLinkIndex", fieldGroupIDChildLinkIndex, parentEl);
3414 
3415  for(auto& uniqueValue : uniqueValues)
3416  {
3417  __SUP_COUT__ << "uniqueValue " << uniqueValue << __E__;
3418 
3419  xmlOut.addTextElementToParent("uniqueValue", uniqueValue, parentEl);
3420  }
3421  }
3422  }
3423  }
3424  catch(std::runtime_error& e)
3425  {
3426  __SUP_SS__ << "Error getting unique field values from path '" << startPath
3427  << "' and field list '" << fieldList << "!'\n\n"
3428  << e.what() << __E__;
3429  __SUP_COUT_ERR__ << "\n" << ss.str();
3430  xmlOut.addTextElementToData("Error", ss.str());
3431  }
3432  catch(...)
3433  {
3434  __SUP_SS__ << "Error getting unique field values from path '" << startPath
3435  << "' and field list '" << fieldList << "!'\n\n"
3436  << __E__;
3437  try
3438  {
3439  throw;
3440  } //one more try to printout extra info
3441  catch(const std::exception& e)
3442  {
3443  ss << "Exception message: " << e.what();
3444  }
3445  catch(...)
3446  {
3447  }
3448  __SUP_COUT_ERR__ << "\n" << ss.str();
3449  xmlOut.addTextElementToData("Error", ss.str());
3450  }
3451 } // end handleFillUniqueFieldValuesForRecordsXML()
3452 
3453 //==============================================================================
3473 void ConfigurationGUISupervisor::handleFillTreeViewXML(
3474  HttpXmlDocument& xmlOut,
3475  ConfigurationManagerRW* cfgMgr,
3476  const std::string& groupName,
3477  const TableGroupKey& groupKey,
3478  const std::string& startPath,
3479  unsigned int depth,
3480  bool hideStatusFalse,
3481  const std::string& modifiedTables,
3482  const std::string& filterList,
3483  const std::string& diffGroupName /* = "" */,
3484  const TableGroupKey& diffGroupKey /* = TableGroupKey() */)
3485 {
3486  __SUP_COUTT__ << "get Tree View: " << groupName << "(" << groupKey << ")" << __E__;
3487 
3488  // return xml
3489  // <groupName="groupName"/>
3490  // <tree="path">
3491  // <node="...">
3492  // <node="...">
3493  // <node="...">
3494  // <value="...">
3495  // </node>
3496  // <node="...">
3497  // <value="...">
3498  // </node>
3499  // </node>
3500  // <node="...">
3501  // <value="..">
3502  // </node>
3503  // ...
3504  // </node>
3505  // </tree>
3506 
3507  // return the startPath as root "tree" element
3508  // and then display all children if depth > 0
3509 
3510  //------------------
3511  //First, if doing diff, load tables into cache and copy.
3512  // Loading will leave tables active in the user cfgMgr..
3513  // which will mess up diff. So order:
3514  // 1. load diff tables in user cfgMgr
3515  // 2. copy from cfgMgr cache to diffCfgMgr
3516  // 3. load tree tables in user cfgMgr
3517 
3518  bool doDiff = (diffGroupName != "" && !diffGroupKey.isInvalid());
3519 
3520  std::map<std::string /*name*/, TableVersion /*version*/> diffMemberMap;
3521  ConfigurationManagerRW tmpCfgMgr("TreeDiff");
3522  ConfigurationManagerRW* diffCfgMgr = &tmpCfgMgr;
3523  std::string diffAccumulateErrors;
3524  if(doDiff)
3525  {
3526  //Load diff tables in cfgMgr so that tables are cached,
3527  // then copy to diffCfgMgr as active tables for tree comparison.
3528  // This is more efficient than loading diff tables from db every tree access.
3529 
3530  for(auto& activeTable : cfgMgr->getActiveVersions())
3531  __SUP_COUT__ << "cfgMgr " << activeTable.first << "-v" << activeTable.second
3532  << __E__;
3533 
3534  cfgMgr->loadTableGroup(diffGroupName,
3535  diffGroupKey,
3536  false /*doActivate*/,
3537  &diffMemberMap,
3538  0 /*progressBar*/,
3539  0 /*accumulateErrors*/,
3540  0 /*groupComment*/,
3541  0 /*groupAuthor*/,
3542  0 /*groupCreationTime*/,
3543  false /*doNotLoadMember*/,
3544  0 /*groupTypeString*/
3545  );
3546 
3547  for(auto& activeTable : cfgMgr->getActiveVersions())
3548  __SUP_COUT__ << "cfgMgr " << activeTable.first << "-v" << activeTable.second
3549  << __E__;
3550 
3551  __SUP_COUTT__ << "Diff Group tables loaded." << __E__;
3552  diffCfgMgr->copyTableGroupFromCache(
3553  *cfgMgr, diffMemberMap, diffGroupName, diffGroupKey);
3554  __SUP_COUTT__ << "Diff Group tables copied to local diff config manager."
3555  << __E__;
3556 
3557  //now activate diff table for tree traversal (without calling init())
3558  for(auto& memberPair : diffMemberMap)
3559  diffCfgMgr->getTableByName(memberPair.first)
3560  ->setActiveView(memberPair.second);
3561 
3562  for(const auto& lastGroupLoaded : cfgMgr->getLastTableGroups())
3563  __SUP_COUT__ << "cfgMgr Last loaded " << lastGroupLoaded.first << ": "
3564  << lastGroupLoaded.second.first.first << "("
3565  << lastGroupLoaded.second.first.second << ")";
3566 
3567  for(const auto& lastGroupLoaded : diffCfgMgr->getLastTableGroups())
3568  __SUP_COUT__ << "diffCfgMgr Last loaded " << lastGroupLoaded.first << ": "
3569  << lastGroupLoaded.second.first.first << "("
3570  << lastGroupLoaded.second.first.second << ")";
3571 
3572  //for complete tree traversal, if config type, then load context tables in diff, if context type, then load config tables in diff
3573  if(diffCfgMgr->getLastTableGroups().size() == 1)
3574  {
3575  __SUP_COUT__ << "Type already loaded to diff = "
3576  << diffCfgMgr->getLastTableGroups().begin()->first << __E__;
3577  try
3578  {
3579  auto groupTypeToLoad = ConfigurationManager::GROUP_TYPE_NAME_CONTEXT;
3580  if(diffCfgMgr->getLastTableGroups().begin()->first ==
3581  ConfigurationManager::GROUP_TYPE_NAME_CONTEXT)
3582  groupTypeToLoad = ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION;
3583  else if(diffCfgMgr->getLastTableGroups().begin()->first ==
3584  ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION)
3585  groupTypeToLoad = ConfigurationManager::GROUP_TYPE_NAME_CONTEXT;
3586 
3587  __SUP_COUTT__
3588  << "Loading " << groupTypeToLoad
3589  << cfgMgr->getLastTableGroups().at(groupTypeToLoad).first.first << "("
3590  << cfgMgr->getLastTableGroups().at(groupTypeToLoad).first.second
3591  << ")" << __E__;
3592 
3593  diffCfgMgr->copyTableGroupFromCache(
3594  *cfgMgr,
3595  cfgMgr->getLastTableGroups().at(groupTypeToLoad).second,
3596  cfgMgr->getLastTableGroups().at(groupTypeToLoad).first.first,
3597  cfgMgr->getLastTableGroups().at(groupTypeToLoad).first.second);
3598 
3599  //now activate diff table for tree traversal (without calling init())
3600  for(auto& memberPair :
3601  cfgMgr->getLastTableGroups().at(groupTypeToLoad).second)
3602  diffCfgMgr->getTableByName(memberPair.first)
3603  ->setActiveView(memberPair.second);
3604  }
3605  catch(...)
3606  {
3607  } //ignore extra group loading errors
3608  }
3609 
3610  for(auto& activeTable : cfgMgr->getActiveVersions())
3611  __SUP_COUTT__ << "cfgMgr " << activeTable.first << "-v" << activeTable.second
3612  << __E__;
3613  for(auto& activeTable : diffCfgMgr->getActiveVersions())
3614  __SUP_COUTT__ << "diffCfgMgr " << activeTable.first << "-v"
3615  << activeTable.second << __E__;
3616 
3617  __SUP_COUTT__ << "Diff Group tables are setup: " << diffAccumulateErrors << __E__;
3618  } // end do diff load
3619 
3620  //------------------
3621  //Setup active tables based on input group and modified tables
3622  bool usingActiveGroups = (groupName == "" || groupKey.isInvalid());
3623  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
3624 
3625  std::string accumulatedErrors = "";
3626  setupActiveTablesXML(
3627  xmlOut,
3628  cfgMgr,
3629  groupName,
3630  groupKey,
3631  modifiedTables,
3632  false, //changed to not refresh all, assume no partially loaded tables (startPath == "/"), // refreshAll, if at root node, reload all tables so that partially loaded tables are not allowed
3633  (startPath == "/"), // get group info
3634  &memberMap, // get group member map
3635  true, // output active tables (default)
3636  &accumulatedErrors // accumulate errors
3637  );
3638 
3639  if(memberMap.size() > ConfigurationManager::getFixedContextMemberNames().size() +
3640  1 /* for optional table */
3641  && startPath == "/")
3642  {
3643  __COUTT__ << "Checking for orphaned tables..." << __E__;
3644 
3645  //check Tree for orphaned tables
3646  std::set<std::string /* table name that is linked to */> linkingTables;
3647  for(const auto& tableInfo : cfgMgr->getAllTableInfo())
3648  {
3649  //for each existing active table, check table for links to tables
3650 
3651  __COUTS__(30) << "Table " << tableInfo.first << __E__;
3652  if(!tableInfo.second.tablePtr_->isActive())
3653  continue; //skip if no active view for table
3654  else
3655  __COUTS__(30) << "Table active " << tableInfo.first << __E__;
3656 
3657  const TableView& view = tableInfo.second.tablePtr_->getView();
3658 
3659  bool addedThisTable = false;
3660  for(unsigned int col = 0; col < view.getNumberOfColumns(); ++col)
3661  {
3662  if(!view.getColumnInfo(col).isChildLink())
3663  continue;
3664 
3665  __COUTS__(30) << "Table " << tableInfo.first
3666  << " col: " << view.getColumnInfo(col).getName() << __E__;
3667 
3668  for(unsigned int r = 0; r < view.getNumberOfRows(); ++r)
3669  {
3670  if(view.getDataView()[r][col] == "" ||
3671  view.getDataView()[r][col] ==
3672  TableViewColumnInfo::DATATYPE_STRING_DEFAULT ||
3673  view.getDataView()[r][col] ==
3674  TableViewColumnInfo::DATATYPE_STRING_ALT_DEFAULT)
3675  continue;
3676 
3677  if(!addedThisTable) //add this table to since it seems to have a link!
3678  {
3679  linkingTables.emplace(tableInfo.first);
3680  addedThisTable = true;
3681  }
3682  linkingTables.emplace(
3683  view.getDataView()[r][col]); //add linked table name to set
3684  }
3685  }
3686  }
3687  __COUTTV__(StringMacros::setToString(linkingTables));
3688 
3689  std::string missingTables = "";
3690  for(const auto& member : memberMap)
3691  {
3692  //if not in linking tables set, then note
3693  if(linkingTables.find(member.first) != linkingTables.end())
3694  continue; //linked-to table, so no warning
3695 
3696  if(missingTables.size())
3697  missingTables += ", ";
3698  missingTables += member.first;
3699  }
3700 
3701  if(missingTables.size())
3702  {
3703  __COUTV__(missingTables);
3704  std::stringstream ss;
3705  ss << "The following member tables of table group '" << groupName << "("
3706  << groupKey
3707  << ")' were identified as possibly orphaned (i.e. no active tables link "
3708  "to these tables, and these tables have no links to other tables):\n\n"
3709  << missingTables << ".\n"
3710  << __E__;
3711  xmlOut.addTextElementToData("NoTreeLinkWarning", ss.str());
3712  }
3713  } //end orphaned table check
3714 
3715  if(accumulatedErrors != "")
3716  {
3717  xmlOut.addTextElementToData("Warning", accumulatedErrors);
3718 
3719  __SUP_COUT__ << "Active tables are setup. Warning string: '" << accumulatedErrors
3720  << "'" << __E__;
3721 
3722  __SUP_COUT__ << "Active table versions: "
3723  << StringMacros::mapToString(cfgMgr->getActiveVersions()) << __E__;
3724  }
3725  else
3726  {
3727  __SUP_COUTT__ << "Active tables are setup. No issues found." << __E__;
3728  __SUP_COUTT__ << "Active table versions: "
3729  << StringMacros::mapToString(cfgMgr->getActiveVersions()) << __E__;
3730  }
3731 
3732  try
3733  {
3734  xercesc::DOMElement* parentEl = xmlOut.addTextElementToData("tree", startPath);
3735 
3736  if(depth == 0)
3737  return; // already returned root node in itself
3738 
3739  std::vector<std::pair<std::string, ConfigurationTree>> rootMap;
3740  std::map<std::string, ConfigurationTree> diffRootMap;
3741 
3742  if(startPath == "/")
3743  {
3744  // then consider the configurationManager the root node
3745 
3746  std::string accumulateTreeErrs;
3747 
3748  if(usingActiveGroups)
3749  rootMap = cfgMgr->getChildren(0, &accumulateTreeErrs);
3750  else
3751  rootMap = cfgMgr->getChildren(&memberMap, &accumulateTreeErrs);
3752 
3753  if(doDiff)
3754  {
3755  diffRootMap =
3756  diffCfgMgr->getChildrenMap(&diffMemberMap, &diffAccumulateErrors);
3757  __SUP_COUTV__(diffRootMap.size());
3758  for(auto& diffChild : diffRootMap)
3759  __SUP_COUTV__(diffChild.first);
3760  }
3761 
3762  __SUP_COUTV__(accumulateTreeErrs);
3763 
3764  if(accumulateTreeErrs != "")
3765  xmlOut.addTextElementToData("TreeErrors", accumulateTreeErrs);
3766  }
3767  else
3768  {
3769  ConfigurationTree startNode =
3770  cfgMgr->getNode(startPath, true /*doNotThrowOnBrokenUIDLinks*/);
3771  if(startNode.isLinkNode() && startNode.isDisconnected())
3772  {
3773  xmlOut.addTextElementToData("DisconnectedStartNode", "1");
3774  return; // quietly ignore disconnected links at depth
3775  // note: at the root level they will be flagged for the user
3776  }
3777 
3778  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap;
3780  filterList,
3781  filterMap,
3782  std::set<char>({';'}) /*pair delimiters*/,
3783  std::set<char>({'='}) /*name/value delimiters*/);
3784 
3785  __COUTV__(StringMacros::mapToString(filterMap));
3786 
3787  rootMap = cfgMgr->getNode(startPath).getChildren(filterMap);
3788 
3789  if(doDiff)
3790  {
3791  try
3792  {
3793  ConfigurationTree diffStartNode = diffCfgMgr->getNode(
3794  startPath, true /*doNotThrowOnBrokenUIDLinks*/);
3795 
3796  if(diffStartNode.isLinkNode() && diffStartNode.isDisconnected())
3797  __SUP_COUTT__ << "Diff Group disconnected node." << __E__;
3798  else
3799  diffRootMap =
3800  diffCfgMgr->getNode(startPath).getChildrenMap(filterMap);
3801  }
3802  catch(const std::runtime_error& e)
3803  {
3804  //if diff node does not exist, user was already notified at parent diff, so ignore error.
3805  __SUP_COUTT__ << "Diff Group node does not exist." << __E__;
3806  }
3807  }
3808  }
3809 
3810  if(!doDiff)
3811  {
3812  for(auto& treePair : rootMap)
3813  recursiveTreeToXML(
3814  treePair.second, depth - 1, xmlOut, parentEl, hideStatusFalse);
3815  }
3816  else //doDiff
3817  {
3818  __SUP_COUTT__ << "Diff Tree recursive handling." << __E__;
3819 
3820  //convert vector rootMap to set for searching
3821  std::set<std::string /* treeNodeName */> rootMapToSearch;
3822  for(const auto& rootMember : rootMap)
3823  rootMapToSearch.emplace(rootMember.first);
3824 
3825  std::stringstream rootSs;
3826  for(const auto& rootMember : rootMap)
3827  rootSs << ", " << rootMember.first;
3828 
3829  //add all tables in diff group that are missing to parentEl
3830  std::stringstream diffRootSs;
3831  for(const auto& diffMember : diffRootMap) //diffMemberMap)
3832  {
3833  diffRootSs << ", " << diffMember.first << ":"
3834  << diffMember.second.getNodeType();
3835  if(rootMapToSearch.find(diffMember.first) ==
3836  rootMapToSearch
3837  .end()) //memberMap.find(diffMember.first) == memberMap.end())
3838  {
3839  std::stringstream missingSs;
3840  missingSs << diffMember.first << //" <<< Not in " <<
3841  // groupName << "(" << groupKey << "), present in " <<
3842  " <<< Only in " << diffGroupName << "(" << diffGroupKey
3843  << ") >>>";
3844  xmlOut.addTextElementToParent(
3845  "diffNodeMissing", missingSs.str(), parentEl);
3846  }
3847 
3848  if(diffMember.second.getNodeType() == "UIDLinkNode")
3849  {
3850  __SUP_COUTT__
3851  << "diff active "
3853  << __E__;
3854  __SUP_COUTT__
3855  << "root active "
3857  << __E__;
3858 
3859  __SUP_COUTT__ << "diff map " << diffRootSs.str() << __E__;
3860  __SUP_COUTT__ << "root map " << rootSs.str() << __E__;
3861 
3862  __SUP_COUTT__ << "\t\t" << diffMember.second.getValueName() << ": "
3863  << diffMember.second.getValueAsString() << __E__;
3864 
3865  __SUP_COUTT__ << diffMember.second.nodeDump();
3866  }
3867  }
3868 
3869  __SUP_COUTT__ << "diff map " << diffRootSs.str() << __E__;
3870  __SUP_COUTT__ << "root map " << rootSs.str() << __E__;
3871 
3872  //recurse
3873  for(auto& treePair : rootMap)
3874  {
3875  if(diffRootMap.find(treePair.first) == diffRootMap.end())
3876  {
3877  __SUP_COUTT__ << "Diff Tree recursive handling... " << treePair.first
3878  << __E__;
3879  ConfigurationTree rootNode(diffCfgMgr, nullptr /* table */);
3880  recursiveTreeToXML(
3881  treePair.second,
3882  depth - 1,
3883  xmlOut,
3884  parentEl,
3885  hideStatusFalse,
3886  rootNode /* root node diffTree to indicate record not found in diff group */);
3887  }
3888  else
3889  {
3890  __SUP_COUTT__ << "Diff Tree recursive handling... " << treePair.first
3891  << __E__;
3892  recursiveTreeToXML(treePair.second,
3893  depth - 1,
3894  xmlOut,
3895  parentEl,
3896  hideStatusFalse,
3897  diffRootMap.at(treePair.first));
3898  }
3899  }
3900  }
3901  }
3902  catch(std::runtime_error& e)
3903  {
3904  __SUP_SS__ << "Error detected generating XML tree!\n\n " << e.what() << __E__;
3905  __SUP_COUT_ERR__ << "\n" << ss.str();
3906  xmlOut.addTextElementToData("Error", ss.str());
3907  }
3908  catch(...)
3909  {
3910  __SUP_SS__ << "Error detected generating XML tree!" << __E__;
3911  try
3912  {
3913  throw;
3914  } //one more try to printout extra info
3915  catch(const std::exception& e)
3916  {
3917  ss << "Exception message: " << e.what();
3918  }
3919  catch(...)
3920  {
3921  }
3922  __SUP_COUT_ERR__ << "\n" << ss.str();
3923  xmlOut.addTextElementToData("Error", ss.str());
3924  }
3925 } // end handleFillTreeViewXML()
3926 
3927 //==============================================================================
3933 void ConfigurationGUISupervisor::recursiveTreeToXML(
3934  const ConfigurationTree& t,
3935  unsigned int depth,
3936  HttpXmlDocument& xmlOut,
3937  xercesc::DOMElement* parentEl,
3938  bool hideStatusFalse,
3939  std::optional<std::reference_wrapper<const ConfigurationTree>> diffTree)
3940 {
3941  __COUTS__(30) << t.getValueAsString() << __E__;
3942 
3943  if(t.isValueNode())
3944  {
3945  __COUTS__(30) << "\t" << t.getValueName() << ": " << t.getValueAsString()
3946  << __E__;
3947 
3948  parentEl = xmlOut.addTextElementToParent("node", t.getValueName(), parentEl);
3949  if(diffTree.has_value() &&
3950  t.getValueName() != TableViewColumnInfo::COL_NAME_COMMENT &&
3951  t.getValueName() != TableViewColumnInfo::COL_NAME_AUTHOR &&
3952  t.getValueName() != TableViewColumnInfo::COL_NAME_CREATION)
3953  {
3954  __COUTS__(30) << "\t\t diff type " << diffTree->get().getNodeType() << __E__;
3955 
3956  if(diffTree->get().isValueNode())
3957  {
3958  __COUTS__(30) << "\t" << diffTree->get().getValueAsString() << " ? "
3959  << t.getValueAsString() << __E__;
3960  __COUTS__(30) << "\t" << diffTree->get().getTableName() << "-v"
3961  << diffTree->get().getTableVersion() << " ? "
3962  << t.getTableName() << "-v" << t.getTableVersion() << __E__;
3963 
3964  if(t.getValueAsString() != diffTree->get().getValueAsString())
3965  {
3966  std::stringstream missingSs; //assume only one group loaded for diff
3967  auto diffGroupPair =
3968  diffTree->get().getConfigurationManager()->getGroupOfLoadedTable(
3969  diffTree->get().getTableName());
3970  missingSs << "<<< '" << diffTree->get().getValueAsString() << "' in "
3971  << diffGroupPair.first << "(" << diffGroupPair.second
3972  << ") >>>";
3973  xmlOut.addTextElementToParent("nodeDiff", missingSs.str(), parentEl);
3974  }
3975  }
3976  else
3977  {
3978  std::stringstream missingSs; //assume only one group loaded for diff
3979  //lookup group name in diffManager based on current node's table (best proxy info for missing diff node at this point)
3980  auto diffGroupPair =
3981  diffTree->get().getConfigurationManager()->getGroupOfLoadedTable(
3982  t.getTableName());
3983  missingSs << "<<< Path not found in " << diffGroupPair.first << "("
3984  << diffGroupPair.second << ") >>>";
3985  xmlOut.addTextElementToParent("nodeDiff", missingSs.str(), parentEl);
3986  }
3987 
3988  __COUTS__(30) << "\t" << t.getValueName() << ": " << t.getValueAsString()
3989  << __E__;
3990 
3991  } //end diff tree handling
3992 
3993  xmlOut.addTextElementToParent("value", t.getValueAsString(), parentEl);
3994  parentEl = xmlOut.addTextElementToParent("valueType", t.getValueType(), parentEl);
3995 
3996  // fixed choice and bitmap both use fixed choices strings
3997  // so output them to xml
3998  if(t.getValueType() == TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA ||
3999  t.getValueType() == TableViewColumnInfo::TYPE_BITMAP_DATA)
4000  {
4001  __COUTS__(30) << t.getValueType() << __E__;
4002 
4003  std::vector<std::string> choices = t.getFixedChoices();
4004  for(const auto& choice : choices)
4005  xmlOut.addTextElementToParent("fixedChoice", choice, parentEl);
4006  }
4007  }
4008  else
4009  {
4010  __COUTS__(30) << "\t" << t.getValueAsString() << __E__;
4011 
4012  if(t.isLinkNode())
4013  {
4014  __COUTS__(30) << "\t\t" << t.getValueName() << ": " << t.getValueAsString()
4015  << __E__;
4016 
4017  // Note: The order of xml fields is required by JavaScript, so do NOT change
4018  // order.
4019  parentEl = xmlOut.addTextElementToParent("node", t.getValueName(), parentEl);
4020 
4021  if(diffTree.has_value())
4022  {
4023  __COUTS__(30) << "\t\t diff type " << diffTree->get().getNodeType()
4024  << __E__;
4025 
4026  if(diffTree->get()
4027  .isRootNode()) //then diff group does not have this uid!
4028  {
4029  __COUTS__(30) << "" << t.getValueAsString() << __E__;
4030  std::stringstream missingSs; //assume only one group loaded for diff
4031  //lookup group name in diffManager based on current node's parent's table (best proxy info for missing diff node at this point)
4032  auto diffGroupPair =
4033  diffTree->get().getConfigurationManager()->getGroupOfLoadedTable(
4034  t.getParentTableName());
4035  missingSs << "<<< Path not found in " << diffGroupPair.first << "("
4036  << diffGroupPair.second << ") >>>";
4037  xmlOut.addTextElementToParent("nodeDiff", missingSs.str(), parentEl);
4038  }
4039  else if(t.isDisconnected() != diffTree->get().isDisconnected())
4040  {
4041  __COUTS__(30) << "\t\t diff isDisconnected "
4042  << diffTree->get().isDisconnected() << __E__;
4043 
4044  std::stringstream missingSs; //assume only one group loaded for diff
4045  //lookup group name in diffManager based on current node's parent's table (best proxy info for diff node at this point)
4046  auto diffGroupPair =
4047  diffTree->get().getConfigurationManager()->getGroupOfLoadedTable(
4048  t.getParentTableName());
4049  missingSs << "<<< Link is "
4050  << (diffTree->get().isDisconnected() ? "DISCONNECTED"
4051  : "connected")
4052  << " in " << diffGroupPair.first << "("
4053  << diffGroupPair.second << ") >>>";
4054  xmlOut.addTextElementToParent("nodeDiff", missingSs.str(), parentEl);
4055  }
4056  else if(!t.isDisconnected() &&
4057  t.isUIDLinkNode() != diffTree->get().isUIDLinkNode())
4058  {
4059  __COUTS__(30) << "" << t.getValueAsString() << __E__;
4060  std::stringstream missingSs; //assume only one group loaded for diff
4061  //lookup group name in diffManager based on current node's parent's table (best proxy info for diff node at this point)
4062  auto diffGroupPair =
4063  diffTree->get().getConfigurationManager()->getGroupOfLoadedTable(
4064  t.getParentTableName());
4065  missingSs << "<<< Link is "
4066  << (diffTree->get().isUIDLinkNode() ? "a UID Link"
4067  : "a Group Link")
4068  << " in " << diffGroupPair.first << "("
4069  << diffGroupPair.second << ") >>>";
4070  xmlOut.addTextElementToParent("nodeDiff", missingSs.str(), parentEl);
4071  }
4072  else if(!t.isDisconnected() && t.isUIDLinkNode() &&
4073  t.getValueAsString() !=
4074  diffTree->get().getValueAsString()) //both are UID link
4075  {
4076  __COUTS__(30) << "" << t.getValueAsString() << __E__;
4077  std::stringstream missingSs; //assume only one group loaded for diff
4078  //lookup group name in diffManager based on current node's parent's table (best proxy info for diff node at this point)
4079  auto diffGroupPair =
4080  diffTree->get().getConfigurationManager()->getGroupOfLoadedTable(
4081  t.getParentTableName());
4082  missingSs << "<<< Link to '" << diffTree->get().getValueAsString()
4083  << "' in " << diffGroupPair.first << "("
4084  << diffGroupPair.second << ") >>>";
4085  xmlOut.addTextElementToParent("nodeDiff", missingSs.str(), parentEl);
4086  }
4087  else if(!t.isDisconnected() && !t.isUIDLinkNode()) //both are Group links
4088  {
4089  __COUTS__(30) << "" << t.getValueAsString() << __E__;
4090  std::stringstream missingSs; //assume only one group loaded for diff
4091 
4092  auto tchildren = t.getChildrenMap();
4093  auto dtchildren = diffTree->get().getChildrenMap();
4094  missingSs << "<<< Group link";
4095  if(tchildren.size() != dtchildren.size())
4096  missingSs << " has " << tchildren.size() << " vs "
4097  << dtchildren.size() << " children..";
4098  for(auto& tchild : tchildren)
4099  if(dtchildren.find(tchild.first) == dtchildren.end())
4100  missingSs << " '" << tchild.first << "' missing..";
4101  for(auto& dtchild : dtchildren)
4102  if(tchildren.find(dtchild.first) == tchildren.end())
4103  missingSs << " '" << dtchild.first << "' present...";
4104 
4105  //only add nodeDiff if ss has been appended
4106  if(missingSs.str().length() > std::string("<<< Group link").length())
4107  {
4108  auto diffGroupPair =
4109  diffTree->get()
4110  .getConfigurationManager()
4111  ->getGroupOfLoadedTable(diffTree->get().getTableName());
4112  missingSs << " in " << diffGroupPair.first << "("
4113  << diffGroupPair.second << ") >>>";
4114  xmlOut.addTextElementToParent(
4115  "nodeDiff", missingSs.str(), parentEl);
4116  }
4117  }
4118  else
4119  __COUTS__(30) << "" << t.getValueAsString() << __E__;
4120  } //end diff tree handling
4121 
4122  if(t.isDisconnected())
4123  {
4124  __COUTS__(30) << t.getValueName() << __E__;
4125 
4126  // xmlOut.addTextElementToParent("value", t.getValueAsString(), parentEl);
4127  // xmlOut.addTextElementToParent("DisconnectedLink", t.getValueAsString(),
4128  // parentEl);
4129 
4130  xmlOut.addTextElementToParent("valueType", t.getValueType(), parentEl);
4131 
4132  // add extra fields for disconnected link
4133  xmlOut.addTextElementToParent(
4134  (t.isGroupLinkNode() ? "Group" : "U") + std::string("ID"),
4136  parentEl);
4137  xmlOut.addTextElementToParent(
4138  "LinkTableName", t.getDisconnectedTableName(), parentEl);
4139  xmlOut.addTextElementToParent(
4140  "LinkIndex", t.getChildLinkIndex(), parentEl);
4141 
4142  // add fixed choices (in case link has them)
4143  xercesc::DOMElement* choicesParentEl =
4144  xmlOut.addTextElementToParent("fixedChoices", "", parentEl);
4145  // try
4146  //{
4147 
4148  std::vector<std::string> choices = t.getFixedChoices();
4149  __COUTS__(30) << "choices.size() " << choices.size() << __E__;
4150 
4151  for(const auto& choice : choices)
4152  xmlOut.addTextElementToParent("fixedChoice", choice, choicesParentEl);
4153  //}
4154  // catch(...)
4155  //{
4156  // __COUT__ << "Ignoring unknown fixed choice error"
4157  //} //ignore no fixed choices for disconnected
4158 
4159  return;
4160  }
4161  // else handle connected links
4162 
4163  xmlOut.addTextElementToParent(
4164  (t.isGroupLinkNode() ? "Group" : "U") + std::string("ID"),
4165  t.getValueAsString(),
4166  parentEl);
4167 
4168  xmlOut.addTextElementToParent("LinkTableName", t.getTableName(), parentEl);
4169  xmlOut.addTextElementToParent("LinkIndex", t.getChildLinkIndex(), parentEl);
4170 
4171  // add fixed choices (in case link has them)
4172  {
4173  xercesc::DOMElement* choicesParentEl =
4174  xmlOut.addTextElementToParent("fixedChoices", "", parentEl);
4175  std::vector<std::string> choices = t.getFixedChoices();
4176 
4177  for(const auto& choice : choices)
4178  xmlOut.addTextElementToParent("fixedChoice", choice, choicesParentEl);
4179  }
4180  }
4181  else // uid node (or root node)
4182  {
4183  __COUTS__(30) << "\t\t" << t.getValueAsString() << __E__;
4184  bool returnNode = true; // default to shown
4185 
4186  if(t.isUIDNode() && hideStatusFalse) // only show if status evaluates to true
4187  returnNode = t.isEnabled();
4188 
4189  if(returnNode)
4190  {
4191  parentEl =
4192  xmlOut.addTextElementToParent("node", t.getValueAsString(), parentEl);
4193  if(t.isUIDNode())
4194  {
4195  xmlOut.addTextElementToParent(
4196  "comment", t.getAuthor() + ": " + t.getComment(), parentEl);
4197  xmlOut.addTextElementToParent(
4198  "nodeStatus", t.isEnabled() ? "1" : "0", parentEl);
4199  }
4200 
4201  if(diffTree.has_value())
4202  {
4203  __COUTS__(30)
4204  << "\t\t diff type " << diffTree->get().getNodeType() << __E__;
4205 
4206  if(diffTree->get()
4207  .isRootNode()) //then diff group does not have this uid!
4208  {
4209  __COUTS__(30) << "" << t.getValueAsString() << __E__;
4210  std::stringstream
4211  missingSs; //assume only one group loaded for diff
4212  //lookup group name in diffManager based on current node's table (best proxy info for diff node at this point)
4213  auto diffGroupPair =
4214  diffTree->get()
4215  .getConfigurationManager()
4216  ->getGroupOfLoadedTable(t.getTableName());
4217  missingSs << "<<< Not in " << diffGroupPair.first << "("
4218  << diffGroupPair.second << ") >>>";
4219  xmlOut.addTextElementToParent(
4220  "nodeDiff", missingSs.str(), parentEl);
4221  }
4222  else
4223  __COUTS__(30) << "" << t.getValueAsString() << __E__;
4224  } //end diff tree handling
4225  }
4226  else //hiding node
4227  return; // done.. no further depth needed for node that is not shown
4228  }
4229 
4230  // if depth>=1 toXml all children
4231  // child.toXml(depth-1)
4232  if(depth >= 1)
4233  {
4234  __COUTS__(30) << "\t\t\t" << t.getValueAsString() << __E__;
4235  auto C = t.getChildren();
4236  for(auto& c : C)
4237  recursiveTreeToXML( //TODO -- implement diffTree for depth > 1 requests
4238  c.second,
4239  depth - 1,
4240  xmlOut,
4241  parentEl,
4242  hideStatusFalse);
4243  }
4244  }
4245 } // end recursiveTreeToXML()
4246 
4247 //==============================================================================
4254 void ConfigurationGUISupervisor::handleGetLinkToChoicesXML(
4255  HttpXmlDocument& xmlOut,
4256  ConfigurationManagerRW* cfgMgr,
4257  const std::string& linkToTableName,
4258  const TableVersion& linkToTableVersion,
4259  const std::string& linkIdType,
4260  const std::string& linkIndex,
4261  const std::string& linkInitId)
4262 try
4263 {
4264  // get table
4265  // if uid link
4266  // return all uids
4267  // if groupid link
4268  // find target column
4269  // create the set of values (unique values only)
4270  // note: insert group unions individually (i.e. groups | separated)
4271 
4272  // get table and activate target version
4273  // rename to re-use code template
4274  const std::string& tableName = linkToTableName;
4275  const TableVersion& version = linkToTableVersion;
4276  TableBase* table = cfgMgr->getTableByName(tableName);
4277  try
4278  {
4279  table->setActiveView(version);
4280  }
4281  catch(...)
4282  {
4283  __SUP_COUT__ << "Failed to find stored version, so attempting to load version: "
4284  << version << __E__;
4285  cfgMgr->getVersionedTableByName(tableName, version);
4286  }
4287 
4288  if(version != table->getViewVersion())
4289  {
4290  __SUP_SS__ << "Target table version (" << version
4291  << ") is not the currently active version (" << table->getViewVersion()
4292  << ". Try refreshing the tree." << __E__;
4293  __SUP_COUT_WARN__ << ss.str();
4294  __SS_THROW__;
4295  }
4296 
4297  __SUP_COUT__ << "Active version is " << table->getViewVersion() << __E__;
4298 
4299  if(linkIdType == "UID")
4300  {
4301  // give all UIDs
4302  unsigned int col = table->getView().getColUID();
4303  for(unsigned int row = 0; row < table->getView().getNumberOfRows(); ++row)
4304  xmlOut.addTextElementToData("linkToChoice",
4305  table->getView().getDataView()[row][col]);
4306  }
4307  else if(linkIdType == "GroupID")
4308  {
4309  // find target column
4310  // create the set of values (unique values only)
4311  // note: insert group unions individually (i.e. groups | separated)
4312 
4313  __SUP_COUTV__(linkIndex);
4314  __SUP_COUTV__(linkInitId);
4315 
4316  std::set<std::string> setOfGroupIDs =
4317  table->getView().getSetOfGroupIDs(linkIndex);
4318 
4319  // build list of groupids
4320  // always include initial link group id in choices
4321  // (even if not in set of group ids)
4322  bool foundInitId = false;
4323  for(const auto& groupID : setOfGroupIDs)
4324  {
4325  if(!foundInitId && linkInitId == groupID)
4326  foundInitId = true; // mark init id found
4327 
4328  xmlOut.addTextElementToData("linkToChoice", groupID);
4329  }
4330  // if init id was not found, add to list
4331  if(!foundInitId)
4332  xmlOut.addTextElementToData("linkToChoice", linkInitId);
4333 
4334  // give all UIDs
4335  unsigned int col = table->getView().getColUID();
4336  for(unsigned int row = 0; row < table->getView().getNumberOfRows(); ++row)
4337  {
4338  xmlOut.addTextElementToData("groupChoice",
4339  table->getView().getDataView()[row][col]);
4340  if(table->getView().isEntryInGroup(row, linkIndex, linkInitId))
4341  xmlOut.addTextElementToData("groupMember",
4342  table->getView().getDataView()[row][col]);
4343  }
4344  }
4345  else
4346  {
4347  __SUP_SS__ << "Unrecognized linkIdType '" << linkIdType << ".'" << __E__;
4348  __SS_THROW__;
4349  }
4350 } //end handleGetLinkToChoicesXML()
4351 catch(std::runtime_error& e)
4352 {
4353  __SUP_SS__ << "Error detected saving tree node!\n\n " << e.what() << __E__;
4354  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
4355  xmlOut.addTextElementToData("Error", ss.str());
4356 }
4357 catch(...)
4358 {
4359  __SUP_SS__ << "Error detected saving tree node!\n\n " << __E__;
4360  try
4361  {
4362  throw;
4363  } //one more try to printout extra info
4364  catch(const std::exception& e)
4365  {
4366  ss << "Exception message: " << e.what();
4367  }
4368  catch(...)
4369  {
4370  }
4371  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
4372  xmlOut.addTextElementToData("Error", ss.str());
4373 } //end handleGetLinkToChoicesXML() catch
4374 
4375 //==============================================================================
4377 void ConfigurationGUISupervisor::handleMergeGroupsXML(
4378  HttpXmlDocument& xmlOut,
4379  ConfigurationManagerRW* cfgMgr,
4380  const std::string& groupANameContext,
4381  const TableGroupKey& groupAKeyContext,
4382  const std::string& groupBNameContext,
4383  const TableGroupKey& groupBKeyContext,
4384  const std::string& groupANameConfig,
4385  const TableGroupKey& groupAKeyConfig,
4386  const std::string& groupBNameConfig,
4387  const TableGroupKey& groupBKeyConfig,
4388  const std::string& author,
4389  const std::string& mergeApproach)
4390 try
4391 {
4392  __SUP_COUT__ << "Merging context group pair " << groupANameContext << " ("
4393  << groupAKeyContext << ") & " << groupBNameContext << " ("
4394  << groupBKeyContext << ") and table group pair " << groupANameConfig
4395  << " (" << groupAKeyConfig << ") & " << groupBNameConfig << " ("
4396  << groupBKeyConfig << ") with approach '" << mergeApproach << __E__;
4397 
4398  // Merges group A and group B
4399  // with consideration for UID conflicts
4400  // Result is a new key of group A's name
4401  //
4402  // There 3 modes:
4403  // Rename -- All records from both groups are maintained, but conflicts from B
4404  // are renamed.
4405  // Must maintain a map of UIDs that are remapped to new name for
4406  // groupB, because linkUID fields must be preserved. Replace --
4407  // Any UID conflicts for a record are replaced by the record from group B.
4408  // Skip -- Any UID conflicts for a record are skipped so that group A record
4409  // remains
4410 
4411  // check valid mode
4412  if(!(mergeApproach == "Rename" || mergeApproach == "Replace" ||
4413  mergeApproach == "Skip"))
4414  {
4415  __SS__ << "Error! Invalid merge approach '" << mergeApproach << ".'" << __E__;
4416  __SS_THROW__;
4417  }
4418 
4419  std::map<std::string /*name*/, TableVersion /*version*/> memberMapAContext,
4420  memberMapBContext, memberMapAConfig, memberMapBConfig;
4421 
4422  // check if skipping group pairs
4423  bool skippingContextPair = false;
4424  bool skippingConfigPair = false;
4425  if(groupANameContext.size() == 0 || groupANameContext[0] == ' ' ||
4426  groupBNameContext.size() == 0 || groupBNameContext[0] == ' ')
4427  {
4428  skippingContextPair = true;
4429  __SUP_COUTV__(skippingContextPair);
4430  }
4431  if(groupANameConfig.size() == 0 || groupANameConfig[0] == ' ' ||
4432  groupBNameConfig.size() == 0 || groupBNameConfig[0] == ' ')
4433  {
4434  skippingConfigPair = true;
4435  __SUP_COUTV__(skippingConfigPair);
4436  }
4437 
4438  // get context group member maps
4439  if(!skippingContextPair)
4440  {
4441  cfgMgr->loadTableGroup(groupANameContext,
4442  groupAKeyContext,
4443  false /*doActivate*/,
4444  &memberMapAContext,
4445  0 /*progressBar*/,
4446  0 /*accumulateErrors*/,
4447  0 /*groupComment*/,
4448  0 /*groupAuthor*/,
4449  0 /*groupCreationTime*/,
4450  false /*doNotLoadMember*/,
4451  0 /*groupTypeString*/
4452  );
4453  __SUP_COUTV__(StringMacros::mapToString(memberMapAContext));
4454 
4455  cfgMgr->loadTableGroup(groupBNameContext,
4456  groupBKeyContext,
4457  false /*doActivate*/,
4458  &memberMapBContext,
4459  0 /*progressBar*/,
4460  0 /*accumulateErrors*/,
4461  0 /*groupComment*/,
4462  0 /*groupAuthor*/,
4463  0 /*groupCreationTime*/,
4464  false /*doNotLoadMember*/,
4465  0 /*groupTypeString*/
4466  );
4467 
4468  __SUP_COUTV__(StringMacros::mapToString(memberMapBContext));
4469  }
4470 
4471  // get table group member maps
4472  if(!skippingConfigPair)
4473  {
4474  cfgMgr->loadTableGroup(groupANameConfig,
4475  groupAKeyConfig,
4476  false /*doActivate*/,
4477  &memberMapAConfig,
4478  0 /*progressBar*/,
4479  0 /*accumulateErrors*/,
4480  0 /*groupComment*/,
4481  0 /*groupAuthor*/,
4482  0 /*groupCreationTime*/,
4483  false /*doNotLoadMember*/,
4484  0 /*groupTypeString*/
4485  );
4486  __SUP_COUTV__(StringMacros::mapToString(memberMapAConfig));
4487 
4488  cfgMgr->loadTableGroup(groupBNameConfig,
4489  groupBKeyConfig,
4490  false /*doActivate*/,
4491  &memberMapBConfig,
4492  0 /*progressBar*/,
4493  0 /*accumulateErrors*/,
4494  0 /*groupComment*/,
4495  0 /*groupAuthor*/,
4496  0 /*groupCreationTime*/,
4497  false /*doNotLoadMember*/,
4498  0 /*groupTypeString*/
4499  );
4500 
4501  __SUP_COUTV__(StringMacros::mapToString(memberMapBConfig));
4502  }
4503 
4504  // for each member of B
4505  // if not found in A member map, add it
4506  // if found in both member maps, and versions are different, load both tables and
4507  // merge
4508 
4509  std::map<std::pair<std::string /*original table*/, std::string /*original uidB*/>,
4510  std::string /*converted uidB*/>
4511  uidConversionMap;
4512  std::map<
4513  std::pair<std::string /*original table*/,
4514  std::pair<std::string /*group linkid*/, std::string /*original gidB*/>>,
4515  std::string /*converted gidB*/>
4516  groupidConversionMap;
4517 
4518  std::stringstream mergeReport;
4519  mergeReport << "======================================" << __E__;
4520  mergeReport << "Time of merge: " << StringMacros::getTimestampString() << __E__;
4521  mergeReport << "Merging context group pair " << groupANameContext << " ("
4522  << groupAKeyContext << ") & " << groupBNameContext << " ("
4523  << groupBKeyContext << ") and table group pair " << groupANameConfig
4524  << " (" << groupAKeyConfig << ") & " << groupBNameConfig << " ("
4525  << groupBKeyConfig << ") with approach '" << mergeApproach << __E__;
4526  mergeReport << "======================================" << __E__;
4527 
4528  // first loop create record conversion map, second loop implement merge (using
4529  // conversion map if Rename)
4530  for(unsigned int i = 0; i < 2; ++i)
4531  {
4532  if(i == 0 && mergeApproach != "Rename")
4533  continue; // only need to construct uidConversionMap for rename approach
4534 
4535  // loop for context and table pair types
4536  for(unsigned int j = 0; j < 2; ++j)
4537  {
4538  if(j == 0 && skippingContextPair) // context
4539  {
4540  __COUT__ << "Skipping context pair..." << __E__;
4541  continue;
4542  }
4543  else if(j == 1 && skippingConfigPair)
4544  {
4545  __COUT__ << "Skipping table pair..." << __E__;
4546  continue;
4547  }
4548 
4549  std::map<std::string /*name*/, TableVersion /*version*/>& memberMapAref =
4550  j == 0 ? memberMapAContext : memberMapAConfig;
4551 
4552  std::map<std::string /*name*/, TableVersion /*version*/>& memberMapBref =
4553  j == 0 ? memberMapBContext : memberMapBConfig;
4554 
4555  if(j == 0) // context
4556  __COUT__ << "Context pair..." << __E__;
4557  else
4558  __COUT__ << "Table pair..." << __E__;
4559 
4560  __COUT__ << "Starting member map B scan." << __E__;
4561  for(const auto& bkey : memberMapBref)
4562  {
4563  __SUP_COUTV__(bkey.first);
4564 
4565  if(memberMapAref.find(bkey.first) == memberMapAref.end())
4566  {
4567  mergeReport << "\n'" << mergeApproach << "'-Missing table '"
4568  << bkey.first << "' A=v" << -1 << ", adding B=v"
4569  << bkey.second << __E__;
4570 
4571  // not found, so add to A member map
4572  memberMapAref[bkey.first] = bkey.second;
4573  }
4574  else if(memberMapAref[bkey.first] != bkey.second)
4575  {
4576  // found table version confict
4577  __SUP_COUTV__(memberMapAref[bkey.first]);
4578  __SUP_COUTV__(bkey.second);
4579 
4580  // load both tables, and merge
4581  TableBase* table = cfgMgr->getTableByName(bkey.first);
4582 
4583  __SUP_COUT__ << "Got table." << __E__;
4584 
4585  TableVersion newVersion = table->mergeViews(
4586  cfgMgr
4587  ->getVersionedTableByName(bkey.first,
4588  memberMapAref[bkey.first])
4589  ->getView(),
4590  cfgMgr->getVersionedTableByName(bkey.first, bkey.second)
4591  ->getView(),
4592  TableVersion() /* destinationVersion*/,
4593  author,
4594  mergeApproach == "Rename"
4595  ? TableBase::MergeApproach::RENAME
4596  : (mergeApproach == "Replace"
4597  ? TableBase::MergeApproach::REPLACE
4598  : TableBase::MergeApproach::SKIP),
4599  uidConversionMap,
4600  groupidConversionMap,
4601  i == 0 /* fillRecordConversionMaps */,
4602  i == 1 /* applyRecordConversionMaps */,
4603  table->getTableName() ==
4604  ConfigurationManager::XDAQ_APPLICATION_TABLE_NAME
4605  /* generateUniqueDataColumns */,
4606  &mergeReport); // dont make destination version the first time
4607 
4608  if(i == 1)
4609  {
4610  __SUP_COUTV__(newVersion);
4611 
4612  try
4613  {
4614  // save all temporary tables to persistent tables
4615  // finish off the version creation
4616  newVersion =
4618  xmlOut,
4619  cfgMgr,
4620  bkey.first,
4621  TableVersion() /*original source version*/,
4622  false /* makeTemporary */,
4623  table,
4624  newVersion /*temporary modified version*/,
4625  false /*ignore duplicates*/,
4626  true /*look for equivalent*/);
4627  }
4628  catch(std::runtime_error& e)
4629  {
4630  __SUP_SS__
4631  << "There was an error saving the '"
4632  << table->getTableName()
4633  << "' merge result to a persistent table version. "
4634  << "Perhaps you can modify this table in one of the "
4635  "groups to resolve this issue, and then re-merge."
4636  << __E__ << e.what();
4637  __SS_THROW__;
4638  }
4639 
4640  __SUP_COUTV__(newVersion);
4641 
4642  memberMapAref[bkey.first] = newVersion;
4643  }
4644  } // end member version conflict handling
4645  } // end B member map loop
4646  } // end context and table loop
4647  } // end top level conversion map or not loop
4648 
4649  // Now save groups
4650 
4651  if(!skippingContextPair)
4652  {
4653  __SUP_COUT__ << "New context member map complete." << __E__;
4654  __SUP_COUTV__(StringMacros::mapToString(memberMapAContext));
4655 
4656  // save the new table group
4657  TableGroupKey newKeyContext = cfgMgr->saveNewTableGroup(
4658  groupANameContext,
4659  memberMapAContext,
4660  "Merger of group " + groupANameContext + " (" + groupAKeyContext.toString() +
4661  ") and " + groupBNameContext + " (" + groupBKeyContext.toString() + ").");
4662 
4663  // return new resulting group
4664  xmlOut.addTextElementToData("ContextGroupName", groupANameContext);
4665  xmlOut.addTextElementToData("ContextGroupKey", newKeyContext.toString());
4666  }
4667  if(!skippingConfigPair)
4668  {
4669  __SUP_COUT__ << "New table member map complete." << __E__;
4670  __SUP_COUTV__(StringMacros::mapToString(memberMapAConfig));
4671 
4672  // save the new table group
4673  TableGroupKey newKeyConfig = cfgMgr->saveNewTableGroup(
4674  groupANameConfig,
4675  memberMapAConfig,
4676  "Merger of group " + groupANameConfig + " (" + groupAKeyConfig.toString() +
4677  ") and " + groupBNameConfig + " (" + groupBKeyConfig.toString() + ").");
4678 
4679  // return new resulting group
4680  xmlOut.addTextElementToData("ConfigGroupName", groupANameConfig);
4681  xmlOut.addTextElementToData("ConfigGroupKey", newKeyConfig.toString());
4682  }
4683 
4684  // output merge report
4685  {
4686  std::string mergeReportBasePath = std::string(__ENV__("USER_DATA"));
4687  std::string mergeReportPath = "/ServiceData/";
4688  // make merge report directories in case they don't exist
4689  mkdir((mergeReportBasePath + mergeReportPath).c_str(), 0755);
4690  mergeReportPath += "ConfigurationGUI_mergeReports/";
4691  // make merge report directories in case they don't exist
4692  mkdir((mergeReportBasePath + mergeReportPath).c_str(), 0755);
4693 
4694  mergeReportPath +=
4695  "merge_" + std::to_string(time(0)) + "_" + std::to_string(clock()) + ".txt";
4696  __SUP_COUTV__(mergeReportPath);
4697 
4698  FILE* fp = fopen((mergeReportBasePath + mergeReportPath).c_str(), "w");
4699  if(fp)
4700  {
4701  fprintf(fp, "%s", mergeReport.str().c_str());
4702  fclose(fp);
4703  xmlOut.addTextElementToData("MergeReportFile",
4704  "/$USER_DATA/" + mergeReportPath);
4705  }
4706  else
4707  xmlOut.addTextElementToData("MergeReportFile", "FILE FAILURE");
4708  } // end output merge report
4709 
4710 } // end handleMergeGroupsXML()
4711 catch(std::runtime_error& e)
4712 {
4713  __SUP_SS__ << "Error merging context group pair " << groupANameContext << " ("
4714  << groupAKeyContext << ") & " << groupBNameContext << " ("
4715  << groupBKeyContext << ") and table group pair " << groupANameConfig
4716  << " (" << groupAKeyConfig << ") & " << groupBNameConfig << " ("
4717  << groupBKeyConfig << ") with approach '" << mergeApproach << "': \n\n"
4718  << e.what() << __E__;
4719  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
4720  xmlOut.addTextElementToData("Error", ss.str());
4721 }
4722 catch(...)
4723 {
4724  __SUP_SS__ << "Unknown error merging context group pair " << groupANameContext << " ("
4725  << groupAKeyContext << ") & " << groupBNameContext << " ("
4726  << groupBKeyContext << ") and table group pair " << groupANameConfig
4727  << " (" << groupAKeyConfig << ") & " << groupBNameConfig << " ("
4728  << groupBKeyConfig << ") with approach '" << mergeApproach << ".' \n\n";
4729  try
4730  {
4731  throw;
4732  } //one more try to printout extra info
4733  catch(const std::exception& e)
4734  {
4735  ss << "Exception message: " << e.what();
4736  }
4737  catch(...)
4738  {
4739  }
4740  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
4741  xmlOut.addTextElementToData("Error", ss.str());
4742 } // end handleMergeGroupsXML() catch
4743 
4744 //==============================================================================
4746 void ConfigurationGUISupervisor::handleSavePlanCommandSequenceXML(
4747  HttpXmlDocument& xmlOut,
4748  ConfigurationManagerRW* cfgMgr,
4749  const std::string& groupName,
4750  const TableGroupKey& groupKey,
4751  const std::string& modifiedTables,
4752  const std::string& author,
4753  const std::string& planName,
4754  const std::string& commandString)
4755 try
4756 {
4757  __COUT__ << "handleSavePlanCommandSequenceXML " << planName << __E__;
4758 
4759  // setup active tables based on input group and modified tables
4760  setupActiveTablesXML(xmlOut,
4761  cfgMgr,
4762  groupName,
4763  groupKey,
4764  modifiedTables,
4765  true /* refresh all */,
4766  false /* getGroupInfo */,
4767  0 /* returnMemberMap */,
4768  false /* outputActiveTables */);
4769 
4770  TableEditStruct planTable(IterateTable::PLAN_TABLE,
4771  cfgMgr); // Table ready for editing!
4772  TableEditStruct targetTable(IterateTable::TARGET_TABLE,
4773  cfgMgr); // Table ready for editing!
4774 
4775  // create table-edit struct for each table that an iterate command type can use
4776  //if two command types have same table, TableEditStruct returns the same temporary version of the table, but then modified_
4777  // will be maintained separately and saving the table becomes a mess.
4778  std::map<std::string /* table name */, TableEditStruct> commandTableToEditMap;
4779  for(const auto& commandPair : IterateTable::commandToTableMap_)
4780  if(commandPair.second != "") // skip tables with no parameters
4781  commandTableToEditMap.emplace(std::pair<std::string, TableEditStruct>(
4782  commandPair.second, TableEditStruct(commandPair.second, cfgMgr)));
4783 
4784  // try to catch any errors while editing..
4785  // if errors delete temporary plan view (if created here)
4786  try
4787  {
4788  // Steps:
4789  // Reset plan commands
4790  // Remove all commands in group "<plan>-Plan"
4791  // Delete linked command parameters row (in separate table)
4792  // If no group remaining, then delete row.
4793  //
4794  // Save plan commands (if modified)
4795  // Create rows and add them to group "<plan>-Plan"
4796  // create row for command paramaters and add to proper table
4797 
4798  std::string groupName = planName + "-Plan";
4799  __SUP_COUT__ << "Handling commands for group " << groupName << __E__;
4800 
4801  unsigned int groupIdCol =
4802  planTable.tableView_->findCol(IterateTable::planTableCols_.GroupID_);
4803  unsigned int cmdTypeCol =
4804  planTable.tableView_->findCol(IterateTable::planTableCols_.CommandType_);
4805 
4806  unsigned int targetGroupIdCol =
4807  targetTable.tableView_->findCol(IterateTable::targetCols_.GroupID_);
4808  unsigned int targetTableCol =
4809  targetTable.tableView_->findCol(IterateTable::targetCols_.TargetLink_);
4810  unsigned int targetUIDCol =
4811  targetTable.tableView_->findCol(IterateTable::targetCols_.TargetLinkUID_);
4812 
4813  std::string groupLinkIndex =
4814  planTable.tableView_->getColumnInfo(groupIdCol).getChildLinkIndex();
4815  __SUP_COUT__ << "groupLinkIndex: " << groupLinkIndex << __E__;
4816 
4817  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> commandUidLink;
4818  {
4819  bool isGroup; // local because we know is uid link
4820  planTable.tableView_->getChildLink(
4821  planTable.tableView_->findCol(IterateTable::planTableCols_.CommandLink_),
4822  isGroup,
4823  commandUidLink);
4824  }
4825 
4826  unsigned int cmdRow, cmdCol;
4827  std::string targetGroupName;
4828 
4829  // Reset existing plan commands
4830  {
4831  std::string targetUID, cmdType;
4832 
4833  for(unsigned int row = 0; row < planTable.tableView_->getNumberOfRows();
4834  ++row)
4835  {
4836  targetUID = planTable.tableView_
4837  ->getDataView()[row][planTable.tableView_->getColUID()];
4838  __SUP_COUT__ << "targetUID: " << targetUID << __E__;
4839 
4840  // remove command from plan group.. if no more groups, delete
4841  if(planTable.tableView_->isEntryInGroup(row, groupLinkIndex, groupName))
4842  {
4843  __SUP_COUT__ << "Removing." << __E__;
4844 
4845  // delete linked command
4846  // find linked UID in table (mapped by type)
4847  cmdType = planTable.tableView_->getDataView()[row][cmdTypeCol];
4848  auto cmdTypeTableIt = IterateTable::commandToTableMap_.find(cmdType);
4849  if(cmdTypeTableIt != IterateTable::commandToTableMap_.end() &&
4850  cmdTypeTableIt->second !=
4851  "") // skip if invalid command type or if no command parameter table
4852  {
4853  TableEditStruct& cmdTypeTableEdit =
4854  commandTableToEditMap.at(cmdTypeTableIt->second);
4855  cmdRow = cmdTypeTableEdit.tableView_->findRow(
4856  cmdTypeTableEdit.tableView_->getColUID(),
4857  planTable.tableView_
4858  ->getDataView()[row][commandUidLink.second]);
4859 
4860  // before deleting row...
4861  // look for target group
4862  // remove all targets in group
4863  try
4864  {
4865  cmdCol = cmdTypeTableEdit.tableView_->findCol(
4866  IterateTable::commandTargetCols_.TargetsLinkGroupID_);
4867  targetGroupName = cmdTypeTableEdit.tableView_
4868  ->getDataView()[cmdRow][cmdCol];
4869 
4870  for(unsigned int trow = 0;
4871  trow < targetTable.tableView_->getNumberOfRows();
4872  ++trow)
4873  {
4874  // remove command from target group..
4875  if(targetTable.tableView_->isEntryInGroup(
4876  trow,
4877  cmdTypeTableEdit.tableView_->getColumnInfo(cmdCol)
4878  .getChildLinkIndex(),
4879  targetGroupName))
4880  {
4881  __SUP_COUT__ << "Removing target." << __E__;
4882  // remove command entry in plan table
4883  if(targetTable.tableView_->removeRowFromGroup(
4884  trow,
4885  targetGroupIdCol,
4886  targetGroupName,
4887  true /*deleteRowIfNoGroup*/))
4888  --trow; // since row was deleted, go back!
4889  }
4890  }
4891  }
4892  catch(...)
4893  {
4894  __SUP_COUT__ << "No targets." << __E__;
4895  }
4896 
4897  // now no more targets, delete row
4898 
4899  cmdTypeTableEdit.tableView_->deleteRow(cmdRow);
4900 
4901  cmdTypeTableEdit.modified_ = true;
4902  }
4903 
4904  // remove command entry in plan table
4905  if(planTable.tableView_->removeRowFromGroup(
4906  row, groupIdCol, groupName, true /*deleteRowIfNoGroup*/))
4907  --row; // since row was deleted, go back!
4908  }
4909  }
4910  }
4911 
4912  // Done resetting existing plan
4913  // Now save new commands
4914 
4915  std::vector<IterateTable::Command> commands;
4916 
4917  // extract command sequence and add to table
4918  // into vector with type, and params
4919  {
4920  std::istringstream f(commandString);
4921  std::string commandSubString, paramSubString, paramValue;
4922  int i;
4923  while(getline(f, commandSubString, ';'))
4924  {
4925  __SUP_COUTT__ << "commandSubString " << commandSubString << __E__;
4926  std::istringstream g(commandSubString);
4927 
4928  i = 0;
4929  while(getline(g, paramSubString, ','))
4930  {
4931  __SUP_COUTT__ << "paramSubString " << paramSubString << __E__;
4932  if(i == 0) // type
4933  {
4934  if(paramSubString != "type")
4935  {
4936  __SUP_SS__ << "Invalid command sequence" << __E__;
4937  __SS_THROW__;
4938  }
4939  // create command object
4940  commands.push_back(IterateTable::Command());
4941 
4942  getline(g, paramValue, ',');
4943  ++i;
4944  __SUP_COUTT__ << "paramValue " << paramValue << __E__;
4945  commands.back().type_ = paramValue;
4946  }
4947  else // params
4948  {
4949  getline(g, paramValue, ',');
4950  ++i;
4951  __SUP_COUTT__ << "paramValue " << paramValue << __E__;
4952 
4953  commands.back().params_.emplace(
4954  std::pair<std::string /*param name*/,
4955  std::string /*param value*/>(
4956  paramSubString,
4957  StringMacros::decodeURIComponent(paramValue)));
4958  }
4959 
4960  ++i;
4961  }
4962  }
4963 
4964  } // end extract command sequence
4965 
4966  __SUP_COUT__ << "commands size " << commands.size() << __E__;
4967 
4968  // at this point, have extracted commands
4969 
4970  // now save commands to plan group
4971  // group should be "<plan>-Plan"
4972 
4973  unsigned int row, tgtRow;
4974  unsigned int targetIndex;
4975  std::string targetStr, cmdUID;
4976 
4977  for(auto& command : commands)
4978  {
4979  __SUP_COUT__ << "command " << command.type_ << __E__;
4980  __SUP_COUT__ << "table " << IterateTable::commandToTableMap_.at(command.type_)
4981  << __E__;
4982 
4983  // create command entry at plan level
4984  row = planTable.tableView_->addRow(
4985  author, true /*incrementUniqueData*/, "planCommand");
4986  planTable.tableView_->addRowToGroup(row, groupIdCol, groupName);
4987 
4988  // set command type
4989  planTable.tableView_->setURIEncodedValue(command.type_, row, cmdTypeCol);
4990 
4991  // set command status true
4992  planTable.tableView_->setValueAsString(
4993  "1", row, planTable.tableView_->getColStatus());
4994 
4995  // create command specifics
4996  auto cmdTypeTableIt = IterateTable::commandToTableMap_.find(command.type_);
4997  if(cmdTypeTableIt != IterateTable::commandToTableMap_.end() &&
4998  cmdTypeTableIt->second !=
4999  "") // skip if invalid command type or if no command parameter table
5000  {
5001  TableEditStruct& cmdTypeTableEdit =
5002  commandTableToEditMap.at(cmdTypeTableIt->second);
5003  __SUP_COUT__ << "table " << cmdTypeTableEdit.tableName_ << __E__;
5004 
5005  // at this point have table, tempVersion, and createdFlag
5006 
5007  // create command parameter entry at command level
5008  cmdRow = cmdTypeTableEdit.tableView_->addRow(
5009  author, true /*incrementUniqueData*/, command.type_ + "_COMMAND_");
5010 
5011  // parameters are linked
5012  // now set value of all parameters
5013  // find parameter column, and set value
5014  // if special target parameter, extract targets
5015  for(auto& param : command.params_)
5016  {
5017  __SUP_COUT__ << "\t param " << param.first << " : " << param.second
5018  << __E__;
5019 
5020  if(param.first == IterateTable::targetParams_.Tables_)
5021  {
5022  __SUP_COUT__ << "\t\t found target tables" << __E__;
5023  std::istringstream f(param.second);
5024 
5025  targetIndex = 0;
5026  while(getline(f, targetStr, '='))
5027  {
5028  __SUP_COUT__ << "\t\t targetStr = " << targetStr << __E__;
5029  if(!command.targets_.size() ||
5030  command.targets_.back().table_ != "")
5031  {
5032  __SUP_COUT__ << "\t\t make targetStr = " << targetStr
5033  << __E__;
5034  // make new target
5035  command.addTarget();
5036  command.targets_.back().table_ = targetStr;
5037  }
5038  else // file existing target
5039  command.targets_[targetIndex++].table_ = targetStr;
5040  }
5041 
5042  continue; // go to next parameter
5043  }
5044 
5045  if(param.first == IterateTable::targetParams_.UIDs_)
5046  {
5047  __SUP_COUT__ << "\t\t found target UIDs" << __E__;
5048  std::istringstream f(param.second);
5049 
5050  targetIndex = 0;
5051  while(getline(f, targetStr, '='))
5052  {
5053  __SUP_COUT__ << "\t\t targetStr = " << targetStr << __E__;
5054  if(!command.targets_.size() ||
5055  command.targets_.back().UID_ != "")
5056  {
5057  __SUP_COUT__ << "\t\t make targetStr = " << targetStr
5058  << __E__;
5059  // make new target
5060  command.addTarget();
5061  command.targets_.back().UID_ = targetStr;
5062  }
5063  else // file existing target
5064  command.targets_[targetIndex++].UID_ = targetStr;
5065  }
5066  continue;
5067  }
5068 
5069  cmdCol = cmdTypeTableEdit.tableView_->findCol(param.first);
5070 
5071  __SUP_COUT__ << "param col " << cmdCol << __E__;
5072 
5073  cmdTypeTableEdit.tableView_->setURIEncodedValue(
5074  param.second, cmdRow, cmdCol);
5075  } // end parameter loop
5076 
5077  cmdUID =
5078  cmdTypeTableEdit.tableView_
5079  ->getDataView()[cmdRow][cmdTypeTableEdit.tableView_->getColUID()];
5080 
5081  if(command.targets_.size())
5082  {
5083  // if targets, create group in target table
5084 
5085  __SUP_COUT__ << "targets found for command UID=" << cmdUID << __E__;
5086 
5087  // create link from command table to target
5088  cmdCol = cmdTypeTableEdit.tableView_->findCol(
5089  IterateTable::commandTargetCols_.TargetsLink_);
5090  cmdTypeTableEdit.tableView_->setValueAsString(
5091  IterateTable::TARGET_TABLE, cmdRow, cmdCol);
5092 
5093  cmdCol = cmdTypeTableEdit.tableView_->findCol(
5094  IterateTable::commandTargetCols_.TargetsLinkGroupID_);
5095  cmdTypeTableEdit.tableView_->setValueAsString(
5096  cmdUID + "_Targets", cmdRow, cmdCol);
5097 
5098  // create row(s) for each target in target table with correct groupID
5099 
5100  for(const auto& target : command.targets_)
5101  {
5102  __SUP_COUT__ << target.table_ << " " << target.UID_ << __E__;
5103 
5104  // create target entry in target table in group
5105  tgtRow = targetTable.tableView_->addRow(
5106  author, true /*incrementUniqueData*/, "commandTarget");
5107  targetTable.tableView_->addRowToGroup(
5108  tgtRow, targetGroupIdCol, cmdUID + "_Targets");
5109 
5110  // set target table
5111  targetTable.tableView_->setValueAsString(
5112  target.table_, tgtRow, targetTableCol);
5113 
5114  // set target UID
5115  targetTable.tableView_->setValueAsString(
5116  target.UID_, tgtRow, targetUIDCol);
5117  }
5118  } // end target handling
5119 
5120  // add link at plan level to created UID
5121  planTable.tableView_->setValueAsString(
5122  cmdTypeTableEdit.tableName_, row, commandUidLink.first);
5123  planTable.tableView_->setValueAsString(
5124  cmdUID, row, commandUidLink.second);
5125 
5126  __SUP_COUT__ << "linked to uid = " << cmdUID << __E__;
5127 
5128  cmdTypeTableEdit.modified_ = true;
5129  } // done with command specifics
5130 
5131  } // end command loop
5132 
5133  // commands are created in the temporary tables
5134  // validate with init
5135 
5136  planTable.tableView_->print();
5137  planTable.tableView_->init(); // verify new table (throws runtime_errors)
5138 
5139  __SUP_COUT__ << "requestType tables:" << __E__;
5140 
5141  for(auto& modifiedConfig : commandTableToEditMap)
5142  {
5143  __SUP_COUTV__(modifiedConfig.second.modified_);
5144  modifiedConfig.second.tableView_->print();
5145  modifiedConfig.second.tableView_->init();
5146  }
5147 
5148  targetTable.tableView_->print();
5149  targetTable.tableView_->init(); // verify new table (throws runtime_errors)
5150 
5151  } // end try for plan
5152  catch(...)
5153  {
5154  __SUP_COUT__ << "Handling command table errors while saving. Erasing all newly "
5155  "created versions."
5156  << __E__;
5157 
5158  // erase all temporary tables if created here
5159 
5160  if(planTable.createdTemporaryVersion_) // if temporary version created here
5161  {
5162  __SUP_COUT__ << "Erasing temporary version " << planTable.tableName_ << "-v"
5163  << planTable.temporaryVersion_ << __E__;
5164  // erase with proper version management
5165  cfgMgr->eraseTemporaryVersion(planTable.tableName_,
5166  planTable.temporaryVersion_);
5167  }
5168 
5169  if(targetTable.createdTemporaryVersion_) // if temporary version created here
5170  {
5171  __SUP_COUT__ << "Erasing temporary version " << targetTable.tableName_ << "-v"
5172  << targetTable.temporaryVersion_ << __E__;
5173  // erase with proper version management
5174  cfgMgr->eraseTemporaryVersion(targetTable.tableName_,
5175  targetTable.temporaryVersion_);
5176  }
5177 
5178  for(auto& modifiedConfig : commandTableToEditMap)
5179  {
5180  if(modifiedConfig.second
5181  .createdTemporaryVersion_) // if temporary version created here
5182  {
5183  __SUP_COUT__ << "Erasing temporary version "
5184  << modifiedConfig.second.tableName_ << "-v"
5185  << modifiedConfig.second.temporaryVersion_ << __E__;
5186  // erase with proper version management
5187  cfgMgr->eraseTemporaryVersion(modifiedConfig.second.tableName_,
5188  modifiedConfig.second.temporaryVersion_);
5189  }
5190  }
5191 
5192  throw; // re-throw
5193  }
5194 
5195  // all edits are complete and tables verified
5196  // need to save all edits properly
5197  // if not modified, discard
5198 
5200  xmlOut,
5201  cfgMgr,
5202  planTable.tableName_,
5203  planTable.originalVersion_,
5204  true /*make temporary*/,
5205  planTable.table_,
5206  planTable.temporaryVersion_,
5207  true /*ignoreDuplicates*/); // save temporary version properly
5208 
5209  __SUP_COUT__ << "Final plan version is " << planTable.tableName_ << "-v"
5210  << finalVersion << __E__;
5211 
5213  xmlOut,
5214  cfgMgr,
5215  targetTable.tableName_,
5216  targetTable.originalVersion_,
5217  true /*make temporary*/,
5218  targetTable.table_,
5219  targetTable.temporaryVersion_,
5220  true /*ignoreDuplicates*/); // save temporary version properly
5221 
5222  __SUP_COUT__ << "Final target version is " << targetTable.tableName_ << "-v"
5223  << finalVersion << __E__;
5224 
5225  for(auto& modifiedConfig : commandTableToEditMap)
5226  {
5227  if(!modifiedConfig.second.modified_)
5228  {
5229  if(modifiedConfig.second
5230  .createdTemporaryVersion_) // if temporary version created here
5231  {
5232  __SUP_COUT__ << "Erasing unmodified temporary version "
5233  << modifiedConfig.second.tableName_ << "-v"
5234  << modifiedConfig.second.temporaryVersion_ << __E__;
5235  // erase with proper version management
5236  cfgMgr->eraseTemporaryVersion(modifiedConfig.second.tableName_,
5237  modifiedConfig.second.temporaryVersion_);
5238  }
5239  continue;
5240  }
5241 
5243  xmlOut,
5244  cfgMgr,
5245  modifiedConfig.second.tableName_,
5246  modifiedConfig.second.originalVersion_,
5247  true /*make temporary*/,
5248  modifiedConfig.second.table_,
5249  modifiedConfig.second.temporaryVersion_,
5250  true /*ignoreDuplicates*/); // save temporary version properly
5251 
5252  __SUP_COUT__ << "Final version is " << modifiedConfig.second.tableName_ << "-v"
5253  << finalVersion << __E__;
5254  }
5255 
5256  handleFillModifiedTablesXML(xmlOut, cfgMgr);
5257 } // end handleSavePlanCommandSequenceXML()
5258 catch(std::runtime_error& e)
5259 {
5260  __SUP_SS__ << "Error detected saving Iteration Plan!\n\n " << e.what() << __E__;
5261  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
5262  xmlOut.addTextElementToData("Error", ss.str());
5263 }
5264 catch(...)
5265 {
5266  __SUP_SS__ << "Error detected saving Iteration Plan!\n\n " << __E__;
5267  try
5268  {
5269  throw;
5270  } //one more try to printout extra info
5271  catch(const std::exception& e)
5272  {
5273  ss << "Exception message: " << e.what();
5274  }
5275  catch(...)
5276  {
5277  }
5278  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
5279  xmlOut.addTextElementToData("Error", ss.str());
5280 } // end handleSavePlanCommandSequenceXML() catch
5281 
5282 //==============================================================================
5292 void ConfigurationGUISupervisor::handleSaveTreeNodeEditXML(HttpXmlDocument& xmlOut,
5293  ConfigurationManagerRW* cfgMgr,
5294  const std::string& tableName,
5295  TableVersion version,
5296  const std::string& type,
5297  const std::string& uid,
5298  const std::string& colName,
5299  const std::string& newValue,
5300  const std::string& author)
5301 try
5302 {
5303  __SUP_COUT__ << "Editing table " << tableName << "(" << version << ") uid=" << uid
5304  << " type=" << type << __E__;
5305 
5306  // get the current table/version
5307  // check if the value is new
5308  // if new edit value (in a temporary version only)
5309 
5310  // get table and activate target version
5311  TableBase* table = cfgMgr->getTableByName(tableName);
5312  try
5313  {
5314  table->setActiveView(version);
5315  }
5316  catch(...)
5317  {
5318  if(version.isTemporaryVersion())
5319  throw; // if temporary, there is no hope to find lost version
5320 
5321  __SUP_COUT__ << "Failed to find stored version, so attempting to load version: "
5322  << version << __E__;
5323  cfgMgr->getVersionedTableByName(tableName, version);
5324  }
5325 
5326  __SUP_COUT__ << "Active version is " << table->getViewVersion() << __E__;
5327  __SUP_COUTTV__(table->getView().getComment());
5328 
5329  if(version != table->getViewVersion())
5330  {
5331  __SUP_SS__ << "Target table version (" << version
5332  << ") is not the currently active version (" << table->getViewVersion()
5333  << "). Try refreshing the tree." << __E__;
5334  __SS_THROW__;
5335  }
5336 
5337  unsigned int col = -1;
5338  if(type == "uid" || type == "delete-uid" || type == "tree-copy")
5339  col = table->getView().getColUID();
5340  else if(type == "node-comment")
5341  col = table->getView().findCol(TableViewColumnInfo::COL_NAME_COMMENT);
5342  else if(type == "link-UID" || type == "link-GroupID" || type == "value" ||
5343  type == "value-groupid" || type == "value-bool" || type == "value-bitmap")
5344  col = table->getView().findCol(colName);
5345  else if(type == "table" || type == "link-comment" || type == "table-newGroupRow" ||
5346  type == "table-newUIDRow" || type == "table-newRow")
5347  ; // column N/A
5348  else
5349  {
5350  __SUP_SS__ << "Impossible! Unrecognized edit type: " << type << __E__;
5351  __SS_THROW__;
5352  }
5353 
5354  // check if the comment value is new before making temporary version
5355  if(type == "table" || type == "link-comment")
5356  {
5357  // editing comment, so check if comment is different
5358  if(table->getView().isURIEncodedCommentTheSame(newValue))
5359  {
5360  __SUP_SS__ << "Comment '" << StringMacros::decodeURIComponent(newValue)
5361  << "' is the same as the current comment. No need to save change."
5362  << __E__;
5363  __SS_THROW__;
5364  }
5365  }
5366 
5367  // version handling:
5368  // always make a new temporary-version from source-version
5369  // edit temporary-version
5370  // if edit fails
5371  // delete temporary-version
5372  // else
5373  // return new temporary-version
5374  // if source-version was temporary
5375  // then delete source-version
5376 
5377  TableVersion temporaryVersion = table->createTemporaryView(version);
5378 
5379  __SUP_COUT__ << "Created temporary version " << temporaryVersion << __E__;
5380 
5381  TableView* cfgView = table->getTemporaryView(temporaryVersion);
5382  cfgView->init(); // prepare maps
5383 
5384  __SUP_COUTTV__(table->getView().getComment());
5385 
5386  // edit/verify new table (throws runtime_errors)
5387  try
5388  {
5389  // have view so edit it
5390  if(type == "table" || type == "link-comment")
5391  {
5392  // edit comment
5393  cfgView->setURIEncodedComment(newValue);
5394  }
5395  else if(type == "table-newRow" || type == "table-newUIDRow")
5396  {
5397  // add row
5398  unsigned int row = cfgView->addRow(
5399  author, true /*incrementUniqueData*/, newValue /*baseNameAutoUID*/);
5400 
5401  // if TableViewColumnInfo::COL_NAME_STATUS exists, set it to true
5402  try
5403  {
5404  col = cfgView->getColStatus();
5405  cfgView->setValueAsString("1", row, col);
5406  }
5407  catch(...)
5408  {
5409  } // if not, ignore
5410 
5411  // set UID value
5412  cfgView->setURIEncodedValue(newValue, row, cfgView->getColUID());
5413  }
5414  else if(type == "table-newGroupRow")
5415  {
5416  // get index value and group id value
5417  unsigned int csvIndex = newValue.find(',');
5418 
5419  std::string linkIndex = newValue.substr(0, csvIndex);
5420  std::string groupId = newValue.substr(csvIndex + 1);
5421 
5422  // get new row UID value from second part of string
5423  csvIndex = groupId.find(',');
5424  std::string newRowUID = groupId.substr(csvIndex + 1);
5425  groupId = groupId.substr(0, csvIndex);
5426 
5427  __SUP_COUT__ << "newValue " << linkIndex << "," << groupId << "," << newRowUID
5428  << __E__;
5429 
5430  // add row
5431  unsigned int row = cfgView->addRow(author,
5432  true /*incrementUniqueData*/,
5433  newRowUID /*baseNameAutoID*/,
5434  -1 /* rowToAdd */,
5435  linkIndex,
5436  groupId);
5437 
5438  // set UID value
5439  cfgView->setURIEncodedValue(newRowUID, row, cfgView->getColUID());
5440 
5441  // find groupId column from link index
5442  col = cfgView->getLinkGroupIDColumn(linkIndex);
5443 
5444  // set group id
5445  cfgView->setURIEncodedValue(groupId, row, col);
5446 
5447  // if TableViewColumnInfo::COL_NAME_STATUS exists, set it to true
5448  try
5449  {
5450  col = cfgView->getColStatus();
5451  cfgView->setValueAsString("1", row, col);
5452  }
5453  catch(...)
5454  {
5455  } // if not, ignore
5456  }
5457  else if(type == "delete-uid")
5458  {
5459  // delete row
5460  unsigned int row = cfgView->findRow(col, uid);
5461  cfgView->deleteRow(row);
5462  }
5463  else if(type == "tree-copy")
5464  {
5465  // recursively copy to depth
5466  __COUTV__(newValue);
5467  std::vector<std::string> paramArray =
5469  __COUTV__(StringMacros::vectorToString(paramArray));
5470 
5471  if(paramArray.size() != 2)
5472  {
5473  __SS__ << "Illegal parameters for tree copy request: must be number of "
5474  "copy instances & depth of copy."
5475  << __E__;
5476  __SS_THROW__;
5477  }
5478 
5479  unsigned int row = cfgView->findRow(col, uid);
5480  __COUTV__(uid);
5481  __COUTV__(row);
5482  unsigned int numberOfInstances = atoi(paramArray[0].c_str());
5483  unsigned int depth = atoi(paramArray[1].c_str());
5484  __COUTV__(depth);
5485  __COUTV__(numberOfInstances);
5486  if(numberOfInstances > 1000)
5487  {
5488  __SS__ << "Illegal parameters - the maximum number of copy instances is "
5489  "1000. Number of instances provided was "
5490  << numberOfInstances << __E__;
5491  __SS_THROW__;
5492  }
5493 
5494  std::map<std::string /*modified table*/, TableVersion /* modified version */>
5495  modifiedTablesMap = cfgMgr->getActiveVersions(); // handling copied from
5496  // ConfigurationGUISupervisor::handleFillModifiedTablesXML()
5497  ConfigurationSupervisorBase::recursiveCopyTreeUIDNode(xmlOut,
5498  cfgMgr,
5499  modifiedTablesMap,
5500  depth,
5501  depth,
5502  numberOfInstances,
5503  cfgView,
5504  uid);
5505  }
5506  else if(type == "uid" || type == "value" || type == "value-groupid" ||
5507  type == "value-bool" || type == "value-bitmap" || type == "node-comment")
5508  {
5509  unsigned int row = cfgView->findRow(cfgView->getColUID(), uid);
5510  if(!cfgView->setURIEncodedValue(newValue, row, col, author))
5511  {
5512  // no change! so discard
5513  __SUP_SS__ << "Value '" << newValue
5514  << "' is the same as the current value. No need to save "
5515  "change to tree node."
5516  << __E__;
5517  __SS_THROW__;
5518  }
5519  }
5520  else if(type == "link-UID" || type == "link-GroupID")
5521  {
5522  bool isGroup;
5523  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> linkPair;
5524  if(!cfgView->getChildLink(col, isGroup, linkPair))
5525  {
5526  // not a link ?!
5527  __SUP_SS__ << "Col '" << colName << "' is not a link column." << __E__;
5528  __SS_THROW__;
5529  }
5530 
5531  __SUP_COUT__ << "linkPair " << linkPair.first << "," << linkPair.second
5532  << __E__;
5533 
5534  std::string linkIndex = cfgView->getColumnInfo(col).getChildLinkIndex();
5535 
5536  __SUP_COUT__ << "linkIndex " << linkIndex << __E__;
5537 
5538  // find table value and id value
5539  unsigned int csvIndexStart = 0, csvIndex = newValue.find(',');
5540 
5541  std::string newTable = newValue.substr(csvIndexStart, csvIndex);
5542  csvIndexStart = csvIndex + 1;
5543  csvIndex = newValue.find(',', csvIndexStart);
5544  std::string newLinkId = newValue.substr(
5545  csvIndexStart,
5546  csvIndex -
5547  csvIndexStart); // if no more commas will take the rest of string
5548 
5549  __SUP_COUT__ << "newValue " << newTable << "," << newLinkId << __E__;
5550 
5551  // change target table in two parts
5552  unsigned int row = cfgView->findRow(cfgView->getColUID(), uid);
5553  bool changed = false;
5554  bool needSecondaryChange = (type == "link-GroupID");
5555 
5556  if(!cfgView->setURIEncodedValue(newTable, row, linkPair.first, author))
5557  {
5558  // no change
5559  __SUP_COUT__ << "Value '" << newTable
5560  << "' is the same as the current value." << __E__;
5561  }
5562  else
5563  {
5564  changed = true;
5565  // do NOT need secondary change for UID
5566  }
5567 
5568  std::string originalValue = cfgView->getValueAsString(row, linkPair.second);
5569  if(!cfgView->setURIEncodedValue(newLinkId, row, linkPair.second, author))
5570  {
5571  // no change
5572  __SUP_COUT__ << "Value '" << newLinkId
5573  << "' is the same as the current value." << __E__;
5574  }
5575  else
5576  {
5577  if(!changed)
5578  needSecondaryChange =
5579  true; // if table was unchanged, then need secondary change for
5580  // UID (groupID is already assumed needed)
5581  changed = true;
5582  }
5583 
5584  if(needSecondaryChange) // do secondary changes to child table target
5585  {
5586  bool secondaryChanged = false;
5587  bool defaultIsInGroup =
5588  false; // use to indicate if a recent new member was created
5589 
5590  // first close out main target table
5591  if(!changed) // if no changes throw out new version
5592  {
5593  __SUP_COUT__ << "No changes to primary view. Erasing temporary table."
5594  << __E__;
5595  table->eraseView(temporaryVersion);
5596  }
5597  else // if changes, save it
5598  {
5599  try
5600  {
5601  cfgView->init(); // verify new table (throws runtime_errors)
5602 
5604  xmlOut,
5605  cfgMgr,
5606  tableName,
5607  version,
5608  true /*make temporary*/,
5609  table,
5610  temporaryVersion,
5611  true /*ignoreDuplicates*/); // save
5612  // temporary
5613  // version
5614  // properly
5615  }
5616  catch(std::runtime_error&
5617  e) // erase temporary view before re-throwing error
5618  {
5619  __SUP_COUT__ << "Caught error while editing main table. Erasing "
5620  "temporary version."
5621  << __E__;
5622  table->eraseView(temporaryVersion);
5623  changed = false; // undo changed bool
5624 
5625  // send warning so that, secondary table can still be changed
5626  xmlOut.addTextElementToData(
5627  "Warning",
5628  "Error saving primary tree node! " + std::string(e.what()));
5629  }
5630  }
5631 
5632  // now, onto linked table
5633 
5634  // get the current linked table/version
5635  // check if the value is new
5636  // if new edit value (in a temporary version only)
5637 
5638  __SUP_COUTV__(newValue);
5639  csvIndexStart = csvIndex + 1;
5640  csvIndex = newValue.find(',', csvIndexStart);
5641  version = TableVersion(newValue.substr(
5642  csvIndexStart, csvIndex - csvIndexStart)); // if no more commas will
5643  // take the rest of string
5644 
5645  if(newTable == TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
5646  {
5647  // done, since init was already tested
5648  // the result should be purposely DISCONNECTED link
5649  return;
5650  }
5651 
5652  // get table and activate target version
5653  table = cfgMgr->getTableByName(newTable);
5654  try
5655  {
5656  table->setActiveView(version);
5657  }
5658  catch(...)
5659  {
5660  if(version.isTemporaryVersion())
5661  throw; // if temporary, there is no hope to find lost version
5662 
5663  __SUP_COUT__ << "Failed to find stored version, so attempting to "
5664  "load version: "
5665  << newTable << " v" << version << __E__;
5666  cfgMgr->getVersionedTableByName(newTable, version);
5667  }
5668 
5669  __SUP_COUT__ << newTable << " active version is "
5670  << table->getViewVersion() << __E__;
5671 
5672  if(version != table->getViewVersion())
5673  {
5674  __SUP_SS__;
5675  if(version.isMockupVersion())
5676  ss << "Target table '" << newTable
5677  << "' is likely not a member of the current table group "
5678  << "since the mock-up version was not successfully loaded. "
5679  << "\n\n"
5680  <<
5681  // same as ConfigurationGUI.html L:9833
5682  (std::string("") +
5683  "To add a table to a group, click the group name to go to "
5684  "the " +
5685  "group view, then click 'Add/Remove/Modify Member Tables.' "
5686  "You " +
5687  "can then add or remove tables and save the new group." +
5688  "\n\n" +
5689  "OR!!! Click the following button to add the table '" +
5690  newTable +
5691  "' to the currently active Configuration Group: " +
5692  "<input type='button' style='color:black !important;' " +
5693  "title='Click to add table to the active Configuration "
5694  "Group' " +
5695  "onclick='addTableToConfigurationGroup(\"" + newTable +
5696  "\"); Debug.closeErrorPop();event.stopPropagation();' "
5697  "value='Add Table'>" +
5698  "</input>")
5699  << __E__;
5700  else
5701  ss << "Target table version (" << version
5702  << ") is not the currently active version ("
5703  << table->getViewVersion() << "). Try refreshing the tree."
5704  << __E__;
5705  __SS_THROW__;
5706  }
5707 
5708  // create temporary version for editing
5709  temporaryVersion = table->createTemporaryView(version);
5710 
5711  __SUP_COUT__ << "Created temporary version " << temporaryVersion << __E__;
5712 
5713  cfgView = table->getTemporaryView(temporaryVersion);
5714 
5715  cfgView->init(); // prepare column lookup map
5716 
5717  if(type == "link-UID")
5718  {
5719  // handle UID links slightly differently
5720  // when editing link-UID,.. if specified name does not exist in child
5721  // table, then change the UID in the child table (rename target
5722  // record).
5723  // Otherwise, it is impossible to rename unique links targets in the
5724  // tree-view GUI.
5725 
5726  col = cfgView->getColUID();
5727  __SUP_COUT__ << "target col " << col << __E__;
5728 
5729  unsigned int row = -1;
5730  try
5731  {
5732  row = cfgView->findRow(col, newLinkId);
5733  }
5734  catch(...) // ignore not found error
5735  {
5736  }
5737  if(row == (unsigned int)-1) // if row not found then add a row
5738  {
5739  __SUP_COUT__ << "New link UID '" << newLinkId
5740  << "' was not found, so attempting to change UID of "
5741  "target record '"
5742  << originalValue << "'" << __E__;
5743  try
5744  {
5745  row = cfgView->findRow(col, originalValue);
5746  if(cfgView->setURIEncodedValue(newLinkId, row, col, author))
5747  {
5748  secondaryChanged = true;
5749  __SUP_COUT__ << "Original target record '"
5750  << originalValue << "' was changed to '"
5751  << newLinkId << "'" << __E__;
5752  }
5753  }
5754  catch(...) // ignore not found error
5755  {
5756  __SUP_COUT__ << "Original target record '" << originalValue
5757  << "' not found." << __E__;
5758  }
5759  }
5760  }
5761  else if(type == "link-GroupID")
5762  {
5763  // handle groupID links slightly differently
5764  // have to look at changing link table too!
5765  // if group ID, set all in member list to be members of group
5766 
5767  col = cfgView->getLinkGroupIDColumn(linkIndex);
5768 
5769  __SUP_COUT__ << "target col " << col << __E__;
5770 
5771  // extract vector of members to be
5772  std::vector<std::string> memberUIDs;
5773  do
5774  {
5775  csvIndexStart = csvIndex + 1;
5776  csvIndex = newValue.find(',', csvIndexStart);
5777  memberUIDs.push_back(
5778  newValue.substr(csvIndexStart, csvIndex - csvIndexStart));
5779  __SUP_COUT__ << "memberUIDs: " << memberUIDs.back() << __E__;
5780  } while(csvIndex !=
5781  (unsigned int)std::string::npos); // no more commas
5782 
5783  // for each row,
5784  // check if should be in group
5785  // if should be but is not
5786  // add to group, CHANGE
5787  // if should not be but is
5788  // remove from group, CHANGE
5789  //
5790 
5791  std::string targetUID;
5792  bool shouldBeInGroup;
5793  bool isInGroup;
5794 
5795  for(unsigned int row = 0; row < cfgView->getNumberOfRows(); ++row)
5796  {
5797  targetUID = cfgView->getDataView()[row][cfgView->getColUID()];
5798  __SUP_COUT__ << "targetUID: " << targetUID << __E__;
5799 
5800  shouldBeInGroup = false;
5801  for(unsigned int i = 0; i < memberUIDs.size(); ++i)
5802  if(targetUID == memberUIDs[i])
5803  {
5804  // found in member uid list
5805  shouldBeInGroup = true;
5806  break;
5807  }
5808 
5809  isInGroup = cfgView->isEntryInGroup(row, linkIndex, newLinkId);
5810 
5811  // if should be but is not
5812  if(shouldBeInGroup && !isInGroup)
5813  {
5814  __SUP_COUT__ << "Changed to YES: " << row << __E__;
5815  secondaryChanged = true;
5816 
5817  cfgView->addRowToGroup(row, col, newLinkId);
5818 
5819  } // if should not be but is
5820  else if(!shouldBeInGroup && isInGroup)
5821  {
5822  __SUP_COUT__ << "Changed to NO: " << row << __E__;
5823  secondaryChanged = true;
5824 
5825  cfgView->removeRowFromGroup(row, col, newLinkId);
5826  }
5827  else if(targetUID ==
5828  cfgView
5829  ->getDefaultRowValues()[cfgView->getColUID()] &&
5830  isInGroup)
5831  {
5832  // use to indicate if a recent new member was created
5833  defaultIsInGroup = true;
5834  }
5835  }
5836  } // end (type == "link-GroupID")
5837 
5838  // first close out main target table
5839  if(!secondaryChanged) // if no changes throw out new version
5840  {
5841  __SUP_COUT__
5842  << "No changes to secondary view. Erasing temporary table."
5843  << __E__;
5844  table->eraseView(temporaryVersion);
5845  }
5846  else // if changes, save it
5847  {
5848  try
5849  {
5850  cfgView->init(); // verify new table (throws runtime_errors)
5851 
5853  xmlOut,
5854  cfgMgr,
5855  newTable,
5856  version,
5857  true /*make temporary*/,
5858  table,
5859  temporaryVersion,
5860  true /*ignoreDuplicates*/); // save
5861  // temporary
5862  // version
5863  // properly
5864  }
5865  catch(std::runtime_error&
5866  e) // erase temporary view before re-throwing error
5867  {
5868  __SUP_COUT__ << "Caught error while editing secondary table. "
5869  "Erasing temporary version."
5870  << __E__;
5871  table->eraseView(temporaryVersion);
5872  secondaryChanged = false; // undo changed bool
5873 
5874  // send warning so that, secondary table can still be changed
5875  xmlOut.addTextElementToData(
5876  "Warning",
5877  "Error saving secondary tree node! " + std::string(e.what()));
5878  }
5879  }
5880 
5881  // Block error message if default is in group, assume new member was just
5882  // created. Blocked because its hard to detect if changes were recently
5883  // made (one idea: to check if all other values are defaults, to assume it
5884  // was just created)
5885  if(0 && !changed && !secondaryChanged && !defaultIsInGroup)
5886  {
5887  __SUP_SS__ << "Link to table '" << newTable << "', linkID '"
5888  << newLinkId
5889  << "', and selected group members are the same as the "
5890  "current value. "
5891  << "No need to save changes to tree." << __E__;
5892  __SS_THROW__;
5893  }
5894 
5895  return; // exit since table inits were already tested
5896  }
5897  else if(0 && !changed) // '0 &&' to block error message because sometimes
5898  // things get setup twice depending on the path of the
5899  // user (e.g. when editing links in tree-view)
5900  { // '0 &&' to block error message also because versions are temporary at
5901  // this point anyway, might as well abuse temporary versions
5902  __SUP_SS__ << "Link to table '" << newTable << "' and linkID '"
5903  << newLinkId
5904  << "' are the same as the current values. No need to save "
5905  "change to tree node."
5906  << __E__;
5907  __SS_THROW__;
5908  }
5909  }
5910 
5911  cfgView->init(); // verify new table (throws runtime_errors)
5912  }
5913  catch(...) // erase temporary view before re-throwing error
5914  {
5915  __SUP_COUT__ << "Caught error while editing. Erasing temporary version." << __E__;
5916  table->eraseView(temporaryVersion);
5917  throw;
5918  }
5919 
5921  xmlOut,
5922  cfgMgr,
5923  tableName,
5924  version,
5925  true /*make temporary*/,
5926  table,
5927  temporaryVersion,
5928  true /*ignoreDuplicates*/); // save temporary version properly
5929 } //end handleSaveTreeNodeEditXML()
5930 catch(std::runtime_error& e)
5931 {
5932  __SUP_SS__ << "Error saving tree node! " << e.what() << __E__;
5933  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
5934  xmlOut.addTextElementToData("Error", ss.str());
5935 }
5936 catch(...)
5937 {
5938  __SUP_SS__ << "Unknown Error saving tree node! " << __E__;
5939  try
5940  {
5941  throw;
5942  } //one more try to printout extra info
5943  catch(const std::exception& e)
5944  {
5945  ss << "Exception message: " << e.what();
5946  }
5947  catch(...)
5948  {
5949  }
5950  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
5951  xmlOut.addTextElementToData("Error", ss.str());
5952 } //end handleSaveTreeNodeEditXML() catch
5953 
5954 //==============================================================================
5995 void ConfigurationGUISupervisor::handleGetTableXML(HttpXmlDocument& xmlOut,
5996  ConfigurationManagerRW* cfgMgr,
5997  const std::string& tableName,
5998  TableVersion version,
5999  bool allowIllegalColumns /* = false */,
6000  bool getRawData /* = false */,
6001  bool descriptionOnly /* = false */)
6002 try
6003 {
6004  char tmpIntStr[100];
6005  xercesc::DOMElement *parentEl, *subparentEl;
6006 
6007  std::string accumulatedErrors = "";
6008 
6009  if(allowIllegalColumns)
6010  xmlOut.addTextElementToData("allowIllegalColumns", "1");
6011 
6012  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo(
6013  allowIllegalColumns /* if allowIllegalColumns, then also refresh */,
6014  allowIllegalColumns ? &accumulatedErrors : 0,
6015  tableName); // filter errors by tableName
6016 
6017  TableBase* table = cfgMgr->getTableByName(tableName);
6018 
6019  if(!getRawData)
6020  {
6021  // send all table names along with
6022  // and check for specific version
6023  xmlOut.addTextElementToData("ExistingTableNames",
6024  TableViewColumnInfo::DATATYPE_LINK_DEFAULT);
6025  for(auto& configPair : allTableInfo)
6026  {
6027  xmlOut.addTextElementToData("ExistingTableNames", configPair.first);
6028  if(configPair.first == tableName && // check that version exists
6029  configPair.second.versions_.find(version) ==
6030  configPair.second.versions_.end())
6031  {
6032  __SUP_COUT__ << "Version not found, so using mockup." << __E__;
6033  version = TableVersion(); // use INVALID
6034  }
6035  }
6036  }
6037 
6038  xmlOut.addTextElementToData("TableName", tableName); // table name
6039  xmlOut.addTextElementToData("TableDescription",
6040  table->getTableDescription()); // table name
6041 
6042  if(descriptionOnly)
6043  return;
6044 
6045  // existing table versions
6046  if(!getRawData)
6047  {
6048  // get version aliases for translation
6049  std::map<
6050  std::string /*table name*/,
6051  std::map<std::string /*version alias*/, TableVersion /*aliased version*/>>
6052  versionAliases;
6053  try
6054  {
6055  // use whatever backbone is currently active
6056  versionAliases = cfgMgr->getVersionAliases();
6057  for(const auto& aliases : versionAliases)
6058  for(const auto& alias : aliases.second)
6059  __SUP_COUTT__ << "ALIAS: " << aliases.first << " " << alias.first
6060  << " ==> " << alias.second << __E__;
6061  }
6062  catch(const std::runtime_error& e)
6063  {
6064  __SUP_COUT__ << "Could not get backbone information for version aliases: "
6065  << e.what() << __E__;
6066  }
6067 
6068  auto tableIterator = versionAliases.find(tableName);
6069 
6070  parentEl = xmlOut.addTextElementToData("TableVersions", "");
6071  //add lo and hi spans, instead of each individual value
6072  TableVersion lo, hi;
6073  for(const TableVersion& v : allTableInfo.at(tableName).versions_)
6074  {
6075  //Steps:
6076  // 1. check for version aliases
6077  // 2. if version aliases, leave as standalone version
6078  // else stack spans of versions for faster xml transfer
6079  std::vector<std::string> aliases;
6080  if(tableIterator != versionAliases.end())
6081  {
6082  // check if this version has one or many aliases
6083  for(const auto& aliasPair : tableIterator->second)
6084  {
6085  if(v == aliasPair.second)
6086  {
6087  __SUP_COUT__ << "Found Alias " << aliasPair.second << " --> "
6088  << aliasPair.first << __E__;
6089  aliases.push_back(aliasPair.first);
6090  }
6091  }
6092  }
6093  //now have version aliases or not
6094 
6095  if(aliases.size()) //keep versions with aliases standalone
6096  {
6097  __SUP_COUT__ << "Handling version w/aliases" << __E__;
6098  }
6099  else if(lo.version() ==
6100  TableVersion::INVALID) //establish start of potential span
6101  {
6102  hi = lo = v;
6103  continue;
6104  }
6105  else if(hi.version() + 1 == v.version()) //span is growing
6106  {
6107  hi = v;
6108  continue;
6109  }
6110  //else jump by more than one, so close out span
6111 
6112  if(lo.version() != TableVersion::INVALID)
6113  {
6114  if(lo == hi) //single value
6115  xmlOut.addTextElementToParent("Version", lo.toString(), parentEl);
6116  else //span
6117  xmlOut.addTextElementToParent(
6118  "Version", "_" + lo.toString() + "_" + hi.toString(), parentEl);
6119  }
6120  hi = lo = v.version();
6121 
6122  if(versionAliases.size()) //keep versions with aliases standalone
6123  {
6124  subparentEl =
6125  xmlOut.addTextElementToParent("Version", v.toString(), parentEl);
6126  for(const auto& alias : aliases)
6127  xmlOut.addTextElementToParent("VersionAlias", alias, subparentEl);
6128  hi = lo = TableVersion::INVALID; //invalidate for fresh start
6129  } //end version alias handling
6130 
6131  } //end version loop
6132 
6133  if(lo.version() != TableVersion::INVALID) //check if last one to do!
6134  {
6135  if(lo == hi) //single value
6136  xmlOut.addTextElementToParent("Version", lo.toString(), parentEl);
6137  else //span
6138  xmlOut.addTextElementToParent(
6139  "Version", "_" + lo.toString() + "_" + hi.toString(), parentEl);
6140  }
6141  } //end existing table version handling
6142 
6143  // table columns and then rows (from table view)
6144 
6145  // get view pointer
6146  TableView* tableViewPtr;
6147  if(version.isInvalid()) // use mock-up
6148  {
6149  tableViewPtr = table->getMockupViewP();
6150  }
6151  else // use view version
6152  {
6153  try
6154  {
6155  // locally accumulate 'manageable' errors getting the version to avoid
6156  // reverting to mockup
6157  std::string localAccumulatedErrors = "";
6158  tableViewPtr =
6159  cfgMgr
6160  ->getVersionedTableByName(tableName,
6161  version,
6162  allowIllegalColumns /*looseColumnMatching*/,
6163  &localAccumulatedErrors,
6164  getRawData)
6165  ->getViewP();
6166 
6167  if(getRawData)
6168  {
6169  xmlOut.addTextElementToData("TableRawData",
6170  tableViewPtr->getSourceRawData());
6171 
6172  const std::set<std::string>& srcColNames =
6173  tableViewPtr->getSourceColumnNames();
6174  for(auto& srcColName : srcColNames)
6175  xmlOut.addTextElementToData("ColumnHeader", srcColName);
6176 
6177  if(!version.isTemporaryVersion())
6178  {
6179  // if version is temporary, view is already ok
6180  table->eraseView(
6181  version); // clear so that the next get will fill the table
6182  tableViewPtr = cfgMgr
6184  tableName,
6185  version,
6186  allowIllegalColumns /*looseColumnMatching*/,
6187  &localAccumulatedErrors,
6188  false /* getRawData */)
6189  ->getViewP();
6190  }
6191  } // end rawData handling
6192 
6193  if(localAccumulatedErrors != "")
6194  xmlOut.addTextElementToData("Error", localAccumulatedErrors);
6195  }
6196  catch(std::runtime_error& e) // default to mock-up for fail-safe in GUI editor
6197  {
6198  __SUP_SS__ << "Failed to get table " << tableName << " version " << version
6199  << "... defaulting to mock-up! " << __E__;
6200  ss << "\n\n...Here is why it failed:\n\n" << e.what() << __E__;
6201 
6202  __SUP_COUT_ERR__ << "\n" << ss.str();
6203  version = TableVersion();
6204  tableViewPtr = table->getMockupViewP();
6205 
6206  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
6207  }
6208  catch(...) // default to mock-up for fail-safe in GUI editor
6209  {
6210  __SUP_SS__ << "Failed to get table " << tableName << " version: " << version
6211  << "... defaulting to mock-up! "
6212  << "(You may want to try again to see what was partially loaded "
6213  "into cache before failure. "
6214  << "If you think, the failure is due to a column name change, "
6215  << "you can also try to Copy the failing view to the new column "
6216  "names using "
6217  << "'Copy and Move' functionality.)" << __E__;
6218  try
6219  {
6220  throw;
6221  } //one more try to printout extra info
6222  catch(const std::exception& e)
6223  {
6224  ss << "Exception message: " << e.what();
6225  }
6226  catch(...)
6227  {
6228  }
6229 
6230  __SUP_COUT_ERR__ << "\n" << ss.str();
6231  version = TableVersion();
6232  tableViewPtr = table->getMockupViewP();
6233 
6234  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
6235  }
6236  }
6237  xmlOut.addTextElementToData("TableVersion", version.toString()); // table version
6238 
6239  if(getRawData)
6240  return; // no need to go further for rawData handling
6241 
6242  // get 'columns' of view
6243  xercesc::DOMElement* choicesParentEl;
6244  parentEl = xmlOut.addTextElementToData("CurrentVersionColumnHeaders", "");
6245 
6246  std::vector<TableViewColumnInfo> colInfo = tableViewPtr->getColumnsInfo();
6247 
6248  for(int i = 0; i < (int)colInfo.size(); ++i) // column headers and types
6249  {
6250  xmlOut.addTextElementToParent("ColumnHeader", colInfo[i].getName(), parentEl);
6251  xmlOut.addTextElementToParent("ColumnType", colInfo[i].getType(), parentEl);
6252  xmlOut.addTextElementToParent(
6253  "ColumnDataType", colInfo[i].getDataType(), parentEl);
6254 
6255  // NOTE!! ColumnDefaultValue defaults may be unique to this version of the table,
6256  // whereas DefaultRowValue are the defaults for the mockup
6257  xmlOut.addTextElementToParent(
6258  "ColumnDefaultValue", colInfo[i].getDefaultValue(), parentEl);
6259 
6260  choicesParentEl = xmlOut.addTextElementToParent("ColumnChoices", "", parentEl);
6261  // add data choices if necessary
6262  if(colInfo[i].getType() == TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA ||
6263  colInfo[i].getType() == TableViewColumnInfo::TYPE_BITMAP_DATA ||
6264  colInfo[i].isChildLink())
6265  {
6266  for(auto& choice : colInfo[i].getDataChoices())
6267  xmlOut.addTextElementToParent("ColumnChoice", choice, choicesParentEl);
6268  }
6269 
6270  xmlOut.addTextElementToParent(
6271  "ColumnMinValue", colInfo[i].getMinValue(), parentEl);
6272  xmlOut.addTextElementToParent(
6273  "ColumnMaxValue", colInfo[i].getMaxValue(), parentEl);
6274  }
6275 
6276  // verify mockup columns after columns are posted to xmlOut
6277  try
6278  {
6279  if(version.isInvalid())
6280  tableViewPtr->init();
6281  }
6282  catch(std::runtime_error& e)
6283  {
6284  // append accumulated errors, because they may be most useful
6285  __THROW__(e.what() + std::string("\n\n") + accumulatedErrors);
6286  }
6287  catch(...)
6288  {
6289  throw;
6290  }
6291 
6292  parentEl = xmlOut.addTextElementToData("CurrentVersionRows", "");
6293 
6294  for(int r = 0; r < (int)tableViewPtr->getNumberOfRows(); ++r)
6295  {
6296  sprintf(tmpIntStr, "%d", r);
6297  xercesc::DOMElement* tmpParentEl =
6298  xmlOut.addTextElementToParent("Row", tmpIntStr, parentEl);
6299 
6300  for(int c = 0; c < (int)tableViewPtr->getNumberOfColumns(); ++c)
6301  {
6302  if(colInfo[c].getDataType() == TableViewColumnInfo::DATATYPE_TIME)
6303  {
6304  std::string timeAsString;
6305  tableViewPtr->getValue(timeAsString, r, c);
6306  xmlOut.addTextElementToParent("Entry", timeAsString, tmpParentEl);
6307  }
6308  else
6309  xmlOut.addTextElementToParent(
6310  "Entry", tableViewPtr->getDataView()[r][c], tmpParentEl);
6311  }
6312  }
6313 
6314  // add "other" fields associated with configView
6315  xmlOut.addTextElementToData("TableComment", tableViewPtr->getComment());
6316  xmlOut.addTextElementToData("TableAuthor", tableViewPtr->getAuthor());
6317  xmlOut.addTextElementToData("TableCreationTime",
6318  std::to_string(tableViewPtr->getCreationTime()));
6319  xmlOut.addTextElementToData("TableLastAccessTime",
6320  std::to_string(tableViewPtr->getLastAccessTime()));
6321 
6322  // add to xml the default row values
6323  // NOTE!! ColumnDefaultValue defaults may be unique to this version of the table,
6324  // whereas DefaultRowValue are the defaults for the mockup
6325  std::vector<std::string> defaultRowValues =
6326  table->getMockupViewP()->getDefaultRowValues();
6327  // don't give author and time.. force default author, let JS fill time
6328  for(unsigned int c = 0; c < defaultRowValues.size() - 2; ++c)
6329  {
6330  xmlOut.addTextElementToData("DefaultRowValue", defaultRowValues[c]);
6331  }
6332 
6333  const std::set<std::string> srcColNames = tableViewPtr->getSourceColumnNames();
6334 
6335  if(accumulatedErrors != "") // add accumulated errors to xmlOut
6336  {
6337  __SUP_SS__ << (std::string("Column errors were allowed for this request, so "
6338  "perhaps you can ignore this, ") +
6339  "but please note the following warnings:\n" + accumulatedErrors)
6340  << __E__;
6341  __SUP_COUT_ERR__ << ss.str();
6342  xmlOut.addTextElementToData("TableWarnings", ss.str());
6343  }
6344  else if(!version.isTemporaryVersion() && // not temporary (these are not filled from
6345  // interface source)
6346  (srcColNames.size() != tableViewPtr->getNumberOfColumns() ||
6347  tableViewPtr->getSourceColumnMismatch() !=
6348  0)) // check for column size mismatch
6349  {
6350  __SUP_SS__ << "\n\nThere were warnings found when loading the table " << tableName
6351  << ":v" << version << ". Please see the details below:\n\n"
6352  << tableViewPtr->getMismatchColumnInfo();
6353 
6354  __SUP_COUT__ << "\n" << ss.str();
6355  xmlOut.addTextElementToData("TableWarnings", ss.str());
6356  }
6357 
6358 } // end handleGetTableXML()
6359 catch(std::runtime_error& e)
6360 {
6361  __SUP_SS__ << "Error getting table view!\n\n " << e.what() << __E__;
6362  __SUP_COUT_ERR__ << ss.str();
6363  xmlOut.addTextElementToData("Error", ss.str());
6364 }
6365 catch(...)
6366 {
6367  __SUP_SS__ << "Error getting table view!\n\n " << __E__;
6368  try
6369  {
6370  throw;
6371  } //one more try to printout extra info
6372  catch(const std::exception& e)
6373  {
6374  ss << "Exception message: " << e.what();
6375  }
6376  catch(...)
6377  {
6378  }
6379  __SUP_COUT_ERR__ << ss.str();
6380  xmlOut.addTextElementToData("Error", ss.str());
6381 } // end handleGetTableXML() catch
6382 
6383 //==============================================================================
6390 ConfigurationManagerRW* ConfigurationGUISupervisor::refreshUserSession(
6391  std::string username, bool refresh)
6392 {
6393  uint64_t sessionIndex =
6394  0; // make session by username for now! (may never want to change back)
6395 
6396  std::stringstream ssMapKey;
6397  ssMapKey << username << ":" << sessionIndex;
6398  std::string mapKey = ssMapKey.str();
6399  __SUP_COUTT__ << "Using Config Session " << mapKey
6400  << " ... Total Session Count: " << userConfigurationManagers_.size()
6401  << " refresh=" << refresh << __E__;
6402 
6403  time_t now = time(0);
6404 
6405  // create new table mgr if not one for active session index
6406 
6407  if(TTEST(1))
6408  {
6409  for(auto& pair : userConfigurationManagers_)
6410  __SUP_COUTTV__(pair.first);
6411  }
6412 
6413  const std::string preLoadCfgMgrName = ":0";
6414  if(userConfigurationManagers_.size() == 1 &&
6415  userConfigurationManagers_.find(preLoadCfgMgrName) !=
6416  userConfigurationManagers_.end())
6417  {
6418  __SUP_COUT__ << "Using pre-loaded Configuration Manager. time=" << time(0) << " "
6419  << clock() << " Setting author from "
6420  << userConfigurationManagers_.at(preLoadCfgMgrName)->getUsername()
6421  << " to " << username << __E__;
6422  userConfigurationManagers_[mapKey] =
6423  userConfigurationManagers_.at(preLoadCfgMgrName);
6424  userLastUseTime_[mapKey] = userLastUseTime_.at(preLoadCfgMgrName);
6425  //also set author!
6426  userConfigurationManagers_.at(mapKey)->setUsername(username);
6427  }
6428 
6429  if(userConfigurationManagers_.find(mapKey) == userConfigurationManagers_.end())
6430  {
6431  __SUP_COUT__ << "Creating new Configuration Manager. time=" << time(0) << " "
6432  << clock() << __E__;
6433  userConfigurationManagers_[mapKey] = new ConfigurationManagerRW(username);
6434 
6435  // update table info for each new configuration manager
6436  // IMPORTANTLY this also fills all configuration manager pointers with instances,
6437  // so we are not dealing with changing pointers later on
6438  userConfigurationManagers_[mapKey]->getAllTableInfo(
6439  true /* refresh */, // load empty instance of everything important
6440  0 /* accumulatedWarnings */,
6441  "" /* errorFilterName */,
6442  true /* getGroupKeys */,
6443  false /* getGroupInfo */,
6444  true /* initializeActiveGroups */);
6445  }
6446  else if(userLastUseTime_.find(mapKey) == userLastUseTime_.end())
6447  {
6448  __SUP_SS__ << "Fatal error managing user sessions! Check the logs for "
6449  "Configuration Interface failure."
6450  << __E__;
6451  __SUP_COUT_ERR__ << "\n" << ss.str();
6452  __SS_THROW__;
6453  }
6454  else if(
6455  refresh ||
6456  (now - userLastUseTime_[mapKey]) >
6457  CONFIGURATION_MANAGER_REFRESH_THRESHOLD) // check if should refresh all table info
6458  {
6459  __SUP_COUT__ << "Refreshing all table info." << __E__;
6460  userConfigurationManagers_[mapKey]->getAllTableInfo(
6461  true /* refresh */,
6462  0 /* accumulatedWarnings */,
6463  "" /* errorFilterName */,
6464  false /* getGroupKeys */,
6465  false /* getGroupInfo */,
6466  true /* initializeActiveGroups */);
6467  }
6468  __SUP_COUTT__ << "Configuration Manager for author="
6469  << userConfigurationManagers_[mapKey]->getUsername()
6470  << " ready. time=" << time(0) << " " << clock() << " runTimeSeconds()="
6471  << userConfigurationManagers_[mapKey]->runTimeSeconds() << __E__;
6472 
6473  // update active sessionIndex last use time
6474  userLastUseTime_[mapKey] = now;
6475 
6476  // check for stale sessions and remove them (so table user maps do not grow forever)
6477  for(std::map<std::string, time_t>::iterator it = userLastUseTime_.begin();
6478  it != userLastUseTime_.end();
6479  ++it)
6480  if(now - it->second > CONFIGURATION_MANAGER_EXPIRATION_TIME) // expired!
6481  {
6482  __SUP_COUT__ << now << ":" << it->second << " = " << now - it->second
6483  << __E__;
6484  delete userConfigurationManagers_[it->first]; // call destructor
6485  if(!(userConfigurationManagers_.erase(it->first))) // erase by key
6486  {
6487  __SUP_SS__ << "Fatal error erasing configuration manager by key!"
6488  << __E__;
6489  __SUP_COUT_ERR__ << "\n" << ss.str();
6490  __SS_THROW__;
6491  }
6492  userLastUseTime_.erase(it); // erase by iterator
6493 
6494  it =
6495  userLastUseTime_
6496  .begin(); // fail safe.. reset it, to avoid trying to understand what happens with the next iterator
6497  }
6498 
6499  return userConfigurationManagers_[mapKey];
6500 } //end refreshUserSession()
6501 
6502 //==============================================================================
6507 void ConfigurationGUISupervisor::handleDeleteTableInfoXML(HttpXmlDocument& xmlOut,
6508  ConfigurationManagerRW* cfgMgr,
6509  std::string& tableName)
6510 {
6511  if(0 == rename((TABLE_INFO_PATH + tableName + TABLE_INFO_EXT).c_str(),
6512  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused").c_str()))
6513  __SUP_COUT_INFO__ << ("Table Info File successfully renamed: " +
6514  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused"))
6515  << __E__;
6516  else
6517  {
6518  __SUP_COUT_ERR__ << ("Error renaming file to " +
6519  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused"))
6520  << __E__;
6521 
6522  xmlOut.addTextElementToData(
6523  "Error",
6524  ("Error renaming Table Info File to " +
6525  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused")));
6526  return;
6527  }
6528 
6529  // reload all with refresh to remove new table
6530  cfgMgr->getAllTableInfo(true /* refresh */);
6531 } // end handleDeleteTableInfoXML()
6532 
6533 //==============================================================================
6540 void ConfigurationGUISupervisor::handleSaveTableInfoXML(
6541  HttpXmlDocument& xmlOut,
6542  ConfigurationManagerRW* cfgMgr,
6543  std::string& tableName,
6544  const std::string& data,
6545  const std::string& tableDescription,
6546  const std::string& columnChoicesCSV,
6547  bool allowOverwrite)
6548 {
6549  // create all caps name and validate
6550  // only allow alpha-numeric names with "Table" at end
6551  std::string capsName;
6552  try
6553  {
6554  capsName = TableBase::convertToCaps(tableName, true);
6555  }
6556  catch(std::runtime_error& e)
6557  { // error! non-alpha
6558  xmlOut.addTextElementToData("Error", e.what());
6559  return;
6560  }
6561 
6562  if(!allowOverwrite)
6563  {
6564  FILE* fp = fopen((TABLE_INFO_PATH + tableName + TABLE_INFO_EXT).c_str(), "r");
6565  if(fp)
6566  {
6567  fclose(fp);
6568  xmlOut.addTextElementToData("TableName", tableName);
6569  xmlOut.addTextElementToData("OverwriteError", "1");
6570  xmlOut.addTextElementToData(
6571  "Error",
6572  "File already exists! ('" +
6573  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT) + "')");
6574  return;
6575  }
6576  }
6577 
6578  __SUP_COUT__ << "capsName=" << capsName << __E__;
6579  __SUP_COUT__ << "tableName=" << tableName << __E__;
6580  __SUP_COUT__ << "tableDescription=" << tableDescription << __E__;
6581  __SUP_COUT__ << "columnChoicesCSV=" << columnChoicesCSV << __E__;
6582 
6583  // create preview string to validate column info before write to file
6584  std::stringstream outss;
6585 
6586  outss << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n";
6587  outss << "\t<ROOT xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
6588  "xsi:noNamespaceSchemaLocation=\"TableInfo.xsd\">\n";
6589  outss << "\t\t<TABLE Name=\"" << tableName << "\">\n";
6590  outss << "\t\t\t<VIEW Name=\"" << capsName
6591  << "\" Type=\"File,Database,DatabaseTest\" Description=\"" << tableDescription
6592  << "\">\n";
6593 
6594  // each column is represented by 4 fields or 6
6595  // - type, name, dataType, defaultValue, minValue, maxValue
6596 
6597  std::istringstream columnChoicesISS(columnChoicesCSV);
6598  std::string columnChoicesString;
6599  std::string columnDefaultValue, columnMinValue, columnMaxValue;
6600  std::vector<std::string> columnParameters;
6601  std::vector<std::string> columnData =
6602  StringMacros::getVectorFromString(data, {';'} /*delimiter*/);
6603 
6604  for(unsigned int c = 0; c < columnData.size() - 1; ++c)
6605  {
6606  columnParameters =
6607  StringMacros::getVectorFromString(columnData[c], {','} /*delimiter*/);
6608  __COUT__ << "Column #" << c << ": "
6609  << StringMacros::vectorToString(columnParameters) << __E__;
6610  for(unsigned int p = 0; p < columnParameters.size(); ++p)
6611  {
6612  __COUT__ << "\t Parameter #" << p << ": " << columnParameters[p] << __E__;
6613  }
6614  __COUT__ << "\t creating the new xml" << __E__;
6615 
6616  std::string& columnType = columnParameters[0];
6617  std::string& columnName = columnParameters[1];
6618  std::string& columnDataType = columnParameters[2];
6619  const std::string columnStorageName =
6620  TableBase::convertToCaps(columnName); // now caps;
6621 
6622  outss << "\t\t\t\t<COLUMN Type=\"";
6623  outss << columnType;
6624  outss << "\" \t Name=\"";
6625  outss << columnName;
6626  outss << "\" \t StorageName=\"";
6627  try
6628  {
6629  outss << columnStorageName;
6630  }
6631  catch(std::runtime_error& e)
6632  { // error! non-alpha
6633  xmlOut.addTextElementToData(
6634  "Error",
6635  std::string("For column name '") + columnName + "' - " + e.what());
6636  return;
6637  }
6638  outss << "\" \t DataType=\"";
6639  outss << columnDataType;
6640 
6641  columnDefaultValue = StringMacros::decodeURIComponent(columnParameters[3]);
6642 
6643  std::string* columnDefaultValuePtr = nullptr;
6644  if(columnDefaultValue !=
6645  TableViewColumnInfo::getDefaultDefaultValue(columnType, columnDataType))
6646  {
6647  __SUP_COUT__ << "FOUND user spec'd default value '" << columnDefaultValue
6648  << "'" << __E__;
6649  outss << "\" \t DefaultValue=\"";
6650  outss << columnParameters[3];
6651  columnDefaultValuePtr = &columnParameters[3];
6652  }
6653  getline(columnChoicesISS, columnChoicesString, ';');
6654  outss << "\" \t DataChoices=\"";
6655  outss << columnChoicesString;
6656 
6657  std::string* columnMinValuePtr = nullptr;
6658  std::string* columnMaxValuePtr = nullptr;
6659 
6660  if(columnParameters.size() > 4 &&
6661  columnDataType == TableViewColumnInfo::DATATYPE_NUMBER)
6662  {
6663  columnMinValue = StringMacros::decodeURIComponent(columnParameters[4]);
6664  if(columnMinValue != "")
6665  {
6666  if(columnMinValue !=
6668  {
6669  __SUP_COUT__ << "FOUND user spec'd min value '" << columnParameters[4]
6670  << "'" << __E__;
6673  {
6674  __SS__ << "Inavlid user spec'd min value '" << columnParameters[4]
6675  << "' which evaluates to '" << columnMinValue
6676  << "' and is not a valid number. The minimum value must "
6677  "be a number (environment variables and math "
6678  "operations are allowed)."
6679  << __E__;
6680  __SS_THROW__;
6681  }
6682  outss << "\" \t MinValue=\"" << columnParameters[4];
6683  columnMinValuePtr = &columnParameters[4];
6684  }
6685  }
6686 
6687  columnMaxValue = StringMacros::decodeURIComponent(columnParameters[5]);
6688  if(columnMaxValue != "")
6689  {
6690  if(columnMaxValue !=
6692  {
6693  __SUP_COUT__ << "FOUND user spec'd max value = " << columnMaxValue
6694  << __E__;
6697  {
6698  __SS__ << "Inavlid user spec'd max value '" << columnParameters[5]
6699  << "' which evaluates to '" << columnMaxValue
6700  << "' and is not a valid number. The maximum value must "
6701  "be a number (environment variables and math "
6702  "operations are allowed)."
6703  << __E__;
6704  __SS_THROW__;
6705  }
6706  outss << "\" \t MaxValue=\"" << columnParameters[5];
6707  columnMaxValuePtr = &columnParameters[5];
6708  }
6709  }
6710  }
6711 
6712  //validate each column before saving a bad table file
6713  try
6714  {
6715  TableViewColumnInfo testCol(columnType,
6716  columnName,
6717  columnStorageName,
6718  columnDataType,
6719  columnDefaultValuePtr,
6720  columnChoicesString,
6721  columnMinValuePtr,
6722  columnMaxValuePtr,
6723  nullptr //capturedExceptionString
6724  );
6725  }
6726  catch(const std::runtime_error& e)
6727  {
6728  __SS__ << "Error identified with Column #" << c << ": \n" << e.what();
6729  __SS_THROW__;
6730  }
6731 
6732  outss << "\"/>\n";
6733  }
6734 
6735  outss << "\t\t\t</VIEW>\n";
6736  outss << "\t\t</TABLE>\n";
6737  outss << "\t</ROOT>\n";
6738 
6739  __SUP_COUT__ << outss.str() << __E__;
6740 
6741  FILE* fp = fopen((TABLE_INFO_PATH + tableName + TABLE_INFO_EXT).c_str(), "w");
6742  if(!fp)
6743  {
6744  xmlOut.addTextElementToData("Error",
6745  "Failed to open destination Table Info file:" +
6746  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT));
6747  return;
6748  }
6749 
6750  fprintf(fp, "%s", outss.str().c_str());
6751  fclose(fp);
6752 
6753  __SUP_COUT_INFO__ << "Finished saving Table Info for '" << tableName
6754  << ".' Looking for errors in all table column info..." << __E__;
6755 
6756  // reload all table info with refresh AND reset to pick up possibly new table
6757  // check for errors related to this tableName
6758  std::string accumulatedErrors = "";
6759  cfgMgr->getAllTableInfo(true /* refresh */, &accumulatedErrors, tableName);
6760 
6761  // if errors associated with this table name stop and report
6762  if(accumulatedErrors != "")
6763  {
6764  __SUP_SS__ << ("The new version of the '" + tableName +
6765  "' table column info was saved, however errors were detected "
6766  "reading back the table '" +
6767  tableName + "' after the save attempt:\n\n" + accumulatedErrors)
6768  << __E__;
6769 
6770  __SUP_COUT_ERR__ << ss.str() << __E__;
6771  xmlOut.addTextElementToData("Error", ss.str());
6772 
6773  return;
6774  }
6775 
6776  // return the new table info
6777  handleGetTableXML(xmlOut, cfgMgr, tableName, TableVersion());
6778 
6779  // After save, debug all table column info
6780  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
6781 
6782  // give a print out of currently illegal table column info
6783  for(const auto& cfgInfo : allTableInfo)
6784  {
6785  try
6786  {
6787  cfgMgr->getTableByName(cfgInfo.first)->getMockupViewP()->init();
6788  }
6789  catch(std::runtime_error& e)
6790  {
6791  __SUP_COUT_WARN__ << "\n\n##############################################\n"
6792  << "Error identified in column info of table '"
6793  << cfgInfo.first << "':\n\n"
6794  << e.what() << "\n\n"
6795  << __E__;
6796  }
6797  }
6798 } // end handleSaveTableInfoXML()
6799 
6800 //==============================================================================
6808 void ConfigurationGUISupervisor::handleSetGroupAliasInBackboneXML(
6809  HttpXmlDocument& xmlOut,
6810  ConfigurationManagerRW* cfgMgr,
6811  const std::string& groupAliasCSV,
6812  const std::string& groupNameCSV,
6813  const std::string& groupKeyCSV,
6814  const std::string& author)
6815 try
6816 {
6817  cfgMgr->loadConfigurationBackbone();
6818  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
6819 
6820  const std::string groupAliasesTableName =
6821  ConfigurationManager::GROUP_ALIASES_TABLE_NAME;
6822  if(activeVersions.find(groupAliasesTableName) == activeVersions.end())
6823  {
6824  __SUP_SS__ << "Active version of " << groupAliasesTableName << " missing!"
6825  << __E__;
6826  xmlOut.addTextElementToData("Error", ss.str());
6827  return;
6828  }
6829 
6830  // put all old backbone versions in xmlOut
6831  const std::set<std::string> backboneMembers = cfgMgr->getBackboneMemberNames();
6832  for(auto& memberName : backboneMembers)
6833  {
6834  __SUP_COUT__ << "activeVersions[\"" << memberName
6835  << "\"]=" << activeVersions[memberName] << __E__;
6836 
6837  xmlOut.addTextElementToData("oldBackboneName", memberName);
6838  xmlOut.addTextElementToData("oldBackboneVersion",
6839  activeVersions[memberName].toString());
6840  }
6841 
6842  // make a temporary version from active view
6843  // modify the chosen groupAlias row
6844  // save as new version
6845 
6846  TableBase* table = cfgMgr->getTableByName(groupAliasesTableName);
6847  TableVersion originalVersion = activeVersions[groupAliasesTableName];
6848  TableVersion temporaryVersion = table->createTemporaryView(originalVersion);
6849 
6850  __SUP_COUT__ << "\t\t temporaryVersion: " << temporaryVersion << __E__;
6851  bool isDifferent = false;
6852 
6853  try
6854  {
6855  TableView* configView = table->getTemporaryView(temporaryVersion);
6856 
6857  unsigned int col = configView->findCol("GroupKeyAlias");
6858  unsigned int ccol = configView->findCol(TableViewColumnInfo::COL_NAME_COMMENT);
6859  unsigned int ncol = configView->findCol("GroupName");
6860  unsigned int kcol = configView->findCol("GroupKey");
6861 
6862  // only make a new version if we are changing compared to active backbone
6863  std::vector<std::string> groupAliases =
6864  StringMacros::getVectorFromString(groupAliasCSV);
6865  std::vector<std::string> groupNames =
6866  StringMacros::getVectorFromString(groupNameCSV);
6867  std::vector<std::string> groupKeys =
6868  StringMacros::getVectorFromString(groupKeyCSV);
6869  __SUP_COUTV__(StringMacros::vectorToString(groupAliases));
6870  __SUP_COUTV__(StringMacros::vectorToString(groupNames));
6871  __SUP_COUTV__(StringMacros::vectorToString(groupKeys));
6872 
6873  size_t i = 0;
6874  for(const auto& groupAlias : groupAliases)
6875  {
6876  if(groupAlias == "" || groupNames[i] == "" || groupKeys[i] == "")
6877  {
6878  //skip empty aliases
6879  __SUP_COUT_WARN__ << "Empty alias parameter found [" << i << "] = {"
6880  << groupAlias << ", " << groupNames[i] << "("
6881  << groupKeys[i] << ")}" << __E__;
6882  ++i;
6883  continue;
6884  }
6885 
6886  bool localIsDifferent = false;
6887  const std::string& groupName = groupNames[i];
6888  const TableGroupKey groupKey(groupKeys[i]);
6889  ++i;
6890 
6891  unsigned int row = -1;
6892  // find groupAlias row
6893  try
6894  {
6895  row = configView->findRow(col, groupAlias);
6896  }
6897  catch(...) // ignore not found error
6898  {
6899  }
6900 
6901  if(row == (unsigned int)-1) // if row not found then add a row
6902  {
6903  localIsDifferent = true;
6904  row = configView->addRow();
6905 
6906  // set all columns in new row
6907  configView->setValue(
6908  "This Group Alias was automatically setup by the server.", row, ccol);
6909  configView->setValue(groupAlias, row, col);
6910  }
6911 
6912  __SUP_COUT__ << "\t\t row: " << row << __E__;
6913 
6914  __SUP_COUT__ << "\t\t groupName: " << groupName << " vs "
6915  << configView->getDataView()[row][ncol] << __E__;
6916  if(groupName != configView->getDataView()[row][ncol])
6917  {
6918  configView->setValue(groupName, row, ncol);
6919  localIsDifferent = true;
6920  }
6921 
6922  __SUP_COUT__ << "\t\t groupKey: " << groupKey << " vs "
6923  << configView->getDataView()[row][kcol] << __E__;
6924  if(groupKey.toString() != configView->getDataView()[row][kcol])
6925  {
6926  configView->setValue(groupKey.toString(), row, kcol);
6927  localIsDifferent = true;
6928  }
6929 
6930  if(localIsDifferent) // set author/time of new record if different
6931  {
6932  configView->setValue(
6933  author,
6934  row,
6935  configView->findCol(TableViewColumnInfo::COL_NAME_AUTHOR));
6936  configView->setValue(
6937  time(0),
6938  row,
6939  configView->findCol(TableViewColumnInfo::COL_NAME_CREATION));
6940  isDifferent = true;
6941  }
6942  } //end group alias modify loop
6943  }
6944  catch(...)
6945  {
6946  __SUP_COUT_ERR__ << "Error editing Group Alias view!" << __E__;
6947 
6948  // delete temporaryVersion
6949  table->eraseView(temporaryVersion);
6950  throw;
6951  }
6952 
6953  TableVersion newAssignedVersion;
6954  if(isDifferent) // make new version if different
6955  {
6956  __SUP_COUT__ << "\t\t**************************** Save as new table version"
6957  << __E__;
6958 
6959  // save or find equivalent
6961  xmlOut,
6962  cfgMgr,
6963  table->getTableName(),
6964  originalVersion,
6965  false /*makeTemporary*/,
6966  table,
6967  temporaryVersion,
6968  false /*ignoreDuplicates*/,
6969  true /*lookForEquivalent*/);
6970  }
6971  else // use existing version
6972  {
6973  __SUP_COUT__
6974  << "\t\t**************************** Using the existing table version"
6975  << __E__;
6976 
6977  // delete temporaryVersion
6978  table->eraseView(temporaryVersion);
6979  newAssignedVersion = activeVersions[groupAliasesTableName];
6980 
6981  xmlOut.addTextElementToData("savedName", groupAliasesTableName);
6982  xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString());
6983  }
6984 
6985  __SUP_COUT__ << "\t\t newAssignedVersion: " << newAssignedVersion << __E__;
6986 } //end handleSetGroupAliasInBackboneXML()
6987 catch(std::runtime_error& e)
6988 {
6989  __SUP_SS__ << "Error saving new Group Alias view!\n\n " << e.what() << __E__;
6990  __SUP_COUT_ERR__ << ss.str();
6991  xmlOut.addTextElementToData("Error", ss.str());
6992 }
6993 catch(...)
6994 {
6995  __SUP_SS__ << "Error saving new Group Alias view!\n\n " << __E__;
6996  try
6997  {
6998  throw;
6999  } //one more try to printout extra info
7000  catch(const std::exception& e)
7001  {
7002  ss << "Exception message: " << e.what();
7003  }
7004  catch(...)
7005  {
7006  }
7007  __SUP_COUT_ERR__ << ss.str();
7008  xmlOut.addTextElementToData("Error", ss.str());
7009 } //end handleSetGroupAliasInBackboneXML() catch
7010 
7011 //==============================================================================
7019 void ConfigurationGUISupervisor::handleSetTableAliasInBackboneXML(
7020  HttpXmlDocument& xmlOut,
7021  ConfigurationManagerRW* cfgMgr,
7022  const std::string& tableAlias,
7023  const std::string& tableName,
7024  TableVersion version,
7025  const std::string& author)
7026 try
7027 {
7028  cfgMgr->loadConfigurationBackbone();
7029  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
7030 
7031  const std::string versionAliasesTableName =
7032  ConfigurationManager::VERSION_ALIASES_TABLE_NAME;
7033  if(activeVersions.find(versionAliasesTableName) == activeVersions.end())
7034  {
7035  __SUP_SS__ << "Active version of " << versionAliasesTableName << " missing!"
7036  << __E__;
7037  xmlOut.addTextElementToData("Error", ss.str());
7038  return;
7039  }
7040 
7041  // put all old backbone versions in xmlOut
7042  const std::set<std::string> backboneMembers = cfgMgr->getBackboneMemberNames();
7043  for(auto& memberName : backboneMembers)
7044  {
7045  __SUP_COUT__ << "activeVersions[\"" << memberName
7046  << "\"]=" << activeVersions[memberName] << __E__;
7047 
7048  xmlOut.addTextElementToData("oldBackboneName", memberName);
7049  xmlOut.addTextElementToData("oldBackboneVersion",
7050  activeVersions[memberName].toString());
7051  }
7052 
7053  // make a temporary version from active view
7054  // modify the chosen versionAlias row
7055  // save as new version
7056 
7057  TableBase* table = cfgMgr->getTableByName(versionAliasesTableName);
7058  TableVersion originalVersion = activeVersions[versionAliasesTableName];
7059  TableVersion temporaryVersion = table->createTemporaryView(originalVersion);
7060 
7061  __SUP_COUT__ << "\t\t temporaryVersion: " << temporaryVersion << __E__;
7062 
7063  bool isDifferent = false;
7064 
7065  try
7066  {
7067  TableView* configView = table->getTemporaryView(temporaryVersion);
7068 
7069  unsigned int col;
7070  unsigned int col2 = configView->findCol("VersionAlias");
7071  unsigned int col3 = configView->findCol("TableName");
7072 
7073  // only make a new version if we are changing compared to active backbone
7074 
7075  unsigned int row = -1;
7076  // find tableName, versionAlias pair
7077  // NOTE: only accept the first pair, repeats are ignored.
7078  try
7079  {
7080  unsigned int tmpRow = -1;
7081  do
7082  { // start looking from beyond last find
7083  tmpRow = configView->findRow(col3, tableName, tmpRow + 1);
7084  } while(configView->getDataView()[tmpRow][col2] != tableAlias);
7085  // at this point the first pair was found! (else exception was thrown)
7086  row = tmpRow;
7087  }
7088  catch(...)
7089  {
7090  }
7091  if(row == (unsigned int)-1) // if row not found then add a row
7092  {
7093  isDifferent = true;
7094  row = configView->addRow();
7095 
7096  // set all columns in new row
7097  col = configView->findCol(TableViewColumnInfo::COL_NAME_COMMENT);
7098  configView->setValue(
7099  std::string("Entry was added by server in ") +
7100  "ConfigurationGUISupervisor::setTableAliasInActiveBackbone().",
7101  row,
7102  col);
7103 
7104  col = configView->findCol("VersionAliasUID");
7105  configView->setValue(
7106  tableName.substr(0, tableName.rfind("Table")) + tableAlias, row, col);
7107 
7108  configView->setValue(tableAlias, row, col2);
7109  configView->setValue(tableName, row, col3);
7110  }
7111 
7112  __SUP_COUT__ << "\t\t row: " << row << __E__;
7113 
7114  col = configView->findCol("Version");
7115  __SUP_COUT__ << "\t\t version: " << version << " vs "
7116  << configView->getDataView()[row][col] << __E__;
7117  if(version.toString() != configView->getDataView()[row][col])
7118  {
7119  configView->setValue(version.toString(), row, col);
7120  isDifferent = true;
7121  }
7122 
7123  if(isDifferent) // set author/time of new version if different
7124  {
7125  configView->setValue(
7126  author, row, configView->findCol(TableViewColumnInfo::COL_NAME_AUTHOR));
7127  configView->setValue(
7128  time(0),
7129  row,
7130  configView->findCol(TableViewColumnInfo::COL_NAME_CREATION));
7131  }
7132  }
7133  catch(...)
7134  {
7135  __SUP_COUT_ERR__ << "Error editing Version Alias view!" << __E__;
7136 
7137  // delete temporaryVersion
7138  table->eraseView(temporaryVersion);
7139  throw;
7140  }
7141 
7142  TableVersion newAssignedVersion;
7143  if(isDifferent) // make new version if different
7144  {
7145  __SUP_COUT__ << "\t\t**************************** Save as new table version"
7146  << __E__;
7147 
7149  xmlOut,
7150  cfgMgr,
7151  table->getTableName(),
7152  originalVersion,
7153  false /*makeTemporary*/,
7154  table,
7155  temporaryVersion,
7156  false /*ignoreDuplicates*/,
7157  true /*lookForEquivalent*/);
7158  }
7159  else // use existing version
7160  {
7161  __SUP_COUT__ << "\t\t**************************** Using existing table version"
7162  << __E__;
7163 
7164  // delete temporaryVersion
7165  table->eraseView(temporaryVersion);
7166  newAssignedVersion = activeVersions[versionAliasesTableName];
7167 
7168  xmlOut.addTextElementToData("savedName", versionAliasesTableName);
7169  xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString());
7170  }
7171 
7172  __SUP_COUT__ << "\t\t newAssignedVersion: " << newAssignedVersion << __E__;
7173 } // end handleSetVersionAliasInBackboneXML()
7174 catch(std::runtime_error& e)
7175 {
7176  __SUP_SS__ << "Error saving new Version Alias view!\n\n " << e.what() << __E__;
7177  __SUP_COUT_ERR__ << ss.str();
7178  xmlOut.addTextElementToData("Error", ss.str());
7179 }
7180 catch(...)
7181 {
7182  __SUP_SS__ << "Error saving new Version Alias view!\n\n " << __E__;
7183  try
7184  {
7185  throw;
7186  } //one more try to printout extra info
7187  catch(const std::exception& e)
7188  {
7189  ss << "Exception message: " << e.what();
7190  }
7191  catch(...)
7192  {
7193  }
7194  __SUP_COUT_ERR__ << ss.str();
7195  xmlOut.addTextElementToData("Error", ss.str());
7196 } // end handleSetVersionAliasInBackboneXML() catch
7197 
7198 //==============================================================================
7204 void ConfigurationGUISupervisor::handleAliasGroupMembersInBackboneXML(
7205  HttpXmlDocument& xmlOut,
7206  ConfigurationManagerRW* cfgMgr,
7207  const std::string& versionAlias,
7208  const std::string& groupName,
7209  TableGroupKey groupKey,
7210  const std::string& author)
7211 try
7212 {
7213  cfgMgr->loadConfigurationBackbone();
7214  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
7215 
7216  const std::string versionAliasesTableName =
7217  ConfigurationManager::VERSION_ALIASES_TABLE_NAME;
7218  if(activeVersions.find(versionAliasesTableName) == activeVersions.end())
7219  {
7220  __SUP_SS__ << "Active version of " << versionAliasesTableName << " missing!"
7221  << __E__;
7222  xmlOut.addTextElementToData("Error", ss.str());
7223  return;
7224  }
7225 
7226  // put all old backbone versions in xmlOut
7227  const std::set<std::string> backboneMembers = cfgMgr->getBackboneMemberNames();
7228  for(auto& memberName : backboneMembers)
7229  {
7230  __SUP_COUT__ << "activeVersions[\"" << memberName
7231  << "\"]=" << activeVersions[memberName] << __E__;
7232 
7233  xmlOut.addTextElementToData("oldBackboneName", memberName);
7234  xmlOut.addTextElementToData("oldBackboneVersion",
7235  activeVersions[memberName].toString());
7236  }
7237 
7238  // make a temporary version from active view
7239  // modify the chosen versionAlias row
7240  // save as new version
7241 
7242  TableBase* table = cfgMgr->getTableByName(versionAliasesTableName);
7243  TableVersion temporaryVersion =
7244  table->createTemporaryView(activeVersions[versionAliasesTableName]);
7245 
7246  __SUP_COUT__ << "\t\t temporaryVersion: " << temporaryVersion << __E__;
7247 
7248  TableView* configView = table->getTemporaryView(temporaryVersion);
7249 
7250  // only make a new version if we are changing compared to active backbone
7251  bool isDifferent = false;
7252 
7253  // get member names and versions
7254  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
7255  try
7256  {
7257  cfgMgr->loadTableGroup(groupName,
7258  groupKey,
7259  false /*doActivate*/,
7260  &memberMap,
7261  0,
7262  0,
7263  0,
7264  0,
7265  0, // defaults
7266  true /*doNotLoadMember*/);
7267  }
7268  catch(...)
7269  {
7270  xmlOut.addTextElementToData(
7271  "Error",
7272  "Table group \"" + TableGroupKey::getFullGroupString(groupName, groupKey) +
7273  "\" can not be retrieved!");
7274  return;
7275  }
7276 
7277  unsigned int col;
7278  unsigned int col2 = configView->findCol("VersionAlias");
7279  unsigned int col3 = configView->findCol("TableName");
7280 
7281  for(auto& memberPair : memberMap)
7282  {
7283  bool thisMemberIsDifferent = false;
7284  unsigned int row = -1;
7285 
7286  __SUP_COUT__ << "Adding alias for " << memberPair.first << "_v"
7287  << memberPair.second << " to " << versionAlias << __E__;
7288 
7289  // find tableName, versionAlias pair
7290  // NOTE: only accept the first pair, repeats are ignored.
7291  try
7292  {
7293  unsigned int tmpRow = -1;
7294  do
7295  { // start looking from beyond last find
7296  tmpRow = configView->findRow(col3, memberPair.first, tmpRow + 1);
7297  } while(configView->getDataView()[tmpRow][col2] != versionAlias);
7298  // at this point the first pair was found! (else exception was thrown)
7299  row = tmpRow;
7300  }
7301  catch(...)
7302  {
7303  }
7304  if(row == (unsigned int)-1) // if row not found then add a row
7305  {
7306  thisMemberIsDifferent = true;
7307  row = configView->addRow();
7308 
7309  // set all columns in new row
7310  col = configView->findCol(TableViewColumnInfo::COL_NAME_COMMENT);
7311  configView->setValue(
7312  std::string("Entry was added by server in ") +
7313  "ConfigurationGUISupervisor::setTableAliasInActiveBackbone().",
7314  row,
7315  col);
7316 
7317  col = configView->getColUID();
7318  configView->setValue(
7319  memberPair.first.substr(0, memberPair.first.rfind("Table")) +
7320  versionAlias,
7321  row,
7322  col);
7323 
7324  configView->setValue(versionAlias, row, col2);
7325  configView->setValue(memberPair.first, row, col3);
7326  }
7327 
7328  col = configView->findCol("Version");
7329 
7330  if(memberPair.second.toString() != configView->getDataView()[row][col])
7331  {
7332  configView->setValue(memberPair.second.toString(), row, col);
7333  thisMemberIsDifferent = true;
7334  }
7335 
7336  if(thisMemberIsDifferent) // change author and time if row is different
7337  {
7338  configView->setValue(
7339  author, row, configView->findCol(TableViewColumnInfo::COL_NAME_AUTHOR));
7340  configView->setValue(
7341  time(0),
7342  row,
7343  configView->findCol(TableViewColumnInfo::COL_NAME_CREATION));
7344  }
7345 
7346  if(thisMemberIsDifferent)
7347  isDifferent = true;
7348  }
7349 
7350  // configView->print();
7351 
7352  TableVersion newAssignedVersion;
7353  if(isDifferent) // make new version if different
7354  {
7355  __SUP_COUT__ << "\t\t**************************** Save v" << temporaryVersion
7356  << " as new table version" << __E__;
7357 
7358  newAssignedVersion =
7359  cfgMgr->saveNewTable(versionAliasesTableName, temporaryVersion);
7360  }
7361  else // use existing version
7362  {
7363  __SUP_COUT__ << "\t\t**************************** Using existing table version"
7364  << __E__;
7365 
7366  // delete temporaryVersion
7367  table->eraseView(temporaryVersion);
7368  newAssignedVersion = activeVersions[versionAliasesTableName];
7369  }
7370 
7371  xmlOut.addTextElementToData("savedName", versionAliasesTableName);
7372  xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString());
7373  __SUP_COUT__ << "\t\t Resulting Version: " << newAssignedVersion << __E__;
7374 } // end handleAliasGroupMembersInBackboneXML()
7375 catch(std::runtime_error& e)
7376 {
7377  __SUP_SS__ << "Error saving new Version Alias view!\n\n " << e.what() << __E__;
7378  __SUP_COUT_ERR__ << ss.str();
7379  xmlOut.addTextElementToData("Error", ss.str());
7380 }
7381 catch(...)
7382 {
7383  __SUP_SS__ << "Error saving new Version Alias view!\n\n " << __E__;
7384  try
7385  {
7386  throw;
7387  } //one more try to printout extra info
7388  catch(const std::exception& e)
7389  {
7390  ss << "Exception message: " << e.what();
7391  }
7392  catch(...)
7393  {
7394  }
7395  __SUP_COUT_ERR__ << ss.str();
7396  xmlOut.addTextElementToData("Error", ss.str());
7397 } // end handleAliasGroupMembersInBackboneXML() catch
7398 
7399 //==============================================================================
7410 void ConfigurationGUISupervisor::handleGroupAliasesXML(HttpXmlDocument& xmlOut,
7411  ConfigurationManagerRW* cfgMgr)
7412 {
7413  cfgMgr->loadConfigurationBackbone();
7414  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
7415 
7416  std::string groupAliasesTableName = ConfigurationManager::GROUP_ALIASES_TABLE_NAME;
7417  if(activeVersions.find(groupAliasesTableName) == activeVersions.end())
7418  {
7419  __SUP_SS__ << "\nActive version of " << groupAliasesTableName << " missing! "
7420  << groupAliasesTableName
7421  << " is a required member of the Backbone table group."
7422  << "\n\nLikely you need to activate a valid Backbone table group."
7423  << __E__;
7424  __SUP_COUT__ << ss.str(); // just output findings, and return empty xml to avoid
7425  // infinite error loops in GUI
7426  // xmlOut.addTextElementToData("Error", ss.str());
7427  return;
7428  }
7429  __SUP_COUT__ << "activeVersions[\"" << groupAliasesTableName
7430  << "\"]=" << activeVersions[groupAliasesTableName] << __E__;
7431  xmlOut.addTextElementToData("GroupAliasesTableName", groupAliasesTableName);
7432  xmlOut.addTextElementToData("GroupAliasesTableVersion",
7433  activeVersions[groupAliasesTableName].toString());
7434 
7435  std::vector<std::pair<std::string, ConfigurationTree>> aliasNodePairs =
7436  cfgMgr->getNode(groupAliasesTableName).getChildren();
7437 
7438  const int numOfThreads = ConfigurationManager::PROCESSOR_COUNT / 2;
7439  __SUP_COUT__ << " PROCESSOR_COUNT " << ConfigurationManager::PROCESSOR_COUNT
7440  << " ==> " << numOfThreads << " threads for alias group loads." << __E__;
7441 
7442  if(numOfThreads < 2) // no multi-threading
7443  {
7444  std::string groupName, groupKey, groupComment, groupAuthor, groupCreateTime,
7445  groupType;
7446  for(auto& aliasNodePair : aliasNodePairs)
7447  {
7448  groupName = aliasNodePair.second.getNode("GroupName").getValueAsString();
7449  groupKey = aliasNodePair.second.getNode("GroupKey").getValueAsString();
7450 
7451  xmlOut.addTextElementToData("GroupAlias", aliasNodePair.first);
7452  xmlOut.addTextElementToData("GroupName", groupName);
7453  xmlOut.addTextElementToData("GroupKey", groupKey);
7454  xmlOut.addTextElementToData(
7455  "AliasComment",
7456  aliasNodePair.second.getNode(TableViewColumnInfo::COL_NAME_COMMENT)
7457  .getValueAsString());
7458 
7459  // get group comment
7460  groupComment =
7461  ConfigurationManager::UNKNOWN_INFO; // clear just in case failure
7462  groupType = ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN;
7463  try
7464  {
7465  cfgMgr->loadTableGroup(groupName,
7466  TableGroupKey(groupKey),
7467  false /* doActivate */,
7468  0 /* groupMembers */,
7469  0 /* progressBar */,
7470  0 /* accumulatedWarnings */,
7471  &groupComment,
7472  &groupAuthor,
7473  &groupCreateTime,
7474  true /*doNotLoadMembers*/,
7475  &groupType);
7476  }
7477  catch(...)
7478  {
7479  __SUP_COUT_WARN__ << "Failed to load group '" << groupName << "("
7480  << groupKey << ")' to extract group comment and type."
7481  << __E__;
7482  }
7483  xmlOut.addTextElementToData("GroupComment", groupComment);
7484  xmlOut.addTextElementToData("GroupType", groupType);
7485  } // end alias pair loop
7486  }
7487  else //multi-threading
7488  {
7489  int threadsLaunched = 0;
7490  int foundThreadIndex = 0;
7491  std::vector<std::shared_ptr<std::atomic<bool>>> threadDone;
7492  for(int i = 0; i < numOfThreads; ++i)
7493  threadDone.push_back(std::make_shared<std::atomic<bool>>(true));
7494 
7495  std::vector<std::shared_ptr<ots::GroupInfo>> sharedGroupInfoPtrs;
7496  std::string groupName, groupKey;
7497 
7498  for(auto& aliasNodePair : aliasNodePairs)
7499  {
7500  //make temporary group info for thread
7501  sharedGroupInfoPtrs.push_back(std::make_shared<ots::GroupInfo>());
7502 
7503  groupName = aliasNodePair.second.getNode("GroupName").getValueAsString();
7504  groupKey = aliasNodePair.second.getNode("GroupKey").getValueAsString();
7505 
7506  if(threadsLaunched >= numOfThreads)
7507  {
7508  //find availableThreadIndex
7509  foundThreadIndex = -1;
7510  while(foundThreadIndex == -1)
7511  {
7512  for(int i = 0; i < numOfThreads; ++i)
7513  if(*(threadDone[i]))
7514  {
7515  foundThreadIndex = i;
7516  break;
7517  }
7518  if(foundThreadIndex == -1)
7519  {
7520  __SUP_COUTT__ << "Waiting for available thread..." << __E__;
7521  usleep(10000);
7522  }
7523  } //end thread search loop
7524  threadsLaunched = numOfThreads - 1;
7525  }
7526  __SUP_COUTT__ << "Starting load group thread... " << groupName << "("
7527  << groupKey << ")" << __E__;
7528  *(threadDone[foundThreadIndex]) = false;
7529 
7530  std::thread(
7531  [](ConfigurationManagerRW* theCfgMgr,
7532  std::string theGroupName,
7533  ots::TableGroupKey theGroupKey,
7534  std::shared_ptr<ots::GroupInfo> theGroupInfo,
7535  std::shared_ptr<std::atomic<bool>> theThreadDone) {
7537  theGroupName,
7538  theGroupKey,
7539  theGroupInfo,
7540  theThreadDone);
7541  },
7542  cfgMgr,
7543  groupName,
7544  TableGroupKey(groupKey),
7545  sharedGroupInfoPtrs.back(),
7546  threadDone[foundThreadIndex])
7547  .detach();
7548 
7549  ++threadsLaunched;
7550  ++foundThreadIndex;
7551 
7552  } //end alias group thread loop
7553 
7554  //check for all threads done
7555  do
7556  {
7557  foundThreadIndex = -1;
7558  for(int i = 0; i < numOfThreads; ++i)
7559  if(!*(threadDone[i]))
7560  {
7561  foundThreadIndex = i;
7562  break;
7563  }
7564  if(foundThreadIndex != -1)
7565  {
7566  __SUP_COUTT__ << "Waiting for thread to finish... " << foundThreadIndex
7567  << __E__;
7568  usleep(10000);
7569  }
7570  } while(foundThreadIndex != -1); //end thread done search loop
7571 
7572  //threads done now, so copy group info
7573  size_t i = 0;
7574  for(auto& aliasNodePair : aliasNodePairs)
7575  {
7576  groupName = aliasNodePair.second.getNode("GroupName").getValueAsString();
7577  groupKey = aliasNodePair.second.getNode("GroupKey").getValueAsString();
7578 
7579  if(groupKey != sharedGroupInfoPtrs[i]->getLatestKey().toString())
7580  {
7581  __SUP_SS__ << "Error loading group information for the group alias '"
7582  << aliasNodePair.first << "' mapping to group '" << groupName
7583  << "(" << groupKey << ")" << __E__;
7584  __SUP_SS_THROW__;
7585  }
7586 
7587  xmlOut.addTextElementToData("GroupAlias", aliasNodePair.first);
7588  xmlOut.addTextElementToData("GroupName", groupName);
7589  xmlOut.addTextElementToData(
7590  "GroupKey", sharedGroupInfoPtrs[i]->getLatestKey().toString());
7591  xmlOut.addTextElementToData(
7592  "AliasComment",
7593  aliasNodePair.second.getNode(TableViewColumnInfo::COL_NAME_COMMENT)
7594  .getValueAsString());
7595 
7596  xmlOut.addTextElementToData(
7597  "GroupComment", sharedGroupInfoPtrs[i]->getLatestKeyGroupComment());
7598  xmlOut.addTextElementToData(
7599  "GroupAuthor", sharedGroupInfoPtrs[i]->getLatestKeyGroupAuthor());
7600  xmlOut.addTextElementToData(
7601  "GroupCreationTime",
7602  sharedGroupInfoPtrs[i]->getLatestKeyGroupCreationTime());
7603  xmlOut.addTextElementToData(
7604  "GroupType", sharedGroupInfoPtrs[i]->getLatestKeyGroupTypeString());
7605  // xmlOut.addTextElementToData("GroupMemberMap", sharedGroupInfoPtrs[i]->latestKeyMemberMap_);
7606  ++i;
7607  } //end copy group info loop
7608 
7609  } //end multi-thread handling
7610 } // end handleGroupAliasesXML
7611 
7612 //==============================================================================
7623 void ConfigurationGUISupervisor::handleVersionAliasesXML(HttpXmlDocument& xmlOut,
7624  ConfigurationManagerRW* cfgMgr)
7625 {
7626  cfgMgr->loadConfigurationBackbone();
7627  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
7628 
7629  std::string versionAliasesTableName =
7630  ConfigurationManager::VERSION_ALIASES_TABLE_NAME;
7631  if(activeVersions.find(versionAliasesTableName) == activeVersions.end())
7632  {
7633  __SUP_SS__ << "Active version of VersionAliases missing!"
7634  << "Make sure you have a valid active Backbone Group." << __E__;
7635  xmlOut.addTextElementToData("Error", ss.str());
7636  return;
7637  }
7638  __SUP_COUT__ << "activeVersions[\"" << versionAliasesTableName
7639  << "\"]=" << activeVersions[versionAliasesTableName] << __E__;
7640  xmlOut.addTextElementToData("VersionAliasesVersion",
7641  activeVersions[versionAliasesTableName].toString());
7642 
7643  std::vector<std::pair<std::string, ConfigurationTree>> aliasNodePairs =
7644  cfgMgr->getNode(versionAliasesTableName).getChildren();
7645 
7646  for(auto& aliasNodePair : aliasNodePairs)
7647  {
7648  // note : these are column names in the versionAliasesTableName table
7649  // VersionAlias, TableName, Version, CommentDescription
7650  xmlOut.addTextElementToData(
7651  "VersionAlias",
7652  aliasNodePair.second.getNode("VersionAlias").getValueAsString());
7653  xmlOut.addTextElementToData(
7654  "TableName", aliasNodePair.second.getNode("TableName").getValueAsString());
7655  xmlOut.addTextElementToData(
7656  "Version", aliasNodePair.second.getNode("Version").getValueAsString());
7657  xmlOut.addTextElementToData(
7658  "Comment",
7659  aliasNodePair.second.getNode(TableViewColumnInfo::COL_NAME_COMMENT)
7660  .getValueAsString());
7661  }
7662 } // end handleVersionAliasesXML()
7663 
7664 //==============================================================================
7670 void ConfigurationGUISupervisor::handleGetTableGroupTypeXML(
7671  HttpXmlDocument& xmlOut, ConfigurationManagerRW* cfgMgr, const std::string& tableList)
7672 {
7673  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
7674  std::string name, versionStr;
7675  auto c = tableList.find(',', 0);
7676  auto i = c;
7677  i = 0; // auto used to get proper index/length type
7678  while(c < tableList.length())
7679  {
7680  // add the table name and version pair to the map
7681  name = tableList.substr(i, c - i);
7682  i = c + 1;
7683  c = tableList.find(',', i);
7684  if(c == std::string::npos) // missing version list entry?!
7685  {
7686  __SUP_SS__ << "Incomplete Table Name-Version pair!" << __E__;
7687  __SUP_COUT_ERR__ << "\n" << ss.str();
7688  xmlOut.addTextElementToData("Error", ss.str());
7689  return;
7690  }
7691 
7692  versionStr = tableList.substr(i, c - i);
7693  i = c + 1;
7694  c = tableList.find(',', i);
7695 
7696  memberMap[name] = TableVersion(versionStr);
7697  }
7698 
7699  std::string groupTypeString = "";
7700  // try to determine type, dont report errors, just mark ots::GroupType::UNKNOWN_TYPE
7701  try
7702  {
7703  // determine the type of the table group
7704  groupTypeString = cfgMgr->getTypeNameOfGroup(memberMap);
7705  xmlOut.addTextElementToData("TableGroupType", groupTypeString);
7706  }
7707  catch(std::runtime_error& e)
7708  {
7709  __SUP_SS__ << "Table group has invalid type! " << e.what() << __E__;
7710  __SUP_COUT__ << "\n" << ss.str();
7711  groupTypeString = ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN;
7712  xmlOut.addTextElementToData("TableGroupType", groupTypeString);
7713  }
7714  catch(...)
7715  {
7716  __SUP_SS__ << "Table group has invalid type! " << __E__;
7717  try
7718  {
7719  throw;
7720  } //one more try to printout extra info
7721  catch(const std::exception& e)
7722  {
7723  ss << "Exception message: " << e.what();
7724  }
7725  catch(...)
7726  {
7727  }
7728  __SUP_COUT__ << "\n" << ss.str();
7729  groupTypeString = ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN;
7730  xmlOut.addTextElementToData("TableGroupType", groupTypeString);
7731  }
7732 } //end handleGetTableGroupTypeXML()
7733 
7734 //==============================================================================
7750 void ConfigurationGUISupervisor::handleTableGroupsXML(HttpXmlDocument& xmlOut,
7751  ConfigurationManagerRW* cfgMgr,
7752  bool returnMembers)
7753 {
7754  __SUP_COUTT__ << "cfgMgr runtime=" << cfgMgr->runTimeSeconds() << __E__;
7755  // use xmlOut.dataSs_ since there is no need for escape the string and it can be a huge data block to escape and recursively print
7756  // xercesc::DOMElement* parentEl;
7757 
7758  // get all group info from cache (if no cache, get from interface)
7759 
7760  if(!cfgMgr->getAllGroupInfo().size() ||
7761  cfgMgr->getAllGroupInfo().begin()->second.getLatestKeyGroupTypeString() == "" ||
7762  cfgMgr->getAllGroupInfo().begin()->second.getLatestKeyGroupTypeString() ==
7763  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN ||
7764  ( //if active Context group type is not defined, then refresh
7765  cfgMgr->getActiveGroupKey(ConfigurationManager::GroupType::CONTEXT_TYPE) !=
7766  TableGroupKey::INVALID &&
7767  cfgMgr->getAllGroupInfo().find(cfgMgr->getActiveGroupName(
7768  ConfigurationManager::GroupType::CONTEXT_TYPE)) !=
7769  cfgMgr->getAllGroupInfo().end() &&
7770  (cfgMgr->getAllGroupInfo()
7771  .at(cfgMgr->getActiveGroupName(
7772  ConfigurationManager::GroupType::CONTEXT_TYPE))
7773  .getLatestKeyGroupTypeString() == "" ||
7774  cfgMgr->getAllGroupInfo()
7775  .at(cfgMgr->getActiveGroupName(
7776  ConfigurationManager::GroupType::CONTEXT_TYPE))
7777  .getLatestKeyGroupTypeString() ==
7778  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN)) ||
7779  ( //if active Config group type is not defined, then refresh
7780  cfgMgr->getActiveGroupKey(
7781  ConfigurationManager::GroupType::CONFIGURATION_TYPE) !=
7782  TableGroupKey::INVALID &&
7783  cfgMgr->getAllGroupInfo().find(cfgMgr->getActiveGroupName(
7784  ConfigurationManager::GroupType::CONFIGURATION_TYPE)) !=
7785  cfgMgr->getAllGroupInfo().end() &&
7786  (cfgMgr->getAllGroupInfo()
7787  .at(cfgMgr->getActiveGroupName(
7788  ConfigurationManager::GroupType::CONFIGURATION_TYPE))
7789  .getLatestKeyGroupTypeString() == "" ||
7790  cfgMgr->getAllGroupInfo()
7791  .at(cfgMgr->getActiveGroupName(
7792  ConfigurationManager::GroupType::CONFIGURATION_TYPE))
7793  .getLatestKeyGroupTypeString() ==
7794  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN)))
7795  {
7796  __SUP_COUT__
7797  << "Group Info cache appears empty or stale. Attempting to regenerate..."
7798  << __E__;
7799  cfgMgr->getAllTableInfo(true /*refresh*/,
7800  0 /* accumulatedWarnings */,
7801  "" /* errorFilterName */,
7802  true /* getGroupKeys */,
7803  true /* getGroupInfo */,
7804  true /* initializeActiveGroups */);
7805  }
7806 
7807  const std::map<std::string, GroupInfo>& allGroupInfo = cfgMgr->getAllGroupInfo();
7808 
7809  __SUP_COUTT__ << "cfgMgr runtime=" << cfgMgr->runTimeSeconds() << __E__;
7810 
7811  std::string groupName;
7812  std::string groupString, groupTypeString, groupComment, groupCreationTime,
7813  groupAuthor;
7814  for(auto& groupInfo : allGroupInfo)
7815  {
7816  groupName = groupInfo.first;
7817 
7818  //get group info and force update of groupKeys from DB Interface cache if possible
7819  cfgMgr->getGroupInfo(groupName, true /* attemptToReloadKeys */);
7820 
7821  if(groupInfo.second.getKeys().size() == 0)
7822  {
7823  __SUP_COUT__ << "Group name '" << groupName
7824  << "' found, but no keys so ignoring." << __E__;
7825  continue;
7826  }
7827 
7828  xmlOut.dataSs_ << "<TableGroupName value='" << groupName << "'/>" << __E__;
7829  xmlOut.dataSs_ << "<TableGroupKey value='" << groupInfo.second.getLatestKey()
7830  << "'/>" << __E__;
7831 
7832  // trusting the cache!
7833  xmlOut.dataSs_ << "<TableGroupType value='"
7834  << groupInfo.second.getLatestKeyGroupTypeString() << "'/>"
7835  << __E__;
7836  xmlOut.dataSs_ << "<TableGroupComment value='"
7838  groupInfo.second.getLatestKeyGroupComment(),
7839  true /* allowWhiteSpace */)
7840  << "'/>" << __E__;
7841  xmlOut.dataSs_ << "<TableGroupAuthor value='"
7842  << groupInfo.second.getLatestKeyGroupAuthor() << "'/>" << __E__;
7843  xmlOut.dataSs_ << "<TableGroupCreationTime value='"
7844  << groupInfo.second.getLatestKeyGroupCreationTime() << "'/>"
7845  << __E__;
7846 
7847  if(returnMembers)
7848  {
7849  // parentEl = xmlOut.addTextElementToData("TableGroupMembers", "");
7850  xmlOut.dataSs_ << "<TableGroupMembers value=''>" << __E__;
7851 
7852  for(auto& memberPair : groupInfo.second.getLatestKeyMemberMap())
7853  {
7854  xmlOut.dataSs_ << "\t<MemberName value='" << memberPair.first << "'/>"
7855  << __E__;
7856  xmlOut.dataSs_ << "\t<MemberVersion value='" << memberPair.second << "'/>"
7857  << __E__;
7858 
7859  // xmlOut.addTextElementToParent("MemberName", memberPair.first, parentEl);
7860  // xmlOut.addTextElementToParent(
7861  // "MemberVersion", memberPair.second.toString(), parentEl);
7862  }
7863  xmlOut.dataSs_ << "</TableGroupMembers>" << __E__;
7864  } // end if returnMembers
7865 
7866  // add other group keys to xml for this group name
7867  // but just empty members (not displayed anyway)
7868  for(auto& keyInSet : groupInfo.second.getKeys())
7869  {
7870  if(keyInSet == groupInfo.second.getLatestKey())
7871  continue; // skip the lastest
7872 
7873  xmlOut.dataSs_ << "<TableGroupName value='" << groupName << "'/>" << __E__;
7874  xmlOut.dataSs_ << "<TableGroupKey value='" << keyInSet << "'/>" << __E__;
7875  // xmlOut.addTextElementToData("TableGroupName", groupName);
7876  // xmlOut.addTextElementToData("TableGroupKey", keyInSet.toString());
7877 
7878  // TODO -- make loadingHistoricalInfo an input parameter
7879  bool loadingHistoricalInfo = false;
7880  if(loadingHistoricalInfo)
7881  {
7882  groupComment = ""; // clear just in case failure
7883  try
7884  {
7885  cfgMgr->loadTableGroup(groupName,
7886  keyInSet,
7887  0,
7888  0,
7889  0,
7890  0,
7891  &groupComment,
7892  0,
7893  0, // mostly defaults
7894  true /*doNotLoadMembers*/,
7895  &groupTypeString);
7896  }
7897  catch(...)
7898  {
7899  groupTypeString = ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN;
7900  __SUP_COUT_WARN__ << "Failed to load group '" << groupName << "("
7901  << keyInSet
7902  << ")' to extract group comment and type." << __E__;
7903  }
7904 
7905  xmlOut.dataSs_ << "<TableGroupType value='" << groupTypeString << "'/>"
7906  << __E__;
7907  xmlOut.dataSs_ << "<TableGroupComment value='"
7908  << StringMacros::escapeString(groupComment,
7909  true /* allowWhiteSpace */)
7910  << "'/>" << __E__;
7911  xmlOut.dataSs_ << "<TableGroupAuthor value='" << groupAuthor << "'/>"
7912  << __E__;
7913  xmlOut.dataSs_ << "<TableGroupCreationTime value='" << groupCreationTime
7914  << "'/>" << __E__;
7915  // xmlOut.addTextElementToData("TableGroupType", groupTypeString);
7916  // xmlOut.addTextElementToData("TableGroupComment", groupComment);
7917  // xmlOut.addTextElementToData("TableGroupAuthor", groupAuthor);
7918  // xmlOut.addTextElementToData("TableGroupCreationTime", groupCreationTime);
7919  }
7920  else
7921  {
7922  // just use guess that historical groups are of same type
7923  xmlOut.dataSs_ << "<TableGroupType value='"
7924  << groupInfo.second.getLatestKeyGroupTypeString() << "'/>"
7925  << __E__;
7926  //leave place holder comment,author,time for javascript parsing
7927  xmlOut.dataSs_ << "<TableGroupComment value='"
7928  << ""
7929  << "'/>" << __E__;
7930  xmlOut.dataSs_ << "<TableGroupAuthor value='"
7931  << ""
7932  << "'/>" << __E__;
7933  xmlOut.dataSs_ << "<TableGroupCreationTime value='"
7934  << ""
7935  << "'/>" << __E__;
7936  }
7937 
7938  if(returnMembers)
7939  {
7940  //need to add empty group members, event for historical groups, for easier Javascript extraction
7941  xmlOut.dataSs_ << "<TableGroupMembers/>" << __E__;
7942  // xmlOut.addTextElementToData("TableGroupMembers", "");
7943  }
7944 
7945  } // end other key loop
7946  __SUP_COUTT__ << groupName << " runtime=" << cfgMgr->runTimeSeconds() << __E__;
7947  } // end primary group loop
7948  __SUP_COUTT__ << "cfgMgr runtime=" << cfgMgr->runTimeSeconds() << __E__;
7949 } // end handleTableGroupsXML()
7950 
7951 //==============================================================================
7963 void ConfigurationGUISupervisor::handleTablesXML(HttpXmlDocument& xmlOut,
7964  ConfigurationManagerRW* cfgMgr)
7965 {
7966  if(cfgMgr->getAllGroupInfo().size() == 0 || cfgMgr->getActiveVersions().size() == 0)
7967  {
7968  __SUP_COUT__ << "Table Info cache appears empty. Attempting to regenerate."
7969  << __E__;
7970  cfgMgr->getAllTableInfo(true /*refresh*/,
7971  0 /* accumulatedWarnings */,
7972  "" /* errorFilterName */,
7973  false /* getGroupKeys */,
7974  false /* getGroupInfo */,
7975  true /* initializeActiveGroups */);
7976  }
7977 
7978  xercesc::DOMElement* parentEl;
7979  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
7980 
7981  // construct specially ordered table name set
7982  std::set<std::string, StringMacros::IgnoreCaseCompareStruct> orderedTableSet;
7983  for(const auto& tablePair : allTableInfo)
7984  orderedTableSet.emplace(tablePair.first);
7985 
7986  // std::map<std::string, TableInfo>::const_iterator it = allTableInfo.begin();
7987 
7988  __SUP_COUT__ << "# of tables found: " << allTableInfo.size() << __E__;
7989 
7990  std::map<std::string, std::map<std::string, TableVersion>> versionAliases =
7991  cfgMgr->getVersionAliases();
7992 
7993  __SUP_COUT__ << "# of tables w/aliases: " << versionAliases.size() << __E__;
7994 
7995  for(const auto& orderedTableName : orderedTableSet) // while(it !=
7996  // allTableInfo.end())
7997  {
7998  std::map<std::string, TableInfo>::const_iterator it =
7999  allTableInfo.find(orderedTableName);
8000  if(it == allTableInfo.end())
8001  {
8002  __SS__ << "Impossible missing table in map '" << orderedTableName << "'"
8003  << __E__;
8004  __SS_THROW__;
8005  }
8006 
8007  // for each table name
8008  // get existing version keys
8009 
8010  // add system table name
8011  xmlOut.addTextElementToData("TableName", it->first);
8012  parentEl = xmlOut.addTextElementToData("TableVersions", "");
8013 
8014  // include aliases for this table (if the versions exist)
8015  if(versionAliases.find(it->first) != versionAliases.end())
8016  for(auto& aliasVersion : versionAliases[it->first])
8017  if(it->second.versions_.find(aliasVersion.second) !=
8018  it->second.versions_
8019  .end()) //Note : scratch version is always an alias ==> ConfigurationManager::SCRATCH_VERSION_ALIAS)
8020  xmlOut.addTextElementToParent(
8021  "Version",
8022  ConfigurationManager::ALIAS_VERSION_PREAMBLE + aliasVersion.first,
8023  parentEl);
8024 
8025  // get all table versions for the current table
8026  // except skip scratch version
8027  // for speed, group versions into spans:
8028  //======
8030  auto vSpanToXML = [](auto const& sortedKeys, auto& xmlOut, auto& configEl) {
8031  //add lo and hi spans, instead of each individual value
8032  size_t lo = -1, hi = -1;
8033  for(auto& keyInOrder : sortedKeys)
8034  {
8035  //skip scratch version
8036  if(keyInOrder.isScratchVersion())
8037  continue;
8038 
8039  if(lo == size_t(-1)) //establish start of potential span
8040  {
8041  hi = lo = keyInOrder.version();
8042  continue;
8043  }
8044  else if(hi + 1 == keyInOrder.version()) //span is growing
8045  {
8046  hi = keyInOrder.version();
8047  continue;
8048  }
8049  //else jump by more than one, so close out span
8050 
8051  if(lo == hi) //single value
8052  xmlOut.addNumberElementToParent("Version", lo, configEl);
8053  else //span
8054  xmlOut.addTextElementToParent(
8055  "Version",
8056  "_" + std::to_string(lo) + "_" + std::to_string(hi),
8057  configEl);
8058  hi = lo = keyInOrder.version();
8059  }
8060 
8061  if(lo != size_t(-1)) //check if last one to do!
8062  {
8063  if(lo == hi) //single value
8064  xmlOut.addNumberElementToParent("Version", lo, configEl);
8065  else //span
8066  xmlOut.addTextElementToParent(
8067  "Version",
8068  "_" + std::to_string(lo) + "_" + std::to_string(hi),
8069  configEl);
8070  }
8071  }; //end local lambda vSpanToXML()
8072 
8073  vSpanToXML(it->second.versions_, xmlOut, parentEl);
8074 
8075  } // end table loop
8076 
8077 } // end handleTablesXML()
8078 
8079 //==============================================================================
8086 void ConfigurationGUISupervisor::handleGetArtdaqNodeRecordsXML(
8087  HttpXmlDocument& xmlOut,
8088  ConfigurationManagerRW* cfgMgr,
8089  const std::string& modifiedTables)
8090 {
8091  __COUT__ << "Retrieving artdaq nodes..." << __E__;
8092 
8093  // setup active tables based on active groups and modified tables
8094  setupActiveTablesXML(
8095  xmlOut, cfgMgr, "", TableGroupKey(-1), modifiedTables, false /* refreshAll */);
8096 
8097  std::map<std::string /*type*/,
8098  std::map<std::string /*record*/, std::vector<std::string /*property*/>>>
8099  nodeTypeToObjectMap;
8100  std::map<std::string /*subsystemName*/, std::string /*destinationSubsystemName*/>
8101  subsystemObjectMap;
8102 
8103  std::vector<std::string /*property*/> artdaqSupervisorInfo;
8104 
8105  std::string artdaqSupervisorName;
8106  const ARTDAQTableBase::ARTDAQInfo& info = ARTDAQTableBase::getARTDAQSystem(
8107  cfgMgr, nodeTypeToObjectMap, subsystemObjectMap, artdaqSupervisorInfo);
8108 
8109  if(artdaqSupervisorInfo.size() != 4 /*expecting 4 artdaq Supervisor parameters*/)
8110  {
8111  __SUP_COUT__ << "No artdaq supervisor found." << __E__;
8112  return;
8113  }
8114 
8115  __SUP_COUT__ << "========== "
8116  << "Found " << info.subsystems.size() << " subsystems." << __E__;
8117 
8118  unsigned int paramIndex = 0; // start at first artdaq Supervisor parameter
8119 
8120  auto parentEl = xmlOut.addTextElementToData("artdaqSupervisor",
8121  artdaqSupervisorInfo[paramIndex++]);
8122 
8123  std::string typeString = "artdaqSupervisor";
8124 
8125  xmlOut.addTextElementToParent(
8126  typeString + "-status", artdaqSupervisorInfo[paramIndex++], parentEl);
8127  xmlOut.addTextElementToParent(
8128  typeString + "-contextAddress", artdaqSupervisorInfo[paramIndex++], parentEl);
8129  xmlOut.addTextElementToParent(
8130  typeString + "-contextPort", artdaqSupervisorInfo[paramIndex++], parentEl);
8131 
8132  for(auto& subsystem : info.subsystems)
8133  {
8134  typeString = "subsystem";
8135 
8136  __SUP_COUT__ << "\t\t"
8137  << "Found " << typeString << " " << subsystem.first << " \t := '"
8138  << subsystem.second.label << "'" << __E__;
8139 
8140  xmlOut.addTextElementToParent(typeString, subsystem.second.label, parentEl);
8141  xmlOut.addTextElementToParent(
8142  typeString + "-id", std::to_string(subsystem.first), parentEl);
8143 
8144  xmlOut.addTextElementToParent(typeString + "-sourcesCount",
8145  std::to_string(subsystem.second.sources.size()),
8146  parentEl);
8147 
8148  // destination
8149  xmlOut.addTextElementToParent(typeString + "-destination",
8150  std::to_string(subsystem.second.destination),
8151  parentEl);
8152 
8153  } // end subsystem handling
8154 
8155  __SUP_COUT__ << "========== "
8156  << "Found " << nodeTypeToObjectMap.size() << " process types." << __E__;
8157 
8158  for(auto& nameTypePair : nodeTypeToObjectMap)
8159  {
8160  typeString = nameTypePair.first;
8161 
8162  __SUP_COUT__ << "\t"
8163  << "Found " << nameTypePair.second.size() << " " << typeString
8164  << "(s)" << __E__;
8165 
8166  for(auto& artdaqNode : nameTypePair.second)
8167  {
8168  __SUP_COUT__ << "\t\t"
8169  << "Found '" << artdaqNode.first << "' " << typeString << __E__;
8170  __SUP_COUTV__(StringMacros::vectorToString(artdaqNode.second));
8171 
8172  if(artdaqNode.second.size() < 2)
8173  {
8174  __SUP_SS__ << "Impossible parameter size for node '" << artdaqNode.first
8175  << "' " << typeString << " - please notify admins!" << __E__;
8176  __SUP_SS_THROW__;
8177  }
8178 
8179  auto nodeEl =
8180  xmlOut.addTextElementToParent(typeString, artdaqNode.first, parentEl);
8181 
8182  paramIndex = 3; // start at 3 after subsystem parameter
8183  if(artdaqNode.second.size() > paramIndex)
8184  {
8185  __SUP_COUTT__ << "\t\t\t"
8186  << "-multinode: " << artdaqNode.second[paramIndex] << __E__;
8187  xmlOut.addTextElementToParent(
8188  typeString + "-multinode", artdaqNode.second[paramIndex++], nodeEl);
8189  }
8190  if(artdaqNode.second.size() > paramIndex)
8191  {
8192  __SUP_COUTT__ << "\t\t\t"
8193  << "-nodefixedwidth: " << artdaqNode.second[paramIndex]
8194  << __E__;
8195  xmlOut.addTextElementToParent(typeString + "-nodefixedwidth",
8196  artdaqNode.second[paramIndex++],
8197  nodeEl);
8198  }
8199  if(artdaqNode.second.size() > paramIndex)
8200  {
8201  __SUP_COUTT__ << "\t\t\t"
8202  << "-hostarray: " << artdaqNode.second[paramIndex] << __E__;
8203  xmlOut.addTextElementToParent(
8204  typeString + "-hostarray", artdaqNode.second[paramIndex++], nodeEl);
8205  }
8206  if(artdaqNode.second.size() > paramIndex)
8207  {
8208  __SUP_COUTT__ << "\t\t\t"
8209  << "-hostfixedwidth: " << artdaqNode.second[paramIndex]
8210  << __E__;
8211  xmlOut.addTextElementToParent(typeString + "-hostfixedwidth",
8212  artdaqNode.second[paramIndex++],
8213  nodeEl);
8214  }
8215 
8216  paramIndex = 0; // return to starting parameter
8217  __SUP_COUTT__ << "\t\t\t"
8218  << "-status: " << artdaqNode.second[paramIndex] << __E__;
8219  xmlOut.addTextElementToParent(
8220  typeString + "-status", artdaqNode.second[paramIndex++], parentEl);
8221  __SUP_COUTT__ << "\t\t\t"
8222  << "-hostname: " << artdaqNode.second[paramIndex] << __E__;
8223  xmlOut.addTextElementToParent(
8224  typeString + "-hostname", artdaqNode.second[paramIndex++], parentEl);
8225  __SUP_COUTT__ << "\t\t\t"
8226  << "-subsystem: " << artdaqNode.second[paramIndex] << __E__;
8227  xmlOut.addTextElementToParent(
8228  typeString + "-subsystem", artdaqNode.second[paramIndex], parentEl);
8229  }
8230  } // end processor type handling
8231 
8232  __SUP_COUT__ << "Done retrieving artdaq nodes." << __E__;
8233 
8234 } // end handleGetArtdaqNodeRecordsXML()
8235 
8236 //==============================================================================
8243 void ConfigurationGUISupervisor::handleSaveArtdaqNodeRecordsXML(
8244  const std::string& nodeString,
8245  const std::string& subsystemString,
8246  HttpXmlDocument& xmlOut,
8247  ConfigurationManagerRW* cfgMgr,
8248  const std::string& modifiedTables)
8249 {
8250  __SUP_COUT__ << "Saving artdaq nodes..." << __E__;
8251 
8252  // setup active tables based on active groups and modified tables
8253  setupActiveTablesXML(
8254  xmlOut, cfgMgr, "", TableGroupKey(-1), modifiedTables, false /* refreshAll */);
8255 
8256  // start node object extraction from nodeString
8257  std::map<std::string /*type*/,
8258  std::map<std::string /*record*/, std::vector<std::string /*property*/>>>
8259  nodeTypeToObjectMap;
8260  {
8261  // nodeString format:
8262  // <type>:<nodeName>=<originalName>,<hostname>,<subsystemName>;<nodeName>=<originalName>,<hostname>,<subsystemName>;
8263  // ... |<type>:...|
8264  // repeat | separated types
8265  std::map<std::string /*type*/, std::string /*typeRecordSetString*/>
8266  nodeTypeToStringMap;
8267  StringMacros::getMapFromString(nodeString, nodeTypeToStringMap, {'|'}, {':'});
8268 
8269  __SUP_COUTV__(StringMacros::mapToString(nodeTypeToStringMap));
8270 
8271  for(auto& typePair : nodeTypeToStringMap)
8272  {
8273  if(typePair.first == "")
8274  continue; // skip empty names
8275 
8276  __SUP_COUTV__(StringMacros::decodeURIComponent(typePair.first));
8277 
8278  nodeTypeToObjectMap.emplace(
8279  std::make_pair(StringMacros::decodeURIComponent(typePair.first),
8280  std::map<std::string /*record*/,
8281  std::vector<std::string /*property*/>>()));
8282 
8283  std::map<std::string /*node*/, std::string /*nodeRecordSetString*/>
8284  nodeRecordToStringMap;
8285 
8287  typePair.second, nodeRecordToStringMap, {';'}, {'='});
8288 
8289  __SUP_COUTV__(StringMacros::mapToString(nodeRecordToStringMap));
8290 
8291  for(auto& nodePair : nodeRecordToStringMap)
8292  {
8293  if(nodePair.first == "")
8294  continue; // skip empty names
8295 
8296  __SUP_COUTV__(StringMacros::decodeURIComponent(nodePair.first));
8297 
8298  std::vector<std::string /*property*/> nodePropertyVector;
8299 
8301  nodePair.second, nodePropertyVector, {','});
8302 
8303  __SUP_COUTV__(StringMacros::vectorToString(nodePropertyVector));
8304 
8305  // decode all properties
8306  for(unsigned int i = 0; i < nodePropertyVector.size(); ++i)
8307  {
8308  __SUP_COUTV__(
8309  StringMacros::decodeURIComponent(nodePropertyVector[i]));
8310 
8311  nodePropertyVector[i] =
8312  StringMacros::decodeURIComponent(nodePropertyVector[i]);
8313  }
8314 
8315  nodeTypeToObjectMap[typePair.first].emplace(
8316  std::make_pair(StringMacros::decodeURIComponent(nodePair.first),
8317  nodePropertyVector));
8318  }
8319  }
8320  } // end node object extraction from nodeString
8321 
8322  // start subsystem object extraction from subsystemString
8323  std::map<std::string /*subsystemName*/, std::string /*destinationSubsystemName*/>
8324  subsystemObjectMap;
8325  {
8326  // subsystemString format:
8327  // <name>:<destination>;<name>:<destination>; ...;
8328  // repeat ; separated subsystems
8329 
8330  std::map<std::string /*subsystemName*/, std::string /*destinationSubsystemName*/>
8331  tmpSubsystemObjectMap;
8333  subsystemString, tmpSubsystemObjectMap, {';'}, {':'});
8334 
8335  __SUP_COUTV__(StringMacros::mapToString(tmpSubsystemObjectMap));
8336 
8337  // decode all values (probably unnecessary, but more future proof)
8338  for(auto& subsystemPair : tmpSubsystemObjectMap)
8339  {
8340  __SUP_COUTV__(StringMacros::decodeURIComponent(subsystemPair.first));
8341  __SUP_COUTV__(StringMacros::decodeURIComponent(subsystemPair.second));
8342 
8343  subsystemObjectMap.emplace(
8344  std::make_pair(StringMacros::decodeURIComponent(subsystemPair.first),
8345  StringMacros::decodeURIComponent(subsystemPair.second)));
8346  }
8347  } // end subsystem object extraction from subsystemString
8348 
8350  cfgMgr, nodeTypeToObjectMap, subsystemObjectMap);
8351 
8352  __SUP_COUT__ << "Done saving artdaq nodes." << __E__;
8353 } // end handleSaveArtdaqNodeRecordsXML()
8354 
8355 //==============================================================================
8362 void ConfigurationGUISupervisor::handleLoadArtdaqNodeLayoutXML(
8363  HttpXmlDocument& xmlOut,
8365  cfgMgr, //force read-only config manager to avoid requiring user-lock (i.e., not ConfigurationManagerRW)
8366  const std::string& contextGroupName /* = "" */,
8367  const TableGroupKey& contextGroupKey /* = INVALID */) const
8368 {
8369  bool usingActiveGroups = (contextGroupName == "" || contextGroupKey.isInvalid());
8370 
8371  //NOTE: must be same/similar code as otsdaq/otsdaq/TablePlugins/ARTDAQTableBase/ARTDAQTableBase.cc:2332
8372  const std::string& finalContextGroupName =
8373  usingActiveGroups
8374  ? cfgMgr->getActiveGroupName(ConfigurationManager::GroupType::CONTEXT_TYPE)
8375  : contextGroupName;
8376  const TableGroupKey& finalContextGroupKey =
8377  usingActiveGroups
8378  ? cfgMgr->getActiveGroupKey(ConfigurationManager::GroupType::CONTEXT_TYPE)
8379  : contextGroupKey;
8380  const std::string& finalConfigGroupName =
8381  cfgMgr->getActiveGroupName(ConfigurationManager::GroupType::CONFIGURATION_TYPE);
8382  const TableGroupKey& finalConfigGroupKey =
8383  cfgMgr->getActiveGroupKey(ConfigurationManager::GroupType::CONFIGURATION_TYPE);
8384 
8385  FILE* fp = nullptr;
8386  //first try context+config name only
8387  {
8388  std::stringstream layoutPath;
8389  layoutPath << ARTDAQTableBase::ARTDAQ_CONFIG_LAYOUTS_PATH << finalContextGroupName
8390  << "_" << finalContextGroupKey << "." << finalConfigGroupName << "_"
8391  << finalConfigGroupKey << ".dat";
8392 
8393  fp = fopen(layoutPath.str().c_str(), "r");
8394  if(!fp)
8395  {
8396  __SUP_COUT__ << "Layout file not found for '" << finalContextGroupName << "("
8397  << finalContextGroupKey << ") + " << finalConfigGroupName << "("
8398  << finalConfigGroupKey << ")': " << layoutPath.str() << __E__;
8399  // return; //try context only!
8400  }
8401  else
8402  __SUP_COUTV__(layoutPath.str());
8403  }
8404  //last try context name only
8405  if(!fp)
8406  {
8407  std::stringstream layoutPath;
8408  layoutPath << ARTDAQTableBase::ARTDAQ_CONFIG_LAYOUTS_PATH << finalContextGroupName
8409  << "_" << finalContextGroupKey << ".dat";
8410  __SUP_COUTV__(layoutPath.str());
8411 
8412  fp = fopen(layoutPath.str().c_str(), "r");
8413  if(!fp)
8414  {
8415  __SUP_COUT__ << "Layout file not found for '" << finalContextGroupName << "("
8416  << finalContextGroupKey << ")': " << layoutPath.str() << __E__;
8417  return;
8418  }
8419  else
8420  __SUP_COUTV__(layoutPath.str());
8421  }
8422 
8423  // file format is line by line
8424  // line 0 -- grid: <rows> <cols>
8425  // line 1-N -- node: <type> <name> <x-grid> <y-grid>
8426 
8427  const size_t maxLineSz = 1000;
8428  char line[maxLineSz];
8429  if(!fgets(line, maxLineSz, fp))
8430  {
8431  fclose(fp);
8432  return;
8433  }
8434  else
8435  {
8436  // extract grid
8437 
8438  unsigned int rows, cols;
8439 
8440  sscanf(line, "%u %u", &rows, &cols);
8441 
8442  __COUT__ << "Grid rows,cols = " << rows << "," << cols << __E__;
8443 
8444  xmlOut.addTextElementToData("grid-rows", std::to_string(rows));
8445  xmlOut.addTextElementToData("grid-cols", std::to_string(cols));
8446  }
8447 
8448  char name[maxLineSz];
8449  char type[maxLineSz];
8450  unsigned int x, y;
8451  while(fgets(line, maxLineSz, fp))
8452  {
8453  // extract node
8454  sscanf(line, "%s %s %u %u", type, name, &x, &y);
8455 
8456  xmlOut.addTextElementToData("node-type", type);
8457  xmlOut.addTextElementToData("node-name", name);
8458  xmlOut.addTextElementToData("node-x", std::to_string(x));
8459  xmlOut.addTextElementToData("node-y", std::to_string(y));
8460  } // end node extraction loop
8461 
8462  fclose(fp);
8463 
8464 } // end handleLoadArtdaqNodeLayoutXML()
8465 
8466 //==============================================================================
8473 void ConfigurationGUISupervisor::handleSaveArtdaqNodeLayoutXML(
8474  HttpXmlDocument& /*xmlOut*/,
8475  ConfigurationManagerRW* cfgMgr,
8476  const std::string& layoutString,
8477  const std::string& contextGroupName,
8478  const TableGroupKey& contextGroupKey)
8479 {
8480  bool usingActiveGroups = (contextGroupName == "" || contextGroupKey.isInvalid());
8481 
8482  const std::string& finalContextGroupName =
8483  usingActiveGroups
8484  ? cfgMgr->getActiveGroupName(ConfigurationManager::GroupType::CONTEXT_TYPE)
8485  : contextGroupName;
8486  const TableGroupKey& finalContextGroupKey =
8487  usingActiveGroups
8488  ? cfgMgr->getActiveGroupKey(ConfigurationManager::GroupType::CONTEXT_TYPE)
8489  : contextGroupKey;
8490  const std::string& finalConfigGroupName =
8491  cfgMgr->getActiveGroupName(ConfigurationManager::GroupType::CONFIGURATION_TYPE);
8492  const TableGroupKey& finalConfigGroupKey =
8493  cfgMgr->getActiveGroupKey(ConfigurationManager::GroupType::CONFIGURATION_TYPE);
8494 
8495  __SUP_COUTV__(layoutString);
8496 
8497  std::stringstream layoutPath;
8498  layoutPath << ARTDAQTableBase::ARTDAQ_CONFIG_LAYOUTS_PATH << finalContextGroupName
8499  << "_" << finalContextGroupKey << "." << finalConfigGroupName << "_"
8500  << finalConfigGroupKey << ".dat";
8501  __SUP_COUTV__(layoutPath.str());
8502 
8503  std::vector<std::string> fields = StringMacros::getVectorFromString(layoutString);
8504  __SUP_COUTV__(StringMacros::vectorToString(fields));
8505 
8506  if(fields.size() < 2 || (fields.size() - 2) % 4 != 0)
8507  {
8508  __SUP_SS__ << "Invalid layout string fields size of " << fields.size() << __E__;
8509  __SUP_SS_THROW__;
8510  }
8511 
8512  FILE* fp = fopen(layoutPath.str().c_str(), "w");
8513  if(!fp)
8514  {
8515  __SUP_SS__ << "Could not open layout file for writing for '"
8516  << finalContextGroupName << "(" << finalContextGroupKey << ") + "
8517  << finalConfigGroupName << "(" << finalConfigGroupKey
8518  << ")': " << layoutPath.str() << __E__;
8519  __SUP_SS_THROW__;
8520  }
8521 
8522  // match load code at ::handleLoadArtdaqNodeLayoutXML()
8523 
8524  // write grid
8525  fprintf(fp, "%s %s\n", fields[0].c_str(), fields[1].c_str());
8526 
8527  // write nodes
8528  for(unsigned int i = 2; i < fields.size(); i += 4)
8529  fprintf(fp,
8530  "%s %s %s %s\n",
8531  fields[i + 0].c_str(),
8532  fields[i + 1].c_str(),
8533  fields[i + 2].c_str(),
8534  fields[i + 3].c_str());
8535 
8536  fclose(fp);
8537 
8538 } // end handleSaveArtdaqNodeLayoutXML()
8539 
8540 //==============================================================================
8542 void ConfigurationGUISupervisor::handleOtherSubsystemActiveGroups(
8543  HttpXmlDocument& xmlOut,
8544  ConfigurationManagerRW* cfgMgr,
8545  bool getFullList,
8546  std::string targetSubsystem /* = "" */)
8547 try
8548 {
8549  try
8550  {
8551  ConfigurationTree node =
8552  cfgMgr->getNode(ConfigurationManager::CONTEXT_SUBSYSTEM_OPTIONAL_TABLE);
8553  auto children = node.getChildren();
8554 
8555  for(auto subsystem : children)
8556  {
8557  __SUP_COUTV__(subsystem.first);
8558  __SUP_COUTV__(
8559  StringMacros::vectorToString(subsystem.second.getChildrenNames()));
8560 
8561  std::string userPath =
8562  subsystem.second.getNode("SubsystemUserDataPath").getValue();
8563  __SUP_COUTV__(userPath);
8564  }
8565  }
8566  catch(const std::runtime_error& e)
8567  {
8568  __SUP_COUT__ << "Ignoring errors in handling other subsystem active groups "
8569  "(assuming the subsystem information map is not setup in "
8570  << ConfigurationManager::CONTEXT_SUBSYSTEM_OPTIONAL_TABLE
8571  << ") -- here is the error: \n"
8572  << e.what() << __E__;
8573  return; //ignore errors if subsystems not defined
8574  }
8575 
8576  //else subsystems are defined, so do not ignore errors!
8577 
8578  ConfigurationTree node =
8579  cfgMgr->getNode(ConfigurationManager::CONTEXT_SUBSYSTEM_OPTIONAL_TABLE);
8580  auto children = node.getChildren();
8581  for(auto subsystem : children)
8582  {
8583  if(targetSubsystem != "" && targetSubsystem != subsystem.first)
8584  continue; //skip non-target subsystem
8585 
8586  xercesc::DOMElement* parent =
8587  xmlOut.addTextElementToData("SubsystemName", subsystem.first);
8588 
8589  if(!getFullList)
8590  continue;
8591 
8592  std::string filename, userDataPath;
8593  std::string username, hostname;
8594 
8595  std::map<std::string /*groupType*/,
8596  std::pair<std::string /*groupName*/, TableGroupKey>>
8597  retMap = cfgMgr->getOtherSubsystemActiveTableGroups(
8598  subsystem.first, &userDataPath, &hostname, &username);
8599 
8600  for(const auto& retPair : retMap)
8601  {
8602  xmlOut.addTextElementToParent("CurrentlyActive" + retPair.first + "GroupName",
8603  retPair.second.first,
8604  parent);
8605  xmlOut.addTextElementToParent("CurrentlyActive" + retPair.first + "GroupKey",
8606  retPair.second.second.toString(),
8607  parent);
8608  }
8609 
8610  std::vector<std::string> filenameTypes = {"Configured",
8611  "Started",
8612  "ActivatedConfig",
8613  "ActivatedContext",
8614  "ActivatedBackbone",
8615  "ActivatedIterator"};
8616 
8617  std::vector<std::string> filenames = {
8618  ConfigurationManager::LAST_CONFIGURED_CONFIG_GROUP_FILE,
8619  ConfigurationManager::LAST_STARTED_CONFIG_GROUP_FILE,
8620  ConfigurationManager::LAST_ACTIVATED_CONFIG_GROUP_FILE,
8621  ConfigurationManager::LAST_ACTIVATED_CONTEXT_GROUP_FILE,
8622  ConfigurationManager::LAST_ACTIVATED_BACKBONE_GROUP_FILE,
8623  ConfigurationManager::LAST_ACTIVATED_ITERATE_GROUP_FILE};
8624 
8625  std::string userPath =
8626  subsystem.second.getNode("SubsystemUserDataPath").getValue();
8627  auto splitPath = StringMacros::getVectorFromString(userPath, {':'});
8628  std::string cmdResult;
8629  for(unsigned int i = 0; i < filenames.size(); ++i)
8630  {
8631  filename = userDataPath + "/ServiceData/RunControlData/" + filenames[i];
8632  __SUP_COUTV__(filename);
8633 
8634  std::string tmpSubsystemFilename =
8635  ConfigurationManager::LAST_TABLE_GROUP_SAVE_PATH + "/" + filenames[i] +
8636  "." + subsystem.first;
8637  __SUP_COUTV__(tmpSubsystemFilename);
8638 
8639  if(splitPath.size() == 2) //must scp
8640  {
8641  if(username.size()) //has username
8642  cmdResult = StringMacros::exec(
8643  ("rm " + tmpSubsystemFilename + " 2>/dev/null; scp " + username +
8644  "@" + hostname + ":" + filename + " " + tmpSubsystemFilename +
8645  " 2>&1; cat " + tmpSubsystemFilename + " 2>&1")
8646  .c_str());
8647  else
8648  cmdResult = StringMacros::exec(
8649  ("rm " + tmpSubsystemFilename + " 2>/dev/null; scp " + hostname +
8650  ":" + filename + " " + tmpSubsystemFilename + " 2>&1; cat " +
8651  tmpSubsystemFilename + " 2>&1")
8652  .c_str());
8653  }
8654  else if(splitPath.size() == 1) //then can just directly access the file
8655  {
8656  cmdResult = StringMacros::exec(("rm " + tmpSubsystemFilename +
8657  " 2>/dev/null; cp " + filename + " " +
8658  tmpSubsystemFilename + " 2>&1; cat " +
8659  tmpSubsystemFilename + " 2>&1")
8660  .c_str());
8661  }
8662 
8663  __SUP_COUTV__(cmdResult);
8664  std::string timeString;
8665  std::pair<std::string /*group name*/, TableGroupKey> theGroup =
8667  filenames[i] + "." + subsystem.first, timeString);
8668 
8669  // fill return parameters
8670  xmlOut.addTextElementToParent(
8671  "Last" + filenameTypes[i] + "GroupName", theGroup.first, parent);
8672  xmlOut.addTextElementToParent("Last" + filenameTypes[i] + "GroupKey",
8673  theGroup.second.toString(),
8674  parent);
8675  xmlOut.addTextElementToParent(
8676  "Last" + filenameTypes[i] + "GroupTime", timeString, parent);
8677  } // end active/recent filename handling
8678 
8679  } //end subsystem loop
8680 } // end getSubsytemTableGroups()
8681 catch(const std::runtime_error& e)
8682 {
8683  __SUP_SS__
8684  << "An error occurred handling subsystem active groups (Please check the "
8685  "subsystem user data path information map setup in the Context group table "
8686  << ConfigurationManager::CONTEXT_SUBSYSTEM_OPTIONAL_TABLE
8687  << ") -- here is the error: \n"
8688  << e.what() << __E__;
8689  __SUP_SS_THROW__;
8690 } // end getSubsytemTableGroups() catch
8691 
8692 //==============================================================================
8694 void ConfigurationGUISupervisor::handleGroupDiff(
8695  HttpXmlDocument& xmlOut,
8696  ConfigurationManagerRW* cfgMgr,
8697  const std::string& groupName,
8698  const TableGroupKey& groupKey,
8699  const TableGroupKey& diffKey /* = TableGroupKey() */,
8700  const std::string& diffGroupNameInput /* = "" */)
8701 {
8702  //Steps:
8703  // - Get group type and load table map
8704  // - Get match type active group table map
8705  // - For each table, compare
8706  std::string diffGroupName;
8707 
8708  if(diffKey.isInvalid())
8709  __SUP_COUT__ << "Differencing group " << groupName << "(" << groupKey
8710  << ") with the active group." << __E__;
8711  else
8712  {
8713  if(diffGroupNameInput == "")
8714  diffGroupName = groupName;
8715  else
8716  diffGroupName = diffGroupNameInput;
8717 
8718  __SUP_COUT__ << "Differencing group " << groupName << "(" << groupKey
8719  << ") with group " << diffGroupName << "(" << diffKey << ")"
8720  << __E__;
8721  }
8722 
8723  try
8724  {
8725  std::map<std::string /*name*/, TableVersion /*version*/> memberMap, diffMemberMap;
8726  std::string groupType, accumulateErrors;
8727  std::stringstream diffReport;
8728  bool noDifference = true;
8729 
8730  cfgMgr->loadTableGroup(
8731  groupName,
8732  groupKey,
8733  false /*doActivate*/,
8734  &memberMap /*groupMembers*/,
8735  0 /*progressBar*/,
8736  &accumulateErrors /*accumulateErrors*/,
8737  0 /*groupComment*/,
8738  0 /*groupAuthor*/,
8739  0 /*groupCreationTime*/,
8740  false /*doNotLoadMember*/,
8741  (diffKey.isInvalid()
8742  ? &groupType
8743  : 0)); //for specified diff group (not active), do not need groupType
8744 
8745  __SUP_COUTV__(StringMacros::mapToString(memberMap));
8746 
8747  std::map<std::string /* groupType */, std::pair<std::string, TableGroupKey>>
8748  activeGroups;
8749  if(diffKey.isInvalid())
8750  {
8751  activeGroups = cfgMgr->getActiveTableGroups();
8752 
8753  __SUP_COUTV__(StringMacros::mapToString(activeGroups));
8754  __SUP_COUTV__(groupType);
8755 
8756  if(activeGroups.find(groupType) == activeGroups.end() ||
8757  activeGroups.at(groupType).first == "" ||
8758  activeGroups.at(groupType).second.isInvalid())
8759  {
8760  __SUP_SS__ << "Could not find an active group of type '" << groupType
8761  << ".' Please check the expected active configuration groups "
8762  "for errors (going to 'System View' of the Config App may "
8763  "reveal errors)."
8764  << __E__;
8765  __SUP_SS_THROW__;
8766  }
8767 
8768  __SUP_COUT__ << "active " << groupType << " group is "
8769  << activeGroups.at(groupType).first << "("
8770  << activeGroups.at(groupType).second << ")" << __E__;
8771 
8772  diffReport << "This difference report is between " << groupType
8773  << " group <b>'" << groupName << "(" << groupKey << ")'</b>"
8774  << " and active group <b>'" << activeGroups.at(groupType).first
8775  << "(" << activeGroups.at(groupType).second << ")'</b>." << __E__;
8776 
8777  cfgMgr->loadTableGroup(activeGroups.at(groupType).first,
8778  activeGroups.at(groupType).second,
8779  false /*doActivate*/,
8780  &diffMemberMap /*groupMembers*/,
8781  0 /*progressBar*/,
8782  &accumulateErrors /*accumulateErrors*/,
8783  0 /*groupComment*/,
8784  0 /*groupAuthor*/,
8785  0 /*groupCreationTime*/,
8786  false /*doNotLoadMember*/);
8787 
8788  diffReport << "\n\n"
8789  << "'" << groupName << "(" << groupKey << ")' has <b>"
8790  << memberMap.size() << " member tables</b>, and "
8791  << "'" << activeGroups.at(groupType).first << "("
8792  << activeGroups.at(groupType).second << ")' has <b>"
8793  << diffMemberMap.size() << " member tables</b>." << __E__;
8794  }
8795  else //specified diff group (not active), so do not need groupType
8796  {
8797  diffReport << "This difference report is between group <b>'" << groupName
8798  << "(" << groupKey << ")'</b>"
8799  << " and group <b>'" << diffGroupName << "(" << diffKey
8800  << ")'</b>." << __E__;
8801 
8802  cfgMgr->loadTableGroup(diffGroupName,
8803  diffKey,
8804  false /*doActivate*/,
8805  &diffMemberMap /*groupMembers*/,
8806  0 /*progressBar*/,
8807  &accumulateErrors /*accumulateErrors*/,
8808  0 /*groupComment*/,
8809  0 /*groupAuthor*/,
8810  0 /*groupCreationTime*/,
8811  false /*doNotLoadMember*/);
8812 
8813  diffReport << "\n\n"
8814  << "'" << groupName << "(" << groupKey << ")' has <b>"
8815  << memberMap.size() << " member tables</b>, and "
8816  << "'" << diffGroupName << "(" << diffKey << ")' has <b>"
8817  << diffMemberMap.size() << " member tables</b>." << __E__;
8818  }
8819 
8820  __SUP_COUTV__(StringMacros::mapToString(diffMemberMap));
8821 
8822  diffReport << "<INDENT><ol>";
8823 
8824  unsigned int tableDifferences = 0;
8825 
8826  for(auto& member : memberMap)
8827  {
8828  if(diffMemberMap.find(member.first) == diffMemberMap.end())
8829  {
8830  diffReport << "\n\n<li>"
8831  << "Table <b>" << member.first << "-v" << member.second
8832  << "</b> not found in active group."
8833  << "</li>" << __E__;
8834  noDifference = false;
8835  ++tableDifferences;
8836  continue;
8837  }
8838 
8839  __SUP_COUTT__ << "Comparing " << member.first << "-v" << member.second
8840  << " ... " << member.first << "-v"
8841  << diffMemberMap.at(member.first) << __E__;
8842 
8843  if(member.second == diffMemberMap.at(member.first))
8844  continue;
8845 
8846  diffReport << "\n\n<li>"
8847  << "Table <b>" << member.first << " v" << member.second
8848  << "</b> in " << groupName << "(" << groupKey << ")' ...vs... "
8849  << " <b>v" << diffMemberMap.at(member.first) << "</b> in "
8850  << diffGroupName << "(" << diffKey << ")':" << __E__;
8851 
8852  TableBase* table = cfgMgr->getTableByName(member.first);
8853 
8854  diffReport << "<ul>";
8855  std::map<std::string /* uid */, std::vector<std::string /* colName */>>
8856  modifiedRecords; //useful for tree diff view display
8857  if(!table->diffTwoVersions(member.second,
8858  diffMemberMap.at(member.first),
8859  &diffReport,
8860  &modifiedRecords))
8861  {
8862  //difference found!
8863  noDifference = false;
8864  ++tableDifferences;
8865  auto parentEl =
8866  xmlOut.addTextElementToData("TableWithDiff", member.first);
8867  for(auto& modifiedRecord : modifiedRecords)
8868  {
8869  auto recordParentEl = xmlOut.addTextElementToParent(
8870  "RecordWithDiff", modifiedRecord.first, parentEl);
8871  for(auto& modifiedColumn : modifiedRecord.second)
8872  xmlOut.addTextElementToParent(
8873  "ColNameWithDiff", modifiedColumn, recordParentEl);
8874  }
8875  }
8876  diffReport << "</ul></li>";
8877 
8878  } //end member table comparison loop
8879 
8880  for(auto& diffMember : diffMemberMap)
8881  {
8882  if(memberMap.find(diffMember.first) == memberMap.end())
8883  {
8884  if(diffKey.isInvalid())
8885  diffReport << "\n\n<li>"
8886  << "Active Group Table <b>" << diffMember.first << "-v"
8887  << diffMember.second << "</b> not found in '" << groupName
8888  << "(" << groupKey << ")'."
8889  << "</li>" << __E__;
8890  else
8891  diffReport << "\n\n<li>" << diffGroupName << "(" << diffKey
8892  << ") Table <b>" << diffMember.first << "-v"
8893  << diffMember.second << "</b> not found in '" << groupName
8894  << "(" << groupKey << ")'."
8895  << "</li>" << __E__;
8896 
8897  noDifference = false;
8898  ++tableDifferences;
8899  continue;
8900  }
8901  }
8902  diffReport << "\n</ol></INDENT>";
8903 
8904  if(diffKey.isInvalid())
8905  {
8906  if(noDifference)
8907  diffReport << "\n\nNo difference found between "
8908  << "<b>'" << groupName << "(" << groupKey
8909  << ")'</b> and active group "
8910  << "<b>'" << activeGroups.at(groupType).first << "("
8911  << activeGroups.at(groupType).second << ")'</b>." << __E__;
8912  else
8913  diffReport << "\n\n<b>" << tableDifferences
8914  << "</b> member table differences identified between "
8915  << "<b>'" << groupName << "(" << groupKey
8916  << ")'</b> and active group "
8917  << "<b>'" << activeGroups.at(groupType).first << "("
8918  << activeGroups.at(groupType).second << ")'</b>." << __E__;
8919  }
8920  else
8921  {
8922  if(noDifference)
8923  diffReport << "\n\nNo difference found between "
8924  << "<b>'" << groupName << "(" << groupKey
8925  << ")'</b> and group "
8926  << "<b>'" << diffGroupName << "(" << diffKey << ")'</b>."
8927  << __E__;
8928  else
8929  diffReport << "\n\n<b>" << tableDifferences
8930  << "</b> member table differences identified between "
8931  << "<b>'" << groupName << "(" << groupKey
8932  << ")'</b> and group "
8933  << "<b>'" << diffGroupName << "(" << diffKey << ")'</b>."
8934  << __E__;
8935  }
8936 
8937  xmlOut.addTextElementToData("NoDifference", noDifference ? "1" : "0");
8938  xmlOut.addTextElementToData("DiffReport", diffReport.str());
8939  }
8940  catch(const std::runtime_error& e)
8941  {
8942  __SUP_COUT_ERR__ << "Caught error while differencing group " << groupName << "("
8943  << groupKey << ") with group " << diffGroupName << "(" << diffKey
8944  << ")" << __E__ << e.what() << __E__;
8945  throw; //rethrow
8946  }
8947 } // end handleGroupDiff()
8948 
8949 //==============================================================================
8951 void ConfigurationGUISupervisor::handleTableDiff(HttpXmlDocument& xmlOut,
8952  ConfigurationManagerRW* cfgMgr,
8953  const std::string& tableName,
8954  const TableVersion& vA,
8955  const TableVersion& vB)
8956 {
8957  __SUP_COUT__ << "Differencing tableName " << tableName << " v" << vA << " with v"
8958  << vB << __E__;
8959 
8960  //first make sure tables are loaded
8961  TableBase* table = cfgMgr->getTableByName(tableName);
8962 
8963  try
8964  {
8965  // locally accumulate 'manageable' errors getting the version to avoid
8966  // reverting to mockup
8967  std::string localAccumulatedErrors = "";
8968  cfgMgr->getVersionedTableByName(tableName,
8969  vA,
8970  false /*looseColumnMatching*/,
8971  &localAccumulatedErrors,
8972  false /*getRawData*/);
8973 
8974  if(localAccumulatedErrors != "")
8975  xmlOut.addTextElementToData("Error", localAccumulatedErrors);
8976  }
8977  catch(std::runtime_error& e) // default to mock-up for fail-safe in GUI editor
8978  {
8979  __SUP_SS__ << "Failed to get table " << tableName << " version " << vA;
8980  ss << "\n\n...Here is why it failed:\n\n" << e.what() << __E__;
8981  __SUP_COUT_ERR__ << "\n" << ss.str();
8982 
8983  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
8984  }
8985  catch(...) // default to mock-up for fail-safe in GUI editor
8986  {
8987  __SUP_SS__ << "Failed to get table " << tableName << " version: " << vA << __E__;
8988  try
8989  {
8990  throw;
8991  } //one more try to printout extra info
8992  catch(const std::exception& e)
8993  {
8994  ss << "Exception message: " << e.what();
8995  }
8996  catch(...)
8997  {
8998  }
8999 
9000  __SUP_COUT_ERR__ << "\n" << ss.str();
9001  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
9002  }
9003  try
9004  {
9005  // locally accumulate 'manageable' errors getting the version to avoid
9006  // reverting to mockup
9007  std::string localAccumulatedErrors = "";
9008  cfgMgr->getVersionedTableByName(tableName,
9009  vB,
9010  false /*looseColumnMatching*/,
9011  &localAccumulatedErrors,
9012  false /*getRawData*/);
9013 
9014  if(localAccumulatedErrors != "")
9015  xmlOut.addTextElementToData("Error", localAccumulatedErrors);
9016  }
9017  catch(std::runtime_error& e) // default to mock-up for fail-safe in GUI editor
9018  {
9019  __SUP_SS__ << "Failed to get table " << tableName << " version " << vB;
9020  ss << "\n\n...Here is why it failed:\n\n" << e.what() << __E__;
9021  __SUP_COUT_ERR__ << "\n" << ss.str();
9022 
9023  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
9024  }
9025  catch(...) // default to mock-up for fail-safe in GUI editor
9026  {
9027  __SUP_SS__ << "Failed to get table " << tableName << " version: " << vB << __E__;
9028  try
9029  {
9030  throw;
9031  } //one more try to printout extra info
9032  catch(const std::exception& e)
9033  {
9034  ss << "Exception message: " << e.what();
9035  }
9036  catch(...)
9037  {
9038  }
9039 
9040  __SUP_COUT_ERR__ << "\n" << ss.str();
9041  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
9042  }
9043 
9044  bool noDifference = true;
9045  std::stringstream diffReport;
9046 
9047  diffReport << "This difference report is between table " << tableName << " v" << vA
9048  << " and v" << vB << "</b>." << __E__;
9049 
9050  diffReport << "<INDENT>";
9051  diffReport << "<ul>";
9052  std::map<std::string /* uid */, std::vector<std::string /* colName */>>
9053  modifiedRecords; //useful for tree diff view display
9054  if(!table->diffTwoVersions(vA, vB, &diffReport))
9055  noDifference = false; //difference found!
9056  diffReport << "</ul></INDENT>";
9057 
9058  xmlOut.addTextElementToData("NoDifference", noDifference ? "1" : "0");
9059  xmlOut.addTextElementToData("DiffReport", diffReport.str());
9060 } // end handleTableDiff()
9061 
9062 //==============================================================================
9065 void ConfigurationGUISupervisor::testXDAQContext()
9066 {
9067  if(0) //keep for debugging
9068  {
9069  __COUT_INFO__ << "Hello0!";
9070  ConfigurationManagerRW cfgMgrInst("ExampleUser");
9071  __COUT_INFO__ << "Hello1!";
9072  ConfigurationManagerRW* cfgMgr = &cfgMgrInst;
9073  __COUT_INFO__ << "Hello2!";
9074  cfgMgr->testXDAQContext();
9075  __COUT_INFO__ << "Hello3!";
9076  return;
9077  }
9078 
9079  try
9080  {
9081  __SUP_COUT_INFO__ << "Attempting test activation of the context group." << __E__;
9083  cfgMgr; // create instance to activate saved context and backbone groups (not config group)
9084  }
9085  catch(const std::runtime_error& e)
9086  {
9087  __SUP_COUT_WARN__
9088  << "The test activation of the context group failed. Ignoring error: \n"
9089  << e.what() << __E__;
9090  return;
9091  }
9092  catch(...)
9093  {
9094  __SUP_COUT_WARN__ << "The test activation of the context group failed. Ignoring."
9095  << __E__;
9096  return;
9097  }
9098 
9099  __SUP_COUT_INFO__ << "Completed test activation of the context group." << __E__;
9100  return;
9101 
9103  // below has been used for debugging.
9104 
9105  // behave like a user
9106  // start with top level xdaq context
9107  // then add and delete rows proof-of-concept
9108  // export xml xdaq table file
9109 
9112  // behave like a new user
9113  //
9114  // ConfigurationManagerRW cfgMgrInst("ExampleUser");
9115 
9116  // ConfigurationManagerRW* cfgMgr =& cfgMgrInst;
9117 
9118  // // std::map<std::string, TableVersion> groupMembers;
9119  // // groupMembers["DesktopIcon"] = TableVersion(2);
9120  // // cfgMgr->saveNewTableGroup("test",
9121  // // groupMembers, "test comment");
9122 
9123  // //
9124  // const std::map<std::string, TableInfo>& allTableInfo =
9125  // cfgMgr->getAllTableInfo(true /* refresh*/);
9126  // __SUP_COUT__ << "allTableInfo.size() = " << allTableInfo.size() << __E__;
9127  // for(auto& mapIt : allTableInfo)
9128  // {
9129  // __SUP_COUT__ << "Table Name: " << mapIt.first << __E__;
9130  // __SUP_COUT__ << "\t\tExisting Versions: " << mapIt.second.versions_.size()
9131  // <<
9132  // __E__;
9133 
9134  // //get version key for the current system table key
9135  // for (auto& v:mapIt.second.versions_)
9136  // {
9137  // __SUP_COUT__ << "\t\t" << v << __E__;
9138  // }
9139  // }
9140  // __SUP_COUTT__ << "Group Info end runtime=" << cfgMgr->runTimeSeconds() << __E__;
9141  // testXDAQContext just a test bed for navigating the new config tree
9142  // cfgMgr->testXDAQContext();
9143 
9146 } // end testXDAQContext()
static void setAndActivateARTDAQSystem(ConfigurationManagerRW *cfgMgr, const std::map< std::string, std::map< std::string, std::vector< std::string >>> &nodeTypeToObjectMap, const std::map< std::string, std::string > &subsystemObjectMap)
static const ARTDAQInfo & getARTDAQSystem(ConfigurationManagerRW *cfgMgr, std::map< std::string, std::map< std::string, std::vector< std::string >>> &nodeTypeToObjectMap, std::map< std::string, std::string > &subsystemObjectMap, std::vector< std::string > &artdaqSupervisoInfo)
static std::string postData(cgicc::Cgicc &cgi, const std::string &needle)
static std::string getData(cgicc::Cgicc &cgi, const std::string &needle)
virtual void forceSupervisorPropertyValues(void) override
override to force supervisor property values (and ignore user settings)
virtual void setSupervisorPropertyDefaults(void) override
ConfigurationGUISupervisor(xdaq::ApplicationStub *s)
static xdaq::Application * instantiate(xdaq::ApplicationStub *s)
TableVersion saveNewTable(const std::string &tableName, TableVersion temporaryVersion=TableVersion(), bool makeTemporary=false)
const GroupInfo & getGroupInfo(const std::string &groupName, bool attemptToReloadKeys=false)
const std::map< std::string, TableInfo > & getAllTableInfo(bool refresh=false, std::string *accumulatedWarnings=0, const std::string &errorFilterName="", bool getGroupKeys=false, bool getGroupInfo=false, bool initializeActiveGroups=false)
void loadTableGroup(const std::string &tableGroupName, const TableGroupKey &tableGroupKey, bool doActivate=false, std::map< std::string, TableVersion > *groupMembers=0, ProgressBar *progressBar=0, std::string *accumulateWarnings=0, std::string *groupComment=0, std::string *groupAuthor=0, std::string *groupCreateTime=0, bool doNotLoadMember=false, std::string *groupTypeString=0, std::map< std::string, std::string > *groupAliases=0, ConfigurationManager::LoadGroupType groupTypeToLoad=ConfigurationManager::LoadGroupType::ALL_TYPES, bool ignoreVersionTracking=false)
TableVersion copyViewToCurrentColumns(const std::string &tableName, TableVersion sourceVersion)
TableGroupKey saveNewTableGroup(const std::string &groupName, std::map< std::string, TableVersion > &groupMembers, const std::string &groupComment=TableViewColumnInfo::DATATYPE_COMMENT_DEFAULT, std::map< std::string, std::string > *groupAliases=0)
void activateTableGroup(const std::string &tableGroupName, TableGroupKey tableGroupKey, std::string *accumulatedTreeErrors=0, std::string *groupTypeString=0)
void clearCachedVersions(const std::string &tableName)
const std::string & getUsername(void) const
std::map< std::string, std::map< std::string, TableVersion > > getVersionAliases(void) const
void eraseTemporaryVersion(const std::string &tableName, TableVersion targetVersion=TableVersion())
static void loadTableGroupThread(ConfigurationManagerRW *cfgMgr, std::string groupName, ots::TableGroupKey groupKey, std::shared_ptr< ots::GroupInfo > theGroupInfo, std::shared_ptr< std::atomic< bool >> theThreadDone)
TableBase * getVersionedTableByName(const std::string &tableName, TableVersion version, bool looseColumnMatching=false, std::string *accumulatedErrors=0, bool getRawData=false)
static const unsigned int PROCESSOR_COUNT
void restoreActiveTableGroups(bool throwErrors=false, const std::string &pathToActiveGroupsFile="", ConfigurationManager::LoadGroupType onlyLoadIfBackboneOrContext=ConfigurationManager::LoadGroupType::ALL_TYPES, std::string *accumulatedWarnings=0)
std::map< std::string, std::pair< std::string, TableGroupKey > > getActiveTableGroups(void) const
std::map< std::string, TableVersion > getActiveVersions(void) const
void copyTableGroupFromCache(const ConfigurationManager &cacheConfigMgr, const std::map< std::string, TableVersion > &groupMembers, const std::string &configGroupName="", const TableGroupKey &tableGroupKey=TableGroupKey(TableGroupKey::INVALID), bool doActivate=false, bool ignoreVersionTracking=false)
ConfigurationTree getNode(const std::string &nodeString, bool doNotThrowOnBrokenUIDLinks=false) const
void init(std::string *accumulatedErrors=0, bool initForWriteAccess=false, std::string *accumulatedWarnings=0)
static const std::string & getTypeNameOfGroup(const std::map< std::string, TableVersion > &memberMap)
void destroyTableGroup(const std::string &theGroup="", bool onlyDeactivate=false)
std::vector< std::pair< std::string, ConfigurationTree > > getChildren(std::map< std::string, TableVersion > *memberMap=0, std::string *accumulatedTreeErrors=0) const
std::map< std::string, ConfigurationTree > getChildrenMap(std::map< std::string, TableVersion > *memberMap=0, std::string *accumulatedTreeErrors=0) const
static std::vector< std::map< std::string, std::string > > loadGroupHistory(const std::string &groupAction, const std::string &groupType, bool formatTime=false)
TableGroupKey loadConfigurationBackbone(void)
const TableBase * getTableByName(const std::string &configurationName) const
static std::pair< std::string, TableGroupKey > loadGroupNameAndKey(const std::string &fileName, std::string &returnedTimeString)
static void getConfigurationStatusXML(HttpXmlDocument &xmlOut, ConfigurationManagerRW *cfgMgr, const std::string &username)
static void handleGetTableGroupXML(HttpXmlDocument &xmlOut, ConfigurationManagerRW *cfgMgr, const std::string &groupName, TableGroupKey groupKey, bool ignoreWarnings=false, bool cacheOnly=false)
static TableVersion saveModifiedVersionXML(HttpXmlDocument &xmlOut, ConfigurationManagerRW *cfgMgr, const std::string &tableName, TableVersion originalVersion, bool makeTemporary, TableBase *config, TableVersion temporaryModifiedVersion, bool ignoreDuplicates=false, bool lookForEquivalent=false)
static void handleCreateTableXML(HttpXmlDocument &xmlOut, ConfigurationManagerRW *cfgMgr, const std::string &tableName, TableVersion version, bool makeTemporary, const std::string &data, const int &dataOffset, const std::string &author, const std::string &comment, bool sourceTableAsIs, bool lookForEquivalent)
static void handleCreateTableGroupXML(HttpXmlDocument &xmlOut, ConfigurationManagerRW *cfgMgr, const std::string &groupName, const std::string &configList, bool allowDuplicates=false, bool ignoreWarnings=false, const std::string &groupComment="", bool lookForEquivalent=false)
bool isUIDNode(void) const
const TableVersion & getTableVersion(void) const
bool isDisconnected(void) const
const std::string & getAuthor(void) const
const std::string & getComment(void) const
std::vector< std::string > getChildrenNames(bool byPriority=false, bool onlyStatusTrue=false) const
bool isEnabled(void) const
const std::string & getTableName(void) const
const unsigned int & getFieldRow(void) const
std::map< std::string, ConfigurationTree > getChildrenMap(std::map< std::string, std::string > filterMap=std::map< std::string, std::string >(), bool onlyStatusTrue=false) const
const std::string & getValueName(void) const
const std::string & getValueAsString(bool returnLinkTableValue=false) const
const std::string & getChildLinkIndex(void) const
const std::string & getDisconnectedTableName(void) const
std::vector< std::pair< std::string, ConfigurationTree > > getChildren(std::map< std::string, std::string > filterMap=std::map< std::string, std::string >(), bool byPriority=false, bool onlyStatusTrue=false) const
std::vector< std::string > getFixedChoices(void) const
bool isLinkNode(void) const
std::vector< ConfigurationTree::RecordField > getCommonFields(const std::vector< std::string > &recordList, const std::vector< std::string > &fieldAcceptList, const std::vector< std::string > &fieldRejectList, unsigned int depth=-1, bool autoSelectFilterFields=false) const
bool isValueNode(void) const
std::set< std::string > getUniqueValuesForField(const std::vector< std::string > &recordList, const std::string &fieldName, std::string *fieldGroupIDChildLinkIndex=0) const
const std::string & getValueType(void) const
bool isGroupLinkNode(void) const
const std::string & getFieldTableName(void) const
const std::string & getDisconnectedLinkID(void) const
const std::string & getParentTableName(void) const
bool isUIDLinkNode(void) const
const unsigned int & getFieldColumn(void) const
static std::string convertToCaps(std::string &str, bool isConfigName=false)
std::string toString(void) const
bool isInvalid(void) const
std::string str() const
static std::string getFullGroupString(const std::string &groupName, const TableGroupKey &key, const std::string &preKey="_v", const std::string &postKey="")
std::string toString(void) const
unsigned int version(void) const
static const std::string DATATYPE_NUMBER
static std::map< std::pair< std::string, std::string >, std::string > getAllDefaultsForGUI(void)
static const std::string & getDefaultDefaultValue(const std::string &type, const std::string &dataType)
static const std::string & getMaxDefaultValue(const std::string &dataType)
static const std::string & getMinDefaultValue(const std::string &dataType)
unsigned int findRow(unsigned int col, const T &value, unsigned int offsetRow=0, bool doNotThrow=false) const
bool isEntryInGroup(const unsigned int &row, const std::string &childLinkIndex, const std::string &groupNeedle) const
void setValueAsString(const std::string &value, unsigned int row, unsigned int col)
void deleteRow(int r)
unsigned int getColStatus(void) const
unsigned int getLinkGroupIDColumn(const std::string &childLinkIndex) const
bool removeRowFromGroup(const unsigned int &row, const unsigned int &col, const std::string &groupID, bool deleteRowIfNoGroupLeft=false)
bool getChildLink(const unsigned int &col, bool &isGroup, std::pair< unsigned int, unsigned int > &linkPair) const
void addRowToGroup(const unsigned int &row, const unsigned int &col, const std::string &groupID)
void init(void)
std::string getValueAsString(unsigned int row, unsigned int col, bool convertEnvironmentVariables=true) const
void getValue(T &value, unsigned int row, unsigned int col, bool doConvertEnvironmentVariables=true) const
unsigned int getColUID(void) const
bool setURIEncodedValue(const std::string &value, const unsigned int &row, const unsigned int &col, const std::string &author="")
unsigned int findCol(const std::string &name) const
void setValue(const T &value, unsigned int row, unsigned int col)
void setURIEncodedComment(const std::string &uriComment)
unsigned int addRow(const std::string &author="", unsigned char incrementUniqueData=false, const std::string &baseNameAutoUID="", unsigned int rowToAdd=(unsigned int) -1, std::string childLinkIndex="", std::string groupId="")
xercesc::DOMElement * addTextElementToParent(const std::string &childName, const std::string &childText, xercesc::DOMElement *parent)
void INIT_MF(const char *name)
static std::string getTimestampString(const std::string &linuxTimeInSeconds)
static void getVectorFromString(const std::string &inputString, std::vector< std::string > &listToReturn, const std::set< char > &delimiter={',', '|', '&'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'}, std::vector< char > *listOfDelimiters=0, bool decodeURIComponents=false)
static std::string exec(const char *cmd)
static std::string setToString(const std::set< T > &setToReturn, const std::string &delimeter=", ")
static std::string escapeString(std::string inString, bool allowWhiteSpace=false, bool forHtml=false)
static std::string vectorToString(const std::vector< T > &setToReturn, const std::string &delimeter=", ")
static std::string convertEnvironmentVariables(const std::string &data)
static bool isNumber(const std::string &stringToCheck)
static std::string mapToString(const std::map< std::string, T > &mapToReturn, const std::string &primaryDelimeter=", ", const std::string &secondaryDelimeter=": ")
static void getMapFromString(const std::string &inputString, std::map< S, T > &mapToReturn, const std::set< char > &pairPairDelimiter={',', '|', '&'}, const std::set< char > &nameValueDelimiter={'=', ':'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'})
static std::string decodeURIComponent(const std::string &data)