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:
commit
1f4112d8ad
@ -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();
|
||||
|
@ -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);
|
||||
|
||||
};
|
||||
|
||||
|
@ -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];
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user