/* * UserListCommands.cpp * * Copyright (C) 2009 Matthias Schiffer * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along * with this program. If not, see . */ #include "UserListCommands.h" #include "Application.h" #include "ConsoleUtil.h" #include "XLSReader.h" #include "XLSSheet.h" #include "Requests/UserLists/UserListListRequest.h" #include "Requests/UserLists/UserListDownloadRequest.h" #include "Requests/UserLists/UserListDiffListRequest.h" #include "Requests/UserLists/UserListUploadRequest.h" #include #include #include #include namespace Mad { namespace Client { const UserListCommands::Command UserListCommands::commands[] = { {{"help", "?", 0}, "help [command]", "Display usage information about user list commands", "Display usage information about a user list command. If no command is given, display a list of all available user list commands.", &UserListCommands::helpCommand}, {{"list", "ls", 0}, "list", "List the stored user lists", "List the stored user lists.", &UserListCommands::listCommand}, {{"show_list", "sl", 0}, "show_list list", "Displays a user list", "Displays a user list.", &UserListCommands::showListCommand}, {{"import", "im", 0}, "import filename", "Imports a user list file", "Imports a user list file. At the moment, this supports only XLS files.", &UserListCommands::importCommand}, {{0}, 0, 0, 0, 0} }; const UserListCommands::Command* UserListCommands::findCommand(const Core::String& command) { for(int i = 0; commands[i].commands[0] != 0; ++i) { for(int j = 0; commands[i].commands[j] != 0; ++j) { if(command == commands[i].commands[j]) { return &commands[i]; } } } return 0; } void UserListCommands::printUsage(const Core::String& command) { const UserListCommands::Command *cmd = findCommand(command); if(cmd) std::cerr << "Usage: " << "user_lists " << cmd->cmdline << std::endl; } void UserListCommands::helpCommand(CommandParser* /*commandParser*/, const std::vector &args) { if(args.size() <= 2) { std::cout << "Available commands:" << std::endl << std::endl; for(int i = 0; commands[i].commands[0] != 0; ++i) { std::cout << commands[i].commands[0]; for(int j = 1; commands[i].commands[j] != 0; ++j) std::cout << ", " << commands[i].commands[j]; std::cout << std::endl << "\t" << commands[i].desc << std::endl; } } else if(args.size() == 3) { const Command* command = findCommand(args[2]); if(command) { std::cout << "Usage: " << "user_lists " << command->cmdline << std::endl << std::endl; std::cout << command->longdesc << std::endl << std::endl; } else std::cerr << args[0] << " " << args[1] << ": Command '" << args[2] << "' doesn't exist." << std::endl; } else { std::cerr << args[0] << " " << args[1] << ": Too many arguments." << std::endl; printUsage("help"); } } void UserListCommands::listCommand(CommandParser *commandParser, const std::vector& /*args*/) { boost::shared_ptr userListRequest(new Requests::UserLists::UserListListRequest(commandParser->application)); boost::shared_ptr userListDiffRequest(new Requests::UserLists::UserListDiffListRequest(commandParser->application)); commandParser->application->getRequestManager()->sendRequest(commandParser->connection, userListRequest); commandParser->application->getRequestManager()->sendRequest(commandParser->connection, userListDiffRequest); userListRequest->wait(); userListDiffRequest->wait(); std::pair, Core::Exception> userListResult = userListRequest->getResult(); std::pair, Core::Exception> userListDiffResult = userListDiffRequest->getResult(); if(userListResult.second) { std::cerr << "An error occurred during your request: " << userListResult.second.what() << "." << std::endl; return; } else if(!userListResult.first || !userListDiffResult.first || userListDiffResult.second) { std::cerr << "An error occurred during your request: " << userListDiffResult.second.what() << "." << std::endl; return; } const Common::XmlData::List *userLists = userListResult.first->getList("userLists"); const Common::XmlData::List *userListDiffs = userListDiffResult.first->getList("userListDiffs"); if(userLists->isEmpty()) { std::cout << "There aren't any user lists stored on the server." << std::endl; } else { if(userLists->getSize() == 1) std::cout << "There is 1 user list stored on the server:" << std::endl; else std::cout << "There are " << userLists->getSize() << " user lists stored on the server:" << std::endl; for(Common::XmlData::List::const_iterator userList = userLists->begin(); userList != userLists->end(); ++userList) { std::cout << " " << userList->get("name") << std::endl; } } std::cout << std::endl; if(!userListDiffs->isEmpty()) { if(userListDiffs->getSize() == 1) std::cout << "There is 1 user list difference record stored on the server:" << std::endl; else std::cout << "There are " << userListDiffs->getSize() << " user list difference records stored on the server:" << std::endl; for(Common::XmlData::List::const_iterator diff = userListDiffs->begin(); diff != userListDiffs->end(); ++diff) { std::cout << " " << diff->get("name") << std::endl; } } } void UserListCommands::showListCommand(CommandParser *commandParser, const std::vector &args) { if(args.size() < 3) { std::cerr << args[0] << " " << args[1] << ": No list name given." << std::endl; printUsage("show_list"); return; } else if(args.size() == 3) { boost::shared_ptr request( new Requests::UserLists::UserListDownloadRequest(commandParser->application, args[2])); commandParser->application->getRequestManager()->sendRequest(commandParser->connection, request); request->wait(); std::pair, Core::Exception> result = request->getResult(); if(!result.first || result.second) { std::cerr << "An error occurred during your request: " << result.second.what() << "." << std::endl; return; } boost::shared_ptr userList = Common::UserLists::Util::deserializeUserList(result.first.get()); std::cout << "User list '" << args[2]; if(userList->isEmpty()) { std::cout << " is empty." << std::endl << std::endl; } else { std::cout << "' contains " << userList->getLength() << " user"; if(userList->getLength() > 1) std::cout << "s"; std::cout << "." << std::endl << std::endl; printUserList(*userList); } } else { std::cerr << args[0] << " " << args[1] << ": Too many arguments." << std::endl; printUsage("show_list"); return; } } void UserListCommands::importCommand(CommandParser *commandParser, const std::vector &args) { if(args.size() < 3) { std::cerr << args[0] << " " << args[1] << ": No filename given." << std::endl; printUsage("import"); return; } else if(args.size() == 3) { try { XLSReader reader(args[2].toLocale()); const std::list > &sheets = reader.getSheets(); if(sheets.empty()) { std::cerr << "There aren't any worksheets in the file." << std::endl; return; } unsigned long sheetNum = 1; if(sheets.size() > 1) { std::cout << "There is more than one worksheet in the file. Which one do you want to import?" << std::endl << std::endl; while(true) { int i = 1; for(std::list >::const_iterator sheet = sheets.begin(); sheet != sheets.end(); ++sheet, ++i) { std::cout << " " << i << ") " << (*sheet)->getTitle() << std::endl; } std::cout << std::endl; std::cout << "Worksheet number: " << std::flush; std::string input; std::getline(std::cin, input); sheetNum = std::strtoul(input.c_str(), 0, 10); if(sheetNum > 0 && sheetNum <= sheets.size()) break; std::cout << "Invalid selection. Please choose one of:" << std::endl << std::endl; } } std::list >::const_iterator sheet = sheets.begin(); std::advance(sheet, sheetNum-1); std::cout << "Imported worksheet:" << std::endl; printSheet(**sheet, 10); std::cout << std::endl; std::cout << "Does the first line of the worksheet contain the column names? (Y/n) " << std::flush; Core::String input = Core::String::getline(std::cin); std::cout << std::endl << std ::endl; input.toLower(); if(input.isEmpty() || input[0] != 'n') { (*sheet)->useFirstRowAsColumnNames(true); printSheet(**sheet, 10); } else { printSheet(**sheet, 5); } std::cout << std::endl; while(true) { std::cout << "Edit column names:" << std::endl << std::endl; for(unsigned i = 0; i < (*sheet)->getColumnCount(); ++i) { std::cout << "[" << i+1 << "] " << (*sheet)->getColumnName(i) << std::endl; } std::cout << std::endl; std::cout << "Enter number of column to edit or press to save the user list: " << std::flush; input = Core::String::getline(std::cin); if(input.isEmpty()) break; unsigned col = strtoul(input.toString().c_str(), 0, 10); if(col < 1 || col > (*sheet)->getColumnCount()) { std::cout << "Invalid input. " << std::endl << std::endl; printSheet(**sheet, 5); std::cout << std::endl; continue; } std::cout << "Enter the new name for column " << col << ". An empty name will mark the column for deletion" << std::endl; std::cout << "New name: " << std::flush; input = Core::String::getline(std::cin); (*sheet)->setColumnName(col-1, input); printSheet(**sheet, 5); std::cout << std::endl; } boost::shared_ptr userList(new Common::UserLists::UserList); const std::vector &rows = (*sheet)->getRows(); unsigned id = 1; for(std::vector::const_iterator rowIt = rows.begin(); rowIt != rows.end(); ++rowIt, ++id) { const std::vector &row = **rowIt; Common::UserLists::UserListEntry entry; std::ostringstream stream; stream << id; entry.setName(stream.str().c_str()); for(unsigned col = 0; col < (*sheet)->getColumnCount(); ++col) { if(!(*sheet)->getColumnName(col).isEmpty()) entry.setDetail((*sheet)->getColumnName(col), row[col]); } userList->addUser(entry); } bool overwrite = false; Core::String name; while(true) { if(!overwrite) { std::cout << "Save the user list as: " << std::flush; name = Core::String::getline(std::cin); } boost::shared_ptr request(new UserLists::UserListUploadRequest( commandParser->application, name, userList, overwrite)); commandParser->application->getRequestManager()->sendRequest(commandParser->connection, request); request->wait(); std::pair, Core::Exception> result = request->getResult(); if(result.second) { if(!overwrite && result.second.getErrorCode() == Core::Exception::DUPLICATE_ENTRY) { std::cout << "There is already a user list stored under that name. Overwrite? (y/N) " << std::flush; input = Core::String::getline(std::cin); std::cout << std::endl; input.toLower(); if(!input.isEmpty() && input[0] == 'y') overwrite = true; continue; } std::cerr << "An error occurred during your request: " << result.second.what() << "." << std::endl; return; } break; } std::cout << "User list stored." << std::endl << std::endl; } catch(Core::Exception e) { std::cerr << "The file can't be read: " << e.what() << "." << std::endl; } } else { std::cerr << args[0] << " " << args[1] << ": Too many arguments." << std::endl; printUsage("import"); return; } } void UserListCommands::printUserList(const Common::UserLists::UserList &list) { Core::String output; std::map lengths; static const Core::String USER_NAME = "User name"; static const Core::String GROUP_NAME = "Group name"; lengths.insert(std::make_pair(USER_NAME, USER_NAME.length())); lengths.insert(std::make_pair(GROUP_NAME, GROUP_NAME.length())); std::set details = list.getDetails(); for(std::set::iterator detail = details.begin(); detail != details.end(); ++detail) { lengths.insert(std::make_pair(*detail, detail->length())); } for(Common::UserLists::UserList::const_iterator user = list.begin(); user != list.end(); ++user) { std::map::iterator it = lengths.find(USER_NAME); if(user->getName().length() > it->second) it->second = user->getName().length(); it = lengths.find(GROUP_NAME); if(user->getGroup().length() > it->second) it->second = user->getGroup().length(); for(std::set::iterator detail = details.begin(); detail != details.end(); ++detail) { it = lengths.find(*detail); Core::String detailStr = user->getDetail(*detail); if(detailStr.length() > it->second) it->second = detailStr.length(); } } output += " "; output += makePaddedString(USER_NAME, lengths.find(USER_NAME)->second); output += " "; output += makePaddedString(GROUP_NAME, lengths.find(GROUP_NAME)->second); for(std::set::iterator detail = details.begin(); detail != details.end(); ++detail) { output += " "; output += makePaddedString(*detail, lengths.find(*detail)->second); } output += "\n"; output += "-"; output += makeDelimiter(lengths.find(USER_NAME)->second); output += "- -"; output += makeDelimiter(lengths.find(GROUP_NAME)->second); for(std::set::iterator detail = details.begin(); detail != details.end(); ++detail) { output += "- -"; output += makeDelimiter(lengths.find(*detail)->second); } output += "-\n"; for(Common::UserLists::UserList::const_iterator user = list.begin(); user != list.end(); ++user) { static const Core::String UNSET(""); output += " "; if(!user->getName().isEmpty()) output += makePaddedString(user->getName(), lengths.find(USER_NAME)->second); else output += makePaddedString(UNSET, lengths.find(USER_NAME)->second); output += " "; if(!user->getGroup().isEmpty()) output += makePaddedString(user->getGroup(), lengths.find(GROUP_NAME)->second); else output += makePaddedString(UNSET, lengths.find(GROUP_NAME)->second); for(std::set::iterator detail = details.begin(); detail != details.end(); ++detail) { output += " "; output += makePaddedString(user->getDetail(*detail), lengths.find(*detail)->second); } output += "\n"; } ConsoleUtil::printPaged(output); } void UserListCommands::printSheet(const XLSSheet &sheet, unsigned rowCount) { static const Core::String DELETE = ""; const std::vector &rows = sheet.getRows(); std::vector lengths(sheet.getColumnCount()); for(unsigned col = 0; col < sheet.getColumnCount(); ++col) { boost::int32_t length = sheet.getColumnName(col).length(); if(length > 0) lengths[col] = length; else lengths[col] = DELETE.length(); } for(std::vector::const_iterator rowIt = rows.begin(); rowIt != rows.end() && (rowCount == 0 || rowIt != rows.begin()+rowCount); ++rowIt) { const std::vector &row = **rowIt; for(unsigned col = 0; col < sheet.getColumnCount(); ++col) { if(row[col].length() > lengths[col]) lengths[col] = row[col].length(); } } for(unsigned col = 0; col < sheet.getColumnCount(); ++col) { std::cout << " "; if(!sheet.getColumnName(col).isEmpty()) std::cout << makePaddedString(sheet.getColumnName(col), lengths[col]); else std::cout << makePaddedString(DELETE, lengths[col]); } std::cout << std::endl; std::cout << " "; for(unsigned col = 0; col < sheet.getColumnCount(); ++col) { std::cout << " -"; std::cout << makeDelimiter(lengths[col]); std::cout << "-"; } std::cout << std::endl; for(std::vector::const_iterator rowIt = rows.begin(); rowIt != rows.end() && (rowCount == 0 || rowIt != rows.begin()+rowCount); ++rowIt) { const std::vector &row = **rowIt; for(unsigned col = 0; col < sheet.getColumnCount(); ++col) { std::cout << " "; std::cout << makePaddedString(row[col], lengths[col]); } std::cout << std::endl; } if(rowCount > 0 && rows.size() > rowCount) { for(unsigned col = 0; col < sheet.getColumnCount(); ++col) { std::cout << " "; std::cout << makePaddedString("...", lengths[col]); } std::cout << std::endl; } } Core::String UserListCommands::makePaddedString(const Core::String &str, boost::int32_t length) { if(str.length() < length) return str + Core::String::fromString(std::string(length-str.length(), ' ')); else return str; } Core::String UserListCommands::makeDelimiter(boost::int32_t length) { return Core::String::fromString(std::string(length, '-')); } void UserListCommands::userListCommand(CommandParser *commandParser, const std::vector &args) { Core::String cmd; if(args.size() < 2) cmd = "help"; else cmd = args[1]; const Command* command = findCommand(cmd); if(command) command->function(commandParser, args); else std::cerr << args[0] << ": Unknown command '" << cmd << "'." << std::endl; } } }