1
0
mirror of https://github.com/altlinux/admc.git synced 2025-02-01 13:47:06 +03:00

Merge pull request #39 from altlinuxteam/drag-drop-better

Drag drop groups
This commit is contained in:
Dmitry Degtyarev 2020-06-10 12:50:23 +04:00 committed by GitHub
commit 1f4112d8ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 167 additions and 79 deletions

View File

@ -257,16 +257,26 @@ bool AdInterface::create_entry(const QString &name, const QString &dn, NewEntryT
}
}
// Used to update membership when changes happen to entry
void AdInterface::reload_attributes_of_entry_groups(const QString &dn) {
// Update all entries that are related to this one through
// membership
void AdInterface::update_related_entries(const QString &dn) {
// Update all groups that have this entry as member
QList<QString> groups = get_attribute_multi(dn, "memberOf");
for (auto group : groups) {
// Only reload if loaded already
if (attributes_map.contains(group)) {
load_attributes(group);
}
}
// Update all entries that are members of this group
QList<QString> members = get_attribute_multi(dn, "member");
for (auto member : members) {
// Only reload if loaded already
if (attributes_map.contains(member)) {
load_attributes(member);
}
}
}
void AdInterface::delete_entry(const QString &dn) {
@ -278,7 +288,7 @@ void AdInterface::delete_entry(const QString &dn) {
result = connection->object_delete(dn_cstr);
if (result == AD_SUCCESS) {
reload_attributes_of_entry_groups(dn);
update_related_entries(dn);
attributes_map.remove(dn);
attributes_loaded.remove(dn);
@ -289,31 +299,44 @@ void AdInterface::delete_entry(const QString &dn) {
}
}
void AdInterface::move_user(const QString &user_dn, const QString &container_dn) {
void AdInterface::move(const QString &dn, const QString &new_container) {
int result = AD_INVALID_DN;
QString user_name = extract_name_from_dn(user_dn);
QString new_dn = "CN=" + user_name + "," + container_dn;
QList<QString> dn_split = dn.split(',');
QString new_dn = dn_split[0] + "," + new_container;
const QByteArray user_dn_array = user_dn.toLatin1();
const char *user_dn_cstr = user_dn_array.constData();
const QByteArray dn_array = dn.toLatin1();
const char *dn_cstr = dn_array.constData();
const QByteArray container_dn_array = container_dn.toLatin1();
const char *container_dn_cstr = container_dn_array.constData();
const QByteArray new_container_array = new_container.toLatin1();
const char *new_container_cstr = new_container_array.constData();
result = connection->move_user(user_dn_cstr, container_dn_cstr);
const bool entry_is_group = is_group(dn);
const bool entry_is_user = is_user(dn);
if (!entry_is_user && !entry_is_group) {
emit move_failed(dn, new_container, new_dn, "AdInterface::move() only supports moving users and groups at the moment");
return;
}
if (entry_is_user) {
result = connection->move_user(dn_cstr, new_container_cstr);
} else {
result = connection->move(dn_cstr, new_container_cstr);
}
if (result == AD_SUCCESS) {
// Unload attributes at old dn
attributes_map.remove(user_dn);
attributes_loaded.remove(user_dn);
attributes_map.remove(dn);
attributes_loaded.remove(dn);
load_attributes(new_dn);
reload_attributes_of_entry_groups(new_dn);
update_related_entries(new_dn);
emit move_user_complete(user_dn, container_dn, new_dn);
emit move_complete(dn, new_container, new_dn);
} else {
emit move_user_failed(user_dn, container_dn, new_dn, get_error_str());
emit move_failed(dn, new_container, new_dn, get_error_str());
}
}
@ -369,7 +392,7 @@ void AdInterface::rename(const QString &dn, const QString &new_name) {
if (result == AD_SUCCESS) {
load_attributes(new_dn);
reload_attributes_of_entry_groups(new_dn);
update_related_entries(new_dn);
emit rename_complete(dn, new_name, new_dn);
} else {
@ -397,6 +420,79 @@ bool AdInterface::is_policy(const QString &dn) {
return attribute_value_exists(dn, "objectClass", "groupPolicyContainer");
}
bool AdInterface::is_container_like(const QString &dn) {
// TODO: check that this includes all fitting objectClasses
const QList<QString> containerlike_objectClasses = {"organizationalUnit", "builtinDomain", "domain"};
for (auto c : containerlike_objectClasses) {
if (AD()->attribute_value_exists(dn, "objectClass", c)) {
return true;
}
}
return false;
}
enum DropType {
DropType_Move,
DropType_AddToGroup,
DropType_None
};
// Determine what kind of drop type is dropping this entry onto target
// If drop type is none, then can't drop this entry on this target
DropType get_drop_type(const QString &dn, const QString &target_dn) {
const bool dropped_is_user = AD()->is_user(dn);
const bool dropped_is_group = AD()->is_group(dn);
const bool target_is_group = AD()->is_group(target_dn);
const bool target_is_ou = AD()->is_ou(target_dn);
const bool target_is_container = AD()->is_container(target_dn);
const bool target_is_container_like = AD()->is_container_like(target_dn);
if (dropped_is_user) {
if (target_is_ou || target_is_container) {
return DropType_Move;
} else if (target_is_group) {
return DropType_AddToGroup;
}
} else if (dropped_is_group) {
if (target_is_ou || target_is_container || target_is_container_like) {
return DropType_Move;
}
}
return DropType_None;
}
bool AdInterface::can_drop_entry(const QString &dn, const QString &target_dn) {
DropType drop_type = get_drop_type(dn, target_dn);
if (drop_type == DropType_None) {
return false;
} else {
return true;
}
}
// General "drop" operation that can either move, link or change membership depending on which types of entries are involved
void AdInterface::drop_entry(const QString &dn, const QString &target_dn) {
DropType drop_type = get_drop_type(dn, target_dn);
switch (drop_type) {
case DropType_Move: {
AD()->move(dn, target_dn);
break;
}
case DropType_AddToGroup: {
AD()->add_user_to_group(target_dn, dn);
break;
}
case DropType_None: {
break;
}
}
}
AdInterface *AD() {
ADMC *app = qobject_cast<ADMC *>(qApp);
AdInterface *ad = app->ad_interface();

View File

@ -76,7 +76,7 @@ public:
bool set_attribute(const QString &dn, const QString &attribute, const QString &value);
bool create_entry(const QString &name, const QString &dn, NewEntryType type);
void delete_entry(const QString &dn);
void move_user(const QString &user_dn, const QString &container_dn);
void move(const QString &dn, const QString &new_container);
void add_user_to_group(const QString &group_dn, const QString &user_dn);
void rename(const QString &dn, const QString &new_name);
@ -85,6 +85,10 @@ public:
bool is_container(const QString &dn);
bool is_ou(const QString &dn);
bool is_policy(const QString &dn);
bool is_container_like(const QString &dn);
bool can_drop_entry(const QString &dn, const QString &target_dn);
void drop_entry(const QString &dn, const QString &target_dn);
signals:
void ad_interface_login_complete(const QString &base, const QString &head);
@ -104,8 +108,8 @@ signals:
void create_entry_complete(const QString &dn, NewEntryType type);
void create_entry_failed(const QString &dn, NewEntryType type, const QString &error_str);
void move_user_complete(const QString &user_dn, const QString &container_dn, const QString &new_dn);
void move_user_failed(const QString &user_dn, const QString &container_dn, const QString &new_dn, const QString &error_str);
void move_complete(const QString &dn, const QString &new_container, const QString &new_dn);
void move_failed(const QString &dn, const QString &new_container, const QString &new_dn, const QString &error_str);
void add_user_to_group_complete(const QString &group_dn, const QString &user_dn);
void add_user_to_group_failed(const QString &group_dn, const QString &user_dn, const QString &error_str);
@ -119,7 +123,7 @@ private:
QSet<QString> attributes_loaded;
void load_attributes(const QString &dn);
void reload_attributes_of_entry_groups(const QString &dn);
void update_related_entries(const QString &dn);
};

View File

@ -42,8 +42,8 @@ AdModel::AdModel(QObject *parent)
AD(), &AdInterface::delete_entry_complete,
this, &AdModel::on_delete_entry_complete);
connect(
AD(), &AdInterface::move_user_complete,
this, &AdModel::on_move_user_complete);
AD(), &AdInterface::move_complete,
this, &AdModel::on_move_complete);
connect(
AD(), &AdInterface::create_entry_complete,
this, &AdModel::on_create_entry_complete);
@ -114,9 +114,9 @@ void AdModel::on_delete_entry_complete(const QString &dn) {
}
}
void AdModel::on_move_user_complete(const QString &user_dn, const QString &container_dn, const QString &new_dn) {
void AdModel::on_move_complete(const QString &dn, const QString &new_container, const QString &new_dn) {
// Remove old entry from model
QList<QStandardItem *> old_items = findItems(user_dn, Qt::MatchExactly | Qt::MatchRecursive, AdModel::Column::DN);
QList<QStandardItem *> old_items = findItems(dn, Qt::MatchExactly | Qt::MatchRecursive, AdModel::Column::DN);
if (old_items.size() > 0) {
QStandardItem *dn_item = old_items[0];
QModelIndex dn_index = dn_item->index();
@ -126,9 +126,9 @@ void AdModel::on_move_user_complete(const QString &user_dn, const QString &conta
// Need to load entry at new parent if the parent has already
// been expanded/fetched
// NOTE: loading if parent has already been fetched will
// NOTE: loading if parent hasn't been fetched will
// create a duplicate
QList<QStandardItem *> parent_items = findItems(container_dn, Qt::MatchExactly | Qt::MatchRecursive, AdModel::Column::DN);
QList<QStandardItem *> parent_items = findItems(new_container, Qt::MatchExactly | Qt::MatchRecursive, AdModel::Column::DN);
if (parent_items.size() > 0) {
QStandardItem *parent_dn_item = parent_items[0];
QModelIndex parent_dn_index = parent_dn_item->index();
@ -221,14 +221,7 @@ void load_row(QList<QStandardItem *> row, const QString &dn) {
bool showInAdvancedViewOnly = AD()->get_attribute(dn, "showInAdvancedViewOnly") == "TRUE";
bool is_container = false;
const QList<QString> container_objectClasses = {"container", "organizationalUnit", "builtinDomain", "domain"};
for (auto c : container_objectClasses) {
if (AD()->attribute_value_exists(dn, "objectClass", c)) {
is_container = true;
break;
}
}
bool is_container = AD()->is_container_like(dn) || AD()->is_container(dn);
QStandardItem *name_item = row[AdModel::Column::Name];
QStandardItem *category_item = row[AdModel::Column::Category];

View File

@ -56,7 +56,7 @@ private slots:
void on_ad_interface_login_complete(const QString &search_base, const QString &head_dn);
void on_load_attributes_complete(const QString &dn);
void on_delete_entry_complete(const QString &dn);
void on_move_user_complete(const QString &user_dn, const QString &container_dn, const QString &new_dn);
void on_move_complete(const QString &dn, const QString &new_container, const QString &new_dn);
void on_create_entry_complete(const QString &dn, NewEntryType type);
void on_rename_complete(const QString &dn, const QString &new_name, const QString &new_dn);

View File

@ -47,8 +47,8 @@ DetailsWidget::DetailsWidget(MembersWidget *members_widget_)
AD(), &AdInterface::delete_entry_complete,
this, &DetailsWidget::on_delete_entry_complete);
connect(
AD(), &AdInterface::move_user_complete,
this, &DetailsWidget::on_move_user_complete);
AD(), &AdInterface::move_complete,
this, &DetailsWidget::on_move_complete);
connect(
AD(), &AdInterface::load_attributes_complete,
this, &DetailsWidget::on_load_attributes_complete);
@ -101,9 +101,9 @@ void DetailsWidget::on_delete_entry_complete(const QString &dn) {
}
}
void DetailsWidget::on_move_user_complete(const QString &user_dn, const QString &container_dn, const QString &new_dn) {
void DetailsWidget::on_move_complete(const QString &dn, const QString &new_container, const QString &new_dn) {
// Switch to the entry at new dn (entry stays the same)
if (target_dn == user_dn) {
if (target_dn == dn) {
change_target(new_dn);
}
}

View File

@ -44,7 +44,7 @@ public slots:
private slots:
void on_ad_interface_login_complete(const QString &search_base, const QString &head_dn);
void on_delete_entry_complete(const QString &dn);
void on_move_user_complete(const QString &user_dn, const QString &container_dn, const QString &new_dn);
void on_move_complete(const QString &dn, const QString &new_container, const QString &new_dn);
void on_load_attributes_complete(const QString &dn);
void on_rename_complete(const QString &dn, const QString &new_name, const QString &new_dn);

View File

@ -51,23 +51,11 @@ QMimeData *EntryModel::mimeData(const QModelIndexList &indexes) const {
bool EntryModel::canDropMimeData(const QMimeData *data, Qt::DropAction, int, int, const QModelIndex &parent) const {
const QString dn = data->text();
const QString parent_dn = get_dn_from_index(parent);
const QString target_dn = get_dn_from_index(parent);
const bool dropped_is_user = AD()->is_user(dn);
const bool can_drop = AD()->can_drop_entry(dn, target_dn);
const bool parent_is_group = AD()->is_group(parent_dn);
const bool parent_is_ou = AD()->is_ou(parent_dn);
const bool parent_is_container = AD()->is_container(parent_dn);
// TODO: support dropping non-users
// TODO: support dropping policies
if (parent_dn == "") {
return false;
} else if (dropped_is_user && (parent_is_group || parent_is_ou || parent_is_container)) {
return true;
} else {
return false;
}
return can_drop;
}
bool EntryModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) {
@ -80,19 +68,9 @@ bool EntryModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int
}
const QString dn = data->text();
const QString parent_dn = get_dn_from_index(parent);
const QString target_dn = get_dn_from_index(parent);
const bool dropped_is_user = AD()->is_user(dn);
const bool parent_is_group = AD()->is_group(parent_dn);
const bool parent_is_ou = AD()->is_ou(parent_dn);
const bool parent_is_container = AD()->is_container(parent_dn);
if (dropped_is_user && (parent_is_ou || parent_is_container)) {
AD()->move_user(dn, parent_dn);
} else if (dropped_is_user && parent_is_group) {
AD()->add_user_to_group(parent_dn, dn);
}
AD()->drop_entry(dn, target_dn);
return true;
}

View File

@ -27,6 +27,10 @@ MembersModel::MembersModel(QObject *parent)
{
setHorizontalHeaderItem(Column::Name, new QStandardItem("Name"));
setHorizontalHeaderItem(Column::DN, new QStandardItem("DN"));
connect(
AD(), &AdInterface::move_complete,
this, &MembersModel::on_move_complete);
}
void MembersModel::change_target(const QString &new_target_dn) {
@ -61,3 +65,13 @@ QString MembersModel::get_dn_from_index(const QModelIndex &index) const {
return EntryModel::get_dn_from_index(index);
}
}
void MembersModel::on_move_complete(const QString &dn, const QString &new_container, const QString &new_dn) {
// If entry is in members model, need to update it's dn
QList<QStandardItem *> items = findItems(dn, Qt::MatchExactly | Qt::MatchRecursive, Column::DN);
if (items.size() > 0) {
QStandardItem *dn_item = items[Column::DN];
dn_item->setText(new_dn);
}
}

View File

@ -41,6 +41,9 @@ public:
void change_target(const QString &new_target_dn);
private slots:
void on_move_complete(const QString &dn, const QString &new_container, const QString &new_dn);
private:
QString target_dn;

View File

@ -61,11 +61,11 @@ StatusBar::StatusBar()
this, &StatusBar::on_create_entry_failed);
connect(
AD(), &AdInterface::move_user_complete,
this, &StatusBar::on_move_user_complete);
AD(), &AdInterface::move_complete,
this, &StatusBar::on_move_complete);
connect(
AD(), &AdInterface::move_user_failed,
this, &StatusBar::on_move_user_failed);
AD(), &AdInterface::move_failed,
this, &StatusBar::on_move_failed);
connect(
AD(), &AdInterface::add_user_to_group_complete,
@ -128,8 +128,8 @@ void StatusBar::on_create_entry_complete(const QString &dn, NewEntryType type) {
showMessage(msg);
}
void StatusBar::on_move_user_complete(const QString &user_dn, const QString &container_dn, const QString &new_dn) {
QString msg = QString("Moved entry \"%1\" to \"%2\"").arg(user_dn).arg(new_dn);
void StatusBar::on_move_complete(const QString &dn, const QString &new_container, const QString &new_dn) {
QString msg = QString("Moved \"%1\" to \"%2\"").arg(dn).arg(new_container);
showMessage(msg);
}
@ -157,8 +157,8 @@ void StatusBar::on_create_entry_failed(const QString &dn, NewEntryType type, con
showMessage(msg);
}
void StatusBar::on_move_user_failed(const QString &user_dn, const QString &container_dn, const QString &new_dn, const QString &error_str) {
QString msg = QString("Failed to move user \"%1\". Error: \"%2\"").arg(user_dn, error_str);
void StatusBar::on_move_failed(const QString &dn, const QString &new_container, const QString &new_dn, const QString &error_str) {
QString msg = QString("Failed to move \"%1\" to \"%2\". Error: \"%3\"").arg(dn, new_container, error_str);
showMessage(msg);
}

View File

@ -50,8 +50,8 @@ private slots:
void on_create_entry_complete(const QString &dn, NewEntryType type);
void on_create_entry_failed(const QString &dn, NewEntryType type, const QString &error_str);
void on_move_user_complete(const QString &user_dn, const QString &container_dn, const QString &new_dn);
void on_move_user_failed(const QString &user_dn, const QString &container_dn, const QString &new_dn, const QString &error_str);
void on_move_complete(const QString &dn, const QString &new_container, const QString &new_dn);
void on_move_failed(const QString &dn, const QString &new_container, const QString &new_dn, const QString &error_str);
void on_add_user_to_group_complete(const QString &group_dn, const QString &user_dn);
void on_add_user_to_group_failed(const QString &group_dn, const QString &user_dn, const QString &error_str);