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