class SLTest(unittest.TestCase): """ Store the serverlist in this directory. """ def setUp(self): #print "setUp" self.sl = ServerList(tempfile.gettempdir(), "serverlisttest.xml") #self.sl.__configFile = os.path.join(self.sl._configPrefix, "serverlist.xml") def tearDown(self): pass """ Writes a serverlist in the current format to disk. """ def writeList(self): #print "writeList" try: f = open(self.sl.getConfigFilePath(), "w") f.write(""" <!DOCTYPE LumaServerFile> <LumaServerList version="1.2"> <LumaLdapServer port="1337" clientCertFile="" followAliases="0" clientCertKeyfile="" bindPassword="******" useCertificate="0" bindDN="" host="directory.d-trust.de" authMethod="1" bindAnon="1" encryptionMethod="0" name="d-trust" autoBase="0" checkServerCertificate="1"> <baseDNs> <base dn="dc=d-trust,dc=de"/> <base dn="dc=ntnu,dc=no.TULL"/> </baseDNs> </LumaLdapServer> <LumaLdapServer port="389" clientCertFile="" followAliases="0" clientCertKeyfile="" bindPassword="******" useCertificate="0" bindDN="" host="at.ntnu.no" authMethod="6" bindAnon="1" encryptionMethod="2" name="ntnu" autoBase="1" checkServerCertificate="2"> <baseDNs> <base dn="dc=lol,dc=com"/> <base dn="dc=ntnu,dc=no"/> </baseDNs> </LumaLdapServer> <LumaLdapServer port="392" clientCertFile="" followAliases="1" clientCertKeyfile="" bindPassword="******" useCertificate="0" bindDN="" host="x500.bund.de" authMethod="0" bindAnon="1" encryptionMethod="1" name="bund" autoBase="0" checkServerCertificate="0"> <baseDNs> <base dn="dc=bund,dc=de"/> </baseDNs> </LumaLdapServer> </LumaServerList> """) f.close() except IOError: print "----------------------" print "WRITE TO DISK FAILED!" print "----------------------" raise def getEmptyServerObject(self): #print "getEmptryServerObject" return ServerObject() """ Read and write and emtpy serverlist to/from disk. """ def testEmptyList(self): #print "testEmptyList" self.sl.setTable([]) self.sl.writeServerList() self.sl.readServerList() self.assertEqual(self.sl.getTable(), []) """ Tests for Read, delete, add and write, better than one BIG test, but each test is still a bit to dependent on the previous tests """ def testAdd(self): #print "testAdd" # Remove and add back the removed item self.sl.addServer(self.getEmptyServerObject()) # Check if it has been added correctly self.assertNotEqual(None, self.sl.getServerObject("")) def testDelete(self): #print "testDelete" # Delete and see if object still in list self.sl.setTable([self.getEmptyServerObject()]) self.assertNotEqual(None, self.sl.getServerObject("")) self.sl.deleteServer("") self.assertEqual(None, self.sl.getServerObject("")) def testRead(self): #print "testRead" # Write the list to disk and have ServerList read it. self.writeList() self._modifyTime = None #IMPORTANT - FORCES READ FROM DISK self.sl.readServerList() # Check that the server list was read correctly self.assertNotEqual(None, self.sl.getServerObject("d-trust")) self.assertNotEqual(None, self.sl.getServerObject("bund")) self.assertNotEqual(None, self.sl.getServerObject("ntnu")) def testWrite(self): #print "testWrite" # Write the list back to disk and read it back self.sl.setTable([self.getEmptyServerObject()]) self.sl.writeServerList() f = open(self.sl.getConfigFilePath(), "r") s = """ <!DOCTYPE LumaServerFile> <LumaServerList version="1.2"> <LumaLdapServer port="389" clientCertFile="" followAliases="0" bindPassword="" useCertificate="0" clientCertKeyFile="" bindDN="" host="" authMethod="0" bindAnon="1" encryptionMethod="0" name="" autoBase="1" checkServerCertificate="0"> <baseDNs/> </LumaLdapServer> </LumaServerList> """ #Verify the list is good read = f.read() f.close() s = s.strip() r = read.strip() self.assertEqual(s, r)
class TemplateWidget(QWidget, Ui_TemplateWidget): def __init__(self): QWidget.__init__(self) self.setupUi(self) self.setObjectName('TemplatePlugin') self._serverList = ServerList() templateList = TemplateList() self._templateList = copy.deepcopy(templateList) self._templateListCopy = None self._returnList = None #ObjectclassAttributeInfo self.preloadedServerMeta = {} self.templateTM = TemplateTableModel(self._templateList, self) self.listViewTemplates.setModel(self.templateTM) self.objectclassTM = ObjectclassTableModel(self) self.listViewObjectclasses.setModel(self.objectclassTM) self.attributeTM = AttributeTableModel(self) self.tableViewAttributes.setModel(self.attributeTM) # Enable/disable editing depending on if we have a server to edit if self.templateTM.rowCount(QModelIndex()) > 0: self.setRightSideEnabled(True) else: self.setRightSideEnabled(False) # Select the first template in the model) index = self.templateTM.index(0,0) # Select the template in the view self.listViewTemplates.selectionModel().select(index, QItemSelectionModel.ClearAndSelect) self.listViewTemplates.selectionModel().setCurrentIndex(index, QItemSelectionModel.ClearAndSelect) self.listViewTemplates.selectionModel().selectionChanged.connect(self.selectedTemplate) # Map columns of the model to fields in the gui self.mapper = QDataWidgetMapper() self.mapper.setModel(self.templateTM) self.mapper.addMapping(self.lineEditDescription, 2) # Set-up of the non-mapped information self.selectedTemplate() def selectedTemplate(self): index = self.listViewTemplates.selectionModel().currentIndex().row() if index >= 0: self.mapper.setCurrentIndex(index) server = self._templateList._templateList[index].server self.labelServerName.setText(server) self.loadServerMeta(server) self.setObjectclasses() self.setAttributes() self.tableViewAttributes.resizeColumnsToContents() self.tableViewAttributes.resizeRowsToContents() def setObjectclasses(self): templateObject = self.getSelectedTemplateObject() if templateObject: self.objectclassTM.setTemplateObject(templateObject) def setAttributes(self): templateObject = self.getSelectedTemplateObject() if templateObject: self.attributeTM.setTemplateObject(templateObject) def loadServerMeta(self, serverName): serverName = unicode(serverName) if not (serverName in self.preloadedServerMeta.keys()): serverMeta = self._serverList.getServerObject(serverName) self.preloadedServerMeta[serverName] = ObjectClassAttributeInfo(serverMeta) return self.preloadedServerMeta[serverName] def setRightSideEnabled(self, enabled): self.lineEditDescription.setEnabled(enabled) self.groupBoxObjectclasses.setEnabled(enabled) self.groupBoxAttributes.setEnabled(enabled) def clearAll(self): self.lineEditDescription.clear() self.labelServerName.clear() self.objectclassTM.setTemplateObject(None) self.attributeTM.setTemplateObject(None) def getSelectedTemplateObject(self): templateIndexes = self.listViewTemplates.selectedIndexes() if len(templateIndexes) > 0: return self._templateList.getTable()[templateIndexes[0].row()] return None def getSelectedObjectclass(self, index): return self.objectclassTM.getObjectclass(index) def getSelectedAttribute(self, index): return self.attributeTM.getAttribute(index) def addTemplate(self): dialog = AddTemplateDialog(self._serverList) if dialog.exec_(): name = dialog.lineEditTemplateName.text() if len(name) < 1 or self._templateList.getTemplateObject(name) != None: QMessageBox.information(self, 'Error', "Invalid name or already used.") return server = dialog.comboBoxServer.currentText() if len(server) < 1: QMessageBox.information(self, 'Error', "Invalid server.") return description = dialog.lineEditDescription.text() tO = TemplateObject(name, server, description) m = self.templateTM m.beginInsertRows(QModelIndex(), m.rowCount(), m.rowCount()) m.insertRow(tO) m.endInsertRows() i = m.index(m.rowCount()-1,0) self.listViewTemplates.selectionModel().select(i, QItemSelectionModel.ClearAndSelect) self.listViewTemplates.selectionModel().setCurrentIndex(i, QItemSelectionModel.ClearAndSelect) #Mark it as current self.mapper.setCurrentIndex(i.row()) self.selectedTemplate() if i.row() == 0: self.setRightSideEnabled(True) def deleteTemplate(self): if self.listViewTemplates.selectionModel().currentIndex().row() < 0: return re = QMessageBox.question(self, "Delete", "Are you sure?", QMessageBox.Yes, QMessageBox.No) if re == QMessageBox.Yes: index = self.listViewTemplates.selectedIndexes()[0] #Currently selected # Delete the template self.listViewTemplates.model().removeRow(index) # When deleting, the view gets updated and selects a new current. # Get it and give it to the mapper newIndex = self.listViewTemplates.selectionModel().currentIndex() self.mapper.setCurrentIndex(newIndex.row()) # Disable editing if no templates left if self.templateTM.rowCount() == 0: self.setRightSideEnabled(False) self.clearAll() def duplicateTemplate(self): name, ok = QInputDialog.getText(self, 'Duplicate', 'Template name') if ok: if len(name) < 1 or self._templateList.getTemplateObject(name) != None: QMessageBox.information(self, 'Error', "Invalid name or already used.") return tO = copy.deepcopy(self.getSelectedTemplateObject()) tO.templateName = name m = self.listViewTemplates.model() m.insertRow(tO) i = m.index(m.rowCount()-1,0) self.listViewTemplates.selectionModel().select(i, QItemSelectionModel.ClearAndSelect) self.listViewTemplates.selectionModel().setCurrentIndex(i, QItemSelectionModel.ClearAndSelect) #Mark it as current self.mapper.setCurrentIndex(i.row()) self.selectedTemplate() def saveTemplate(self): self._templateList.save() def addObjectclass(self): server = self.labelServerName.text() dialog = AddObjectclassDialog(self.loadServerMeta(server), self.getSelectedTemplateObject()) if dialog.exec_(): for i in dialog.listWidgetObjectclasses.selectedIndexes(): item = dialog.listWidgetObjectclasses.itemFromIndex(i) self.objectclassTM.insertRow(str(item.text())) self.refreshMustAttributes() def deleteObjectclass(self): dOc = self.listViewObjectclasses.selectedIndexes() if dOc: server = self.labelServerName.text() tO = self.getSelectedTemplateObject() attributes = self.attributeTM.attributes dialog = DeleteObjectclassDialog(self.loadServerMeta(server), tO, dOc, attributes) if dialog.exec_(): self.objectclassTM.removeRows(dOc) self.refreshAllAttributes() def refreshMustAttributes(self): tO = self.getSelectedTemplateObject() for attr in tO.attributes.values(): if attr.must: self.attributeTM.removeAlways(attr) server = self.labelServerName.text() ocai = self.loadServerMeta(server) attributeNameList = ocai.getAllMusts(tO.objectclasses) for name in attributeNameList: single = ocai.isSingle(name) binary = ocai.isBinary(name) self.attributeTM.addRow(name, True, single, binary, "", False) def refreshAllAttributes(self): tO = self.getSelectedTemplateObject() server = self.labelServerName.text() ocai = self.loadServerMeta(server) must, may = ocai.getAllAttributes(tO.objectclasses) for attr in tO.attributes.items(): if (not attr[0] in must) and (not attr[0] in may): self.attributeTM.removeAlways(attr[1]) elif not attr[0] in must: attr[1].must = False def addAttribute(self): server = self.labelServerName.text() dialog = AddAttributeDialog(self.loadServerMeta(server), self.getSelectedTemplateObject()) if dialog.exec_(): for i in dialog.tableView.selectedIndexes(): if(i.column() == 0): a = dialog.attributeTM.getAttribute(i) self.attributeTM.addRow(a.attributeName, a.must, a.single, a.binary, a.defaultValue, a.customMust) self.tableViewAttributes.resizeRowsToContents() self.tableViewAttributes.resizeColumnsToContents() def deleteAttributes(self): if len(self.tableViewAttributes.selectedIndexes()): re = QMessageBox.question(self, self.tr('Delete'), self.tr("Are you sure you want to delete the selected attributes?"), QMessageBox.Yes, QMessageBox.No) if re == QMessageBox.Yes: self.attributeTM.removeRows(self.tableViewAttributes.selectedIndexes()) def changeEvent(self, e): """Overloaded so we can catch the LanguageChange event, and at translation support to the plugin """ if e.type() == QtCore.QEvent.LanguageChange: self.retranslateUi(self) else: QWidget.changeEvent(self, e) def retranslate(self, all=True): """For dynamic retranslation of the plugin text strings """ self.retranslateUi(self)
class TemplateWidget(QWidget, Ui_TemplateWidget): def __init__(self): QWidget.__init__(self) self.setupUi(self) self.setObjectName('TemplatePlugin') self._serverList = ServerList() templateList = TemplateList() self._templateList = copy.deepcopy(templateList) self._templateListCopy = None self._returnList = None #ObjectclassAttributeInfo self.preloadedServerMeta = {} self.templateTM = TemplateTableModel(self._templateList, self) self.listViewTemplates.setModel(self.templateTM) self.objectclassTM = ObjectclassTableModel(self) self.listViewObjectclasses.setModel(self.objectclassTM) self.attributeTM = AttributeTableModel(self) self.tableViewAttributes.setModel(self.attributeTM) # Enable/disable editing depending on if we have a server to edit if self.templateTM.rowCount(QModelIndex()) > 0: self.setRightSideEnabled(True) else: self.setRightSideEnabled(False) # Select the first template in the model) index = self.templateTM.index(0, 0) # Select the template in the view self.listViewTemplates.selectionModel().select( index, QItemSelectionModel.ClearAndSelect) self.listViewTemplates.selectionModel().setCurrentIndex( index, QItemSelectionModel.ClearAndSelect) self.listViewTemplates.selectionModel().selectionChanged.connect( self.selectedTemplate) # Map columns of the model to fields in the gui self.mapper = QDataWidgetMapper() self.mapper.setModel(self.templateTM) self.mapper.addMapping(self.lineEditDescription, 2) # Set-up of the non-mapped information self.selectedTemplate() def selectedTemplate(self): index = self.listViewTemplates.selectionModel().currentIndex().row() if index >= 0: self.mapper.setCurrentIndex(index) server = self._templateList._templateList[index].server self.labelServerName.setText(server) self.loadServerMeta(server) self.setObjectclasses() self.setAttributes() self.tableViewAttributes.resizeColumnsToContents() self.tableViewAttributes.resizeRowsToContents() def setObjectclasses(self): templateObject = self.getSelectedTemplateObject() if templateObject: self.objectclassTM.setTemplateObject(templateObject) def setAttributes(self): templateObject = self.getSelectedTemplateObject() if templateObject: self.attributeTM.setTemplateObject(templateObject) def loadServerMeta(self, serverName): serverName = unicode(serverName) if not (serverName in self.preloadedServerMeta.keys()): serverMeta = self._serverList.getServerObject(serverName) self.preloadedServerMeta[serverName] = ObjectClassAttributeInfo( serverMeta) return self.preloadedServerMeta[serverName] def setRightSideEnabled(self, enabled): self.lineEditDescription.setEnabled(enabled) self.groupBoxObjectclasses.setEnabled(enabled) self.groupBoxAttributes.setEnabled(enabled) def clearAll(self): self.lineEditDescription.clear() self.labelServerName.clear() self.objectclassTM.setTemplateObject(None) self.attributeTM.setTemplateObject(None) def getSelectedTemplateObject(self): templateIndexes = self.listViewTemplates.selectedIndexes() if len(templateIndexes) > 0: return self._templateList.getTable()[templateIndexes[0].row()] return None def getSelectedObjectclass(self, index): return self.objectclassTM.getObjectclass(index) def getSelectedAttribute(self, index): return self.attributeTM.getAttribute(index) def addTemplate(self): dialog = AddTemplateDialog(self._serverList) if dialog.exec_(): name = dialog.lineEditTemplateName.text() if len(name) < 1 or self._templateList.getTemplateObject( name) != None: QMessageBox.information(self, 'Error', "Invalid name or already used.") return server = dialog.comboBoxServer.currentText() if len(server) < 1: QMessageBox.information(self, 'Error', "Invalid server.") return description = dialog.lineEditDescription.text() tO = TemplateObject(name, server, description) m = self.templateTM m.beginInsertRows(QModelIndex(), m.rowCount(), m.rowCount()) m.insertRow(tO) m.endInsertRows() i = m.index(m.rowCount() - 1, 0) self.listViewTemplates.selectionModel().select( i, QItemSelectionModel.ClearAndSelect) self.listViewTemplates.selectionModel().setCurrentIndex( i, QItemSelectionModel.ClearAndSelect) #Mark it as current self.mapper.setCurrentIndex(i.row()) self.selectedTemplate() if i.row() == 0: self.setRightSideEnabled(True) def deleteTemplate(self): if self.listViewTemplates.selectionModel().currentIndex().row() < 0: return re = QMessageBox.question(self, "Delete", "Are you sure?", QMessageBox.Yes, QMessageBox.No) if re == QMessageBox.Yes: index = self.listViewTemplates.selectedIndexes()[ 0] #Currently selected # Delete the template self.listViewTemplates.model().removeRow(index) # When deleting, the view gets updated and selects a new current. # Get it and give it to the mapper newIndex = self.listViewTemplates.selectionModel().currentIndex() self.mapper.setCurrentIndex(newIndex.row()) # Disable editing if no templates left if self.templateTM.rowCount() == 0: self.setRightSideEnabled(False) self.clearAll() def duplicateTemplate(self): name, ok = QInputDialog.getText(self, 'Duplicate', 'Template name') if ok: if len(name) < 1 or self._templateList.getTemplateObject( name) != None: QMessageBox.information(self, 'Error', "Invalid name or already used.") return tO = copy.deepcopy(self.getSelectedTemplateObject()) tO.templateName = name m = self.listViewTemplates.model() m.insertRow(tO) i = m.index(m.rowCount() - 1, 0) self.listViewTemplates.selectionModel().select( i, QItemSelectionModel.ClearAndSelect) self.listViewTemplates.selectionModel().setCurrentIndex( i, QItemSelectionModel.ClearAndSelect) #Mark it as current self.mapper.setCurrentIndex(i.row()) self.selectedTemplate() def saveTemplate(self): self._templateList.save() def addObjectclass(self): server = self.labelServerName.text() dialog = AddObjectclassDialog(self.loadServerMeta(server), self.getSelectedTemplateObject()) if dialog.exec_(): for i in dialog.listWidgetObjectclasses.selectedIndexes(): item = dialog.listWidgetObjectclasses.itemFromIndex(i) self.objectclassTM.insertRow(str(item.text())) self.refreshMustAttributes() def deleteObjectclass(self): dOc = self.listViewObjectclasses.selectedIndexes() if dOc: server = self.labelServerName.text() tO = self.getSelectedTemplateObject() attributes = self.attributeTM.attributes dialog = DeleteObjectclassDialog(self.loadServerMeta(server), tO, dOc, attributes) if dialog.exec_(): self.objectclassTM.removeRows(dOc) self.refreshAllAttributes() def refreshMustAttributes(self): tO = self.getSelectedTemplateObject() for attr in tO.attributes.values(): if attr.must: self.attributeTM.removeAlways(attr) server = self.labelServerName.text() ocai = self.loadServerMeta(server) attributeNameList = ocai.getAllMusts(tO.objectclasses) for name in attributeNameList: single = ocai.isSingle(name) binary = ocai.isBinary(name) self.attributeTM.addRow(name, True, single, binary, "", False) def refreshAllAttributes(self): tO = self.getSelectedTemplateObject() server = self.labelServerName.text() ocai = self.loadServerMeta(server) must, may = ocai.getAllAttributes(tO.objectclasses) for attr in tO.attributes.items(): if (not attr[0] in must) and (not attr[0] in may): self.attributeTM.removeAlways(attr[1]) elif not attr[0] in must: attr[1].must = False def addAttribute(self): server = self.labelServerName.text() dialog = AddAttributeDialog(self.loadServerMeta(server), self.getSelectedTemplateObject()) if dialog.exec_(): for i in dialog.tableView.selectedIndexes(): if (i.column() == 0): a = dialog.attributeTM.getAttribute(i) self.attributeTM.addRow(a.attributeName, a.must, a.single, a.binary, a.defaultValue, a.customMust) self.tableViewAttributes.resizeRowsToContents() self.tableViewAttributes.resizeColumnsToContents() def deleteAttributes(self): if len(self.tableViewAttributes.selectedIndexes()): re = QMessageBox.question( self, self.tr('Delete'), self. tr("Are you sure you want to delete the selected attributes?"), QMessageBox.Yes, QMessageBox.No) if re == QMessageBox.Yes: self.attributeTM.removeRows( self.tableViewAttributes.selectedIndexes()) def changeEvent(self, e): """Overloaded so we can catch the LanguageChange event, and at translation support to the plugin """ if e.type() == QtCore.QEvent.LanguageChange: self.retranslateUi(self) else: QWidget.changeEvent(self, e) def retranslate(self, all=True): """For dynamic retranslation of the plugin text strings """ self.retranslateUi(self)
class BrowserView(QWidget): """Luma LDAP Browser plugin """ # Custom signals used reloadSignal = QtCore.pyqtSignal(QtCore.QModelIndex) clearSignal = QtCore.pyqtSignal(QtCore.QModelIndex) __logger = logging.getLogger(__name__) def __init__(self, parent=None, configPrefix=None): """ :param configPrefix: defines the location of serverlist. :type configPrefix: string """ super(BrowserView, self).__init__(parent) self.__logger = logging.getLogger(__name__) self.setObjectName("PLUGIN_BROWSER") self.templateList = TemplateList() # The serverlist used self.serverList = ServerList(configPrefix) self.serversChangedMessage = QtGui.QErrorMessage() self.mainLayout = QtGui.QHBoxLayout(self) self.splitter = QtGui.QSplitter(self) # Create the model self.ldaptreemodel = LDAPTreeItemModel(self.serverList, self) self.ldaptreemodel.workingSignal.connect(self.setBusy) # Set up the entrylist (uses the model) self.__setupEntryList() # The editor for entries self.tabWidget = QtGui.QTabWidget(self) #self.tabWidget.setDocumentMode(True) self.tabWidget.setMovable(True) self.setMinimumWidth(200) self.tabWidget.setTabsClosable(True) self.tabWidget.tabCloseRequested.connect(self.tabCloseClicked) self.tabWidget.setUsesScrollButtons(True) sizePolicy = self.tabWidget.sizePolicy() sizePolicy.setHorizontalStretch(1) self.tabWidget.setSizePolicy(sizePolicy) # Remember and looks up open tabs self.openTabs = {} self.splitter.addWidget(self.entryList) self.splitter.addWidget(self.tabWidget) self.mainLayout.addWidget(self.splitter) # Used to signal the ldaptreemodel with a index # which needs processing (reloading, clearing) self.reloadSignal.connect(self.ldaptreemodel.reloadItem) self.clearSignal.connect(self.ldaptreemodel.clearItem) eventFilter = BrowserPluginEventFilter(self) self.installEventFilter(eventFilter) self.__createContextMenu() self.retranslateUi() self.progress = QMessageBox( 1, self.str_PLEASE_WAIT, self.str_PLEASE_WAIT_MSG, QMessageBox.Ignore, parent=self ) # For testing ONLY # AND ONLY ON SMALL LDAP-SERVERS SINCE IT LOADS BASICALLY ALL ENTIRES #import modeltest #self.modeltest = modeltest.ModelTest(self.ldaptreemodel, self); def setBusy(self, status): """ Helper-method. """ if status == True: self.progress.show() qApp.setOverrideCursor(Qt.WaitCursor) else: if not self.progress.isHidden(): self.progress.hide() qApp.restoreOverrideCursor() def __setupEntryList(self): # The view for server-content self.entryList = QtGui.QTreeView(self) self.entryList.setMinimumWidth(200) #self.entryList.setMaximumWidth(400) #self.entryList.setAlternatingRowColors(True) # Somewhat cool, but should be removed if deemed too taxing self.entryList.setAnimated(True) self.entryList.setUniformRowHeights(True) # MAJOR optimalization #self.entryList.setExpandsOnDoubleClick(False) self.entryList.setModel(self.ldaptreemodel) self.entryList.setMouseTracking(True) self.entryList.viewport().setMouseTracking(True) # For right-clicking in the tree self.entryList.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.entryList.customContextMenuRequested.connect(self.rightClick) # When something is activated (doubleclick, <enter> etc.) self.entryList.activated.connect(self.viewItem) self.delegate = LoadingDelegate(self.entryList) self.entryList.setItemDelegate(self.delegate) self.entryList.setSelectionMode( QtGui.QAbstractItemView.ExtendedSelection) def __createContextMenu(self): """Creates the context menu for the tree view. """ self.contextMenu = QMenu() self.contextMenuServerSettings = QAction(self) self.contextMenu.addAction(self.contextMenuServerSettings) self.contextMenu.addSeparator() self.contextMenuOpen = QAction(self) self.contextMenu.addAction(self.contextMenuOpen) self.contextMenuReload = QAction(self) self.contextMenu.addAction(self.contextMenuReload) self.contextMenuClear = QAction(self) self.contextMenu.addAction(self.contextMenuClear) self.contextMenuFilter = QAction(self) self.contextMenu.addAction(self.contextMenuFilter) self.contextMenuLimit = QAction(self) self.contextMenu.addAction(self.contextMenuLimit) self.contextMenu.addSeparator() self.contextMenuAdd = QMenu() self.contextMenu.addMenu(self.contextMenuAdd) self.contextMenuDelete = QMenu() self.contextMenu.addMenu(self.contextMenuDelete) self.contextMenuExport = QMenu() self.contextMenu.addMenu(self.contextMenuExport) # Connect the context menu actions to the correct slots self.contextMenuServerSettings.triggered.connect( self.editServerSettings) self.contextMenuOpen.triggered.connect(self.openChoosen) self.contextMenuReload.triggered.connect(self.reloadChoosen) self.contextMenuClear.triggered.connect(self.clearChoosen) self.contextMenuFilter.triggered.connect(self.filterChoosen) self.contextMenuLimit.triggered.connect(self.limitChoosen) def rightClick(self, point): """ Called when the view is right-clicked. Displays a context menu with possible actions. :param point: contains the global screen coordinates for the right-click that generated this call. :type potin: QPoint """ # This is a list of QModelIndex objects, which will be used by # the various context menu slots. # We therfore store it as a class member self.selection = self.entryList.selectedIndexes() openSupport = True reloadSupport = True clearSupport = True filterSupport = True limitSupport = True addSupport = True deleteSupport = True exportSupport = True editServerSupport = True # The number of selected items is used for naming of the actions # added to the submenues numselected = len(self.selection) # View disabled menu if nothing selected self.contextMenu.setEnabled(True) # Remember to enable if a selection if not numselected > 0: # If nothing is selected self.contextMenu.setEnabled(False) # Disable self.contextMenu.exec_(self.entryList.mapToGlobal(point)) # Show return # Iterate through the list of selected indexes, and # validate what operations are supported. That is, # if one of the selected indexes do not support an # operation, we cannot allow to apply that operation # on the whole selection for index in self.selection: item = index.internalPointer() operations = item.getSupportedOperations() if not AbstractLDAPTreeItem.SUPPORT_OPEN & operations: openSupport = False if not AbstractLDAPTreeItem.SUPPORT_RELOAD & operations: reloadSupport = False if not AbstractLDAPTreeItem.SUPPORT_CLEAR & operations: clearSupport = False if not AbstractLDAPTreeItem.SUPPORT_FILTER & operations: filterSupport = False if not AbstractLDAPTreeItem.SUPPORT_LIMIT & operations: limitSupport = False if not AbstractLDAPTreeItem.SUPPORT_ADD & operations: addSupport = False if not AbstractLDAPTreeItem.SUPPORT_DELETE & operations: deleteSupport = False if not AbstractLDAPTreeItem.SUPPORT_EXPORT & operations: exportSupport = False if index.internalPointer().getParentServerItem() == None: editServerSupport = False # Now we just use the *Support variables to enable|disable # the context menu actions. self.contextMenuOpen.setEnabled(openSupport) self.contextMenuReload.setEnabled(reloadSupport) self.contextMenuClear.setEnabled(clearSupport) self.contextMenuFilter.setEnabled(filterSupport) self.contextMenuLimit.setEnabled(limitSupport) self.contextMenuServerSettings.setEnabled(editServerSupport) # For the submenues in the context menu, we add appropriate # actions, based on single|multi selection, or disable the menu # altogether if there is no support for the operation. if (limitSupport or filterSupport or openSupport) \ and not numselected == 1: self.contextMenuLimit.setEnabled(False) self.contextMenuFilter.setEnabled(False) self.contextMenuOpen.setEnabled(False) if addSupport and numselected == 1: self.contextMenuAdd.setEnabled(True) # template templateMenu = QMenu(self.str_TEMPLATE) self.contextMenuAdd.addMenu(templateMenu) index = self.selection[0] for template in self.templateList.getTable(): sO = index.internalPointer().smartObject() if template.server == sO.serverMeta.name: method = lambda name = template.templateName, i = index : self.addTemplateChoosen(name, i) templateMenu.addAction(template.templateName, method) else: self.contextMenuAdd.setEnabled(False) if numselected != 1: self.contextMenuServerSettings.setEnabled(False) if deleteSupport: self.contextMenuDelete.setEnabled(True) if numselected == 1: self.contextMenuDelete.addAction( self.str_ITEM, self.deleteSelection ) self.contextMenuDelete.addAction( self.str_SUBTREE_ONLY, self.deleteSubtree ) #self.contextMenuDelete.addAction( # self.str_SUBTREE_PARENTS, self.deleteSelection #) else: self.contextMenuDelete.addAction( self.str_ITEMS, self.deleteSelection ) self.contextMenuDelete.addAction( self.str_SUBTREES, self.deleteSubtree ) #self.contextMenuDelete.addAction( # self.str_SUBTREES_PARENTS, self.deleteSelection #) else: self.contextMenuDelete.setEnabled(False) if exportSupport: self.contextMenuExport.setEnabled(True) if numselected == 1: self.contextMenuExport.addAction( self.str_ITEM, self.exportItems ) self.contextMenuExport.addAction( self.str_SUBTREE, self.exportSubtrees ) self.contextMenuExport.addAction( self.str_SUBTREE_PARENTS, self.exportSubtreeWithParents ) else: self.contextMenuExport.addAction( self.str_ITEMS, self.exportItems ) self.contextMenuExport.addAction( self.str_SUBTREES, self.exportSubtrees ) self.contextMenuExport.addAction( self.str_SUBTREES_PARENTS, self.exportSubtreeWithParents ) else: self.contextMenuExport.setEnabled(False) # Finally we execute the context menu self.contextMenu.exec_(self.entryList.mapToGlobal(point)) # We need to clear all the submenues after each right click # selection, if not; the submenu actions will be added and # thus duplicated for every selection the user makes. # FIXME: Find a better way of handling this issue. self.contextMenuAdd.clear() self.contextMenuDelete.clear() self.contextMenuExport.clear() """ Following methods are called from a context-menu. """ def openChoosen(self): if len(self.selection) == 1: self.viewItem(self.selection[0]) def reloadChoosen(self): for index in self.selection: self.reloadSignal.emit(index) def clearChoosen(self): for index in self.selection: self.clearSignal.emit(index) def limitChoosen(self): # Have the item set the limit for us, the reload for index in self.selection: ok = index.internalPointer().setLimit() if ok: self.reloadSignal.emit(index) def filterChoosen(self): # Have the item set the filter, then reload for index in self.selection: ok = index.internalPointer().setFilter() if ok: self.reloadSignal.emit(index) def addTemplateChoosen(self, templateName, index): serverMeta = index.internalPointer().smartObject().serverMeta baseDN = index.internalPointer().smartObject().getDN() template = self.templateList.getTemplateObject(templateName) smartO = template.getDataObject(serverMeta, baseDN) self.addNewEntry(index, smartO, template) def addNewEntry(self, parentIndex, defaultSmartObject=None, template=None): tmp = NewEntryDialog(parentIndex, defaultSmartObject, entryTemplate=template) if tmp.exec_(): ret = QMessageBox.question(self, QtCore.QCoreApplication.translate("BrowserView","Add"), QtCore.QCoreApplication.translate("BrowserView", "Do you want to reload to show the changes?"), QMessageBox.Yes|QMessageBox.No) if ret == QMessageBox.Yes: self.ldaptreemodel.reloadItem(self.selection[0]) """ Utility-methods """ def isOpen(self, smartObject): rep = self.getRepForSmartObject(smartObject) # The {}.has_key() method will be removed in the future version # of Python. Use the 'in' operation instead. [PEP8] #if self.openTabs.has_key(str(rep)): if str(rep) in self.openTabs: return True else: return False def getRepForSmartObject(self, smartObject): serverName = smartObject.getServerAlias() dn = smartObject.getDN() return (serverName, dn) def viewItem(self, index): """Opens items for viewing. """ item = index.internalPointer() supports = item.getSupportedOperations() # If we can't open this item, then don't if not supports & AbstractLDAPTreeItem.SUPPORT_OPEN: self.__logger.debug("Item didn't support open.") return smartObject = index.internalPointer().smartObject() rep = self.getRepForSmartObject(smartObject) # If the smartobject is already open, switch to it if self.isOpen(smartObject): x = self.openTabs[str(rep)] self.tabWidget.setCurrentWidget(x) return # Saves a representation of the opened entry to avoid opening duplicates # and open it x = AdvancedObjectWidget(QtCore.QPersistentModelIndex(index)) x.initModel(smartObject) self.openTabs[str(rep)] = x self.tabWidget.addTab(x, smartObject.getPrettyRDN()) self.tabWidget.setCurrentWidget(x) def deleteIndex(self, index): # Remember the smartObject for later sO = index.internalPointer().smartObject() # Try to delete (success, message) = self.ldaptreemodel.deleteItem(index) if success: # Close open edit-windows if any self.__closeTabIfOpen(sO) # Notify success return (True, message) else: # Notify fail return (False, message) def __closeTabIfOpen(self, sO): if self.isOpen(sO): rep = self.getRepForSmartObject(sO) x = self.openTabs.pop(str(rep)) i = self.tabWidget.indexOf(x) if i != -1: self.tabWidget.removeTab(i) def deleteSelection(self, subTree=False): """Slot for the context menu. Opens the DeleteDialog with the selected entries, giving the user the option to validate the selection before deleting. This is for deleting the item + possibly it's subtree. See deleteOnlySubtreeOfSelection() for only subtree. """ # Only a single item if len(self.selection) == 1 and not subTree: # Confirmation-message ok = QMessageBox.question( self, self.str_DELETE, self.str_REALLY_DELETE, QMessageBox.Yes | QMessageBox.No ) if ok == QMessageBox.No: return index = self.selection[0] (status, message) = self.deleteIndex(index) if not status: QMessageBox.critical( self, self.str_ERROR, self.str_ERROR_MSG.format( index.data().toPyObject(), message ) ) return # Make persistent indexes and list of smartObjects to be deleted persistenSelection = [] sOList = [] for x in self.selection: persistenSelection.append(QPersistentModelIndex(x)) sOList.append(x.internalPointer().smartObject()) # Create gui self.setBusy(True) deleteDialog = DeleteDialog(sOList, subTree) self.setBusy(False) status = deleteDialog.exec_() if status: # the dialog was not canceled if subTree: # Reload the items whos subtree was deleted for x in self.selection: self.ldaptreemodel.reloadItem(x) return # If all rows were removed successfully, just call # removeRows on all selected items (reloading all items of # the parent can be expensive) if deleteDialog.passedItemsWasDeleted: for x in persistenSelection: if x.isValid: i = x.sibling(x.row(), 0) # QModelIndex self.__closeTabIfOpen( i.internalPointer().smartObject()) self.ldaptreemodel.removeRow(x.row(), x.parent()) return # If not, call reload on the parent of all the items? else: tmp = QMessageBox.question( self, self.str_DELETION, self.str_DELETION_MSG, buttons=QMessageBox.Yes | QMessageBox.No, defaultButton=QMessageBox.Yes ) if tmp == QMessageBox.Yes: for x in persistenSelection: # index might not be valid if the parent was # reloaded by a previous item if x.isValid(): self.ldaptreemodel.reloadItem(x.parent()) return # Was cancelled so do nothing else: pass def deleteSubtree(self): self.deleteSelection(subTree=True) def exportItems(self): """Slot for the context menu. """ self.__exportSelection(scope=0) def exportSubtrees(self): """Slot for the context menu. """ self.__exportSelection(scope=1) def exportSubtreeWithParents(self): """Slot for the context menu. """ self.__exportSelection(scope=2) def __exportSelection(self, scope=0): """Slot for the context menu. Opens the ExportDialog with the selected entries, giving the user the option to validate the selection before exporting. :param scope: The scope selection. 0 = SCOPE_BASE -> Item(s), 1 = SCOPE_ONELEVEL -> Subtree(s); 2 = SCOPE_SUBTREE -> Subtree(s) with parent :type scope: int """ exportObjects = [] msg = '' self.setBusy(True) for index in self.selection: smartObject = index.internalPointer().smartObject() serverName = smartObject.getServerAlias() dn = smartObject.getDN() serverObject = self.serverList.getServerObject(serverName) con = LumaConnectionWrapper(serverObject, self) # For both subtree and subtree with parent, we fetch the # whole subtree including the parent, with a basic sync # search operation. Then, if only the subtree is to be # exported, we remove the smartObject(s) selected. if scope > 0: pass # Do a search on the whole subtree # 2 = ldap.SCOPE_SUBTREE #elif scope == 2: success, e = con.bindSync() if not success: self.__logger.error(str(e)) continue success, result, e = con.searchSync(base=dn, scope=2) if success: exportObjects.extend(result) else: self.__logger.error(str(e)) # If only the subtree is to be selected, we remove # the parent, which happens to be the smartObject(s) # initialy selected. if scope == 1: exportObjects.remove(smartObject) # For scope == 0 we need not do any LDAP search operation # because we already got what we need else: exportObjects.append(smartObject) # Initialize the export dialog # and give it the items for export dialog = ExportDialog(msg) dialog.setExportItems(exportObjects) self.setBusy(False) dialog.exec_() def editServerSettings(self): """Slot for the context menu. Opens the ServerDialog with the selected server. """ try: items = self.selection serverItem = items[0].internalPointer().getParentServerItem() serverName = serverItem.serverMeta.name serverDialog = ServerDialog(serverName) r = serverDialog.exec_() if r: self.serversChangedMessage.showMessage( self.str_SERVER_CHANGED_MSG ) except Exception, e: self.__logger.error(str(e)) QMessageBox.information( self, self.str_ERROR, self.str_SEE_LOG_DETAILS )
class BrowserView(QWidget): """Luma LDAP Browser plugin """ # Custom signals used reloadSignal = QtCore.pyqtSignal(QtCore.QModelIndex) clearSignal = QtCore.pyqtSignal(QtCore.QModelIndex) __logger = logging.getLogger(__name__) def __init__(self, parent=None, configPrefix=None): """ :param configPrefix: defines the location of serverlist. :type configPrefix: string """ super(BrowserView, self).__init__(parent) self.__logger = logging.getLogger(__name__) self.setObjectName("PLUGIN_BROWSER") self.templateList = TemplateList() # The serverlist used self.serverList = ServerList(configPrefix) self.serversChangedMessage = QtGui.QErrorMessage() self.mainLayout = QtGui.QHBoxLayout(self) self.splitter = QtGui.QSplitter(self) # Create the model self.ldaptreemodel = LDAPTreeItemModel(self.serverList, self) self.ldaptreemodel.workingSignal.connect(self.setBusy) # Set up the entrylist (uses the model) self.__setupEntryList() # The editor for entries self.tabWidget = QtGui.QTabWidget(self) #self.tabWidget.setDocumentMode(True) self.tabWidget.setMovable(True) self.setMinimumWidth(200) self.tabWidget.setTabsClosable(True) self.tabWidget.tabCloseRequested.connect(self.tabCloseClicked) self.tabWidget.setUsesScrollButtons(True) sizePolicy = self.tabWidget.sizePolicy() sizePolicy.setHorizontalStretch(1) self.tabWidget.setSizePolicy(sizePolicy) # Remember and looks up open tabs self.openTabs = {} self.splitter.addWidget(self.entryList) self.splitter.addWidget(self.tabWidget) self.mainLayout.addWidget(self.splitter) # Used to signal the ldaptreemodel with a index # which needs processing (reloading, clearing) self.reloadSignal.connect(self.ldaptreemodel.reloadItem) self.clearSignal.connect(self.ldaptreemodel.clearItem) eventFilter = BrowserPluginEventFilter(self) self.installEventFilter(eventFilter) self.__createContextMenu() self.retranslateUi() self.progress = QMessageBox(1, self.str_PLEASE_WAIT, self.str_PLEASE_WAIT_MSG, QMessageBox.Ignore, parent=self) # For testing ONLY # AND ONLY ON SMALL LDAP-SERVERS SINCE IT LOADS BASICALLY ALL ENTIRES #import modeltest #self.modeltest = modeltest.ModelTest(self.ldaptreemodel, self); def setBusy(self, status): """ Helper-method. """ if status == True: self.progress.show() qApp.setOverrideCursor(Qt.WaitCursor) else: if not self.progress.isHidden(): self.progress.hide() qApp.restoreOverrideCursor() def __setupEntryList(self): # The view for server-content self.entryList = QtGui.QTreeView(self) self.entryList.setMinimumWidth(200) #self.entryList.setMaximumWidth(400) #self.entryList.setAlternatingRowColors(True) # Somewhat cool, but should be removed if deemed too taxing self.entryList.setAnimated(True) self.entryList.setUniformRowHeights(True) # MAJOR optimalization #self.entryList.setExpandsOnDoubleClick(False) self.entryList.setModel(self.ldaptreemodel) self.entryList.setMouseTracking(True) self.entryList.viewport().setMouseTracking(True) # For right-clicking in the tree self.entryList.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.entryList.customContextMenuRequested.connect(self.rightClick) # When something is activated (doubleclick, <enter> etc.) self.entryList.activated.connect(self.viewItem) self.delegate = LoadingDelegate(self.entryList) self.entryList.setItemDelegate(self.delegate) self.entryList.setSelectionMode( QtGui.QAbstractItemView.ExtendedSelection) def __createContextMenu(self): """Creates the context menu for the tree view. """ self.contextMenu = QMenu() self.contextMenuServerSettings = QAction(self) self.contextMenu.addAction(self.contextMenuServerSettings) self.contextMenu.addSeparator() self.contextMenuOpen = QAction(self) self.contextMenu.addAction(self.contextMenuOpen) self.contextMenuReload = QAction(self) self.contextMenu.addAction(self.contextMenuReload) self.contextMenuClear = QAction(self) self.contextMenu.addAction(self.contextMenuClear) self.contextMenuFilter = QAction(self) self.contextMenu.addAction(self.contextMenuFilter) self.contextMenuLimit = QAction(self) self.contextMenu.addAction(self.contextMenuLimit) self.contextMenu.addSeparator() self.contextMenuAdd = QMenu() self.contextMenu.addMenu(self.contextMenuAdd) self.contextMenuDelete = QMenu() self.contextMenu.addMenu(self.contextMenuDelete) self.contextMenuExport = QMenu() self.contextMenu.addMenu(self.contextMenuExport) # Connect the context menu actions to the correct slots self.contextMenuServerSettings.triggered.connect( self.editServerSettings) self.contextMenuOpen.triggered.connect(self.openChoosen) self.contextMenuReload.triggered.connect(self.reloadChoosen) self.contextMenuClear.triggered.connect(self.clearChoosen) self.contextMenuFilter.triggered.connect(self.filterChoosen) self.contextMenuLimit.triggered.connect(self.limitChoosen) def rightClick(self, point): """ Called when the view is right-clicked. Displays a context menu with possible actions. :param point: contains the global screen coordinates for the right-click that generated this call. :type potin: QPoint """ # This is a list of QModelIndex objects, which will be used by # the various context menu slots. # We therfore store it as a class member self.selection = self.entryList.selectedIndexes() openSupport = True reloadSupport = True clearSupport = True filterSupport = True limitSupport = True addSupport = True deleteSupport = True exportSupport = True editServerSupport = True # The number of selected items is used for naming of the actions # added to the submenues numselected = len(self.selection) # View disabled menu if nothing selected self.contextMenu.setEnabled(True) # Remember to enable if a selection if not numselected > 0: # If nothing is selected self.contextMenu.setEnabled(False) # Disable self.contextMenu.exec_(self.entryList.mapToGlobal(point)) # Show return # Iterate through the list of selected indexes, and # validate what operations are supported. That is, # if one of the selected indexes do not support an # operation, we cannot allow to apply that operation # on the whole selection for index in self.selection: item = index.internalPointer() operations = item.getSupportedOperations() if not AbstractLDAPTreeItem.SUPPORT_OPEN & operations: openSupport = False if not AbstractLDAPTreeItem.SUPPORT_RELOAD & operations: reloadSupport = False if not AbstractLDAPTreeItem.SUPPORT_CLEAR & operations: clearSupport = False if not AbstractLDAPTreeItem.SUPPORT_FILTER & operations: filterSupport = False if not AbstractLDAPTreeItem.SUPPORT_LIMIT & operations: limitSupport = False if not AbstractLDAPTreeItem.SUPPORT_ADD & operations: addSupport = False if not AbstractLDAPTreeItem.SUPPORT_DELETE & operations: deleteSupport = False if not AbstractLDAPTreeItem.SUPPORT_EXPORT & operations: exportSupport = False if index.internalPointer().getParentServerItem() == None: editServerSupport = False # Now we just use the *Support variables to enable|disable # the context menu actions. self.contextMenuOpen.setEnabled(openSupport) self.contextMenuReload.setEnabled(reloadSupport) self.contextMenuClear.setEnabled(clearSupport) self.contextMenuFilter.setEnabled(filterSupport) self.contextMenuLimit.setEnabled(limitSupport) self.contextMenuServerSettings.setEnabled(editServerSupport) # For the submenues in the context menu, we add appropriate # actions, based on single|multi selection, or disable the menu # altogether if there is no support for the operation. if (limitSupport or filterSupport or openSupport) \ and not numselected == 1: self.contextMenuLimit.setEnabled(False) self.contextMenuFilter.setEnabled(False) self.contextMenuOpen.setEnabled(False) if addSupport and numselected == 1: self.contextMenuAdd.setEnabled(True) # template templateMenu = QMenu(self.str_TEMPLATE) self.contextMenuAdd.addMenu(templateMenu) index = self.selection[0] for template in self.templateList.getTable(): sO = index.internalPointer().smartObject() if template.server == sO.serverMeta.name: method = lambda name=template.templateName, i=index: self.addTemplateChoosen( name, i) templateMenu.addAction(template.templateName, method) else: self.contextMenuAdd.setEnabled(False) if numselected != 1: self.contextMenuServerSettings.setEnabled(False) if deleteSupport: self.contextMenuDelete.setEnabled(True) if numselected == 1: self.contextMenuDelete.addAction(self.str_ITEM, self.deleteSelection) self.contextMenuDelete.addAction(self.str_SUBTREE_ONLY, self.deleteSubtree) #self.contextMenuDelete.addAction( # self.str_SUBTREE_PARENTS, self.deleteSelection #) else: self.contextMenuDelete.addAction(self.str_ITEMS, self.deleteSelection) self.contextMenuDelete.addAction(self.str_SUBTREES, self.deleteSubtree) #self.contextMenuDelete.addAction( # self.str_SUBTREES_PARENTS, self.deleteSelection #) else: self.contextMenuDelete.setEnabled(False) if exportSupport: self.contextMenuExport.setEnabled(True) if numselected == 1: self.contextMenuExport.addAction(self.str_ITEM, self.exportItems) self.contextMenuExport.addAction(self.str_SUBTREE, self.exportSubtrees) self.contextMenuExport.addAction(self.str_SUBTREE_PARENTS, self.exportSubtreeWithParents) else: self.contextMenuExport.addAction(self.str_ITEMS, self.exportItems) self.contextMenuExport.addAction(self.str_SUBTREES, self.exportSubtrees) self.contextMenuExport.addAction(self.str_SUBTREES_PARENTS, self.exportSubtreeWithParents) else: self.contextMenuExport.setEnabled(False) # Finally we execute the context menu self.contextMenu.exec_(self.entryList.mapToGlobal(point)) # We need to clear all the submenues after each right click # selection, if not; the submenu actions will be added and # thus duplicated for every selection the user makes. # FIXME: Find a better way of handling this issue. self.contextMenuAdd.clear() self.contextMenuDelete.clear() self.contextMenuExport.clear() """ Following methods are called from a context-menu. """ def openChoosen(self): if len(self.selection) == 1: self.viewItem(self.selection[0]) def reloadChoosen(self): for index in self.selection: self.reloadSignal.emit(index) def clearChoosen(self): for index in self.selection: self.clearSignal.emit(index) def limitChoosen(self): # Have the item set the limit for us, the reload for index in self.selection: ok = index.internalPointer().setLimit() if ok: self.reloadSignal.emit(index) def filterChoosen(self): # Have the item set the filter, then reload for index in self.selection: ok = index.internalPointer().setFilter() if ok: self.reloadSignal.emit(index) def addTemplateChoosen(self, templateName, index): serverMeta = index.internalPointer().smartObject().serverMeta baseDN = index.internalPointer().smartObject().getDN() template = self.templateList.getTemplateObject(templateName) smartO = template.getDataObject(serverMeta, baseDN) self.addNewEntry(index, smartO, template) def addNewEntry(self, parentIndex, defaultSmartObject=None, template=None): tmp = NewEntryDialog(parentIndex, defaultSmartObject, entryTemplate=template) if tmp.exec_(): ret = QMessageBox.question( self, QtCore.QCoreApplication.translate("BrowserView", "Add"), QtCore.QCoreApplication.translate( "BrowserView", "Do you want to reload to show the changes?"), QMessageBox.Yes | QMessageBox.No) if ret == QMessageBox.Yes: self.ldaptreemodel.reloadItem(self.selection[0]) """ Utility-methods """ def isOpen(self, smartObject): rep = self.getRepForSmartObject(smartObject) # The {}.has_key() method will be removed in the future version # of Python. Use the 'in' operation instead. [PEP8] #if self.openTabs.has_key(str(rep)): if str(rep) in self.openTabs: return True else: return False def getRepForSmartObject(self, smartObject): serverName = smartObject.getServerAlias() dn = smartObject.getDN() return (serverName, dn) def viewItem(self, index): """Opens items for viewing. """ item = index.internalPointer() supports = item.getSupportedOperations() # If we can't open this item, then don't if not supports & AbstractLDAPTreeItem.SUPPORT_OPEN: self.__logger.debug("Item didn't support open.") return smartObject = index.internalPointer().smartObject() rep = self.getRepForSmartObject(smartObject) # If the smartobject is already open, switch to it if self.isOpen(smartObject): x = self.openTabs[str(rep)] self.tabWidget.setCurrentWidget(x) return # Saves a representation of the opened entry to avoid opening duplicates # and open it x = AdvancedObjectWidget(QtCore.QPersistentModelIndex(index)) x.initModel(smartObject) self.openTabs[str(rep)] = x self.tabWidget.addTab(x, smartObject.getPrettyRDN()) self.tabWidget.setCurrentWidget(x) def deleteIndex(self, index): # Remember the smartObject for later sO = index.internalPointer().smartObject() # Try to delete (success, message) = self.ldaptreemodel.deleteItem(index) if success: # Close open edit-windows if any self.__closeTabIfOpen(sO) # Notify success return (True, message) else: # Notify fail return (False, message) def __closeTabIfOpen(self, sO): if self.isOpen(sO): rep = self.getRepForSmartObject(sO) x = self.openTabs.pop(str(rep)) i = self.tabWidget.indexOf(x) if i != -1: self.tabWidget.removeTab(i) def deleteSelection(self, subTree=False): """Slot for the context menu. Opens the DeleteDialog with the selected entries, giving the user the option to validate the selection before deleting. This is for deleting the item + possibly it's subtree. See deleteOnlySubtreeOfSelection() for only subtree. """ # Only a single item if len(self.selection) == 1 and not subTree: # Confirmation-message ok = QMessageBox.question(self, self.str_DELETE, self.str_REALLY_DELETE, QMessageBox.Yes | QMessageBox.No) if ok == QMessageBox.No: return index = self.selection[0] (status, message) = self.deleteIndex(index) if not status: QMessageBox.critical( self, self.str_ERROR, self.str_ERROR_MSG.format(index.data().toPyObject(), message)) return # Make persistent indexes and list of smartObjects to be deleted persistenSelection = [] sOList = [] for x in self.selection: persistenSelection.append(QPersistentModelIndex(x)) sOList.append(x.internalPointer().smartObject()) # Create gui self.setBusy(True) deleteDialog = DeleteDialog(sOList, subTree) self.setBusy(False) status = deleteDialog.exec_() if status: # the dialog was not canceled if subTree: # Reload the items whos subtree was deleted for x in self.selection: self.ldaptreemodel.reloadItem(x) return # If all rows were removed successfully, just call # removeRows on all selected items (reloading all items of # the parent can be expensive) if deleteDialog.passedItemsWasDeleted: for x in persistenSelection: if x.isValid: i = x.sibling(x.row(), 0) # QModelIndex self.__closeTabIfOpen( i.internalPointer().smartObject()) self.ldaptreemodel.removeRow(x.row(), x.parent()) return # If not, call reload on the parent of all the items? else: tmp = QMessageBox.question(self, self.str_DELETION, self.str_DELETION_MSG, buttons=QMessageBox.Yes | QMessageBox.No, defaultButton=QMessageBox.Yes) if tmp == QMessageBox.Yes: for x in persistenSelection: # index might not be valid if the parent was # reloaded by a previous item if x.isValid(): self.ldaptreemodel.reloadItem(x.parent()) return # Was cancelled so do nothing else: pass def deleteSubtree(self): self.deleteSelection(subTree=True) def exportItems(self): """Slot for the context menu. """ self.__exportSelection(scope=0) def exportSubtrees(self): """Slot for the context menu. """ self.__exportSelection(scope=1) def exportSubtreeWithParents(self): """Slot for the context menu. """ self.__exportSelection(scope=2) def __exportSelection(self, scope=0): """Slot for the context menu. Opens the ExportDialog with the selected entries, giving the user the option to validate the selection before exporting. :param scope: The scope selection. 0 = SCOPE_BASE -> Item(s), 1 = SCOPE_ONELEVEL -> Subtree(s); 2 = SCOPE_SUBTREE -> Subtree(s) with parent :type scope: int """ exportObjects = [] msg = '' self.setBusy(True) for index in self.selection: smartObject = index.internalPointer().smartObject() serverName = smartObject.getServerAlias() dn = smartObject.getDN() serverObject = self.serverList.getServerObject(serverName) con = LumaConnectionWrapper(serverObject, self) # For both subtree and subtree with parent, we fetch the # whole subtree including the parent, with a basic sync # search operation. Then, if only the subtree is to be # exported, we remove the smartObject(s) selected. if scope > 0: pass # Do a search on the whole subtree # 2 = ldap.SCOPE_SUBTREE #elif scope == 2: success, e = con.bindSync() if not success: self.__logger.error(str(e)) continue success, result, e = con.searchSync(base=dn, scope=2) if success: exportObjects.extend(result) else: self.__logger.error(str(e)) # If only the subtree is to be selected, we remove # the parent, which happens to be the smartObject(s) # initialy selected. if scope == 1: exportObjects.remove(smartObject) # For scope == 0 we need not do any LDAP search operation # because we already got what we need else: exportObjects.append(smartObject) # Initialize the export dialog # and give it the items for export dialog = ExportDialog(msg) dialog.setExportItems(exportObjects) self.setBusy(False) dialog.exec_() def editServerSettings(self): """Slot for the context menu. Opens the ServerDialog with the selected server. """ try: items = self.selection serverItem = items[0].internalPointer().getParentServerItem() serverName = serverItem.serverMeta.name serverDialog = ServerDialog(serverName) r = serverDialog.exec_() if r: self.serversChangedMessage.showMessage( self.str_SERVER_CHANGED_MSG) except Exception, e: self.__logger.error(str(e)) QMessageBox.information(self, self.str_ERROR, self.str_SEE_LOG_DETAILS)