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