$treeview $search $mathjax $extrastylesheet
otsdaq_utilities
v2_03_00
$projectbrief
|
$projectbrief
|
$searchbox |
00001 #include "otsdaq-utilities/ConfigurationGUI/ConfigurationGUISupervisor.h" 00002 #include "otsdaq-core/CgiDataUtilities/CgiDataUtilities.h" 00003 #include "otsdaq-core/Macros/CoutMacros.h" 00004 #include "otsdaq-core/MessageFacility/MessageFacility.h" 00005 #include "otsdaq-core/TablePluginDataFormats/IterateTable.h" 00006 #include "otsdaq-core/XmlUtilities/HttpXmlDocument.h" 00007 00008 #if MESSAGEFACILITY_HEX_VERSION > 0x20100 00009 #include <boost/stacktrace.hpp> 00010 #endif 00011 00012 #include "otsdaq-core/TablePluginDataFormats/XDAQContextTable.h" //for context relaunch 00013 00014 #include <xdaq/NamespaceURI.h> 00015 00016 #include <iostream> 00017 #include <map> 00018 #include <utility> 00019 00020 using namespace ots; 00021 00022 #undef __MF_SUBJECT__ 00023 #define __MF_SUBJECT__ "CfgGUI" 00024 00025 #define TABLE_INFO_PATH std::string(getenv("TABLE_INFO_PATH")) + "/" 00026 #define TABLE_INFO_EXT std::string("Info.xml") 00027 00030 xdaq::Application* ConfigurationGUISupervisor::instantiate(xdaq::ApplicationStub* stub) 00031 { 00032 return new ConfigurationGUISupervisor(stub); 00033 } 00034 00035 //======================================================================================================================== 00036 // new user gets a config mgr assigned 00037 // user can fill any of the tables (fill from version or init empty), which becomes the 00038 // active view for that table 00039 ConfigurationGUISupervisor::ConfigurationGUISupervisor(xdaq::ApplicationStub* stub) 00040 : CoreSupervisorBase(stub) 00041 { 00042 __SUP_COUT__ << "Constructor started." << __E__; 00043 00044 INIT_MF("ConfigurationGUI"); 00045 00046 init(); 00047 __SUP_COUT__ << "Constructor complete." << __E__; 00048 } 00049 00050 //======================================================================================================================== 00051 ConfigurationGUISupervisor::~ConfigurationGUISupervisor(void) { destroy(); } 00052 00053 //======================================================================================================================== 00054 void ConfigurationGUISupervisor::init(void) 00055 { 00056 __SUP_COUT__ << "Initializing..." << __E__; 00057 00058 __SUP_COUT__ << "Activating saved context, which may prepare for normal mode..." 00059 << __E__; 00060 try 00061 { 00062 testXDAQContext(); // test context group activation 00063 } 00064 catch(...) 00065 { 00066 __COUT_WARN__ << "Failed test context group activation. otsdaq, in Normal mode, " 00067 "will not launch when this test fails. " 00068 << "Check the active context group from within Wizard Mode." 00069 << __E__; 00070 } 00071 } 00072 00073 //======================================================================================================================== 00074 void ConfigurationGUISupervisor::destroy(void) 00075 { 00076 // called by destructor 00077 for(std::map<std::string, ConfigurationManagerRW*>::iterator it = 00078 userConfigurationManagers_.begin(); 00079 it != userConfigurationManagers_.end(); 00080 ++it) 00081 { 00082 delete it->second; 00083 it->second = 0; 00084 } 00085 userConfigurationManagers_.clear(); 00086 00087 // NOTE: Moved to ConfigurationGUISupervisor [FIXME is this correct?? should we use 00088 // shared_ptr??] 00089 if(ConfigurationInterface::getInstance(true) != 0) 00090 delete ConfigurationInterface::getInstance(true); 00091 } 00092 00093 //======================================================================================================================== 00094 void ConfigurationGUISupervisor::defaultPage(xgi::Input* in, xgi::Output* out) 00095 { 00096 cgicc::Cgicc cgiIn(in); 00097 std::string configWindowName = 00098 CgiDataUtilities::getData(cgiIn, "configWindowName"); // from GET 00099 if(configWindowName == "tableEditor") 00100 *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame " 00101 "src='/WebPath/html/ConfigurationTableEditor.html?urn=" 00102 << this->getApplicationDescriptor()->getLocalId() << "'></frameset></html>"; 00103 if(configWindowName == "iterate") 00104 *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame " 00105 "src='/WebPath/html/Iterate.html?urn=" 00106 << this->getApplicationDescriptor()->getLocalId() << "'></frameset></html>"; 00107 else 00108 *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame " 00109 "src='/WebPath/html/ConfigurationGUI.html?urn=" 00110 << this->getApplicationDescriptor()->getLocalId() << "'></frameset></html>"; 00111 } 00112 00113 //======================================================================================================================== 00114 // When overriding, setup default property values here 00115 // called by CoreSupervisorBase constructor 00116 void ConfigurationGUISupervisor::setSupervisorPropertyDefaults(void) 00117 { 00118 CorePropertySupervisorBase::setSupervisorProperty( 00119 CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.UserPermissionsThreshold, 00120 "*=10 | deleteTreeNodeRecords=255 | saveTableInfo=255 | " 00121 "deleteTableInfo=255"); // experienced users to edit, admins to delete 00122 CorePropertySupervisorBase::setSupervisorProperty( 00123 CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.RequireUserLockRequestTypes, 00124 "*"); // all 00125 } 00126 00127 //======================================================================================================================== 00128 // forceSupervisorPropertyValues 00129 // override to force supervisor property values (and ignore user settings) 00130 void ConfigurationGUISupervisor::forceSupervisorPropertyValues() 00131 { 00132 CorePropertySupervisorBase::setSupervisorProperty( 00133 CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes, 00134 ""); // none 00135 CorePropertySupervisorBase::setSupervisorProperty( 00136 CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.CheckUserLockRequestTypes, 00137 "*"); // all 00138 } 00139 00140 //======================================================================================================================== 00141 void ConfigurationGUISupervisor::request(const std::string& requestType, 00142 cgicc::Cgicc& cgiIn, 00143 HttpXmlDocument& xmlOut, 00144 const WebUsers::RequestUserInfo& userInfo) try 00145 { 00146 // Commands 00147 00148 // gatewayLaunchOTS -- and other StartOTS commands 00149 00150 // saveTableInfo 00151 // deleteTableInfo 00152 // flattenToSystemAliases 00153 // versionTracking 00154 // getColumnTypes 00155 // getGroupAliases 00156 // setGroupAliasInActiveBackbone 00157 // setVersionAliasInActiveBackbone 00158 // setAliasOfGroupMembers 00159 // getVersionAliases 00160 // getTableGroups 00161 // getTableGroupType 00162 // getTables 00163 // getContextMemberNames 00164 // getBackboneMemberNames 00165 // getIterateMemberNames 00166 // getSpecificTableGroup 00167 // saveNewTableGroup 00168 // getSpecificTable 00169 // saveSpecificTable 00170 // clearTableTemporaryVersions 00171 // clearTableCachedVersions 00172 // 00173 // ---- associated with JavaScript Config API 00174 // getTreeView 00175 // getTreeNodeCommonFields 00176 // getUniqueFieldValuesForRecords 00177 // getTreeNodeFieldValues 00178 // setTreeNodeFieldValues 00179 // addTreeNodeRecords 00180 // deleteTreeNodeRecords 00181 // ---- end associated with JavaScript Config API 00182 // 00183 // activateTableGroup 00184 // getActiveTableGroups 00185 // copyViewToCurrentColumns 00186 // saveTreeNodeEdit 00187 // getAffectedActiveGroups 00188 // getLinkToChoices 00189 // getLastTableGroups 00190 // mergeGroups 00191 // 00192 // ---- associated with JavaScript Iterate App 00193 // savePlanCommandSequence 00194 // ---- end associated with JavaScript Iterate App 00195 00196 // acquire user's configuration manager based on username& activeSessionIndex 00197 std::string refresh = CgiDataUtilities::getData(cgiIn, "refresh"); // from GET 00198 //__SUP_COUT__ << "refresh: " << refresh << __E__; 00199 00200 // refresh to reload from info files and db (maintains temporary views!) 00201 ConfigurationManagerRW* cfgMgr = refreshUserSession( 00202 userInfo.username_, userInfo.activeUserSessionIndex_, (refresh == "1")); 00203 00204 if(requestType == "saveTableInfo") 00205 { 00206 std::string tableName = 00207 CgiDataUtilities::getData(cgiIn, "tableName"); // from GET 00208 std::string columnCSV = 00209 CgiDataUtilities::postData(cgiIn, "columnCSV"); // from POST 00210 std::string allowOverwrite = 00211 CgiDataUtilities::getData(cgiIn, "allowOverwrite"); // from GET 00212 std::string tableDescription = 00213 CgiDataUtilities::postData(cgiIn, "tableDescription"); // from POST 00214 std::string columnChoicesCSV = 00215 CgiDataUtilities::postData(cgiIn, "columnChoicesCSV"); // from POST 00216 00217 // columnCSV = CgiDataUtilities::decodeURIComponent(columnCSV); 00218 // tableDescription = CgiDataUtilities::decodeURIComponent(tableDescription); 00219 00220 __SUP_COUT__ << "tableName: " << tableName << __E__; 00221 __SUP_COUT__ << "columnCSV: " << columnCSV << __E__; 00222 __SUP_COUT__ << "tableDescription: " << tableDescription << __E__; 00223 __SUP_COUT__ << "columnChoicesCSV: " << columnChoicesCSV << __E__; 00224 __SUP_COUT__ << "allowOverwrite: " << allowOverwrite << __E__; 00225 00226 if(!allSupervisorInfo_.isWizardMode()) 00227 { 00228 __SUP_SS__ << "Improper permissions for saving table info." << __E__; 00229 xmlOut.addTextElementToData("Error", ss.str()); 00230 } 00231 else 00232 handleSaveTableInfoXML(xmlOut, 00233 cfgMgr, 00234 tableName, 00235 columnCSV, 00236 tableDescription, 00237 columnChoicesCSV, 00238 allowOverwrite == "1"); 00239 } 00240 else if(requestType == "deleteTableInfo") 00241 { 00242 std::string tableName = 00243 CgiDataUtilities::getData(cgiIn, "tableName"); // from GET 00244 __SUP_COUT__ << "tableName: " << tableName << __E__; 00245 handleDeleteTableInfoXML(xmlOut, cfgMgr, tableName); 00246 } 00247 else if(requestType == "gatewayLaunchOTS" || requestType == "gatewayLaunchWiz" || 00248 requestType == "flattenToSystemAliases") 00249 { 00250 // NOTE: similar to Supervisor version but does not keep active sessions 00251 __SUP_COUT_WARN__ << requestType << " command received! " << __E__; 00252 __MOUT_WARN__ << requestType << " command received! " << __E__; 00253 00254 // now launch 00255 __SUP_COUT_INFO__ << "Launching... " << __E__; 00256 00257 __SUP_COUT__ << "Extracting target context hostnames... " << __E__; 00258 std::vector<std::string> hostnames; 00259 try 00260 { 00261 cfgMgr->init(); // completely reset to re-align with any changes 00262 00263 const XDAQContextTable* contextTable = 00264 cfgMgr->__GET_CONFIG__(XDAQContextTable); 00265 00266 auto contexts = contextTable->getContexts(); 00267 unsigned int i, j; 00268 for(const auto& context : contexts) 00269 { 00270 if(!context.status_) 00271 continue; 00272 00273 // find last slash 00274 j = 0; // default to whole string 00275 for(i = 0; i < context.address_.size(); ++i) 00276 if(context.address_[i] == '/') 00277 j = i + 1; 00278 hostnames.push_back(context.address_.substr(j)); 00279 __SUP_COUT__ << "hostname = " << hostnames.back() << __E__; 00280 } 00281 } 00282 catch(...) 00283 { 00284 __SUP_SS__ << "\nTransition to Configuring interrupted! " 00285 << "The Configuration Manager could not be initialized." << __E__; 00286 00287 __SUP_COUT_ERR__ << "\n" << ss.str(); 00288 return; 00289 } 00290 00291 for(const auto& hostname : hostnames) 00292 { 00293 std::string fn = (std::string(getenv("SERVICE_DATA_PATH")) + 00294 "/StartOTS_action_" + hostname + ".cmd"); 00295 FILE* fp = fopen(fn.c_str(), "w"); 00296 if(fp) 00297 { 00298 if(requestType == "gatewayLaunchOTS") 00299 fprintf(fp, "LAUNCH_OTS"); 00300 else if(requestType == "gatewayLaunchWiz") 00301 fprintf(fp, "LAUNCH_WIZ"); 00302 else if(requestType == "flattenToSystemAliases") 00303 { 00304 fprintf(fp, "FLATTEN_TO_SYSTEM_ALIASES"); 00305 fclose(fp); 00306 break; // only do at one host 00307 } 00308 00309 fclose(fp); 00310 } 00311 else 00312 __SUP_COUT_ERR__ << "Unable to open command file: " << fn << __E__; 00313 } 00314 } 00315 // else if(requestType == "launchOTS") 00316 // { 00317 // __SUP_COUT_WARN__ << "launchOTS command received! Launching... " << __E__; 00318 // 00319 // FILE* fp = fopen((std::string(getenv("SERVICE_DATA_PATH")) + 00320 // "/StartOTS_action.cmd").c_str(),"w"); 00321 // if(fp) 00322 // { 00323 // fprintf(fp,"LAUNCH_OTS"); 00324 // fclose(fp); 00325 // } 00326 // else 00327 // __SUP_COUT_ERR__ << "Unable to open command file: " << 00328 //(std::string(getenv("SERVICE_DATA_PATH")) + 00329 // "/StartOTS_action.cmd") << __E__; 00330 // } 00331 // else if(requestType == "launchWiz") 00332 // { 00333 // __SUP_COUT_WARN__ << "launchWiz command received! Launching... " << __E__; 00334 // 00335 // FILE* fp = fopen((std::string(getenv("SERVICE_DATA_PATH")) + 00336 // "/StartOTS_action.cmd").c_str(),"w"); 00337 // if(fp) 00338 // { 00339 // fprintf(fp,"LAUNCH_WIZ"); 00340 // fclose(fp); 00341 // } 00342 // else 00343 // __SUP_COUT_ERR__ << "Unable to open command file: " << 00344 //(std::string(getenv("SERVICE_DATA_PATH")) + 00345 // "/StartOTS_action.cmd") << __E__; 00346 // } 00347 // else if(requestType == "flattenToSystemAliases") 00348 // { 00349 // __SUP_COUT_WARN__ << "flattenToSystemAliases command received! Launching... " 00350 //<< 00351 //__E__; 00352 // 00353 // FILE* fp = fopen((std::string(getenv("SERVICE_DATA_PATH")) + 00354 // "/StartOTS_action.cmd").c_str(),"w"); 00355 // if(fp) 00356 // { 00357 // fprintf(fp,"FLATTEN_TO_SYSTEM_ALIASES"); 00358 // fclose(fp); 00359 // } 00360 // else 00361 // __SUP_COUT_ERR__ << "Unable to open command file: " << 00362 //(std::string(getenv("SERVICE_DATA_PATH")) + 00363 // "/StartOTS_action.cmd") << __E__; 00364 // } 00365 else if(requestType == "versionTracking") 00366 { 00367 std::string type = CgiDataUtilities::getData(cgiIn, "Type"); // from GET 00368 __SUP_COUT__ << "type: " << type << __E__; 00369 00370 if(type == "Get") 00371 xmlOut.addTextElementToData( 00372 "versionTrackingStatus", 00373 ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF"); 00374 else if(type == "ON") 00375 { 00376 ConfigurationInterface::setVersionTrackingEnabled(true); 00377 xmlOut.addTextElementToData( 00378 "versionTrackingStatus", 00379 ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF"); 00380 } 00381 else if(type == "OFF") 00382 { 00383 ConfigurationInterface::setVersionTrackingEnabled(false); 00384 xmlOut.addTextElementToData( 00385 "versionTrackingStatus", 00386 ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF"); 00387 } 00388 } 00389 else if(requestType == "getColumnTypes") 00390 { 00391 // return the possible column types and their defaults 00392 std::vector<std::string> allTypes = TableViewColumnInfo::getAllTypesForGUI(); 00393 std::vector<std::string> allDataTypes = 00394 TableViewColumnInfo::getAllDataTypesForGUI(); 00395 std::map<std::pair<std::string, std::string>, std::string> allDefaults = 00396 TableViewColumnInfo::getAllDefaultsForGUI(); 00397 00398 for(const auto& type : allTypes) 00399 xmlOut.addTextElementToData("columnTypeForGUI", type); 00400 for(const auto& dataType : allDataTypes) 00401 xmlOut.addTextElementToData("columnDataTypeForGUI", dataType); 00402 00403 for(const auto& colDefault : allDefaults) 00404 { 00405 xmlOut.addTextElementToData("columnDefaultDataType", colDefault.first.first); 00406 xmlOut.addTextElementToData("columnDefaultTypeFilter", 00407 colDefault.first.second); 00408 xmlOut.addTextElementToData("columnDefaultValue", colDefault.second); 00409 } 00410 } 00411 else if(requestType == "getGroupAliases") 00412 { 00413 // Since this is called from setting up System View in the config GUI 00414 // give option for reloading "persistent" active configurations 00415 bool reloadActive = 00416 1 == CgiDataUtilities::getDataAsInt(cgiIn, "reloadActiveGroups"); // from GET 00417 00418 __SUP_COUT__ << "reloadActive: " << reloadActive << __E__; 00419 bool wasError = false; 00420 if(reloadActive) 00421 { 00422 try 00423 { 00424 cfgMgr->clearAllCachedVersions(); 00425 cfgMgr->restoreActiveTableGroups(true); 00426 } 00427 catch(std::runtime_error& e) 00428 { 00429 __SUP_SS__ << ("Error loading active groups!\n\n" + std::string(e.what())) 00430 << __E__; 00431 __SUP_COUT_ERR__ << "\n" << ss.str(); 00432 xmlOut.addTextElementToData("Error", ss.str()); 00433 wasError = true; 00434 } 00435 catch(...) 00436 { 00437 __SUP_SS__ << ("Error loading active groups!\n\n") << __E__; 00438 __SUP_COUT_ERR__ << "\n" << ss.str(); 00439 xmlOut.addTextElementToData("Error", ss.str()); 00440 wasError = true; 00441 } 00442 } 00443 00444 handleGroupAliasesXML(xmlOut, cfgMgr); 00445 } 00446 else if(requestType == "setGroupAliasInActiveBackbone") 00447 { 00448 std::string groupAlias = 00449 CgiDataUtilities::getData(cgiIn, "groupAlias"); // from GET 00450 std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName"); // from 00451 // GET 00452 std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey"); // from GET 00453 00454 __SUP_COUT__ << "groupAlias: " << groupAlias << __E__; 00455 __SUP_COUT__ << "groupName: " << groupName << __E__; 00456 __SUP_COUT__ << "groupKey: " << groupKey << __E__; 00457 00458 handleSetGroupAliasInBackboneXML(xmlOut, 00459 cfgMgr, 00460 groupAlias, 00461 groupName, 00462 TableGroupKey(groupKey), 00463 userInfo.username_); 00464 } 00465 else if(requestType == "setVersionAliasInActiveBackbone") 00466 { 00467 std::string versionAlias = 00468 CgiDataUtilities::getData(cgiIn, "versionAlias"); // from GET 00469 std::string tableName = 00470 CgiDataUtilities::getData(cgiIn, "tableName"); // from GET 00471 std::string version = CgiDataUtilities::getData(cgiIn, "version"); // from GET 00472 00473 __SUP_COUT__ << "versionAlias: " << versionAlias << __E__; 00474 __SUP_COUT__ << "tableName: " << tableName << __E__; 00475 __SUP_COUT__ << "version: " << version << __E__; 00476 00477 handleSetVersionAliasInBackboneXML(xmlOut, 00478 cfgMgr, 00479 versionAlias, 00480 tableName, 00481 TableVersion(version), 00482 userInfo.username_); 00483 } 00484 else if(requestType == "setAliasOfGroupMembers") 00485 { 00486 std::string versionAlias = 00487 CgiDataUtilities::getData(cgiIn, "versionAlias"); // from GET 00488 std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName"); // from 00489 // GET 00490 std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey"); // from GET 00491 00492 __SUP_COUT__ << "versionAlias: " << versionAlias << __E__; 00493 __SUP_COUT__ << "groupName: " << groupName << __E__; 00494 __SUP_COUT__ << "groupKey: " << groupKey << __E__; 00495 00496 handleAliasGroupMembersInBackboneXML(xmlOut, 00497 cfgMgr, 00498 versionAlias, 00499 groupName, 00500 TableGroupKey(groupKey), 00501 userInfo.username_); 00502 } 00503 else if(requestType == "getVersionAliases") 00504 { 00505 handleVersionAliasesXML(xmlOut, cfgMgr); 00506 } 00507 else if(requestType == "getTableGroups") 00508 { 00509 bool doNotReturnMembers = 00510 CgiDataUtilities::getDataAsInt(cgiIn, "doNotReturnMembers") == 1 00511 ? true 00512 : false; // from GET 00513 00514 __SUP_COUT__ << "doNotReturnMembers: " << doNotReturnMembers << __E__; 00515 handleTableGroupsXML(xmlOut, cfgMgr, !doNotReturnMembers); 00516 } 00517 else if(requestType == "getTableGroupType") 00518 { 00519 std::string tableList = 00520 CgiDataUtilities::postData(cgiIn, "tableList"); // from POST 00521 __SUP_COUT__ << "tableList: " << tableList << __E__; 00522 00523 handleGetTableGroupTypeXML(xmlOut, cfgMgr, tableList); 00524 } 00525 else if(requestType == "getTables") 00526 { 00527 std::string allowIllegalColumns = 00528 CgiDataUtilities::getData(cgiIn, "allowIllegalColumns"); // from GET 00529 00530 __SUP_COUT__ << "allowIllegalColumns: " << allowIllegalColumns << __E__; 00531 00532 handleTablesXML(xmlOut, cfgMgr, allowIllegalColumns == "1"); 00533 } 00534 else if(requestType == "getContextMemberNames") 00535 { 00536 std::set<std::string> members = cfgMgr->getContextMemberNames(); 00537 00538 for(auto& member : members) 00539 xmlOut.addTextElementToData("ContextMember", member); 00540 } 00541 else if(requestType == "getBackboneMemberNames") 00542 { 00543 std::set<std::string> members = cfgMgr->getBackboneMemberNames(); 00544 00545 for(auto& member : members) 00546 xmlOut.addTextElementToData("BackboneMember", member); 00547 } 00548 else if(requestType == "getIterateMemberNames") 00549 { 00550 std::set<std::string> members = cfgMgr->getIterateMemberNames(); 00551 00552 for(auto& member : members) 00553 xmlOut.addTextElementToData("IterateMember", member); 00554 } 00555 else if(requestType == "getSpecificTableGroup") 00556 { 00557 std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName"); // from 00558 // GET 00559 std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey"); // from GET 00560 00561 __SUP_COUT__ << "groupName: " << groupName << __E__; 00562 __SUP_COUT__ << "groupKey: " << groupKey << __E__; 00563 00564 handleGetTableGroupXML(xmlOut, cfgMgr, groupName, TableGroupKey(groupKey)); 00565 } 00566 else if(requestType == "saveNewTableGroup") 00567 { 00568 std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName"); // from 00569 // GET 00570 bool ignoreWarnings = 00571 CgiDataUtilities::getDataAsInt(cgiIn, "ignoreWarnings"); // from GET 00572 bool allowDuplicates = 00573 CgiDataUtilities::getDataAsInt(cgiIn, "allowDuplicates"); // from GET 00574 bool lookForEquivalent = 00575 CgiDataUtilities::getDataAsInt(cgiIn, "lookForEquivalent"); // from GET 00576 std::string tableList = 00577 CgiDataUtilities::postData(cgiIn, "tableList"); // from POST 00578 std::string comment = 00579 CgiDataUtilities::getData(cgiIn, "groupComment"); // from GET 00580 00581 __SUP_COUT__ << "saveNewTableGroup: " << groupName << __E__; 00582 __SUP_COUT__ << "tableList: " << tableList << __E__; 00583 __SUP_COUT__ << "ignoreWarnings: " << ignoreWarnings << __E__; 00584 __SUP_COUT__ << "allowDuplicates: " << allowDuplicates << __E__; 00585 __SUP_COUT__ << "lookForEquivalent: " << lookForEquivalent << __E__; 00586 __SUP_COUT__ << "comment: " << comment << __E__; 00587 00588 handleCreateTableGroupXML(xmlOut, 00589 cfgMgr, 00590 groupName, 00591 tableList, 00592 allowDuplicates, 00593 ignoreWarnings, 00594 comment, 00595 lookForEquivalent); 00596 } 00597 else if(requestType == "getSpecificTable") 00598 { 00599 std::string tableName = 00600 CgiDataUtilities::getData(cgiIn, "tableName"); // from GET 00601 std::string versionStr = CgiDataUtilities::getData(cgiIn, "version"); // from GET 00602 int dataOffset = CgiDataUtilities::getDataAsInt(cgiIn, "dataOffset"); // from GET 00603 int chunkSize = CgiDataUtilities::getDataAsInt(cgiIn, "chunkSize"); // from GET 00604 00605 std::string allowIllegalColumns = 00606 CgiDataUtilities::getData(cgiIn, "allowIllegalColumns"); // from GET 00607 __SUP_COUT__ << "allowIllegalColumns: " << (allowIllegalColumns == "1") << __E__; 00608 00609 __SUP_COUT__ << "getSpecificTable: " << tableName << " versionStr: " << versionStr 00610 << " chunkSize: " << chunkSize << " dataOffset: " << dataOffset 00611 << __E__; 00612 00613 TableVersion version; 00614 const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo(); 00615 std::string versionAlias; 00616 00617 if(allTableInfo.find(tableName) != allTableInfo.end()) 00618 { 00619 if(versionStr == "" && // take latest version if no version specified 00620 allTableInfo.at(tableName).versions_.size()) 00621 version = *(allTableInfo.at(tableName).versions_.rbegin()); 00622 else if(versionStr.find(ConfigurationManager::ALIAS_VERSION_PREAMBLE) == 0) 00623 { 00624 // convert alias to version 00625 std::map<std::string /*table*/, 00626 std::map<std::string /*alias*/, TableVersion>> 00627 versionAliases = cfgMgr->getVersionAliases(); 00628 00629 versionAlias = versionStr.substr( 00630 ConfigurationManager::ALIAS_VERSION_PREAMBLE.size()); 00631 // if(versionAlias == 00632 // ConfigurationManager::SCRATCH_VERSION_ALIAS) 00634 // { 00635 // version = TableVersion::SCRATCH; 00636 // __SUP_COUT__ << "version alias translated to: " << version 00637 //<< 00638 //__E__; 00639 // } 00640 // else 00641 if(versionAliases.find(tableName) != versionAliases.end() && 00642 versionAliases[tableName].find(versionStr.substr( 00643 ConfigurationManager::ALIAS_VERSION_PREAMBLE.size())) != 00644 versionAliases[tableName].end()) 00645 { 00646 version = versionAliases[tableName][versionStr.substr( 00647 ConfigurationManager::ALIAS_VERSION_PREAMBLE.size())]; 00648 __SUP_COUT__ << "version alias translated to: " << version << __E__; 00649 } 00650 else 00651 __SUP_COUT_WARN__ 00652 << "version alias '" 00653 << versionStr.substr( 00654 ConfigurationManager::ALIAS_VERSION_PREAMBLE.size()) 00655 << "'was not found in active version aliases!" << __E__; 00656 } 00657 else // else take specified version 00658 version = atoi(versionStr.c_str()); 00659 } 00660 00661 __SUP_COUT__ << "version: " << version << __E__; 00662 00663 handleGetTableXML(xmlOut, 00664 cfgMgr, 00665 tableName, 00666 TableVersion(version), 00667 (allowIllegalColumns == "1")); 00668 // append author column default value 00669 xmlOut.addTextElementToData("DefaultRowValue", userInfo.username_); 00670 } 00671 else if(requestType == "saveSpecificTable") 00672 { 00673 std::string tableName = 00674 CgiDataUtilities::getData(cgiIn, "tableName"); // from GET 00675 int version = CgiDataUtilities::getDataAsInt(cgiIn, "version"); // from GET 00676 int dataOffset = CgiDataUtilities::getDataAsInt(cgiIn, "dataOffset"); // from GET 00677 bool sourceTableAsIs = 00678 CgiDataUtilities::getDataAsInt(cgiIn, "sourceTableAsIs"); // from GET 00679 bool lookForEquivalent = 00680 CgiDataUtilities::getDataAsInt(cgiIn, "lookForEquivalent"); // from GET 00681 int temporary = CgiDataUtilities::getDataAsInt(cgiIn, "temporary"); // from GET 00682 std::string comment = 00683 CgiDataUtilities::getData(cgiIn, "tableComment"); // from GET 00684 00685 std::string data = CgiDataUtilities::postData(cgiIn, "data"); // from POST 00686 // data format: commas and semi-colons indicate new row 00687 // r0c0,r0c1,...,r0cN,;r1c0,... 00688 00689 __SUP_COUT__ << "tableName: " << tableName << " version: " << version 00690 << " temporary: " << temporary << " dataOffset: " << dataOffset 00691 << __E__; 00692 __SUP_COUT__ << "comment: " << comment << __E__; 00693 __SUP_COUT__ << "data: " << data << __E__; 00694 __SUP_COUT__ << "sourceTableAsIs: " << sourceTableAsIs << __E__; 00695 __SUP_COUT__ << "lookForEquivalent: " << lookForEquivalent << __E__; 00696 00697 handleCreateTableXML(xmlOut, 00698 cfgMgr, 00699 tableName, 00700 TableVersion(version), 00701 temporary, 00702 data, 00703 dataOffset, 00704 userInfo.username_, 00705 comment, 00706 sourceTableAsIs, 00707 lookForEquivalent); 00708 } 00709 else if(requestType == "clearTableTemporaryVersions") 00710 { 00711 std::string tableName = 00712 CgiDataUtilities::getData(cgiIn, "tableName"); // from GET 00713 __SUP_COUT__ << "tableName: " << tableName << __E__; 00714 00715 try 00716 { 00717 cfgMgr->eraseTemporaryVersion(tableName); 00718 } 00719 catch(std::runtime_error& e) 00720 { 00721 __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__; 00722 xmlOut.addTextElementToData( 00723 "Error", "Error clearing temporary views!\n " + std::string(e.what())); 00724 } 00725 catch(...) 00726 { 00727 __SUP_COUT__ << "Error detected!\n\n " << __E__; 00728 xmlOut.addTextElementToData("Error", "Error clearing temporary views! "); 00729 } 00730 } 00731 else if(requestType == "clearTableCachedVersions") 00732 { 00733 std::string tableName = 00734 CgiDataUtilities::getData(cgiIn, "tableName"); // from GET 00735 __SUP_COUT__ << "tableName: " << tableName << __E__; 00736 00737 try 00738 { 00739 cfgMgr->clearCachedVersions(tableName); 00740 } 00741 catch(std::runtime_error& e) 00742 { 00743 __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__; 00744 xmlOut.addTextElementToData( 00745 "Error", "Error clearing cached views!\n " + std::string(e.what())); 00746 } 00747 catch(...) 00748 { 00749 __SUP_COUT__ << "Error detected!\n\n " << __E__; 00750 xmlOut.addTextElementToData("Error", "Error clearing cached views! "); 00751 } 00752 } 00753 else if(requestType == "getTreeView") 00754 { 00755 std::string configGroup = CgiDataUtilities::getData(cgiIn, "configGroup"); 00756 std::string configGroupKey = CgiDataUtilities::getData(cgiIn, "configGroupKey"); 00757 std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath"); 00758 std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables"); 00759 std::string filterList = CgiDataUtilities::postData(cgiIn, "filterList"); 00760 int depth = CgiDataUtilities::getDataAsInt(cgiIn, "depth"); 00761 bool hideStatusFalse = CgiDataUtilities::getDataAsInt(cgiIn, "hideStatusFalse"); 00762 00763 __SUP_COUT__ << "configGroup: " << configGroup << __E__; 00764 __SUP_COUT__ << "configGroupKey: " << configGroupKey << __E__; 00765 __SUP_COUT__ << "startPath: " << startPath << __E__; 00766 __SUP_COUT__ << "depth: " << depth << __E__; 00767 __SUP_COUT__ << "hideStatusFalse: " << hideStatusFalse << __E__; 00768 __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__; 00769 __SUP_COUT__ << "filterList: " << filterList << __E__; 00770 00771 handleFillTreeViewXML(xmlOut, 00772 cfgMgr, 00773 configGroup, 00774 TableGroupKey(configGroupKey), 00775 startPath, 00776 depth, 00777 hideStatusFalse, 00778 modifiedTables, 00779 filterList); 00780 } 00781 else if(requestType == "getTreeNodeCommonFields") 00782 { 00783 std::string configGroup = CgiDataUtilities::getData(cgiIn, "configGroup"); 00784 std::string configGroupKey = CgiDataUtilities::getData(cgiIn, "configGroupKey"); 00785 std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath"); 00786 std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables"); 00787 std::string fieldList = CgiDataUtilities::postData(cgiIn, "fieldList"); 00788 std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList"); 00789 int depth = CgiDataUtilities::getDataAsInt(cgiIn, "depth"); 00790 00791 __SUP_COUT__ << "configGroup: " << configGroup << __E__; 00792 __SUP_COUT__ << "configGroupKey: " << configGroupKey << __E__; 00793 __SUP_COUT__ << "startPath: " << startPath << __E__; 00794 __SUP_COUT__ << "depth: " << depth << __E__; 00795 __SUP_COUT__ << "fieldList: " << fieldList << __E__; 00796 __SUP_COUT__ << "recordList: " << recordList << __E__; 00797 __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__; 00798 00799 handleFillTreeNodeCommonFieldsXML(xmlOut, 00800 cfgMgr, 00801 configGroup, 00802 TableGroupKey(configGroupKey), 00803 startPath, 00804 depth, 00805 modifiedTables, 00806 recordList, 00807 fieldList); 00808 } 00809 else if(requestType == "getUniqueFieldValuesForRecords") 00810 { 00811 std::string configGroup = CgiDataUtilities::getData(cgiIn, "configGroup"); 00812 std::string configGroupKey = CgiDataUtilities::getData(cgiIn, "configGroupKey"); 00813 std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath"); 00814 std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables"); 00815 std::string fieldList = CgiDataUtilities::postData(cgiIn, "fieldList"); 00816 std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList"); 00817 00818 __SUP_COUT__ << "configGroup: " << configGroup << __E__; 00819 __SUP_COUT__ << "configGroupKey: " << configGroupKey << __E__; 00820 __SUP_COUT__ << "startPath: " << startPath << __E__; 00821 __SUP_COUT__ << "fieldList: " << fieldList << __E__; 00822 __SUP_COUT__ << "recordList: " << recordList << __E__; 00823 __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__; 00824 00825 handleFillUniqueFieldValuesForRecordsXML(xmlOut, 00826 cfgMgr, 00827 configGroup, 00828 TableGroupKey(configGroupKey), 00829 startPath, 00830 modifiedTables, 00831 recordList, 00832 fieldList); 00833 } 00834 else if(requestType == "getTreeNodeFieldValues") 00835 { 00836 std::string configGroup = CgiDataUtilities::getData(cgiIn, "configGroup"); 00837 std::string configGroupKey = CgiDataUtilities::getData(cgiIn, "configGroupKey"); 00838 std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath"); 00839 std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables"); 00840 std::string fieldList = CgiDataUtilities::postData(cgiIn, "fieldList"); 00841 std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList"); 00842 00843 __SUP_COUT__ << "configGroup: " << configGroup << __E__; 00844 __SUP_COUT__ << "configGroupKey: " << configGroupKey << __E__; 00845 __SUP_COUT__ << "startPath: " << startPath << __E__; 00846 __SUP_COUT__ << "fieldList: " << fieldList << __E__; 00847 __SUP_COUT__ << "recordList: " << recordList << __E__; 00848 __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__; 00849 00850 handleFillGetTreeNodeFieldValuesXML(xmlOut, 00851 cfgMgr, 00852 configGroup, 00853 TableGroupKey(configGroupKey), 00854 startPath, 00855 modifiedTables, 00856 recordList, 00857 fieldList); 00858 } 00859 else if(requestType == "setTreeNodeFieldValues") 00860 { 00861 std::string configGroup = CgiDataUtilities::getData(cgiIn, "configGroup"); 00862 std::string configGroupKey = CgiDataUtilities::getData(cgiIn, "configGroupKey"); 00863 std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath"); 00864 std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables"); 00865 std::string fieldList = CgiDataUtilities::postData(cgiIn, "fieldList"); 00866 std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList"); 00867 std::string valueList = CgiDataUtilities::postData(cgiIn, "valueList"); 00868 00869 __SUP_COUT__ << "configGroup: " << configGroup << __E__; 00870 __SUP_COUT__ << "configGroupKey: " << configGroupKey << __E__; 00871 __SUP_COUT__ << "startPath: " << startPath << __E__; 00872 __SUP_COUT__ << "fieldList: " << fieldList << __E__; 00873 __SUP_COUT__ << "valueList: " << valueList << __E__; 00874 __SUP_COUT__ << "recordList: " << recordList << __E__; 00875 __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__; 00876 00877 handleFillSetTreeNodeFieldValuesXML(xmlOut, 00878 cfgMgr, 00879 configGroup, 00880 TableGroupKey(configGroupKey), 00881 startPath, 00882 modifiedTables, 00883 recordList, 00884 fieldList, 00885 valueList, 00886 userInfo.username_); 00887 } 00888 else if(requestType == "addTreeNodeRecords") 00889 { 00890 std::string configGroup = CgiDataUtilities::getData(cgiIn, "configGroup"); 00891 std::string configGroupKey = CgiDataUtilities::getData(cgiIn, "configGroupKey"); 00892 std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath"); 00893 std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables"); 00894 std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList"); 00895 00896 __SUP_COUT__ << "configGroup: " << configGroup << __E__; 00897 __SUP_COUT__ << "configGroupKey: " << configGroupKey << __E__; 00898 __SUP_COUT__ << "startPath: " << startPath << __E__; 00899 __SUP_COUT__ << "recordList: " << recordList << __E__; 00900 __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__; 00901 00902 handleFillCreateTreeNodeRecordsXML(xmlOut, 00903 cfgMgr, 00904 configGroup, 00905 TableGroupKey(configGroupKey), 00906 startPath, 00907 modifiedTables, 00908 recordList, 00909 userInfo.username_); 00910 } 00911 else if(requestType == "deleteTreeNodeRecords") 00912 { 00913 std::string configGroup = CgiDataUtilities::getData(cgiIn, "configGroup"); 00914 std::string configGroupKey = CgiDataUtilities::getData(cgiIn, "configGroupKey"); 00915 std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath"); 00916 std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables"); 00917 std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList"); 00918 00919 __SUP_COUT__ << "configGroup: " << configGroup << __E__; 00920 __SUP_COUT__ << "configGroupKey: " << configGroupKey << __E__; 00921 __SUP_COUT__ << "startPath: " << startPath << __E__; 00922 __SUP_COUT__ << "recordList: " << recordList << __E__; 00923 __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__; 00924 00925 handleFillDeleteTreeNodeRecordsXML(xmlOut, 00926 cfgMgr, 00927 configGroup, 00928 TableGroupKey(configGroupKey), 00929 startPath, 00930 modifiedTables, 00931 recordList); 00932 } 00933 else if(requestType == "getAffectedActiveGroups") 00934 { 00935 std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName"); 00936 std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey"); 00937 std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables"); 00938 __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__; 00939 __SUP_COUT__ << "groupName: " << groupName << __E__; 00940 __SUP_COUT__ << "groupKey: " << groupKey << __E__; 00941 00942 handleGetAffectedGroupsXML( 00943 xmlOut, cfgMgr, groupName, TableGroupKey(groupKey), modifiedTables); 00944 } 00945 else if(requestType == "saveTreeNodeEdit") 00946 { 00947 std::string editNodeType = CgiDataUtilities::getData(cgiIn, "editNodeType"); 00948 std::string targetTable = CgiDataUtilities::getData(cgiIn, "targetTable"); 00949 std::string targetTableVersion = 00950 CgiDataUtilities::getData(cgiIn, "targetTableVersion"); 00951 std::string targetUID = CgiDataUtilities::getData(cgiIn, "targetUID"); 00952 std::string targetColumn = CgiDataUtilities::getData(cgiIn, "targetColumn"); 00953 std::string newValue = CgiDataUtilities::postData(cgiIn, "newValue"); 00954 00955 __SUP_COUT__ << "editNodeType: " << editNodeType << __E__; 00956 __SUP_COUT__ << "targetTable: " << targetTable << __E__; 00957 __SUP_COUT__ << "targetTableVersion: " << targetTableVersion << __E__; 00958 __SUP_COUT__ << "targetUID: " << targetUID << __E__; 00959 __SUP_COUT__ << "targetColumn: " << targetColumn << __E__; 00960 __SUP_COUT__ << "newValue: " << newValue << __E__; 00961 00962 handleSaveTreeNodeEditXML(xmlOut, 00963 cfgMgr, 00964 targetTable, 00965 TableVersion(targetTableVersion), 00966 editNodeType, 00967 CgiDataUtilities::decodeURIComponent(targetUID), 00968 CgiDataUtilities::decodeURIComponent(targetColumn), 00969 newValue, 00970 userInfo.username_); 00971 } 00972 else if(requestType == "getLinkToChoices") 00973 { 00974 std::string linkToTableName = CgiDataUtilities::getData(cgiIn, "linkToTableName"); 00975 std::string linkToTableVersion = 00976 CgiDataUtilities::getData(cgiIn, "linkToTableVersion"); 00977 std::string linkIdType = CgiDataUtilities::getData(cgiIn, "linkIdType"); 00978 std::string linkIndex = CgiDataUtilities::decodeURIComponent( 00979 CgiDataUtilities::getData(cgiIn, "linkIndex")); 00980 std::string linkInitId = CgiDataUtilities::getData(cgiIn, "linkInitId"); 00981 00982 __SUP_COUT__ << "linkToTableName: " << linkToTableName << __E__; 00983 __SUP_COUT__ << "linkToTableVersion: " << linkToTableVersion << __E__; 00984 __SUP_COUT__ << "linkIdType: " << linkIdType << __E__; 00985 __SUP_COUT__ << "linkIndex: " << linkIndex << __E__; 00986 __SUP_COUT__ << "linkInitId: " << linkInitId << __E__; 00987 00988 handleGetLinkToChoicesXML(xmlOut, 00989 cfgMgr, 00990 linkToTableName, 00991 TableVersion(linkToTableVersion), 00992 linkIdType, 00993 linkIndex, 00994 linkInitId); 00995 } 00996 else if(requestType == "activateTableGroup") 00997 { 00998 std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName"); 00999 std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey"); 01000 bool ignoreWarnings = CgiDataUtilities::getDataAsInt(cgiIn, "ignoreWarnings"); 01001 01002 __SUP_COUT__ << "Activating config: " << groupName << "(" << groupKey << ")" 01003 << __E__; 01004 __SUP_COUT__ << "ignoreWarnings: " << ignoreWarnings << __E__; 01005 01006 // add flag for GUI handling 01007 xmlOut.addTextElementToData("AttemptedGroupActivation", "1"); 01008 xmlOut.addTextElementToData("AttemptedGroupActivationName", groupName); 01009 xmlOut.addTextElementToData("AttemptedGroupActivationKey", groupKey); 01010 01011 std::string accumulatedTreeErrors; 01012 try 01013 { 01014 cfgMgr->activateTableGroup( 01015 groupName, 01016 TableGroupKey(groupKey), 01017 ignoreWarnings 01018 ? 0 01019 : &accumulatedTreeErrors); // if ignore warning then pass null 01020 } 01021 catch(std::runtime_error& e) 01022 { 01023 // NOTE it is critical for flimsy error parsing in JS GUI to leave 01024 // single quotes around the groupName and groupKey and have them be 01025 // the first single quotes encountered in the error mesage! 01026 __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__; 01027 xmlOut.addTextElementToData( 01028 "Error", 01029 "Error activating config group '" + groupName + "(" + groupKey + ")" + 01030 ".' Please see details below:\n\n" + std::string(e.what())); 01031 __SUP_COUT_ERR__ << "Errors detected so de-activating group: " << groupName 01032 << " (" << groupKey << ")" << __E__; 01033 try // just in case any lingering pieces, lets deactivate 01034 { 01035 cfgMgr->destroyTableGroup(groupName, true); 01036 } 01037 catch(...) 01038 { 01039 } 01040 } 01041 catch(cet::exception& e) 01042 { 01043 // NOTE it is critical for flimsy error parsing in JS GUI to leave 01044 // single quotes around the groupName and groupKey and have them be 01045 // the first single quotes encountered in the error mesage! 01046 01047 __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__; 01048 xmlOut.addTextElementToData("Error", 01049 "Error activating config group '" + groupName + 01050 "(" + groupKey + ")" + "!'\n\n" + 01051 std::string(e.what())); 01052 __SUP_COUT_ERR__ << "Errors detected so de-activating group: " << groupName 01053 << " (" << groupKey << ")" << __E__; 01054 try // just in case any lingering pieces, lets deactivate 01055 { 01056 cfgMgr->destroyTableGroup(groupName, true); 01057 } 01058 catch(...) 01059 { 01060 } 01061 } 01062 catch(...) 01063 { 01064 __SUP_COUT__ << "Error detected!" << __E__; 01065 throw; // unexpected exception! 01066 } 01067 01068 if(accumulatedTreeErrors != "") 01069 xmlOut.addTextElementToData("Error", 01070 "Warnings were found when activating group '" + 01071 groupName + "(" + groupKey + ")" + 01072 "'! Please see details below:\n\n" + 01073 accumulatedTreeErrors); 01074 } 01075 else if(requestType == "getActiveTableGroups") 01076 ; // do nothing, since they are always returned 01077 else if(requestType == "copyViewToCurrentColumns") 01078 { 01079 std::string tableName = 01080 CgiDataUtilities::getData(cgiIn, "tableName"); // from GET 01081 std::string sourceVersion = CgiDataUtilities::getData(cgiIn, "sourceVersion"); 01082 01083 __SUP_COUT__ << "tableName: " << tableName << __E__; 01084 __SUP_COUT__ << "sourceVersion: " << sourceVersion << __E__; 01085 __SUP_COUT__ << "userInfo.username_: " << userInfo.username_ << __E__; 01086 01087 // copy source version to new temporary version 01088 TableVersion newTemporaryVersion; 01089 try 01090 { 01091 // force emptying of cache for this table 01092 newTemporaryVersion = 01093 cfgMgr->copyViewToCurrentColumns(tableName, TableVersion(sourceVersion)); 01094 // 01095 // getTableByName(tableName)->reset(); 01096 // 01097 // //make sure source version is loaded 01098 // //need to load with loose column rules! 01099 // config = cfgMgr->getVersionedTableByName(tableName, 01100 // TableVersion(sourceVersion), true); 01101 // 01102 // //copy from source version to a new temporary version 01103 // newTemporaryVersion = config->copyView(config->getView(), 01104 // TableVersion(),userName); 01105 01106 __SUP_COUT__ << "New temporary version = " << newTemporaryVersion << __E__; 01107 } 01108 catch(std::runtime_error& e) 01109 { 01110 __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__; 01111 xmlOut.addTextElementToData("Error", 01112 "Error copying view from '" + tableName + "_v" + 01113 sourceVersion + "'! " + 01114 std::string(e.what())); 01115 } 01116 catch(...) 01117 { 01118 __SUP_COUT__ << "Error detected!\n\n " << __E__; 01119 xmlOut.addTextElementToData( 01120 "Error", 01121 "Error copying view from '" + tableName + "_v" + sourceVersion + "'! "); 01122 } 01123 01124 handleGetTableXML(xmlOut, cfgMgr, tableName, newTemporaryVersion); 01125 } 01126 else if(requestType == "getLastTableGroups") 01127 { 01128 XDAQ_CONST_CALL xdaq::ApplicationDescriptor* gatewaySupervisor = 01129 allSupervisorInfo_.isWizardMode() ? allSupervisorInfo_.getWizardDescriptor() 01130 : allSupervisorInfo_.getGatewayDescriptor(); 01131 01132 std::string timeString; 01133 std::pair<std::string /*group name*/, TableGroupKey> theGroup = 01134 theRemoteWebUsers_.getLastConfigGroup( 01135 gatewaySupervisor, "Configured", timeString); 01136 xmlOut.addTextElementToData("LastConfiguredGroupName", theGroup.first); 01137 xmlOut.addTextElementToData("LastConfiguredGroupKey", theGroup.second.toString()); 01138 xmlOut.addTextElementToData("LastConfiguredGroupTime", timeString); 01139 theGroup = theRemoteWebUsers_.getLastConfigGroup( 01140 gatewaySupervisor, "Started", timeString); 01141 xmlOut.addTextElementToData("LastStartedGroupName", theGroup.first); 01142 xmlOut.addTextElementToData("LastStartedGroupKey", theGroup.second.toString()); 01143 xmlOut.addTextElementToData("LastStartedGroupTime", timeString); 01144 } 01145 else if(requestType == "savePlanCommandSequence") 01146 { 01147 std::string planName = CgiDataUtilities::getData(cgiIn, "planName"); // from GET 01148 std::string commands = CgiDataUtilities::postData(cgiIn, "commands"); // from 01149 // POST 01150 std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables"); 01151 std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName"); 01152 std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey"); 01153 01154 __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__; 01155 __SUP_COUT__ << "planName: " << planName << __E__; 01156 __SUP_COUT__ << "commands: " << commands << __E__; 01157 __SUP_COUT__ << "groupName: " << groupName << __E__; 01158 __SUP_COUT__ << "groupKey: " << groupKey << __E__; 01159 01160 handleSavePlanCommandSequenceXML(xmlOut, 01161 cfgMgr, 01162 groupName, 01163 TableGroupKey(groupKey), 01164 modifiedTables, 01165 userInfo.username_, 01166 planName, 01167 commands); 01168 } 01169 else if(requestType == "mergeGroups") 01170 { 01171 std::string groupANameContext = 01172 CgiDataUtilities::getData(cgiIn, "groupANameContext"); 01173 std::string groupAKeyContext = 01174 CgiDataUtilities::getData(cgiIn, "groupAKeyContext"); 01175 std::string groupBNameContext = 01176 CgiDataUtilities::getData(cgiIn, "groupBNameContext"); 01177 std::string groupBKeyContext = 01178 CgiDataUtilities::getData(cgiIn, "groupBKeyContext"); 01179 std::string groupANameConfig = 01180 CgiDataUtilities::getData(cgiIn, "groupANameConfig"); 01181 std::string groupAKeyConfig = 01182 CgiDataUtilities::getData(cgiIn, "groupAKeyConfig"); 01183 std::string groupBNameConfig = 01184 CgiDataUtilities::getData(cgiIn, "groupBNameConfig"); 01185 std::string groupBKeyConfig = 01186 CgiDataUtilities::getData(cgiIn, "groupBKeyConfig"); 01187 std::string mergeApproach = 01188 CgiDataUtilities::getData(cgiIn, "mergeApproach"); 01189 01190 __SUP_COUTV__(groupANameContext); 01191 __SUP_COUTV__(groupAKeyContext); 01192 __SUP_COUTV__(groupBNameContext); 01193 __SUP_COUTV__(groupBKeyContext); 01194 __SUP_COUTV__(groupANameConfig); 01195 __SUP_COUTV__(groupAKeyConfig); 01196 __SUP_COUTV__(groupBNameConfig); 01197 __SUP_COUTV__(groupBKeyConfig); 01198 __SUP_COUTV__(mergeApproach); 01199 01200 handleMergeGroupsXML(xmlOut, 01201 cfgMgr, 01202 groupANameContext, 01203 TableGroupKey(groupAKeyContext), 01204 groupBNameContext, 01205 TableGroupKey(groupBKeyContext), 01206 groupANameConfig, 01207 TableGroupKey(groupAKeyConfig), 01208 groupBNameConfig, 01209 TableGroupKey(groupBKeyConfig), 01210 userInfo.username_, 01211 mergeApproach); 01212 } 01213 else 01214 { 01215 __SUP_SS__ << "requestType '" << requestType << "' request not recognized." 01216 << __E__; 01217 __SUP_COUT__ << "\n" << ss.str(); 01218 xmlOut.addTextElementToData("Error", ss.str()); 01219 } 01220 01221 //__SUP_COUT__ << "Wrapping up..." << __E__; 01222 01223 // always add active config groups to xml response 01224 std::map<std::string /*type*/, std::pair<std::string /*groupName*/, TableGroupKey>> 01225 activeGroupMap = cfgMgr->getActiveTableGroups(); 01226 01227 for(auto& type : activeGroupMap) 01228 { 01229 xmlOut.addTextElementToData(type.first + "-ActiveGroupName", type.second.first); 01230 xmlOut.addTextElementToData(type.first + "-ActiveGroupKey", 01231 type.second.second.toString()); 01232 //__SUP_COUT__ << "ActiveGroup " << type.first << " " << type.second.first << "(" 01233 //<< type.second.second << ")" << __E__; 01234 } 01235 01236 // always add version tracking bool 01237 xmlOut.addTextElementToData( 01238 "versionTracking", 01239 ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF"); 01240 // 01241 // __SUP_COUT__ << __E__; 01242 // xmlOut.outputXmlDocument(0,true,true); 01243 // __SUP_COUT__ << __E__; 01244 01245 } // end ::request() 01246 catch(const std::runtime_error& e) 01247 { 01248 __SS__ << "A fatal error occurred while handling the request '" << requestType 01249 << ".' Error: " << e.what() << __E__; 01250 __COUT_ERR__ << "\n" << ss.str(); 01251 xmlOut.addTextElementToData("Error", ss.str()); 01252 01253 try 01254 { 01255 // always add version tracking bool 01256 xmlOut.addTextElementToData( 01257 "versionTracking", 01258 ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF"); 01259 } 01260 catch(...) 01261 { 01262 __COUT_ERR__ << "Error getting version tracking status!" << __E__; 01263 } 01264 } 01265 catch(...) 01266 { 01267 __SS__ << "An unknown fatal error occurred while handling the request '" 01268 << requestType << ".'" << __E__; 01269 __COUT_ERR__ << "\n" << ss.str(); 01270 xmlOut.addTextElementToData("Error", ss.str()); 01271 01272 try 01273 { 01274 // always add version tracking bool 01275 xmlOut.addTextElementToData( 01276 "versionTracking", 01277 ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF"); 01278 } 01279 catch(...) 01280 { 01281 __COUT_ERR__ << "Error getting version tracking status!" << __E__; 01282 } 01283 } 01284 01285 //======================================================================================================================== 01286 // handleGetAffectedGroupsXML 01287 // checks which of the active groups are affected 01288 // by the tables changing in the modified tables list. 01289 // 01290 // returns for each group affected: 01291 // the group name/key affected 01292 // and modified member map 01293 void ConfigurationGUISupervisor::handleGetAffectedGroupsXML( 01294 HttpXmlDocument& xmlOut, 01295 ConfigurationManagerRW* cfgMgr, 01296 const std::string& rootGroupName, 01297 const TableGroupKey& rootGroupKey, 01298 const std::string& modifiedTables) try 01299 { 01300 // determine type of rootGroup 01301 // replace the matching type in considered groups 01302 // for each considered table group 01303 // 01304 // check if there is a modified table that is also a member of that group 01305 // if so, 01306 // make xml entry pair 01307 01308 std::map<std::string, std::pair<std::string, TableGroupKey>> consideredGroups = 01309 cfgMgr->getActiveTableGroups(); 01310 01311 // check that there is a context and config group to consider 01312 // if there is not, then pull from failed list 01313 if(consideredGroups[ConfigurationManager::ACTIVE_GROUP_NAME_CONTEXT] 01314 .second.isInvalid()) 01315 { 01316 __SUP_COUT__ << "Finding a context group to consider..." << __E__; 01317 if(cfgMgr->getFailedTableGroups().find( 01318 ConfigurationManager::ACTIVE_GROUP_NAME_CONTEXT) != 01319 cfgMgr->getFailedTableGroups().end()) 01320 { 01321 consideredGroups[ConfigurationManager::ACTIVE_GROUP_NAME_CONTEXT] = 01322 cfgMgr->getFailedTableGroups().at( 01323 ConfigurationManager::ACTIVE_GROUP_NAME_CONTEXT); 01324 } 01325 else if(cfgMgr->getFailedTableGroups().find( 01326 ConfigurationManager::ACTIVE_GROUP_NAME_UNKNOWN) != 01327 cfgMgr->getFailedTableGroups().end()) 01328 { 01329 consideredGroups[ConfigurationManager::ACTIVE_GROUP_NAME_CONTEXT] = 01330 cfgMgr->getFailedTableGroups().at( 01331 ConfigurationManager::ACTIVE_GROUP_NAME_UNKNOWN); 01332 } 01333 } 01334 if(consideredGroups[ConfigurationManager::ACTIVE_GROUP_NAME_CONFIGURATION] 01335 .second.isInvalid()) 01336 { 01337 __SUP_COUT__ << "Finding a table group to consider..." << __E__; 01338 if(cfgMgr->getFailedTableGroups().find( 01339 ConfigurationManager::ACTIVE_GROUP_NAME_CONFIGURATION) != 01340 cfgMgr->getFailedTableGroups().end()) 01341 { 01342 consideredGroups[ConfigurationManager::ACTIVE_GROUP_NAME_CONFIGURATION] = 01343 cfgMgr->getFailedTableGroups().at( 01344 ConfigurationManager::ACTIVE_GROUP_NAME_CONFIGURATION); 01345 } 01346 else if(cfgMgr->getFailedTableGroups().find( 01347 ConfigurationManager::ACTIVE_GROUP_NAME_UNKNOWN) != 01348 cfgMgr->getFailedTableGroups().end()) 01349 { 01350 consideredGroups[ConfigurationManager::ACTIVE_GROUP_NAME_CONFIGURATION] = 01351 cfgMgr->getFailedTableGroups().at( 01352 ConfigurationManager::ACTIVE_GROUP_NAME_UNKNOWN); 01353 } 01354 } 01355 01356 __SUP_COUTV__(StringMacros::mapToString(consideredGroups)); 01357 01358 // determine the type of table group 01359 try 01360 { 01361 std::map<std::string /*name*/, TableVersion /*version*/> rootGroupMemberMap; 01362 01363 cfgMgr->loadTableGroup(rootGroupName, 01364 rootGroupKey, 01365 0, 01366 &rootGroupMemberMap, 01367 0, 01368 0, 01369 0, 01370 0, 01371 0, // defaults 01372 true); // doNotLoadMember 01373 01374 const std::string& groupType = cfgMgr->getTypeNameOfGroup(rootGroupMemberMap); 01375 01376 consideredGroups[groupType] = 01377 std::pair<std::string, TableGroupKey>(rootGroupName, rootGroupKey); 01378 } 01379 catch(const std::runtime_error& e) 01380 { 01381 // if actual group name was attempted re-throw 01382 if(rootGroupName.size()) 01383 { 01384 __SUP_SS__ << "Failed to determine type of table group for " << rootGroupName 01385 << "(" << rootGroupKey << ")! " << e.what() << __E__; 01386 __SUP_COUT_ERR__ << "\n" << ss.str(); 01387 __SS_THROW__; 01388 } 01389 01390 // else assume it was the intention to just consider the active groups 01391 __SUP_COUT__ << "Did not modify considered active groups due to empty root group " 01392 "name - assuming this was intentional." 01393 << __E__; 01394 } 01395 catch(...) 01396 { 01397 // if actual group name was attempted re-throw 01398 if(rootGroupName.size()) 01399 { 01400 __SUP_COUT_ERR__ << "Failed to determine type of table group for " 01401 << rootGroupName << "(" << rootGroupKey << ")!" << __E__; 01402 throw; 01403 } 01404 01405 // else assume it was the intention to just consider the active groups 01406 __SUP_COUT__ << "Did not modify considered active groups due to empty root group " 01407 "name - assuming this was intentional." 01408 << __E__; 01409 } 01410 01411 std::map<std::string /*name*/, TableVersion /*version*/> modifiedTablesMap; 01412 std::map<std::string /*name*/, TableVersion /*version*/>::iterator 01413 modifiedTablesMapIt; 01414 { 01415 std::istringstream f(modifiedTables); 01416 std::string table, version; 01417 while(getline(f, table, ',')) 01418 { 01419 getline(f, version, ','); 01420 modifiedTablesMap.insert( 01421 std::pair<std::string /*name*/, TableVersion /*version*/>( 01422 table, TableVersion(version))); 01423 } 01424 __SUP_COUT__ << modifiedTables << __E__; 01425 for(auto& pair : modifiedTablesMap) 01426 __SUP_COUT__ << "modified table " << pair.first << ":" << pair.second 01427 << __E__; 01428 } 01429 01430 bool affected; 01431 DOMElement* parentEl; 01432 std::string groupComment; 01433 for(auto group : consideredGroups) 01434 { 01435 if(group.second.second.isInvalid()) 01436 continue; // skip invalid 01437 01438 __SUP_COUT__ << "Considering " << group.first << " group " << group.second.first 01439 << " (" << group.second.second << ")" << __E__; 01440 01441 affected = false; 01442 01443 std::map<std::string /*name*/, TableVersion /*version*/> memberMap; 01444 cfgMgr->loadTableGroup(group.second.first, 01445 group.second.second, 01446 0, 01447 &memberMap, 01448 0, 01449 0, 01450 &groupComment, 01451 0, 01452 0, // mostly defaults 01453 true /*doNotLoadMember*/); 01454 01455 __SUP_COUT__ << "groupComment = " << groupComment << __E__; 01456 01457 for(auto& table : memberMap) 01458 { 01459 if((modifiedTablesMapIt = modifiedTablesMap.find(table.first)) != 01460 modifiedTablesMap 01461 .end() && // check if version is different for member table 01462 table.second != (*modifiedTablesMapIt).second) 01463 { 01464 __SUP_COUT__ << "Affected by " << (*modifiedTablesMapIt).first << ":" 01465 << (*modifiedTablesMapIt).second << __E__; 01466 affected = true; 01467 memberMap[table.first] = (*modifiedTablesMapIt).second; 01468 } 01469 } 01470 01471 if(affected) 01472 { 01473 parentEl = xmlOut.addTextElementToData("AffectedActiveGroup", ""); 01474 xmlOut.addTextElementToParent("GroupName", group.second.first, parentEl); 01475 xmlOut.addTextElementToParent( 01476 "GroupKey", group.second.second.toString(), parentEl); 01477 xmlOut.addTextElementToParent("GroupComment", groupComment, parentEl); 01478 01479 for(auto& table : memberMap) 01480 { 01481 xmlOut.addTextElementToParent("MemberName", table.first, parentEl); 01482 xmlOut.addTextElementToParent( 01483 "MemberVersion", table.second.toString(), parentEl); 01484 } 01485 } 01486 } 01487 } 01488 catch(std::runtime_error& e) 01489 { 01490 __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__; 01491 xmlOut.addTextElementToData( 01492 "Error", "Error getting affected groups! " + std::string(e.what())); 01493 } 01494 catch(...) 01495 { 01496 __SUP_COUT__ << "Error detected!\n\n " << __E__; 01497 xmlOut.addTextElementToData("Error", "Error getting affected groups! "); 01498 } 01499 01500 //======================================================================================================================== 01501 // setupActiveTables 01502 // setup active tables based on input group and modified tables 01503 // 01504 // if groupName == "" || groupKey is invalid 01505 // then do for active groups 01506 // if valid, then replace appropriate active group with specified group 01507 // Also replace active versions of modified tables with the specified version 01508 void ConfigurationGUISupervisor::setupActiveTablesXML( 01509 HttpXmlDocument& xmlOut, 01510 ConfigurationManagerRW* cfgMgr, 01511 const std::string& groupName, 01512 const TableGroupKey& groupKey, 01513 const std::string& modifiedTables, 01514 bool refreshAll, 01515 bool doGetGroupInfo, 01516 std::map<std::string /*name*/, TableVersion /*version*/>* returnMemberMap, 01517 bool outputActiveTables, 01518 std::string* accumulatedErrors) try 01519 { 01520 if(accumulatedErrors) 01521 *accumulatedErrors = ""; 01522 01523 xmlOut.addTextElementToData("configGroup", groupName); 01524 xmlOut.addTextElementToData("configGroupKey", groupKey.toString()); 01525 01526 bool usingActiveGroups = (groupName == "" || groupKey.isInvalid()); 01527 01528 // reload all tables so that partially loaded tables are not allowed 01529 if(usingActiveGroups || refreshAll) 01530 cfgMgr->getAllTableInfo(true, accumulatedErrors); // do refresh 01531 01532 const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo(false); 01533 01534 std::map<std::string /*name*/, TableVersion /*version*/> modifiedTablesMap; 01535 std::map<std::string /*name*/, TableVersion /*version*/>::iterator 01536 modifiedTablesMapIt; 01537 01538 if(usingActiveGroups) 01539 { 01540 // no need to load a target group 01541 __SUP_COUT__ << "Using active groups." << __E__; 01542 } 01543 else 01544 { 01545 __SUP_COUT__ << "Loading group '" << groupName << "(" << groupKey << ")'" 01546 << __E__; 01547 01548 std::string groupComment, groupAuthor, configGroupCreationTime; 01549 01550 // only same member map if object pointer was passed 01551 cfgMgr->loadTableGroup(groupName, 01552 groupKey, 01553 false /*doActivate*/, 01554 returnMemberMap, 01555 0 /*progressBar*/, 01556 accumulatedErrors, 01557 doGetGroupInfo ? &groupComment : 0, 01558 doGetGroupInfo ? &groupAuthor : 0, 01559 doGetGroupInfo ? &configGroupCreationTime : 0); 01560 01561 if(doGetGroupInfo) 01562 { 01563 xmlOut.addTextElementToData("configGroupComment", groupComment); 01564 xmlOut.addTextElementToData("configGroupAuthor", groupAuthor); 01565 xmlOut.addTextElementToData("configGroupCreationTime", 01566 configGroupCreationTime); 01567 } 01568 01569 if(accumulatedErrors && *accumulatedErrors != "") 01570 __SUP_COUTV__(*accumulatedErrors); 01571 01572 } 01573 01574 // extract modified tables 01575 { 01576 std::istringstream f(modifiedTables); 01577 std::string table, version; 01578 while(getline(f, table, ',')) 01579 { 01580 getline(f, version, ','); 01581 modifiedTablesMap.insert( 01582 std::pair<std::string /*name*/, TableVersion /*version*/>( 01583 table, TableVersion(version))); 01584 } 01585 //__SUP_COUT__ << modifiedTables << __E__; 01586 for(auto& pair : modifiedTablesMap) 01587 __SUP_COUT__ << "modified table " << pair.first << ":" << pair.second 01588 << __E__; 01589 } 01590 01591 // add all active table pairs to xmlOut 01592 std::map<std::string, TableVersion> allActivePairs = cfgMgr->getActiveVersions(); 01593 xmlOut.addTextElementToData("DefaultNoLink", 01594 TableViewColumnInfo::DATATYPE_LINK_DEFAULT); 01595 for(auto& activePair : allActivePairs) 01596 { 01597 if(outputActiveTables) 01598 xmlOut.addTextElementToData("ActiveTableName", activePair.first); 01599 01600 // check if name is in modifiedTables 01601 // if so, activate the temporary version 01602 if((modifiedTablesMapIt = modifiedTablesMap.find(activePair.first)) != 01603 modifiedTablesMap.end()) 01604 { 01605 __SUP_COUT__ << "Found modified table " << (*modifiedTablesMapIt).first 01606 << ": trying... " << (*modifiedTablesMapIt).second << __E__; 01607 01608 try 01609 { 01610 allTableInfo.at(activePair.first) 01611 .tablePtr_->setActiveView((*modifiedTablesMapIt).second); 01612 } 01613 catch(...) 01614 { 01615 __SUP_SS__ 01616 << "Modified table version v" << (*modifiedTablesMapIt).second 01617 << " failed. Reverting to v" 01618 << allTableInfo.at(activePair.first).tablePtr_->getView().getVersion() 01619 << "." << __E__; 01620 __SUP_COUT_WARN__ << "Warning detected!\n\n " << ss.str() << __E__; 01621 xmlOut.addTextElementToData( 01622 "Warning", 01623 "Error setting up active tables!\n\n" + std::string(ss.str())); 01624 } 01625 } 01626 01627 if(outputActiveTables) 01628 { 01629 xmlOut.addTextElementToData("ActiveTableVersion", 01630 allTableInfo.at(activePair.first) 01631 .tablePtr_->getView() 01632 .getVersion() 01633 .toString()); 01634 xmlOut.addTextElementToData( 01635 "ActiveTableComment", 01636 allTableInfo.at(activePair.first).tablePtr_->getView().getComment()); 01637 } 01638 01639 //__SUP_COUT__ << "Active table = " << 01640 // activePair.first << "-v" << 01641 // allTableInfo.at(activePair.first).tablePtr_->getView().getVersion() << 01642 //__E__; 01643 } 01644 } 01645 catch(std::runtime_error& e) 01646 { 01647 __SUP_SS__ << ("Error setting up active tables!\n\n" + std::string(e.what())) 01648 << __E__; 01649 __SUP_COUT_ERR__ << "\n" << ss.str(); 01650 xmlOut.addTextElementToData("Error", ss.str()); 01651 } 01652 catch(...) 01653 { 01654 __SUP_SS__ << ("Error setting up active tables!\n\n") << __E__; 01655 __SUP_COUT_ERR__ << "\n" << ss.str(); 01656 xmlOut.addTextElementToData("Error", ss.str()); 01657 } 01658 01659 //======================================================================================================================== 01660 // handleFillCreateTreeNodeRecordsXML 01661 // Creates the records in the appropriate table 01662 // and creates a temporary version. 01663 // the modified-<modified tables> list is returned in xml 01664 // 01665 // if groupName == "" || groupKey is invalid 01666 // then do for active groups 01667 // 01668 // parameters 01669 // configGroupName (full name with key) 01670 // starting node path 01671 // modifiedTables := CSV of table/version pairs 01672 // recordList := CSV list of records to create 01673 // 01674 void ConfigurationGUISupervisor::handleFillCreateTreeNodeRecordsXML( 01675 HttpXmlDocument& xmlOut, 01676 ConfigurationManagerRW* cfgMgr, 01677 const std::string& groupName, 01678 const TableGroupKey& groupKey, 01679 const std::string& startPath, 01680 const std::string& modifiedTables, 01681 const std::string& recordList, 01682 const std::string& author) 01683 { 01684 // setup active tables based on input group and modified tables 01685 setupActiveTablesXML(xmlOut, 01686 cfgMgr, 01687 groupName, 01688 groupKey, 01689 modifiedTables, 01690 true /* refresh all */, 01691 false /* getGroupInfo */, 01692 0 /* returnMemberMap */, 01693 false /* outputActiveTables */); 01694 01695 try 01696 { 01697 ConfigurationTree targetNode = cfgMgr->getNode(startPath); 01698 TableBase* config = cfgMgr->getTableByName(targetNode.getTableName()); 01699 01700 __SUP_COUT__ << config->getTableName() << __E__; 01701 TableVersion temporaryVersion; 01702 01703 // if current version is not temporary 01704 // create temporary 01705 // else re-modify temporary version 01706 // edit temporary version directly 01707 // then after all edits return active versions 01708 // 01709 01710 bool firstSave = true; 01711 01712 // save current version 01713 TableView backupView; 01714 01715 // extract record list 01716 { 01717 std::istringstream f(recordList); 01718 std::string recordUID; 01719 unsigned int i; 01720 01721 while(getline(f, recordUID, ',')) // for each record 01722 { 01723 recordUID = StringMacros::decodeURIComponent(recordUID); 01724 01725 __SUP_COUT__ << "recordUID " << recordUID << __E__; 01726 01727 if(firstSave) // handle version bookkeeping 01728 { 01729 if(!(temporaryVersion = targetNode.getTableVersion()) 01730 .isTemporaryVersion()) 01731 { 01732 __SUP_COUT__ << "Start version " << temporaryVersion << __E__; 01733 // create temporary version for editing 01734 temporaryVersion = config->createTemporaryView(temporaryVersion); 01735 cfgMgr->saveNewTable(targetNode.getTableName(), 01736 temporaryVersion, 01737 true); // proper bookkeeping for temporary 01738 // version with the new version 01739 01740 __SUP_COUT__ << "Created temporary version " << temporaryVersion 01741 << __E__; 01742 } 01743 else // else table is already temporary version 01744 __SUP_COUT__ << "Using temporary version " << temporaryVersion 01745 << __E__; 01746 01747 firstSave = false; 01748 01749 // copy original to backup before modifying 01750 backupView.copy(config->getView(), temporaryVersion, author); 01751 } 01752 01753 // at this point have valid temporary version to edit 01754 01755 // copy "table-newRow" type edit from handleSaveTreeNodeEditXML() 01756 // functionality 01757 01758 // add row 01759 unsigned int row = config->getViewP()->addRow( 01760 author, true /*incrementUniqueData*/); // increment all unique data 01761 // fields to void conflict 01762 01763 // if TableViewColumnInfo::COL_NAME_STATUS exists, set it to true 01764 try 01765 { 01766 unsigned int col = config->getViewP()->getColStatus(); 01767 config->getViewP()->setURIEncodedValue("1", row, col); 01768 } 01769 catch(...) 01770 { 01771 } // if not, ignore 01772 01773 // set UID value 01774 config->getViewP()->setURIEncodedValue( 01775 recordUID, row, config->getViewP()->getColUID()); 01776 } 01777 } 01778 01779 if(!firstSave) // only test table if there was a change 01780 { 01781 try 01782 { 01783 config->getViewP()->init(); // verify new table (throws runtime_errors) 01784 } 01785 catch(...) 01786 { 01787 __SUP_COUT_INFO__ << "Reverting to original view." << __E__; 01788 __SUP_COUT__ << "Before:" << __E__; 01789 config->getViewP()->print(); 01790 config->getViewP()->copy(backupView, temporaryVersion, author); 01791 __SUP_COUT__ << "After:" << __E__; 01792 config->getViewP()->print(); 01793 01794 throw; // rethrow 01795 } 01796 } 01797 01798 handleFillModifiedTablesXML(xmlOut, cfgMgr); 01799 } 01800 catch(std::runtime_error& e) 01801 { 01802 __SUP_SS__ << ("Error creating new record(s)!\n\n" + std::string(e.what())) 01803 << __E__; 01804 __SUP_COUT_ERR__ << "\n" << ss.str(); 01805 xmlOut.addTextElementToData("Error", ss.str()); 01806 } 01807 catch(...) 01808 { 01809 __SUP_SS__ << ("Error creating new record(s)!\n\n") << __E__; 01810 __SUP_COUT_ERR__ << "\n" << ss.str(); 01811 xmlOut.addTextElementToData("Error", ss.str()); 01812 } 01813 } 01814 01815 //======================================================================================================================== 01816 // handleFillModifiedTablesXML 01817 // fills <modified tables> as used by ConfigurationAPI 01818 void ConfigurationGUISupervisor::handleFillModifiedTablesXML( 01819 HttpXmlDocument& xmlOut, ConfigurationManagerRW* cfgMgr) try 01820 { 01821 // return modified <modified tables> 01822 const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo(); 01823 std::map<std::string, TableVersion> allActivePairs = cfgMgr->getActiveVersions(); 01824 for(auto& activePair : allActivePairs) 01825 { 01826 xmlOut.addTextElementToData("NewActiveTableName", activePair.first); 01827 xmlOut.addTextElementToData("NewActiveTableVersion", 01828 allTableInfo.at(activePair.first) 01829 .tablePtr_->getView() 01830 .getVersion() 01831 .toString()); 01832 xmlOut.addTextElementToData( 01833 "NewActiveTableComment", 01834 allTableInfo.at(activePair.first).tablePtr_->getView().getComment()); 01835 } 01836 } 01837 catch(std::runtime_error& e) 01838 { 01839 __SUP_SS__ << ("Error!\n\n" + std::string(e.what())) << __E__; 01840 __SUP_COUT_ERR__ << "\n" << ss.str(); 01841 xmlOut.addTextElementToData("Error", ss.str()); 01842 } 01843 catch(...) 01844 { 01845 __SUP_SS__ << ("Error!\n\n") << __E__; 01846 __SUP_COUT_ERR__ << "\n" << ss.str(); 01847 xmlOut.addTextElementToData("Error", ss.str()); 01848 } 01849 01850 //======================================================================================================================== 01851 // handleFillDeleteTreeNodeRecordsXML 01852 // Deletes the records in the appropriate table 01853 // and creates a temporary version. 01854 // the modified-<modified tables> list is returned in xml 01855 // 01856 // if groupName == "" || groupKey is invalid 01857 // then do for active groups 01858 // 01859 // parameters 01860 // configGroupName (full name with key) 01861 // starting node path 01862 // modifiedTables := CSV of table/version pairs 01863 // recordList := CSV list of records to create 01864 // 01865 void ConfigurationGUISupervisor::handleFillDeleteTreeNodeRecordsXML( 01866 HttpXmlDocument& xmlOut, 01867 ConfigurationManagerRW* cfgMgr, 01868 const std::string& groupName, 01869 const TableGroupKey& groupKey, 01870 const std::string& startPath, 01871 const std::string& modifiedTables, 01872 const std::string& recordList) 01873 { 01874 // setup active tables based on input group and modified tables 01875 setupActiveTablesXML(xmlOut, 01876 cfgMgr, 01877 groupName, 01878 groupKey, 01879 modifiedTables, 01880 true /* refresh all */, 01881 false /* getGroupInfo */, 01882 0 /* returnMemberMap */, 01883 false /* outputActiveTables */); 01884 01885 try 01886 { 01887 ConfigurationTree targetNode = cfgMgr->getNode(startPath); 01888 TableBase* config = cfgMgr->getTableByName(targetNode.getTableName()); 01889 01890 __SUP_COUT__ << config->getTableName() << __E__; 01891 TableVersion temporaryVersion; 01892 01893 // if current version is not temporary 01894 // create temporary 01895 // else re-modify temporary version 01896 // edit temporary version directly 01897 // then after all edits return active versions 01898 // 01899 01900 bool firstSave = true; 01901 01902 // extract record list 01903 { 01904 std::istringstream f(recordList); 01905 std::string recordUID; 01906 unsigned int i; 01907 01908 while(getline(f, recordUID, ',')) // for each record 01909 { 01910 recordUID = StringMacros::decodeURIComponent(recordUID); 01911 01912 __SUP_COUT__ << "recordUID " << recordUID << __E__; 01913 01914 if(firstSave) // handle version bookkeeping 01915 { 01916 if(!(temporaryVersion = targetNode.getTableVersion()) 01917 .isTemporaryVersion()) 01918 { 01919 __SUP_COUT__ << "Start version " << temporaryVersion << __E__; 01920 // create temporary version for editing 01921 temporaryVersion = config->createTemporaryView(temporaryVersion); 01922 cfgMgr->saveNewTable(targetNode.getTableName(), 01923 temporaryVersion, 01924 true); // proper bookkeeping for temporary 01925 // version with the new version 01926 01927 __SUP_COUT__ << "Created temporary version " << temporaryVersion 01928 << __E__; 01929 } 01930 else // else table is already temporary version 01931 __SUP_COUT__ << "Using temporary version " << temporaryVersion 01932 << __E__; 01933 01934 firstSave = false; 01935 } 01936 01937 // at this point have valid temporary version to edit 01938 01939 // copy "delete-uid" type edit from handleSaveTreeNodeEditXML() 01940 // functionality 01941 unsigned int row = config->getViewP()->findRow( 01942 config->getViewP()->getColUID(), recordUID); 01943 config->getViewP()->deleteRow(row); 01944 } 01945 } 01946 01947 if(!firstSave) // only test table if there was a change 01948 config->getViewP()->init(); // verify new table (throws runtime_errors) 01949 01950 handleFillModifiedTablesXML(xmlOut, cfgMgr); 01951 } 01952 catch(std::runtime_error& e) 01953 { 01954 __SUP_SS__ << ("Error removing record(s)!\n\n" + std::string(e.what())) << __E__; 01955 __SUP_COUT_ERR__ << "\n" << ss.str(); 01956 xmlOut.addTextElementToData("Error", ss.str()); 01957 } 01958 catch(...) 01959 { 01960 __SUP_SS__ << ("Error removing record(s)!\n\n") << __E__; 01961 __SUP_COUT_ERR__ << "\n" << ss.str(); 01962 xmlOut.addTextElementToData("Error", ss.str()); 01963 } 01964 } 01965 01966 //======================================================================================================================== 01967 // handleFillSetTreeNodeFieldValuesXML 01968 // writes for each record, the field/value pairs to the appropriate table 01969 // and creates a temporary version. 01970 // the modified-<modified tables> list is returned in xml 01971 // 01972 // if groupName == "" || groupKey is invalid 01973 // then do for active groups 01974 // 01975 // parameters 01976 // configGroupName (full name with key) 01977 // starting node path 01978 // modifiedTables := CSV of table/version pairs 01979 // recordList := CSV list of records for which to write values for fields 01980 // fieldList := CSV of relative-to-record-path to fields to write to each record 01981 // valueList := CSV of values corresponding to fields 01982 // 01983 void ConfigurationGUISupervisor::handleFillSetTreeNodeFieldValuesXML( 01984 HttpXmlDocument& xmlOut, 01985 ConfigurationManagerRW* cfgMgr, 01986 const std::string& groupName, 01987 const TableGroupKey& groupKey, 01988 const std::string& startPath, 01989 const std::string& modifiedTables, 01990 const std::string& recordList, 01991 const std::string& fieldList, 01992 const std::string& valueList, 01993 const std::string& author) 01994 { 01995 // setup active tables based on input group and modified tables 01996 setupActiveTablesXML(xmlOut, 01997 cfgMgr, 01998 groupName, 01999 groupKey, 02000 modifiedTables, 02001 true /* refresh all */, 02002 false /* getGroupInfo */, 02003 0 /* returnMemberMap */, 02004 false /* outputActiveTables */); 02005 02006 // for each field 02007 // return field/value pair in xml 02008 02009 try 02010 { 02011 std::vector<std::string /*relative-path*/> fieldPaths; 02012 // extract field list 02013 { 02014 std::istringstream f(fieldList); 02015 std::string fieldPath; 02016 while(getline(f, fieldPath, ',')) 02017 { 02018 fieldPaths.push_back(StringMacros::decodeURIComponent(fieldPath)); 02019 } 02020 __SUP_COUT__ << fieldList << __E__; 02021 for(const auto& field : fieldPaths) 02022 __SUP_COUT__ << "fieldPath " << field << __E__; 02023 } 02024 02025 std::vector<std::string /*relative-path*/> fieldValues; 02026 // extract value list 02027 { 02028 std::istringstream f(valueList); 02029 std::string fieldValue; 02030 while(getline(f, fieldValue, ',')) 02031 { 02032 fieldValues.push_back( 02033 fieldValue); // setURIEncodedValue is expected 02034 // StringMacros::decodeURIComponent(fieldValue)); 02035 } 02036 02037 // if last value is "" then push empty value 02038 if(valueList.size() && valueList[valueList.size() - 1] == ',') 02039 fieldValues.push_back(""); 02040 02041 __SUP_COUT__ << valueList << __E__; 02042 for(const auto& value : fieldValues) 02043 __SUP_COUT__ << "fieldValue " << value << __E__; 02044 } 02045 02046 if(fieldPaths.size() != fieldValues.size()) 02047 { 02048 __SUP_SS__; 02049 __THROW__(ss.str() + "Mismatch in fields and values array size!"); 02050 } 02051 02052 // extract record list 02053 { 02054 TableBase* config; 02055 TableVersion temporaryVersion; 02056 std::istringstream f(recordList); 02057 std::string recordUID; 02058 unsigned int i; 02059 02060 while(getline(f, recordUID, ',')) // for each record 02061 { 02062 recordUID = StringMacros::decodeURIComponent(recordUID); 02063 02064 //__SUP_COUT__ << "recordUID " << recordUID << __E__; 02065 02066 DOMElement* parentEl = 02067 xmlOut.addTextElementToData("fieldValues", recordUID); 02068 02069 // for each field, set value 02070 for(i = 0; i < fieldPaths.size(); ++i) 02071 { 02072 __SUP_COUT__ << "fieldPath " << fieldPaths[i] << __E__; 02073 __SUP_COUT__ << "fieldValue " << fieldValues[i] << __E__; 02074 02075 // doNotThrowOnBrokenUIDLinks so that link UIDs can be edited like 02076 // other fields 02077 ConfigurationTree targetNode = 02078 cfgMgr->getNode(startPath + "/" + recordUID + "/" + fieldPaths[i], 02079 true /*doNotThrowOnBrokenUIDLinks*/); 02080 02081 // need table, uid, columnName to set a value 02082 02083 // assume correct table version is loaded by setupActiveTablesXML() 02084 // config = cfgMgr->getTableByName( 02085 // targetNode.getTableName()); 02086 // 02087 //__SUP_COUT__ << "Active version is " << config->getViewVersion() << 02088 //__E__; 02089 02090 // mimic handleSaveTreeNodeEditXML L 1750 02091 // Actually call it! .. 02092 // with a modifier? 02093 // or 02094 // handleSaveTreeNodeEditXML(xmlOut, 02095 // cfgMgr, 02096 // targetNode.getTableName(), 02097 // targetNode.getTableVersion(), 02098 // "value", 02099 // targetNode.getUIDAsString(), 02100 // targetNode.getValueName(), //col name 02101 // fieldValues[i] 02102 // ); 02103 02104 // or 02105 // (because problem is this would create a new temporary version each 02106 // time) if current version is not temporary 02107 // create temporary 02108 // else re-modify temporary version 02109 // edit temporary version directly 02110 // then after all edits return active versions 02111 // 02112 02113 __SUP_COUT__ << "Getting table " << targetNode.getFieldTableName() 02114 << __E__; 02115 02116 // if link must get parent config name 02117 config = cfgMgr->getTableByName( 02118 targetNode.getFieldTableName()); // NOT getTableName! 02119 if(!(temporaryVersion = config->getViewP()->getVersion()) 02120 .isTemporaryVersion()) 02121 { 02122 // create temporary version for editing 02123 temporaryVersion = 02124 config->createTemporaryView(config->getViewP()->getVersion()); 02125 cfgMgr->saveNewTable(config->getTableName(), 02126 temporaryVersion, 02127 true); // proper bookkeeping for temporary 02128 // version with the new version 02129 02130 __SUP_COUT__ << "Created temporary version " 02131 << config->getTableName() << "-v" << temporaryVersion 02132 << __E__; 02133 } 02134 // else //else table is already temporary version 02135 __SUP_COUT__ << "Using temporary version " << config->getTableName() 02136 << "-v" << temporaryVersion << __E__; 02137 02138 // copy "value" type edit from handleSaveTreeNodeEditXML() 02139 // functionality 02140 config->getViewP()->setURIEncodedValue(fieldValues[i], 02141 targetNode.getFieldRow(), 02142 targetNode.getFieldColumn(), 02143 author); 02144 02145 config->getViewP() 02146 ->init(); // verify new table (throws runtime_errors) 02147 } 02148 } 02149 } 02150 02151 handleFillModifiedTablesXML(xmlOut, cfgMgr); 02152 } 02153 catch(std::runtime_error& e) 02154 { 02155 __SUP_SS__ << ("Error setting field values!\n\n" + std::string(e.what())) 02156 << __E__; 02157 __SUP_COUT_ERR__ << "\n" << ss.str(); 02158 xmlOut.addTextElementToData("Error", ss.str()); 02159 } 02160 catch(...) 02161 { 02162 __SUP_SS__ << ("Error setting field values!\n\n") << __E__; 02163 __SUP_COUT_ERR__ << "\n" << ss.str(); 02164 xmlOut.addTextElementToData("Error", ss.str()); 02165 } 02166 } 02167 02168 //======================================================================================================================== 02169 // handleFillGetTreeNodeFieldValuesXML 02170 // returns for each record, xml list of field/value pairs 02171 // field := relative-path 02172 // 02173 // if groupName == "" || groupKey is invalid 02174 // then do for active groups 02175 // 02176 // parameters 02177 // configGroupName (full name with key) 02178 // starting node path 02179 // modifiedTables := CSV of table/version pairs 02180 // recordStr := CSV list of records for which to lookup values for fields 02181 // fieldList := CSV of relative-to-record-path to filter common fields 02182 // 02183 void ConfigurationGUISupervisor::handleFillGetTreeNodeFieldValuesXML( 02184 HttpXmlDocument& xmlOut, 02185 ConfigurationManagerRW* cfgMgr, 02186 const std::string& groupName, 02187 const TableGroupKey& groupKey, 02188 const std::string& startPath, 02189 const std::string& modifiedTables, 02190 const std::string& recordList, 02191 const std::string& fieldList) 02192 { 02193 // setup active tables based on input group and modified tables 02194 setupActiveTablesXML(xmlOut, cfgMgr, groupName, groupKey, modifiedTables); 02195 02196 // for each field 02197 // return field/value pair in xml 02198 02199 try 02200 { 02201 std::vector<std::string /*relative-path*/> fieldPaths; 02202 // extract field list 02203 { 02204 std::istringstream f(fieldList); 02205 std::string fieldPath; 02206 while(getline(f, fieldPath, ',')) 02207 { 02208 fieldPaths.push_back(StringMacros::decodeURIComponent(fieldPath)); 02209 } 02210 __SUP_COUT__ << fieldList << __E__; 02211 for(auto& field : fieldPaths) 02212 __SUP_COUT__ << "fieldPath " << field << __E__; 02213 } 02214 02215 // extract record list 02216 { 02217 std::istringstream f(recordList); 02218 std::string recordUID; 02219 while(getline(f, recordUID, ',')) // for each record 02220 { 02221 recordUID = StringMacros::decodeURIComponent(recordUID); 02222 02223 __SUP_COUT__ << "recordUID " << recordUID << __E__; 02224 02225 DOMElement* parentEl = 02226 xmlOut.addTextElementToData("fieldValues", recordUID); 02227 02228 // for each field, get value 02229 for(const auto& fieldPath : fieldPaths) 02230 { 02231 __SUP_COUT__ << "fieldPath " << fieldPath << __E__; 02232 02233 xmlOut.addTextElementToParent("FieldPath", fieldPath, parentEl); 02234 xmlOut.addTextElementToParent( 02235 "FieldValue", 02236 cfgMgr->getNode(startPath + "/" + recordUID + "/" + fieldPath) 02237 .getValueAsString(), 02238 parentEl); 02239 } 02240 } 02241 } 02242 } 02243 catch(std::runtime_error& e) 02244 { 02245 __SUP_SS__ << ("Error getting field values!\n\n" + std::string(e.what())) 02246 << __E__; 02247 __SUP_COUT_ERR__ << "\n" << ss.str(); 02248 xmlOut.addTextElementToData("Error", ss.str()); 02249 } 02250 catch(...) 02251 { 02252 __SUP_SS__ << ("Error getting field values!\n\n") << __E__; 02253 __SUP_COUT_ERR__ << "\n" << ss.str(); 02254 xmlOut.addTextElementToData("Error", ss.str()); 02255 } 02256 } 02257 02258 //======================================================================================================================== 02259 // handleFillTreeNodeCommonFieldsXML 02260 // returns xml list of common fields among records 02261 // field := relative-path 02262 // 02263 // if groupName == "" || groupKey is invalid 02264 // then do for active groups 02265 // 02266 // parameters 02267 // configGroupName (full name with key) 02268 // starting node path 02269 // depth from starting node path 02270 // modifiedTables := CSV of table/version pairs 02271 // recordList := CSV of records to search for fields 02272 // fieldList := CSV of relative-to-record-path to filter common fields 02273 // (accept or reject [use ! as first character to reject]) 02274 // [use leading* to ignore relative path - note that only leading and trailing 02275 // wildcards work] 02276 // 02277 void ConfigurationGUISupervisor::handleFillTreeNodeCommonFieldsXML( 02278 HttpXmlDocument& xmlOut, 02279 ConfigurationManagerRW* cfgMgr, 02280 const std::string& groupName, 02281 const TableGroupKey& groupKey, 02282 const std::string& startPath, 02283 unsigned int depth, 02284 const std::string& modifiedTables, 02285 const std::string& recordList, 02286 const std::string& fieldList) 02287 { 02288 // setup active tables based on input group and modified tables 02289 setupActiveTablesXML(xmlOut, cfgMgr, groupName, groupKey, modifiedTables); 02290 02291 try 02292 { 02293 DOMElement* parentEl = xmlOut.addTextElementToData("fields", startPath); 02294 02295 if(depth == 0) 02296 { 02297 __SUP_SS__ << "Depth of search must be greater than 0." << __E__; 02298 __SUP_COUT__ << ss.str(); 02299 __SS_THROW__; // done if 0 depth, no fields 02300 } 02301 02302 // do not allow traversing for common fields from root level 02303 // the tree view should be used for such a purpose 02304 // if(startPath == "/") 02305 // return; 02306 02307 std::vector<ConfigurationTree::RecordField> retFieldList; 02308 02309 { 02310 ConfigurationTree startNode = cfgMgr->getNode(startPath); 02311 if(startNode.isLinkNode() && startNode.isDisconnected()) 02312 { 02313 __SUP_SS__ << "Start path was a disconnected link node!" << __E__; 02314 __SUP_COUT_ERR__ << "\n" << ss.str(); 02315 __SS_THROW__; 02316 return; // quietly ignore disconnected links at depth 02317 // note: at the root level they will be flagged for the user 02318 } 02319 02320 std::vector<std::string /*relative-path*/> fieldAcceptList, fieldRejectList; 02321 if(fieldList != "") 02322 { 02323 // extract field filter list 02324 { 02325 std::istringstream f(fieldList); 02326 std::string fieldPath, decodedFieldPath; 02327 while(getline(f, fieldPath, ',')) 02328 { 02329 decodedFieldPath = StringMacros::decodeURIComponent(fieldPath); 02330 02331 if(decodedFieldPath[0] == '!') // reject field 02332 fieldRejectList.push_back(decodedFieldPath.substr(1)); 02333 else 02334 fieldAcceptList.push_back(decodedFieldPath); 02335 } 02336 __SUP_COUT__ << fieldList << __E__; 02337 for(auto& field : fieldAcceptList) 02338 __SUP_COUT__ << "fieldAcceptList " << field << __E__; 02339 for(auto& field : fieldRejectList) 02340 __SUP_COUT__ << "fieldRejectList " << field << __E__; 02341 } 02342 } 02343 02344 std::vector<std::string /*relative-path*/> records; 02345 if(recordList == "*") // handle all records case 02346 { 02347 records.clear(); 02348 records = startNode.getChildrenNames(); 02349 __SUP_COUT__ << "Translating wildcard..." << __E__; 02350 for(auto& record : records) 02351 __SUP_COUT__ << "recordList " << record << __E__; 02352 } 02353 else if(recordList != "") 02354 { 02355 // extract record list 02356 { 02357 std::istringstream f(recordList); 02358 std::string recordStr; 02359 while(getline(f, recordStr, ',')) 02360 { 02361 records.push_back(StringMacros::decodeURIComponent(recordStr)); 02362 } 02363 __SUP_COUT__ << recordList << __E__; 02364 for(auto& record : records) 02365 __SUP_COUT__ << "recordList " << record << __E__; 02366 } 02367 } 02368 02369 retFieldList = startNode.getCommonFields( 02370 records, fieldAcceptList, fieldRejectList, depth); 02371 } 02372 02373 DOMElement* parentTypeEl; 02374 for(const auto& fieldInfo : retFieldList) 02375 { 02376 xmlOut.addTextElementToParent( 02377 "FieldTableName", fieldInfo.tableName_, parentEl); 02378 xmlOut.addTextElementToParent( 02379 "FieldColumnName", fieldInfo.columnName_, parentEl); 02380 xmlOut.addTextElementToParent( 02381 "FieldRelativePath", fieldInfo.relativePath_, parentEl); 02382 xmlOut.addTextElementToParent( 02383 "FieldColumnType", fieldInfo.columnInfo_->getType(), parentEl); 02384 xmlOut.addTextElementToParent( 02385 "FieldColumnDataType", fieldInfo.columnInfo_->getDataType(), parentEl); 02386 xmlOut.addTextElementToParent("FieldColumnDefaultValue", 02387 fieldInfo.columnInfo_->getDefaultValue(), 02388 parentEl); 02389 02390 parentTypeEl = 02391 xmlOut.addTextElementToParent("FieldColumnDataChoices", "", parentEl); 02392 02393 // if there are associated data choices, send info 02394 auto dataChoices = fieldInfo.columnInfo_->getDataChoices(); 02395 xmlOut.addTextElementToParent( 02396 "FieldColumnDataChoice", // add default to list to mimic tree handling 02397 fieldInfo.columnInfo_->getDefaultValue(), 02398 parentTypeEl); 02399 for(const auto& dataChoice : dataChoices) 02400 xmlOut.addTextElementToParent( 02401 "FieldColumnDataChoice", dataChoice, parentTypeEl); 02402 } 02403 } 02404 catch(std::runtime_error& e) 02405 { 02406 __SUP_SS__ << ("Error getting common fields!\n\n" + std::string(e.what())) 02407 << __E__; 02408 __SUP_COUT_ERR__ << "\n" << ss.str(); 02409 xmlOut.addTextElementToData("Error", ss.str()); 02410 } 02411 catch(...) 02412 { 02413 __SUP_SS__ << ("Error getting common fields!\n\n") << __E__; 02414 __SUP_COUT_ERR__ << "\n" << ss.str(); 02415 xmlOut.addTextElementToData("Error", ss.str()); 02416 } 02417 } 02418 02419 //======================================================================================================================== 02420 // handleFillUniqueFieldValuesForRecordsXML 02421 // returns xml list of unique values for each fields among records 02422 // field := relative-path 02423 // 02424 // return xml 02425 // <xml> 02426 // <field val=relative-path> 02427 // <unique_val val=uval0> 02428 // <unique_val val=uval1> 02429 // .. next unique value 02430 // </field> 02431 // ... next field 02432 // </xml> 02433 // 02434 // if groupName == "" || groupKey is invalid 02435 // then do for active groups 02436 // 02437 // parameters 02438 // configGroupName (full name with key) 02439 // starting node path 02440 // modifiedTables := CSV of table/version pairs 02441 // recordList := CSV of records to search for unique values 02442 // fieldList := CSV of fields relative-to-record-path for which to get list of unique 02443 // values 02444 // 02445 void ConfigurationGUISupervisor::handleFillUniqueFieldValuesForRecordsXML( 02446 HttpXmlDocument& xmlOut, 02447 ConfigurationManagerRW* cfgMgr, 02448 const std::string& groupName, 02449 const TableGroupKey& groupKey, 02450 const std::string& startPath, 02451 const std::string& modifiedTables, 02452 const std::string& recordList, 02453 const std::string& fieldList) 02454 { 02455 // setup active tables based on input group and modified tables 02456 setupActiveTablesXML(xmlOut, cfgMgr, groupName, groupKey, modifiedTables); 02457 02458 try 02459 { 02460 // do not allow traversing for common fields from root level 02461 // the tree view should be used for such a purpose 02462 if(startPath == "/") 02463 return; 02464 02465 std::vector<std::string /*relative-path*/> fieldsToGet; 02466 if(fieldList != "") 02467 { 02468 // extract field filter list 02469 { 02470 std::istringstream f(fieldList); 02471 std::string fieldPath; 02472 while(getline(f, fieldPath, ',')) 02473 { 02474 fieldsToGet.push_back(StringMacros::decodeURIComponent(fieldPath)); 02475 } 02476 __SUP_COUT__ << fieldList << __E__; 02477 for(auto& field : fieldsToGet) 02478 __SUP_COUT__ << "fieldsToGet " << field << __E__; 02479 } 02480 } 02481 02482 ConfigurationTree startNode = cfgMgr->getNode(startPath); 02483 if(startNode.isLinkNode() && startNode.isDisconnected()) 02484 { 02485 __SUP_SS__ << "Start path was a disconnected link node!" << __E__; 02486 __SUP_COUT_ERR__ << "\n" << ss.str(); 02487 __SS_THROW__; 02488 } 02489 02490 std::vector<std::string /*relative-path*/> records; 02491 if(recordList == "*") // handle all records case 02492 { 02493 records.clear(); 02494 records = startNode.getChildrenNames(); 02495 __SUP_COUT__ << "Translating wildcard..." << __E__; 02496 for(auto& record : records) 02497 __SUP_COUT__ << "recordList " << record << __E__; 02498 } 02499 else if(recordList != "") 02500 { 02501 // extract record list 02502 { 02503 std::istringstream f(recordList); 02504 std::string recordStr; 02505 while(getline(f, recordStr, ',')) 02506 { 02507 records.push_back(StringMacros::decodeURIComponent(recordStr)); 02508 } 02509 __SUP_COUT__ << recordList << __E__; 02510 for(auto& record : records) 02511 __SUP_COUT__ << "recordList " << record << __E__; 02512 } 02513 } 02514 02515 // loop through each field and get unique values among records 02516 for(auto& field : fieldsToGet) 02517 { 02518 __SUP_COUT__ << "fieldsToGet " << field << __E__; 02519 02520 DOMElement* parentEl = xmlOut.addTextElementToData("field", field); 02521 02522 // use set to force sorted unique values 02523 std::set<std::string /*unique-values*/> uniqueValues; 02524 02525 uniqueValues = 02526 cfgMgr->getNode(startPath).getUniqueValuesForField(records, field); 02527 02528 for(auto& uniqueValue : uniqueValues) 02529 { 02530 __SUP_COUT__ << "uniqueValue " << uniqueValue << __E__; 02531 02532 xmlOut.addTextElementToParent("uniqueValue", uniqueValue, parentEl); 02533 } 02534 } 02535 } 02536 catch(std::runtime_error& e) 02537 { 02538 __SUP_SS__ << ("Error getting common fields!\n\n" + std::string(e.what())) 02539 << __E__; 02540 __SUP_COUT_ERR__ << "\n" << ss.str(); 02541 xmlOut.addTextElementToData("Error", ss.str()); 02542 } 02543 catch(...) 02544 { 02545 __SUP_SS__ << ("Error getting common fields!\n\n") << __E__; 02546 __SUP_COUT_ERR__ << "\n" << ss.str(); 02547 xmlOut.addTextElementToData("Error", ss.str()); 02548 } 02549 } 02550 02551 //======================================================================================================================== 02552 // handleFillTreeViewXML 02553 // returns xml tree from path for given depth 02554 // 02555 // if groupName == "" || groupKey is invalid 02556 // then return tree for active groups 02557 // 02558 // parameters 02559 // configGroupName (full name with key) 02560 // starting node path 02561 // depth from starting node path 02562 // modifiedTables := CSV of table/version pairs 02563 // filterList := relative-to-record-path=value(,value,...);path=value... filtering 02564 // records with relative path not meeting all filter criteria 02565 // - can accept multiple values per field (values separated by commas) (i.e. OR) 02566 // - fields/value pairs separated by ; for AND 02567 // - Note: limitation here is there is no OR among fields/value pairs (in future, 02568 // could separate field/value pairs by : for OR) e.g. 02569 //"LinkToFETypeTable=NIMPlus,TemplateUDP;FEInterfacePluginName=NIMPlusPlugin" 02570 // 02571 void ConfigurationGUISupervisor::handleFillTreeViewXML(HttpXmlDocument& xmlOut, 02572 ConfigurationManagerRW* cfgMgr, 02573 const std::string& groupName, 02574 const TableGroupKey& groupKey, 02575 const std::string& startPath, 02576 unsigned int depth, 02577 bool hideStatusFalse, 02578 const std::string& modifiedTables, 02579 const std::string& filterList) 02580 { 02581 // return xml 02582 // <groupName="groupName"/> 02583 // <tree="path"> 02584 // <node="..."> 02585 // <node="..."> 02586 // <node="..."> 02587 // <value="..."> 02588 // </node> 02589 // <node="..."> 02590 // <value="..."> 02591 // </node> 02592 // </node> 02593 // <node="..."> 02594 // <value=".."> 02595 // </node> 02596 // ... 02597 // </node> 02598 // </tree> 02599 02600 // return the startPath as root "tree" element 02601 // and then display all children if depth > 0 02602 02603 // Think about using this in the future to clean up the code 02604 // But may not work well since there is some special functionality used below 02605 // like getting group comments, and not reloading everything except for at root level 02607 // .... 02608 // // setup active tables based on input group and modified tables 02609 02610 bool usingActiveGroups = (groupName == "" || groupKey.isInvalid()); 02611 std::map<std::string /*name*/, TableVersion /*version*/> memberMap; 02612 02613 std::string accumulatedErrors = ""; 02614 try 02615 { 02616 setupActiveTablesXML( 02617 xmlOut, 02618 cfgMgr, 02619 groupName, 02620 groupKey, 02621 modifiedTables, 02622 (startPath == "/"), // refreshAll, if at root node, reload all 02623 // tables so that partially loaded tables are 02624 // not allowed 02625 (startPath == "/"), // get group info 02626 &memberMap, // get group member map 02627 true, // output active tables (default) 02628 &accumulatedErrors // accumulate errors 02629 ); 02630 } 02631 catch(const std::runtime_error& e) 02632 { 02633 __SS__ << "Error occured setting up active tables: " << e.what() << __E__; 02634 accumulatedErrors += ss.str(); 02635 } 02636 catch(...) 02637 { 02638 __SS__ << "Unknown error occured setting up active tables." << __E__; 02639 accumulatedErrors += ss.str(); 02640 } 02641 02642 if(accumulatedErrors != "") 02643 { 02644 xmlOut.addTextElementToData("Warning", accumulatedErrors); 02645 02646 __SUP_COUT__ << "Active tables are setup. Warning string: '" << accumulatedErrors 02647 << "'" << __E__; 02648 } 02649 else 02650 __SUP_COUT__ << "Active tables are setup. No issues found." << __E__; 02651 02652 try 02653 { 02654 DOMElement* parentEl = xmlOut.addTextElementToData("tree", startPath); 02655 02656 if(depth == 0) 02657 return; // already returned root node in itself 02658 02659 std::vector<std::pair<std::string, ConfigurationTree>> rootMap; 02660 02661 if(startPath == "/") 02662 { 02663 // then consider the configurationManager the root node 02664 02665 std::string accumulateTreeErrs; 02666 02667 if(usingActiveGroups) 02668 rootMap = cfgMgr->getChildren(0, &accumulateTreeErrs); 02669 else 02670 rootMap = cfgMgr->getChildren(&memberMap, &accumulateTreeErrs); 02671 02672 __SUP_COUT__ << "accumulateTreeErrs = " << accumulateTreeErrs << __E__; 02673 if(accumulateTreeErrs != "") 02674 xmlOut.addTextElementToData("TreeErrors", accumulateTreeErrs); 02675 } 02676 else 02677 { 02678 ConfigurationTree startNode = 02679 cfgMgr->getNode(startPath, true /*doNotThrowOnBrokenUIDLinks*/); 02680 if(startNode.isLinkNode() && startNode.isDisconnected()) 02681 { 02682 xmlOut.addTextElementToData("DisconnectedStartNode", "1"); 02683 return; // quietly ignore disconnected links at depth 02684 // note: at the root level they will be flagged for the user 02685 } 02686 02687 std::map<std::string /*relative-path*/, std::string /*value*/> filterMap; 02688 StringMacros::getMapFromString( 02689 filterList, 02690 filterMap, 02691 std::set<char>({';'}) /*pair delimiters*/, 02692 std::set<char>({'='}) /*name/value delimiters*/); 02693 02694 __COUTV__(StringMacros::mapToString(filterMap)); 02695 02696 rootMap = cfgMgr->getNode(startPath).getChildren(filterMap); 02697 } 02698 02699 for(auto& treePair : rootMap) 02700 recursiveTreeToXML( 02701 treePair.second, depth - 1, xmlOut, parentEl, hideStatusFalse); 02702 } 02703 catch(std::runtime_error& e) 02704 { 02705 __SUP_SS__ << "Error detected generating XML tree!\n\n " << e.what() << __E__; 02706 __SUP_COUT_ERR__ << "\n" << ss.str(); 02707 xmlOut.addTextElementToData("Error", ss.str()); 02708 } 02709 catch(...) 02710 { 02711 __SUP_SS__ << "Error detected generating XML tree!" << __E__; 02712 __SUP_COUT_ERR__ << "\n" << ss.str(); 02713 xmlOut.addTextElementToData("Error", ss.str()); 02714 } 02715 } 02716 02717 //============================================================================== 02718 // recursiveToXml 02719 // output tree to XML from this node for desired depth 02720 // depth of 0 means output only this node's value 02721 // depth of 1 means include this node's children's values, etc.. 02722 // depth of -1(unsigned int) effectively means output full tree 02723 void ConfigurationGUISupervisor::recursiveTreeToXML(const ConfigurationTree& t, 02724 unsigned int depth, 02725 HttpXmlDocument& xmlOut, 02726 DOMElement* parentEl, 02727 bool hideStatusFalse) 02728 { 02729 //__COUT__ << t.getValueAsString() << __E__; 02730 02731 if(t.isValueNode()) 02732 { 02733 parentEl = xmlOut.addTextElementToParent("node", t.getValueName(), parentEl); 02734 xmlOut.addTextElementToParent("value", t.getValueAsString(), parentEl); 02735 parentEl = xmlOut.addTextElementToParent("valueType", t.getValueType(), parentEl); 02736 02737 // fixed choice and bitmap both use fixed choices strings 02738 // so output them to xml 02739 if(t.getValueType() == TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA || 02740 t.getValueType() == TableViewColumnInfo::TYPE_BITMAP_DATA) 02741 { 02742 //__COUT__ << t.getValueType() << __E__; 02743 02744 std::vector<std::string> choices = t.getFixedChoices(); 02745 for(const auto& choice : choices) 02746 xmlOut.addTextElementToParent("fixedChoice", choice, parentEl); 02747 } 02748 } 02749 else 02750 { 02751 if(t.isLinkNode()) 02752 { 02753 //__COUT__ << t.getValueName() << __E__; 02754 02755 // Note: The order of xml fields is required by JavaScript, so do NOT change 02756 // order. 02757 parentEl = xmlOut.addTextElementToParent("node", t.getValueName(), parentEl); 02758 02759 if(t.isDisconnected()) 02760 { 02761 __COUT__ << t.getValueName() << __E__; 02762 02763 // xmlOut.addTextElementToParent("value", t.getValueAsString(), parentEl); 02764 // xmlOut.addTextElementToParent("DisconnectedLink", t.getValueAsString(), 02765 // parentEl); 02766 02767 xmlOut.addTextElementToParent("valueType", t.getValueType(), parentEl); 02768 02769 // add extra fields for disconnected link 02770 xmlOut.addTextElementToParent( 02771 (t.isGroupLinkNode() ? "Group" : "U") + std::string("ID"), 02772 t.getDisconnectedLinkID(), 02773 parentEl); 02774 xmlOut.addTextElementToParent( 02775 "LinkTableName", t.getDisconnectedTableName(), parentEl); 02776 xmlOut.addTextElementToParent( 02777 "LinkIndex", t.getChildLinkIndex(), parentEl); 02778 02779 // add fixed choices (in case link has them) 02780 DOMElement* choicesParentEl = 02781 xmlOut.addTextElementToParent("fixedChoices", "", parentEl); 02782 // try 02783 //{ 02784 02785 std::vector<std::string> choices = t.getFixedChoices(); 02786 __COUT__ << "choices.size() " << choices.size() << __E__; 02787 02788 for(const auto& choice : choices) 02789 xmlOut.addTextElementToParent("fixedChoice", choice, choicesParentEl); 02790 //} 02791 // catch(...) 02792 //{ 02793 // __COUT__ << "Ignoring unknown fixed choice error" 02794 //} //ignore no fixed choices for disconnected 02795 02796 return; 02797 } 02798 02799 // handle connected links 02800 02801 xmlOut.addTextElementToParent( 02802 (t.isGroupLinkNode() ? "Group" : "U") + std::string("ID"), 02803 t.getValueAsString(), 02804 parentEl); 02805 02806 xmlOut.addTextElementToParent("LinkTableName", t.getTableName(), parentEl); 02807 02808 xmlOut.addTextElementToParent("LinkIndex", t.getChildLinkIndex(), parentEl); 02809 02810 // add fixed choices (in case link has them) 02811 { 02812 DOMElement* choicesParentEl = 02813 xmlOut.addTextElementToParent("fixedChoices", "", parentEl); 02814 std::vector<std::string> choices = t.getFixedChoices(); 02815 02816 //__COUT__ << "choices.size() " << choices.size() << __E__; 02817 02818 for(const auto& choice : choices) 02819 xmlOut.addTextElementToParent("fixedChoice", choice, choicesParentEl); 02820 } 02821 } 02822 else // uid node 02823 { 02824 bool returnNode = true; // default to shown 02825 02826 if(hideStatusFalse) // only show if status evaluates to true 02827 { 02828 try // try to get Status child as boolean.. 02829 { // if Status bool doesn't exist exception will be thrown 02830 t.getNode(TableViewColumnInfo::COL_NAME_STATUS).getValue(returnNode); 02831 } 02832 catch(...) 02833 { 02834 } 02835 } 02836 02837 if(returnNode) 02838 parentEl = 02839 xmlOut.addTextElementToParent("node", t.getValueAsString(), parentEl); 02840 else 02841 return; // done.. no further depth needed for node that is not shown 02842 } 02843 02844 // if depth>=1 toXml all children 02845 // child.toXml(depth-1) 02846 if(depth >= 1) 02847 { 02848 auto C = t.getChildren(); 02849 for(auto& c : C) 02850 recursiveTreeToXML( 02851 c.second, depth - 1, xmlOut, parentEl, hideStatusFalse); 02852 } 02853 } 02854 } 02855 02856 //======================================================================================================================== 02857 // handleGetLinkToChoicesXML 02858 // return all possible choices for link 02859 // linkIdType = "UID" or "GroupID" 02860 // 02861 // as xml: 02862 // <linkToChoice = xxx> 02863 void ConfigurationGUISupervisor::handleGetLinkToChoicesXML( 02864 HttpXmlDocument& xmlOut, 02865 ConfigurationManagerRW* cfgMgr, 02866 const std::string& linkToTableName, 02867 const TableVersion& linkToTableVersion, 02868 const std::string& linkIdType, 02869 const std::string& linkIndex, 02870 const std::string& linkInitId) try 02871 { 02872 // get table 02873 // if uid link 02874 // return all uids 02875 // if groupid link 02876 // find target column 02877 // create the set of values (unique values only) 02878 // note: insert group unions individually (i.e. groups | separated) 02879 02880 // get table and activate target version 02881 // rename to re-use code template 02882 const std::string& tableName = linkToTableName; 02883 const TableVersion& version = linkToTableVersion; 02884 TableBase* config = cfgMgr->getTableByName(tableName); 02885 try 02886 { 02887 config->setActiveView(version); 02888 } 02889 catch(...) 02890 { 02891 __SUP_COUT__ << "Failed to find stored version, so attempting to load version: " 02892 << version << __E__; 02893 cfgMgr->getVersionedTableByName(tableName, version); 02894 } 02895 02896 if(version != config->getViewVersion()) 02897 { 02898 __SUP_SS__ << "Target table version (" << version 02899 << ") is not the currently active version (" 02900 << config->getViewVersion() << ". Try refreshing the tree." << __E__; 02901 __SUP_COUT_WARN__ << ss.str(); 02902 __SS_THROW__; 02903 } 02904 02905 __SUP_COUT__ << "Active version is " << config->getViewVersion() << __E__; 02906 02907 if(linkIdType == "UID") 02908 { 02909 // give all UIDs 02910 unsigned int col = config->getView().getColUID(); 02911 for(unsigned int row = 0; row < config->getView().getNumberOfRows(); ++row) 02912 xmlOut.addTextElementToData("linkToChoice", 02913 config->getView().getDataView()[row][col]); 02914 } 02915 else if(linkIdType == "GroupID") 02916 { 02917 // find target column 02918 // create the set of values (unique values only) 02919 // note: insert group unions individually (i.e. groups | separated) 02920 02921 __SUP_COUTV__(linkIndex); 02922 __SUP_COUTV__(linkInitId); 02923 02924 std::set<std::string> setOfGroupIDs = 02925 config->getView().getSetOfGroupIDs(linkIndex); 02926 02927 // build list of groupids 02928 // always include initial link group id in choices 02929 // (even if not in set of group ids) 02930 bool foundInitId = false; 02931 for(const auto& groupID : setOfGroupIDs) 02932 { 02933 if(!foundInitId && linkInitId == groupID) 02934 foundInitId = true; // mark init id found 02935 02936 xmlOut.addTextElementToData("linkToChoice", groupID); 02937 } 02938 // if init id was not found, add to list 02939 if(!foundInitId) 02940 xmlOut.addTextElementToData("linkToChoice", linkInitId); 02941 02942 // give all UIDs 02943 unsigned int col = config->getView().getColUID(); 02944 for(unsigned int row = 0; row < config->getView().getNumberOfRows(); ++row) 02945 { 02946 xmlOut.addTextElementToData("groupChoice", 02947 config->getView().getDataView()[row][col]); 02948 if(config->getView().isEntryInGroup(row, linkIndex, linkInitId)) 02949 xmlOut.addTextElementToData("groupMember", 02950 config->getView().getDataView()[row][col]); 02951 } 02952 } 02953 else 02954 { 02955 __SUP_SS__ << "Unrecognized linkIdType '" << linkIdType << ".'" << __E__; 02956 __SS_THROW__; 02957 } 02958 } 02959 catch(std::runtime_error& e) 02960 { 02961 __SUP_SS__ << "Error detected saving tree node!\n\n " << e.what() << __E__; 02962 __SUP_COUT_ERR__ << "\n" << ss.str() << __E__; 02963 xmlOut.addTextElementToData("Error", ss.str()); 02964 } 02965 catch(...) 02966 { 02967 __SUP_SS__ << "Error detected saving tree node!\n\n " << __E__; 02968 __SUP_COUT_ERR__ << "\n" << ss.str() << __E__; 02969 xmlOut.addTextElementToData("Error", ss.str()); 02970 } 02971 02972 //======================================================================================================================== 02973 // handleMergeGroupsXML 02974 void ConfigurationGUISupervisor::handleMergeGroupsXML( 02975 HttpXmlDocument& xmlOut, 02976 ConfigurationManagerRW* cfgMgr, 02977 const std::string& groupANameContext, 02978 const TableGroupKey& groupAKeyContext, 02979 const std::string& groupBNameContext, 02980 const TableGroupKey& groupBKeyContext, 02981 const std::string& groupANameConfig, 02982 const TableGroupKey& groupAKeyConfig, 02983 const std::string& groupBNameConfig, 02984 const TableGroupKey& groupBKeyConfig, 02985 const std::string& author, 02986 const std::string& mergeApproach) try 02987 { 02988 __SUP_COUT__ << "Merging context group pair " << groupANameContext << " (" 02989 << groupAKeyContext << ") & " << groupBNameContext << " (" 02990 << groupBKeyContext << ") and config group pair " << groupANameConfig 02991 << " (" << groupAKeyConfig << ") & " << groupBNameConfig << " (" 02992 << groupBKeyConfig << ") with approach '" << mergeApproach << __E__; 02993 02994 // Merges group A and group B 02995 // with consideration for UID conflicts 02996 // Result is a new key of group A's name 02997 // 02998 // There 3 modes: 02999 // Rename -- All records from both groups are maintained, but conflicts from B 03000 // are renamed. 03001 // Must maintain a map of UIDs that are remapped to new name for 03002 // groupB, because linkUID fields must be preserved. Replace -- 03003 // Any UID conflicts for a record are replaced by the record from group B. 03004 // Skip -- Any UID conflicts for a record are skipped so that group A record 03005 // remains 03006 03007 // check valid mode 03008 if(!(mergeApproach == "Rename" || mergeApproach == "Replace" || 03009 mergeApproach == "Skip")) 03010 { 03011 __SS__ << "Error! Invalid merge approach '" << mergeApproach << ".'" << __E__; 03012 __SS_THROW__; 03013 } 03014 03015 std::map<std::string /*name*/, TableVersion /*version*/> memberMapAContext, 03016 memberMapBContext, memberMapAConfig, memberMapBConfig; 03017 03018 // check if skipping group pairs 03019 bool skippingContextPair = false; 03020 bool skippingConfigPair = false; 03021 if(groupANameContext.size() == 0 || groupANameContext[0] == ' ' || 03022 groupBNameContext.size() == 0 || groupBNameContext[0] == ' ') 03023 { 03024 skippingContextPair = true; 03025 __SUP_COUTV__(skippingContextPair); 03026 } 03027 if(groupANameConfig.size() == 0 || groupANameConfig[0] == ' ' || 03028 groupBNameConfig.size() == 0 || groupBNameConfig[0] == ' ') 03029 { 03030 skippingConfigPair = true; 03031 __SUP_COUTV__(skippingConfigPair); 03032 } 03033 03034 // get context group member maps 03035 if(!skippingContextPair) 03036 { 03037 cfgMgr->loadTableGroup(groupANameContext, 03038 groupAKeyContext, 03039 false /*doActivate*/, 03040 &memberMapAContext, 03041 0 /*progressBar*/, 03042 0 /*accumulateErrors*/, 03043 0 /*groupComment*/, 03044 0 /*groupAuthor*/, 03045 0 /*groupCreationTime*/, 03046 false /*doNotLoadMember*/, 03047 0 /*groupTypeString*/ 03048 ); 03049 __SUP_COUTV__(StringMacros::mapToString(memberMapAContext)); 03050 03051 cfgMgr->loadTableGroup(groupBNameContext, 03052 groupBKeyContext, 03053 false /*doActivate*/, 03054 &memberMapBContext, 03055 0 /*progressBar*/, 03056 0 /*accumulateErrors*/, 03057 0 /*groupComment*/, 03058 0 /*groupAuthor*/, 03059 0 /*groupCreationTime*/, 03060 false /*doNotLoadMember*/, 03061 0 /*groupTypeString*/ 03062 ); 03063 03064 __SUP_COUTV__(StringMacros::mapToString(memberMapBContext)); 03065 } 03066 03067 // get config group member maps 03068 if(!skippingConfigPair) 03069 { 03070 cfgMgr->loadTableGroup(groupANameConfig, 03071 groupAKeyConfig, 03072 false /*doActivate*/, 03073 &memberMapAConfig, 03074 0 /*progressBar*/, 03075 0 /*accumulateErrors*/, 03076 0 /*groupComment*/, 03077 0 /*groupAuthor*/, 03078 0 /*groupCreationTime*/, 03079 false /*doNotLoadMember*/, 03080 0 /*groupTypeString*/ 03081 ); 03082 __SUP_COUTV__(StringMacros::mapToString(memberMapAConfig)); 03083 03084 cfgMgr->loadTableGroup(groupBNameConfig, 03085 groupBKeyConfig, 03086 false /*doActivate*/, 03087 &memberMapBConfig, 03088 0 /*progressBar*/, 03089 0 /*accumulateErrors*/, 03090 0 /*groupComment*/, 03091 0 /*groupAuthor*/, 03092 0 /*groupCreationTime*/, 03093 false /*doNotLoadMember*/, 03094 0 /*groupTypeString*/ 03095 ); 03096 03097 __SUP_COUTV__(StringMacros::mapToString(memberMapBConfig)); 03098 } 03099 03100 // for each member of B 03101 // if not found in A member map, add it 03102 // if found in both member maps, and versions are different, load both tables and 03103 // merge 03104 03105 std::map<std::pair<std::string /*original table*/, std::string /*original uidB*/>, 03106 std::string /*converted uidB*/> 03107 uidConversionMap; 03108 std::map< 03109 std::pair<std::string /*original table*/, 03110 std::pair<std::string /*group linkid*/, std::string /*original gidB*/>>, 03111 std::string /*converted gidB*/> 03112 groupidConversionMap; 03113 03114 // first loop create record conversion map, second loop implement merge (using 03115 // conversion map if Rename) 03116 for(unsigned int i = 0; i < 2; ++i) 03117 { 03118 if(i == 0 && mergeApproach != "Rename") 03119 continue; // only need to construct uidConversionMap for rename approach 03120 03121 // loop for context and config pair types 03122 for(unsigned int j = 0; j < 2; ++j) 03123 { 03124 if(j == 0 && skippingContextPair) // context 03125 { 03126 __COUT__ << "Skipping context pair..." << __E__; 03127 continue; 03128 } 03129 else if(j == 1 && skippingConfigPair) 03130 { 03131 __COUT__ << "Skipping config pair..." << __E__; 03132 continue; 03133 } 03134 03135 std::map<std::string /*name*/, TableVersion /*version*/>& memberMapAref = 03136 j == 0 ? memberMapAContext : memberMapAConfig; 03137 03138 std::map<std::string /*name*/, TableVersion /*version*/>& memberMapBref = 03139 j == 0 ? memberMapBContext : memberMapBConfig; 03140 03141 if(j == 0) // context 03142 __COUT__ << "Context pair..." << __E__; 03143 else 03144 __COUT__ << "Config pair..." << __E__; 03145 03146 __COUT__ << "Starting member map B scan." << __E__; 03147 for(const auto bkey : memberMapBref) 03148 { 03149 __SUP_COUTV__(bkey.first); 03150 03151 if(memberMapAref.find(bkey.first) == memberMapAref.end()) 03152 { 03153 // not found, so add to A member map 03154 memberMapAref[bkey.first] = bkey.second; 03155 } 03156 else if(memberMapAref[bkey.first] != bkey.second) 03157 { 03158 // found table version confict 03159 __SUP_COUTV__(memberMapAref[bkey.first]); 03160 __SUP_COUTV__(bkey.second); 03161 03162 // load both tables, and merge 03163 TableBase* config = cfgMgr->getTableByName(bkey.first); 03164 03165 __SUP_COUT__ << "Got table." << __E__; 03166 03167 TableVersion newVersion = config->mergeViews( 03168 cfgMgr 03169 ->getVersionedTableByName(bkey.first, 03170 memberMapAref[bkey.first]) 03171 ->getView(), 03172 cfgMgr->getVersionedTableByName(bkey.first, bkey.second) 03173 ->getView(), 03174 TableVersion() /* destinationVersion*/, 03175 author, 03176 mergeApproach /*Rename,Replace,Skip*/, 03177 uidConversionMap, 03178 groupidConversionMap, 03179 i == 0 /* fillRecordConversionMaps */, 03180 i == 1 /* applyRecordConversionMaps */, 03181 config->getTableName() == 03182 ConfigurationManager:: 03183 XDAQ_APPLICATION_TABLE_NAME /* generateUniqueDataColumns 03184 */ 03185 ); // dont make destination version the first time 03186 03187 if(i == 1) 03188 { 03189 __SUP_COUTV__(newVersion); 03190 03191 try 03192 { 03193 // save all temporary tables to persistent tables 03194 // finish off the version creation 03195 newVersion = saveModifiedVersionXML( 03196 xmlOut, 03197 cfgMgr, 03198 bkey.first, 03199 TableVersion() /*original source version*/, 03200 false /* makeTemporary */, 03201 config, 03202 newVersion /*temporary modified version*/, 03203 false /*ignore duplicates*/, 03204 true /*look for equivalent*/); 03205 } 03206 catch(std::runtime_error& e) 03207 { 03208 __SUP_SS__ 03209 << "There was an error saving the '" 03210 << config->getTableName() 03211 << "' merge result to a persistent table version. " 03212 << "Perhaps you can modify this table in one of the " 03213 "groups to resolve this issue, and then re-merge." 03214 << __E__ << e.what(); 03215 __SS_THROW__; 03216 } 03217 03218 __SUP_COUTV__(newVersion); 03219 03220 memberMapAref[bkey.first] = newVersion; 03221 } 03222 } // end member version conflict handling 03223 } // end B member map loop 03224 } // end context and config loop 03225 } // end top level conversion map or not loop 03226 03227 // Now save groups 03228 03229 if(!skippingContextPair) 03230 { 03231 __SUP_COUT__ << "New context member map complete." << __E__; 03232 __SUP_COUTV__(StringMacros::mapToString(memberMapAContext)); 03233 03234 // save the new table group 03235 TableGroupKey newKeyContext = cfgMgr->saveNewTableGroup( 03236 groupANameContext, 03237 memberMapAContext, 03238 "Merger of group " + groupANameContext + " (" + groupAKeyContext.toString() + 03239 ") and " + groupBNameContext + " (" + groupBKeyContext.toString() + ")."); 03240 03241 // return new resulting group 03242 xmlOut.addTextElementToData("ContextGroupName", groupANameContext); 03243 xmlOut.addTextElementToData("ContextGroupKey", newKeyContext.toString()); 03244 } 03245 if(!skippingConfigPair) 03246 { 03247 __SUP_COUT__ << "New config member map complete." << __E__; 03248 __SUP_COUTV__(StringMacros::mapToString(memberMapAConfig)); 03249 03250 // save the new table group 03251 TableGroupKey newKeyConfig = cfgMgr->saveNewTableGroup( 03252 groupANameConfig, 03253 memberMapAConfig, 03254 "Merger of group " + groupANameConfig + " (" + groupAKeyConfig.toString() + 03255 ") and " + groupBNameConfig + " (" + groupBKeyConfig.toString() + ")."); 03256 03257 // return new resulting group 03258 xmlOut.addTextElementToData("ConfigGroupName", groupANameConfig); 03259 xmlOut.addTextElementToData("ConfigGroupKey", newKeyConfig.toString()); 03260 } 03261 03262 } // end handleMergeGroupsXML 03263 catch(std::runtime_error& e) 03264 { 03265 __SUP_SS__ << "Error merging context group pair " << groupANameContext << " (" 03266 << groupAKeyContext << ") & " << groupBNameContext << " (" 03267 << groupBKeyContext << ") and config group pair " << groupANameConfig 03268 << " (" << groupAKeyConfig << ") & " << groupBNameConfig << " (" 03269 << groupBKeyConfig << ") with approach '" << mergeApproach << "': \n\n" 03270 << e.what() << __E__; 03271 __SUP_COUT_ERR__ << "\n" << ss.str() << __E__; 03272 xmlOut.addTextElementToData("Error", ss.str()); 03273 } 03274 catch(...) 03275 { 03276 __SUP_SS__ << "Unknown error merging context group pair " << groupANameContext << " (" 03277 << groupAKeyContext << ") & " << groupBNameContext << " (" 03278 << groupBKeyContext << ") and config group pair " << groupANameConfig 03279 << " (" << groupAKeyConfig << ") & " << groupBNameConfig << " (" 03280 << groupBKeyConfig << ") with approach '" << mergeApproach << ".' \n\n"; 03281 __SUP_COUT_ERR__ << "\n" << ss.str() << __E__; 03282 xmlOut.addTextElementToData("Error", ss.str()); 03283 } 03284 03285 //======================================================================================================================== 03286 // handleSavePlanCommandSequenceXML 03287 void ConfigurationGUISupervisor::handleSavePlanCommandSequenceXML( 03288 HttpXmlDocument& xmlOut, 03289 ConfigurationManagerRW* cfgMgr, 03290 const std::string& groupName, 03291 const TableGroupKey& groupKey, 03292 const std::string& modifiedTables, 03293 const std::string& author, 03294 const std::string& planName, 03295 const std::string& commandString) try 03296 { 03297 __MOUT__ << "handleSavePlanCommandSequenceXML" << __E__; 03298 03299 // setup active tables based on input group and modified tables 03300 setupActiveTablesXML(xmlOut, 03301 cfgMgr, 03302 groupName, 03303 groupKey, 03304 modifiedTables, 03305 true /* refresh all */, 03306 false /* getGroupInfo */, 03307 0 /* returnMemberMap */, 03308 false /* outputActiveTables */); 03309 03310 TableEditStruct planTable(IterateTable::PLAN_TABLE, 03311 cfgMgr); // Table ready for editing! 03312 TableEditStruct targetTable(IterateTable::TARGET_TABLE, 03313 cfgMgr); // Table ready for editing! 03314 03315 // create table-edit struct for each iterate command type 03316 std::map<std::string, TableEditStruct> commandTypeToCommandTableMap; 03317 for(const auto& commandPair : IterateTable::commandToTableMap_) 03318 if(commandPair.second != "") // skip tables with no parameters 03319 commandTypeToCommandTableMap.emplace(std::pair<std::string, TableEditStruct>( 03320 commandPair.first, TableEditStruct(commandPair.second, cfgMgr))); 03321 03322 // try to catch any errors while editing.. 03323 // if errors delete temporary plan view (if created here) 03324 try 03325 { 03326 // Steps: 03327 // Reset plan commands 03328 // Remove all commands in group "<plan>-Plan" 03329 // Delete linked command parameters row (in separate table) 03330 // If no group remaining, then delete row. 03331 // 03332 // Save plan commands (if modified) 03333 // Create rows and add them to group "<plan>-Plan" 03334 // create row for command paramaters and add to proper table 03335 03336 std::string groupName = planName + "-Plan"; 03337 __SUP_COUT__ << "Handling commands for group " << groupName << __E__; 03338 03339 unsigned int groupIdCol = 03340 planTable.tableView_->findCol(IterateTable::planTableCols_.GroupID_); 03341 unsigned int cmdTypeCol = 03342 planTable.tableView_->findCol(IterateTable::planTableCols_.CommandType_); 03343 03344 unsigned int targetGroupIdCol = 03345 targetTable.tableView_->findCol(IterateTable::targetCols_.GroupID_); 03346 unsigned int targetTableCol = 03347 targetTable.tableView_->findCol(IterateTable::targetCols_.TargetLink_); 03348 unsigned int targetUIDCol = 03349 targetTable.tableView_->findCol(IterateTable::targetCols_.TargetLinkUID_); 03350 03351 std::string groupLinkIndex = 03352 planTable.tableView_->getColumnInfo(groupIdCol).getChildLinkIndex(); 03353 __SUP_COUT__ << "groupLinkIndex: " << groupLinkIndex << __E__; 03354 03355 std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> commandUidLink; 03356 { 03357 bool isGroup; // local because we know is uid link 03358 planTable.tableView_->getChildLink( 03359 planTable.tableView_->findCol(IterateTable::planTableCols_.CommandLink_), 03360 isGroup, 03361 commandUidLink); 03362 } 03363 03364 unsigned int cmdRow, cmdCol; 03365 std::string targetGroupName; 03366 03367 // Reset existing plan commands 03368 { 03369 std::string targetUID, cmdType; 03370 03371 for(unsigned int row = 0; row < planTable.tableView_->getNumberOfRows(); 03372 ++row) 03373 { 03374 targetUID = planTable.tableView_ 03375 ->getDataView()[row][planTable.tableView_->getColUID()]; 03376 __SUP_COUT__ << "targetUID: " << targetUID << __E__; 03377 03378 // remove command from plan group.. if no more groups, delete 03379 if(planTable.tableView_->isEntryInGroup(row, groupLinkIndex, groupName)) 03380 { 03381 __SUP_COUT__ << "Removing." << __E__; 03382 03383 // delete linked command 03384 // find linked UID in table (mapped by type) 03385 cmdType = planTable.tableView_->getDataView()[row][cmdTypeCol]; 03386 if(commandTypeToCommandTableMap.find(cmdType) != 03387 commandTypeToCommandTableMap 03388 .end()) // skip if invalid command type 03389 { 03390 cmdRow = 03391 commandTypeToCommandTableMap[cmdType].tableView_->findRow( 03392 commandTypeToCommandTableMap[cmdType] 03393 .tableView_->getColUID(), 03394 planTable.tableView_ 03395 ->getDataView()[row][commandUidLink.second]); 03396 03397 // before deleting row... 03398 // look for target group 03399 // remove all targets in group 03400 try 03401 { 03402 cmdCol = 03403 commandTypeToCommandTableMap[cmdType].tableView_->findCol( 03404 IterateTable::commandTargetCols_.TargetsLinkGroupID_); 03405 targetGroupName = 03406 commandTypeToCommandTableMap[cmdType] 03407 .tableView_->getDataView()[cmdRow][cmdCol]; 03408 03409 for(unsigned int trow = 0; 03410 trow < targetTable.tableView_->getNumberOfRows(); 03411 ++trow) 03412 { 03413 // remove command from target group.. 03414 if(targetTable.tableView_->isEntryInGroup( 03415 trow, 03416 commandTypeToCommandTableMap[cmdType] 03417 .tableView_->getColumnInfo(cmdCol) 03418 .getChildLinkIndex(), 03419 targetGroupName)) 03420 { 03421 __SUP_COUT__ << "Removing target." << __E__; 03422 // remove command entry in plan table 03423 if(targetTable.tableView_->removeRowFromGroup( 03424 trow, 03425 targetGroupIdCol, 03426 targetGroupName, 03427 true /*deleteRowIfNoGroup*/)) 03428 --trow; // since row was deleted, go back! 03429 } 03430 } 03431 } 03432 catch(...) 03433 { 03434 __SUP_COUT__ << "No targets." << __E__; 03435 } 03436 03437 // now no more targets, delete row 03438 03439 commandTypeToCommandTableMap[cmdType].tableView_->deleteRow( 03440 cmdRow); 03441 03442 commandTypeToCommandTableMap[cmdType].modified_ = true; 03443 } 03444 03445 // remove command entry in plan table 03446 if(planTable.tableView_->removeRowFromGroup( 03447 row, groupIdCol, groupName, true /*deleteRowIfNoGroup*/)) 03448 --row; // since row was deleted, go back! 03449 } 03450 } 03451 } 03452 03453 // Done resetting existing plan 03454 // Now save new commands 03455 03456 std::vector<IterateTable::Command> commands; 03457 03458 // extract command sequence and add to table 03459 // into vector with type, and params 03460 { 03461 std::istringstream f(commandString); 03462 std::string commandSubString, paramSubString, paramValue; 03463 int i; 03464 while(getline(f, commandSubString, ';')) 03465 { 03466 //__SUP_COUT__ << "commandSubString " << commandSubString << __E__; 03467 std::istringstream g(commandSubString); 03468 03469 i = 0; 03470 while(getline(g, paramSubString, ',')) 03471 { 03472 //__SUP_COUT__ << "paramSubString " << paramSubString << __E__; 03473 if(i == 0) // type 03474 { 03475 if(paramSubString != "type") 03476 { 03477 __SUP_SS__ << "Invalid command sequence" << __E__; 03478 __SS_THROW__; 03479 } 03480 // create command object 03481 commands.push_back(IterateTable::Command()); 03482 03483 getline(g, paramValue, ','); 03484 ++i; 03485 //__SUP_COUT__ << "paramValue " << paramValue << __E__; 03486 commands.back().type_ = paramValue; 03487 } 03488 else // params 03489 { 03490 getline(g, paramValue, ','); 03491 ++i; 03492 //__SUP_COUT__ << "paramValue " << paramValue << __E__; 03493 03494 commands.back().params_.emplace( 03495 std::pair<std::string /*param name*/, 03496 std::string /*param value*/>( 03497 paramSubString, 03498 StringMacros::decodeURIComponent(paramValue))); 03499 } 03500 03501 ++i; 03502 } 03503 } 03504 03505 } // end extract command sequence 03506 03507 __SUP_COUT__ << "commands size " << commands.size() << __E__; 03508 03509 // at this point, have extracted commands 03510 03511 // now save commands to plan group 03512 // group should be "<plan>-Plan" 03513 03514 unsigned int row, tgtRow; 03515 unsigned int targetIndex; 03516 std::string targetStr, cmdUID; 03517 03518 for(auto& command : commands) 03519 { 03520 __SUP_COUT__ << "command " << command.type_ << __E__; 03521 __SUP_COUT__ << "table " << IterateTable::commandToTableMap_.at(command.type_) 03522 << __E__; 03523 03524 // create command entry at plan level 03525 row = planTable.tableView_->addRow(author, "planCommand"); 03526 planTable.tableView_->addRowToGroup(row, groupIdCol, groupName); 03527 03528 // set command type 03529 planTable.tableView_->setURIEncodedValue(command.type_, row, cmdTypeCol); 03530 03531 // set command status true 03532 planTable.tableView_->setValueAsString( 03533 "1", row, planTable.tableView_->getColStatus()); 03534 03535 // create command specifics 03536 if(commandTypeToCommandTableMap.find(command.type_) != 03537 commandTypeToCommandTableMap.end()) // if table exists in map! (some 03538 // commands may have no parameters) 03539 { 03540 __SUP_COUT__ << "table " 03541 << commandTypeToCommandTableMap[command.type_].tableName_ 03542 << __E__; 03543 03544 // at this point have config, tempVersion, and createdFlag 03545 03546 // create command parameter entry at command level 03547 cmdRow = commandTypeToCommandTableMap[command.type_].tableView_->addRow( 03548 author, true /*incrementUniqueData*/, command.type_ + "_COMMAND_"); 03549 03550 // parameters are linked 03551 // now set value of all parameters 03552 // find parameter column, and set value 03553 // if special target parameter, extract targets 03554 for(auto& param : command.params_) 03555 { 03556 __SUP_COUT__ << "\t param " << param.first << " : " << param.second 03557 << __E__; 03558 03559 if(param.first == IterateTable::targetParams_.Tables_) 03560 { 03561 __SUP_COUT__ << "\t\t found target tables" << __E__; 03562 std::istringstream f(param.second); 03563 03564 targetIndex = 0; 03565 while(getline(f, targetStr, '=')) 03566 { 03567 __SUP_COUT__ << "\t\t targetStr = " << targetStr << __E__; 03568 if(!command.targets_.size() || 03569 command.targets_.back().table_ != "") 03570 { 03571 __SUP_COUT__ << "\t\t make targetStr = " << targetStr 03572 << __E__; 03573 // make new target 03574 command.addTarget(); 03575 command.targets_.back().table_ = targetStr; 03576 } 03577 else // file existing target 03578 command.targets_[targetIndex++].table_ = targetStr; 03579 } 03580 03581 continue; // go to next parameter 03582 } 03583 03584 if(param.first == IterateTable::targetParams_.UIDs_) 03585 { 03586 __SUP_COUT__ << "\t\t found target UIDs" << __E__; 03587 std::istringstream f(param.second); 03588 03589 targetIndex = 0; 03590 while(getline(f, targetStr, '=')) 03591 { 03592 __SUP_COUT__ << "\t\t targetStr = " << targetStr << __E__; 03593 if(!command.targets_.size() || 03594 command.targets_.back().UID_ != "") 03595 { 03596 __SUP_COUT__ << "\t\t make targetStr = " << targetStr 03597 << __E__; 03598 // make new target 03599 command.addTarget(); 03600 command.targets_.back().UID_ = targetStr; 03601 } 03602 else // file existing target 03603 command.targets_[targetIndex++].UID_ = targetStr; 03604 } 03605 continue; 03606 } 03607 03608 cmdCol = 03609 commandTypeToCommandTableMap[command.type_].tableView_->findCol( 03610 param.first); 03611 03612 __SUP_COUT__ << "param col " << cmdCol << __E__; 03613 03614 commandTypeToCommandTableMap[command.type_] 03615 .tableView_->setURIEncodedValue(param.second, cmdRow, cmdCol); 03616 } // end parameter loop 03617 03618 cmdUID = 03619 commandTypeToCommandTableMap[command.type_].tableView_->getDataView() 03620 [cmdRow][commandTypeToCommandTableMap[command.type_] 03621 .tableView_->getColUID()]; 03622 03623 if(command.targets_.size()) 03624 { 03625 // if targets, create group in target table 03626 03627 __SUP_COUT__ << "targets found for command UID=" << cmdUID << __E__; 03628 03629 // create link from command table to target 03630 cmdCol = 03631 commandTypeToCommandTableMap[command.type_].tableView_->findCol( 03632 IterateTable::commandTargetCols_.TargetsLink_); 03633 commandTypeToCommandTableMap[command.type_] 03634 .tableView_->setValueAsString( 03635 IterateTable::TARGET_TABLE, cmdRow, cmdCol); 03636 03637 cmdCol = 03638 commandTypeToCommandTableMap[command.type_].tableView_->findCol( 03639 IterateTable::commandTargetCols_.TargetsLinkGroupID_); 03640 commandTypeToCommandTableMap[command.type_] 03641 .tableView_->setValueAsString( 03642 cmdUID + "_Targets", cmdRow, cmdCol); 03643 03644 // create row(s) for each target in target table with correct groupID 03645 03646 for(const auto& target : command.targets_) 03647 { 03648 __SUP_COUT__ << target.table_ << " " << target.UID_ << __E__; 03649 03650 // create target entry in target table in group 03651 tgtRow = targetTable.tableView_->addRow(author, "commandTarget"); 03652 targetTable.tableView_->addRowToGroup( 03653 tgtRow, targetGroupIdCol, cmdUID + "_Targets"); 03654 03655 // set target table 03656 targetTable.tableView_->setValueAsString( 03657 target.table_, tgtRow, targetTableCol); 03658 03659 // set target UID 03660 targetTable.tableView_->setValueAsString( 03661 target.UID_, tgtRow, targetUIDCol); 03662 } 03663 } // end target handling 03664 03665 // add link at plan level to created UID 03666 planTable.tableView_->setValueAsString( 03667 commandTypeToCommandTableMap[command.type_].tableName_, 03668 row, 03669 commandUidLink.first); 03670 planTable.tableView_->setValueAsString( 03671 cmdUID, row, commandUidLink.second); 03672 03673 __SUP_COUT__ << "linked to uid = " << cmdUID << __E__; 03674 03675 commandTypeToCommandTableMap[command.type_].modified_ = true; 03676 } // done with command specifics 03677 03678 } // end command loop 03679 03680 // commands are created in the temporary tables 03681 // validate with init 03682 03683 planTable.tableView_->print(); 03684 planTable.tableView_->init(); // verify new table (throws runtime_errors) 03685 03686 __SUP_COUT__ << "requestType tables:" << __E__; 03687 03688 for(auto& modifiedConfig : commandTypeToCommandTableMap) 03689 { 03690 modifiedConfig.second.tableView_->print(); 03691 modifiedConfig.second.tableView_->init(); 03692 } 03693 03694 targetTable.tableView_->print(); 03695 targetTable.tableView_->init(); // verify new table (throws runtime_errors) 03696 03697 } // end try for plan 03698 catch(...) 03699 { 03700 __SUP_COUT__ << "Handling command table errors while saving. Erasing all newly " 03701 "created versions." 03702 << __E__; 03703 03704 // erase all temporary tables if created here 03705 03706 if(planTable.createdTemporaryVersion_) // if temporary version created here 03707 { 03708 __SUP_COUT__ << "Erasing temporary version " << planTable.tableName_ << "-v" 03709 << planTable.temporaryVersion_ << __E__; 03710 // erase with proper version management 03711 cfgMgr->eraseTemporaryVersion(planTable.tableName_, 03712 planTable.temporaryVersion_); 03713 } 03714 03715 if(targetTable.createdTemporaryVersion_) // if temporary version created here 03716 { 03717 __SUP_COUT__ << "Erasing temporary version " << targetTable.tableName_ << "-v" 03718 << targetTable.temporaryVersion_ << __E__; 03719 // erase with proper version management 03720 cfgMgr->eraseTemporaryVersion(targetTable.tableName_, 03721 targetTable.temporaryVersion_); 03722 } 03723 03724 for(auto& modifiedConfig : commandTypeToCommandTableMap) 03725 { 03726 if(modifiedConfig.second 03727 .createdTemporaryVersion_) // if temporary version created here 03728 { 03729 __SUP_COUT__ << "Erasing temporary version " 03730 << modifiedConfig.second.tableName_ << "-v" 03731 << modifiedConfig.second.temporaryVersion_ << __E__; 03732 // erase with proper version management 03733 cfgMgr->eraseTemporaryVersion(modifiedConfig.second.tableName_, 03734 modifiedConfig.second.temporaryVersion_); 03735 } 03736 } 03737 03738 throw; // re-throw 03739 } 03740 03741 // all edits are complete and tables verified 03742 // need to save all edits properly 03743 // if not modified, discard 03744 03745 TableVersion finalVersion = saveModifiedVersionXML( 03746 xmlOut, 03747 cfgMgr, 03748 planTable.tableName_, 03749 planTable.originalVersion_, 03750 true /*make temporary*/, 03751 planTable.table_, 03752 planTable.temporaryVersion_, 03753 true /*ignoreDuplicates*/); // save temporary version properly 03754 03755 __SUP_COUT__ << "Final plan version is " << planTable.tableName_ << "-v" 03756 << finalVersion << __E__; 03757 03758 finalVersion = saveModifiedVersionXML( 03759 xmlOut, 03760 cfgMgr, 03761 targetTable.tableName_, 03762 targetTable.originalVersion_, 03763 true /*make temporary*/, 03764 targetTable.table_, 03765 targetTable.temporaryVersion_, 03766 true /*ignoreDuplicates*/); // save temporary version properly 03767 03768 __SUP_COUT__ << "Final target version is " << targetTable.tableName_ << "-v" 03769 << finalVersion << __E__; 03770 03771 for(auto& modifiedConfig : commandTypeToCommandTableMap) 03772 { 03773 if(!modifiedConfig.second.modified_) 03774 { 03775 if(modifiedConfig.second 03776 .createdTemporaryVersion_) // if temporary version created here 03777 { 03778 __SUP_COUT__ << "Erasing unmodified temporary version " 03779 << modifiedConfig.second.tableName_ << "-v" 03780 << modifiedConfig.second.temporaryVersion_ << __E__; 03781 // erase with proper version management 03782 cfgMgr->eraseTemporaryVersion(modifiedConfig.second.tableName_, 03783 modifiedConfig.second.temporaryVersion_); 03784 } 03785 continue; 03786 } 03787 03788 finalVersion = saveModifiedVersionXML( 03789 xmlOut, 03790 cfgMgr, 03791 modifiedConfig.second.tableName_, 03792 modifiedConfig.second.originalVersion_, 03793 true /*make temporary*/, 03794 modifiedConfig.second.table_, 03795 modifiedConfig.second.temporaryVersion_, 03796 true /*ignoreDuplicates*/); // save temporary version properly 03797 03798 __SUP_COUT__ << "Final version is " << modifiedConfig.second.tableName_ << "-v" 03799 << finalVersion << __E__; 03800 } 03801 03802 handleFillModifiedTablesXML(xmlOut, cfgMgr); 03803 } 03804 catch(std::runtime_error& e) 03805 { 03806 __SUP_SS__ << "Error detected saving Iteration Plan!\n\n " << e.what() << __E__; 03807 __SUP_COUT_ERR__ << "\n" << ss.str() << __E__; 03808 xmlOut.addTextElementToData("Error", ss.str()); 03809 } 03810 catch(...) 03811 { 03812 __SUP_SS__ << "Error detected saving Iteration Plan!\n\n " << __E__; 03813 __SUP_COUT_ERR__ << "\n" << ss.str() << __E__; 03814 xmlOut.addTextElementToData("Error", ss.str()); 03815 } // end handleSavePlanCommandSequenceXML 03816 03817 //======================================================================================================================== 03818 // handleSaveTreeNodeEditXML 03819 // Changes the value specified by UID/Column 03820 // in the specified version of the table. 03821 // 03822 // Error, if the specified version is not the active one. 03823 // If the version is not temporary make a new temporary version 03824 // 03825 // return this information on success 03826 // <resultingTargetTableVersion = xxx> 03827 void ConfigurationGUISupervisor::handleSaveTreeNodeEditXML(HttpXmlDocument& xmlOut, 03828 ConfigurationManagerRW* cfgMgr, 03829 const std::string& tableName, 03830 TableVersion version, 03831 const std::string& type, 03832 const std::string& uid, 03833 const std::string& colName, 03834 const std::string& newValue, 03835 const std::string& author) try 03836 { 03837 __SUP_COUT__ << "table " << tableName << "(" << version << ")" << __E__; 03838 03839 // get the current table/version 03840 // check if the value is new 03841 // if new edit value (in a temporary version only) 03842 03843 // get table and activate target version 03844 TableBase* config = cfgMgr->getTableByName(tableName); 03845 try 03846 { 03847 config->setActiveView(version); 03848 } 03849 catch(...) 03850 { 03851 if(version.isTemporaryVersion()) 03852 throw; // if temporary, there is no hope to find lost version 03853 03854 __SUP_COUT__ << "Failed to find stored version, so attempting to load version: " 03855 << version << __E__; 03856 cfgMgr->getVersionedTableByName(tableName, version); 03857 } 03858 03859 __SUP_COUT__ << "Active version is " << config->getViewVersion() << __E__; 03860 03861 if(version != config->getViewVersion()) 03862 { 03863 __SUP_SS__ << "Target table version (" << version 03864 << ") is not the currently active version (" 03865 << config->getViewVersion() << ". Try refreshing the tree." << __E__; 03866 __SS_THROW__; 03867 } 03868 03869 unsigned int col = -1; 03870 if(type == "uid" || type == "delete-uid") 03871 col = config->getView().getColUID(); 03872 else if(type == "link-UID" || type == "link-GroupID" || type == "value" || 03873 type == "value-groupid" || type == "value-bool" || type == "value-bitmap") 03874 col = config->getView().findCol(colName); 03875 else if(type == "table" || type == "link-comment" || type == "table-newGroupRow" || 03876 type == "table-newUIDRow" || type == "table-newRow") 03877 ; // column N/A 03878 else 03879 { 03880 __SUP_SS__ << "Impossible! Unrecognized edit type: " << type << __E__; 03881 __SS_THROW__; 03882 } 03883 03884 // check if the comment value is new before making temporary version 03885 if(type == "table" || type == "link-comment") 03886 { 03887 // editing comment, so check if comment is different 03888 if(config->getView().isURIEncodedCommentTheSame(newValue)) 03889 { 03890 __SUP_SS__ << "Comment '" << newValue 03891 << "' is the same as the current comment. No need to save change." 03892 << __E__; 03893 __SS_THROW__; 03894 } 03895 } 03896 03897 // version handling: 03898 // always make a new temporary-version from source-version 03899 // edit temporary-version 03900 // if edit fails 03901 // delete temporary-version 03902 // else 03903 // return new temporary-version 03904 // if source-version was temporary 03905 // then delete source-version 03906 03907 TableVersion temporaryVersion = config->createTemporaryView(version); 03908 03909 __SUP_COUT__ << "Created temporary version " << temporaryVersion << __E__; 03910 03911 TableView* cfgView = config->getTemporaryView(temporaryVersion); 03912 03913 // edit/verify new table (throws runtime_errors) 03914 try 03915 { 03916 // have view so edit it 03917 if(type == "table" || type == "link-comment") 03918 { 03919 // edit comment 03920 cfgView->setURIEncodedComment(newValue); 03921 } 03922 else if(type == "table-newRow" || type == "table-newUIDRow") 03923 { 03924 // add row 03925 unsigned int row = cfgView->addRow(author, true /*incrementUniqueData*/); 03926 03927 // if TableViewColumnInfo::COL_NAME_STATUS exists, set it to true 03928 try 03929 { 03930 col = cfgView->getColStatus(); 03931 cfgView->setValueAsString("1", row, col); 03932 } 03933 catch(...) 03934 { 03935 } // if not, ignore 03936 03937 // set UID value 03938 cfgView->setURIEncodedValue(newValue, row, cfgView->getColUID()); 03939 } 03940 else if(type == "table-newGroupRow") 03941 { 03942 // add row 03943 unsigned int row = cfgView->addRow(author, true /*incrementUniqueData*/); 03944 03945 // get index value and group id value 03946 unsigned int csvIndex = newValue.find(','); 03947 03948 std::string linkIndex = newValue.substr(0, csvIndex); 03949 std::string groupId = newValue.substr(csvIndex + 1); 03950 03951 // get new row UID value from second part of string 03952 csvIndex = groupId.find(','); 03953 std::string newRowUID = groupId.substr(csvIndex + 1); 03954 groupId = groupId.substr(0, csvIndex); 03955 03956 __SUP_COUT__ << "newValue " << linkIndex << "," << groupId << "," << newRowUID 03957 << __E__; 03958 03959 // set UID value 03960 cfgView->setURIEncodedValue(newRowUID, row, cfgView->getColUID()); 03961 03962 // find groupId column from link index 03963 col = cfgView->getColLinkGroupID(linkIndex); 03964 03965 // set group id 03966 cfgView->setURIEncodedValue(groupId, row, col); 03967 03968 // if TableViewColumnInfo::COL_NAME_STATUS exists, set it to true 03969 try 03970 { 03971 col = cfgView->getColStatus(); 03972 cfgView->setValueAsString("1", row, col); 03973 } 03974 catch(...) 03975 { 03976 } // if not, ignore 03977 } 03978 else if(type == "delete-uid") 03979 { 03980 // delete row 03981 unsigned int row = cfgView->findRow(col, uid); 03982 cfgView->deleteRow(row); 03983 } 03984 else if(type == "uid" || type == "value" || type == "value-groupid" || 03985 type == "value-bool" || type == "value-bitmap") 03986 { 03987 unsigned int row = cfgView->findRow(cfgView->getColUID(), uid); 03988 if(!cfgView->setURIEncodedValue(newValue, row, col, author)) 03989 { 03990 // no change! so discard 03991 __SUP_SS__ << "Value '" << newValue 03992 << "' is the same as the current value. No need to save " 03993 "change to tree node." 03994 << __E__; 03995 __SS_THROW__; 03996 } 03997 } 03998 else if(type == "link-UID" || type == "link-GroupID") 03999 { 04000 bool isGroup; 04001 std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> linkPair; 04002 if(!cfgView->getChildLink(col, isGroup, linkPair)) 04003 { 04004 // not a link ?! 04005 __SUP_SS__ << "Col '" << colName << "' is not a link column." << __E__; 04006 __SS_THROW__; 04007 } 04008 04009 __SUP_COUT__ << "linkPair " << linkPair.first << "," << linkPair.second 04010 << __E__; 04011 04012 std::string linkIndex = cfgView->getColumnInfo(col).getChildLinkIndex(); 04013 04014 __SUP_COUT__ << "linkIndex " << linkIndex << __E__; 04015 04016 // find table value and id value 04017 unsigned int csvIndexStart = 0, csvIndex = newValue.find(','); 04018 04019 std::string newTable = newValue.substr(csvIndexStart, csvIndex); 04020 csvIndexStart = csvIndex + 1; 04021 csvIndex = newValue.find(',', csvIndexStart); 04022 std::string newLinkId = newValue.substr( 04023 csvIndexStart, 04024 csvIndex - 04025 csvIndexStart); // if no more commas will take the rest of string 04026 04027 __SUP_COUT__ << "newValue " << newTable << "," << newLinkId << __E__; 04028 04029 // change target table in two parts 04030 unsigned int row = cfgView->findRow(cfgView->getColUID(), uid); 04031 bool changed = false; 04032 if(!cfgView->setURIEncodedValue(newTable, row, linkPair.first, author)) 04033 { 04034 // no change 04035 __SUP_COUT__ << "Value '" << newTable 04036 << "' is the same as the current value." << __E__; 04037 } 04038 else 04039 changed = true; 04040 04041 if(!cfgView->setURIEncodedValue(newLinkId, row, linkPair.second, author)) 04042 { 04043 // no change 04044 __SUP_COUT__ << "Value '" << newLinkId 04045 << "' is the same as the current value." << __E__; 04046 } 04047 else 04048 changed = true; 04049 04050 // handle groupID links slightly differently 04051 // have to look at changing link table too! 04052 // if group ID set all in member list to be members of group 04053 if(type == "link-GroupID") 04054 { 04055 bool secondaryChanged = false; 04056 04057 // first close out main target table 04058 if(!changed) // if no changes throw out new version 04059 { 04060 __SUP_COUT__ << "No changes to primary view. Erasing temporary table." 04061 << __E__; 04062 config->eraseView(temporaryVersion); 04063 } 04064 else // if changes, save it 04065 { 04066 try 04067 { 04068 cfgView->init(); // verify new table (throws runtime_errors) 04069 04070 saveModifiedVersionXML(xmlOut, 04071 cfgMgr, 04072 tableName, 04073 version, 04074 true /*make temporary*/, 04075 config, 04076 temporaryVersion, 04077 true /*ignoreDuplicates*/); // save 04078 // temporary 04079 // version 04080 // properly 04081 } 04082 catch(std::runtime_error& 04083 e) // erase temporary view before re-throwing error 04084 { 04085 __SUP_COUT__ << "Caught error while editing main table. Erasing " 04086 "temporary version." 04087 << __E__; 04088 config->eraseView(temporaryVersion); 04089 changed = false; // undo changed bool 04090 04091 // send warning so that, secondary table can still be changed 04092 xmlOut.addTextElementToData( 04093 "Warning", 04094 "Error saving primary tree node! " + std::string(e.what())); 04095 } 04096 } 04097 04098 // now, onto linked table 04099 04100 // get the current linked table/version 04101 // check if the value is new 04102 // if new edit value (in a temporary version only) 04103 04104 csvIndexStart = csvIndex + 1; 04105 csvIndex = newValue.find(',', csvIndexStart); 04106 version = TableVersion(newValue.substr( 04107 csvIndexStart, csvIndex - csvIndexStart)); // if no more commas will 04108 // take the rest of string 04109 04110 if(newTable == TableViewColumnInfo::DATATYPE_LINK_DEFAULT) 04111 { 04112 // done, since init was already tested 04113 // the result should be purposely DISCONNECTED link 04114 return; 04115 } 04116 04117 // get table and activate target version 04118 config = cfgMgr->getTableByName(newTable); 04119 try 04120 { 04121 config->setActiveView(version); 04122 } 04123 catch(...) 04124 { 04125 __SUP_COUT__ << "Failed to find stored version, so attempting to " 04126 "load version: " 04127 << version << __E__; 04128 cfgMgr->getVersionedTableByName(newTable, version); 04129 } 04130 04131 __SUP_COUT__ << "Active version is " << config->getViewVersion() << __E__; 04132 04133 if(version != config->getViewVersion()) 04134 { 04135 __SUP_SS__ << "Target table version (" << version 04136 << ") is not the currently active version (" 04137 << config->getViewVersion() << ". Try refreshing the tree." 04138 << __E__; 04139 __SS_THROW__; 04140 } 04141 04142 // create temporary version for editing 04143 temporaryVersion = config->createTemporaryView(version); 04144 04145 __SUP_COUT__ << "Created temporary version " << temporaryVersion << __E__; 04146 04147 cfgView = config->getTemporaryView(temporaryVersion); 04148 04149 col = cfgView->getColLinkGroupID(linkIndex); 04150 04151 __SUP_COUT__ << "target col " << col << __E__; 04152 04153 // extract vector of members to be 04154 std::vector<std::string> memberUIDs; 04155 do 04156 { 04157 csvIndexStart = csvIndex + 1; 04158 csvIndex = newValue.find(',', csvIndexStart); 04159 memberUIDs.push_back( 04160 newValue.substr(csvIndexStart, csvIndex - csvIndexStart)); 04161 __SUP_COUT__ << "memberUIDs: " << memberUIDs.back() << __E__; 04162 } while(csvIndex != (unsigned int)std::string::npos); // no more commas 04163 04164 // for each row, 04165 // check if should be in group 04166 // if should be but is not 04167 // add to group, CHANGE 04168 // if should not be but is 04169 // remove from group, CHANGE 04170 // 04171 04172 std::string targetUID; 04173 bool shouldBeInGroup; 04174 bool defaultIsInGroup = 04175 false; // use to indicate if a recent new member was created 04176 bool isInGroup; 04177 04178 for(unsigned int row = 0; row < cfgView->getNumberOfRows(); ++row) 04179 { 04180 targetUID = cfgView->getDataView()[row][cfgView->getColUID()]; 04181 __SUP_COUT__ << "targetUID: " << targetUID << __E__; 04182 04183 shouldBeInGroup = false; 04184 for(unsigned int i = 0; i < memberUIDs.size(); ++i) 04185 if(targetUID == memberUIDs[i]) 04186 { 04187 // found in member uid list 04188 shouldBeInGroup = true; 04189 break; 04190 } 04191 04192 isInGroup = cfgView->isEntryInGroup(row, linkIndex, newLinkId); 04193 04194 // if should be but is not 04195 if(shouldBeInGroup && !isInGroup) 04196 { 04197 __SUP_COUT__ << "Changed YES: " << row << __E__; 04198 secondaryChanged = true; 04199 04200 cfgView->addRowToGroup(row, col, newLinkId); 04201 04202 } // if should not be but is 04203 else if(!shouldBeInGroup && isInGroup) 04204 { 04205 __SUP_COUT__ << "Changed NO: " << row << __E__; 04206 secondaryChanged = true; 04207 04208 cfgView->removeRowFromGroup(row, col, newLinkId); 04209 } 04210 else if(targetUID == 04211 cfgView->getDefaultRowValues()[cfgView->getColUID()] && 04212 isInGroup) 04213 { 04214 // use to indicate if a recent new member was created 04215 defaultIsInGroup = true; 04216 } 04217 } 04218 04219 // first close out main target table 04220 if(!secondaryChanged) // if no changes throw out new version 04221 { 04222 __SUP_COUT__ 04223 << "No changes to secondary view. Erasing temporary table." 04224 << __E__; 04225 config->eraseView(temporaryVersion); 04226 } 04227 else // if changes, save it 04228 { 04229 try 04230 { 04231 cfgView->init(); // verify new table (throws runtime_errors) 04232 04233 saveModifiedVersionXML(xmlOut, 04234 cfgMgr, 04235 newTable, 04236 version, 04237 true /*make temporary*/, 04238 config, 04239 temporaryVersion, 04240 true /*ignoreDuplicates*/); // save 04241 // temporary 04242 // version 04243 // properly 04244 } 04245 catch(std::runtime_error& 04246 e) // erase temporary view before re-throwing error 04247 { 04248 __SUP_COUT__ << "Caught error while editing secondary table. " 04249 "Erasing temporary version." 04250 << __E__; 04251 config->eraseView(temporaryVersion); 04252 secondaryChanged = false; // undo changed bool 04253 04254 // send warning so that, secondary table can still be changed 04255 xmlOut.addTextElementToData( 04256 "Warning", 04257 "Error saving secondary tree node! " + std::string(e.what())); 04258 } 04259 } 04260 04261 // block error message if default is in group, assume new member was just 04262 // created RAR: block because its hard to detect if changes were recently 04263 // made (one idea: to check if all other values are defaults, to assume it 04264 // was just created) 04265 if(0 && !changed && !secondaryChanged && !defaultIsInGroup) 04266 { 04267 __SUP_SS__ << "Link to table '" << newTable << "', linkID '" 04268 << newLinkId 04269 << "', and selected group members are the same as the " 04270 "current value. " 04271 << "No need to save changes to tree." << __E__; 04272 __SS_THROW__; 04273 } 04274 04275 return; // exit since table inits were already tested 04276 } 04277 else if(0 && !changed) // block error message because sometimes things get 04278 // setup twice depending on the path of the user (e.g. 04279 // when editing links in tree-view) 04280 { // RAR: block also becuase versions are temporary at this point anyway, 04281 // might as well abuse temporary versions 04282 __SUP_SS__ << "Link to table '" << newTable << "' and linkID '" 04283 << newLinkId 04284 << "' are the same as the current values. No need to save " 04285 "change to tree node." 04286 << __E__; 04287 __SS_THROW__; 04288 } 04289 } 04290 04291 cfgView->init(); // verify new table (throws runtime_errors) 04292 } 04293 catch(...) // erase temporary view before re-throwing error 04294 { 04295 __SUP_COUT__ << "Caught error while editing. Erasing temporary version." << __E__; 04296 config->eraseView(temporaryVersion); 04297 throw; 04298 } 04299 04300 saveModifiedVersionXML(xmlOut, 04301 cfgMgr, 04302 tableName, 04303 version, 04304 true /*make temporary*/, 04305 config, 04306 temporaryVersion, 04307 true /*ignoreDuplicates*/); // save temporary version properly 04308 } 04309 catch(std::runtime_error& e) 04310 { 04311 __SUP_SS__ << "Error saving tree node! " << e.what() << __E__; 04312 __SUP_COUT_ERR__ << "\n" << ss.str() << __E__; 04313 xmlOut.addTextElementToData("Error", ss.str()); 04314 } 04315 catch(...) 04316 { 04317 __SUP_SS__ << "Unknown Error saving tree node! " << __E__; 04318 __SUP_COUT_ERR__ << "\n" << ss.str() << __E__; 04319 xmlOut.addTextElementToData("Error", ss.str()); 04320 } 04321 04322 //======================================================================================================================== 04323 // handleGetTableGroupXML 04324 // 04325 // give the detail of specific table specified 04326 // groupKey=-1 returns latest 04327 // 04328 // Find historical group keys 04329 // and figure out all member configurations versions 04330 04331 // 04332 // return this information 04333 // <group name=xxx key=xxx> 04334 // <historical key=xxx> 04335 // <historical key=xxx> 04336 // .... 04337 // <table name=xxx version=xxx /> 04338 // <historical version=xxx> 04339 // <historical version=xxx> 04340 // ... 04341 // </table> 04342 // <table name=xxx version=xxx> 04343 // ... 04344 // </table> 04345 void ConfigurationGUISupervisor::handleGetTableGroupXML(HttpXmlDocument& xmlOut, 04346 ConfigurationManagerRW* cfgMgr, 04347 const std::string& groupName, 04348 TableGroupKey groupKey) try 04349 { 04350 char tmpIntStr[100]; 04351 DOMElement *parentEl, *configEl; 04352 04353 // steps: 04354 // if invalid key, get latest key 04355 // get specific group with key 04356 // give member names and versions 04357 // get all table groups to locate historical keys 04358 // get all groups to find historical keys 04359 04360 // std::set<std::string /*name+version*/> allGroups = 04361 // cfgMgr->getConfigurationInterface()->getAllTableGroupNames(groupName); 04362 // std::string name; 04363 // TableGroupKey key; 04364 // //put them in a set to sort them as TableGroupKey defines for operator< 04365 // std::set<TableGroupKey> sortedKeys; 04366 // for(auto& group: allGroups) 04367 // { 04368 // //now uses database filter 04369 // TableGroupKey::getGroupNameAndKey(group,name,key); 04370 // //if(name == groupName) 04371 // sortedKeys.emplace(key); 04372 // } 04373 04374 const GroupInfo& groupInfo = cfgMgr->getGroupInfo(groupName); 04375 const std::set<TableGroupKey>& sortedKeys = groupInfo.keys_; // rename 04376 04377 if(groupKey.isInvalid() || // if invalid or not found, get latest 04378 sortedKeys.find(groupKey) == sortedKeys.end()) 04379 { 04380 if(sortedKeys.size()) 04381 groupKey = *sortedKeys.rbegin(); 04382 __SUP_COUT__ << "Group key requested was invalid or not found, going with latest " 04383 << groupKey << __E__; 04384 } 04385 04386 xmlOut.addTextElementToData("TableGroupName", groupName); 04387 xmlOut.addTextElementToData("TableGroupKey", groupKey.toString()); 04388 04389 // add all other sorted keys for this groupName 04390 for(auto& keyInOrder : sortedKeys) 04391 xmlOut.addTextElementToData("HistoricalTableGroupKey", keyInOrder.toString()); 04392 04393 parentEl = xmlOut.addTextElementToData("TableGroupMembers", ""); 04394 04395 // get specific group with key 04396 std::map<std::string /*name*/, TableVersion /*version*/> memberMap; 04397 std::map<std::string /*name*/, std::string /*alias*/> groupMemberAliases; 04398 04399 __SUP_COUT__ << "groupName=" << groupName << __E__; 04400 __SUP_COUT__ << "groupKey=" << groupKey << __E__; 04401 04402 const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo(); 04403 std::map<std::string, TableInfo>::const_iterator it; 04404 04405 // load group so comments can be had 04406 // and also group metadata (author, comment, createTime) 04407 try 04408 { 04409 std::string groupAuthor, groupComment, groupCreationTime, groupTypeString; 04410 std::string accumulateErrors; 04411 04412 cfgMgr->loadTableGroup(groupName, 04413 groupKey, 04414 false /*doActivate*/, 04415 &memberMap, 04416 0 /*progressBar*/, 04417 &accumulateErrors /*accumulateErrors*/, 04418 &groupComment, 04419 &groupAuthor, 04420 &groupCreationTime, 04421 false /*doNotLoadMember*/, 04422 &groupTypeString, 04423 &groupMemberAliases); 04424 04425 if(accumulateErrors != "") 04426 { 04427 __SUP_SS__ << accumulateErrors; 04428 __SUP_SS_THROW__; 04429 } 04430 04431 xmlOut.addTextElementToData("TableGroupAuthor", groupAuthor); 04432 xmlOut.addTextElementToData("TableGroupComment", groupComment); 04433 xmlOut.addTextElementToData("TableGroupCreationTime", groupCreationTime); 04434 xmlOut.addTextElementToData("TableGroupType", groupTypeString); 04435 } 04436 catch(const std::runtime_error& e) 04437 { 04438 __SUP_SS__ << "Table group \"" + groupName + "(" + groupKey.toString() + ")" + 04439 "\" members can not be loaded!\n\n" + e.what(); 04440 __SUP_COUT_ERR__ << ss.str(); 04441 xmlOut.addTextElementToData("Error", ss.str()); 04442 // return; 04443 } 04444 catch(...) 04445 { 04446 __SUP_SS__ << "Table group \"" + groupName + "(" + groupKey.toString() + ")" + 04447 "\" members can not be loaded!" 04448 << __E__; 04449 __SUP_COUT_ERR__ << ss.str(); 04450 xmlOut.addTextElementToData("Error", ss.str()); 04451 // return; 04452 } 04453 04454 __COUTV__(StringMacros::mapToString(groupMemberAliases)); 04455 04456 std::map<std::string, std::map<std::string, TableVersion>> versionAliases = 04457 cfgMgr->getVersionAliases(); 04458 04459 __SUP_COUT__ << "# of table version aliases: " << versionAliases.size() << __E__; 04460 04461 // Seperate loop to get name and version 04462 for(auto& memberPair : memberMap) 04463 { 04464 xmlOut.addTextElementToParent("MemberName", memberPair.first, parentEl); 04465 04466 // if member is in groupMemberAliases, then alias version 04467 if(groupMemberAliases.find(memberPair.first) != groupMemberAliases.end()) 04468 configEl = xmlOut.addTextElementToParent( 04469 "MemberVersion", 04470 ConfigurationManager::ALIAS_VERSION_PREAMBLE + 04471 groupMemberAliases[memberPair.first], // return the ALIAS:<alias> 04472 parentEl); 04473 else 04474 configEl = xmlOut.addTextElementToParent( 04475 "MemberVersion", memberPair.second.toString(), parentEl); 04476 04477 it = allTableInfo.find(memberPair.first); 04478 if(it == allTableInfo.end()) 04479 { 04480 xmlOut.addTextElementToData( 04481 "Error", "Table \"" + memberPair.first + "\" can not be retrieved!"); 04482 continue; 04483 } 04484 04485 if(versionAliases.find(it->first) != versionAliases.end()) 04486 for(auto& aliasVersion : versionAliases[it->first]) 04487 xmlOut.addTextElementToParent( 04488 "TableExistingVersion", 04489 ConfigurationManager::ALIAS_VERSION_PREAMBLE + aliasVersion.first, 04490 configEl); 04491 04492 for(auto& version : it->second.versions_) 04493 // if(version == memberPair.second) continue; //CHANGED by RAR on 11/14/2016 04494 // (might as well show all versions in list to avoid user confusion) else 04495 xmlOut.addTextElementToParent( 04496 "TableExistingVersion", version.toString(), configEl); 04497 } 04498 // Seperate loop just for getting the Member Comment 04499 for(auto& memberPair : memberMap) 04500 { 04501 //__SUP_COUT__ << "\tMember config " << memberPair.first << ":" << 04502 // memberPair.second << __E__; 04503 04504 // xmlOut.addTextElementToParent("MemberName", memberPair.first, parentEl); 04505 // if(commentsLoaded) 04506 xmlOut.addTextElementToParent( 04507 "MemberComment", 04508 allTableInfo.at(memberPair.first).tablePtr_->getView().getComment(), 04509 parentEl); 04510 // else 04511 // xmlOut.addTextElementToParent("MemberComment", "", parentEl); 04512 04513 // __SUP_COUT__ << "\tMember config " << memberPair.first << ":" << 04514 // memberPair.second << __E__; 04515 04516 // configEl = xmlOut.addTextElementToParent("MemberVersion", 04517 // memberPair.second.toString(), parentEl); 04518 04519 /* it = allTableInfo.find(memberPair.first); 04520 if(it == allTableInfo.end()) 04521 { 04522 xmlOut.addTextElementToData("Error","Table \"" + 04523 memberPair.first + 04524 "\" can not be retrieved!"); 04525 return; 04526 } 04527 */ 04528 // include aliases for this table 04529 /*if(versionAliases.find(it->first) != versionAliases.end()) 04530 for (auto& aliasVersion:versionAliases[it->first]) 04531 xmlOut.addTextElementToParent("TableExistingVersion", 04532 ConfigurationManager::ALIAS_VERSION_PREAMBLE + aliasVersion.first, 04533 configEl); 04534 04535 for (auto& version:it->second.versions_) 04536 //if(version == memberPair.second) continue; //CHANGED by RAR on 11/14/2016 04537 (might as well show all versions in list to avoid user confusion) 04538 //else 04539 xmlOut.addTextElementToParent("TableExistingVersion", 04540 version.toString(), configEl); 04541 */ 04542 } 04543 04544 return; 04545 } 04546 catch(std::runtime_error& e) 04547 { 04548 __SUP_SS__ << ("Error!\n\n" + std::string(e.what())) << __E__; 04549 __SUP_COUT_ERR__ << "\n" << ss.str(); 04550 xmlOut.addTextElementToData("Error", ss.str()); 04551 } 04552 catch(...) 04553 { 04554 __SUP_SS__ << ("Error!\n\n") << __E__; 04555 __SUP_COUT_ERR__ << "\n" << ss.str(); 04556 xmlOut.addTextElementToData("Error", ss.str()); 04557 } 04558 04559 //======================================================================================================================== 04560 // handleGetTableXML 04561 // 04562 // if INVALID or version does not exists, default to mock-up 04563 // 04564 // give the detail of specific table specified 04565 // by tableName and version 04566 // 04567 // if no version selected, default to latest version 04568 // if no versions exists, default to mock-up 04569 // 04570 // return existing versions 04571 // return column headers 04572 // return number of rows 04573 // from dataOffset 04574 // first CHUNK_SIZE rows 04575 // 04576 // return this information 04577 //<table name=xxx version=xxx rowCount=xxx chunkReq=xxx chunkSz=xxx> 04578 // <existing version=xxx> 04579 // <existing version=xxx> 04580 // .... 04581 // <colhdr name=xxx> 04582 // <colhdr name=xxx> 04583 // .... 04584 // <rowdata> 04585 // <cell value=xxx> 04586 // <cell value=xxx> 04587 // .... 04588 // </rowdata> 04589 // <rowdata> 04590 // .... 04591 // </rowdata> 04592 // .... 04593 //</table> 04594 // 04595 // 04596 // Note: options.. if allowIllegalColumns then attempts to load data to current mockup 04597 // column names 04598 // if not allowIllegalColumns, then it is still ok if the source has more or less 04599 // columns: the client is notified through "TableWarnings" field in this case. 04600 void ConfigurationGUISupervisor::handleGetTableXML(HttpXmlDocument& xmlOut, 04601 ConfigurationManagerRW* cfgMgr, 04602 const std::string& tableName, 04603 TableVersion version, 04604 bool allowIllegalColumns) try 04605 { 04606 char tmpIntStr[100]; 04607 DOMElement *parentEl, *subparentEl; 04608 04609 std::string accumulatedErrors = ""; 04610 04611 const std::map<std::string, TableInfo>& 04612 allTableInfo = // if allowIllegalColumns, then also refresh 04613 cfgMgr->getAllTableInfo(allowIllegalColumns, 04614 allowIllegalColumns ? &accumulatedErrors : 0, 04615 tableName); // filter errors by tableName 04616 04617 TableBase* table = cfgMgr->getTableByName(tableName); 04618 04619 // send all table names along with 04620 // and check for specific version 04621 xmlOut.addTextElementToData("ExistingTableNames", 04622 TableViewColumnInfo::DATATYPE_LINK_DEFAULT); 04623 for(auto& configPair : allTableInfo) 04624 { 04625 xmlOut.addTextElementToData("ExistingTableNames", configPair.first); 04626 if(configPair.first == tableName && // check that version exists 04627 configPair.second.versions_.find(version) == configPair.second.versions_.end()) 04628 { 04629 __SUP_COUT__ << "Version not found, so using mockup." << __E__; 04630 version = TableVersion(); // use INVALID 04631 } 04632 } 04633 04634 xmlOut.addTextElementToData("TableName", tableName); // table name 04635 xmlOut.addTextElementToData("TableDescription", 04636 table->getTableDescription()); // table name 04637 04638 // existing table versions 04639 { 04640 // get version aliases for translation 04641 std::map< 04642 std::string /*table name*/, 04643 std::map<std::string /*version alias*/, TableVersion /*aliased version*/>> 04644 versionAliases; 04645 try 04646 { 04647 // use whatever backbone is currently active 04648 versionAliases = cfgMgr->getVersionAliases(); 04649 for(const auto& aliases : versionAliases) 04650 for(const auto& alias : aliases.second) 04651 __SUP_COUT__ << "ALIAS: " << aliases.first << " " << alias.first 04652 << " ==> " << alias.second << __E__; 04653 } 04654 catch(const std::runtime_error& e) 04655 { 04656 __SUP_COUT__ << "Could not get backbone information for version aliases: " 04657 << e.what() << __E__; 04658 } 04659 04660 auto tableIterator = versionAliases.find(tableName); 04661 04662 parentEl = xmlOut.addTextElementToData("TableVersions", ""); 04663 for(const TableVersion& v : allTableInfo.at(tableName).versions_) 04664 { 04665 subparentEl = 04666 xmlOut.addTextElementToParent("Version", v.toString(), parentEl); 04667 04668 if(tableIterator != versionAliases.end()) 04669 { 04670 // check if this version has one or many aliases 04671 for(const auto& aliasPair : tableIterator->second) 04672 { 04673 // __SUP_COUT__ << "Checking " << aliasPair.second << 04674 //" 04675 //--> 04676 //" 04677 //<< aliasPair.first << " for " << v << 04678 //__E__; 04679 if(v == aliasPair.second) 04680 { 04681 __SUP_COUT__ << "Found Alias " << aliasPair.second << " --> " 04682 << aliasPair.first << __E__; 04683 xmlOut.addTextElementToParent( 04684 "VersionAlias", aliasPair.first, subparentEl); 04685 } 04686 } 04687 } 04688 } 04689 } 04690 04691 // table columns and then rows (from table view) 04692 04693 // get view pointer 04694 TableView* cfgViewPtr; 04695 if(version.isInvalid()) // use mock-up 04696 { 04697 cfgViewPtr = table->getMockupViewP(); 04698 } 04699 else // use view version 04700 { 04701 try 04702 { 04703 cfgViewPtr = cfgMgr->getVersionedTableByName(tableName, version)->getViewP(); 04704 } 04705 catch(std::runtime_error& e) // default to mock-up for fail-safe in GUI editor 04706 { 04707 __SUP_SS__ << "Failed to get table " << tableName << " version " << version 04708 << "... defaulting to mock-up! " << __E__; 04709 ss << "\n\n...Here is why it failed:\n\n" << e.what() << __E__; 04710 04711 __SUP_COUT_ERR__ << "\n" << ss.str(); 04712 version = TableVersion(); 04713 cfgViewPtr = table->getMockupViewP(); 04714 04715 xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str()); 04716 } 04717 catch(...) // default to mock-up for fail-safe in GUI editor 04718 { 04719 __SUP_SS__ << "Failed to get table " << tableName << " version: " << version 04720 << "... defaulting to mock-up! " 04721 << "(You may want to try again to see what was partially loaded " 04722 "into cache before failure. " 04723 << "If you think, the failure is due to a column name change, " 04724 << "you can also try to Copy the failing view to the new column " 04725 "names using " 04726 << "'Copy and Move' functionality.)" << __E__; 04727 04728 __SUP_COUT_ERR__ << "\n" << ss.str(); 04729 version = TableVersion(); 04730 cfgViewPtr = table->getMockupViewP(); 04731 04732 xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str()); 04733 } 04734 } 04735 xmlOut.addTextElementToData("TableVersion", version.toString()); // table version 04736 04737 // get 'columns' of view 04738 DOMElement* choicesParentEl; 04739 parentEl = xmlOut.addTextElementToData("CurrentVersionColumnHeaders", ""); 04740 04741 std::vector<TableViewColumnInfo> colInfo = cfgViewPtr->getColumnsInfo(); 04742 04743 for(int i = 0; i < (int)colInfo.size(); ++i) // column headers and types 04744 { 04745 // __SUP_COUT__ << "\t\tCol " << i << ": " << colInfo[i].getType() << "() " 04746 //<< colInfo[i].getName() << " " 04747 // << colInfo[i].getStorageName() << " " << colInfo[i].getDataType() 04748 //<< 04749 //__E__; 04750 04751 xmlOut.addTextElementToParent("ColumnHeader", colInfo[i].getName(), parentEl); 04752 xmlOut.addTextElementToParent("ColumnType", colInfo[i].getType(), parentEl); 04753 xmlOut.addTextElementToParent( 04754 "ColumnDataType", colInfo[i].getDataType(), parentEl); 04755 04756 choicesParentEl = xmlOut.addTextElementToParent("ColumnChoices", "", parentEl); 04757 // add data choices if necessary 04758 if(colInfo[i].getType() == TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA || 04759 colInfo[i].getType() == TableViewColumnInfo::TYPE_BITMAP_DATA || 04760 colInfo[i].isChildLink()) 04761 { 04762 for(auto& choice : colInfo[i].getDataChoices()) 04763 xmlOut.addTextElementToParent("ColumnChoice", choice, choicesParentEl); 04764 } 04765 } 04766 04767 // verify mockup columns after columns are posted to xmlOut 04768 try 04769 { 04770 if(version.isInvalid()) 04771 cfgViewPtr->init(); 04772 } 04773 catch(std::runtime_error& e) 04774 { 04775 // append accumulated errors, because they may be most useful 04776 __THROW__(e.what() + std::string("\n\n") + accumulatedErrors); 04777 } 04778 catch(...) 04779 { 04780 throw; 04781 } 04782 04783 parentEl = xmlOut.addTextElementToData("CurrentVersionRows", ""); 04784 04785 for(int r = 0; r < (int)cfgViewPtr->getNumberOfRows(); ++r) 04786 { 04787 //__SUP_COUT__ << "\t\tRow " << r << ": " << __E__; 04788 04789 sprintf(tmpIntStr, "%d", r); 04790 DOMElement* tmpParentEl = 04791 xmlOut.addTextElementToParent("Row", tmpIntStr, parentEl); 04792 04793 for(int c = 0; c < (int)cfgViewPtr->getNumberOfColumns(); ++c) 04794 { 04795 if(colInfo[c].getDataType() == TableViewColumnInfo::DATATYPE_TIME) 04796 { 04797 std::string timeAsString; 04798 cfgViewPtr->getValue(timeAsString, r, c); 04799 xmlOut.addTextElementToParent("Entry", timeAsString, tmpParentEl); 04800 } 04801 else 04802 xmlOut.addTextElementToParent( 04803 "Entry", cfgViewPtr->getDataView()[r][c], tmpParentEl); 04804 } 04805 } 04806 04807 // add "other" fields associated with configView 04808 xmlOut.addTextElementToData("TableComment", cfgViewPtr->getComment()); 04809 xmlOut.addTextElementToData("TableAuthor", cfgViewPtr->getAuthor()); 04810 xmlOut.addTextElementToData("TableCreationTime", 04811 std::to_string(cfgViewPtr->getCreationTime())); 04812 xmlOut.addTextElementToData("TableLastAccessTime", 04813 std::to_string(cfgViewPtr->getLastAccessTime())); 04814 04815 // add to xml the default row values 04816 std::vector<std::string> defaultRowValues = cfgViewPtr->getDefaultRowValues(); 04817 // don't give author and time.. force default author, let JS fill time 04818 for(unsigned int c = 0; c < defaultRowValues.size() - 2; ++c) 04819 { 04820 // __SUP_COUT__ << "Default for c" << c << "=" << 04821 // cfgViewPtr->getColumnInfo(c).getName() << " is " << 04822 // defaultRowValues[c] << __E__; 04823 xmlOut.addTextElementToData("DefaultRowValue", defaultRowValues[c]); 04824 } 04825 04826 if(accumulatedErrors != "") // add accumulated errors to xmlOut 04827 { 04828 __SUP_SS__ << (std::string("Column errors were allowed for this request, so " 04829 "maybe you can ignore this, ") + 04830 "but please note the following errors:\n" + accumulatedErrors) 04831 << __E__; 04832 __SUP_COUT_ERR__ << ss.str(); 04833 xmlOut.addTextElementToData("Error", ss.str()); 04834 } 04835 else if(!version.isTemporaryVersion() && // not temporary (these are not filled from 04836 // interface source) 04837 (cfgViewPtr->getDataColumnSize() != cfgViewPtr->getNumberOfColumns() || 04838 cfgViewPtr->getSourceColumnMismatch() != 04839 0)) // check for column size mismatch 04840 { 04841 __SUP_SS__ << "\n\nThere were warnings found when loading the table " << tableName 04842 << ":v" << version << ". Please see the details below:\n\n" 04843 << "The source column size was found to be " 04844 << cfgViewPtr->getDataColumnSize() 04845 << ", and the current number of columns for this table is " 04846 << cfgViewPtr->getNumberOfColumns() << ". This resulted in a count of " 04847 << cfgViewPtr->getSourceColumnMismatch() 04848 << " source column mismatches, and a count of " 04849 << cfgViewPtr->getSourceColumnMissing() << " table entries missing in " 04850 << cfgViewPtr->getNumberOfRows() << " row(s) of data." << __E__; 04851 04852 const std::set<std::string> srcColNames = cfgViewPtr->getSourceColumnNames(); 04853 ss << "\n\nSource column names in ALPHABETICAL order were as follows:\n"; 04854 char index = 'a'; 04855 std::string preIndexStr = ""; 04856 for(auto& srcColName : srcColNames) 04857 { 04858 ss << "\n\t" << preIndexStr << index << ". " << srcColName; 04859 if(index == 'z') // wrap-around 04860 { 04861 preIndexStr += 'a'; // keep adding index 'digits' for wrap-around 04862 index = 'a'; 04863 } 04864 else 04865 ++index; 04866 } 04867 ss << __E__; 04868 04869 std::set<std::string> destColNames = cfgViewPtr->getColumnStorageNames(); 04870 ss << "\n\nCurrent table column names in ALPHABETICAL order are as follows:\n"; 04871 index = 'a'; 04872 preIndexStr = ""; 04873 for(auto& destColName : destColNames) 04874 { 04875 ss << "\n\t" << preIndexStr << index << ". " << destColName; 04876 if(index == 'z') // wrap-around 04877 { 04878 preIndexStr += 'a'; // keep adding index 'digits' for wrap-around 04879 index = 'a'; 04880 } 04881 else 04882 ++index; 04883 } 04884 ss << __E__; 04885 04886 __SUP_COUT__ << "\n" << ss.str(); 04887 xmlOut.addTextElementToData("TableWarnings", ss.str()); 04888 } 04889 } // end handleGetTableXML() 04890 catch(std::runtime_error& e) 04891 { 04892 __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__; 04893 xmlOut.addTextElementToData("Error", "Error getting view! " + std::string(e.what())); 04894 } 04895 catch(...) 04896 { 04897 __SUP_COUT__ << "Error detected!\n\n " << __E__; 04898 xmlOut.addTextElementToData("Error", "Error getting view! "); 04899 } 04900 04901 //======================================================================================================================== 04902 // saveModifiedVersionXML 04903 // 04904 // once source version has been modified in temporary version 04905 // this function finishes it off. 04906 TableVersion ConfigurationGUISupervisor::saveModifiedVersionXML( 04907 HttpXmlDocument& xmlOut, 04908 ConfigurationManagerRW* cfgMgr, 04909 const std::string& tableName, 04910 TableVersion originalVersion, 04911 bool makeTemporary, 04912 TableBase* config, 04913 TableVersion temporaryModifiedVersion, 04914 bool ignoreDuplicates, 04915 bool lookForEquivalent) 04916 { 04917 bool needToEraseTemporarySource = 04918 (originalVersion.isTemporaryVersion() && !makeTemporary); 04919 04920 // check for duplicate tables already in cache 04921 if(!ignoreDuplicates) 04922 { 04923 __SUP_COUT__ << "Checking for duplicate tables..." << __E__; 04924 04925 TableVersion duplicateVersion; 04926 04927 { 04928 //"DEEP" checking 04929 // load into cache 'recent' versions for this table 04930 // 'recent' := those already in cache, plus highest version numbers not 04931 // in cache 04932 const std::map<std::string, TableInfo>& allTableInfo = 04933 cfgMgr->getAllTableInfo(); // do not refresh 04934 04935 auto versionReverseIterator = 04936 allTableInfo.at(tableName).versions_.rbegin(); // get reverse iterator 04937 __SUP_COUT__ << "Filling up cached from " << config->getNumberOfStoredViews() 04938 << " to max count of " << config->MAX_VIEWS_IN_CACHE << __E__; 04939 for(; config->getNumberOfStoredViews() < config->MAX_VIEWS_IN_CACHE && 04940 versionReverseIterator != allTableInfo.at(tableName).versions_.rend(); 04941 ++versionReverseIterator) 04942 { 04943 __SUP_COUT__ << "Versions in reverse order " << *versionReverseIterator 04944 << __E__; 04945 try 04946 { 04947 cfgMgr->getVersionedTableByName( 04948 tableName, *versionReverseIterator); // load to cache 04949 } 04950 catch(const std::runtime_error& e) 04951 { 04952 __SUP_COUT__ 04953 << "Error loadiing historical version, but ignoring: " << e.what() 04954 << __E__; 04955 } 04956 } 04957 } 04958 04959 __SUP_COUT__ << "Checking duplicate..." << __E__; 04960 04961 duplicateVersion = config->checkForDuplicate( 04962 temporaryModifiedVersion, 04963 (!originalVersion.isTemporaryVersion() && !makeTemporary) 04964 ? TableVersion() 04965 : // if from persistent to persistent, then include original version in 04966 // search 04967 originalVersion); 04968 04969 if(lookForEquivalent && !duplicateVersion.isInvalid()) 04970 { 04971 // found an equivalent! 04972 __SUP_COUT__ << "Equivalent table found in version v" << duplicateVersion 04973 << __E__; 04974 04975 // if duplicate version was temporary, do not use 04976 if(duplicateVersion.isTemporaryVersion() && !makeTemporary) 04977 { 04978 __SUP_COUT__ << "Need persistent. Duplicate version was temporary. " 04979 "Abandoning duplicate." 04980 << __E__; 04981 duplicateVersion = TableVersion(); // set invalid 04982 } 04983 else 04984 { 04985 // erase and return equivalent version 04986 04987 // erase modified equivalent version 04988 cfgMgr->eraseTemporaryVersion(tableName, temporaryModifiedVersion); 04989 04990 // erase original if needed 04991 if(needToEraseTemporarySource) 04992 cfgMgr->eraseTemporaryVersion(tableName, originalVersion); 04993 04994 xmlOut.addTextElementToData("savedName", tableName); 04995 xmlOut.addTextElementToData("savedVersion", duplicateVersion.toString()); 04996 xmlOut.addTextElementToData("foundEquivalentVersion", "1"); 04997 04998 __SUP_COUT__ << "\t\t equivalent AssignedVersion: " << duplicateVersion 04999 << __E__; 05000 05001 return duplicateVersion; 05002 } 05003 } 05004 05005 if(!duplicateVersion.isInvalid()) 05006 { 05007 __SUP_SS__ 05008 << "This version of table '" << tableName << 05009 "' is identical to another version currently cached v" 05010 << duplicateVersion << ". No reason to save a duplicate." << __E__; 05011 __SUP_COUT_ERR__ << "\n" << ss.str(); 05012 05013 // delete temporaryModifiedVersion 05014 config->eraseView(temporaryModifiedVersion); 05015 __SS_THROW__; 05016 } 05017 05018 __SUP_COUT__ << "Check for duplicate tables complete." << __E__; 05019 } 05020 05021 if(makeTemporary) 05022 __SUP_COUT__ << "\t\t**************************** Save as temporary table version" 05023 << __E__; 05024 else 05025 __SUP_COUT__ << "\t\t**************************** Save as new table version" 05026 << __E__; 05027 05028 TableVersion newAssignedVersion = 05029 cfgMgr->saveNewTable(tableName, temporaryModifiedVersion, makeTemporary); 05030 05031 if(needToEraseTemporarySource) 05032 cfgMgr->eraseTemporaryVersion(tableName, originalVersion); 05033 05034 xmlOut.addTextElementToData("savedName", tableName); 05035 xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString()); 05036 05037 __SUP_COUT__ << "\t\t newAssignedVersion: " << newAssignedVersion << __E__; 05038 return newAssignedVersion; 05039 } 05040 05041 //======================================================================================================================== 05042 // handleCreateTableXML 05043 // 05044 // Save the detail of specific table specified 05045 // by tableName and version 05046 // ...starting from dataOffset 05047 // 05048 // Note: if starting version is -1 start from mock-up 05049 void ConfigurationGUISupervisor::handleCreateTableXML(HttpXmlDocument& xmlOut, 05050 ConfigurationManagerRW* cfgMgr, 05051 const std::string& tableName, 05052 TableVersion version, 05053 bool makeTemporary, 05054 const std::string& data, 05055 const int& dataOffset, 05056 const std::string& author, 05057 const std::string& comment, 05058 bool sourceTableAsIs, 05059 bool lookForEquivalent) try 05060 { 05061 //__SUP_COUT__ << "handleCreateTableXML: " << tableName << " version: " << 05062 // version 05063 // << " dataOffset: " << dataOffset << __E__; 05064 05065 //__SUP_COUT__ << "data: " << data << __E__; 05066 05067 // create temporary version from starting version 05068 if(!version.isInvalid()) // if not using mock-up, make sure starting version is 05069 // loaded 05070 { 05071 try 05072 { 05073 cfgMgr->getVersionedTableByName(tableName, version); 05074 } 05075 catch(...) 05076 { 05077 // force to mockup 05078 version = TableVersion(); 05079 } 05080 } 05081 05082 TableBase* config = cfgMgr->getTableByName(tableName); 05083 05084 // check that the source version has the right number of columns 05085 // if there is a mismatch, start from mockup 05086 if(!version.isInvalid()) // if not using mock-up, then the starting version is the 05087 // active one 05088 { 05089 // compare active to mockup column counts 05090 if(config->getViewP()->getDataColumnSize() != 05091 config->getMockupViewP()->getNumberOfColumns() || 05092 config->getViewP()->getSourceColumnMismatch() != 0) 05093 { 05094 __SUP_COUT__ << "config->getViewP()->getNumberOfColumns() " 05095 << config->getViewP()->getNumberOfColumns() << __E__; 05096 __SUP_COUT__ << "config->getMockupViewP()->getNumberOfColumns() " 05097 << config->getMockupViewP()->getNumberOfColumns() << __E__; 05098 __SUP_COUT__ << "config->getViewP()->getSourceColumnMismatch() " 05099 << config->getViewP()->getSourceColumnMismatch() << __E__; 05100 __SUP_COUT_INFO__ 05101 << "Source view v" << version 05102 << " has a mismatch in the number of columns, so using mockup as source." 05103 << __E__; 05104 version = TableVersion(); // invalid = mockup 05105 } 05106 } 05107 05108 // create a temporary version from the source version 05109 TableVersion temporaryVersion = config->createTemporaryView(version); 05110 05111 __SUP_COUT__ << "\t\ttemporaryVersion: " << temporaryVersion << __E__; 05112 05113 TableView* cfgView = config->getTemporaryView(temporaryVersion); 05114 05115 int retVal; 05116 try 05117 { 05118 // returns -1 on error that data was unchanged 05119 retVal = sourceTableAsIs ? 0 : cfgView->fillFromCSV(data, dataOffset, author); 05120 05121 if(retVal == 1) // data was same but columns are different! 05122 { 05123 __SUP_COUT__ << "Data was the same, but columns have changed!" << __E__; 05124 __SUP_COUTV__(sourceTableAsIs); 05125 lookForEquivalent = false; // clear 05126 } 05127 05128 cfgView->setURIEncodedComment(comment); 05129 __SUP_COUT__ << "Table comment was set to:\n\t" << cfgView->getComment() << __E__; 05130 } 05131 catch(...) // erase temporary view before re-throwing error 05132 { 05133 __SUP_COUT__ << "Caught error while editing. Erasing temporary version." << __E__; 05134 config->eraseView(temporaryVersion); 05135 throw; 05136 } 05137 05138 // Note: be careful with any further table operations at this point.. 05139 // must catch errors and erase temporary version on failure. 05140 05141 // only consider it an error if source version was persistent version 05142 // allow it if source version is temporary and we are making a persistent version now 05143 // also, allow it if version tracking is off. 05144 if(retVal < 0 && (!version.isTemporaryVersion() || makeTemporary) && 05145 ConfigurationInterface::isVersionTrackingEnabled()) 05146 { 05147 if(!version.isInvalid() && // if source version was mockup, then consider it 05148 // attempt to create a blank table 05149 !version.isScratchVersion()) // if source version was scratch, then consider 05150 // it attempt to make it persistent 05151 { 05152 __SUP_SS__ 05153 << "No rows were modified! No reason to fill a view with same content." 05154 << __E__; 05155 __SUP_COUT_ERR__ << "\n" << ss.str(); 05156 // delete temporaryVersion 05157 config->eraseView(temporaryVersion); 05158 __SS_THROW__; 05159 } 05160 else if(version.isInvalid()) 05161 __SUP_COUT__ << "This was interpreted as an attempt to create a blank table." 05162 << __E__; 05163 else if(version.isScratchVersion()) 05164 __SUP_COUT__ << "This was interpreted as an attempt to make a persistent " 05165 "version of the scratch table." 05166 << __E__; 05167 else 05168 { 05169 __SUP_SS__; 05170 __THROW__(ss.str() + "impossible!"); 05171 } 05172 } 05173 else if(retVal < 0 && (version.isTemporaryVersion() && !makeTemporary)) 05174 { 05175 __SUP_COUT__ << "Allowing the static data because this is converting from " 05176 "temporary to persistent version." 05177 << __E__; 05178 } 05179 else if(retVal < 0 && !ConfigurationInterface::isVersionTrackingEnabled()) 05180 { 05181 __SUP_COUT__ << "Allowing the static data because version tracking is OFF." 05182 << __E__; 05183 } 05184 else if(retVal < 0) 05185 { 05186 __SUP_SS__ << "This should not be possible! Fatal error." << __E__; 05187 // delete temporaryVersion 05188 config->eraseView(temporaryVersion); 05189 __SS_THROW__; 05190 } 05191 05192 // note: if sourceTableAsIs, accept equivalent versions 05193 saveModifiedVersionXML(xmlOut, 05194 cfgMgr, 05195 tableName, 05196 version, 05197 makeTemporary, 05198 config, 05199 temporaryVersion, 05200 false /*ignoreDuplicates*/, 05201 lookForEquivalent || sourceTableAsIs /*lookForEquivalent*/); 05202 } // end handleCreateTableXML() 05203 catch(std::runtime_error& e) 05204 { 05205 __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__; 05206 xmlOut.addTextElementToData("Error", 05207 "Error saving new view!\n " + std::string(e.what())); 05208 } 05209 catch(...) 05210 { 05211 __SUP_COUT__ << "Error detected!\n\n " << __E__; 05212 xmlOut.addTextElementToData("Error", "Error saving new view! "); 05213 } 05214 05215 //======================================================================================================================== 05216 // refreshUserSession 05217 // Finds/creates the active user session based on username& actionSessionIndex 05218 // 05219 // Returns a configurationMangager instance dedictated to the user. 05220 // This configurationManager will have at least empty instances of all base 05221 // configurations (no null pointers) and will load the backbone configurations to 05222 // specified backboneVersion 05223 // 05224 // If backboneVersion is -1, then latest, and backboneVersion passed by reference 05225 // will be updated 05226 ConfigurationManagerRW* ConfigurationGUISupervisor::refreshUserSession( 05227 std::string username, uint64_t activeSessionIndex, bool refresh) 05228 //, TableVersion& backboneVersion) 05229 { 05230 activeSessionIndex = 05231 0; // make session by username for now! (may never want to change back) 05232 05233 std::stringstream ssMapKey; 05234 ssMapKey << username << ":" << activeSessionIndex; 05235 std::string mapKey = ssMapKey.str(); 05236 __SUP_COUT__ << "Config Session: " << mapKey 05237 << " ... out of size: " << userConfigurationManagers_.size() << __E__; 05238 05239 time_t now = time(0); 05240 05241 // create new config mgr if not one for active session index 05242 if(userConfigurationManagers_.find(mapKey) == userConfigurationManagers_.end()) 05243 { 05244 __SUP_COUT_INFO__ << "Creating new Configuration Manager." << __E__; 05245 userConfigurationManagers_[mapKey] = new ConfigurationManagerRW(username); 05246 05247 // update table info for each new configuration manager 05248 // IMPORTANTLY this also fills all configuration manager pointers with instances, 05249 // so we are not dealing with changing pointers later on 05250 userConfigurationManagers_[mapKey]->getAllTableInfo( 05251 true); // load empty instance of everything important 05252 } 05253 else if(userLastUseTime_.find(mapKey) == userLastUseTime_.end()) 05254 { 05255 __SUP_SS__ << "Fatal error managing userLastUseTime_!" << __E__; 05256 __SUP_COUT_ERR__ << "\n" << ss.str(); 05257 __SS_THROW__; 05258 } 05259 else if(refresh || (now - userLastUseTime_[mapKey]) > 05260 CONFIGURATION_MANAGER_REFRESH_THRESHOLD) // check if should 05261 // refresh all config 05262 // info 05263 { 05264 __SUP_COUT_INFO__ << "Refreshing all table info." << __E__; 05265 userConfigurationManagers_[mapKey]->getAllTableInfo(true); 05266 } 05267 05268 // load backbone configurations always based on backboneVersion 05269 // if backboneVersion is -1, then latest 05270 // backboneVersion = 0;// 05271 // userConfigurationManagers_[mapKey]->loadConfigurationBackbone(backboneVersion); 05272 05273 // update active sessionIndex last use time 05274 userLastUseTime_[mapKey] = now; 05275 05276 // check for stale sessions and remove them (so config user maps do not grow forever) 05277 for(std::map<std::string, time_t>::iterator it = userLastUseTime_.begin(); 05278 it != userLastUseTime_.end(); 05279 ++it) 05280 if(now - it->second > CONFIGURATION_MANAGER_EXPIRATION_TIME) // expired! 05281 { 05282 __SUP_COUT__ << now << ":" << it->second << " = " << now - it->second 05283 << __E__; 05284 delete userConfigurationManagers_[it->first]; // call destructor 05285 if(!(userConfigurationManagers_.erase(it->first))) // erase by key 05286 { 05287 __SUP_SS__ << "Fatal error erasing configuration manager by key!" 05288 << __E__; 05289 __SUP_COUT_ERR__ << "\n" << ss.str(); 05290 __SS_THROW__; 05291 } 05292 userLastUseTime_.erase(it); // erase by iterator 05293 05294 it = userLastUseTime_.begin(); // fail safe.. reset it, to avoid trying to 05295 // understand what happens with the next 05296 // iterator 05297 } 05298 05299 return userConfigurationManagers_[mapKey]; 05300 } 05301 05302 //======================================================================================================================== 05303 // handleCreateTableGroupXML 05304 // 05305 // Save a new TableGroup: 05306 // Search for existing TableGroupKeys for this TableGroup 05307 // Append a "bumped" system key to name 05308 // Save based on list of tableName/TableVersion 05309 // 05310 // tableList parameter is comma separated table name and version 05311 // 05312 // Note: if version of -1 (INVALID/MOCKUP) is given and there are no other existing 05313 // table versions... a new table version is generated using the mockup table. 05314 // 05315 // Table Version Alias Handling: 05316 // Allow table versions to be specified as an alias with ALIAS: preamble. Aliased 05317 // versions will be translated according to the active backbone at activation 05318 // time. 05319 // 05320 // 05321 void ConfigurationGUISupervisor::handleCreateTableGroupXML( 05322 HttpXmlDocument& xmlOut, 05323 ConfigurationManagerRW* cfgMgr, 05324 const std::string& groupName, 05325 const std::string& tableList, 05326 bool allowDuplicates, 05327 bool ignoreWarnings, 05328 const std::string& groupComment, 05329 bool lookForEquivalent) try 05330 { 05331 __SUP_COUT__ << "handleCreateTableGroupXML \n"; 05332 05333 xmlOut.addTextElementToData("AttemptedNewGroupName", groupName); 05334 05335 // make sure not using partial tables or anything weird when creating the group 05336 // so start from scratch and load backbone 05337 const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo(true); 05338 cfgMgr->loadConfigurationBackbone(); 05339 05340 std::map<std::string /*tableName*/, 05341 std::map<std::string /*aliasName*/, TableVersion /*version*/>> 05342 versionAliases = cfgMgr->getVersionAliases(); 05343 for(const auto& aliases : versionAliases) 05344 for(const auto& alias : aliases.second) 05345 __SUP_COUT__ << aliases.first << " " << alias.first << " " << alias.second 05346 << __E__; 05347 05348 std::map<std::string /*name*/, TableVersion /*version*/> groupMembers; 05349 std::map<std::string /*name*/, std::string /*alias*/> groupAliases; 05350 05351 std::string name, versionStr, alias; 05352 TableVersion version; 05353 auto c = tableList.find(',', 0); 05354 auto i = c; 05355 i = 0; // auto used to get proper index/length type 05356 while(c < tableList.length()) 05357 { 05358 // add the table and version pair to the map 05359 name = tableList.substr(i, c - i); 05360 i = c + 1; 05361 c = tableList.find(',', i); 05362 if(c == std::string::npos) // missing version list entry?! 05363 { 05364 __SUP_SS__ << "Incomplete Table Name-Version pair!" << __E__; 05365 __SUP_COUT_ERR__ << "\n" << ss.str(); 05366 xmlOut.addTextElementToData("Error", ss.str()); 05367 return; 05368 } 05369 05370 versionStr = tableList.substr(i, c - i); 05371 i = c + 1; 05372 c = tableList.find(',', i); 05373 05374 //__SUP_COUT__ << "name: " << name << __E__; 05375 //__SUP_COUT__ << "versionStr: " << versionStr << __E__; 05376 05377 // check if version is an alias and convert 05378 if(versionStr.find(ConfigurationManager::ALIAS_VERSION_PREAMBLE) == 0) 05379 { 05380 alias = 05381 versionStr.substr(ConfigurationManager::ALIAS_VERSION_PREAMBLE.size()); 05382 05383 __SUP_COUT__ << "Found alias " << name << " " << versionStr << __E__; 05384 05385 // convert alias to version 05386 if(versionAliases.find(name) != versionAliases.end() && 05387 versionAliases[name].find(alias) != versionAliases[name].end()) 05388 { 05389 version = versionAliases[name][alias]; 05390 __SUP_COUT__ << "version alias '" << alias 05391 << "'translated to: " << version << __E__; 05392 05393 groupAliases[name] = alias; 05394 } 05395 else 05396 { 05397 __SUP_SS__ << "version alias '" 05398 << versionStr.substr( 05399 ConfigurationManager::ALIAS_VERSION_PREAMBLE.size()) 05400 << "' was not found in active version aliases!" << __E__; 05401 __SUP_COUT_ERR__ << "\n" << ss.str(); 05402 xmlOut.addTextElementToData("Error", ss.str()); 05403 return; 05404 } 05405 } 05406 else 05407 version = TableVersion(versionStr); 05408 05409 if(version.isTemporaryVersion()) 05410 { 05411 __SUP_SS__ << "Groups can not be created using temporary member tables. " 05412 << "Table member '" << name << "' with temporary version '" 05413 << version << "' is illegal." << __E__; 05414 xmlOut.addTextElementToData("Error", ss.str()); 05415 return; 05416 } 05417 05418 // enforce that table exists 05419 if(allTableInfo.find(name) == allTableInfo.end()) 05420 { 05421 __SUP_SS__ << "Groups can not be created using mock-up member tables of " 05422 "undefined tables. " 05423 << "Table member '" << name << "' is not defined." << __E__; 05424 xmlOut.addTextElementToData("Error", ss.str()); 05425 return; 05426 } 05427 05428 if(version.isMockupVersion()) 05429 { 05430 // if mockup, then generate a new persistent version to use based on mockup 05431 TableBase* config = cfgMgr->getTableByName(name); 05432 // create a temporary version from the mockup as source version 05433 TableVersion temporaryVersion = config->createTemporaryView(); 05434 __SUP_COUT__ << "\t\ttemporaryVersion: " << temporaryVersion << __E__; 05435 05436 // if other versions exist check for another mockup, and use that instead 05437 __SUP_COUT__ << "Creating version from mock-up for name: " << name 05438 << " inputVersionStr: " << versionStr << __E__; 05439 05440 // set table comment 05441 config->getTemporaryView(temporaryVersion) 05442 ->setComment("Auto-generated from mock-up."); 05443 05444 // finish off the version creation 05445 version = 05446 saveModifiedVersionXML(xmlOut, 05447 cfgMgr, 05448 name, 05449 TableVersion() /*original source is mockup*/, 05450 false /* makeTemporary */, 05451 config, 05452 temporaryVersion /*temporary modified version*/, 05453 false /*ignore duplicates*/, 05454 true /*look for equivalent*/); 05455 05456 __SUP_COUT__ << "Using mockup version: " << version << __E__; 05457 } 05458 05459 //__SUP_COUT__ << "version: " << version << __E__; 05460 groupMembers[name] = version; 05461 } // end member verification loop 05462 05463 __COUTV__(StringMacros::mapToString(groupAliases)); 05464 05465 if(!allowDuplicates) 05466 { 05467 __SUP_COUT__ << "Checking for duplicate groups..." << __E__; 05468 TableGroupKey foundKey = 05469 cfgMgr->findTableGroup(groupName, groupMembers, groupAliases); 05470 05471 if(!foundKey.isInvalid()) 05472 { 05473 // return found equivalent key 05474 xmlOut.addTextElementToData("TableGroupName", groupName); 05475 xmlOut.addTextElementToData("TableGroupKey", foundKey.toString()); 05476 05477 if(lookForEquivalent) 05478 { 05479 __SUP_COUT__ << "Found equivalent group key (" << foundKey << ") for " 05480 << groupName << "." << __E__; 05481 // allow this equivalent group to be the response without an error 05482 xmlOut.addTextElementToData("foundEquivalentKey", "1"); // indicator 05483 05484 // insert get table info 05485 handleGetTableGroupXML(xmlOut, cfgMgr, groupName, foundKey); 05486 return; 05487 } 05488 else // treat as error, if not looking for equivalent 05489 { 05490 __SUP_COUT__ << "Treating duplicate group as error." << __E__; 05491 __SUP_SS__ << ("Failed to create table group: " + groupName + 05492 ". It is a duplicate of an existing group key (" + 05493 foundKey.toString() + ")"); 05494 __SUP_COUT_ERR__ << ss.str() << __E__; 05495 xmlOut.addTextElementToData("Error", ss.str()); 05496 return; 05497 } 05498 } 05499 05500 __SUP_COUT__ << "Check for duplicate groups complete." << __E__; 05501 } 05502 05503 // check the group for errors before creating group 05504 try 05505 { 05506 cfgMgr->loadMemberMap(groupMembers); 05507 05508 std::string accumulateErrors = ""; 05509 for(auto& groupMemberPair : groupMembers) 05510 { 05511 TableView* cfgViewPtr = 05512 cfgMgr->getTableByName(groupMemberPair.first)->getViewP(); 05513 if(cfgViewPtr->getDataColumnSize() != cfgViewPtr->getNumberOfColumns() || 05514 cfgViewPtr->getSourceColumnMismatch() != 05515 0) // check for column size mismatch 05516 { 05517 __SUP_SS__ << "\n\nThere were errors found in loading a member table " 05518 << groupMemberPair.first << ":v" << cfgViewPtr->getVersion() 05519 << ". Please see the details below:\n\n" 05520 << "The source column size was found to be " 05521 << cfgViewPtr->getDataColumnSize() 05522 << ", and the current number of columns for this table is " 05523 << cfgViewPtr->getNumberOfColumns() 05524 << ". This resulted in a count of " 05525 << cfgViewPtr->getSourceColumnMismatch() 05526 << " source column mismatches, and a count of " 05527 << cfgViewPtr->getSourceColumnMissing() 05528 << " table entries missing in " 05529 << cfgViewPtr->getNumberOfRows() << " row(s) of data." 05530 << __E__; 05531 05532 const std::set<std::string> srcColNames = 05533 cfgViewPtr->getSourceColumnNames(); 05534 ss << "\n\nSource column names were as follows:\n"; 05535 char index = 'a'; 05536 for(auto& srcColName : srcColNames) 05537 ss << "\n\t" << index++ << ". " << srcColName; 05538 ss << __E__; 05539 05540 std::set<std::string> destColNames = cfgViewPtr->getColumnStorageNames(); 05541 ss << "\n\nCurrent table column names are as follows:\n"; 05542 index = 'a'; 05543 for(auto& destColName : destColNames) 05544 ss << "\n\t" << index++ << ". " << destColName; 05545 ss << __E__; 05546 05547 __SUP_COUT_ERR__ << "\n" << ss.str(); 05548 xmlOut.addTextElementToData("Error", ss.str()); 05549 return; 05550 } 05551 } 05552 } 05553 catch(std::runtime_error& e) 05554 { 05555 __SUP_SS__ << "Failed to create config group: " << groupName 05556 << ".\nThere were problems loading the chosen members:\n\n" 05557 << e.what() << __E__; 05558 __SUP_COUT_ERR__ << "\n" << ss.str(); 05559 xmlOut.addTextElementToData("Error", ss.str()); 05560 return; 05561 } 05562 catch(...) 05563 { 05564 __SUP_SS__ << "Failed to create config group: " << groupName << __E__; 05565 __SUP_COUT_ERR__ << "\n" << ss.str(); 05566 xmlOut.addTextElementToData("Error", ss.str()); 05567 return; 05568 } 05569 05570 // check the tree for warnings before creating group 05571 std::string accumulateTreeErrs; 05572 cfgMgr->getChildren(&groupMembers, &accumulateTreeErrs); 05573 if(accumulateTreeErrs != "") 05574 { 05575 __SUP_COUT_WARN__ << "\n" << accumulateTreeErrs << __E__; 05576 if(!ignoreWarnings) 05577 { 05578 xmlOut.addTextElementToData("TreeErrors", accumulateTreeErrs); 05579 return; 05580 } 05581 } 05582 05583 TableGroupKey newKey; 05584 try 05585 { 05586 __COUT__ << "Saving new group..." << __E__; 05587 newKey = cfgMgr->saveNewTableGroup( 05588 groupName, groupMembers, groupComment, &groupAliases); 05589 } 05590 catch(std::runtime_error& e) 05591 { 05592 __SUP_COUT_ERR__ << "Failed to create config group: " << groupName << __E__; 05593 __SUP_COUT_ERR__ << "\n\n" << e.what() << __E__; 05594 xmlOut.addTextElementToData( 05595 "Error", "Failed to create table group: " + groupName + ".\n\n" + e.what()); 05596 return; 05597 } 05598 catch(...) 05599 { 05600 __SUP_COUT_ERR__ << "Failed to create table group: " << groupName << __E__; 05601 xmlOut.addTextElementToData("Error", 05602 "Failed to create table group: " + groupName); 05603 return; 05604 } 05605 05606 // insert get table info 05607 __COUT__ << "Loading new table group..." << __E__; 05608 handleGetTableGroupXML(xmlOut, cfgMgr, groupName, newKey); 05609 } 05610 catch(std::runtime_error& e) 05611 { 05612 __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__; 05613 xmlOut.addTextElementToData("Error", 05614 "Error saving table group! " + std::string(e.what())); 05615 } 05616 catch(...) 05617 { 05618 __SUP_COUT__ << "Unknown Error detected!\n\n " << __E__; 05619 xmlOut.addTextElementToData("Error", "Error saving table group! "); 05620 } 05621 05622 //======================================================================================================================== 05623 // handleDeleteTableInfoXML 05624 // 05625 // return nothing except Error in xmlOut 05626 // 05627 void ConfigurationGUISupervisor::handleDeleteTableInfoXML(HttpXmlDocument& xmlOut, 05628 ConfigurationManagerRW* cfgMgr, 05629 std::string& tableName) 05630 { 05631 if(0 == rename((TABLE_INFO_PATH + tableName + TABLE_INFO_EXT).c_str(), 05632 (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused").c_str())) 05633 __SUP_COUT_INFO__ << ("Table Info File successfully renamed: " + 05634 (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused")) 05635 << __E__; 05636 else 05637 { 05638 __SUP_COUT_ERR__ << ("Error renaming file to " + 05639 (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused")) 05640 << __E__; 05641 05642 xmlOut.addTextElementToData( 05643 "Error", 05644 ("Error renaming Table Info File to " + 05645 (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused"))); 05646 return; 05647 } 05648 05649 // reload all with refresh to remove new table 05650 cfgMgr->getAllTableInfo(true); 05651 } // end handleDeleteTableInfoXML() 05652 05653 //======================================================================================================================== 05654 // handleSaveTableInfoXML 05655 // 05656 // write new info file for tableName based CSV column info 05657 // data="type,name,dataType;type,name,dataType;..." 05658 // return resulting handleGetTableXML mock-up view 05659 // 05660 void ConfigurationGUISupervisor::handleSaveTableInfoXML( 05661 HttpXmlDocument& xmlOut, 05662 ConfigurationManagerRW* cfgMgr, 05663 std::string& tableName, 05664 const std::string& data, 05665 const std::string& tableDescription, 05666 const std::string& columnChoicesCSV, 05667 bool allowOverwrite) 05668 { 05669 // create all caps name and validate 05670 // only allow alpha-numeric names with "Table" at end 05671 std::string capsName; 05672 try 05673 { 05674 capsName = TableBase::convertToCaps(tableName, true); 05675 } 05676 catch(std::runtime_error& e) 05677 { // error! non-alpha 05678 xmlOut.addTextElementToData("Error", e.what()); 05679 return; 05680 } 05681 05682 if(!allowOverwrite) 05683 { 05684 FILE* fp = fopen((TABLE_INFO_PATH + tableName + TABLE_INFO_EXT).c_str(), "r"); 05685 if(fp) 05686 { 05687 fclose(fp); 05688 xmlOut.addTextElementToData("TableName", tableName); 05689 xmlOut.addTextElementToData("OverwriteError", "1"); 05690 xmlOut.addTextElementToData( 05691 "Error", 05692 "File already exists! ('" + 05693 (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT) + "')"); 05694 return; 05695 } 05696 } 05697 05698 __SUP_COUT__ << "capsName=" << capsName << __E__; 05699 __SUP_COUT__ << "tableName=" << tableName << __E__; 05700 __SUP_COUT__ << "tableDescription=" << tableDescription << __E__; 05701 __SUP_COUT__ << "columnChoicesCSV=" << columnChoicesCSV << __E__; 05702 05703 // create preview string to validate column info before write to file 05704 std::stringstream outss; 05705 05706 outss << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n"; 05707 outss << "\t<ROOT xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " 05708 "xsi:noNamespaceSchemaLocation=\"TableInfo.xsd\">\n"; 05709 outss << "\t\t<TABLE Name=\"" << tableName << "\">\n"; 05710 outss << "\t\t\t<VIEW Name=\"" << capsName 05711 << "\" Type=\"File,Database,DatabaseTest\" Description=\"" << tableDescription 05712 << "\">\n"; 05713 05714 // each column is represented by 3 fields 05715 // - type, name, dataType 05716 int i = 0; // use to parse data std::string 05717 int j = data.find(',', i); // find next field delimiter 05718 int k = data.find(';', i); // find next col delimiter 05719 05720 std::istringstream columnChoicesISS(columnChoicesCSV); 05721 std::string columnChoicesString; 05722 std::string columnType; 05723 05724 while(k != (int)(std::string::npos)) 05725 { 05726 // type 05727 columnType = data.substr(i, j - i); 05728 outss << "\t\t\t\t<COLUMN Type=\""; 05729 outss << columnType; 05730 05731 i = j + 1; 05732 j = data.find(',', i); // find next field delimiter 05733 05734 // name and storage name 05735 outss << "\" \t Name=\""; 05736 capsName = data.substr(i, j - i); // not caps yet 05737 outss << capsName; 05738 outss << "\" \t StorageName=\""; 05739 05740 try 05741 { 05742 outss << TableBase::convertToCaps(capsName); // now caps 05743 } 05744 catch(std::runtime_error& e) 05745 { // error! non-alpha 05746 xmlOut.addTextElementToData("Error", 05747 std::string("For column name '") + 05748 data.substr(i, j - i) + "' - " + e.what()); 05749 return; 05750 } 05751 05752 i = j + 1; 05753 j = data.find(',', i); // find next field delimiter 05754 05755 // data type 05756 outss << "\" \t DataType=\""; 05757 outss << data.substr(i, k - i); 05758 05759 // fixed data choices for TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA 05760 getline(columnChoicesISS, columnChoicesString, ';'); 05761 //__SUP_COUT__ << "columnChoicesString = " << columnChoicesString << __E__; 05762 outss << "\" \t DataChoices=\""; 05763 outss << columnChoicesString; 05764 05765 // end column info 05766 outss << "\"/>\n"; 05767 05768 i = k + 1; 05769 j = data.find(',', i); // find next field delimiter 05770 k = data.find(';', i); // find new col delimiter 05771 } 05772 05773 outss << "\t\t\t</VIEW>\n"; 05774 outss << "\t\t</TABLE>\n"; 05775 outss << "\t</ROOT>\n"; 05776 05777 __SUP_COUT__ << outss.str() << __E__; 05778 05779 FILE* fp = fopen((TABLE_INFO_PATH + tableName + TABLE_INFO_EXT).c_str(), "w"); 05780 if(!fp) 05781 { 05782 xmlOut.addTextElementToData("Error", 05783 "Failed to open destination Table Info file:" + 05784 (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT)); 05785 return; 05786 } 05787 05788 fprintf(fp, "%s", outss.str().c_str()); 05789 fclose(fp); 05790 05791 // reload all config info with refresh AND reset to pick up possibly new config 05792 // check for errors related to this tableName 05793 std::string accumulatedErrors = ""; 05794 cfgMgr->getAllTableInfo(true, &accumulatedErrors, tableName); 05795 05796 // if errors associated with this config name stop and report 05797 if(accumulatedErrors != "") 05798 { 05799 __SUP_SS__ << ("The new version of the '" + tableName + 05800 "' table column info was saved, however errors were detected " 05801 "reading back the table '" + 05802 tableName + "' after the save attempt:\n\n" + accumulatedErrors) 05803 << __E__; 05804 05805 __SUP_COUT_ERR__ << ss.str() << __E__; 05806 xmlOut.addTextElementToData("Error", ss.str()); 05807 05808 // if error detected reading back then move the saved table info to 05809 // .unused 05810 // // This was disabled by RAR on 11/4/2016.. (just keep broken info files) 05811 // // ... especially since now there is a Delete button 05812 if(0) 05813 { 05814 // table info is illegal so report error, and disable file 05815 05816 // if error detected //move file to ".unused" 05817 if(0 == 05818 rename((TABLE_INFO_PATH + tableName + TABLE_INFO_EXT).c_str(), 05819 (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused").c_str())) 05820 __SUP_COUT_INFO__ 05821 << ("File successfully renamed: " + 05822 (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused")) 05823 << __E__; 05824 else 05825 05826 __SUP_COUT_ERR__ 05827 << ("Error renaming file to " + 05828 (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused")) 05829 << __E__; 05830 05831 // reload all with refresh to remove new table 05832 cfgMgr->getAllTableInfo(true); 05833 } 05834 return; 05835 } 05836 05837 // return the new table info 05838 handleGetTableXML(xmlOut, cfgMgr, tableName, TableVersion()); 05839 05840 // debug all table column info 05841 // FIXME -- possibly remove this debug feature in future 05842 const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo(); 05843 05844 // give a print out of currently illegal table column info 05845 __SUP_COUT_INFO__ << "Looking for errors in all table column info..." << __E__; 05846 for(const auto& cfgInfo : allTableInfo) 05847 { 05848 try 05849 { 05850 cfgMgr->getTableByName(cfgInfo.first)->getMockupViewP()->init(); 05851 } 05852 catch(std::runtime_error& e) 05853 { 05854 __SUP_COUT_WARN__ << "\n\n##############################################\n" 05855 << "Error identified in column info of table '" 05856 << cfgInfo.first << "':\n\n" 05857 << e.what() << "\n\n" 05858 << __E__; 05859 } 05860 } 05861 } // end handleSaveTableInfoXML() 05862 05863 //======================================================================================================================== 05864 // handleSetGroupAliasInBackboneXML 05865 // open current backbone 05866 // modify GroupAliases 05867 // save as new version of groupAliases 05868 // return new version of groupAliases 05869 // 05870 // Note: very similar to ConfigurationGUISupervisor::handleSetVersionAliasInBackboneXML 05871 void ConfigurationGUISupervisor::handleSetGroupAliasInBackboneXML( 05872 HttpXmlDocument& xmlOut, 05873 ConfigurationManagerRW* cfgMgr, 05874 const std::string& groupAlias, 05875 const std::string& groupName, 05876 TableGroupKey groupKey, 05877 const std::string& author) try 05878 { 05879 cfgMgr->loadConfigurationBackbone(); 05880 std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions(); 05881 05882 const std::string groupAliasesTableName = 05883 ConfigurationManager::GROUP_ALIASES_TABLE_NAME; 05884 if(activeVersions.find(groupAliasesTableName) == activeVersions.end()) 05885 { 05886 __SUP_SS__ << "Active version of " << groupAliasesTableName << " missing!" 05887 << __E__; 05888 xmlOut.addTextElementToData("Error", ss.str()); 05889 return; 05890 } 05891 05892 // put all old backbone versions in xmlOut 05893 const std::set<std::string> backboneMembers = cfgMgr->getBackboneMemberNames(); 05894 for(auto& memberName : backboneMembers) 05895 { 05896 __SUP_COUT__ << "activeVersions[\"" << memberName 05897 << "\"]=" << activeVersions[memberName] << __E__; 05898 05899 xmlOut.addTextElementToData("oldBackboneName", memberName); 05900 xmlOut.addTextElementToData("oldBackboneVersion", 05901 activeVersions[memberName].toString()); 05902 } 05903 05904 // make a temporary version from active view 05905 // modify the chosen groupAlias row 05906 // save as new version 05907 05908 TableBase* config = cfgMgr->getTableByName(groupAliasesTableName); 05909 TableVersion originalVersion = activeVersions[groupAliasesTableName]; 05910 TableVersion temporaryVersion = config->createTemporaryView(originalVersion); 05911 05912 __SUP_COUT__ << "\t\t temporaryVersion: " << temporaryVersion << __E__; 05913 bool isDifferent = false; 05914 05915 try 05916 { 05917 TableView* configView = config->getTemporaryView(temporaryVersion); 05918 05919 unsigned int col = configView->findCol("GroupKeyAlias"); 05920 05921 // only make a new version if we are changing compared to active backbone 05922 05923 unsigned int row = -1; 05924 // find groupAlias row 05925 try 05926 { 05927 row = configView->findRow(col, groupAlias); 05928 } 05929 catch(...) 05930 { 05931 } 05932 if(row == (unsigned int)-1) // if row not found then add a row 05933 { 05934 isDifferent = true; 05935 row = configView->addRow(); 05936 05937 // set all columns in new row 05938 col = configView->findCol("CommentDescription"); 05939 configView->setValue( 05940 "This Group Alias was automatically setup by the server.", row, col); 05941 col = configView->findCol("GroupKeyAlias"); 05942 configView->setValue(groupAlias, row, col); 05943 } 05944 05945 __SUP_COUT__ << "\t\t row: " << row << __E__; 05946 05947 col = configView->findCol("GroupName"); 05948 05949 __SUP_COUT__ << "\t\t groupName: " << groupName << " vs " 05950 << configView->getDataView()[row][col] << __E__; 05951 if(groupName != configView->getDataView()[row][col]) 05952 { 05953 configView->setValue(groupName, row, col); 05954 isDifferent = true; 05955 } 05956 05957 col = configView->findCol("GroupKey"); 05958 __SUP_COUT__ << "\t\t groupKey: " << groupKey << " vs " 05959 << configView->getDataView()[row][col] << __E__; 05960 if(groupKey.toString() != configView->getDataView()[row][col]) 05961 { 05962 configView->setValue(groupKey.toString(), row, col); 05963 isDifferent = true; 05964 } 05965 05966 if(isDifferent) // set author/time of new version if different 05967 { 05968 configView->setValue(author, row, configView->findCol("Author")); 05969 configView->setValue( 05970 time(0), row, configView->findCol("RecordInsertionTime")); 05971 } 05972 } 05973 catch(...) 05974 { 05975 __SUP_COUT_ERR__ << "Error editing Group Alias view!" << __E__; 05976 05977 // delete temporaryVersion 05978 config->eraseView(temporaryVersion); 05979 throw; 05980 } 05981 05982 TableVersion newAssignedVersion; 05983 if(isDifferent) // make new version if different 05984 { 05985 __SUP_COUT__ << "\t\t**************************** Save as new table version" 05986 << __E__; 05987 05988 // newAssignedVersion = 05989 // cfgMgr->saveNewTable(groupAliasesTableName,temporaryVersion); 05990 05991 // save or find equivalent 05992 05993 newAssignedVersion = saveModifiedVersionXML(xmlOut, 05994 cfgMgr, 05995 config->getTableName(), 05996 originalVersion, 05997 false /*makeTemporary*/, 05998 config, 05999 temporaryVersion, 06000 false /*ignoreDuplicates*/, 06001 true /*lookForEquivalent*/); 06002 } 06003 else // use existing version 06004 { 06005 __SUP_COUT__ 06006 << "\t\t**************************** Using the existing table version" 06007 << __E__; 06008 06009 // delete temporaryVersion 06010 config->eraseView(temporaryVersion); 06011 newAssignedVersion = activeVersions[groupAliasesTableName]; 06012 06013 xmlOut.addTextElementToData("savedName", groupAliasesTableName); 06014 xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString()); 06015 } 06016 06017 __SUP_COUT__ << "\t\t newAssignedVersion: " << newAssignedVersion << __E__; 06018 } 06019 catch(std::runtime_error& e) 06020 { 06021 __SUP_COUT_ERR__ << "Error detected!\n\n " << e.what() << __E__; 06022 xmlOut.addTextElementToData( 06023 "Error", "Error saving new Group Alias view!\n " + std::string(e.what())); 06024 } 06025 catch(...) 06026 { 06027 __SUP_COUT_ERR__ << "Error detected!\n\n " << __E__; 06028 xmlOut.addTextElementToData("Error", "Error saving new Group Alias view! "); 06029 } 06030 06031 //======================================================================================================================== 06032 // handleSetVersionAliasInBackboneXML 06033 // open current backbone 06034 // modify VersionAliases 06035 // save as new version of VersionAliases 06036 // return new version of VersionAliases 06037 // 06038 // Note: very similar to ConfigurationGUISupervisor::handleSetGroupAliasInBackboneXML 06039 void ConfigurationGUISupervisor::handleSetVersionAliasInBackboneXML( 06040 HttpXmlDocument& xmlOut, 06041 ConfigurationManagerRW* cfgMgr, 06042 const std::string& versionAlias, 06043 const std::string& tableName, 06044 TableVersion version, 06045 const std::string& author) try 06046 { 06047 cfgMgr->loadConfigurationBackbone(); 06048 std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions(); 06049 06050 const std::string versionAliasesTableName = 06051 ConfigurationManager::VERSION_ALIASES_TABLE_NAME; 06052 if(activeVersions.find(versionAliasesTableName) == activeVersions.end()) 06053 { 06054 __SUP_SS__ << "Active version of " << versionAliasesTableName << " missing!" 06055 << __E__; 06056 xmlOut.addTextElementToData("Error", ss.str()); 06057 return; 06058 } 06059 06060 // put all old backbone versions in xmlOut 06061 const std::set<std::string> backboneMembers = cfgMgr->getBackboneMemberNames(); 06062 for(auto& memberName : backboneMembers) 06063 { 06064 __SUP_COUT__ << "activeVersions[\"" << memberName 06065 << "\"]=" << activeVersions[memberName] << __E__; 06066 06067 xmlOut.addTextElementToData("oldBackboneName", memberName); 06068 xmlOut.addTextElementToData("oldBackboneVersion", 06069 activeVersions[memberName].toString()); 06070 } 06071 06072 // make a temporary version from active view 06073 // modify the chosen versionAlias row 06074 // save as new version 06075 06076 TableBase* config = cfgMgr->getTableByName(versionAliasesTableName); 06077 TableVersion originalVersion = activeVersions[versionAliasesTableName]; 06078 TableVersion temporaryVersion = config->createTemporaryView(originalVersion); 06079 06080 __SUP_COUT__ << "\t\t temporaryVersion: " << temporaryVersion << __E__; 06081 06082 bool isDifferent = false; 06083 06084 try 06085 { 06086 TableView* configView = config->getTemporaryView(temporaryVersion); 06087 06088 unsigned int col; 06089 unsigned int col2 = configView->findCol("VersionAlias"); 06090 unsigned int col3 = configView->findCol("TableName"); 06091 06092 // only make a new version if we are changing compared to active backbone 06093 06094 unsigned int row = -1; 06095 // find tableName, versionAlias pair 06096 // NOTE: only accept the first pair, repeats are ignored. 06097 try 06098 { 06099 unsigned int tmpRow = -1; 06100 do 06101 { // start looking from beyond last find 06102 tmpRow = configView->findRow(col3, tableName, tmpRow + 1); 06103 } while(configView->getDataView()[tmpRow][col2] != versionAlias); 06104 // at this point the first pair was found! (else exception was thrown) 06105 row = tmpRow; 06106 } 06107 catch(...) 06108 { 06109 } 06110 if(row == (unsigned int)-1) // if row not found then add a row 06111 { 06112 isDifferent = true; 06113 row = configView->addRow(); 06114 06115 // set all columns in new row 06116 col = configView->findCol("CommentDescription"); 06117 configView->setValue( 06118 std::string("Entry was added by server in ") + 06119 "ConfigurationGUISupervisor::setVersionAliasInActiveBackbone().", 06120 row, 06121 col); 06122 06123 col = configView->findCol("VersionAliasUID"); 06124 configView->setValue( 06125 tableName.substr(0, tableName.rfind("Table")) + versionAlias, row, col); 06126 06127 configView->setValue(versionAlias, row, col2); 06128 configView->setValue(tableName, row, col3); 06129 } 06130 06131 __SUP_COUT__ << "\t\t row: " << row << __E__; 06132 06133 col = configView->findCol("Version"); 06134 __SUP_COUT__ << "\t\t version: " << version << " vs " 06135 << configView->getDataView()[row][col] << __E__; 06136 if(version.toString() != configView->getDataView()[row][col]) 06137 { 06138 configView->setValue(version.toString(), row, col); 06139 isDifferent = true; 06140 } 06141 06142 if(isDifferent) // set author/time of new version if different 06143 { 06144 configView->setValue(author, row, configView->findCol("Author")); 06145 configView->setValue( 06146 time(0), row, configView->findCol("RecordInsertionTime")); 06147 } 06148 } 06149 catch(...) 06150 { 06151 __SUP_COUT_ERR__ << "Error editing Version Alias view!" << __E__; 06152 06153 // delete temporaryVersion 06154 config->eraseView(temporaryVersion); 06155 throw; 06156 } 06157 06158 TableVersion newAssignedVersion; 06159 if(isDifferent) // make new version if different 06160 { 06161 __SUP_COUT__ << "\t\t**************************** Save as new table version" 06162 << __E__; 06163 06164 // newAssignedVersion = 06165 // cfgMgr->saveNewTable(versionAliasesTableName,temporaryVersion); 06166 06167 newAssignedVersion = saveModifiedVersionXML(xmlOut, 06168 cfgMgr, 06169 config->getTableName(), 06170 originalVersion, 06171 false /*makeTemporary*/, 06172 config, 06173 temporaryVersion, 06174 false /*ignoreDuplicates*/, 06175 true /*lookForEquivalent*/); 06176 } 06177 else // use existing version 06178 { 06179 __SUP_COUT__ << "\t\t**************************** Using existing table version" 06180 << __E__; 06181 06182 // delete temporaryVersion 06183 config->eraseView(temporaryVersion); 06184 newAssignedVersion = activeVersions[versionAliasesTableName]; 06185 06186 xmlOut.addTextElementToData("savedAlias", versionAliasesTableName); 06187 xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString()); 06188 } 06189 06190 __SUP_COUT__ << "\t\t newAssignedVersion: " << newAssignedVersion << __E__; 06191 } 06192 catch(std::runtime_error& e) 06193 { 06194 __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__; 06195 xmlOut.addTextElementToData( 06196 "Error", "Error saving new Version Alias view!\n " + std::string(e.what())); 06197 } 06198 catch(...) 06199 { 06200 __SUP_COUT__ << "Error detected!\n\n " << __E__; 06201 xmlOut.addTextElementToData("Error", "Error saving new Version Alias view! "); 06202 } 06203 06204 //======================================================================================================================== 06205 // handleAliasGroupMembersInBackboneXML 06206 // open current backbone 06207 // modify VersionAliases 06208 // save as new version of VersionAliases 06209 // return new version of VersionAliases 06210 void ConfigurationGUISupervisor::handleAliasGroupMembersInBackboneXML( 06211 HttpXmlDocument& xmlOut, 06212 ConfigurationManagerRW* cfgMgr, 06213 const std::string& versionAlias, 06214 const std::string& groupName, 06215 TableGroupKey groupKey, 06216 const std::string& author) try 06217 { 06218 cfgMgr->loadConfigurationBackbone(); 06219 std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions(); 06220 06221 const std::string versionAliasesTableName = 06222 ConfigurationManager::VERSION_ALIASES_TABLE_NAME; 06223 if(activeVersions.find(versionAliasesTableName) == activeVersions.end()) 06224 { 06225 __SUP_SS__ << "Active version of " << versionAliasesTableName << " missing!" 06226 << __E__; 06227 xmlOut.addTextElementToData("Error", ss.str()); 06228 return; 06229 } 06230 06231 // put all old backbone versions in xmlOut 06232 const std::set<std::string> backboneMembers = cfgMgr->getBackboneMemberNames(); 06233 for(auto& memberName : backboneMembers) 06234 { 06235 __SUP_COUT__ << "activeVersions[\"" << memberName 06236 << "\"]=" << activeVersions[memberName] << __E__; 06237 06238 xmlOut.addTextElementToData("oldBackboneName", memberName); 06239 xmlOut.addTextElementToData("oldBackboneVersion", 06240 activeVersions[memberName].toString()); 06241 } 06242 06243 // make a temporary version from active view 06244 // modify the chosen versionAlias row 06245 // save as new version 06246 06247 TableBase* config = cfgMgr->getTableByName(versionAliasesTableName); 06248 TableVersion temporaryVersion = 06249 config->createTemporaryView(activeVersions[versionAliasesTableName]); 06250 06251 __SUP_COUT__ << "\t\t temporaryVersion: " << temporaryVersion << __E__; 06252 06253 TableView* configView = config->getTemporaryView(temporaryVersion); 06254 06255 // only make a new version if we are changing compared to active backbone 06256 bool isDifferent = false; 06257 06258 // get member names and versions 06259 std::map<std::string /*name*/, TableVersion /*version*/> memberMap; 06260 try 06261 { 06262 cfgMgr->loadTableGroup(groupName, 06263 groupKey, 06264 false /*doActivate*/, 06265 &memberMap, 06266 0, 06267 0, 06268 0, 06269 0, 06270 0, // defaults 06271 true /*doNotLoadMember*/); 06272 } 06273 catch(...) 06274 { 06275 xmlOut.addTextElementToData( 06276 "Error", 06277 "Table group \"" + TableGroupKey::getFullGroupString(groupName, groupKey) + 06278 "\" can not be retrieved!"); 06279 return; 06280 } 06281 06282 unsigned int col; 06283 unsigned int col2 = configView->findCol("VersionAlias"); 06284 unsigned int col3 = configView->findCol("TableName"); 06285 06286 for(auto& memberPair : memberMap) 06287 { 06288 bool thisMemberIsDifferent = false; 06289 unsigned int row = -1; 06290 06291 __SUP_COUT__ << "Adding alias for " << memberPair.first << "_v" 06292 << memberPair.second << __E__; 06293 06294 // find tableName, versionAlias pair 06295 // NOTE: only accept the first pair, repeats are ignored. 06296 try 06297 { 06298 unsigned int tmpRow = -1; 06299 do 06300 { // start looking from beyond last find 06301 tmpRow = configView->findRow(col3, memberPair.first, tmpRow + 1); 06302 __SUP_COUT__ << configView->getDataView()[tmpRow][col2] << __E__; 06303 } while(configView->getDataView()[tmpRow][col2] != versionAlias); 06304 // at this point the first pair was found! (else exception was thrown) 06305 row = tmpRow; 06306 } 06307 catch(...) 06308 { 06309 } 06310 if(row == (unsigned int)-1) // if row not found then add a row 06311 { 06312 thisMemberIsDifferent = true; 06313 row = configView->addRow(); 06314 06315 // set all columns in new row 06316 col = configView->findCol("CommentDescription"); 06317 configView->setValue( 06318 std::string("Entry was added by server in ") + 06319 "ConfigurationGUISupervisor::setVersionAliasInActiveBackbone().", 06320 row, 06321 col); 06322 06323 col = configView->getColUID(); 06324 configView->setValue( 06325 memberPair.first.substr(0, memberPair.first.rfind("Table")) + 06326 versionAlias, 06327 row, 06328 col); 06329 06330 configView->setValue(versionAlias, row, col2); 06331 configView->setValue(memberPair.first, row, col3); 06332 } 06333 06334 __SUP_COUT__ << "\t\t row: " << row << __E__; 06335 06336 col = configView->findCol("Version"); 06337 __SUP_COUT__ << "\t\t col: " << col << __E__; 06338 __SUP_COUT__ << "\t\t version: " << memberPair.second << " vs " 06339 << configView->getDataView()[row][col] << __E__; 06340 if(memberPair.second.toString() != configView->getDataView()[row][col]) 06341 { 06342 configView->setValue(memberPair.second.toString(), row, col); 06343 thisMemberIsDifferent = true; 06344 } 06345 06346 if(thisMemberIsDifferent) // change author and time if row is different 06347 { 06348 configView->setValue(author, row, configView->findCol("Author")); 06349 configView->setValue( 06350 time(0), row, configView->findCol("RecordInsertionTime")); 06351 } 06352 06353 if(thisMemberIsDifferent) 06354 isDifferent = true; 06355 } 06356 06357 // configView->print(); 06358 06359 TableVersion newAssignedVersion; 06360 if(isDifferent) // make new version if different 06361 { 06362 __SUP_COUT__ << "\t\t**************************** Save as new table version" 06363 << __E__; 06364 06365 newAssignedVersion = 06366 cfgMgr->saveNewTable(versionAliasesTableName, temporaryVersion); 06367 } 06368 else // use existing version 06369 { 06370 __SUP_COUT__ << "\t\t**************************** Using existing table version" 06371 << __E__; 06372 06373 // delete temporaryVersion 06374 config->eraseView(temporaryVersion); 06375 newAssignedVersion = activeVersions[versionAliasesTableName]; 06376 } 06377 06378 xmlOut.addTextElementToData("savedAlias", versionAliasesTableName); 06379 xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString()); 06380 __SUP_COUT__ << "\t\t newAssignedVersion: " << newAssignedVersion << __E__; 06381 } 06382 catch(std::runtime_error& e) 06383 { 06384 __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__; 06385 xmlOut.addTextElementToData( 06386 "Error", "Error saving new Version Alias view!\n " + std::string(e.what())); 06387 } 06388 catch(...) 06389 { 06390 __SUP_COUT__ << "Error detected!\n\n " << __E__; 06391 xmlOut.addTextElementToData("Error", "Error saving new Version Alias view! "); 06392 } 06393 06394 //======================================================================================================================== 06395 // handleGroupAliasesXML 06396 // 06397 // return aliases and backbone groupAlias table version 06398 // 06399 // return this information: 06400 // <backbone groupTableName=xxx version=xxx> 06401 // <group alias=xxx name=xxx key=xxx comment=xxx> 06402 // <group alias=xxx name=xxx key=xxx comment=xxx> 06403 // ... 06404 // 06405 void ConfigurationGUISupervisor::handleGroupAliasesXML(HttpXmlDocument& xmlOut, 06406 ConfigurationManagerRW* cfgMgr) 06407 { 06408 cfgMgr->loadConfigurationBackbone(); 06409 std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions(); 06410 06411 std::string groupAliasesTableName = ConfigurationManager::GROUP_ALIASES_TABLE_NAME; 06412 if(activeVersions.find(groupAliasesTableName) == activeVersions.end()) 06413 { 06414 __SUP_SS__ << "\nActive version of " << groupAliasesTableName << " missing! " 06415 << groupAliasesTableName 06416 << " is a required member of the Backbone table group." 06417 << "\n\nLikely you need to activate a valid Backbone table group." 06418 << __E__; 06419 xmlOut.addTextElementToData("Error", ss.str()); 06420 return; 06421 } 06422 __SUP_COUT__ << "activeVersions[\"" << groupAliasesTableName 06423 << "\"]=" << activeVersions[groupAliasesTableName] << __E__; 06424 xmlOut.addTextElementToData("GroupAliasesTableName", groupAliasesTableName); 06425 xmlOut.addTextElementToData("GroupAliasesTableVersion", 06426 activeVersions[groupAliasesTableName].toString()); 06427 06428 std::vector<std::pair<std::string, ConfigurationTree>> aliasNodePairs = 06429 cfgMgr->getNode(groupAliasesTableName).getChildren(); 06430 06431 std::string groupName, groupKey, groupComment, groupType; 06432 for(auto& aliasNodePair : aliasNodePairs) 06433 { 06434 groupName = aliasNodePair.second.getNode("GroupName").getValueAsString(); 06435 groupKey = aliasNodePair.second.getNode("GroupKey").getValueAsString(); 06436 06437 xmlOut.addTextElementToData("GroupAlias", aliasNodePair.first); 06438 xmlOut.addTextElementToData("GroupName", groupName); 06439 xmlOut.addTextElementToData("GroupKey", groupKey); 06440 xmlOut.addTextElementToData( 06441 "AliasComment", 06442 aliasNodePair.second.getNode("CommentDescription").getValueAsString()); 06443 06444 // get group comment 06445 groupComment = ""; // clear just in case failure 06446 groupType = "Invalid"; 06447 try 06448 { 06449 cfgMgr->loadTableGroup(groupName, 06450 TableGroupKey(groupKey), 06451 0, 06452 0, 06453 0, 06454 0, 06455 &groupComment, 06456 0, 06457 0, // mostly defaults 06458 true /*doNotLoadMembers*/, 06459 &groupType); 06460 } 06461 catch(...) 06462 { 06463 __SUP_COUT_WARN__ << "Failed to load group '" << groupName << "(" << groupKey 06464 << ")' to extract group comment and type." << __E__; 06465 } 06466 xmlOut.addTextElementToData("GroupComment", groupComment); 06467 xmlOut.addTextElementToData("GroupType", groupType); 06468 } 06469 } 06470 06471 //======================================================================================================================== 06472 // handleTableVersionAliasesXML 06473 // 06474 // return version aliases and backbone versionAliases table version 06475 // 06476 // return this information: 06477 // <backbone aliasTableName=xxx version=xxx> 06478 // <version alias=xxx name=xxx version=xxx comment=xxx> 06479 // <version alias=xxx name=xxx version=xxx comment=xxx> 06480 // ... 06481 // 06482 void ConfigurationGUISupervisor::handleVersionAliasesXML(HttpXmlDocument& xmlOut, 06483 ConfigurationManagerRW* cfgMgr) 06484 { 06485 cfgMgr->loadConfigurationBackbone(); 06486 std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions(); 06487 06488 std::string versionAliasesTableName = 06489 ConfigurationManager::VERSION_ALIASES_TABLE_NAME; 06490 if(activeVersions.find(versionAliasesTableName) == activeVersions.end()) 06491 { 06492 __SUP_SS__ << "Active version of VersionAliases missing!" 06493 << "Make sure you have a valid active Backbone Group." << __E__; 06494 xmlOut.addTextElementToData("Error", ss.str()); 06495 return; 06496 } 06497 __SUP_COUT__ << "activeVersions[\"" << versionAliasesTableName 06498 << "\"]=" << activeVersions[versionAliasesTableName] << __E__; 06499 xmlOut.addTextElementToData("VersionAliasesVersion", 06500 activeVersions[versionAliasesTableName].toString()); 06501 06502 std::vector<std::pair<std::string, ConfigurationTree>> aliasNodePairs = 06503 cfgMgr->getNode(versionAliasesTableName).getChildren(); 06504 06505 for(auto& aliasNodePair : aliasNodePairs) 06506 { 06507 // note : these are column names in the versionAliasesTableName table 06508 // VersionAlias, TableName, Version, CommentDescription 06509 xmlOut.addTextElementToData( 06510 "VersionAlias", 06511 aliasNodePair.second.getNode("VersionAlias").getValueAsString()); 06512 xmlOut.addTextElementToData( 06513 "TableName", aliasNodePair.second.getNode("TableName").getValueAsString()); 06514 xmlOut.addTextElementToData( 06515 "Version", aliasNodePair.second.getNode("Version").getValueAsString()); 06516 xmlOut.addTextElementToData( 06517 "Comment", 06518 aliasNodePair.second.getNode("CommentDescription").getValueAsString()); 06519 } 06520 } 06521 06522 //======================================================================================================================== 06523 // handleGetTableGroupTypeXML 06524 // 06525 // return this information based on member table list 06526 // <TableGroupType value=xxx> 06527 // 06528 void ConfigurationGUISupervisor::handleGetTableGroupTypeXML( 06529 HttpXmlDocument& xmlOut, 06530 ConfigurationManagerRW* cfgMgr, 06531 const std::string& tableList) 06532 { 06533 std::map<std::string /*name*/, TableVersion /*version*/> memberMap; 06534 std::string name, versionStr; 06535 auto c = tableList.find(',', 0); 06536 auto i = c; 06537 i = 0; // auto used to get proper index/length type 06538 while(c < tableList.length()) 06539 { 06540 // add the table name and version pair to the map 06541 name = tableList.substr(i, c - i); 06542 i = c + 1; 06543 c = tableList.find(',', i); 06544 if(c == std::string::npos) // missing version list entry?! 06545 { 06546 __SUP_SS__ << "Incomplete Table Name-Version pair!" << __E__; 06547 __SUP_COUT_ERR__ << "\n" << ss.str(); 06548 xmlOut.addTextElementToData("Error", ss.str()); 06549 return; 06550 } 06551 06552 versionStr = tableList.substr(i, c - i); 06553 i = c + 1; 06554 c = tableList.find(',', i); 06555 06556 memberMap[name] = TableVersion(versionStr); 06557 } 06558 06559 std::string groupTypeString = ""; 06560 // try to determine type, dont report errors, just mark "Invalid" 06561 try 06562 { 06563 // determine the type of the table group 06564 groupTypeString = cfgMgr->getTypeNameOfGroup(memberMap); 06565 xmlOut.addTextElementToData("TableGroupType", groupTypeString); 06566 } 06567 catch(std::runtime_error& e) 06568 { 06569 __SUP_SS__ << "Table group has invalid type! " << e.what() << __E__; 06570 __SUP_COUT__ << "\n" << ss.str(); 06571 groupTypeString = "Invalid"; 06572 xmlOut.addTextElementToData("TableGroupType", groupTypeString); 06573 } 06574 catch(...) 06575 { 06576 __SUP_SS__ << "Table group has invalid type! " << __E__; 06577 __SUP_COUT__ << "\n" << ss.str(); 06578 groupTypeString = "Invalid"; 06579 xmlOut.addTextElementToData("TableGroupType", groupTypeString); 06580 } 06581 } 06582 06583 //======================================================================================================================== 06584 // handleTableGroupsXML 06585 // 06586 // if returnMembers then 06587 // return type, comment and members 06588 // else just name and key 06589 // 06590 // return this information 06591 // <group name=xxx key=xxx> 06592 // <config name=xxx version=xxx /> 06593 // <config name=xxx version=xxx /> 06594 // ... 06595 // </group> 06596 // <group name=xxx key=xxx>...</group> 06597 // ... 06598 // 06599 void ConfigurationGUISupervisor::handleTableGroupsXML(HttpXmlDocument& xmlOut, 06600 ConfigurationManagerRW* cfgMgr, 06601 bool returnMembers) 06602 { 06603 DOMElement* parentEl; 06604 06605 // get all group info from cache (if no cache, get from interface) 06606 06607 if(!cfgMgr->getAllGroupInfo() 06608 .size()) // empty cache is strange, attempt to get from interface 06609 { 06610 __SUP_COUT__ << "Cache is empty? Attempting to regenerate." << __E__; 06611 cfgMgr->getAllTableInfo(true /*refresh*/); 06612 } 06613 06614 const std::map<std::string, GroupInfo>& allGroupInfo = cfgMgr->getAllGroupInfo(); 06615 06616 // ConfigurationInterface* theInterface = cfgMgr->getConfigurationInterface(); 06617 // std::set<std::string /*name*/> configGroups = 06618 // theInterface->getAllTableGroupNames(); 06619 // __SUP_COUT__ << "Number of Config groups: " << configGroups.size() << __E__; 06620 // 06621 // TableGroupKey groupKey; 06622 // std::string groupName; 06623 // 06624 // std::map<std::string /*groupName*/,std::set<TableGroupKey> > allGroupsWithKeys; 06625 // for(auto& groupString:configGroups) 06626 // { 06627 // TableGroupKey::getGroupNameAndKey(groupString,groupName,groupKey); 06628 // allGroupsWithKeys[groupName].emplace(groupKey); 06629 // 06630 // //__SUP_COUT__ << "Config group " << groupString << " := " << groupName << 06631 // //"(" << groupKey << ")" << __E__; 06632 // } 06633 06634 TableGroupKey groupKey; 06635 std::string groupName; 06636 std::string groupString, groupTypeString, groupComment, groupCreationTime, 06637 groupAuthor; 06638 for(auto& groupInfo : allGroupInfo) 06639 { 06640 groupName = groupInfo.first; 06641 if(groupInfo.second.keys_.size() == 0) 06642 { 06643 __SUP_COUT__ << "Group name '" << groupName 06644 << "' found, but no keys so ignoring." << __E__; 06645 continue; 06646 } 06647 06648 groupKey = *(groupInfo.second.keys_.rbegin()); 06649 06650 xmlOut.addTextElementToData("TableGroupName", groupName); 06651 xmlOut.addTextElementToData("TableGroupKey", groupKey.toString()); 06652 06653 // trusting the cache! 06654 xmlOut.addTextElementToData("TableGroupType", 06655 groupInfo.second.latestKeyGroupTypeString_); 06656 xmlOut.addTextElementToData("TableGroupComment", 06657 groupInfo.second.latestKeyGroupComment_); 06658 xmlOut.addTextElementToData("TableGroupAuthor", 06659 groupInfo.second.latestKeyGroupAuthor_); 06660 xmlOut.addTextElementToData("TableGroupCreationTime", 06661 groupInfo.second.latestKeyGroupCreationTime_); 06662 06663 if(returnMembers) 06664 { 06665 // groupTypeString = "Invalid"; 06666 // groupComment = ""; //clear just in case failure 06667 06668 // groupString = TableGroupKey::getFullGroupString(groupName,groupKey); 06669 06670 //__SUP_COUT__ << "Latest Config group " << groupString << " := " << groupName 06671 //<< 06672 // "(" << groupKey << ")" << __E__; 06673 06674 parentEl = xmlOut.addTextElementToData("TableGroupMembers", ""); 06675 // 06676 // std::map<std::string /*name*/, TableVersion /*version*/> 06677 // memberMap; 06678 // //try to determine type, dont report errors, just mark "Invalid" 06679 // try 06680 // { 06681 // //determine the type of the table group 06682 // memberMap = cfgMgr->loadTableGroup(groupName,groupKey, 06683 // 0,0,0,&groupComment,0,0, //mostly defaults 06684 // true /*doNotLoadMembers*/,&groupTypeString); 06685 // //groupTypeString = cfgMgr->getTypeNameOfGroup(memberMap); 06686 // xmlOut.addTextElementToData("TableGroupType", 06687 // groupTypeString); 06688 // xmlOut.addTextElementToData("TableGroupComment", 06689 // groupComment); 06690 // } 06691 // catch(std::runtime_error& e) 06692 // { 06693 // __SUP_SS__ << "Table group \"" + groupString + 06694 // "\" has invalid type! " + e.what() << __E__; 06695 // __SUP_COUT__ << "\n" << ss.str(); 06696 // groupTypeString = "Invalid"; 06697 // xmlOut.addTextElementToData("TableGroupType", 06698 // groupTypeString); 06699 // xmlOut.addTextElementToData("TableGroupComment", 06700 // groupComment); continue; 06701 // } 06702 // catch(...) 06703 // { 06704 // __SUP_SS__ << "Table group \"" + groupString + 06705 // "\" has invalid type! " << __E__; 06706 // __SUP_COUT__ << "\n" << ss.str(); 06707 // groupTypeString = "Invalid"; 06708 // xmlOut.addTextElementToData("TableGroupType", 06709 // groupTypeString); 06710 // xmlOut.addTextElementToData("TableGroupComment", 06711 // groupComment); continue; 06712 // } 06713 06714 for(auto& memberPair : groupInfo.second.latestKeyMemberMap_) 06715 { 06716 //__SUP_COUT__ << "\tMember config " << memberPair.first << ":" << 06717 // memberPair.second << __E__; 06718 xmlOut.addTextElementToParent("MemberName", memberPair.first, parentEl); 06719 xmlOut.addTextElementToParent( 06720 "MemberVersion", memberPair.second.toString(), parentEl); 06721 } 06722 } // end if returnMembers 06723 06724 // add other group keys to xml for this group name 06725 // but just empty members (not displayed anyway) 06726 for(auto& keyInSet : groupInfo.second.keys_) 06727 { 06728 if(keyInSet == groupKey) 06729 continue; // skip the lastest 06730 xmlOut.addTextElementToData("TableGroupName", groupName); 06731 xmlOut.addTextElementToData("TableGroupKey", keyInSet.toString()); 06732 06733 // assume latest in cache reflects others (for speed) 06734 xmlOut.addTextElementToData("TableGroupType", 06735 groupInfo.second.latestKeyGroupTypeString_); 06736 xmlOut.addTextElementToData("TableGroupComment", 06737 groupInfo.second.latestKeyGroupComment_); 06738 xmlOut.addTextElementToData("TableGroupAuthor", 06739 groupInfo.second.latestKeyGroupAuthor_); 06740 xmlOut.addTextElementToData("TableGroupCreationTime", 06741 groupInfo.second.latestKeyGroupCreationTime_); 06742 06743 if(returnMembers) 06744 { 06745 xmlOut.addTextElementToData("TableGroupMembers", ""); 06746 06747 // TODO -- make loadingHistoricalInfo an input parameter 06748 bool loadingHistoricalInfo = false; 06749 if(loadingHistoricalInfo) 06750 { 06751 groupComment = ""; // clear just in case failure 06752 try 06753 { 06754 cfgMgr->loadTableGroup(groupName, 06755 keyInSet, 06756 0, 06757 0, 06758 0, 06759 0, 06760 &groupComment, 06761 0, 06762 0, // mostly defaults 06763 true /*doNotLoadMembers*/, 06764 &groupTypeString); 06765 } 06766 catch(...) 06767 { 06768 groupTypeString = "Invalid"; 06769 __SUP_COUT_WARN__ 06770 << "Failed to load group '" << groupName << "(" << keyInSet 06771 << ")' to extract group comment and type." << __E__; 06772 } 06773 06774 xmlOut.addTextElementToData("TableGroupType", groupTypeString); 06775 xmlOut.addTextElementToData("TableGroupComment", groupComment); 06776 xmlOut.addTextElementToData("TableGroupAuthor", groupAuthor); 06777 xmlOut.addTextElementToData("TableGroupCreationTime", 06778 groupCreationTime); 06779 } 06780 } 06781 06782 } // end other key loop 06783 } // end primary group loop 06784 } 06785 06786 //======================================================================================================================== 06787 // handleTablesXML 06788 // 06789 // return this information 06790 // <table name=xxx> 06791 // <version key=xxx /> 06792 // <version key=xxx /> 06793 // ... 06794 // </table> 06795 // <table name=xxx>...</table> 06796 // ... 06797 // 06798 void ConfigurationGUISupervisor::handleTablesXML(HttpXmlDocument& xmlOut, 06799 ConfigurationManagerRW* cfgMgr, 06800 bool allowIllegalColumns) 06801 { 06802 DOMElement* parentEl; 06803 06804 std::string accumulatedErrors = ""; 06805 const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo( 06806 allowIllegalColumns, 06807 allowIllegalColumns ? &accumulatedErrors 06808 : 0); // if allowIllegalColumns, then also refresh 06809 std::map<std::string, TableInfo>::const_iterator it = allTableInfo.begin(); 06810 06811 __SUP_COUT__ << "# of tables found: " << allTableInfo.size() << __E__; 06812 06813 std::map<std::string, std::map<std::string, TableVersion>> versionAliases = 06814 cfgMgr->getVersionAliases(); 06815 06816 __SUP_COUT__ << "# of tables w/aliases: " << versionAliases.size() << __E__; 06817 06818 while(it != allTableInfo.end()) 06819 { 06820 // for each table name 06821 // get existing version keys 06822 06823 //__SUP_COUT__ << "Name: " << it->first << " - #ofVersions: " << 06824 // it->second.versions_.size() << __E__; 06825 06826 // add system table name 06827 xmlOut.addTextElementToData("TableName", it->first); 06828 parentEl = xmlOut.addTextElementToData("TableVersions", ""); 06829 06830 // include aliases for this table (if the versions exist) 06831 if(versionAliases.find(it->first) != versionAliases.end()) 06832 for(auto& aliasVersion : versionAliases[it->first]) 06833 if(it->second.versions_.find(aliasVersion.second) != 06834 it->second.versions_.end()) 06835 // if(aliasVersion.first != 06836 // ConfigurationManager::SCRATCH_VERSION_ALIAS) //NOT NEEDED IF 06837 // SCRATCH IS ALWAYS ALIAS 06838 xmlOut.addTextElementToParent( 06839 "Version", 06840 ConfigurationManager::ALIAS_VERSION_PREAMBLE + aliasVersion.first, 06841 parentEl); 06842 // else //NOT NEEDED IF SCRATCH IS ALWAYS ALIAS 06843 // __SUP_COUT_ERR__ << "Alias for table " << it->first << " is a 06844 // reserved alias '" << 06845 // ConfigurationManager::SCRATCH_VERSION_ALIAS << "' - this 06846 // is illegal." << __E__; 06847 06848 // //if scratch version exists, add an alias for it /NOT NEEDED IF SCRATCH IS 06849 // ALWAYS ALIAS 06850 // if(it->second.versions_.find(TableVersion(TableVersion::SCRATCH)) != 06851 // it->second.versions_.end()) 06852 // xmlOut.addTextElementToParent("Version", 06853 // ConfigurationManager::ALIAS_VERSION_PREAMBLE + 06854 // ConfigurationManager::SCRATCH_VERSION_ALIAS, parentEl); 06855 06856 // get all table versions for the current table 06857 // except skip scratch version 06858 for(auto& version : it->second.versions_) 06859 if(!version.isScratchVersion()) 06860 xmlOut.addTextElementToParent("Version", version.toString(), parentEl); 06861 06862 ++it; 06863 } 06864 06865 if(accumulatedErrors != "") 06866 xmlOut.addTextElementToData( 06867 "Error", 06868 std::string("Column errors were allowed for this request, ") + 06869 "but please note the following errors:\n" + accumulatedErrors); 06870 } 06871 06872 //======================================================================================================================== 06873 // testXDAQContext 06874 // test activation of context group 06875 void ConfigurationGUISupervisor::testXDAQContext() 06876 { 06877 try 06878 { 06879 __SUP_COUT__ << "Attempting test activation of the context group." << __E__; 06880 ConfigurationManager cfgMgr; // create instance to activate saved groups 06881 } 06882 catch(const std::runtime_error& e) 06883 { 06884 __SUP_COUT_WARN__ 06885 << "The test activation of the context group failed. Ignoring error: \n" 06886 << e.what() << __E__; 06887 } 06888 catch(...) 06889 { 06890 __SUP_COUT_WARN__ << "The test activation of the context group failed. Ignoring." 06891 << __E__; 06892 } 06893 return; 06894 06896 // below has been used for debugging. 06897 06898 // behave like a user 06899 // start with top level xdaq context 06900 // then add and delete rows proof-of-concept 06901 // export xml xdaq config file 06902 06905 // behave like a new user 06906 // 06907 // ConfigurationManagerRW cfgMgrInst("ExampleUser"); 06908 // 06909 // ConfigurationManagerRW* cfgMgr =& cfgMgrInst; 06910 06911 // std::map<std::string, TableVersion> groupMembers; 06912 // groupMembers["DesktopIcon"] = TableVersion(2); 06913 // cfgMgr->saveNewTableGroup("test", 06914 // groupMembers, "test comment"); 06915 06916 // // 06917 // const std::map<std::string, TableInfo>& allTableInfo = 06918 // cfgMgr->getAllTableInfo(true); 06919 // __SUP_COUT__ << "allTableInfo.size() = " << allTableInfo.size() << __E__; 06920 // for(auto& mapIt : allTableInfo) 06921 // { 06922 // __SUP_COUT__ << "Config Name: " << mapIt.first << __E__; 06923 // __SUP_COUT__ << "\t\tExisting Versions: " << mapIt.second.versions_.size() << 06924 //__E__; 06925 // 06926 // //get version key for the current system table key 06927 // for (auto& v:mapIt.second.versions_) 06928 // { 06929 // __SUP_COUT__ << "\t\t" << v << __E__; 06930 // } 06931 // } 06932 06933 // testXDAQContext just a test bed for navigating the new config tree 06934 // cfgMgr->testXDAQContext(); 06935 06938 }