Skip to content

Commit 003a105

Browse files
authored
Merge pull request #275 from HEMPED/main
test hem's version
2 parents 0ea6422 + d892ba4 commit 003a105

File tree

15 files changed

+877
-555
lines changed

15 files changed

+877
-555
lines changed

couchdb/vodle/_design/vodle/validate_doc_update.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ function (newDoc, savedDoc, userCtx) {
3838
}
3939
*/
4040
} else {
41-
// if doc already exists, let noone update or delete it:
42-
if (savedDoc) {
43-
throw ({forbidden: 'Noone may update or delete existing poll documents.'})
41+
// if doc already exists, let noone update or delete it, unless its delegation map:
42+
if (savedDoc && !_id.endsWith("inverse_indirect_map") && !_id.endsWith("direct_delegation_map")) {
43+
throw ({forbidden: 'Noone may update or delete existing poll documents.'});
4444
}
4545
// let only the voters create it:
4646
let doc_pid = _id.substring(pollprefix.length, _id.indexOf("§"));

src/app/data.service.ts

Lines changed: 72 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -214,12 +214,12 @@ function myhash(what): string {
214214

215215
export type del_option_spec_t = {type: "+" | "-", oids: Array<string>};
216216
export type del_request_t = {option_spec: del_option_spec_t, public_key: string};
217-
export type del_response_t = {option_spec: del_option_spec_t};
217+
export type del_response_t = {option_spec: del_option_spec_t, status: "agreed" | "declined" | "revoked", "decline_cycle", "decline_self"};
218218
export type del_signed_response_t = string;
219219
export type del_agreement_t = { // by pid, did
220220
client_vid?: string,
221221
delegate_vid?: string,
222-
status?: "pending" | "agreed" | "declined" | "revoked",
222+
status?: "pending" | "agreed" | "declined" | "revoked" | "declined_cycle" | "declined_self",
223223
accepted_oids?: Set<string>, // oids accepted for delegation by delegate
224224
active_oids?: Set<string> // among those, oids currently activated for delegation by client
225225
};
@@ -312,13 +312,6 @@ export class DataService implements OnDestroy {
312312

313313
delegation_agreements_caches: Record<string, Map<string, del_agreement_t>>; // by pid, did
314314

315-
direct_delegation_map_caches: Record<string, Map<string, Map<string, string>>>; // redundant storage of direct delegation data, not stored in database
316-
inv_direct_delegation_map_caches: Record<string, Map<string, Map<string, Set<string>>>>; // redundant storage of inverse direct delegation data, not stored in database
317-
indirect_delegation_map_caches: Record<string, Map<string, Map<string, Set<string>>>>; // redundant storage of indirect delegation data, not stored in database
318-
inv_indirect_delegation_map_caches: Record<string, Map<string, Map<string, Set<string>>>>; // redundant storage of inverse indirect delegation data, not stored in database
319-
effective_delegation_map_caches: Record<string, Map<string, Map<string, string>>>; // redundant storage of effective delegation data, not stored in database
320-
inv_effective_delegation_map_caches: Record<string, Map<string, Map<string, Set<string>>>>; // redundant storage of inverse effective delegation data, not stored in database
321-
322315
tally_caches: Record<string, {}>; // temporary storage of tally data, not stored in database
323316

324317
news_keys: Set<string>;
@@ -436,12 +429,6 @@ export class DataService implements OnDestroy {
436429
this.outgoing_dids_caches = {};
437430
this.incoming_dids_caches = {};
438431
this.delegation_agreements_caches = {};
439-
this.direct_delegation_map_caches = {};
440-
this.inv_direct_delegation_map_caches = {};
441-
this.indirect_delegation_map_caches = {};
442-
this.inv_indirect_delegation_map_caches = {};
443-
this.effective_delegation_map_caches = {};
444-
this.inv_effective_delegation_map_caches = {};
445432
this.proxy_ratings_map_caches = {};
446433
this.max_proxy_ratings_map_caches = {};
447434
this.argmax_proxy_ratings_map_caches = {};
@@ -1522,7 +1509,9 @@ export class DataService implements OnDestroy {
15221509
} else {
15231510
this.G.L.error("DataService.setp change option attempted for existing entry", pid, key, value);
15241511
}
1525-
} else {
1512+
} else if (key == 'inverse_indirect_map') {
1513+
return this._setp_in_polldb(pid, key, value);
1514+
}else {
15261515
this.G.L.error("DataService.setp non-local attempted for non-draft poll", pid, key, value);
15271516
}
15281517
}
@@ -1568,7 +1557,7 @@ export class DataService implements OnDestroy {
15681557
// other polls' data is stored in poll's own database.
15691558
// construct key for poll db:
15701559
const pkey = this.get_voter_key_prefix(pid, vid) + key;
1571-
// this.G.L.trace("getv", pid, key, vid, pkey)
1560+
// this.G.L.trace("getv", pid, key, vid, pkey)
15721561
this.ensure_poll_cache(pid);
15731562
value = this.poll_caches[pid][pkey] || '';
15741563
}
@@ -1591,8 +1580,51 @@ export class DataService implements OnDestroy {
15911580
}
15921581
}
15931582

1594-
delv(pid: string, key: string) {
1595-
// delete a voter data item
1583+
// inverse_indirect_map:- key: voterid, value: [voterid1, voterid2, voterid3] voterids that have effectively delegated to the voterid
1584+
set_inverse_indirect_map(pid: string, val: Map<string, string>) {
1585+
const mapKey = `poll.${pid}.inverse_indirect_map`;
1586+
this._setp_in_polldb(pid, mapKey, JSON.stringify(Array.from(val.entries())));
1587+
}
1588+
1589+
set_effective_delegation(pid: string, vid: string, val: string[]) {
1590+
const mapKey = `poll.${pid}.inverse_indirect_map`;
1591+
const currentMap = this.get_inverse_indirect_map(pid);
1592+
1593+
currentMap.set(vid, JSON.stringify(val)); // Add or update the key-value pair
1594+
this._setp_in_polldb(pid, mapKey, JSON.stringify(Array.from(currentMap.entries())));
1595+
}
1596+
1597+
get_inverse_indirect_map(pid: string): Map<string, string> {
1598+
const cache = this.poll_caches[pid]['poll.' + pid + '.inverse_indirect_map'] || '[]';
1599+
const ps = cache ? JSON.parse(cache) : {};
1600+
const mp = new Map<string, string>(ps);
1601+
return mp;
1602+
}
1603+
1604+
// direct_delegation_map:- key: voterid, value: [[voterid, rank, is_current_delegation], [voterid2, rank, is_current_delegate]] of the voter that the key has delegated to.
1605+
// When ranked delegation is not allowed, the rank is always 0 and the size of the value is 1.
1606+
set_direct_delegation_map(pid: string, val: Map<string, Array<[string, string, string]>>) {
1607+
const mapKey = `poll.${pid}.direct_delegation_map`;
1608+
this._setp_in_polldb(pid, mapKey, JSON.stringify(Array.from(val.entries())));
1609+
}
1610+
1611+
get_direct_delegation_map(pid: string): Map<string, Array<[string, string, string]>> {
1612+
const cache = this.poll_caches[pid]['poll.' + pid + '.direct_delegation_map'] || '[]';
1613+
const ps = cache ? JSON.parse(cache) : {};
1614+
const mp = new Map<string, Array<[string, string, string]>>(ps);
1615+
return mp;
1616+
}
1617+
1618+
clear_direct_delegation_map(pid: string) {
1619+
const mapKey = `poll.${pid}.direct_delegation_map`;
1620+
this._setp_in_polldb(pid, mapKey, JSON.stringify([]));
1621+
}
1622+
1623+
delv(pid: string, key: string, vid?: string) {
1624+
if(!this.getv(pid, key)) {
1625+
this.G.L.warn("DataService.delv nothing to delete", pid, key);
1626+
return;
1627+
}
15961628
if (this.pid_is_draft(pid)) {
15971629
const ukey = get_poll_key_prefix(pid) + this.get_voter_key_prefix(pid) + key;
15981630
delete this.user_cache[ukey];
@@ -1605,6 +1637,10 @@ export class DataService implements OnDestroy {
16051637
}
16061638
}
16071639

1640+
get_ranked_delegation_allowed(pid: string): boolean {
1641+
return this.poll_caches[pid]['allow_ranked'] == 'true';
1642+
}
1643+
16081644
// TODO: delv!
16091645

16101646
get_example_docs(): Promise<any> {
@@ -1654,6 +1690,18 @@ export class DataService implements OnDestroy {
16541690
return this.store_user_data(ukey, this.user_cache, ukey);
16551691
}
16561692

1693+
setv_global_for_poll(pid: string, key: string, value: string): boolean {
1694+
// set global voter data item in poll db:
1695+
value = value || '';
1696+
// construct key for poll db:
1697+
// return 'voter.' + (vid ? vid : this.getp(pid, 'myvid')) + "§";
1698+
const pkey = "§";
1699+
this.ensure_poll_cache(pid);
1700+
this.G.L.trace("DataService.setv_global_for_poll", pid, key, value);
1701+
this.poll_caches[pid][pkey] = value;
1702+
return this.store_poll_data(pid, pkey, this.poll_caches[pid], pkey, false);
1703+
}
1704+
16571705
setv_in_polldb(pid: string, key: string, value: string, vid?: string): boolean {
16581706
/** Set voter data item in poll db.
16591707
* If necessary, mark the database entry with poll's due date
@@ -1770,6 +1818,9 @@ export class DataService implements OnDestroy {
17701818
this.page.onDataChange();
17711819
}
17721820
}
1821+
if (change.docs.some((doc) => doc._id.endsWith('shared_set'))) {
1822+
console.log('shared_set has been updated', change.docs);
1823+
}
17731824
this.G.L.exit("DataService.handle_poll_db_change", pid, this.pending_changes);
17741825
}
17751826

@@ -2317,7 +2368,7 @@ export class DataService implements OnDestroy {
23172368
// key existed in poll db, check whether update is allowed.
23182369
const value = dict[dict_key];
23192370
const enc_value = encrypt(value, poll_pw);
2320-
if ((key != 'due') && (key != 'state') && (decrypt(doc.value, poll_pw) != value)) {
2371+
if ((key != 'due') && (key != 'state') && (decrypt(doc.value, poll_pw) != value) && (key.indexOf("inverse_indirect_map") == -1) && (key.indexOf("direct_delegation_map") == -1)) {
23212372
// this is not allowed for poll docs!
23222373
this.G.L.error("DataService.store_poll_data tried changing an existing poll data item", pid, key, value);
23232374
} else if ((key == 'due') && (doc.due != value)) {
@@ -2379,7 +2430,7 @@ export class DataService implements OnDestroy {
23792430
// check which voter's data this is:
23802431
const vid_prefix = key.slice(0, key.indexOf("§")),
23812432
vid = this.user_cache[get_poll_key_prefix(pid) + 'myvid'];
2382-
if (vid_prefix != 'voter.' + vid && this.poll_caches[pid]['is_test']!='true') {
2433+
if (vid_prefix != 'voter.' + vid && this.poll_caches[pid]['is_test']!='true' && key.indexOf("inverse_indirect_map") == -1) {
23832434
// it is not allowed to alter other voters' data!
23842435
this.G.L.error("DataService.store_poll_data tried changing another voter's data item", pid, key);
23852436

src/app/delegation-dialog/delegation-dialog.page.html

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,20 @@ <h1 [innerHtml]="'delegation-request.header'|translate"></h1>
6060
</ion-input>
6161
</small>
6262
</ion-item>
63+
64+
<ng-container *ngIf="this.G.D.get_ranked_delegation_allowed(this.parent.pid)">
65+
<ion-item>
66+
<small>
67+
<ion-label position="floating" color="primary" [innerHtml]="'delegation-request.rank-label'|translate"></ion-label>
68+
<ion-select aria-label="Rank" interface="popover" [placeholder]="rank"
69+
(ionChange)="rank_changed($event)"
70+
>
71+
<ion-select-option *ngFor="let i of rank_options" [value]="i">{{i}}</ion-select-option>
72+
</ion-select>
73+
</small>
74+
</ion-item>
75+
</ng-container>
76+
6377
<ng-container *ngIf="can_share">
6478
<ion-item lines="none">
6579
<ion-col class="ion-no-padding ion-no-margin">

src/app/delegation-dialog/delegation-dialog.page.ts

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ along with vodle. If not, see <https://www.gnu.org/licenses/>.
1818
*/
1919

2020
import { Component, OnInit, Input, ViewChild } from '@angular/core';
21-
import { Validators, UntypedFormBuilder, UntypedFormGroup, UntypedFormControl, ValidationErrors, AbstractControl } from '@angular/forms';
21+
import { Validators, UntypedFormBuilder, UntypedFormGroup, UntypedFormControl, ValidationErrors, AbstractControl, Form } from '@angular/forms';
2222
import { IonInput, PopoverController } from '@ionic/angular';
2323
import { TranslateService } from '@ngx-translate/core';
2424

@@ -59,6 +59,8 @@ export class DelegationDialogPage implements OnInit {
5959
message_title: string;
6060
message_body: string;
6161
mailto_url: string;
62+
rank: number;
63+
rank_options: number[];
6264

6365
constructor(
6466
private popover: PopoverController,
@@ -75,8 +77,14 @@ export class DelegationDialogPage implements OnInit {
7577
this.can_share = Capacitor.isNativePlatform() || this.can_use_web_share;
7678
this.formGroup = this.formBuilder.group({
7779
delegate_nickname: new UntypedFormControl('', Validators.required),
78-
from: new UntypedFormControl(this.G.S.email)
80+
from: new UntypedFormControl(this.G.S.email),
7981
});
82+
83+
// checks if ranked delegation is allowed and if so, initialises the values needed for the drop-down menu
84+
if (this.G.D.get_ranked_delegation_allowed(this.parent.pid)) {
85+
this.initialise_rank_values();
86+
}
87+
8088
// TODO: what if already some delegation active or pending?
8189
// prepare a new delegation:
8290
[this.p, this.did, this.request, this.private_key, this.agreement] = this.G.Del.prepare_delegation(this.parent.pid);
@@ -92,6 +100,24 @@ export class DelegationDialogPage implements OnInit {
92100
setTimeout(() => this.focus_element.setFocus(), 100);
93101
}
94102

103+
initialise_rank_values() {
104+
const uid = this.parent.p.myvid;
105+
const dir_del_map = this.G.D.get_direct_delegation_map(this.parent.pid);
106+
const dir_del = dir_del_map.get(uid) || [];
107+
var ranks = Array.from({ length: environment.delegation.max_delegations }, (_, i) => i + 1);
108+
for (const entry of dir_del) {
109+
if (entry === undefined) {
110+
continue;
111+
}
112+
const indexToRemove: number = ranks.indexOf(Number(entry[1]));
113+
if (indexToRemove !== -1) {
114+
ranks.splice(indexToRemove, 1);
115+
}
116+
}
117+
this.rank = ranks[0];
118+
this.rank_options = ranks;
119+
}
120+
95121
delegate_nickname_changed() {
96122
const delegate_nickname = this.formGroup.get('delegate_nickname').value;
97123
this.G.D.setp(this.p.pid, "del_nickname." + this.did, delegate_nickname);
@@ -105,6 +131,10 @@ export class DelegationDialogPage implements OnInit {
105131
this.update_request();
106132
}
107133

134+
rank_changed(e) {
135+
this.rank = e.detail.value;
136+
}
137+
108138
update_request() {
109139
this.G.L.entry("DelegationDialogPage.update_request");
110140
this.mailto_url = "mailto:" + encodeURIComponent(this.formGroup.get('delegate_nickname').value) + "?subject=" + encodeURIComponent(this.message_title) + "&body=" + encodeURIComponent(this.message_body);
@@ -147,6 +177,7 @@ export class DelegationDialogPage implements OnInit {
147177
}).then(res => {
148178
this.G.L.info("DelegationDialogPage.share_button_clicked succeeded", res);
149179
this.G.Del.after_request_was_sent(this.parent.pid, this.did, this.request, this.private_key, this.agreement);
180+
this.G.Del.set_delegate_rank(this.parent.pid, this.did, this.rank);
150181
this.popover.dismiss();
151182
}).catch(err => {
152183
this.G.L.error("DelegationDialogPage.share_button_clicked failed", err);
@@ -159,6 +190,7 @@ export class DelegationDialogPage implements OnInit {
159190
this.from_changed();
160191
window.navigator.clipboard.writeText(this.delegation_link);
161192
this.G.Del.after_request_was_sent(this.parent.pid, this.did, this.request, this.private_key, this.agreement);
193+
this.G.Del.set_delegate_rank(this.parent.pid, this.did, this.rank);
162194
LocalNotifications.schedule({
163195
notifications: [{
164196
title: this.translate.instant("delegation-request.notification-copied-link-title"),
@@ -182,6 +214,7 @@ export class DelegationDialogPage implements OnInit {
182214
this.delegate_nickname_changed();
183215
this.from_changed();
184216
this.G.Del.after_request_was_sent(this.parent.pid, this.did, this.request, this.private_key, this.agreement);
217+
this.G.Del.set_delegate_rank(this.parent.pid, this.did, this.rank);
185218
this.parent.update_delegation_info();
186219
this.popover.dismiss();
187220
this.G.L.exit("DelegationDialogPage.email_button_clicked");

0 commit comments

Comments
 (0)