1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/conf/postgresql/schema.sql Fri Apr 22 16:22:58 2016 +0200
1.3 @@ -0,0 +1,163 @@
1.4 +-- Object store tables.
1.5 +
1.6 +create table objects (
1.7 + store_user varchar not null,
1.8 + object_uid varchar not null,
1.9 + object_text varchar not null,
1.10 + status varchar not null, -- 'active', 'cancelled'
1.11 + primary key(store_user, object_uid)
1.12 +);
1.13 +
1.14 +create table countered_objects (
1.15 + store_user varchar not null,
1.16 + other varchar not null,
1.17 + object_uid varchar not null,
1.18 + object_text varchar not null,
1.19 + primary key(store_user, object_uid)
1.20 +);
1.21 +
1.22 +create table recurrences (
1.23 + store_user varchar not null,
1.24 + object_uid varchar not null,
1.25 + object_recurrenceid varchar not null,
1.26 + object_text varchar not null,
1.27 + status varchar not null, -- 'active', 'cancelled'
1.28 + primary key(store_user, object_uid, object_recurrenceid)
1.29 +);
1.30 +
1.31 +create table countered_recurrences (
1.32 + store_user varchar not null,
1.33 + other varchar not null,
1.34 + object_uid varchar not null,
1.35 + object_recurrenceid varchar not null,
1.36 + object_text varchar not null,
1.37 + primary key(store_user, object_uid, object_recurrenceid)
1.38 +);
1.39 +
1.40 +-- Object store free/busy details.
1.41 +
1.42 +create table freebusy (
1.43 + store_user varchar not null,
1.44 + "start" varchar not null,
1.45 + "end" varchar not null,
1.46 + object_uid varchar,
1.47 + transp varchar,
1.48 + object_recurrenceid varchar,
1.49 + summary varchar,
1.50 + organiser varchar,
1.51 + expires varchar
1.52 +);
1.53 +
1.54 +create index freebusy_start on freebusy(store_user, "start");
1.55 +create index freebusy_end on freebusy(store_user, "end");
1.56 +
1.57 +create table freebusy_offers (
1.58 + store_user varchar not null,
1.59 + "start" varchar not null,
1.60 + "end" varchar not null,
1.61 + object_uid varchar,
1.62 + transp varchar,
1.63 + object_recurrenceid varchar,
1.64 + summary varchar,
1.65 + organiser varchar,
1.66 + expires varchar
1.67 +);
1.68 +
1.69 +create index freebusy_offers_start on freebusy_offers(store_user, "start");
1.70 +create index freebusy_offers_end on freebusy_offers(store_user, "end");
1.71 +
1.72 +create table freebusy_other (
1.73 + store_user varchar not null,
1.74 + other varchar not null,
1.75 + "start" varchar not null,
1.76 + "end" varchar not null,
1.77 + object_uid varchar,
1.78 + transp varchar,
1.79 + object_recurrenceid varchar,
1.80 + summary varchar,
1.81 + organiser varchar,
1.82 + expires varchar
1.83 +);
1.84 +
1.85 +create index freebusy_other_start on freebusy_other(store_user, other, "start");
1.86 +create index freebusy_other_end on freebusy_other(store_user, other, "end");
1.87 +
1.88 +create table freebusy_providers (
1.89 + store_user varchar not null,
1.90 + object_uid varchar not null,
1.91 + object_recurrenceid varchar
1.92 +);
1.93 +
1.94 +create index freebusy_providers_store_user on freebusy_providers(store_user);
1.95 +
1.96 +create table freebusy_provider_datetimes (
1.97 + store_user varchar not null,
1.98 + "start" varchar
1.99 +);
1.100 +
1.101 +create index freebusy_provider_datetimes_store_user on freebusy_provider_datetimes(store_user);
1.102 +
1.103 +-- Object store request details.
1.104 +
1.105 +create table requests (
1.106 + store_user varchar not null,
1.107 + object_uid varchar not null,
1.108 + object_recurrenceid varchar,
1.109 + request_type varchar
1.110 +);
1.111 +
1.112 +create index requests_object_uid on requests(store_user, object_uid);
1.113 +
1.114 +
1.115 +
1.116 +-- Journal store tables.
1.117 +
1.118 +-- Journal free/busy details.
1.119 +
1.120 +create table quota_freebusy (
1.121 + quota varchar not null,
1.122 + user_group varchar not null,
1.123 + "start" varchar not null,
1.124 + "end" varchar not null,
1.125 + object_uid varchar,
1.126 + transp varchar,
1.127 + object_recurrenceid varchar,
1.128 + summary varchar,
1.129 + organiser varchar,
1.130 + expires varchar
1.131 +);
1.132 +
1.133 +create index quota_freebusy_start on quota_freebusy(quota, user_group, "start");
1.134 +create index quota_freebusy_end on quota_freebusy(quota, user_group, "end");
1.135 +
1.136 +create table user_freebusy (
1.137 + quota varchar not null,
1.138 + store_user varchar not null,
1.139 + "start" varchar not null,
1.140 + "end" varchar not null,
1.141 + object_uid varchar,
1.142 + transp varchar,
1.143 + object_recurrenceid varchar,
1.144 + summary varchar,
1.145 + organiser varchar,
1.146 + expires varchar
1.147 +);
1.148 +
1.149 +create index user_freebusy_start on user_freebusy(quota, store_user, "start");
1.150 +create index user_freebusy_end on user_freebusy(quota, store_user, "end");
1.151 +
1.152 +-- Journal user groups and limits.
1.153 +
1.154 +create table quota_limits (
1.155 + quota varchar not null,
1.156 + user_group varchar not null,
1.157 + quota_limit varchar not null,
1.158 + primary key(user_group)
1.159 +);
1.160 +
1.161 +create table user_groups (
1.162 + quota varchar not null,
1.163 + store_user varchar not null,
1.164 + user_group varchar not null,
1.165 + primary key(store_user, user_group)
1.166 +);
2.1 --- a/docs/wiki/Administration Tue Apr 19 21:20:57 2016 +0200
2.2 +++ b/docs/wiki/Administration Fri Apr 22 16:22:58 2016 +0200
2.3 @@ -131,3 +131,38 @@
2.4 use of this tool will be performed by the packaging system provided by an
2.5 operating system distribution. The `tools/install.sh` script runs the above
2.6 tool as part of the installation process.
2.7 +
2.8 +== Copying Stores and Changing Store Types ==
2.9 +
2.10 +A rudimentary tool is provided that can copy data between stores, even those of
2.11 +different types, thus allowing the migration of data from one kind of store to
2.12 +another. Although it does not perform the copying in the most efficient manner,
2.13 +it provides a convenient method of copying that uses the software's own general
2.14 +interfaces for store access and thus acts as a way of verifying that these are
2.15 +functioning correctly.
2.16 +
2.17 +To copy a configured store to another filesystem location:
2.18 +
2.19 +{{{
2.20 +tools/copy_store.py -t file /tmp/store /tmp/journal
2.21 +}}}
2.22 +
2.23 +To copy a configured store to a database (which must have been initialised):
2.24 +
2.25 +{{{
2.26 +tools/copy_store.py -t postgresql 'dbname=store' 'dbname=journal'
2.27 +}}}
2.28 +
2.29 +To copy an explicitly-specified file store to another filesystem location:
2.30 +
2.31 +{{{
2.32 +tools/copy_store.py \
2.33 + -f file /var/lib/imip-agent/store /var/lib/imip-agent/journal \
2.34 + -t file /tmp/store /tmp/journal
2.35 +}}}
2.36 +
2.37 +The help message for the tool provides general guidance for its use:
2.38 +
2.39 +{{{
2.40 +tools/copy_store.py --help
2.41 +}}}
3.1 --- a/docs/wiki/Configuration Tue Apr 19 21:20:57 2016 +0200
3.2 +++ b/docs/wiki/Configuration Fri Apr 22 16:22:58 2016 +0200
3.3 @@ -95,11 +95,15 @@
3.4
3.5 === System-Level and Tool Configuration ===
3.6
3.7 -Given a choice of [[../SystemUsers|system users and groups]], and the choices
3.8 -made when configuring how imip-agent integrates with other components, the
3.9 -resulting configuration must be indicated in the `config.sh` file. Since
3.10 -the `tools/install.sh` script depends on this configuration, changes must
3.11 -be made to the file in the `tools/config.sh` location before installation
3.12 +The `config.sh` file must indicate choices in the following areas:
3.13 +
3.14 + * The [[../DataStore|data store]] type to be employed
3.15 +
3.16 + * The [[../SystemUsers|system users and groups]] involved with running the
3.17 + software and storing data
3.18 +
3.19 +Since the `tools/install.sh` script depends on this configuration, changes
3.20 +must be made to the file in the `tools/config.sh` location before installation
3.21 can occur.
3.22
3.23 {{{#!table
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/docs/wiki/DataStore Fri Apr 22 16:22:58 2016 +0200
4.3 @@ -0,0 +1,17 @@
4.4 += Data Store =
4.5 +
4.6 +The data store for imip-agent holds calendar data and free/busy information.
4.7 +The following data store types exist:
4.8 +
4.9 + * A [[../FileStore|file store]] employing textual files in the filesystem
4.10 +
4.11 + * A [[../DatabaseStore|database store]] employing database tables managed
4.12 + by a database management system
4.13 +
4.14 +For simplicity, the file store is the default storage mechanism for imip-agent,
4.15 +but the database store is provided as an alternative where different operating
4.16 +characteristics are desired.
4.17 +
4.18 +The [[../Configuration|configuration]] files (`config.sh` and `config.py`) need
4.19 +updating to reflect the choice of data store, with the directory parameters
4.20 +set to appropriate values for the chosen store type.
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/docs/wiki/DatabaseStore Fri Apr 22 16:22:58 2016 +0200
5.3 @@ -0,0 +1,122 @@
5.4 += Database Store =
5.5 +
5.6 +The database data store offers a mechanism for storing calendar objects and free/busy
5.7 +details in a database system, currently focusing on relational database systems, with
5.8 +specific support for PostgreSQL.
5.9 +
5.10 +Benefits of the database store include convenient and standardised interfaces for
5.11 +querying and inspecting the data, together with various guarantees around the durability
5.12 +of the stored data. However, more administrative knowledge is usually required to operate
5.13 +database installations satisfactorily, and activities such as archiving require extra
5.14 +planning and expertise.
5.15 +
5.16 +The [[../FileStore|file store]] offers a more convenient method of managing calendar
5.17 +data, albeit offering arguably less scalability and less convenience when data volumes
5.18 +become significantly large.
5.19 +
5.20 +== Configuration Settings ==
5.21 +
5.22 +The [[../Configuration|configuration]] files (`config.sh` and `config.py`) need to be
5.23 +updated when choosing a database store.
5.24 +
5.25 +{{{#!table
5.26 +'''File''' || '''Setting''' || '''Value''' || '''Description'''
5.27 +==
5.28 +`config.sh`
5.29 +||<rowspan="2"> `STORE_TYPE`
5.30 +||<rowspan="2"> `postgresql`
5.31 +||<rowspan="2"> Selects a specific database store type; currently, only `postgresql`
5.32 + .. is supported
5.33 +==
5.34 +`config.py`
5.35 +==
5.36 +<rowspan="2"> `config.py`
5.37 +|| `STORE_DIR`
5.38 +|| `dbname=imip_agent`
5.39 +|| Indicates a connection string for the store database
5.40 +==
5.41 +`JOURNAL_DIR`
5.42 +|| `dbname=imip_agent`
5.43 +|| Indicates a connection string for the journal database
5.44 +}}}
5.45 +
5.46 +== Journal Structure ==
5.47 +
5.48 +The journal information is retained in a collection of tables. Unlike the structure of
5.49 +the [[../FileStore|file store]] in the [[../FilesystemUsage|filesystem]], each table
5.50 +contains details for all quota groups, with each query indicating the group to select
5.51 +the appropriate information.
5.52 +
5.53 +{{{#!table
5.54 +'''Table''' || '''Purpose'''
5.55 +==
5.56 +`quota_freebusy`
5.57 +|| Period descriptions describing reservations for resources sharing a quota (`quota`)
5.58 +.. made by users or groups (`user_group`), structured similarly to the `freebusy` table
5.59 +.. in the data store
5.60 +==
5.61 +`quota_limits`
5.62 +|| A mapping from user identities or group identifiers to quota limits
5.63 +==
5.64 +`user_freebusy`
5.65 +|| Period descriptions for reservations made in the context of a quota (`quota`) by a
5.66 +.. specific user (`store_user`), structured similarly to the `freebusy` table in the
5.67 +.. data store
5.68 +==
5.69 +`user_groups`
5.70 +|| A mapping from user identities to group identifiers indicating the sharing of a quota
5.71 +.. across a number of users
5.72 +}}}
5.73 +
5.74 +== Store Structure ==
5.75 +
5.76 +The store information is retained in a collection of tables. Unlike the structure of
5.77 +the [[../FileStore|file store]] in the [[../FilesystemUsage|filesystem]], each table
5.78 +contains details for all users, with each query indicating the user to select
5.79 +the appropriate information.
5.80 +
5.81 +{{{#!table
5.82 +'''Table''' || '''Purpose'''
5.83 +==
5.84 +`countered_objects`
5.85 +|| Retains counter-proposals corresponding to events held in the `objects` table
5.86 +.. received by each user (`store_user`) from another user (`other`)
5.87 +==
5.88 +`countered_recurrences`
5.89 +|| Retains counter-proposals corresponding to events held in the `recurrences` table
5.90 +.. received by each user (`store_user`) from another user (`other`)
5.91 +==
5.92 +`freebusy`
5.93 +|| Period descriptions indicating start and end datetimes (in UTC), unique identifier
5.94 +.. (`object_uid`), transparency, recurrence identifier (`object_recurrenceid`),
5.95 +.. summary and organiser, reflecting the schedule of each user (`store_user`)
5.96 +==
5.97 +`freebusy_offers`
5.98 +|| Period descriptions for scheduling offers made by counter-proposals sent by each
5.99 +.. user (`store_user`)
5.100 +==
5.101 +`freebusy_other`
5.102 +|| Period descriptions received or deduced for a user (`store_user`) structured
5.103 +.. similarly to the user's own `freebusy` records
5.104 +==
5.105 +`freebusy_providers`
5.106 +|| Details of [[../EventRecurrences|recurring events]] for which new free/busy records
5.107 +.. must be [[../CronIntegration|periodically generated]] because these events recur
5.108 +.. indefinitely, selectable for each user (`store_user`)
5.109 +==
5.110 +`freebusy_provider_datetimes`
5.111 +|| Date/time details associated with the `freebusy_providers` information
5.112 +==
5.113 +`objects`
5.114 +|| Records for each user (`store_data`) containing received event data (`object_text`)
5.115 +==
5.116 +`requests`
5.117 +|| A collections of records, each belonging to a specific user (`store_user`)
5.118 +.. consisting of a unique identifier (`object_uid`), normalised recurrence identifier
5.119 +.. (`object_recurrenceid`) if appropriate, and (optionally) a scheduling method,
5.120 +.. indicating the availability of an incoming scheduling request for handling by a user
5.121 +==
5.122 +`recurrences`
5.123 +|| Records for each user (`store_data`) containing received recurrence event data
5.124 +.. (`object_text`)
5.125 +}}}
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/docs/wiki/FileStore Fri Apr 22 16:22:58 2016 +0200
6.3 @@ -0,0 +1,40 @@
6.4 += File Store =
6.5 +
6.6 +The file data store is the default mechanism for storing calendar objects and
6.7 +free/busy details, making use of various directories as described in the
6.8 +[[../FilesystemUsage|filesystem usage guide]].
6.9 +
6.10 +Benefits of the file store include transparency and ease of administration:
6.11 +all data is stored in text files, direct modification of certain files can be
6.12 +performed to change the system's behaviour, archiving is possible using
6.13 +traditional filesystem tools. However, the simple representation may make
6.14 +certain operations costly, such as the modification of tabular data, and
6.15 +querying of data may not always be particularly convenient.
6.16 +
6.17 +Thus, the [[../DatabaseStore|database store]] exists as an alternative, offering
6.18 +different characteristics to those of the file store.
6.19 +
6.20 +== Configuration Settings ==
6.21 +
6.22 +The [[../Configuration|configuration]] files (`config.sh` and `config.py`) need to be
6.23 +updated when choosing a file store.
6.24 +
6.25 +{{{#!table
6.26 +'''File''' || '''Setting''' || '''Value''' || '''Description'''
6.27 +==
6.28 +`config.sh`
6.29 +||<rowspan="2"> `STORE_TYPE`
6.30 +||<rowspan="2"> `file`
6.31 +||<rowspan="2"> Selects the default file storage type
6.32 +==
6.33 +`config.py`
6.34 +==
6.35 +<rowspan="2"> `config.py`
6.36 +|| `STORE_DIR`
6.37 +|| `/var/lib/imip-agent/store`
6.38 +|| Indicates the filesystem location of the data store
6.39 +==
6.40 +`JOURNAL_DIR`
6.41 +|| `/var/lib/imip-agent/journal`
6.42 +|| Indicates the filesystem location of the journal
6.43 +}}}
7.1 --- a/docs/wiki/FilesystemUsage Tue Apr 19 21:20:57 2016 +0200
7.2 +++ b/docs/wiki/FilesystemUsage Fri Apr 22 16:22:58 2016 +0200
7.3 @@ -26,6 +26,11 @@
7.4 Note that the free/busy resources are located in `/var/www` as opposed to
7.5 `/var/lib` since they are intended to be published on the Web.
7.6
7.7 +Meanwhile, the journal and store resources are only present in the filesystem
7.8 +if the [[../FileStore|file store]] is in use. Where a
7.9 +[[../DatabaseStore|database store]] is being used instead, such resources are
7.10 +located in a database system.
7.11 +
7.12 == Journal Structure ==
7.13
7.14 Within the journal directory are a collection of subdirectories, each of which
8.1 --- a/docs/wiki/FrontPage Tue Apr 19 21:20:57 2016 +0200
8.2 +++ b/docs/wiki/FrontPage Fri Apr 22 16:22:58 2016 +0200
8.3 @@ -165,6 +165,7 @@
8.4 * [[/CalendaringSupport|Calendaring Support]]
8.5 * [[/CounterProposals|Counter-Proposals and Offers]]
8.6 * [[/CronIntegration|Cron Task Scheduler Integration]]
8.7 + * [[/DataStore|Data Store]]
8.8 * [[/EventRecurrences|Event Recurrences]]
8.9 * [[/FilesystemUsage|Filesystem Usage]]
8.10 * [[/IncomingMessages|Incoming Messages]]
9.1 --- a/docs/wiki/GettingStarted Tue Apr 19 21:20:57 2016 +0200
9.2 +++ b/docs/wiki/GettingStarted Fri Apr 22 16:22:58 2016 +0200
9.3 @@ -101,6 +101,10 @@
9.4 || Postfix routing and transport configuration
9.5 || [[../MailIntegration|E-Mail Integration]] and
9.6 .. [[../MailboxIntegration|Mailbox Integration]]
9.7 +==
9.8 +`postgresql`
9.9 +|| PostgreSQL configuration
9.10 +|| [[../DatabaseStore|Database Store]]
9.11 }}}
9.12
9.13 In addition, a `tools` directory provides a configuration helper tool
10.1 --- a/docs/wiki/MailIntegration--MTA Tue Apr 19 21:20:57 2016 +0200
10.2 +++ b/docs/wiki/MailIntegration--MTA Fri Apr 22 16:22:58 2016 +0200
10.3 @@ -77,3 +77,32 @@
10.4 mailserver:example.com
10.5 }}}
10.6 }}}}
10.7 +
10.8 +== Useful Commands ==
10.9 +
10.10 +The following commands prove useful when troubleshooting and appear to be
10.11 +available as shown within a Debian environment.
10.12 +
10.13 +{{{#!table
10.14 +'''Task''' || '''Exim''' || '''Postfix'''
10.15 +==
10.16 +Check the mail queue
10.17 +||<colspan="2"> `mailq`
10.18 +==
10.19 +Process the mail queue
10.20 +|| `sendmail -q` (or `exim -q` or `runq`)
10.21 +|| `sendmail -q` (or `postqueue`)
10.22 +==
10.23 +Flush the mail queue
10.24 +|| `exim -qff`
10.25 +|| `postqueue -f`
10.26 +==
10.27 +Deliver a specific message
10.28 +|| `exim -M <identifier>`
10.29 +|| `postqueue -i <identifier>`
10.30 +==
10.31 +Test delivery for an address
10.32 +||<colspan="2"> `sendmail -bt <address>` (see also `sendmail -v -bv <address>` and `sendmail -v -bvs <address>`)
10.33 +}}}
10.34 +
10.35 +See [[http://bradthemad.org/tech/notes/exim_cheatsheet.php|Exim Cheatsheet]] and [[http://www.postfix.org/DEBUG_README.html|Postfix Debugging Howto]] for more guidance.
11.1 --- a/docs/wiki/Prerequisites Tue Apr 19 21:20:57 2016 +0200
11.2 +++ b/docs/wiki/Prerequisites Fri Apr 22 16:22:58 2016 +0200
11.3 @@ -11,6 +11,11 @@
11.4 Python:: python
11.5 pytz:: python-tz
11.6
11.7 +If the [[../DatabaseStore|database store]] is used, the following packages are required:
11.8 +
11.9 + PostgreSQL:: postgresql-9.4 (version may vary between releases)
11.10 + psycopg2:: python-psycopg2
11.11 +
11.12 To provide localised messages:
11.13
11.14 gettext:: gettext
12.1 --- a/docs/wiki/Resources Tue Apr 19 21:20:57 2016 +0200
12.2 +++ b/docs/wiki/Resources Fri Apr 22 16:22:58 2016 +0200
12.3 @@ -372,7 +372,8 @@
12.4
12.5 ==== Initialising Quotas ====
12.6
12.7 -Within the journal storage area (described in the [[../FilesystemUsage|filesystem guide]]),
12.8 +Within the journal storage area (described in the
12.9 +[[../FilesystemUsage|filesystem guide]] and [[../DatabaseStore|database guide]]),
12.10 a quota group directory must be initialised with a `limits` file indicating
12.11 the amount of time that can be occupied by the cumulative total of all events
12.12 scheduled by an individual user or a group of which they are a member. For
12.13 @@ -395,6 +396,35 @@
12.14 particular user will be unable to reserve the resource unless defined as a
12.15 member of a group listed in the `limits` file, as described below.
12.16
12.17 +{{{{#!wiki tip
12.18 +In a deployment using the [[../FileStore|file store]], files as described
12.19 +above and below hold mappings and definitions in the given format. In a
12.20 +deployment using the [[../DatabaseStore|database store]], database tables
12.21 +hold such mappings with each column dedicated to a particular kind of
12.22 +information.
12.23 +
12.24 +The examples here can be transcribed by just taking each
12.25 +element and putting it in the appropriate column within a table, making
12.26 +sure to set the `quota` column to indicate which quota is involved. For
12.27 +example, to set the above limits in PostgreSQL, the following operations
12.28 +may be used:
12.29 +
12.30 +{{{
12.31 +insert into quota_limits (quota, user_group, quota_limit) values (
12.32 + 'mailto:resource-car-cadillac@example.com',
12.33 + 'mailto:vincent.vole@example.com',
12.34 + 'PT10H');
12.35 +insert into quota_limits (quota, user_group, quota_limit) values (
12.36 + 'mailto:resource-car-cadillac@example.com',
12.37 + '*',
12.38 + 'PT10H');
12.39 +}}}
12.40 +
12.41 +Here, the `quota` column is set to `mailto:resource-car-cadillac@example.com`.
12.42 +In a file-based journal, the equivalent `limits` file would be placed within a
12.43 +quota directory having the name `mailto:resource-car-cadillac@example.com`.
12.44 +}}}}
12.45 +
12.46 ==== Sharing Quotas Across Users ====
12.47
12.48 When the use of resources is to be shared between users in such a way that
13.1 --- a/docs/wiki/Testing Tue Apr 19 21:20:57 2016 +0200
13.2 +++ b/docs/wiki/Testing Fri Apr 22 16:22:58 2016 +0200
13.3 @@ -90,6 +90,27 @@
13.4 are presented with message content, and testing for the desired effects of
13.5 running those programs with such content.
13.6
13.7 +{{{
13.8 +./test_all.sh
13.9 +}}}
13.10 +
13.11 +The test suite by default (or by indicating `file` as the data store type),
13.12 +records test information in subdirectories of `/tmp`.
13.13 +
13.14 +To run all tests against a different data store, such as a
13.15 +[[../DatabaseStore|database store]] instead of the [[../FileStore|file store]],
13.16 +the `STORE_TYPE` environment variable can be specified as in the following
13.17 +example:
13.18 +
13.19 +{{{
13.20 +STORE_TYPE=postgresql ./test_all.sh
13.21 +}}}
13.22 +
13.23 +The test suite records `postgresql` tests in a specially-created database
13.24 +called `imip_agent_test` that the system user running the tests must be allowed
13.25 +to create and drop. This may require the granting of administrative rights
13.26 +within PostgreSQL for the system user concerned.
13.27 +
13.28 Individual tests may also be run directly from the topmost level of the
13.29 source code distribution. For example:
13.30
13.31 @@ -104,3 +125,50 @@
13.32 output from various commands from the last test script invocation; the
13.33 `err.tmp` will contain tracebacks indicating serious error conditions,
13.34 should any have occurred.
13.35 +
13.36 +== Testing in the Deployment Environment ==
13.37 +
13.38 +Although the above testing may indicate that the software is functional,
13.39 +it does not demonstrate that the software has been successfully integrated
13.40 +into the deployment environment. One elementary test involves sending mail
13.41 +to an address that should be configured to handle incoming calendar messages.
13.42 +
13.43 +A basic script is provided that replicates a subset of the functionality in
13.44 +the traditional `mail` command for sending messages. It is invoked by
13.45 +specifying the sender and recipients of a message and by passing the message
13.46 +itself to the script's standard input. For example:
13.47 +
13.48 +{{{
13.49 +tools/sendmail.py paul.boddie@example.com resource-room-confroom@example.com \
13.50 + < tests/templates/event-request.txt
13.51 +}}}
13.52 +
13.53 +Here, the sender (`paul.boddie@example.com`) and recipient
13.54 +(`resource-room-confroom@example.com`) must match the identities specified
13.55 +within the supplied file (`tests/templates/event-request.txt`), and for a
13.56 +specific deployment environment these identities will need to be changed.
13.57 +The following lines from the supplied file would be involved:
13.58 +
13.59 +{{{
13.60 +From: paul.boddie@example.com
13.61 +To: resource-room-confroom@example.com
13.62 +ORGANIZER:mailto:paul.boddie@example.com
13.63 +ATTENDEE;ROLE=CHAIR:mailto:paul.boddie@example.com
13.64 +ATTENDEE;RSVP=TRUE:mailto:resource-room-confroom@example.com
13.65 +}}}
13.66 +
13.67 +It makes most sense to choose a recipient acting as a resource so that an
13.68 +automated response may be generated with the sender receiving this response.
13.69 +However, other kinds of recipients may also be tested in this way.
13.70 +
13.71 +The result of this invocation will become known via the following sources of
13.72 +information:
13.73 +
13.74 + * The sender's mailbox (if the recipient sends an automated response)
13.75 + * The sender's data store
13.76 + * The recipient's mailbox (if the recipient is configured to store mail)
13.77 + * The recipient's data store
13.78 + * Mail system logs (particularly in case of errors)
13.79 +
13.80 +See the [[../MailIntegration|mail integration guide]] for more information
13.81 +about configuring and troubleshooting mail systems.
14.1 --- a/imiptools/__init__.py Tue Apr 19 21:20:57 2016 +0200
14.2 +++ b/imiptools/__init__.py Fri Apr 22 16:22:58 2016 +0200
14.3 @@ -25,7 +25,7 @@
14.4 from imiptools.content import handle_itip_part
14.5 from imiptools.data import get_address, get_addresses, get_uri
14.6 from imiptools.mail import Messenger
14.7 -import imiptools.stores.file
14.8 +from imiptools.stores import get_store, get_publisher, get_journal
14.9 import sys, os
14.10
14.11 # Postfix exit codes.
14.12 @@ -56,6 +56,7 @@
14.13 self.outgoing_only = outgoing_only
14.14 self.messenger = None
14.15 self.lmtp_socket = None
14.16 + self.store_type = None
14.17 self.store_dir = None
14.18 self.publishing_dir = None
14.19 self.journal_dir = None
14.20 @@ -63,13 +64,13 @@
14.21 self.debug = False
14.22
14.23 def get_store(self):
14.24 - return imiptools.stores.file.FileStore(self.store_dir)
14.25 + return get_store(self.store_type, self.store_dir)
14.26
14.27 def get_publisher(self):
14.28 - return self.publishing_dir and imiptools.stores.file.FilePublisher(self.publishing_dir) or None
14.29 + return self.publishing_dir and get_publisher(self.publishing_dir) or None
14.30
14.31 def get_journal(self):
14.32 - return imiptools.stores.file.FileJournal(self.journal_dir)
14.33 + return get_journal(self.store_type, self.journal_dir)
14.34
14.35 def process(self, f, original_recipients):
14.36
14.37 @@ -122,6 +123,7 @@
14.38 recipients = []
14.39 senders = []
14.40 lmtp = []
14.41 + store_type = []
14.42 store_dir = []
14.43 publishing_dir = []
14.44 preferences_dir = []
14.45 @@ -152,6 +154,11 @@
14.46 elif arg == "-L":
14.47 local_smtp = True
14.48
14.49 + # Switch to getting the store type.
14.50 +
14.51 + elif arg == "-T":
14.52 + l = store_type
14.53 +
14.54 # Switch to getting the store directory.
14.55
14.56 elif arg == "-S":
14.57 @@ -179,11 +186,14 @@
14.58 else:
14.59 l.append(arg)
14.60
14.61 - self.messenger = Messenger(lmtp_socket=lmtp and lmtp[0] or None, local_smtp=local_smtp, sender=senders and senders[0] or None)
14.62 - self.store_dir = store_dir and store_dir[0] or None
14.63 - self.publishing_dir = publishing_dir and publishing_dir[0] or None
14.64 - self.preferences_dir = preferences_dir and preferences_dir[0] or None
14.65 - self.journal_dir = journal_dir and journal_dir[0] or None
14.66 + getvalue = lambda value, default=None: value and value[0] or default
14.67 +
14.68 + self.messenger = Messenger(lmtp_socket=getvalue(lmtp), local_smtp=local_smtp, sender=getvalue(senders))
14.69 + self.store_type = getvalue(store_type, config.STORE_TYPE)
14.70 + self.store_dir = getvalue(store_dir)
14.71 + self.publishing_dir = getvalue(publishing_dir)
14.72 + self.preferences_dir = getvalue(preferences_dir)
14.73 + self.journal_dir = getvalue(journal_dir)
14.74 self.process(stream, original_recipients)
14.75
14.76 def __call__(self):
14.77 @@ -198,6 +208,7 @@
14.78 if "--help" in args:
14.79 print >>sys.stderr, """\
14.80 Usage: %s [ -o <recipient> ... ] [-s <sender> ... ] [ -l <socket> | -L ] \\
14.81 + [ -T <store type ] \\
14.82 [ -S <store directory> ] [ -P <publishing directory> ] \\
14.83 [ -p <preferences directory> ] [ -j <journal directory> ] [ -d ]
14.84
14.85 @@ -225,6 +236,7 @@
14.86 -p Indicates the location of user preference directories
14.87 -S Indicates the location of the calendar data store containing user storage
14.88 directories
14.89 +-T Indicates the store and journal type (the configured value if omitted)
14.90
14.91 Output options:
14.92
15.1 --- a/imiptools/client.py Tue Apr 19 21:20:57 2016 +0200
15.2 +++ b/imiptools/client.py Fri Apr 22 16:22:58 2016 +0200
15.3 @@ -27,11 +27,8 @@
15.4 from imiptools.dates import check_permitted_values, format_datetime, get_default_timezone, \
15.5 get_duration, get_timestamp
15.6 from imiptools.i18n import get_translator
15.7 -from imiptools.period import can_schedule, remove_event_periods, \
15.8 - remove_additional_periods, remove_affected_period, \
15.9 - update_freebusy
15.10 from imiptools.profile import Preferences
15.11 -import imiptools.stores.file
15.12 +from imiptools.stores import get_store, get_publisher, get_journal
15.13
15.14 class Client:
15.15
15.16 @@ -51,11 +48,11 @@
15.17
15.18 self.user = user
15.19 self.messenger = messenger
15.20 - self.store = store or imiptools.stores.file.FileStore()
15.21 - self.journal = journal or imiptools.stores.file.FileJournal()
15.22 + self.store = store or get_store(config.STORE_TYPE, config.STORE_DIR)
15.23 + self.journal = journal or get_journal(config.STORE_TYPE, config.JOURNAL_DIR)
15.24
15.25 try:
15.26 - self.publisher = publisher or imiptools.stores.file.FilePublisher()
15.27 + self.publisher = publisher or get_publisher(config.PUBLISH_DIR)
15.28 except OSError:
15.29 self.publisher = None
15.30
15.31 @@ -288,7 +285,7 @@
15.32 offer.
15.33 """
15.34
15.35 - update_freebusy(freebusy, periods, transp, uid, recurrenceid, summary, organiser, expires)
15.36 + freebusy.update_freebusy(periods, transp, uid, recurrenceid, summary, organiser, expires)
15.37
15.38 # Preparation of messages communicating the state of events.
15.39
15.40 @@ -388,6 +385,13 @@
15.41
15.42 return get_uri(self.obj.get_value("ORGANIZER")) == self.user
15.43
15.44 + def is_recurrence(self):
15.45 +
15.46 + "Return whether the current object is a recurrence of its parent."
15.47 +
15.48 + parent = self.get_parent_object()
15.49 + return parent and parent.has_recurrence(self.get_tzid(), self.obj.get_recurrenceid())
15.50 +
15.51 # Common operations on calendar data.
15.52
15.53 def update_senders(self, obj=None):
15.54 @@ -626,7 +630,7 @@
15.55 # this point, so we update our copy for serialisation as the bundled
15.56 # free/busy object.
15.57
15.58 - freebusy = self.store.get_freebusy(self.user)
15.59 + freebusy = self.store.get_freebusy(self.user).copy()
15.60 self.update_freebusy(freebusy, self.user, from_organiser)
15.61
15.62 # Bundle free/busy information if appropriate.
15.63 @@ -862,7 +866,7 @@
15.64 Indicate whether within 'freebusy' the given 'periods' can be scheduled.
15.65 """
15.66
15.67 - return can_schedule(freebusy, periods, self.uid, self.recurrenceid)
15.68 + return freebusy.can_schedule(periods, self.uid, self.recurrenceid)
15.69
15.70 def have_new_object(self, strict=True):
15.71
15.72 @@ -993,9 +997,9 @@
15.73
15.74 "Remove this event from the given 'freebusy' collection."
15.75
15.76 - removed = remove_event_periods(freebusy, self.uid, self.recurrenceid)
15.77 + removed = freebusy.remove_event_periods(self.uid, self.recurrenceid)
15.78 if not removed and self.recurrenceid:
15.79 - return remove_affected_period(freebusy, self.uid, self.get_recurrence_start_point(self.recurrenceid))
15.80 + return freebusy.remove_affected_period(self.uid, self.get_recurrence_start_point(self.recurrenceid))
15.81 else:
15.82 return removed
15.83
15.84 @@ -1011,18 +1015,18 @@
15.85
15.86 if self.recurrenceid:
15.87 recurrenceid = self.get_recurrence_start_point(self.recurrenceid)
15.88 - remove_affected_period(freebusy, self.uid, recurrenceid)
15.89 + freebusy.remove_affected_period(self.uid, recurrenceid)
15.90 else:
15.91 # Remove obsolete recurrence periods.
15.92
15.93 - remove_additional_periods(freebusy, self.uid, recurrenceids)
15.94 + freebusy.remove_additional_periods(self.uid, recurrenceids)
15.95
15.96 # Remove original periods affected by additional recurrences.
15.97
15.98 if recurrenceids:
15.99 for recurrenceid in recurrenceids:
15.100 recurrenceid = self.get_recurrence_start_point(recurrenceid)
15.101 - remove_affected_period(freebusy, self.uid, recurrenceid)
15.102 + freebusy.remove_affected_period(self.uid, recurrenceid)
15.103
15.104 def update_freebusy(self, freebusy, user, as_organiser, offer=False):
15.105
15.106 @@ -1137,7 +1141,7 @@
15.107
15.108 self.acquire_lock()
15.109 try:
15.110 - freebusy = self.store.get_freebusy_for_other(self.user, user)
15.111 + freebusy = self.store.get_freebusy_for_other_for_update(self.user, user)
15.112 fn(freebusy, user, for_organiser, True)
15.113
15.114 # Tidy up any obsolete recurrences.
15.115 @@ -1192,7 +1196,7 @@
15.116 organiser of an event if 'for_organiser' is set to a true value.
15.117 """
15.118
15.119 - freebusy = self.store.get_freebusy(self.user)
15.120 + freebusy = self.store.get_freebusy_for_update(self.user)
15.121
15.122 # Obtain the attendance attributes for this user, if available.
15.123
15.124 @@ -1219,7 +1223,7 @@
15.125
15.126 "Remove free/busy information when handling an object."
15.127
15.128 - freebusy = self.store.get_freebusy(self.user)
15.129 + freebusy = self.store.get_freebusy_for_update(self.user)
15.130
15.131 self.remove_from_freebusy(freebusy)
15.132 self.remove_freebusy_for_recurrences(freebusy)
15.133 @@ -1238,7 +1242,7 @@
15.134
15.135 "Update free/busy offers when handling an object."
15.136
15.137 - freebusy = self.store.get_freebusy_offers(self.user)
15.138 + freebusy = self.store.get_freebusy_offers_for_update(self.user)
15.139
15.140 # Obtain the attendance attributes for this user, if available.
15.141
15.142 @@ -1256,7 +1260,7 @@
15.143
15.144 "Remove free/busy offers when handling an object."
15.145
15.146 - freebusy = self.store.get_freebusy_offers(self.user)
15.147 + freebusy = self.store.get_freebusy_offers_for_update(self.user)
15.148
15.149 self.remove_from_freebusy(freebusy)
15.150 self.remove_freebusy_for_recurrences(freebusy)
16.1 --- a/imiptools/config.py Tue Apr 19 21:20:57 2016 +0200
16.2 +++ b/imiptools/config.py Fri Apr 22 16:22:58 2016 +0200
16.3 @@ -14,6 +14,10 @@
16.4
16.5 OUTGOING_PREFIX = "people-outgoing"
16.6
16.7 +# The store (and journal) type.
16.8 +
16.9 +STORE_TYPE = "file"
16.10 +
16.11 # The location of the stored calendar information.
16.12
16.13 STORE_DIR = "/var/lib/imip-agent/store"
17.1 --- a/imiptools/data.py Tue Apr 19 21:20:57 2016 +0200
17.2 +++ b/imiptools/data.py Fri Apr 22 16:22:58 2016 +0200
17.3 @@ -3,7 +3,7 @@
17.4 """
17.5 Interpretation of vCalendar content.
17.6
17.7 -Copyright (C) 2014, 2015 Paul Boddie <paul@boddie.org.uk>
17.8 +Copyright (C) 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk>
17.9
17.10 This program is free software; you can redistribute it and/or modify it under
17.11 the terms of the GNU General Public License as published by the Free Software
17.12 @@ -29,7 +29,7 @@
17.13 get_recurrence_start_point, \
17.14 get_time, get_tzid, to_datetime, to_timezone, \
17.15 to_utc_datetime
17.16 -from imiptools.period import FreeBusyPeriod, Period, RecurringPeriod, period_overlaps
17.17 +from imiptools.period import FreeBusyPeriod, Period, RecurringPeriod
17.18 from vCalendar import iterwrite, parse, ParseError, to_dict, to_node
17.19 from vRecurrence import get_parameters, get_rule
17.20 import email.utils
17.21 @@ -44,6 +44,25 @@
17.22 "Access to calendar structures."
17.23
17.24 def __init__(self, fragment):
17.25 +
17.26 + """
17.27 + Initialise the object with the given 'fragment'. This must be a
17.28 + dictionary mapping an object type (such as "VEVENT") to a tuple
17.29 + containing the object details and attributes, each being a dictionary
17.30 + itself.
17.31 +
17.32 + The result of parse_object can be processed to obtain a fragment by
17.33 + obtaining a collection of records for an object type. For example:
17.34 +
17.35 + l = parse_object(f, encoding, "VCALENDAR")
17.36 + events = l["VEVENT"]
17.37 + event = events[0]
17.38 +
17.39 + Then, the specific object must be presented as follows:
17.40 +
17.41 + object = Object({"VEVENT" : event})
17.42 + """
17.43 +
17.44 self.objtype, (self.details, self.attr) = fragment.items()[0]
17.45
17.46 def get_uid(self):
17.47 @@ -148,6 +167,9 @@
17.48 def to_part(self, method):
17.49 return to_part(method, [self.to_node()])
17.50
17.51 + def to_string(self):
17.52 + return to_string(self.to_node())
17.53 +
17.54 # Direct access to the structure.
17.55
17.56 def has_key(self, name):
17.57 @@ -216,7 +238,7 @@
17.58
17.59 return (dtstart, dtstart_attr), (dtend, dtend_attr)
17.60
17.61 - def get_periods(self, tzid, end=None):
17.62 + def get_periods(self, tzid, end=None, inclusive=False):
17.63
17.64 """
17.65 Return periods defined by this object, employing the given 'tzid' where
17.66 @@ -225,9 +247,34 @@
17.67
17.68 If 'end' is omitted, only explicit recurrences and recurrences from
17.69 explicitly-terminated rules will be returned.
17.70 +
17.71 + If 'inclusive' is set to a true value, any period occurring at the 'end'
17.72 + will be included.
17.73 + """
17.74 +
17.75 + return get_periods(self, tzid, end, inclusive)
17.76 +
17.77 + def has_period(self, tzid, period):
17.78 +
17.79 + """
17.80 + Return whether this object, employing the given 'tzid' where no time
17.81 + zone information is defined, has the given 'period'.
17.82 """
17.83
17.84 - return get_periods(self, tzid, end)
17.85 + return period in self.get_periods(tzid, period.get_start_point(), inclusive=True)
17.86 +
17.87 + def has_recurrence(self, tzid, recurrenceid):
17.88 +
17.89 + """
17.90 + Return whether this object, employing the given 'tzid' where no time
17.91 + zone information is defined, has the given 'recurrenceid'.
17.92 + """
17.93 +
17.94 + start_point = self.get_recurrence_start_point(recurrenceid, tzid)
17.95 + for p in self.get_periods(tzid, start_point, inclusive=True):
17.96 + if p.get_start_point() == start_point:
17.97 + return True
17.98 + return False
17.99
17.100 def get_active_periods(self, recurrenceids, tzid, end=None):
17.101
17.102 @@ -568,7 +615,7 @@
17.103 # Get a constrained view if start and end limits are specified.
17.104
17.105 if period:
17.106 - periods = period_overlaps(freebusy, period, True)
17.107 + periods = freebusy.period_overlaps(period, True)
17.108 else:
17.109 periods = freebusy
17.110
17.111 @@ -617,6 +664,19 @@
17.112
17.113 return None
17.114
17.115 +def parse_string(s, encoding, objtype=None):
17.116 +
17.117 + """
17.118 + Parse the iTIP content from 's' having the given 'encoding'. If 'objtype' is
17.119 + given, only objects of that type will be returned. Otherwise, the root of
17.120 + the content will be returned as a dictionary with a single key indicating
17.121 + the object type.
17.122 +
17.123 + Return None if the content was not readable or suitable.
17.124 + """
17.125 +
17.126 + return parse_object(StringIO(s), encoding, objtype)
17.127 +
17.128 def to_part(method, calendar):
17.129
17.130 """
17.131 @@ -636,8 +696,23 @@
17.132 out.close()
17.133
17.134 def to_stream(out, fragment, encoding="utf-8"):
17.135 +
17.136 + "Write to the 'out' stream the given 'fragment'."
17.137 +
17.138 iterwrite(out, encoding=encoding).append(fragment)
17.139
17.140 +def to_string(fragment, encoding="utf-8"):
17.141 +
17.142 + "Return a string encoding the given 'fragment'."
17.143 +
17.144 + out = StringIO()
17.145 + try:
17.146 + to_stream(out, fragment, encoding)
17.147 + return out.getvalue()
17.148 +
17.149 + finally:
17.150 + out.close()
17.151 +
17.152 # Structure access functions.
17.153
17.154 def get_items(d, name, all=True):
18.1 --- a/imiptools/handlers/common.py Tue Apr 19 21:20:57 2016 +0200
18.2 +++ b/imiptools/handlers/common.py Fri Apr 22 16:22:58 2016 +0200
18.3 @@ -3,7 +3,7 @@
18.4 """
18.5 Common handler functionality for different entities.
18.6
18.7 -Copyright (C) 2014, 2015 Paul Boddie <paul@boddie.org.uk>
18.8 +Copyright (C) 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk>
18.9
18.10 This program is free software; you can redistribute it and/or modify it under
18.11 the terms of the GNU General Public License as published by the Free Software
18.12 @@ -22,7 +22,7 @@
18.13 from imiptools.data import get_address, get_uri, make_freebusy, to_part, \
18.14 uri_dict
18.15 from imiptools.dates import format_datetime
18.16 -from imiptools.period import FreeBusyPeriod, Period, replace_overlapping
18.17 +from imiptools.period import FreeBusyPeriod, Period
18.18
18.19 class CommonFreebusy:
18.20
18.21 @@ -58,8 +58,8 @@
18.22 period = Period(dtstart, dtend, self.get_tzid())
18.23
18.24 for sender, sender_attr in senders:
18.25 - stored_freebusy = self.store.get_freebusy_for_other(self.user, sender)
18.26 - replace_overlapping(stored_freebusy, period, freebusy)
18.27 + stored_freebusy = self.store.get_freebusy_for_other_for_update(self.user, sender)
18.28 + stored_freebusy.replace_overlapping(period, freebusy)
18.29 self.store.set_freebusy_for_other(self.user, stored_freebusy, sender)
18.30
18.31 def request(self):
18.32 @@ -143,4 +143,50 @@
18.33
18.34 self.add_result("REFRESH", [get_address(organiser)], obj.to_part("REFRESH"))
18.35
18.36 + def is_newly_separated_occurrence(self):
18.37 +
18.38 + "Return whether the current object is a newly-separated occurrence."
18.39 +
18.40 + # Obtain any stored object.
18.41 +
18.42 + obj = self.get_stored_object_version()
18.43 +
18.44 + # Handle any newly-separated, valid occurrence.
18.45 +
18.46 + return not obj and self.is_recurrence()
18.47 +
18.48 + def make_separate_occurrence(self, for_organiser=False):
18.49 +
18.50 + """
18.51 + Set the current object as a separate occurrence and redefine free/busy
18.52 + records in terms of this new occurrence for other participants.
18.53 + """
18.54 +
18.55 + parent = self.get_parent_object()
18.56 + if not parent:
18.57 + return False
18.58 +
18.59 + # Transfer attendance information from the parent.
18.60 +
18.61 + parent_attendees = uri_dict(parent.get_value_map("ATTENDEE"))
18.62 + attendee_map = uri_dict(self.obj.get_value_map("ATTENDEE"))
18.63 +
18.64 + for attendee, attendee_attr in parent_attendees.items():
18.65 + if not attendee_map.has_key(attendee):
18.66 + attendee_map[attendee] = attendee_attr
18.67 +
18.68 + self.obj["ATTENDEE"] = attendee_map.items()
18.69 + self.obj.remove_all(["RDATE", "RRULE"])
18.70 +
18.71 + # Create or revive the occurrence.
18.72 +
18.73 + self.store.remove_cancellation(self.user, self.uid, self.recurrenceid)
18.74 + self.store.set_event(self.user, self.uid, self.recurrenceid, self.obj.to_node())
18.75 +
18.76 + # Update free/busy details for the current object for all attendees.
18.77 +
18.78 + self.update_freebusy_from_attendees(attendee_map.keys())
18.79 +
18.80 + return True
18.81 +
18.82 # vim: tabstop=4 expandtab shiftwidth=4
19.1 --- a/imiptools/handlers/person.py Tue Apr 19 21:20:57 2016 +0200
19.2 +++ b/imiptools/handlers/person.py Fri Apr 22 16:22:58 2016 +0200
19.3 @@ -3,7 +3,7 @@
19.4 """
19.5 Handlers for a person for whom scheduling is performed.
19.6
19.7 -Copyright (C) 2014, 2015 Paul Boddie <paul@boddie.org.uk>
19.8 +Copyright (C) 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk>
19.9
19.10 This program is free software; you can redistribute it and/or modify it under
19.11 the terms of the GNU General Public License as published by the Free Software
19.12 @@ -205,10 +205,35 @@
19.13
19.14 "As organiser, update attendance from valid attendees."
19.15
19.16 - if self.merge_attendance(attendees):
19.17 - self.update_freebusy_from_attendees(attendees)
19.18 + # Occurrences that are still part of a parent object are separated,
19.19 + # attendance information transferred, and the free/busy details updated.
19.20 +
19.21 + if self.is_newly_separated_occurrence():
19.22 + if self.make_separate_occurrence(for_organiser=True):
19.23 +
19.24 + # Update free/busy details for the event.
19.25 +
19.26 + self.update_event_in_freebusy(for_organiser=True)
19.27 +
19.28 + # Produce a REQUEST for the created occurrence for other
19.29 + # attendees of the parent event.
19.30
19.31 - return True
19.32 + obj = self.get_parent_object()
19.33 + stored_attendees = set(obj.get_values("ATTENDEE"))
19.34 + attendees = stored_attendees.difference(attendees)
19.35 +
19.36 + for attendee in attendees:
19.37 + methods, parts = self.get_message_parts(self.obj, "REQUEST", attendee)
19.38 + self.add_results(methods, [get_address(attendee)], parts)
19.39 +
19.40 + return True
19.41 +
19.42 + # Merge the attendance for the received object.
19.43 +
19.44 + elif self.merge_attendance(attendees):
19.45 + return self.update_freebusy_from_attendees(attendees)
19.46 +
19.47 + return False
19.48
19.49 def _refresh(self, organiser, attendees):
19.50
20.1 --- a/imiptools/handlers/person_outgoing.py Tue Apr 19 21:20:57 2016 +0200
20.2 +++ b/imiptools/handlers/person_outgoing.py Fri Apr 22 16:22:58 2016 +0200
20.3 @@ -4,7 +4,7 @@
20.4 Handlers for a person for whom scheduling is performed, inspecting outgoing
20.5 messages to obtain scheduling done externally.
20.6
20.7 -Copyright (C) 2014, 2015 Paul Boddie <paul@boddie.org.uk>
20.8 +Copyright (C) 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk>
20.9
20.10 This program is free software; you can redistribute it and/or modify it under
20.11 the terms of the GNU General Public License as published by the Free Software
20.12 @@ -120,11 +120,19 @@
20.13 self.store.remove_cancellation(self.user, self.uid, self.recurrenceid)
20.14
20.15 else:
20.16 + # Occurrences that are still part of a parent object are separated,
20.17 + # attendance information transferred, and the free/busy details
20.18 + # updated.
20.19 +
20.20 + if self.is_newly_separated_occurrence():
20.21 + self.make_separate_occurrence(for_organiser=not from_organiser)
20.22 +
20.23 # Obtain valid attendees, merging their attendance with the stored
20.24 # object.
20.25
20.26 - attendees = self.require_attendees(from_organiser)
20.27 - self.merge_attendance(attendees)
20.28 + else:
20.29 + attendees = self.require_attendees(from_organiser)
20.30 + self.merge_attendance(attendees)
20.31
20.32 # Remove any associated request.
20.33
21.1 --- a/imiptools/handlers/scheduling/freebusy.py Tue Apr 19 21:20:57 2016 +0200
21.2 +++ b/imiptools/handlers/scheduling/freebusy.py Fri Apr 22 16:22:58 2016 +0200
21.3 @@ -21,8 +21,6 @@
21.4
21.5 from imiptools.data import uri_values
21.6 from imiptools.dates import ValidityError, to_timezone
21.7 -from imiptools.period import coalesce_freebusy, invert_freebusy, \
21.8 - periods_from, remove_periods
21.9
21.10 def schedule_in_freebusy(handler, args, freebusy=None):
21.11
21.12 @@ -105,29 +103,34 @@
21.13 # There should already be free/busy information for the user.
21.14
21.15 user_freebusy = handler.get_store().get_freebusy(handler.user)
21.16 - busy = user_freebusy
21.17 +
21.18 + # Maintain a separate copy of the data.
21.19 +
21.20 + busy = user_freebusy.copy()
21.21
21.22 # Subtract any periods from this event from the free/busy collections.
21.23
21.24 - event_periods = handler.remove_from_freebusy(user_freebusy)
21.25 + event_periods = handler.remove_from_freebusy(busy)
21.26
21.27 # Find busy periods for the other attendees.
21.28
21.29 for attendee in uri_values(handler.obj.get_values("ATTENDEE")):
21.30 if attendee != handler.user:
21.31 - freebusy = handler.get_store().get_freebusy_for_other(handler.user, attendee)
21.32 +
21.33 + # Get a copy of the attendee's free/busy data.
21.34 +
21.35 + freebusy = handler.get_store().get_freebusy_for_other(handler.user, attendee).copy()
21.36 if freebusy:
21.37 - remove_periods(freebusy, event_periods)
21.38 + freebusy.remove_periods(event_periods)
21.39 busy += freebusy
21.40
21.41 # Obtain the combined busy periods.
21.42
21.43 - busy.sort()
21.44 - busy = coalesce_freebusy(busy)
21.45 + busy = busy.coalesce_freebusy()
21.46
21.47 # Obtain free periods.
21.48
21.49 - free = invert_freebusy(busy)
21.50 + free = busy.invert_freebusy()
21.51 permitted_values = handler.get_permitted_values()
21.52 periods = []
21.53
21.54 @@ -153,7 +156,7 @@
21.55
21.56 # Get free periods from the time of each period.
21.57
21.58 - for found in periods_from(free, period):
21.59 + for found in free.periods_from(period):
21.60
21.61 # Skip any periods before the last period.
21.62
22.1 --- a/imiptools/handlers/scheduling/quota.py Tue Apr 19 21:20:57 2016 +0200
22.2 +++ b/imiptools/handlers/scheduling/quota.py Fri Apr 22 16:22:58 2016 +0200
22.3 @@ -82,7 +82,7 @@
22.4 # Update the journal entries.
22.5
22.6 journal = handler.get_journal()
22.7 - entries = journal.get_entries(quota, group)
22.8 + entries = journal.get_entries_for_update(quota, group)
22.9 handler.update_freebusy(entries, group, False)
22.10 journal.set_entries(quota, group, entries)
22.11
22.12 @@ -105,7 +105,7 @@
22.13 # Update the journal entries.
22.14
22.15 journal = handler.get_journal()
22.16 - entries = journal.get_entries(quota, group)
22.17 + entries = journal.get_entries_for_update(quota, group)
22.18 handler.remove_from_freebusy(entries)
22.19 journal.set_entries(quota, group, entries)
22.20
22.21 @@ -209,7 +209,7 @@
22.22 quota, organiser = _get_quota_and_identity(handler, args)
22.23
22.24 journal = handler.get_journal()
22.25 - freebusy = journal.get_freebusy(quota, organiser)
22.26 + freebusy = journal.get_freebusy_for_update(quota, organiser)
22.27 handler.update_freebusy(freebusy, organiser, True)
22.28 journal.set_freebusy(quota, organiser, freebusy)
22.29
22.30 @@ -223,7 +223,7 @@
22.31 quota, organiser = _get_quota_and_identity(handler, args)
22.32
22.33 journal = handler.get_journal()
22.34 - freebusy = journal.get_freebusy(quota, organiser)
22.35 + freebusy = journal.get_freebusy_for_update(quota, organiser)
22.36 handler.remove_from_freebusy(freebusy)
22.37 journal.set_freebusy(quota, organiser, freebusy)
22.38
23.1 --- a/imiptools/period.py Tue Apr 19 21:20:57 2016 +0200
23.2 +++ b/imiptools/period.py Fri Apr 22 16:22:58 2016 +0200
23.3 @@ -28,11 +28,27 @@
23.4 get_start_of_day, \
23.5 get_tzid, \
23.6 to_timezone, to_utc_datetime
23.7 +from imiptools.sql import DatabaseOperations
23.8
23.9 def ifnone(x, y):
23.10 if x is None: return y
23.11 else: return x
23.12
23.13 +def from_strings(t, encoding):
23.14 + return tuple([from_string(s, encoding) for s in t])
23.15 +
23.16 +def from_string(s, encoding):
23.17 + if s:
23.18 + return unicode(s, encoding)
23.19 + else:
23.20 + return s
23.21 +
23.22 +def to_string(s, encoding):
23.23 + if s:
23.24 + return s.encode(encoding)
23.25 + else:
23.26 + return s
23.27 +
23.28 class Comparable:
23.29
23.30 "A date/datetime wrapper that allows comparisons with other types."
23.31 @@ -279,8 +295,13 @@
23.32 return None
23.33 d = get_recurrence_start(recurrenceid)
23.34 dt = get_recurrence_start_point(recurrenceid, self.tzid)
23.35 - if self.get_start() == d or self.get_start_point() == dt:
23.36 +
23.37 + # Compare the start to dates only, using the normalised start datetime
23.38 + # for comparisons with the start point.
23.39 +
23.40 + if not isinstance(d, datetime) and self.get_start() == d or self.get_start_point() == dt:
23.41 return recurrenceid
23.42 +
23.43 return None
23.44
23.45 # Value correction methods.
23.46 @@ -356,17 +377,19 @@
23.47 self.organiser = organiser or None
23.48 self.expires = expires or None
23.49
23.50 - def as_tuple(self, strings_only=False):
23.51 + def as_tuple(self, strings_only=False, string_datetimes=False):
23.52
23.53 """
23.54 - Return the initialisation parameter tuple, converting false value
23.55 - parameters to strings if 'strings_only' is set to a true value.
23.56 + Return the initialisation parameter tuple, converting datetimes and
23.57 + false value parameters to strings if 'strings_only' is set to a true
23.58 + value. Otherwise, if 'string_datetimes' is set to a true value, only the
23.59 + datetime values are converted to strings.
23.60 """
23.61
23.62 null = lambda x: (strings_only and [""] or [x])[0]
23.63 return (
23.64 - strings_only and format_datetime(self.get_start_point()) or self.start,
23.65 - strings_only and format_datetime(self.get_end_point()) or self.end,
23.66 + (strings_only or string_datetimes) and format_datetime(self.get_start_point()) or self.start,
23.67 + (strings_only or string_datetimes) and format_datetime(self.get_end_point()) or self.end,
23.68 self.uid or null(self.uid),
23.69 self.transp or strings_only and "OPAQUE" or None,
23.70 self.recurrenceid or null(self.recurrenceid),
23.71 @@ -454,280 +477,675 @@
23.72 def make_corrected(self, start, end):
23.73 return self.__class__(start, end, self.tzid, self.origin, self.get_start_attr(), self.get_end_attr())
23.74
23.75 -# Time and period management.
23.76 +class FreeBusyCollectionBase:
23.77 +
23.78 + "Common operations on free/busy period collections."
23.79 +
23.80 + def __init__(self, mutable=True):
23.81 + self.mutable = mutable
23.82 +
23.83 + def _check_mutable(self):
23.84 + if not self.mutable:
23.85 + raise TypeError, "Cannot mutate this collection."
23.86 +
23.87 + def copy(self):
23.88 +
23.89 + "Make an independent mutable copy of the collection."
23.90
23.91 -def can_schedule(freebusy, periods, uid, recurrenceid):
23.92 + return FreeBusyCollection(list(self), True)
23.93 +
23.94 + # List emulation methods.
23.95 +
23.96 + def __iadd__(self, periods):
23.97 + for period in periods:
23.98 + self.insert_period(period)
23.99 + return self
23.100 +
23.101 + def append(self, period):
23.102 + self.insert_period(period)
23.103 +
23.104 + # Operations.
23.105 +
23.106 + def can_schedule(self, periods, uid, recurrenceid):
23.107
23.108 - """
23.109 - Return whether the 'freebusy' list can accommodate the given 'periods'
23.110 - employing the specified 'uid' and 'recurrenceid'.
23.111 - """
23.112 + """
23.113 + Return whether the collection can accommodate the given 'periods'
23.114 + employing the specified 'uid' and 'recurrenceid'.
23.115 + """
23.116 +
23.117 + for conflict in self.have_conflict(periods, True):
23.118 + if conflict.uid != uid or conflict.recurrenceid != recurrenceid:
23.119 + return False
23.120 +
23.121 + return True
23.122 +
23.123 + def have_conflict(self, periods, get_conflicts=False):
23.124
23.125 - for conflict in have_conflict(freebusy, periods, True):
23.126 - if conflict.uid != uid or conflict.recurrenceid != recurrenceid:
23.127 + """
23.128 + Return whether any period in the collection overlaps with the given
23.129 + 'periods', returning a collection of such overlapping periods if
23.130 + 'get_conflicts' is set to a true value.
23.131 + """
23.132 +
23.133 + conflicts = set()
23.134 + for p in periods:
23.135 + overlapping = self.period_overlaps(p, get_conflicts)
23.136 + if overlapping:
23.137 + if get_conflicts:
23.138 + conflicts.update(overlapping)
23.139 + else:
23.140 + return True
23.141 +
23.142 + if get_conflicts:
23.143 + return conflicts
23.144 + else:
23.145 return False
23.146
23.147 - return True
23.148 + def period_overlaps(self, period, get_periods=False):
23.149 +
23.150 + """
23.151 + Return whether any period in the collection overlaps with the given
23.152 + 'period', returning a collection of overlapping periods if 'get_periods'
23.153 + is set to a true value.
23.154 + """
23.155 +
23.156 + overlapping = self.get_overlapping(period)
23.157 +
23.158 + if get_periods:
23.159 + return overlapping
23.160 + else:
23.161 + return len(overlapping) != 0
23.162 +
23.163 + def replace_overlapping(self, period, replacements):
23.164 +
23.165 + """
23.166 + Replace existing periods in the collection within the given 'period',
23.167 + using the given 'replacements'.
23.168 + """
23.169 +
23.170 + self._check_mutable()
23.171 +
23.172 + self.remove_overlapping(period)
23.173 + for replacement in replacements:
23.174 + self.insert_period(replacement)
23.175 +
23.176 + def coalesce_freebusy(self):
23.177 +
23.178 + "Coalesce the periods in the collection, returning a new collection."
23.179 +
23.180 + if not self:
23.181 + return FreeBusyCollection()
23.182 +
23.183 + fb = []
23.184 +
23.185 + it = iter(self)
23.186 + period = it.next()
23.187 +
23.188 + start = period.get_start_point()
23.189 + end = period.get_end_point()
23.190 +
23.191 + try:
23.192 + while True:
23.193 + period = it.next()
23.194 + if period.get_start_point() > end:
23.195 + fb.append(FreeBusyPeriod(start, end))
23.196 + start = period.get_start_point()
23.197 + end = period.get_end_point()
23.198 + else:
23.199 + end = max(end, period.get_end_point())
23.200 + except StopIteration:
23.201 + pass
23.202 +
23.203 + fb.append(FreeBusyPeriod(start, end))
23.204 + return FreeBusyCollection(fb)
23.205 +
23.206 + def invert_freebusy(self):
23.207 +
23.208 + "Return the free periods from the collection as a new collection."
23.209 +
23.210 + if not self:
23.211 + return FreeBusyCollection([FreeBusyPeriod(None, None)])
23.212 +
23.213 + # Coalesce periods that overlap or are adjacent.
23.214 +
23.215 + fb = self.coalesce_freebusy()
23.216 + free = []
23.217 +
23.218 + # Add a start-of-time period if appropriate.
23.219 +
23.220 + first = fb[0].get_start_point()
23.221 + if first:
23.222 + free.append(FreeBusyPeriod(None, first))
23.223 +
23.224 + start = fb[0].get_end_point()
23.225 +
23.226 + for period in fb[1:]:
23.227 + free.append(FreeBusyPeriod(start, period.get_start_point()))
23.228 + start = period.get_end_point()
23.229 +
23.230 + # Add an end-of-time period if appropriate.
23.231 +
23.232 + if start:
23.233 + free.append(FreeBusyPeriod(start, None))
23.234 +
23.235 + return FreeBusyCollection(free)
23.236 +
23.237 + def update_freebusy(self, periods, transp, uid, recurrenceid, summary, organiser, expires=None):
23.238 +
23.239 + """
23.240 + Update the free/busy details with the given 'periods', 'transp' setting,
23.241 + 'uid' plus 'recurrenceid' and 'summary' and 'organiser' details.
23.242 +
23.243 + An optional 'expires' datetime string indicates the expiry time of any
23.244 + free/busy offer.
23.245 + """
23.246 +
23.247 + self._check_mutable()
23.248 +
23.249 + self.remove_event_periods(uid, recurrenceid)
23.250 +
23.251 + for p in periods:
23.252 + self.insert_period(FreeBusyPeriod(p.get_start_point(), p.get_end_point(), uid, transp, recurrenceid, summary, organiser, expires))
23.253 +
23.254 +class FreeBusyCollection(FreeBusyCollectionBase):
23.255 +
23.256 + "An abstraction for a collection of free/busy periods."
23.257 +
23.258 + def __init__(self, periods=None, mutable=True):
23.259 +
23.260 + """
23.261 + Initialise the collection with the given list of 'periods', or start an
23.262 + empty collection if no list is given.
23.263 + """
23.264 +
23.265 + FreeBusyCollectionBase.__init__(self, mutable)
23.266 + self.periods = periods or []
23.267 +
23.268 + # List emulation methods.
23.269 +
23.270 + def __nonzero__(self):
23.271 + return bool(self.periods)
23.272 +
23.273 + def __iter__(self):
23.274 + return iter(self.periods)
23.275 +
23.276 + def __len__(self):
23.277 + return len(self.periods)
23.278 +
23.279 + def __getitem__(self, i):
23.280 + return self.periods[i]
23.281 +
23.282 + # Operations.
23.283 +
23.284 + def insert_period(self, period):
23.285 +
23.286 + "Insert the given 'period' into the collection."
23.287 +
23.288 + self._check_mutable()
23.289 +
23.290 + i = bisect_left(self.periods, period)
23.291 + if i == len(self.periods):
23.292 + self.periods.append(period)
23.293 + elif self.periods[i] != period:
23.294 + self.periods.insert(i, period)
23.295 +
23.296 + def remove_periods(self, periods):
23.297 +
23.298 + "Remove the given 'periods' from the collection."
23.299 +
23.300 + self._check_mutable()
23.301
23.302 -def have_conflict(freebusy, periods, get_conflicts=False):
23.303 + for period in periods:
23.304 + i = bisect_left(self.periods, period)
23.305 + if i < len(self.periods) and self.periods[i] == period:
23.306 + del self.periods[i]
23.307 +
23.308 + def remove_event_periods(self, uid, recurrenceid=None):
23.309 +
23.310 + """
23.311 + Remove from the collection all periods associated with 'uid' and
23.312 + 'recurrenceid' (which if omitted causes the "parent" object's periods to
23.313 + be referenced).
23.314 +
23.315 + Return the removed periods.
23.316 + """
23.317 +
23.318 + self._check_mutable()
23.319 +
23.320 + removed = []
23.321 + i = 0
23.322 + while i < len(self.periods):
23.323 + fb = self.periods[i]
23.324 + if fb.uid == uid and fb.recurrenceid == recurrenceid:
23.325 + removed.append(self.periods[i])
23.326 + del self.periods[i]
23.327 + else:
23.328 + i += 1
23.329 +
23.330 + return removed
23.331 +
23.332 + def remove_additional_periods(self, uid, recurrenceids=None):
23.333 +
23.334 + """
23.335 + Remove from the collection all periods associated with 'uid' having a
23.336 + recurrence identifier indicating an additional or modified period.
23.337 +
23.338 + If 'recurrenceids' is specified, remove all periods associated with
23.339 + 'uid' that do not have a recurrence identifier in the given list.
23.340 +
23.341 + Return the removed periods.
23.342 + """
23.343 +
23.344 + self._check_mutable()
23.345 +
23.346 + removed = []
23.347 + i = 0
23.348 + while i < len(self.periods):
23.349 + fb = self.periods[i]
23.350 + if fb.uid == uid and fb.recurrenceid and (
23.351 + recurrenceids is None or
23.352 + recurrenceids is not None and fb.recurrenceid not in recurrenceids
23.353 + ):
23.354 + removed.append(self.periods[i])
23.355 + del self.periods[i]
23.356 + else:
23.357 + i += 1
23.358 +
23.359 + return removed
23.360 +
23.361 + def remove_affected_period(self, uid, start):
23.362 +
23.363 + """
23.364 + Remove from the collection the period associated with 'uid' that
23.365 + provides an occurrence starting at the given 'start' (provided by a
23.366 + recurrence identifier, converted to a datetime). A recurrence identifier
23.367 + is used to provide an alternative time period whilst also acting as a
23.368 + reference to the originally-defined occurrence.
23.369 +
23.370 + Return any removed period in a list.
23.371 + """
23.372 +
23.373 + self._check_mutable()
23.374 +
23.375 + removed = []
23.376 +
23.377 + search = Period(start, start)
23.378 + found = bisect_left(self.periods, search)
23.379 +
23.380 + while found < len(self.periods):
23.381 + fb = self.periods[found]
23.382 +
23.383 + # Stop looking if the start no longer matches the recurrence identifier.
23.384 +
23.385 + if fb.get_start_point() != search.get_start_point():
23.386 + break
23.387 +
23.388 + # If the period belongs to the parent object, remove it and return.
23.389 +
23.390 + if not fb.recurrenceid and uid == fb.uid:
23.391 + removed.append(self.periods[found])
23.392 + del self.periods[found]
23.393 + break
23.394 +
23.395 + # Otherwise, keep looking for a matching period.
23.396 +
23.397 + found += 1
23.398 +
23.399 + return removed
23.400 +
23.401 + def periods_from(self, period):
23.402 +
23.403 + "Return the entries in the collection at or after 'period'."
23.404 +
23.405 + first = bisect_left(self.periods, period)
23.406 + return self.periods[first:]
23.407 +
23.408 + def periods_until(self, period):
23.409 +
23.410 + "Return the entries in the collection before 'period'."
23.411 +
23.412 + last = bisect_right(self.periods, Period(period.get_end(), period.get_end(), period.get_tzid()))
23.413 + return self.periods[:last]
23.414 +
23.415 + def get_overlapping(self, period):
23.416 +
23.417 + """
23.418 + Return the entries in the collection providing periods overlapping with
23.419 + 'period'.
23.420 + """
23.421 +
23.422 + # Find the range of periods potentially overlapping the period in the
23.423 + # free/busy collection.
23.424 +
23.425 + startpoints = self.periods_until(period)
23.426 +
23.427 + # Find the range of periods potentially overlapping the period in a version
23.428 + # of the free/busy collection sorted according to end datetimes.
23.429 +
23.430 + endpoints = [(Period(fb.get_end_point(), fb.get_end_point()), fb) for fb in startpoints]
23.431 + endpoints.sort()
23.432 + first = bisect_left(endpoints, (Period(period.get_start_point(), period.get_start_point()),))
23.433 + endpoints = endpoints[first:]
23.434 +
23.435 + overlapping = set()
23.436 +
23.437 + for p, fb in endpoints:
23.438 + if fb.overlaps(period):
23.439 + overlapping.add(fb)
23.440 +
23.441 + overlapping = list(overlapping)
23.442 + overlapping.sort()
23.443 + return overlapping
23.444 +
23.445 + def remove_overlapping(self, period):
23.446 +
23.447 + "Remove all periods overlapping with 'period' from the collection."
23.448 +
23.449 + self._check_mutable()
23.450 +
23.451 + overlapping = self.get_overlapping(period)
23.452 +
23.453 + if overlapping:
23.454 + for fb in overlapping:
23.455 + self.periods.remove(fb)
23.456 +
23.457 +class FreeBusyDatabaseCollection(FreeBusyCollectionBase, DatabaseOperations):
23.458
23.459 """
23.460 - Return whether any period in 'freebusy' overlaps with the given 'periods',
23.461 - returning a collection of such overlapping periods if 'get_conflicts' is
23.462 - set to a true value.
23.463 - """
23.464 -
23.465 - conflicts = set()
23.466 - for p in periods:
23.467 - overlapping = period_overlaps(freebusy, p, get_conflicts)
23.468 - if overlapping:
23.469 - if get_conflicts:
23.470 - conflicts.update(overlapping)
23.471 - else:
23.472 - return True
23.473 -
23.474 - if get_conflicts:
23.475 - return conflicts
23.476 - else:
23.477 - return False
23.478 -
23.479 -def insert_period(freebusy, period):
23.480 -
23.481 - "Insert into 'freebusy' the given 'period'."
23.482 -
23.483 - i = bisect_left(freebusy, period)
23.484 - if i == len(freebusy):
23.485 - freebusy.append(period)
23.486 - elif freebusy[i] != period:
23.487 - freebusy.insert(i, period)
23.488 -
23.489 -def remove_periods(freebusy, periods):
23.490 -
23.491 - "Remove from 'freebusy' the given 'periods'."
23.492 -
23.493 - for period in periods:
23.494 - i = bisect_left(freebusy, period)
23.495 - if i < len(freebusy) and freebusy[i] == period:
23.496 - del freebusy[i]
23.497 -
23.498 -def remove_event_periods(freebusy, uid, recurrenceid=None):
23.499 -
23.500 - """
23.501 - Remove from 'freebusy' all periods associated with 'uid' and 'recurrenceid'
23.502 - (which if omitted causes the "parent" object's periods to be referenced).
23.503 -
23.504 - Return the removed periods.
23.505 - """
23.506 -
23.507 - removed = []
23.508 - i = 0
23.509 - while i < len(freebusy):
23.510 - fb = freebusy[i]
23.511 - if fb.uid == uid and fb.recurrenceid == recurrenceid:
23.512 - removed.append(freebusy[i])
23.513 - del freebusy[i]
23.514 - else:
23.515 - i += 1
23.516 -
23.517 - return removed
23.518 -
23.519 -def remove_additional_periods(freebusy, uid, recurrenceids=None):
23.520 -
23.521 - """
23.522 - Remove from 'freebusy' all periods associated with 'uid' having a
23.523 - recurrence identifier indicating an additional or modified period.
23.524 -
23.525 - If 'recurrenceids' is specified, remove all periods associated with 'uid'
23.526 - that do not have a recurrence identifier in the given list.
23.527 -
23.528 - Return the removed periods.
23.529 - """
23.530 -
23.531 - removed = []
23.532 - i = 0
23.533 - while i < len(freebusy):
23.534 - fb = freebusy[i]
23.535 - if fb.uid == uid and fb.recurrenceid and (
23.536 - recurrenceids is None or
23.537 - recurrenceids is not None and fb.recurrenceid not in recurrenceids
23.538 - ):
23.539 - removed.append(freebusy[i])
23.540 - del freebusy[i]
23.541 - else:
23.542 - i += 1
23.543 -
23.544 - return removed
23.545 -
23.546 -def remove_affected_period(freebusy, uid, start):
23.547 -
23.548 - """
23.549 - Remove from 'freebusy' a period associated with 'uid' that provides an
23.550 - occurrence starting at the given 'start' (provided by a recurrence
23.551 - identifier, converted to a datetime). A recurrence identifier is used to
23.552 - provide an alternative time period whilst also acting as a reference to the
23.553 - originally-defined occurrence.
23.554 -
23.555 - Return any removed period in a list.
23.556 + An abstraction for a collection of free/busy periods stored in a database
23.557 + system.
23.558 """
23.559
23.560 - removed = []
23.561 + period_columns = ["start", "end", "object_uid", "transp", "object_recurrenceid", "summary", "organiser", "expires"]
23.562 +
23.563 + def __init__(self, cursor, table_name, column_names=None, filter_values=None, mutable=True, paramstyle=None):
23.564
23.565 - search = Period(start, start)
23.566 - found = bisect_left(freebusy, search)
23.567 + """
23.568 + Initialise the collection with the given 'cursor' and with the
23.569 + 'table_name', 'column_names' and 'filter_values' configuring the
23.570 + selection of data.
23.571 + """
23.572 +
23.573 + FreeBusyCollectionBase.__init__(self, mutable)
23.574 + DatabaseOperations.__init__(self, column_names, filter_values, paramstyle)
23.575 + self.cursor = cursor
23.576 + self.table_name = table_name
23.577
23.578 - while found < len(freebusy):
23.579 - fb = freebusy[found]
23.580 + def make_period(self, t):
23.581 + return FreeBusyPeriod(*from_strings(t, "utf-8"))
23.582
23.583 - # Stop looking if the start no longer matches the recurrence identifier.
23.584 + # List emulation methods.
23.585 +
23.586 + def __nonzero__(self):
23.587 + return len(self) and True or False
23.588
23.589 - if fb.get_start_point() != search.get_start_point():
23.590 - break
23.591 -
23.592 - # If the period belongs to the parent object, remove it and return.
23.593 + def __iter__(self):
23.594 + query, values = self.get_query(
23.595 + "select %(columns)s from %(table)s :condition" % {
23.596 + "columns" : self.columnlist(self.period_columns),
23.597 + "table" : self.table_name
23.598 + })
23.599 + self.cursor.execute(query, values)
23.600 + return iter(map(lambda t: self.make_period(t), self.cursor.fetchall()))
23.601
23.602 - if not fb.recurrenceid and uid == fb.uid:
23.603 - removed.append(freebusy[found])
23.604 - del freebusy[found]
23.605 - break
23.606 + def __len__(self):
23.607 + query, values = self.get_query(
23.608 + "select count(*) from %(table)s :condition" % {
23.609 + "table" : self.table_name
23.610 + })
23.611 + self.cursor.execute(query, values)
23.612 + result = self.cursor.fetchone()
23.613 + return result and int(result[0]) or 0
23.614
23.615 - # Otherwise, keep looking for a matching period.
23.616 + def __getitem__(self, i):
23.617 + return list(iter(self))[i]
23.618
23.619 - found += 1
23.620 + # Operations.
23.621
23.622 - return removed
23.623 + def insert_period(self, period):
23.624 +
23.625 + "Insert the given 'period' into the collection."
23.626
23.627 -def periods_from(freebusy, period):
23.628 + self._check_mutable()
23.629 +
23.630 + columns, values = self.period_columns, period.as_tuple(string_datetimes=True)
23.631
23.632 - "Return the entries in 'freebusy' at or after 'period'."
23.633 + query, values = self.get_query(
23.634 + "insert into %(table)s (:columns) values (:values)" % {
23.635 + "table" : self.table_name
23.636 + },
23.637 + columns, [to_string(v, "utf-8") for v in values])
23.638
23.639 - first = bisect_left(freebusy, period)
23.640 - return freebusy[first:]
23.641 + self.cursor.execute(query, values)
23.642 +
23.643 + def remove_periods(self, periods):
23.644
23.645 -def periods_until(freebusy, period):
23.646 + "Remove the given 'periods' from the collection."
23.647
23.648 - "Return the entries in 'freebusy' before 'period'."
23.649 + self._check_mutable()
23.650 +
23.651 + for period in periods:
23.652 + values = period.as_tuple(string_datetimes=True)
23.653
23.654 - last = bisect_right(freebusy, Period(period.get_end(), period.get_end(), period.get_tzid()))
23.655 - return freebusy[:last]
23.656 + query, values = self.get_query(
23.657 + "delete from %(table)s :condition" % {
23.658 + "table" : self.table_name
23.659 + },
23.660 + self.period_columns, [to_string(v, "utf-8") for v in values])
23.661
23.662 -def get_overlapping(freebusy, period):
23.663 + self.cursor.execute(query, values)
23.664 +
23.665 + def remove_event_periods(self, uid, recurrenceid=None):
23.666 +
23.667 + """
23.668 + Remove from the collection all periods associated with 'uid' and
23.669 + 'recurrenceid' (which if omitted causes the "parent" object's periods to
23.670 + be referenced).
23.671
23.672 - """
23.673 - Return the entries in 'freebusy' providing periods overlapping with
23.674 - 'period'.
23.675 - """
23.676 + Return the removed periods.
23.677 + """
23.678 +
23.679 + self._check_mutable()
23.680 +
23.681 + if recurrenceid:
23.682 + columns, values = ["object_uid", "object_recurrenceid"], [uid, recurrenceid]
23.683 + else:
23.684 + columns, values = ["object_uid", "object_recurrenceid is null"], [uid]
23.685
23.686 - # Find the range of periods potentially overlapping the period in the
23.687 - # free/busy collection.
23.688 + query, _values = self.get_query(
23.689 + "select %(columns)s from %(table)s :condition" % {
23.690 + "columns" : self.columnlist(self.period_columns),
23.691 + "table" : self.table_name
23.692 + },
23.693 + columns, values)
23.694
23.695 - startpoints = periods_until(freebusy, period)
23.696 -
23.697 - # Find the range of periods potentially overlapping the period in a version
23.698 - # of the free/busy collection sorted according to end datetimes.
23.699 + self.cursor.execute(query, _values)
23.700 + removed = self.cursor.fetchall()
23.701
23.702 - endpoints = [(Period(fb.get_end_point(), fb.get_end_point()), fb) for fb in startpoints]
23.703 - endpoints.sort()
23.704 - first = bisect_left(endpoints, (Period(period.get_start_point(), period.get_start_point()),))
23.705 - endpoints = endpoints[first:]
23.706 + query, values = self.get_query(
23.707 + "delete from %(table)s :condition" % {
23.708 + "table" : self.table_name
23.709 + },
23.710 + columns, values)
23.711 +
23.712 + self.cursor.execute(query, values)
23.713
23.714 - overlapping = set()
23.715 + return map(lambda t: self.make_period(t), removed)
23.716 +
23.717 + def remove_additional_periods(self, uid, recurrenceids=None):
23.718
23.719 - for p, fb in endpoints:
23.720 - if fb.overlaps(period):
23.721 - overlapping.add(fb)
23.722 + """
23.723 + Remove from the collection all periods associated with 'uid' having a
23.724 + recurrence identifier indicating an additional or modified period.
23.725 +
23.726 + If 'recurrenceids' is specified, remove all periods associated with
23.727 + 'uid' that do not have a recurrence identifier in the given list.
23.728
23.729 - overlapping = list(overlapping)
23.730 - overlapping.sort()
23.731 - return overlapping
23.732 + Return the removed periods.
23.733 + """
23.734 +
23.735 + self._check_mutable()
23.736
23.737 -def period_overlaps(freebusy, period, get_periods=False):
23.738 + if not recurrenceids:
23.739 + columns, values = ["object_uid", "object_recurrenceid is not null"], [uid]
23.740 + else:
23.741 + columns, values = ["object_uid", "object_recurrenceid not in ?", "object_recurrenceid is not null"], [uid, tuple(recurrenceids)]
23.742
23.743 - """
23.744 - Return whether any period in 'freebusy' overlaps with the given 'period',
23.745 - returning a collection of overlapping periods if 'get_periods' is set to a
23.746 - true value.
23.747 - """
23.748 + query, _values = self.get_query(
23.749 + "select %(columns)s from %(table)s :condition" % {
23.750 + "columns" : self.columnlist(self.period_columns),
23.751 + "table" : self.table_name
23.752 + },
23.753 + columns, values)
23.754 +
23.755 + self.cursor.execute(query, _values)
23.756 + removed = self.cursor.fetchall()
23.757
23.758 - overlapping = get_overlapping(freebusy, period)
23.759 + query, values = self.get_query(
23.760 + "delete from %(table)s :condition" % {
23.761 + "table" : self.table_name
23.762 + },
23.763 + columns, values)
23.764
23.765 - if get_periods:
23.766 - return overlapping
23.767 - else:
23.768 - return len(overlapping) != 0
23.769 + self.cursor.execute(query, values)
23.770
23.771 -def remove_overlapping(freebusy, period):
23.772 + return map(lambda t: self.make_period(t), removed)
23.773 +
23.774 + def remove_affected_period(self, uid, start):
23.775
23.776 - "Remove from 'freebusy' all periods overlapping with 'period'."
23.777 + """
23.778 + Remove from the collection the period associated with 'uid' that
23.779 + provides an occurrence starting at the given 'start' (provided by a
23.780 + recurrence identifier, converted to a datetime). A recurrence identifier
23.781 + is used to provide an alternative time period whilst also acting as a
23.782 + reference to the originally-defined occurrence.
23.783
23.784 - overlapping = get_overlapping(freebusy, period)
23.785 + Return any removed period in a list.
23.786 + """
23.787
23.788 - if overlapping:
23.789 - for fb in overlapping:
23.790 - freebusy.remove(fb)
23.791 + self._check_mutable()
23.792 +
23.793 + start = format_datetime(start)
23.794 +
23.795 + columns, values = ["object_uid", "start", "object_recurrenceid is null"], [uid, start]
23.796
23.797 -def replace_overlapping(freebusy, period, replacements):
23.798 + query, _values = self.get_query(
23.799 + "select %(columns)s from %(table)s :condition" % {
23.800 + "columns" : self.columnlist(self.period_columns),
23.801 + "table" : self.table_name
23.802 + },
23.803 + columns, values)
23.804
23.805 - """
23.806 - Replace existing periods in 'freebusy' within the given 'period', using the
23.807 - given 'replacements'.
23.808 - """
23.809 + self.cursor.execute(query, _values)
23.810 + removed = self.cursor.fetchall()
23.811 +
23.812 + query, values = self.get_query(
23.813 + "delete from %(table)s :condition" % {
23.814 + "table" : self.table_name
23.815 + },
23.816 + columns, values)
23.817
23.818 - remove_overlapping(freebusy, period)
23.819 - for replacement in replacements:
23.820 - insert_period(freebusy, replacement)
23.821 + self.cursor.execute(query, values)
23.822 +
23.823 + return map(lambda t: self.make_period(t), removed)
23.824 +
23.825 + def periods_from(self, period):
23.826 +
23.827 + "Return the entries in the collection at or after 'period'."
23.828 +
23.829 + start = format_datetime(period.get_start_point())
23.830
23.831 -def coalesce_freebusy(freebusy):
23.832 + columns, values = [], []
23.833
23.834 - "Coalesce the periods in 'freebusy'."
23.835 + if start:
23.836 + columns.append("start >= ?")
23.837 + values.append(start)
23.838
23.839 - if not freebusy:
23.840 - return freebusy
23.841 + query, values = self.get_query(
23.842 + "select %(columns)s from %(table)s :condition" % {
23.843 + "columns" : self.columnlist(self.period_columns),
23.844 + "table" : self.table_name
23.845 + },
23.846 + columns, values)
23.847
23.848 - fb = []
23.849 - start = freebusy[0].get_start_point()
23.850 - end = freebusy[0].get_end_point()
23.851 + self.cursor.execute(query, values)
23.852 +
23.853 + return map(lambda t: self.make_period(t), self.cursor.fetchall())
23.854 +
23.855 + def periods_until(self, period):
23.856
23.857 - for period in freebusy[1:]:
23.858 - if period.get_start_point() > end:
23.859 - fb.append(FreeBusyPeriod(start, end))
23.860 - start = period.get_start_point()
23.861 - end = period.get_end_point()
23.862 - else:
23.863 - end = max(end, period.get_end_point())
23.864 + "Return the entries in the collection before 'period'."
23.865 +
23.866 + end = format_datetime(period.get_end_point())
23.867 +
23.868 + columns, values = [], []
23.869 +
23.870 + if end:
23.871 + columns.append("start < ?")
23.872 + values.append(end)
23.873
23.874 - fb.append(FreeBusyPeriod(start, end))
23.875 - return fb
23.876 + query, values = self.get_query(
23.877 + "select %(columns)s from %(table)s :condition" % {
23.878 + "columns" : self.columnlist(self.period_columns),
23.879 + "table" : self.table_name
23.880 + },
23.881 + columns, values)
23.882
23.883 -def invert_freebusy(freebusy):
23.884 + self.cursor.execute(query, values)
23.885
23.886 - "Return the free periods from 'freebusy'."
23.887 + return map(lambda t: self.make_period(t), self.cursor.fetchall())
23.888 +
23.889 + def get_overlapping(self, period):
23.890
23.891 - if not freebusy:
23.892 - return [FreeBusyPeriod(None, None)]
23.893 + """
23.894 + Return the entries in the collection providing periods overlapping with
23.895 + 'period'.
23.896 + """
23.897
23.898 - # Coalesce periods that overlap or are adjacent.
23.899 + columns, values = self._get_period_values(period)
23.900
23.901 - fb = coalesce_freebusy(freebusy)
23.902 - free = []
23.903 + query, values = self.get_query(
23.904 + "select %(columns)s from %(table)s :condition" % {
23.905 + "columns" : self.columnlist(self.period_columns),
23.906 + "table" : self.table_name
23.907 + },
23.908 + columns, values)
23.909
23.910 - # Add a start-of-time period if appropriate.
23.911 + self.cursor.execute(query, values)
23.912
23.913 - first = fb[0].get_start_point()
23.914 - if first:
23.915 - free.append(FreeBusyPeriod(None, first))
23.916 + return map(lambda t: self.make_period(t), self.cursor.fetchall())
23.917 +
23.918 + def remove_overlapping(self, period):
23.919
23.920 - start = fb[0].get_end_point()
23.921 + "Remove all periods overlapping with 'period' from the collection."
23.922 +
23.923 + self._check_mutable()
23.924 +
23.925 + columns, values = self._get_period_values(period)
23.926
23.927 - for period in fb[1:]:
23.928 - free.append(FreeBusyPeriod(start, period.get_start_point()))
23.929 - start = period.get_end_point()
23.930 + query, values = self.get_query(
23.931 + "delete from %(table)s :condition" % {
23.932 + "table" : self.table_name
23.933 + },
23.934 + columns, values)
23.935 +
23.936 + self.cursor.execute(query, values)
23.937 +
23.938 + def _get_period_values(self, period):
23.939
23.940 - # Add an end-of-time period if appropriate.
23.941 + start = format_datetime(period.get_start_point())
23.942 + end = format_datetime(period.get_end_point())
23.943 +
23.944 + columns, values = [], []
23.945
23.946 - if start:
23.947 - free.append(FreeBusyPeriod(start, None))
23.948 + if end:
23.949 + columns.append("start < ?")
23.950 + values.append(end)
23.951 + if start:
23.952 + columns.append("end > ?")
23.953 + values.append(start)
23.954
23.955 - return free
23.956 + return columns, values
23.957
23.958 # Period layout.
23.959
23.960 @@ -1014,19 +1432,4 @@
23.961
23.962 return spans
23.963
23.964 -def update_freebusy(freebusy, periods, transp, uid, recurrenceid, summary, organiser, expires=None):
23.965 -
23.966 - """
23.967 - Update the free/busy details with the given 'periods', 'transp' setting,
23.968 - 'uid' plus 'recurrenceid' and 'summary' and 'organiser' details.
23.969 -
23.970 - An optional 'expires' datetime string indicates the expiry time of any
23.971 - free/busy offer.
23.972 - """
23.973 -
23.974 - remove_event_periods(freebusy, uid, recurrenceid)
23.975 -
23.976 - for p in periods:
23.977 - insert_period(freebusy, FreeBusyPeriod(p.get_start_point(), p.get_end_point(), uid, transp, recurrenceid, summary, organiser, expires))
23.978 -
23.979 # vim: tabstop=4 expandtab shiftwidth=4
24.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
24.2 +++ b/imiptools/sql.py Fri Apr 22 16:22:58 2016 +0200
24.3 @@ -0,0 +1,154 @@
24.4 +#!/usr/bin/env python
24.5 +
24.6 +"""
24.7 +Database utilities.
24.8 +
24.9 +Copyright (C) 2016 Paul Boddie <paul@boddie.org.uk>
24.10 +
24.11 +This program is free software; you can redistribute it and/or modify it under
24.12 +the terms of the GNU General Public License as published by the Free Software
24.13 +Foundation; either version 3 of the License, or (at your option) any later
24.14 +version.
24.15 +
24.16 +This program is distributed in the hope that it will be useful, but WITHOUT
24.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
24.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
24.19 +details.
24.20 +
24.21 +You should have received a copy of the GNU General Public License along with
24.22 +this program. If not, see <http://www.gnu.org/licenses/>.
24.23 +"""
24.24 +
24.25 +import re
24.26 +
24.27 +class DatabaseOperations:
24.28 +
24.29 + "Special database-related operations."
24.30 +
24.31 + def __init__(self, column_names=None, filter_values=None, paramstyle=None):
24.32 + self.column_names = column_names
24.33 + self.filter_values = filter_values
24.34 + self.paramstyle = paramstyle
24.35 +
24.36 + def get_query(self, query, columns=None, values=None, setcolumns=None,
24.37 + setvalues=None):
24.38 +
24.39 + """
24.40 + Return 'query' parameterised with condition clauses indicated by
24.41 + ":condition" in 'query' that are themselves populated using the given
24.42 + 'columns' and 'values' together with any conditions provided when
24.43 + initialising this class.
24.44 +
24.45 + If 'setcolumns' and 'setvalues' are given, such column details and
24.46 + values will be used to parameterise ":set" clauses in the query.
24.47 + """
24.48 +
24.49 + columns = self.merge_default_columns(columns)
24.50 + values = self.merge_default_values(values)
24.51 +
24.52 + condition = self.get_condition(columns)
24.53 + columnlist = self.columnlist(columns)
24.54 + placeholders = self.placeholders(values)
24.55 + setters = self.get_setters(setcolumns)
24.56 +
24.57 + setvalues = setvalues or []
24.58 +
24.59 + # Obtain the placeholder markers in order.
24.60 +
24.61 + parts = re.split("(:(?:condition|set|columns|values)(?=[^a-zA-Z]|$))", query)
24.62 +
24.63 + l = [parts[0]]
24.64 + is_placeholder = True
24.65 + all_values = []
24.66 +
24.67 + for part in parts[1:]:
24.68 + if is_placeholder:
24.69 +
24.70 + # Replace ":condition", replicating the given values.
24.71 +
24.72 + if part == ":condition":
24.73 + all_values += values
24.74 + l.append(condition)
24.75 +
24.76 + # Replace ":set", replicating the given values.
24.77 +
24.78 + elif part == ":set":
24.79 + all_values += setvalues
24.80 + l.append(setters)
24.81 +
24.82 + # Replace ":columns", providing a column list.
24.83 +
24.84 + elif part == ":columns":
24.85 + l.append(columnlist)
24.86 +
24.87 + # Replace ":values", replicating the given values.
24.88 +
24.89 + elif part == ":values":
24.90 + all_values += values
24.91 + l.append(placeholders)
24.92 +
24.93 + else:
24.94 + l.append(part)
24.95 + else:
24.96 + l.append(part)
24.97 +
24.98 + is_placeholder = not is_placeholder
24.99 +
24.100 + query = "".join(l)
24.101 + return query, all_values
24.102 +
24.103 + def get_condition(self, columns=None):
24.104 +
24.105 + "Return a condition clause featuring the given 'columns'."
24.106 +
24.107 + l = self._get_columns(columns)
24.108 + return "where %s" % " and ".join(l)
24.109 +
24.110 + def get_setters(self, columns=None):
24.111 +
24.112 + "Return set operations featuring the given 'columns'."
24.113 +
24.114 + l = self._get_columns(columns)
24.115 + return "set %s" % ", ".join(l)
24.116 +
24.117 + def _get_columns(self, columns=None):
24.118 +
24.119 + "Return a list of statements or tests involving 'columns'."
24.120 +
24.121 + l = []
24.122 +
24.123 + if columns:
24.124 + for column in columns:
24.125 + if " " in column:
24.126 + column_name, remaining = column.split(" ", 1)
24.127 + l.append("%s %s" % (self._quote(column_name), remaining.replace("?", self._param())))
24.128 + else:
24.129 + l.append("%s = %s" % (self._quote(column), self._param()))
24.130 +
24.131 + return l
24.132 +
24.133 + def _quote(self, column):
24.134 + return '"%s"' % column
24.135 +
24.136 + def merge_default_columns(self, columns=None):
24.137 + return list(self.column_names or []) + list(columns or [])
24.138 +
24.139 + def merge_default_values(self, values=None):
24.140 + return list(self.filter_values or []) + list(values or [])
24.141 +
24.142 + def columnlist(self, columns=None):
24.143 + return ", ".join([self._quote(column) for column in columns])
24.144 +
24.145 + def placeholders(self, values=None):
24.146 + return ", ".join([self._param()] * len(values))
24.147 +
24.148 + def _param(self):
24.149 +
24.150 + # NOTE: To be expanded.
24.151 +
24.152 + if self.paramstyle == "pyformat":
24.153 + return "%s"
24.154 + else:
24.155 + return "?"
24.156 +
24.157 +# vim: tabstop=4 expandtab shiftwidth=4
25.1 --- a/imiptools/stores/__init__.py Tue Apr 19 21:20:57 2016 +0200
25.2 +++ b/imiptools/stores/__init__.py Fri Apr 22 16:22:58 2016 +0200
25.3 @@ -3,7 +3,7 @@
25.4 """
25.5 General support for calendar data storage.
25.6
25.7 -Copyright (C) 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk>
25.8 +Copyright (C) 2016 Paul Boddie <paul@boddie.org.uk>
25.9
25.10 This program is free software; you can redistribute it and/or modify it under
25.11 the terms of the GNU General Public License as published by the Free Software
25.12 @@ -19,568 +19,25 @@
25.13 this program. If not, see <http://www.gnu.org/licenses/>.
25.14 """
25.15
25.16 -from imiptools.dates import format_datetime
25.17 -
25.18 -class StoreBase:
25.19 -
25.20 - "The core operations of a data store."
25.21 -
25.22 - def acquire_lock(self, user, timeout=None):
25.23 - pass
25.24 -
25.25 - def release_lock(self, user):
25.26 - pass
25.27 -
25.28 - # User discovery.
25.29 -
25.30 - def get_users(self):
25.31 -
25.32 - "Return a list of users."
25.33 -
25.34 - pass
25.35 -
25.36 - # Event and event metadata access.
25.37 -
25.38 - def get_events(self, user):
25.39 -
25.40 - "Return a list of event identifiers."
25.41 -
25.42 - pass
25.43 -
25.44 - def get_all_events(self, user):
25.45 -
25.46 - "Return a set of (uid, recurrenceid) tuples for all events."
25.47 -
25.48 - uids = self.get_events(user)
25.49 - if not uids:
25.50 - return set()
25.51 -
25.52 - all_events = set()
25.53 - for uid in uids:
25.54 - all_events.add((uid, None))
25.55 - all_events.update([(uid, recurrenceid) for recurrenceid in self.get_recurrences(user, uid)])
25.56 -
25.57 - return all_events
25.58 -
25.59 - def get_event(self, user, uid, recurrenceid=None, dirname=None):
25.60 -
25.61 - """
25.62 - Get the event for the given 'user' with the given 'uid'. If
25.63 - the optional 'recurrenceid' is specified, a specific instance or
25.64 - occurrence of an event is returned.
25.65 - """
25.66 -
25.67 - pass
25.68 -
25.69 - def get_complete_event(self, user, uid):
25.70 -
25.71 - "Get the event for the given 'user' with the given 'uid'."
25.72 -
25.73 - pass
25.74 -
25.75 - def set_event(self, user, uid, recurrenceid, node):
25.76 -
25.77 - """
25.78 - Set an event for 'user' having the given 'uid' and 'recurrenceid' (which
25.79 - if the latter is specified, a specific instance or occurrence of an
25.80 - event is referenced), using the given 'node' description.
25.81 - """
25.82 +from imiptools.stores import file
25.83 +from imiptools.stores.database import stores as database_stores
25.84
25.85 - if recurrenceid:
25.86 - return self.set_recurrence(user, uid, recurrenceid, node)
25.87 - else:
25.88 - return self.set_complete_event(user, uid, node)
25.89 -
25.90 - def set_complete_event(self, user, uid, node):
25.91 -
25.92 - "Set an event for 'user' having the given 'uid' and 'node'."
25.93 -
25.94 - pass
25.95 -
25.96 - def remove_event(self, user, uid, recurrenceid=None):
25.97 -
25.98 - """
25.99 - Remove an event for 'user' having the given 'uid'. If the optional
25.100 - 'recurrenceid' is specified, a specific instance or occurrence of an
25.101 - event is removed.
25.102 - """
25.103 -
25.104 - if recurrenceid:
25.105 - return self.remove_recurrence(user, uid, recurrenceid)
25.106 - else:
25.107 - for recurrenceid in self.get_recurrences(user, uid) or []:
25.108 - self.remove_recurrence(user, uid, recurrenceid)
25.109 - return self.remove_complete_event(user, uid)
25.110 -
25.111 - def remove_complete_event(self, user, uid):
25.112 -
25.113 - "Remove an event for 'user' having the given 'uid'."
25.114 -
25.115 - self.remove_recurrences(user, uid)
25.116 - return self.remove_parent_event(user, uid)
25.117 -
25.118 - def remove_parent_event(self, user, uid):
25.119 -
25.120 - "Remove the parent event for 'user' having the given 'uid'."
25.121 -
25.122 - pass
25.123 -
25.124 - def get_recurrences(self, user, uid):
25.125 -
25.126 - """
25.127 - Get additional event instances for an event of the given 'user' with the
25.128 - indicated 'uid'. Both active and cancelled recurrences are returned.
25.129 - """
25.130 -
25.131 - return self.get_active_recurrences(user, uid) + self.get_cancelled_recurrences(user, uid)
25.132 -
25.133 - def get_active_recurrences(self, user, uid):
25.134 -
25.135 - """
25.136 - Get additional event instances for an event of the given 'user' with the
25.137 - indicated 'uid'. Cancelled recurrences are not returned.
25.138 - """
25.139 -
25.140 - pass
25.141 -
25.142 - def get_cancelled_recurrences(self, user, uid):
25.143 -
25.144 - """
25.145 - Get additional event instances for an event of the given 'user' with the
25.146 - indicated 'uid'. Only cancelled recurrences are returned.
25.147 - """
25.148 -
25.149 - pass
25.150 -
25.151 - def get_recurrence(self, user, uid, recurrenceid):
25.152 -
25.153 - """
25.154 - For the event of the given 'user' with the given 'uid', return the
25.155 - specific recurrence indicated by the 'recurrenceid'.
25.156 - """
25.157 +# Build a catalogue of store types.
25.158
25.159 - pass
25.160 -
25.161 - def set_recurrence(self, user, uid, recurrenceid, node):
25.162 -
25.163 - "Set an event for 'user' having the given 'uid' and 'node'."
25.164 -
25.165 - pass
25.166 -
25.167 - def remove_recurrence(self, user, uid, recurrenceid):
25.168 -
25.169 - """
25.170 - Remove a special recurrence from an event stored by 'user' having the
25.171 - given 'uid' and 'recurrenceid'.
25.172 - """
25.173 -
25.174 - pass
25.175 -
25.176 - def remove_recurrences(self, user, uid):
25.177 -
25.178 - """
25.179 - Remove all recurrences for an event stored by 'user' having the given
25.180 - 'uid'.
25.181 - """
25.182 -
25.183 - for recurrenceid in self.get_recurrences(user, uid):
25.184 - self.remove_recurrence(user, uid, recurrenceid)
25.185 -
25.186 - return self.remove_recurrence_collection(user, uid)
25.187 -
25.188 - def remove_recurrence_collection(self, user, uid):
25.189 -
25.190 - """
25.191 - Remove the collection of recurrences stored by 'user' having the given
25.192 - 'uid'.
25.193 - """
25.194 -
25.195 - pass
25.196 -
25.197 - # Free/busy period providers, upon extension of the free/busy records.
25.198 -
25.199 - def _get_freebusy_providers(self, user):
25.200 -
25.201 - """
25.202 - Return the free/busy providers for the given 'user'.
25.203 -
25.204 - This function returns any stored datetime and a list of providers as a
25.205 - 2-tuple. Each provider is itself a (uid, recurrenceid) tuple.
25.206 - """
25.207 -
25.208 - pass
25.209 -
25.210 - def get_freebusy_providers(self, user, dt=None):
25.211 -
25.212 - """
25.213 - Return a set of uncancelled events of the form (uid, recurrenceid)
25.214 - providing free/busy details beyond the given datetime 'dt'.
25.215 -
25.216 - If 'dt' is not specified, all events previously found to provide
25.217 - details will be returned. Otherwise, if 'dt' is earlier than the
25.218 - datetime recorded for the known providers, None is returned, indicating
25.219 - that the list of providers must be recomputed.
25.220 -
25.221 - This function returns a list of (uid, recurrenceid) tuples upon success.
25.222 - """
25.223 -
25.224 - t = self._get_freebusy_providers(user)
25.225 - if not t:
25.226 - return None
25.227 -
25.228 - dt_string, t = t
25.229 -
25.230 - # If the requested datetime is earlier than the stated datetime, the
25.231 - # providers will need to be recomputed.
25.232 -
25.233 - if dt:
25.234 - providers_dt = get_datetime(dt_string)
25.235 - if not providers_dt or providers_dt > dt:
25.236 - return None
25.237 -
25.238 - # Otherwise, return the providers.
25.239 -
25.240 - return t[1:]
25.241 -
25.242 - def _set_freebusy_providers(self, user, dt_string, t):
25.243 -
25.244 - "Set the given provider timestamp 'dt_string' and table 't'."
25.245 -
25.246 - pass
25.247 -
25.248 - def set_freebusy_providers(self, user, dt, providers):
25.249 -
25.250 - """
25.251 - Define the uncancelled events providing free/busy details beyond the
25.252 - given datetime 'dt'.
25.253 - """
25.254 -
25.255 - t = []
25.256 -
25.257 - for obj in providers:
25.258 - t.append((obj.get_uid(), obj.get_recurrenceid()))
25.259 -
25.260 - return self._set_freebusy_providers(user, format_datetime(dt), t)
25.261 -
25.262 - def append_freebusy_provider(self, user, provider):
25.263 -
25.264 - "For the given 'user', append the free/busy 'provider'."
25.265 -
25.266 - t = self._get_freebusy_providers(user)
25.267 - if not t:
25.268 - return False
25.269 -
25.270 - dt_string, t = t
25.271 - t.append((provider.get_uid(), provider.get_recurrenceid()))
25.272 -
25.273 - return self._set_freebusy_providers(user, dt_string, t)
25.274 -
25.275 - def remove_freebusy_provider(self, user, provider):
25.276 -
25.277 - "For the given 'user', remove the free/busy 'provider'."
25.278 -
25.279 - t = self._get_freebusy_providers(user)
25.280 - if not t:
25.281 - return False
25.282 -
25.283 - dt_string, t = t
25.284 - try:
25.285 - t.remove((provider.get_uid(), provider.get_recurrenceid()))
25.286 - except ValueError:
25.287 - return False
25.288 -
25.289 - return self._set_freebusy_providers(user, dt_string, t)
25.290 -
25.291 - # Free/busy period access.
25.292 -
25.293 - def get_freebusy(self, user, name=None):
25.294 -
25.295 - "Get free/busy details for the given 'user'."
25.296 -
25.297 - pass
25.298 -
25.299 - def get_freebusy_for_other(self, user, other):
25.300 +stores = {
25.301 + "file" : file,
25.302 + }
25.303 +stores.update(database_stores)
25.304
25.305 - "For the given 'user', get free/busy details for the 'other' user."
25.306 -
25.307 - pass
25.308 -
25.309 - def set_freebusy(self, user, freebusy, name=None):
25.310 -
25.311 - "For the given 'user', set 'freebusy' details."
25.312 -
25.313 - pass
25.314 -
25.315 - def set_freebusy_for_other(self, user, freebusy, other):
25.316 -
25.317 - "For the given 'user', set 'freebusy' details for the 'other' user."
25.318 -
25.319 - pass
25.320 -
25.321 - # Tentative free/busy periods related to countering.
25.322 -
25.323 - def get_freebusy_offers(self, user):
25.324 -
25.325 - "Get free/busy offers for the given 'user'."
25.326 -
25.327 - pass
25.328 -
25.329 - def set_freebusy_offers(self, user, freebusy):
25.330 -
25.331 - "For the given 'user', set 'freebusy' offers."
25.332 -
25.333 - return self.set_freebusy(user, freebusy, "freebusy-offers")
25.334 -
25.335 - # Requests and counter-proposals.
25.336 -
25.337 - def get_requests(self, user):
25.338 -
25.339 - "Get requests for the given 'user'."
25.340 -
25.341 - pass
25.342 -
25.343 - def set_requests(self, user, requests):
25.344 -
25.345 - "For the given 'user', set the list of queued 'requests'."
25.346 -
25.347 - pass
25.348 -
25.349 - def set_request(self, user, uid, recurrenceid=None, type=None):
25.350 -
25.351 - """
25.352 - For the given 'user', set the queued 'uid' and 'recurrenceid',
25.353 - indicating a request, along with any given 'type'.
25.354 - """
25.355 -
25.356 - pass
25.357 -
25.358 - def queue_request(self, user, uid, recurrenceid=None, type=None):
25.359 -
25.360 - """
25.361 - Queue a request for 'user' having the given 'uid'. If the optional
25.362 - 'recurrenceid' is specified, the entry refers to a specific instance
25.363 - or occurrence of an event. The 'type' parameter can be used to indicate
25.364 - a specific type of request.
25.365 - """
25.366 -
25.367 - requests = self.get_requests(user) or []
25.368 -
25.369 - if not self.have_request(requests, uid, recurrenceid):
25.370 - return self.set_request(user, uid, recurrenceid, type)
25.371 -
25.372 - return False
25.373 +# Access functions.
25.374
25.375 - def dequeue_request(self, user, uid, recurrenceid=None):
25.376 -
25.377 - """
25.378 - Dequeue all requests for 'user' having the given 'uid'. If the optional
25.379 - 'recurrenceid' is specified, all requests for that specific instance or
25.380 - occurrence of an event are dequeued.
25.381 - """
25.382 -
25.383 - requests = self.get_requests(user) or []
25.384 - result = []
25.385 -
25.386 - for request in requests:
25.387 - if request[:2] != (uid, recurrenceid):
25.388 - result.append(request)
25.389 -
25.390 - self.set_requests(user, result)
25.391 - return True
25.392 -
25.393 - def has_request(self, user, uid, recurrenceid=None, type=None, strict=False):
25.394 - return self.have_request(self.get_requests(user) or [], uid, recurrenceid, type, strict)
25.395 -
25.396 - def have_request(self, requests, uid, recurrenceid=None, type=None, strict=False):
25.397 -
25.398 - """
25.399 - Return whether 'requests' contains a request with the given 'uid' and
25.400 - any specified 'recurrenceid' and 'type'. If 'strict' is set to a true
25.401 - value, the precise type of the request must match; otherwise, any type
25.402 - of request for the identified object may be matched.
25.403 - """
25.404 -
25.405 - for request in requests:
25.406 - if request[:2] == (uid, recurrenceid) and (
25.407 - not strict or
25.408 - not request[2:] and not type or
25.409 - request[2:] and request[2] == type):
25.410 -
25.411 - return True
25.412 -
25.413 - return False
25.414 -
25.415 - def get_counters(self, user, uid, recurrenceid=None):
25.416 -
25.417 - """
25.418 - For the given 'user', return a list of users from whom counter-proposals
25.419 - have been received for the given 'uid' and optional 'recurrenceid'.
25.420 - """
25.421 -
25.422 - pass
25.423 -
25.424 - def get_counter(self, user, other, uid, recurrenceid=None):
25.425 -
25.426 - """
25.427 - For the given 'user', return the counter-proposal from 'other' for the
25.428 - given 'uid' and optional 'recurrenceid'.
25.429 - """
25.430 -
25.431 - pass
25.432 -
25.433 - def set_counter(self, user, other, node, uid, recurrenceid=None):
25.434 -
25.435 - """
25.436 - For the given 'user', store a counter-proposal received from 'other' the
25.437 - given 'node' representing that proposal for the given 'uid' and
25.438 - 'recurrenceid'.
25.439 - """
25.440 -
25.441 - pass
25.442 -
25.443 - def remove_counters(self, user, uid, recurrenceid=None):
25.444 +def get_store(store_type, store_dir):
25.445 + return stores[store_type].Store(store_dir)
25.446
25.447 - """
25.448 - For the given 'user', remove all counter-proposals associated with the
25.449 - given 'uid' and 'recurrenceid'.
25.450 - """
25.451 -
25.452 - pass
25.453 -
25.454 - def remove_counter(self, user, other, uid, recurrenceid=None):
25.455 -
25.456 - """
25.457 - For the given 'user', remove any counter-proposal from 'other'
25.458 - associated with the given 'uid' and 'recurrenceid'.
25.459 - """
25.460 -
25.461 - pass
25.462 -
25.463 - # Event cancellation.
25.464 -
25.465 - def cancel_event(self, user, uid, recurrenceid=None):
25.466 -
25.467 - """
25.468 - Cancel an event for 'user' having the given 'uid'. If the optional
25.469 - 'recurrenceid' is specified, a specific instance or occurrence of an
25.470 - event is cancelled.
25.471 - """
25.472 -
25.473 - pass
25.474 -
25.475 - def uncancel_event(self, user, uid, recurrenceid=None):
25.476 -
25.477 - """
25.478 - Uncancel an event for 'user' having the given 'uid'. If the optional
25.479 - 'recurrenceid' is specified, a specific instance or occurrence of an
25.480 - event is uncancelled.
25.481 - """
25.482 -
25.483 - pass
25.484 -
25.485 - def remove_cancellations(self, user, uid, recurrenceid=None):
25.486 -
25.487 - """
25.488 - Remove cancellations for 'user' for any event having the given 'uid'. If
25.489 - the optional 'recurrenceid' is specified, a specific instance or
25.490 - occurrence of an event is affected.
25.491 - """
25.492 -
25.493 - # Remove all recurrence cancellations if a general event is indicated.
25.494 -
25.495 - if not recurrenceid:
25.496 - for _recurrenceid in self.get_cancelled_recurrences(user, uid):
25.497 - self.remove_cancellation(user, uid, _recurrenceid)
25.498 -
25.499 - return self.remove_cancellation(user, uid, recurrenceid)
25.500 -
25.501 - def remove_cancellation(self, user, uid, recurrenceid=None):
25.502 -
25.503 - """
25.504 - Remove a cancellation for 'user' for the event having the given 'uid'.
25.505 - If the optional 'recurrenceid' is specified, a specific instance or
25.506 - occurrence of an event is affected.
25.507 - """
25.508 -
25.509 - pass
25.510 -
25.511 -class PublisherBase:
25.512 -
25.513 - "The core operations of a data publisher."
25.514 -
25.515 - def set_freebusy(self, user, freebusy):
25.516 +def get_publisher(publishing_dir):
25.517 + return file.Publisher(publishing_dir)
25.518
25.519 - "For the given 'user', set 'freebusy' details."
25.520 -
25.521 - pass
25.522 -
25.523 -class JournalBase:
25.524 -
25.525 - "The core operations of a journal system supporting quotas."
25.526 -
25.527 - # Quota and user identity/group discovery.
25.528 -
25.529 - def get_quotas(self):
25.530 -
25.531 - "Return a list of quotas."
25.532 -
25.533 - pass
25.534 -
25.535 - def get_quota_users(self, quota):
25.536 -
25.537 - "Return a list of quota users."
25.538 -
25.539 - pass
25.540 -
25.541 - # Groups of users sharing quotas.
25.542 -
25.543 - def get_groups(self, quota):
25.544 -
25.545 - "Return the identity mappings for the given 'quota' as a dictionary."
25.546 -
25.547 - pass
25.548 -
25.549 - def get_limits(self, quota):
25.550 -
25.551 - """
25.552 - Return the limits for the 'quota' as a dictionary mapping identities or
25.553 - groups to durations.
25.554 - """
25.555 -
25.556 - pass
25.557 -
25.558 - # Free/busy period access for users within quota groups.
25.559 -
25.560 - def get_freebusy(self, quota, user):
25.561 -
25.562 - "Get free/busy details for the given 'quota' and 'user'."
25.563 -
25.564 - pass
25.565 -
25.566 - def set_freebusy(self, quota, user, freebusy):
25.567 -
25.568 - "For the given 'quota' and 'user', set 'freebusy' details."
25.569 -
25.570 - pass
25.571 -
25.572 - # Journal entry methods.
25.573 -
25.574 - def get_entries(self, quota, group):
25.575 -
25.576 - """
25.577 - Return a list of journal entries for the given 'quota' for the indicated
25.578 - 'group'.
25.579 - """
25.580 -
25.581 - pass
25.582 -
25.583 - def set_entries(self, quota, group, entries):
25.584 -
25.585 - """
25.586 - For the given 'quota' and indicated 'group', set the list of journal
25.587 - 'entries'.
25.588 - """
25.589 -
25.590 - pass
25.591 +def get_journal(store_type, journal_dir):
25.592 + return stores[store_type].Journal(journal_dir)
25.593
25.594 # vim: tabstop=4 expandtab shiftwidth=4
26.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
26.2 +++ b/imiptools/stores/common.py Fri Apr 22 16:22:58 2016 +0200
26.3 @@ -0,0 +1,693 @@
26.4 +#!/usr/bin/env python
26.5 +
26.6 +"""
26.7 +General support for calendar data storage.
26.8 +
26.9 +Copyright (C) 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk>
26.10 +
26.11 +This program is free software; you can redistribute it and/or modify it under
26.12 +the terms of the GNU General Public License as published by the Free Software
26.13 +Foundation; either version 3 of the License, or (at your option) any later
26.14 +version.
26.15 +
26.16 +This program is distributed in the hope that it will be useful, but WITHOUT
26.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
26.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
26.19 +details.
26.20 +
26.21 +You should have received a copy of the GNU General Public License along with
26.22 +this program. If not, see <http://www.gnu.org/licenses/>.
26.23 +"""
26.24 +
26.25 +from imiptools.dates import format_datetime, get_datetime
26.26 +
26.27 +class StoreBase:
26.28 +
26.29 + "The core operations of a data store."
26.30 +
26.31 + # User discovery.
26.32 +
26.33 + def get_users(self):
26.34 +
26.35 + "Return a list of users."
26.36 +
26.37 + pass
26.38 +
26.39 + # Event and event metadata access.
26.40 +
26.41 + def get_all_events(self, user, dirname=None):
26.42 +
26.43 + """
26.44 + Return a set of (uid, recurrenceid) tuples for all events. Unless
26.45 + 'dirname' is specified, only active events are returned; otherwise,
26.46 + events from the given 'dirname' are returned.
26.47 + """
26.48 +
26.49 + cancelled = self.get_cancelled_events(user)
26.50 + active = self.get_events(user)
26.51 +
26.52 + if dirname == "cancellations":
26.53 + uids = cancelled
26.54 + else:
26.55 + uids = active
26.56 +
26.57 + if not uids:
26.58 + return set()
26.59 +
26.60 + all_events = set()
26.61 +
26.62 + # Add all qualifying parent events to the result set.
26.63 +
26.64 + for uid in uids:
26.65 + all_events.add((uid, None))
26.66 +
26.67 + # Process all parent events regardless of status to find those in the
26.68 + # category/directory of interest.
26.69 +
26.70 + for uid in active + cancelled:
26.71 +
26.72 + if dirname == "cancellations":
26.73 + recurrenceids = self.get_cancelled_recurrences(user, uid)
26.74 + else:
26.75 + recurrenceids = self.get_active_recurrences(user, uid)
26.76 +
26.77 + all_events.update([(uid, recurrenceid) for recurrenceid in recurrenceids])
26.78 +
26.79 + return all_events
26.80 +
26.81 + def get_events(self, user):
26.82 +
26.83 + "Return a list of event identifiers."
26.84 +
26.85 + pass
26.86 +
26.87 + def get_cancelled_events(self, user):
26.88 +
26.89 + "Return a list of event identifiers for cancelled events."
26.90 +
26.91 + pass
26.92 +
26.93 + def get_event(self, user, uid, recurrenceid=None, dirname=None):
26.94 +
26.95 + """
26.96 + Get the event for the given 'user' with the given 'uid'. If
26.97 + the optional 'recurrenceid' is specified, a specific instance or
26.98 + occurrence of an event is returned.
26.99 + """
26.100 +
26.101 + pass
26.102 +
26.103 + def get_complete_event(self, user, uid):
26.104 +
26.105 + "Get the event for the given 'user' with the given 'uid'."
26.106 +
26.107 + pass
26.108 +
26.109 + def set_event(self, user, uid, recurrenceid, node):
26.110 +
26.111 + """
26.112 + Set an event for 'user' having the given 'uid' and 'recurrenceid' (which
26.113 + if the latter is specified, a specific instance or occurrence of an
26.114 + event is referenced), using the given 'node' description.
26.115 + """
26.116 +
26.117 + if recurrenceid:
26.118 + return self.set_recurrence(user, uid, recurrenceid, node)
26.119 + else:
26.120 + return self.set_complete_event(user, uid, node)
26.121 +
26.122 + def set_complete_event(self, user, uid, node):
26.123 +
26.124 + "Set an event for 'user' having the given 'uid' and 'node'."
26.125 +
26.126 + pass
26.127 +
26.128 + def remove_event(self, user, uid, recurrenceid=None):
26.129 +
26.130 + """
26.131 + Remove an event for 'user' having the given 'uid'. If the optional
26.132 + 'recurrenceid' is specified, a specific instance or occurrence of an
26.133 + event is removed.
26.134 + """
26.135 +
26.136 + if recurrenceid:
26.137 + return self.remove_recurrence(user, uid, recurrenceid)
26.138 + else:
26.139 + for recurrenceid in self.get_recurrences(user, uid) or []:
26.140 + self.remove_recurrence(user, uid, recurrenceid)
26.141 + return self.remove_complete_event(user, uid)
26.142 +
26.143 + def remove_complete_event(self, user, uid):
26.144 +
26.145 + "Remove an event for 'user' having the given 'uid'."
26.146 +
26.147 + self.remove_recurrences(user, uid)
26.148 + return self.remove_parent_event(user, uid)
26.149 +
26.150 + def remove_parent_event(self, user, uid):
26.151 +
26.152 + "Remove the parent event for 'user' having the given 'uid'."
26.153 +
26.154 + pass
26.155 +
26.156 + def get_recurrences(self, user, uid):
26.157 +
26.158 + """
26.159 + Get additional event instances for an event of the given 'user' with the
26.160 + indicated 'uid'. Both active and cancelled recurrences are returned.
26.161 + """
26.162 +
26.163 + return self.get_active_recurrences(user, uid) + self.get_cancelled_recurrences(user, uid)
26.164 +
26.165 + def get_active_recurrences(self, user, uid):
26.166 +
26.167 + """
26.168 + Get additional event instances for an event of the given 'user' with the
26.169 + indicated 'uid'. Cancelled recurrences are not returned.
26.170 + """
26.171 +
26.172 + pass
26.173 +
26.174 + def get_cancelled_recurrences(self, user, uid):
26.175 +
26.176 + """
26.177 + Get additional event instances for an event of the given 'user' with the
26.178 + indicated 'uid'. Only cancelled recurrences are returned.
26.179 + """
26.180 +
26.181 + pass
26.182 +
26.183 + def get_recurrence(self, user, uid, recurrenceid):
26.184 +
26.185 + """
26.186 + For the event of the given 'user' with the given 'uid', return the
26.187 + specific recurrence indicated by the 'recurrenceid'.
26.188 + """
26.189 +
26.190 + pass
26.191 +
26.192 + def set_recurrence(self, user, uid, recurrenceid, node):
26.193 +
26.194 + "Set an event for 'user' having the given 'uid' and 'node'."
26.195 +
26.196 + pass
26.197 +
26.198 + def remove_recurrence(self, user, uid, recurrenceid):
26.199 +
26.200 + """
26.201 + Remove a special recurrence from an event stored by 'user' having the
26.202 + given 'uid' and 'recurrenceid'.
26.203 + """
26.204 +
26.205 + pass
26.206 +
26.207 + def remove_recurrences(self, user, uid):
26.208 +
26.209 + """
26.210 + Remove all recurrences for an event stored by 'user' having the given
26.211 + 'uid'.
26.212 + """
26.213 +
26.214 + for recurrenceid in self.get_recurrences(user, uid):
26.215 + self.remove_recurrence(user, uid, recurrenceid)
26.216 +
26.217 + return self.remove_recurrence_collection(user, uid)
26.218 +
26.219 + def remove_recurrence_collection(self, user, uid):
26.220 +
26.221 + """
26.222 + Remove the collection of recurrences stored by 'user' having the given
26.223 + 'uid'.
26.224 + """
26.225 +
26.226 + pass
26.227 +
26.228 + # Free/busy period providers, upon extension of the free/busy records.
26.229 +
26.230 + def _get_freebusy_providers(self, user):
26.231 +
26.232 + """
26.233 + Return the free/busy providers for the given 'user'.
26.234 +
26.235 + This function returns any stored datetime and a list of providers as a
26.236 + 2-tuple. Each provider is itself a (uid, recurrenceid) tuple.
26.237 + """
26.238 +
26.239 + pass
26.240 +
26.241 + def get_freebusy_providers(self, user, dt=None):
26.242 +
26.243 + """
26.244 + Return a set of uncancelled events of the form (uid, recurrenceid)
26.245 + providing free/busy details beyond the given datetime 'dt'.
26.246 +
26.247 + If 'dt' is not specified, all events previously found to provide
26.248 + details will be returned. Otherwise, if 'dt' is earlier than the
26.249 + datetime recorded for the known providers, None is returned, indicating
26.250 + that the list of providers must be recomputed.
26.251 +
26.252 + This function returns a list of (uid, recurrenceid) tuples upon success.
26.253 + """
26.254 +
26.255 + t = self._get_freebusy_providers(user)
26.256 + if not t:
26.257 + return None
26.258 +
26.259 + dt_string, t = t
26.260 +
26.261 + # If the requested datetime is earlier than the stated datetime, the
26.262 + # providers will need to be recomputed.
26.263 +
26.264 + if dt:
26.265 + providers_dt = get_datetime(dt_string)
26.266 + if not providers_dt or providers_dt > dt:
26.267 + return None
26.268 +
26.269 + # Otherwise, return the providers.
26.270 +
26.271 + return t
26.272 +
26.273 + def _set_freebusy_providers(self, user, dt_string, t):
26.274 +
26.275 + "Set the given provider timestamp 'dt_string' and table 't'."
26.276 +
26.277 + pass
26.278 +
26.279 + def set_freebusy_providers(self, user, dt, providers):
26.280 +
26.281 + """
26.282 + Define the uncancelled events providing free/busy details beyond the
26.283 + given datetime 'dt'.
26.284 + """
26.285 +
26.286 + t = []
26.287 +
26.288 + for obj in providers:
26.289 + t.append((obj.get_uid(), obj.get_recurrenceid()))
26.290 +
26.291 + return self._set_freebusy_providers(user, format_datetime(dt), t)
26.292 +
26.293 + def append_freebusy_provider(self, user, provider):
26.294 +
26.295 + "For the given 'user', append the free/busy 'provider'."
26.296 +
26.297 + t = self._get_freebusy_providers(user)
26.298 + if not t:
26.299 + return False
26.300 +
26.301 + dt_string, t = t
26.302 + t.append((provider.get_uid(), provider.get_recurrenceid()))
26.303 +
26.304 + return self._set_freebusy_providers(user, dt_string, t)
26.305 +
26.306 + def remove_freebusy_provider(self, user, provider):
26.307 +
26.308 + "For the given 'user', remove the free/busy 'provider'."
26.309 +
26.310 + t = self._get_freebusy_providers(user)
26.311 + if not t:
26.312 + return False
26.313 +
26.314 + dt_string, t = t
26.315 + try:
26.316 + t.remove((provider.get_uid(), provider.get_recurrenceid()))
26.317 + except ValueError:
26.318 + return False
26.319 +
26.320 + return self._set_freebusy_providers(user, dt_string, t)
26.321 +
26.322 + # Free/busy period access.
26.323 +
26.324 + def get_freebusy(self, user, name=None, mutable=False):
26.325 +
26.326 + "Get free/busy details for the given 'user'."
26.327 +
26.328 + pass
26.329 +
26.330 + def get_freebusy_for_other(self, user, other, mutable=False):
26.331 +
26.332 + "For the given 'user', get free/busy details for the 'other' user."
26.333 +
26.334 + pass
26.335 +
26.336 + def get_freebusy_for_update(self, user, name=None):
26.337 +
26.338 + """
26.339 + Get free/busy details for the given 'user' that may be updated,
26.340 + potentially affecting the stored information directly.
26.341 + """
26.342 +
26.343 + return self.get_freebusy(user, name, True)
26.344 +
26.345 + def get_freebusy_for_other_for_update(self, user, other):
26.346 +
26.347 + """
26.348 + For the given 'user', get free/busy details for the 'other' user
26.349 + that may be updated, potentially affecting the stored information
26.350 + directly.
26.351 + """
26.352 +
26.353 + return self.get_freebusy_for_other(user, other, True)
26.354 +
26.355 + def set_freebusy(self, user, freebusy, name=None):
26.356 +
26.357 + "For the given 'user', set 'freebusy' details."
26.358 +
26.359 + pass
26.360 +
26.361 + def set_freebusy_for_other(self, user, freebusy, other):
26.362 +
26.363 + "For the given 'user', set 'freebusy' details for the 'other' user."
26.364 +
26.365 + pass
26.366 +
26.367 + def get_freebusy_others(self, user):
26.368 +
26.369 + """
26.370 + For the given 'user', return a list of other users for whom free/busy
26.371 + information is retained.
26.372 + """
26.373 +
26.374 + pass
26.375 +
26.376 + # Tentative free/busy periods related to countering.
26.377 +
26.378 + def get_freebusy_offers(self, user, mutable=False):
26.379 +
26.380 + "Get free/busy offers for the given 'user'."
26.381 +
26.382 + pass
26.383 +
26.384 + def get_freebusy_offers_for_update(self, user):
26.385 +
26.386 + """
26.387 + Get free/busy offers for the given 'user' that may be updated,
26.388 + potentially affecting the stored information directly.
26.389 + """
26.390 +
26.391 + return self.get_freebusy_offers(user, True)
26.392 +
26.393 + def set_freebusy_offers(self, user, freebusy):
26.394 +
26.395 + "For the given 'user', set 'freebusy' offers."
26.396 +
26.397 + return self.set_freebusy(user, freebusy, "freebusy-offers")
26.398 +
26.399 + # Requests and counter-proposals.
26.400 +
26.401 + def get_requests(self, user):
26.402 +
26.403 + "Get requests for the given 'user'."
26.404 +
26.405 + pass
26.406 +
26.407 + def set_requests(self, user, requests):
26.408 +
26.409 + "For the given 'user', set the list of queued 'requests'."
26.410 +
26.411 + pass
26.412 +
26.413 + def set_request(self, user, uid, recurrenceid=None, type=None):
26.414 +
26.415 + """
26.416 + For the given 'user', set the queued 'uid' and 'recurrenceid',
26.417 + indicating a request, along with any given 'type'.
26.418 + """
26.419 +
26.420 + pass
26.421 +
26.422 + def queue_request(self, user, uid, recurrenceid=None, type=None):
26.423 +
26.424 + """
26.425 + Queue a request for 'user' having the given 'uid'. If the optional
26.426 + 'recurrenceid' is specified, the entry refers to a specific instance
26.427 + or occurrence of an event. The 'type' parameter can be used to indicate
26.428 + a specific type of request.
26.429 + """
26.430 +
26.431 + requests = self.get_requests(user) or []
26.432 +
26.433 + if not self.have_request(requests, uid, recurrenceid):
26.434 + return self.set_request(user, uid, recurrenceid, type)
26.435 +
26.436 + return False
26.437 +
26.438 + def dequeue_request(self, user, uid, recurrenceid=None):
26.439 +
26.440 + """
26.441 + Dequeue all requests for 'user' having the given 'uid'. If the optional
26.442 + 'recurrenceid' is specified, all requests for that specific instance or
26.443 + occurrence of an event are dequeued.
26.444 + """
26.445 +
26.446 + requests = self.get_requests(user) or []
26.447 + result = []
26.448 +
26.449 + for request in requests:
26.450 + if request[:2] != (uid, recurrenceid):
26.451 + result.append(request)
26.452 +
26.453 + self.set_requests(user, result)
26.454 + return True
26.455 +
26.456 + def has_request(self, user, uid, recurrenceid=None, type=None, strict=False):
26.457 + return self.have_request(self.get_requests(user) or [], uid, recurrenceid, type, strict)
26.458 +
26.459 + def have_request(self, requests, uid, recurrenceid=None, type=None, strict=False):
26.460 +
26.461 + """
26.462 + Return whether 'requests' contains a request with the given 'uid' and
26.463 + any specified 'recurrenceid' and 'type'. If 'strict' is set to a true
26.464 + value, the precise type of the request must match; otherwise, any type
26.465 + of request for the identified object may be matched.
26.466 + """
26.467 +
26.468 + for request in requests:
26.469 + if request[:2] == (uid, recurrenceid) and (
26.470 + not strict or
26.471 + not request[2:] and not type or
26.472 + request[2:] and request[2] == type):
26.473 +
26.474 + return True
26.475 +
26.476 + return False
26.477 +
26.478 + def get_counters(self, user, uid, recurrenceid=None):
26.479 +
26.480 + """
26.481 + For the given 'user', return a list of users from whom counter-proposals
26.482 + have been received for the given 'uid' and optional 'recurrenceid'.
26.483 + """
26.484 +
26.485 + pass
26.486 +
26.487 + def get_counter(self, user, other, uid, recurrenceid=None):
26.488 +
26.489 + """
26.490 + For the given 'user', return the counter-proposal from 'other' for the
26.491 + given 'uid' and optional 'recurrenceid'.
26.492 + """
26.493 +
26.494 + pass
26.495 +
26.496 + def set_counter(self, user, other, node, uid, recurrenceid=None):
26.497 +
26.498 + """
26.499 + For the given 'user', store a counter-proposal received from 'other' the
26.500 + given 'node' representing that proposal for the given 'uid' and
26.501 + 'recurrenceid'.
26.502 + """
26.503 +
26.504 + pass
26.505 +
26.506 + def remove_counters(self, user, uid, recurrenceid=None):
26.507 +
26.508 + """
26.509 + For the given 'user', remove all counter-proposals associated with the
26.510 + given 'uid' and 'recurrenceid'.
26.511 + """
26.512 +
26.513 + pass
26.514 +
26.515 + def remove_counter(self, user, other, uid, recurrenceid=None):
26.516 +
26.517 + """
26.518 + For the given 'user', remove any counter-proposal from 'other'
26.519 + associated with the given 'uid' and 'recurrenceid'.
26.520 + """
26.521 +
26.522 + pass
26.523 +
26.524 + # Event cancellation.
26.525 +
26.526 + def cancel_event(self, user, uid, recurrenceid=None):
26.527 +
26.528 + """
26.529 + Cancel an event for 'user' having the given 'uid'. If the optional
26.530 + 'recurrenceid' is specified, a specific instance or occurrence of an
26.531 + event is cancelled.
26.532 + """
26.533 +
26.534 + pass
26.535 +
26.536 + def uncancel_event(self, user, uid, recurrenceid=None):
26.537 +
26.538 + """
26.539 + Uncancel an event for 'user' having the given 'uid'. If the optional
26.540 + 'recurrenceid' is specified, a specific instance or occurrence of an
26.541 + event is uncancelled.
26.542 + """
26.543 +
26.544 + pass
26.545 +
26.546 + def remove_cancellations(self, user, uid, recurrenceid=None):
26.547 +
26.548 + """
26.549 + Remove cancellations for 'user' for any event having the given 'uid'. If
26.550 + the optional 'recurrenceid' is specified, a specific instance or
26.551 + occurrence of an event is affected.
26.552 + """
26.553 +
26.554 + # Remove all recurrence cancellations if a general event is indicated.
26.555 +
26.556 + if not recurrenceid:
26.557 + for _recurrenceid in self.get_cancelled_recurrences(user, uid):
26.558 + self.remove_cancellation(user, uid, _recurrenceid)
26.559 +
26.560 + return self.remove_cancellation(user, uid, recurrenceid)
26.561 +
26.562 + def remove_cancellation(self, user, uid, recurrenceid=None):
26.563 +
26.564 + """
26.565 + Remove a cancellation for 'user' for the event having the given 'uid'.
26.566 + If the optional 'recurrenceid' is specified, a specific instance or
26.567 + occurrence of an event is affected.
26.568 + """
26.569 +
26.570 + pass
26.571 +
26.572 +class PublisherBase:
26.573 +
26.574 + "The core operations of a data publisher."
26.575 +
26.576 + def set_freebusy(self, user, freebusy):
26.577 +
26.578 + "For the given 'user', set 'freebusy' details."
26.579 +
26.580 + pass
26.581 +
26.582 +class JournalBase:
26.583 +
26.584 + "The core operations of a journal system supporting quotas."
26.585 +
26.586 + # Quota and user identity/group discovery.
26.587 +
26.588 + def get_quotas(self):
26.589 +
26.590 + "Return a list of quotas."
26.591 +
26.592 + pass
26.593 +
26.594 + def get_quota_users(self, quota):
26.595 +
26.596 + "Return a list of quota users."
26.597 +
26.598 + pass
26.599 +
26.600 + # Groups of users sharing quotas.
26.601 +
26.602 + def get_groups(self, quota):
26.603 +
26.604 + "Return the identity mappings for the given 'quota' as a dictionary."
26.605 +
26.606 + pass
26.607 +
26.608 + def set_group(self, quota, store_user, user_group):
26.609 +
26.610 + """
26.611 + For the given 'quota', set a mapping from 'store_user' to 'user_group'.
26.612 + """
26.613 +
26.614 + pass
26.615 +
26.616 + def get_limits(self, quota):
26.617 +
26.618 + """
26.619 + Return the limits for the 'quota' as a dictionary mapping identities or
26.620 + groups to durations.
26.621 + """
26.622 +
26.623 + pass
26.624 +
26.625 + def set_limit(self, quota, group, limit):
26.626 +
26.627 + """
26.628 + For the given 'quota', set for a user 'group' the given 'limit' on
26.629 + resource usage.
26.630 + """
26.631 +
26.632 + pass
26.633 +
26.634 + # Free/busy period access for users within quota groups.
26.635 +
26.636 + def get_freebusy_users(self, quota):
26.637 +
26.638 + """
26.639 + Return a list of users whose free/busy details are retained for the
26.640 + given 'quota'.
26.641 + """
26.642 +
26.643 + pass
26.644 +
26.645 + def get_freebusy(self, quota, user, mutable=False):
26.646 +
26.647 + "Get free/busy details for the given 'quota' and 'user'."
26.648 +
26.649 + pass
26.650 +
26.651 + def get_freebusy_for_update(self, quota, user):
26.652 +
26.653 + """
26.654 + Get free/busy details for the given 'quota' and 'user' that may be
26.655 + updated, potentially affecting the stored information directly.
26.656 + """
26.657 +
26.658 + return self.get_freebusy(quota, user, True)
26.659 +
26.660 + def set_freebusy(self, quota, user, freebusy):
26.661 +
26.662 + "For the given 'quota' and 'user', set 'freebusy' details."
26.663 +
26.664 + pass
26.665 +
26.666 + # Journal entry methods.
26.667 +
26.668 + def get_entries(self, quota, group, mutable=False):
26.669 +
26.670 + """
26.671 + Return a list of journal entries for the given 'quota' for the indicated
26.672 + 'group'.
26.673 + """
26.674 +
26.675 + pass
26.676 +
26.677 + def get_entries_for_update(self, quota, group):
26.678 +
26.679 + """
26.680 + Return a list of journal entries for the given 'quota' for the indicated
26.681 + 'group' that may be updated, potentially affecting the stored
26.682 + information directly.
26.683 + """
26.684 +
26.685 + return self.get_entries(quota, group, True)
26.686 +
26.687 + def set_entries(self, quota, group, entries):
26.688 +
26.689 + """
26.690 + For the given 'quota' and indicated 'group', set the list of journal
26.691 + 'entries'.
26.692 + """
26.693 +
26.694 + pass
26.695 +
26.696 +# vim: tabstop=4 expandtab shiftwidth=4
27.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
27.2 +++ b/imiptools/stores/database/__init__.py Fri Apr 22 16:22:58 2016 +0200
27.3 @@ -0,0 +1,28 @@
27.4 +#!/usr/bin/env python
27.5 +
27.6 +"""
27.7 +General support for database stores.
27.8 +
27.9 +Copyright (C) 2016 Paul Boddie <paul@boddie.org.uk>
27.10 +
27.11 +This program is free software; you can redistribute it and/or modify it under
27.12 +the terms of the GNU General Public License as published by the Free Software
27.13 +Foundation; either version 3 of the License, or (at your option) any later
27.14 +version.
27.15 +
27.16 +This program is distributed in the hope that it will be useful, but WITHOUT
27.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
27.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
27.19 +details.
27.20 +
27.21 +You should have received a copy of the GNU General Public License along with
27.22 +this program. If not, see <http://www.gnu.org/licenses/>.
27.23 +"""
27.24 +
27.25 +from imiptools.stores.database import postgresql
27.26 +
27.27 +stores = {
27.28 + "postgresql" : postgresql,
27.29 + }
27.30 +
27.31 +# vim: tabstop=4 expandtab shiftwidth=4
28.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
28.2 +++ b/imiptools/stores/database/common.py Fri Apr 22 16:22:58 2016 +0200
28.3 @@ -0,0 +1,1001 @@
28.4 +#!/usr/bin/env python
28.5 +
28.6 +"""
28.7 +A database store of calendar data.
28.8 +
28.9 +Copyright (C) 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk>
28.10 +
28.11 +This program is free software; you can redistribute it and/or modify it under
28.12 +the terms of the GNU General Public License as published by the Free Software
28.13 +Foundation; either version 3 of the License, or (at your option) any later
28.14 +version.
28.15 +
28.16 +This program is distributed in the hope that it will be useful, but WITHOUT
28.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
28.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
28.19 +details.
28.20 +
28.21 +You should have received a copy of the GNU General Public License along with
28.22 +this program. If not, see <http://www.gnu.org/licenses/>.
28.23 +"""
28.24 +
28.25 +from imiptools.stores.common import StoreBase, JournalBase
28.26 +
28.27 +from datetime import datetime
28.28 +from imiptools.data import parse_string, to_string
28.29 +from imiptools.dates import format_datetime, get_datetime, to_timezone
28.30 +from imiptools.period import FreeBusyDatabaseCollection
28.31 +from imiptools.sql import DatabaseOperations
28.32 +
28.33 +class DatabaseStoreBase(DatabaseOperations):
28.34 +
28.35 + "A database store supporting user-specific locking."
28.36 +
28.37 + def __init__(self, connection, paramstyle=None):
28.38 + DatabaseOperations.__init__(self, paramstyle=paramstyle)
28.39 + self.connection = connection
28.40 + self.cursor = connection.cursor()
28.41 +
28.42 + def acquire_lock(self, user, timeout=None):
28.43 + pass
28.44 +
28.45 + def release_lock(self, user):
28.46 + pass
28.47 +
28.48 +class DatabaseStore(DatabaseStoreBase, StoreBase):
28.49 +
28.50 + "A database store of tabular free/busy data and objects."
28.51 +
28.52 + # User discovery.
28.53 +
28.54 + def get_users(self):
28.55 +
28.56 + "Return a list of users."
28.57 +
28.58 + query = "select distinct store_user from (" \
28.59 + "select store_user from freebusy " \
28.60 + "union all select store_user from objects " \
28.61 + "union all select store_user from recurrences" \
28.62 + ") as users"
28.63 + self.cursor.execute(query)
28.64 + return [r[0] for r in self.cursor.fetchall()]
28.65 +
28.66 + # Event and event metadata access.
28.67 +
28.68 + def get_all_events(self, user, dirname=None):
28.69 +
28.70 + """
28.71 + Return a set of (uid, recurrenceid) tuples for all events. Unless
28.72 + 'dirname' is specified, only active events are returned; otherwise,
28.73 + events from the given 'dirname' are returned.
28.74 + """
28.75 +
28.76 + columns, values = self.get_event_table_filters(dirname)
28.77 +
28.78 + columns += ["store_user"]
28.79 + values += [user]
28.80 +
28.81 + query, values = self.get_query(
28.82 + "select object_uid, null as object_recurrenceid from objects :condition "
28.83 + "union all "
28.84 + "select object_uid, object_recurrenceid from recurrences :condition",
28.85 + columns, values)
28.86 +
28.87 + self.cursor.execute(query, values)
28.88 + return self.cursor.fetchall()
28.89 +
28.90 + def get_events(self, user, dirname=None):
28.91 +
28.92 + "Return a list of event identifiers."
28.93 +
28.94 + columns, values = self.get_event_table_filters(dirname)
28.95 +
28.96 + columns += ["store_user"]
28.97 + values += [user]
28.98 +
28.99 + query, values = self.get_query(
28.100 + "select object_uid from objects :condition",
28.101 + columns, values)
28.102 +
28.103 + self.cursor.execute(query, values)
28.104 + return [r[0] for r in self.cursor.fetchall()]
28.105 +
28.106 + def get_cancelled_events(self, user):
28.107 +
28.108 + "Return a list of event identifiers for cancelled events."
28.109 +
28.110 + return self.get_events(user, "cancellations")
28.111 +
28.112 + def get_event(self, user, uid, recurrenceid=None, dirname=None):
28.113 +
28.114 + """
28.115 + Get the event for the given 'user' with the given 'uid'. If
28.116 + the optional 'recurrenceid' is specified, a specific instance or
28.117 + occurrence of an event is returned.
28.118 + """
28.119 +
28.120 + table = self.get_event_table(recurrenceid, dirname)
28.121 + columns, values = self.get_event_table_filters(dirname)
28.122 +
28.123 + if recurrenceid:
28.124 + columns += ["store_user", "object_uid", "object_recurrenceid"]
28.125 + values += [user, uid, recurrenceid]
28.126 + else:
28.127 + columns += ["store_user", "object_uid"]
28.128 + values += [user, uid]
28.129 +
28.130 + query, values = self.get_query(
28.131 + "select object_text from %(table)s :condition" % {
28.132 + "table" : table
28.133 + },
28.134 + columns, values)
28.135 +
28.136 + self.cursor.execute(query, values)
28.137 + result = self.cursor.fetchone()
28.138 + return result and parse_string(result[0], "utf-8")
28.139 +
28.140 + def get_complete_event(self, user, uid):
28.141 +
28.142 + "Get the event for the given 'user' with the given 'uid'."
28.143 +
28.144 + columns = ["store_user", "object_uid"]
28.145 + values = [user, uid]
28.146 +
28.147 + query, values = self.get_query(
28.148 + "select object_text from objects :condition",
28.149 + columns, values)
28.150 +
28.151 + self.cursor.execute(query, values)
28.152 + result = self.cursor.fetchone()
28.153 + return result and parse_string(result[0], "utf-8")
28.154 +
28.155 + def set_complete_event(self, user, uid, node):
28.156 +
28.157 + "Set an event for 'user' having the given 'uid' and 'node'."
28.158 +
28.159 + columns = ["store_user", "object_uid"]
28.160 + values = [user, uid]
28.161 + setcolumns = ["object_text", "status"]
28.162 + setvalues = [to_string(node, "utf-8"), "active"]
28.163 +
28.164 + query, values = self.get_query(
28.165 + "update objects :set :condition",
28.166 + columns, values, setcolumns, setvalues)
28.167 +
28.168 + self.cursor.execute(query, values)
28.169 +
28.170 + if self.cursor.rowcount > 0 or self.get_complete_event(user, uid):
28.171 + return True
28.172 +
28.173 + columns = ["store_user", "object_uid", "object_text", "status"]
28.174 + values = [user, uid, to_string(node, "utf-8"), "active"]
28.175 +
28.176 + query, values = self.get_query(
28.177 + "insert into objects (:columns) values (:values)",
28.178 + columns, values)
28.179 +
28.180 + self.cursor.execute(query, values)
28.181 + return True
28.182 +
28.183 + def remove_parent_event(self, user, uid):
28.184 +
28.185 + "Remove the parent event for 'user' having the given 'uid'."
28.186 +
28.187 + columns = ["store_user", "object_uid"]
28.188 + values = [user, uid]
28.189 +
28.190 + query, values = self.get_query(
28.191 + "delete from objects :condition",
28.192 + columns, values)
28.193 +
28.194 + self.cursor.execute(query, values)
28.195 + return self.cursor.rowcount > 0
28.196 +
28.197 + def get_active_recurrences(self, user, uid):
28.198 +
28.199 + """
28.200 + Get additional event instances for an event of the given 'user' with the
28.201 + indicated 'uid'. Cancelled recurrences are not returned.
28.202 + """
28.203 +
28.204 + columns = ["store_user", "object_uid", "status"]
28.205 + values = [user, uid, "active"]
28.206 +
28.207 + query, values = self.get_query(
28.208 + "select object_recurrenceid from recurrences :condition",
28.209 + columns, values)
28.210 +
28.211 + self.cursor.execute(query, values)
28.212 + return [t[0] for t in self.cursor.fetchall() or []]
28.213 +
28.214 + def get_cancelled_recurrences(self, user, uid):
28.215 +
28.216 + """
28.217 + Get additional event instances for an event of the given 'user' with the
28.218 + indicated 'uid'. Only cancelled recurrences are returned.
28.219 + """
28.220 +
28.221 + columns = ["store_user", "object_uid", "status"]
28.222 + values = [user, uid, "cancelled"]
28.223 +
28.224 + query, values = self.get_query(
28.225 + "select object_recurrenceid from recurrences :condition",
28.226 + columns, values)
28.227 +
28.228 + self.cursor.execute(query, values)
28.229 + return [t[0] for t in self.cursor.fetchall() or []]
28.230 +
28.231 + def get_recurrence(self, user, uid, recurrenceid):
28.232 +
28.233 + """
28.234 + For the event of the given 'user' with the given 'uid', return the
28.235 + specific recurrence indicated by the 'recurrenceid'.
28.236 + """
28.237 +
28.238 + columns = ["store_user", "object_uid", "object_recurrenceid"]
28.239 + values = [user, uid, recurrenceid]
28.240 +
28.241 + query, values = self.get_query(
28.242 + "select object_text from recurrences :condition",
28.243 + columns, values)
28.244 +
28.245 + self.cursor.execute(query, values)
28.246 + result = self.cursor.fetchone()
28.247 + return result and parse_string(result[0], "utf-8")
28.248 +
28.249 + def set_recurrence(self, user, uid, recurrenceid, node):
28.250 +
28.251 + "Set an event for 'user' having the given 'uid' and 'node'."
28.252 +
28.253 + columns = ["store_user", "object_uid", "object_recurrenceid"]
28.254 + values = [user, uid, recurrenceid]
28.255 + setcolumns = ["object_text", "status"]
28.256 + setvalues = [to_string(node, "utf-8"), "active"]
28.257 +
28.258 + query, values = self.get_query(
28.259 + "update recurrences :set :condition",
28.260 + columns, values, setcolumns, setvalues)
28.261 +
28.262 + self.cursor.execute(query, values)
28.263 +
28.264 + if self.cursor.rowcount > 0 or self.get_recurrence(user, uid, recurrenceid):
28.265 + return True
28.266 +
28.267 + columns = ["store_user", "object_uid", "object_recurrenceid", "object_text", "status"]
28.268 + values = [user, uid, recurrenceid, to_string(node, "utf-8"), "active"]
28.269 +
28.270 + query, values = self.get_query(
28.271 + "insert into recurrences (:columns) values (:values)",
28.272 + columns, values)
28.273 +
28.274 + self.cursor.execute(query, values)
28.275 + return True
28.276 +
28.277 + def remove_recurrence(self, user, uid, recurrenceid):
28.278 +
28.279 + """
28.280 + Remove a special recurrence from an event stored by 'user' having the
28.281 + given 'uid' and 'recurrenceid'.
28.282 + """
28.283 +
28.284 + columns = ["store_user", "object_uid", "object_recurrenceid"]
28.285 + values = [user, uid, recurrenceid]
28.286 +
28.287 + query, values = self.get_query(
28.288 + "delete from recurrences :condition",
28.289 + columns, values)
28.290 +
28.291 + self.cursor.execute(query, values)
28.292 + return True
28.293 +
28.294 + def remove_recurrences(self, user, uid):
28.295 +
28.296 + """
28.297 + Remove all recurrences for an event stored by 'user' having the given
28.298 + 'uid'.
28.299 + """
28.300 +
28.301 + columns = ["store_user", "object_uid"]
28.302 + values = [user, uid]
28.303 +
28.304 + query, values = self.get_query(
28.305 + "delete from recurrences :condition",
28.306 + columns, values)
28.307 +
28.308 + self.cursor.execute(query, values)
28.309 + return True
28.310 +
28.311 + # Event table computation.
28.312 +
28.313 + def get_event_table(self, recurrenceid=None, dirname=None):
28.314 +
28.315 + "Get the table providing events for any specified 'dirname'."
28.316 +
28.317 + if recurrenceid:
28.318 + return self.get_recurrence_table(dirname)
28.319 + else:
28.320 + return self.get_complete_event_table(dirname)
28.321 +
28.322 + def get_event_table_filters(self, dirname=None):
28.323 +
28.324 + "Get filter details for any specified 'dirname'."
28.325 +
28.326 + if dirname == "cancellations":
28.327 + return ["status"], ["cancelled"]
28.328 + else:
28.329 + return ["status"], ["active"]
28.330 +
28.331 + def get_complete_event_table(self, dirname=None):
28.332 +
28.333 + "Get the table providing events for any specified 'dirname'."
28.334 +
28.335 + if dirname == "counters":
28.336 + return "countered_objects"
28.337 + else:
28.338 + return "objects"
28.339 +
28.340 + def get_recurrence_table(self, dirname=None):
28.341 +
28.342 + "Get the table providing recurrences for any specified 'dirname'."
28.343 +
28.344 + if dirname == "counters":
28.345 + return "countered_recurrences"
28.346 + else:
28.347 + return "recurrences"
28.348 +
28.349 + # Free/busy period providers, upon extension of the free/busy records.
28.350 +
28.351 + def _get_freebusy_providers(self, user):
28.352 +
28.353 + """
28.354 + Return the free/busy providers for the given 'user'.
28.355 +
28.356 + This function returns any stored datetime and a list of providers as a
28.357 + 2-tuple. Each provider is itself a (uid, recurrenceid) tuple.
28.358 + """
28.359 +
28.360 + columns = ["store_user"]
28.361 + values = [user]
28.362 +
28.363 + query, values = self.get_query(
28.364 + "select object_uid, object_recurrenceid from freebusy_providers :condition",
28.365 + columns, values)
28.366 +
28.367 + self.cursor.execute(query, values)
28.368 + providers = self.cursor.fetchall()
28.369 +
28.370 + columns = ["store_user"]
28.371 + values = [user]
28.372 +
28.373 + query, values = self.get_query(
28.374 + "select start from freebusy_provider_datetimes :condition",
28.375 + columns, values)
28.376 +
28.377 + self.cursor.execute(query, values)
28.378 + result = self.cursor.fetchone()
28.379 + dt_string = result and result[0]
28.380 +
28.381 + return dt_string, providers
28.382 +
28.383 + def _set_freebusy_providers(self, user, dt_string, t):
28.384 +
28.385 + "Set the given provider timestamp 'dt_string' and table 't'."
28.386 +
28.387 + # NOTE: Locking?
28.388 +
28.389 + columns = ["store_user"]
28.390 + values = [user]
28.391 +
28.392 + query, values = self.get_query(
28.393 + "delete from freebusy_providers :condition",
28.394 + columns, values)
28.395 +
28.396 + self.cursor.execute(query, values)
28.397 +
28.398 + columns = ["store_user", "object_uid", "object_recurrenceid"]
28.399 +
28.400 + for uid, recurrenceid in t:
28.401 + values = [user, uid, recurrenceid]
28.402 +
28.403 + query, values = self.get_query(
28.404 + "insert into freebusy_providers (:columns) values (:values)",
28.405 + columns, values)
28.406 +
28.407 + self.cursor.execute(query, values)
28.408 +
28.409 + columns = ["store_user"]
28.410 + values = [user]
28.411 + setcolumns = ["start"]
28.412 + setvalues = [dt_string]
28.413 +
28.414 + query, values = self.get_query(
28.415 + "update freebusy_provider_datetimes :set :condition",
28.416 + columns, values, setcolumns, setvalues)
28.417 +
28.418 + self.cursor.execute(query, values)
28.419 +
28.420 + if self.cursor.rowcount > 0:
28.421 + return True
28.422 +
28.423 + columns = ["store_user", "start"]
28.424 + values = [user, dt_string]
28.425 +
28.426 + query, values = self.get_query(
28.427 + "insert into freebusy_provider_datetimes (:columns) values (:values)",
28.428 + columns, values)
28.429 +
28.430 + self.cursor.execute(query, values)
28.431 + return True
28.432 +
28.433 + # Free/busy period access.
28.434 +
28.435 + def get_freebusy(self, user, name=None, mutable=False):
28.436 +
28.437 + "Get free/busy details for the given 'user'."
28.438 +
28.439 + table = name or "freebusy"
28.440 + return FreeBusyDatabaseCollection(self.cursor, table, ["store_user"], [user], mutable, self.paramstyle)
28.441 +
28.442 + def get_freebusy_for_other(self, user, other, mutable=False):
28.443 +
28.444 + "For the given 'user', get free/busy details for the 'other' user."
28.445 +
28.446 + table = "freebusy_other"
28.447 + return FreeBusyDatabaseCollection(self.cursor, table, ["store_user", "other"], [user, other], mutable, self.paramstyle)
28.448 +
28.449 + def set_freebusy(self, user, freebusy, name=None):
28.450 +
28.451 + "For the given 'user', set 'freebusy' details."
28.452 +
28.453 + table = name or "freebusy"
28.454 +
28.455 + if not isinstance(freebusy, FreeBusyDatabaseCollection) or freebusy.table_name != table:
28.456 + fbc = FreeBusyDatabaseCollection(self.cursor, table, ["store_user"], [user], True, self.paramstyle)
28.457 + fbc += freebusy
28.458 +
28.459 + return True
28.460 +
28.461 + def set_freebusy_for_other(self, user, freebusy, other):
28.462 +
28.463 + "For the given 'user', set 'freebusy' details for the 'other' user."
28.464 +
28.465 + table = "freebusy_other"
28.466 +
28.467 + if not isinstance(freebusy, FreeBusyDatabaseCollection) or freebusy.table_name != table:
28.468 + fbc = FreeBusyDatabaseCollection(self.cursor, table, ["store_user", "other"], [user, other], True, self.paramstyle)
28.469 + fbc += freebusy
28.470 +
28.471 + return True
28.472 +
28.473 + def get_freebusy_others(self, user):
28.474 +
28.475 + """
28.476 + For the given 'user', return a list of other users for whom free/busy
28.477 + information is retained.
28.478 + """
28.479 +
28.480 + columns = ["store_user"]
28.481 + values = [user]
28.482 +
28.483 + query, values = self.get_query(
28.484 + "select distinct other from freebusy_other :condition",
28.485 + columns, values)
28.486 +
28.487 + self.cursor.execute(query, values)
28.488 + return [r[0] for r in self.cursor.fetchall()]
28.489 +
28.490 + # Tentative free/busy periods related to countering.
28.491 +
28.492 + def get_freebusy_offers(self, user, mutable=False):
28.493 +
28.494 + "Get free/busy offers for the given 'user'."
28.495 +
28.496 + # Expire old offers and save the collection if modified.
28.497 +
28.498 + now = format_datetime(to_timezone(datetime.utcnow(), "UTC"))
28.499 + columns = ["store_user", "expires"]
28.500 + values = [user, now]
28.501 +
28.502 + query, values = self.get_query(
28.503 + "delete from freebusy_offers :condition",
28.504 + columns, values)
28.505 +
28.506 + self.cursor.execute(query, values)
28.507 +
28.508 + return self.get_freebusy(user, "freebusy_offers", mutable)
28.509 +
28.510 + def set_freebusy_offers(self, user, freebusy):
28.511 +
28.512 + "For the given 'user', set 'freebusy' offers."
28.513 +
28.514 + return self.set_freebusy(user, freebusy, "freebusy_offers")
28.515 +
28.516 + # Requests and counter-proposals.
28.517 +
28.518 + def get_requests(self, user):
28.519 +
28.520 + "Get requests for the given 'user'."
28.521 +
28.522 + columns = ["store_user"]
28.523 + values = [user]
28.524 +
28.525 + query, values = self.get_query(
28.526 + "select object_uid, object_recurrenceid, request_type from requests :condition",
28.527 + columns, values)
28.528 +
28.529 + self.cursor.execute(query, values)
28.530 + return self.cursor.fetchall()
28.531 +
28.532 + def set_request(self, user, uid, recurrenceid=None, type=None):
28.533 +
28.534 + """
28.535 + For the given 'user', set the queued 'uid' and 'recurrenceid',
28.536 + indicating a request, along with any given 'type'.
28.537 + """
28.538 +
28.539 + columns = ["store_user", "object_uid", "object_recurrenceid", "request_type"]
28.540 + values = [user, uid, recurrenceid, type]
28.541 +
28.542 + query, values = self.get_query(
28.543 + "insert into requests (:columns) values (:values)",
28.544 + columns, values)
28.545 +
28.546 + self.cursor.execute(query, values)
28.547 + return True
28.548 +
28.549 + def queue_request(self, user, uid, recurrenceid=None, type=None):
28.550 +
28.551 + """
28.552 + Queue a request for 'user' having the given 'uid'. If the optional
28.553 + 'recurrenceid' is specified, the entry refers to a specific instance
28.554 + or occurrence of an event. The 'type' parameter can be used to indicate
28.555 + a specific type of request.
28.556 + """
28.557 +
28.558 + if recurrenceid:
28.559 + columns = ["store_user", "object_uid", "object_recurrenceid"]
28.560 + values = [user, uid, recurrenceid]
28.561 + else:
28.562 + columns = ["store_user", "object_uid"]
28.563 + values = [user, uid]
28.564 +
28.565 + setcolumns = ["request_type"]
28.566 + setvalues = [type]
28.567 +
28.568 + query, values = self.get_query(
28.569 + "update requests :set :condition",
28.570 + columns, values, setcolumns, setvalues)
28.571 +
28.572 + self.cursor.execute(query, values)
28.573 +
28.574 + if self.cursor.rowcount > 0:
28.575 + return
28.576 +
28.577 + self.set_request(user, uid, recurrenceid, type)
28.578 +
28.579 + def dequeue_request(self, user, uid, recurrenceid=None):
28.580 +
28.581 + """
28.582 + Dequeue all requests for 'user' having the given 'uid'. If the optional
28.583 + 'recurrenceid' is specified, all requests for that specific instance or
28.584 + occurrence of an event are dequeued.
28.585 + """
28.586 +
28.587 + if recurrenceid:
28.588 + columns = ["store_user", "object_uid", "object_recurrenceid"]
28.589 + values = [user, uid, recurrenceid]
28.590 + else:
28.591 + columns = ["store_user", "object_uid"]
28.592 + values = [user, uid]
28.593 +
28.594 + query, values = self.get_query(
28.595 + "delete from requests :condition",
28.596 + columns, values)
28.597 +
28.598 + self.cursor.execute(query, values)
28.599 + return True
28.600 +
28.601 + def get_counters(self, user, uid, recurrenceid=None):
28.602 +
28.603 + """
28.604 + For the given 'user', return a list of users from whom counter-proposals
28.605 + have been received for the given 'uid' and optional 'recurrenceid'.
28.606 + """
28.607 +
28.608 + table = self.get_event_table(recurrenceid, "counters")
28.609 +
28.610 + if recurrenceid:
28.611 + columns = ["store_user", "object_uid", "object_recurrenceid"]
28.612 + values = [user, uid, recurrenceid]
28.613 + else:
28.614 + columns = ["store_user", "object_uid"]
28.615 + values = [user, uid]
28.616 +
28.617 + query, values = self.get_query(
28.618 + "select other from %(table)s :condition" % {
28.619 + "table" : table
28.620 + },
28.621 + columns, values)
28.622 +
28.623 + self.cursor.execute(query, values)
28.624 + return [r[0] for r in self.cursor.fetchall()]
28.625 +
28.626 + def get_counter(self, user, other, uid, recurrenceid=None):
28.627 +
28.628 + """
28.629 + For the given 'user', return the counter-proposal from 'other' for the
28.630 + given 'uid' and optional 'recurrenceid'.
28.631 + """
28.632 +
28.633 + table = self.get_event_table(recurrenceid, "counters")
28.634 +
28.635 + if recurrenceid:
28.636 + columns = ["store_user", "other", "object_uid", "object_recurrenceid"]
28.637 + values = [user, other, uid, recurrenceid]
28.638 + else:
28.639 + columns = ["store_user", "other", "object_uid"]
28.640 + values = [user, other, uid]
28.641 +
28.642 + query, values = self.get_query(
28.643 + "select object_text from %(table)s :condition" % {
28.644 + "table" : table
28.645 + },
28.646 + columns, values)
28.647 +
28.648 + self.cursor.execute(query, values)
28.649 + result = self.cursor.fetchone()
28.650 + return result and parse_string(result[0], "utf-8")
28.651 +
28.652 + def set_counter(self, user, other, node, uid, recurrenceid=None):
28.653 +
28.654 + """
28.655 + For the given 'user', store a counter-proposal received from 'other' the
28.656 + given 'node' representing that proposal for the given 'uid' and
28.657 + 'recurrenceid'.
28.658 + """
28.659 +
28.660 + table = self.get_event_table(recurrenceid, "counters")
28.661 +
28.662 + if recurrenceid:
28.663 + columns = ["store_user", "other", "object_uid", "object_recurrenceid", "object_text"]
28.664 + values = [user, other, uid, recurrenceid, to_string(node, "utf-8")]
28.665 + else:
28.666 + columns = ["store_user", "other", "object_uid", "object_text"]
28.667 + values = [user, other, uid, to_string(node, "utf-8")]
28.668 +
28.669 + query, values = self.get_query(
28.670 + "insert into %(table)s (:columns) values (:values)" % {
28.671 + "table" : table
28.672 + },
28.673 + columns, values)
28.674 +
28.675 + self.cursor.execute(query, values)
28.676 + return True
28.677 +
28.678 + def remove_counters(self, user, uid, recurrenceid=None):
28.679 +
28.680 + """
28.681 + For the given 'user', remove all counter-proposals associated with the
28.682 + given 'uid' and 'recurrenceid'.
28.683 + """
28.684 +
28.685 + table = self.get_event_table(recurrenceid, "counters")
28.686 +
28.687 + if recurrenceid:
28.688 + columns = ["store_user", "object_uid", "object_recurrenceid"]
28.689 + values = [user, uid, recurrenceid]
28.690 + else:
28.691 + columns = ["store_user", "object_uid"]
28.692 + values = [user, uid]
28.693 +
28.694 + query, values = self.get_query(
28.695 + "delete from %(table)s :condition" % {
28.696 + "table" : table
28.697 + },
28.698 + columns, values)
28.699 +
28.700 + self.cursor.execute(query, values)
28.701 + return True
28.702 +
28.703 + def remove_counter(self, user, other, uid, recurrenceid=None):
28.704 +
28.705 + """
28.706 + For the given 'user', remove any counter-proposal from 'other'
28.707 + associated with the given 'uid' and 'recurrenceid'.
28.708 + """
28.709 +
28.710 + table = self.get_event_table(recurrenceid, "counters")
28.711 +
28.712 + if recurrenceid:
28.713 + columns = ["store_user", "other", "object_uid", "object_recurrenceid"]
28.714 + values = [user, other, uid, recurrenceid]
28.715 + else:
28.716 + columns = ["store_user", "other", "object_uid"]
28.717 + values = [user, other, uid]
28.718 +
28.719 + query, values = self.get_query(
28.720 + "delete from %(table)s :condition" % {
28.721 + "table" : table
28.722 + },
28.723 + columns, values)
28.724 +
28.725 + self.cursor.execute(query, values)
28.726 + return True
28.727 +
28.728 + # Event cancellation.
28.729 +
28.730 + def cancel_event(self, user, uid, recurrenceid=None):
28.731 +
28.732 + """
28.733 + Cancel an event for 'user' having the given 'uid'. If the optional
28.734 + 'recurrenceid' is specified, a specific instance or occurrence of an
28.735 + event is cancelled.
28.736 + """
28.737 +
28.738 + table = self.get_event_table(recurrenceid)
28.739 +
28.740 + if recurrenceid:
28.741 + columns = ["store_user", "object_uid", "object_recurrenceid"]
28.742 + values = [user, uid, recurrenceid]
28.743 + else:
28.744 + columns = ["store_user", "object_uid"]
28.745 + values = [user, uid]
28.746 +
28.747 + setcolumns = ["status"]
28.748 + setvalues = ["cancelled"]
28.749 +
28.750 + query, values = self.get_query(
28.751 + "update %(table)s :set :condition" % {
28.752 + "table" : table
28.753 + },
28.754 + columns, values, setcolumns, setvalues)
28.755 +
28.756 + self.cursor.execute(query, values)
28.757 + return True
28.758 +
28.759 + def uncancel_event(self, user, uid, recurrenceid=None):
28.760 +
28.761 + """
28.762 + Uncancel an event for 'user' having the given 'uid'. If the optional
28.763 + 'recurrenceid' is specified, a specific instance or occurrence of an
28.764 + event is uncancelled.
28.765 + """
28.766 +
28.767 + table = self.get_event_table(recurrenceid)
28.768 +
28.769 + if recurrenceid:
28.770 + columns = ["store_user", "object_uid", "object_recurrenceid"]
28.771 + values = [user, uid, recurrenceid]
28.772 + else:
28.773 + columns = ["store_user", "object_uid"]
28.774 + values = [user, uid]
28.775 +
28.776 + setcolumns = ["status"]
28.777 + setvalues = ["active"]
28.778 +
28.779 + query, values = self.get_query(
28.780 + "update %(table)s :set :condition" % {
28.781 + "table" : table
28.782 + },
28.783 + columns, values, setcolumns, setvalues)
28.784 +
28.785 + self.cursor.execute(query, values)
28.786 + return True
28.787 +
28.788 + def remove_cancellation(self, user, uid, recurrenceid=None):
28.789 +
28.790 + """
28.791 + Remove a cancellation for 'user' for the event having the given 'uid'.
28.792 + If the optional 'recurrenceid' is specified, a specific instance or
28.793 + occurrence of an event is affected.
28.794 + """
28.795 +
28.796 + table = self.get_event_table(recurrenceid)
28.797 +
28.798 + if recurrenceid:
28.799 + columns = ["store_user", "object_uid", "object_recurrenceid", "status"]
28.800 + values = [user, uid, recurrenceid, "cancelled"]
28.801 + else:
28.802 + columns = ["store_user", "object_uid", "status"]
28.803 + values = [user, uid, "cancelled"]
28.804 +
28.805 + query, values = self.get_query(
28.806 + "delete from %(table)s :condition" % {
28.807 + "table" : table
28.808 + },
28.809 + columns, values)
28.810 +
28.811 + self.cursor.execute(query, values)
28.812 + return True
28.813 +
28.814 +class DatabaseJournal(DatabaseStoreBase, JournalBase):
28.815 +
28.816 + "A journal system to support quotas."
28.817 +
28.818 + # Quota and user identity/group discovery.
28.819 +
28.820 + def get_quotas(self):
28.821 +
28.822 + "Return a list of quotas."
28.823 +
28.824 + query = "select distinct quota from (" \
28.825 + "select quota from quota_freebusy " \
28.826 + "union all select quota from quota_limits" \
28.827 + ") as quotas"
28.828 + self.cursor.execute(query)
28.829 + return [r[0] for r in self.cursor.fetchall()]
28.830 +
28.831 + def get_quota_users(self, quota):
28.832 +
28.833 + "Return a list of quota users."
28.834 +
28.835 + columns = ["quota"]
28.836 + values = [quota]
28.837 +
28.838 + query, values = self.get_query(
28.839 + "select distinct user_group from quota_freebusy :condition",
28.840 + columns, values)
28.841 +
28.842 + self.cursor.execute(query, values)
28.843 + return [r[0] for r in self.cursor.fetchall()]
28.844 +
28.845 + # Groups of users sharing quotas.
28.846 +
28.847 + def get_groups(self, quota):
28.848 +
28.849 + "Return the identity mappings for the given 'quota' as a dictionary."
28.850 +
28.851 + columns = ["quota"]
28.852 + values = [quota]
28.853 +
28.854 + query, values = self.get_query(
28.855 + "select store_user, user_group from user_groups :condition",
28.856 + columns, values)
28.857 +
28.858 + self.cursor.execute(query, values)
28.859 + return dict(self.cursor.fetchall())
28.860 +
28.861 + def set_group(self, quota, store_user, user_group):
28.862 +
28.863 + """
28.864 + For the given 'quota', set a mapping from 'store_user' to 'user_group'.
28.865 + """
28.866 +
28.867 + columns = ["quota", "store_user"]
28.868 + values = [quota, store_user]
28.869 + setcolumns = ["user_group"]
28.870 + setvalues = [user_group]
28.871 +
28.872 + query, values = self.get_query(
28.873 + "update user_groups :set :condition",
28.874 + columns, values, setcolumns, setvalues)
28.875 +
28.876 + self.cursor.execute(query, values)
28.877 +
28.878 + if self.cursor.rowcount > 0:
28.879 + return True
28.880 +
28.881 + columns = ["quota", "store_user", "user_group"]
28.882 + values = [quota, store_user, user_group]
28.883 +
28.884 + query, values = self.get_query(
28.885 + "insert into user_groups (:columns) values (:values)",
28.886 + columns, values)
28.887 +
28.888 + self.cursor.execute(query, values)
28.889 + return True
28.890 +
28.891 + def get_limits(self, quota):
28.892 +
28.893 + """
28.894 + Return the limits for the 'quota' as a dictionary mapping identities or
28.895 + groups to durations.
28.896 + """
28.897 +
28.898 + columns = ["quota"]
28.899 + values = [quota]
28.900 +
28.901 + query, values = self.get_query(
28.902 + "select user_group, quota_limit from quota_limits :condition",
28.903 + columns, values)
28.904 +
28.905 + self.cursor.execute(query, values)
28.906 + return dict(self.cursor.fetchall())
28.907 +
28.908 + def set_limit(self, quota, group, limit):
28.909 +
28.910 + """
28.911 + For the given 'quota', set for a user 'group' the given 'limit' on
28.912 + resource usage.
28.913 + """
28.914 +
28.915 + columns = ["quota", "user_group"]
28.916 + values = [quota, group]
28.917 + setcolumns = ["quota_limit"]
28.918 + setvalues = [limit]
28.919 +
28.920 + query, values = self.get_query(
28.921 + "update quota_limits :set :condition",
28.922 + columns, values, setcolumns, setvalues)
28.923 +
28.924 + self.cursor.execute(query, values)
28.925 +
28.926 + if self.cursor.rowcount > 0:
28.927 + return True
28.928 +
28.929 + columns = ["quota", "user_group", "quota_limit"]
28.930 + values = [quota, group, limit]
28.931 +
28.932 + query, values = self.get_query(
28.933 + "insert into quota_limits (:columns) values (:values)",
28.934 + columns, values)
28.935 +
28.936 + self.cursor.execute(query, values)
28.937 + return True
28.938 +
28.939 + # Free/busy period access for users within quota groups.
28.940 +
28.941 + def get_freebusy_users(self, quota):
28.942 +
28.943 + """
28.944 + Return a list of users whose free/busy details are retained for the
28.945 + given 'quota'.
28.946 + """
28.947 +
28.948 + columns = ["quota"]
28.949 + values = [quota]
28.950 +
28.951 + query, values = self.get_query(
28.952 + "select distinct store_user from user_freebusy :condition",
28.953 + columns, values)
28.954 +
28.955 + self.cursor.execute(query, values)
28.956 + return [r[0] for r in self.cursor.fetchall()]
28.957 +
28.958 + def get_freebusy(self, quota, user, mutable=False):
28.959 +
28.960 + "Get free/busy details for the given 'quota' and 'user'."
28.961 +
28.962 + table = "user_freebusy"
28.963 + return FreeBusyDatabaseCollection(self.cursor, table, ["quota", "store_user"], [quota, user], mutable, self.paramstyle)
28.964 +
28.965 + def set_freebusy(self, quota, user, freebusy):
28.966 +
28.967 + "For the given 'quota' and 'user', set 'freebusy' details."
28.968 +
28.969 + table = "user_freebusy"
28.970 +
28.971 + if not isinstance(freebusy, FreeBusyDatabaseCollection) or freebusy.table_name != table:
28.972 + fbc = FreeBusyDatabaseCollection(self.cursor, table, ["quota", "store_user"], [quota, user], True, self.paramstyle)
28.973 + fbc += freebusy
28.974 +
28.975 + return True
28.976 +
28.977 + # Journal entry methods.
28.978 +
28.979 + def get_entries(self, quota, group, mutable=False):
28.980 +
28.981 + """
28.982 + Return a list of journal entries for the given 'quota' for the indicated
28.983 + 'group'.
28.984 + """
28.985 +
28.986 + table = "quota_freebusy"
28.987 + return FreeBusyDatabaseCollection(self.cursor, table, ["quota", "user_group"], [quota, group], mutable, self.paramstyle)
28.988 +
28.989 + def set_entries(self, quota, group, entries):
28.990 +
28.991 + """
28.992 + For the given 'quota' and indicated 'group', set the list of journal
28.993 + 'entries'.
28.994 + """
28.995 +
28.996 + table = "quota_freebusy"
28.997 +
28.998 + if not isinstance(entries, FreeBusyDatabaseCollection) or entries.table_name != table:
28.999 + fbc = FreeBusyDatabaseCollection(self.cursor, table, ["quota", "user_group"], [quota, group], True, self.paramstyle)
28.1000 + fbc += entries
28.1001 +
28.1002 + return True
28.1003 +
28.1004 +# vim: tabstop=4 expandtab shiftwidth=4
29.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
29.2 +++ b/imiptools/stores/database/postgresql.py Fri Apr 22 16:22:58 2016 +0200
29.3 @@ -0,0 +1,66 @@
29.4 +#!/usr/bin/env python
29.5 +
29.6 +"""
29.7 +A PostgreSQL database store of calendar data.
29.8 +
29.9 +Copyright (C) 2016 Paul Boddie <paul@boddie.org.uk>
29.10 +
29.11 +This program is free software; you can redistribute it and/or modify it under
29.12 +the terms of the GNU General Public License as published by the Free Software
29.13 +Foundation; either version 3 of the License, or (at your option) any later
29.14 +version.
29.15 +
29.16 +This program is distributed in the hope that it will be useful, but WITHOUT
29.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
29.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
29.19 +details.
29.20 +
29.21 +You should have received a copy of the GNU General Public License along with
29.22 +this program. If not, see <http://www.gnu.org/licenses/>.
29.23 +"""
29.24 +
29.25 +from imiptools.config import STORE_DIR, JOURNAL_DIR
29.26 +from imiptools.stores.database.common import DatabaseStore, DatabaseJournal
29.27 +import psycopg2
29.28 +
29.29 +class Store(DatabaseStore):
29.30 +
29.31 + "A PostgreSQL database store of calendar objects and free/busy data."
29.32 +
29.33 + def __init__(self, store_dir=None):
29.34 +
29.35 + "Interpret 'store_dir' as a connection string."
29.36 +
29.37 + connection = psycopg2.connect(store_dir or STORE_DIR)
29.38 + connection.autocommit = True
29.39 + DatabaseStore.__init__(self, connection, psycopg2.paramstyle)
29.40 +
29.41 + def acquire_lock(self, user, timeout=None):
29.42 + query = "select pg_advisory_lock(20160311)"
29.43 + self.cursor.execute(query)
29.44 +
29.45 + def release_lock(self, user):
29.46 + query = "select pg_advisory_unlock(20160311)"
29.47 + self.cursor.execute(query)
29.48 +
29.49 +class Journal(DatabaseJournal):
29.50 +
29.51 + "A PostgreSQL journal system supporting quotas."
29.52 +
29.53 + def __init__(self, store_dir=None):
29.54 +
29.55 + "Interpret 'store_dir' as a connection string."
29.56 +
29.57 + connection = psycopg2.connect(store_dir or JOURNAL_DIR)
29.58 + connection.autocommit = True
29.59 + DatabaseJournal.__init__(self, connection, psycopg2.paramstyle)
29.60 +
29.61 + def acquire_lock(self, user, timeout=None):
29.62 + query = "select pg_advisory_lock(20160312)"
29.63 + self.cursor.execute(query)
29.64 +
29.65 + def release_lock(self, user):
29.66 + query = "select pg_advisory_unlock(20160312)"
29.67 + self.cursor.execute(query)
29.68 +
29.69 +# vim: tabstop=4 expandtab shiftwidth=4
30.1 --- a/imiptools/stores/file.py Tue Apr 19 21:20:57 2016 +0200
30.2 +++ b/imiptools/stores/file.py Fri Apr 22 16:22:58 2016 +0200
30.3 @@ -19,14 +19,14 @@
30.4 this program. If not, see <http://www.gnu.org/licenses/>.
30.5 """
30.6
30.7 -from imiptools.stores import StoreBase, PublisherBase, JournalBase
30.8 +from imiptools.stores.common import StoreBase, PublisherBase, JournalBase
30.9
30.10 from datetime import datetime
30.11 from imiptools.config import STORE_DIR, PUBLISH_DIR, JOURNAL_DIR
30.12 from imiptools.data import make_calendar, parse_object, to_stream
30.13 from imiptools.dates import format_datetime, get_datetime, to_timezone
30.14 from imiptools.filesys import fix_permissions, FileBase
30.15 -from imiptools.period import FreeBusyPeriod
30.16 +from imiptools.period import FreeBusyPeriod, FreeBusyCollection
30.17 from imiptools.text import parse_line
30.18 from os.path import isdir, isfile, join
30.19 from os import listdir, remove, rmdir
30.20 @@ -148,7 +148,7 @@
30.21 finally:
30.22 self.release_lock(user)
30.23
30.24 -class FileStore(FileStoreBase, StoreBase):
30.25 +class Store(FileStoreBase, StoreBase):
30.26
30.27 "A file store of tabular free/busy data and objects."
30.28
30.29 @@ -232,25 +232,19 @@
30.30
30.31 filename = self.get_object_in_store(user, "objects")
30.32 if not filename or not isdir(filename):
30.33 - return None
30.34 + return []
30.35
30.36 return [name for name in listdir(filename) if isfile(join(filename, name))]
30.37
30.38 - def get_event_filename(self, user, uid, recurrenceid=None, dirname=None, username=None):
30.39 + def get_cancelled_events(self, user):
30.40
30.41 - """
30.42 - Get the filename providing the event for the given 'user' with the given
30.43 - 'uid'. If the optional 'recurrenceid' is specified, a specific instance
30.44 - or occurrence of an event is returned.
30.45 + "Return a list of event identifiers for cancelled events."
30.46
30.47 - Where 'dirname' is specified, the given directory name is used as the
30.48 - base of the location within which any filename will reside.
30.49 - """
30.50 + filename = self.get_object_in_store(user, "cancellations", "objects")
30.51 + if not filename or not isdir(filename):
30.52 + return []
30.53
30.54 - if recurrenceid:
30.55 - return self.get_recurrence_filename(user, uid, recurrenceid, dirname, username)
30.56 - else:
30.57 - return self.get_complete_event_filename(user, uid, dirname, username)
30.58 + return [name for name in listdir(filename) if isfile(join(filename, name))]
30.59
30.60 def get_event(self, user, uid, recurrenceid=None, dirname=None):
30.61
30.62 @@ -266,21 +260,6 @@
30.63
30.64 return filename and self._get_object(user, filename)
30.65
30.66 - def get_complete_event_filename(self, user, uid, dirname=None, username=None):
30.67 -
30.68 - """
30.69 - Get the filename providing the event for the given 'user' with the given
30.70 - 'uid'.
30.71 -
30.72 - Where 'dirname' is specified, the given directory name is used as the
30.73 - base of the location within which any filename will reside.
30.74 -
30.75 - Where 'username' is specified, the event details will reside in a file
30.76 - bearing that name within a directory having 'uid' as its name.
30.77 - """
30.78 -
30.79 - return self.get_object_in_store(user, dirname, "objects", uid, username)
30.80 -
30.81 def get_complete_event(self, user, uid):
30.82
30.83 "Get the event for the given 'user' with the given 'uid'."
30.84 @@ -346,21 +325,6 @@
30.85
30.86 return [name for name in listdir(filename) if isfile(join(filename, name))]
30.87
30.88 - def get_recurrence_filename(self, user, uid, recurrenceid, dirname=None, username=None):
30.89 -
30.90 - """
30.91 - For the event of the given 'user' with the given 'uid', return the
30.92 - filename providing the recurrence with the given 'recurrenceid'.
30.93 -
30.94 - Where 'dirname' is specified, the given directory name is used as the
30.95 - base of the location within which any filename will reside.
30.96 -
30.97 - Where 'username' is specified, the event details will reside in a file
30.98 - bearing that name within a directory having 'uid' as its name.
30.99 - """
30.100 -
30.101 - return self.get_object_in_store(user, dirname, "recurrences", uid, recurrenceid, username)
30.102 -
30.103 def get_recurrence(self, user, uid, recurrenceid):
30.104
30.105 """
30.106 @@ -410,6 +374,54 @@
30.107
30.108 return True
30.109
30.110 + # Event filename computation.
30.111 +
30.112 + def get_event_filename(self, user, uid, recurrenceid=None, dirname=None, username=None):
30.113 +
30.114 + """
30.115 + Get the filename providing the event for the given 'user' with the given
30.116 + 'uid'. If the optional 'recurrenceid' is specified, a specific instance
30.117 + or occurrence of an event is returned.
30.118 +
30.119 + Where 'dirname' is specified, the given directory name is used as the
30.120 + base of the location within which any filename will reside.
30.121 + """
30.122 +
30.123 + if recurrenceid:
30.124 + return self.get_recurrence_filename(user, uid, recurrenceid, dirname, username)
30.125 + else:
30.126 + return self.get_complete_event_filename(user, uid, dirname, username)
30.127 +
30.128 + def get_recurrence_filename(self, user, uid, recurrenceid, dirname=None, username=None):
30.129 +
30.130 + """
30.131 + For the event of the given 'user' with the given 'uid', return the
30.132 + filename providing the recurrence with the given 'recurrenceid'.
30.133 +
30.134 + Where 'dirname' is specified, the given directory name is used as the
30.135 + base of the location within which any filename will reside.
30.136 +
30.137 + Where 'username' is specified, the event details will reside in a file
30.138 + bearing that name within a directory having 'uid' as its name.
30.139 + """
30.140 +
30.141 + return self.get_object_in_store(user, dirname, "recurrences", uid, recurrenceid, username)
30.142 +
30.143 + def get_complete_event_filename(self, user, uid, dirname=None, username=None):
30.144 +
30.145 + """
30.146 + Get the filename providing the event for the given 'user' with the given
30.147 + 'uid'.
30.148 +
30.149 + Where 'dirname' is specified, the given directory name is used as the
30.150 + base of the location within which any filename will reside.
30.151 +
30.152 + Where 'username' is specified, the event details will reside in a file
30.153 + bearing that name within a directory having 'uid' as its name.
30.154 + """
30.155 +
30.156 + return self.get_object_in_store(user, dirname, "objects", uid, username)
30.157 +
30.158 # Free/busy period providers, upon extension of the free/busy records.
30.159
30.160 def _get_freebusy_providers(self, user):
30.161 @@ -450,28 +462,34 @@
30.162
30.163 # Free/busy period access.
30.164
30.165 - def get_freebusy(self, user, name=None):
30.166 + def get_freebusy(self, user, name=None, mutable=False):
30.167
30.168 "Get free/busy details for the given 'user'."
30.169
30.170 filename = self.get_object_in_store(user, name or "freebusy")
30.171 +
30.172 if not filename or not isfile(filename):
30.173 - return []
30.174 + periods = []
30.175 else:
30.176 - return map(lambda t: FreeBusyPeriod(*t),
30.177 + periods = map(lambda t: FreeBusyPeriod(*t),
30.178 self._get_table_atomic(user, filename))
30.179
30.180 - def get_freebusy_for_other(self, user, other):
30.181 + return FreeBusyCollection(periods, mutable)
30.182 +
30.183 + def get_freebusy_for_other(self, user, other, mutable=False):
30.184
30.185 "For the given 'user', get free/busy details for the 'other' user."
30.186
30.187 filename = self.get_object_in_store(user, "freebusy-other", other)
30.188 +
30.189 if not filename or not isfile(filename):
30.190 - return []
30.191 + periods = []
30.192 else:
30.193 - return map(lambda t: FreeBusyPeriod(*t),
30.194 + periods = map(lambda t: FreeBusyPeriod(*t),
30.195 self._get_table_atomic(user, filename))
30.196
30.197 + return FreeBusyCollection(periods, mutable)
30.198 +
30.199 def set_freebusy(self, user, freebusy, name=None):
30.200
30.201 "For the given 'user', set 'freebusy' details."
30.202 @@ -481,7 +499,7 @@
30.203 return False
30.204
30.205 self._set_table_atomic(user, filename,
30.206 - map(lambda fb: fb.as_tuple(strings_only=True), freebusy))
30.207 + map(lambda fb: fb.as_tuple(strings_only=True), list(freebusy)))
30.208 return True
30.209
30.210 def set_freebusy_for_other(self, user, freebusy, other):
30.211 @@ -493,12 +511,26 @@
30.212 return False
30.213
30.214 self._set_table_atomic(user, filename,
30.215 - map(lambda fb: fb.as_tuple(strings_only=True), freebusy))
30.216 + map(lambda fb: fb.as_tuple(strings_only=True), list(freebusy)))
30.217 return True
30.218
30.219 + def get_freebusy_others(self, user):
30.220 +
30.221 + """
30.222 + For the given 'user', return a list of other users for whom free/busy
30.223 + information is retained.
30.224 + """
30.225 +
30.226 + filename = self.get_object_in_store(user, "freebusy-other")
30.227 +
30.228 + if not filename or not isdir(filename):
30.229 + return []
30.230 +
30.231 + return listdir(filename)
30.232 +
30.233 # Tentative free/busy periods related to countering.
30.234
30.235 - def get_freebusy_offers(self, user):
30.236 + def get_freebusy_offers(self, user, mutable=False):
30.237
30.238 "Get free/busy offers for the given 'user'."
30.239
30.240 @@ -522,7 +554,7 @@
30.241 finally:
30.242 self.release_lock(user)
30.243
30.244 - return offers
30.245 + return FreeBusyCollection(offers, mutable)
30.246
30.247 # Requests and counter-proposals.
30.248
30.249 @@ -532,7 +564,7 @@
30.250
30.251 filename = self.get_object_in_store(user, queue)
30.252 if not filename or not isfile(filename):
30.253 - return None
30.254 + return []
30.255
30.256 return self._get_table_atomic(user, filename, [(1, None), (2, None)])
30.257
30.258 @@ -603,7 +635,7 @@
30.259
30.260 filename = self.get_event_filename(user, uid, recurrenceid, "counters")
30.261 if not filename or not isdir(filename):
30.262 - return False
30.263 + return []
30.264
30.265 return [name for name in listdir(filename) if isfile(join(filename, name))]
30.266
30.267 @@ -615,8 +647,8 @@
30.268 """
30.269
30.270 filename = self.get_event_filename(user, uid, recurrenceid, "counters", other)
30.271 - if not filename:
30.272 - return False
30.273 + if not filename or not isfile(filename):
30.274 + return None
30.275
30.276 return self._get_object(user, filename)
30.277
30.278 @@ -718,7 +750,7 @@
30.279
30.280 return False
30.281
30.282 -class FilePublisher(FileBase, PublisherBase):
30.283 +class Publisher(FileBase, PublisherBase):
30.284
30.285 "A publisher of objects."
30.286
30.287 @@ -754,7 +786,7 @@
30.288
30.289 return True
30.290
30.291 -class FileJournal(FileStoreBase, JournalBase):
30.292 +class Journal(FileStoreBase, JournalBase):
30.293
30.294 "A journal system to support quotas."
30.295
30.296 @@ -791,6 +823,22 @@
30.297
30.298 return dict(self._get_table_atomic(quota, filename, tab_separated=False))
30.299
30.300 + def set_group(self, quota, store_user, user_group):
30.301 +
30.302 + """
30.303 + For the given 'quota', set a mapping from 'store_user' to 'user_group'.
30.304 + """
30.305 +
30.306 + filename = self.get_object_in_store(quota, "groups")
30.307 + if not filename:
30.308 + return False
30.309 +
30.310 + groups = self.get_groups(quota) or {}
30.311 + groups[store_user] = user_group
30.312 +
30.313 + self._set_table_atomic(quota, filename, groups.items())
30.314 + return True
30.315 +
30.316 def get_limits(self, quota):
30.317
30.318 """
30.319 @@ -800,22 +848,55 @@
30.320
30.321 filename = self.get_object_in_store(quota, "limits")
30.322 if not filename or not isfile(filename):
30.323 - return None
30.324 + return {}
30.325
30.326 return dict(self._get_table_atomic(quota, filename, tab_separated=False))
30.327
30.328 + def set_limit(self, quota, group, limit):
30.329 +
30.330 + """
30.331 + For the given 'quota', set for a user 'group' the given 'limit' on
30.332 + resource usage.
30.333 + """
30.334 +
30.335 + filename = self.get_object_in_store(quota, "limits")
30.336 + if not filename:
30.337 + return False
30.338 +
30.339 + limits = self.get_limits(quota) or {}
30.340 + limits[group] = limit
30.341 +
30.342 + self._set_table_atomic(quota, filename, limits.items())
30.343 + return True
30.344 +
30.345 # Free/busy period access for users within quota groups.
30.346
30.347 - def get_freebusy(self, quota, user):
30.348 + def get_freebusy_users(self, quota):
30.349 +
30.350 + """
30.351 + Return a list of users whose free/busy details are retained for the
30.352 + given 'quota'.
30.353 + """
30.354 +
30.355 + filename = self.get_object_in_store(quota, "freebusy")
30.356 + if not filename or not isdir(filename):
30.357 + return []
30.358 +
30.359 + return listdir(filename)
30.360 +
30.361 + def get_freebusy(self, quota, user, mutable=False):
30.362
30.363 "Get free/busy details for the given 'quota' and 'user'."
30.364
30.365 filename = self.get_object_in_store(quota, "freebusy", user)
30.366 - if not filename or not isfile(filename):
30.367 - return []
30.368
30.369 - return map(lambda t: FreeBusyPeriod(*t),
30.370 - self._get_table_atomic(quota, filename))
30.371 + if not filename or not isfile(filename):
30.372 + periods = []
30.373 + else:
30.374 + periods = map(lambda t: FreeBusyPeriod(*t),
30.375 + self._get_table_atomic(quota, filename))
30.376 +
30.377 + return FreeBusyCollection(periods, mutable)
30.378
30.379 def set_freebusy(self, quota, user, freebusy):
30.380
30.381 @@ -826,12 +907,12 @@
30.382 return False
30.383
30.384 self._set_table_atomic(quota, filename,
30.385 - map(lambda fb: fb.as_tuple(strings_only=True), freebusy))
30.386 + map(lambda fb: fb.as_tuple(strings_only=True), list(freebusy)))
30.387 return True
30.388
30.389 # Journal entry methods.
30.390
30.391 - def get_entries(self, quota, group):
30.392 + def get_entries(self, quota, group, mutable=False):
30.393
30.394 """
30.395 Return a list of journal entries for the given 'quota' for the indicated
30.396 @@ -839,11 +920,14 @@
30.397 """
30.398
30.399 filename = self.get_object_in_store(quota, "journal", group)
30.400 - if not filename or not isfile(filename):
30.401 - return []
30.402
30.403 - return map(lambda t: FreeBusyPeriod(*t),
30.404 - self._get_table_atomic(quota, filename))
30.405 + if not filename or not isfile(filename):
30.406 + periods = []
30.407 + else:
30.408 + periods = map(lambda t: FreeBusyPeriod(*t),
30.409 + self._get_table_atomic(quota, filename))
30.410 +
30.411 + return FreeBusyCollection(periods, mutable)
30.412
30.413 def set_entries(self, quota, group, entries):
30.414
30.415 @@ -857,7 +941,7 @@
30.416 return False
30.417
30.418 self._set_table_atomic(quota, filename,
30.419 - map(lambda fb: fb.as_tuple(strings_only=True), entries))
30.420 + map(lambda fb: fb.as_tuple(strings_only=True), list(entries)))
30.421 return True
30.422
30.423 # vim: tabstop=4 expandtab shiftwidth=4
31.1 --- a/imipweb/calendar.py Tue Apr 19 21:20:57 2016 +0200
31.2 +++ b/imipweb/calendar.py Fri Apr 22 16:22:58 2016 +0200
31.3 @@ -3,7 +3,7 @@
31.4 """
31.5 A Web interface to an event calendar.
31.6
31.7 -Copyright (C) 2014, 2015 Paul Boddie <paul@boddie.org.uk>
31.8 +Copyright (C) 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk>
31.9
31.10 This program is free software; you can redistribute it and/or modify it under
31.11 the terms of the GNU General Public License as published by the Free Software
31.12 @@ -26,7 +26,6 @@
31.13 get_start_of_next_day, get_timestamp, ends_on_same_day, \
31.14 to_date, to_timezone
31.15 from imiptools.period import add_day_start_points, add_empty_days, add_slots, \
31.16 - get_overlapping, \
31.17 get_scale, get_slots, get_spans, partition_by_day, \
31.18 remove_end_slot, Period, Point
31.19 from imipweb.resource import FormUtilities, ResourceClient
31.20 @@ -323,8 +322,8 @@
31.21 view_end = view_period.get_end()
31.22 duration = view_period.get_duration()
31.23
31.24 - preceding_events = view_start and get_overlapping(freebusy, Period(None, view_start, self.get_tzid())) or []
31.25 - following_events = view_end and get_overlapping(freebusy, Period(view_end, None, self.get_tzid())) or []
31.26 + preceding_events = view_start and freebusy.get_overlapping(Period(None, view_start, self.get_tzid())) or []
31.27 + following_events = view_end and freebusy.get_overlapping(Period(view_end, None, self.get_tzid())) or []
31.28
31.29 last_preceding = preceding_events and to_date(preceding_events[-1].get_end()) + timedelta(1) or None
31.30 first_following = following_events and to_date(following_events[0].get_start()) or None
31.31 @@ -487,7 +486,7 @@
31.32 # Filter periods outside the given view.
31.33
31.34 if view_period:
31.35 - periods = get_overlapping(periods, view_period)
31.36 + periods = periods.get_overlapping(view_period)
31.37
31.38 # Get the time scale with start and end points.
31.39
32.1 --- a/imipweb/event.py Tue Apr 19 21:20:57 2016 +0200
32.2 +++ b/imipweb/event.py Fri Apr 22 16:22:58 2016 +0200
32.3 @@ -3,7 +3,7 @@
32.4 """
32.5 A Web interface to a calendar event.
32.6
32.7 -Copyright (C) 2014, 2015 Paul Boddie <paul@boddie.org.uk>
32.8 +Copyright (C) 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk>
32.9
32.10 This program is free software; you can redistribute it and/or modify it under
32.11 the terms of the GNU General Public License as published by the Free Software
32.12 @@ -23,7 +23,6 @@
32.13 uri_parts, uri_values
32.14 from imiptools.dates import format_datetime, to_timezone
32.15 from imiptools.mail import Messenger
32.16 -from imiptools.period import have_conflict
32.17 from imipweb.data import EventPeriod, event_period_from_period, FormPeriod, PeriodError
32.18 from imipweb.resource import DateTimeFormUtilities, FormUtilities, ResourceClientForObject
32.19
32.20 @@ -713,7 +712,7 @@
32.21 partstat = participant_attr and participant_attr.get("PARTSTAT")
32.22 recurrences = self.obj.get_recurrence_start_points(recurrenceids, tzid)
32.23
32.24 - for p in have_conflict(freebusy, periods, True):
32.25 + for p in freebusy.have_conflict(periods, True):
32.26 if not self.recurrenceid and p.is_replaced(recurrences):
32.27 continue
32.28
33.1 --- a/imipweb/resource.py Tue Apr 19 21:20:57 2016 +0200
33.2 +++ b/imipweb/resource.py Fri Apr 22 16:22:58 2016 +0200
33.3 @@ -23,6 +23,7 @@
33.4 from imiptools.client import Client, ClientForObject
33.5 from imiptools.data import get_uri
33.6 from imiptools.dates import format_datetime, to_date
33.7 +from imiptools.period import FreeBusyCollection
33.8 from imipweb.data import event_period_from_period, form_period_from_period, \
33.9 FormDate, PeriodError
33.10 from imipweb.env import CGIEnvironment
33.11 @@ -151,7 +152,7 @@
33.12
33.13 "Return a list of periods comprising the request summary."
33.14
33.15 - summary = []
33.16 + summary = FreeBusyCollection()
33.17
33.18 for uid, recurrenceid, request_type in self._get_requests():
33.19
34.1 --- a/messages/da_DK.imip-agent.po Tue Apr 19 21:20:57 2016 +0200
34.2 +++ b/messages/da_DK.imip-agent.po Fri Apr 22 16:22:58 2016 +0200
34.3 @@ -6,8 +6,8 @@
34.4 msgid ""
34.5 msgstr ""
34.6 "Project-Id-Version: 71b35ab1f0bd+\n"
34.7 -"Report-Msgid-Bugs-To: Jonas Smedegaard <dr@jones.dk>\n"
34.8 -"POT-Creation-Date: 2015-11-05 23:53+0100\n"
34.9 +"Report-Msgid-Bugs-To: \n"
34.10 +"POT-Creation-Date: 2016-04-18 21:06+0200\n"
34.11 "PO-Revision-Date: 2016-04-08 09:19+0200\n"
34.12 "Last-Translator: Jonas Smedegaard <dr@jones.dk>\n"
34.13 "Language-Team: Danish\n"
34.14 @@ -16,7 +16,7 @@
34.15 "Content-Type: text/plain; charset=UTF-8\n"
34.16 "Content-Transfer-Encoding: 8bit\n"
34.17
34.18 -#: imiptools/handlers/__init__.py:77
34.19 +#: imiptools/handlers/__init__.py:78
34.20 #, python-format
34.21 msgid ""
34.22 "If your mail program cannot handle this message, you may view the details "
34.23 @@ -173,272 +173,272 @@
34.24 msgid "Do not participate"
34.25 msgstr "Deltag ikke"
34.26
34.27 -#: imipweb/calendar.py:133
34.28 +#: imipweb/calendar.py:132
34.29 #, python-format
34.30 msgid "New event at %s"
34.31 msgstr "Ny begivenhed ved %s"
34.32
34.33 -#: imipweb/calendar.py:231
34.34 +#: imipweb/calendar.py:230
34.35 msgid "Pending requests:"
34.36 msgstr "Afventende forespørgsler:"
34.37
34.38 -#: imipweb/calendar.py:253
34.39 +#: imipweb/calendar.py:252
34.40 msgid "There are no pending requests."
34.41 msgstr "Der er ingen afventende forespørgsler."
34.42
34.43 -#: imipweb/calendar.py:270
34.44 +#: imipweb/calendar.py:269
34.45 msgid "Participants for scheduling:"
34.46 msgstr "Deltagere til gruppeplanlægning:"
34.47
34.48 -#: imipweb/calendar.py:275 imipweb/event.py:426 imipweb/event.py:525
34.49 +#: imipweb/calendar.py:274 imipweb/event.py:425 imipweb/event.py:524
34.50 msgid "Remove"
34.51 msgstr "Fjern"
34.52
34.53 -#: imipweb/calendar.py:280
34.54 +#: imipweb/calendar.py:279
34.55 msgid "Add"
34.56 msgstr "Tilføj"
34.57
34.58 -#: imipweb/calendar.py:304
34.59 +#: imipweb/calendar.py:303
34.60 msgid "Select days or periods for a new event."
34.61 msgstr "Vælg dage eller perioder til en ny begivenhed."
34.62
34.63 -#: imipweb/calendar.py:305
34.64 +#: imipweb/calendar.py:304
34.65 msgid "Hide busy time periods"
34.66 msgstr "Skjul optagede perioder"
34.67
34.68 -#: imipweb/calendar.py:306
34.69 +#: imipweb/calendar.py:305
34.70 msgid "Show busy time periods"
34.71 msgstr "Vis optagede perioder"
34.72
34.73 -#: imipweb/calendar.py:307
34.74 +#: imipweb/calendar.py:306
34.75 msgid "Show empty days"
34.76 msgstr "Vis ledige dage"
34.77
34.78 -#: imipweb/calendar.py:308
34.79 +#: imipweb/calendar.py:307
34.80 msgid "Hide empty days"
34.81 msgstr "Skjul ledige dage"
34.82
34.83 -#: imipweb/calendar.py:309 imipweb/calendar.py:846
34.84 +#: imipweb/calendar.py:308 imipweb/calendar.py:845
34.85 msgid "Clear selections"
34.86 msgstr "Nulstil valgte perioder"
34.87
34.88 -#: imipweb/calendar.py:339
34.89 +#: imipweb/calendar.py:338
34.90 msgid "Show earlier events"
34.91 msgstr "Vis tidligere begivenheder"
34.92
34.93 -#: imipweb/calendar.py:345
34.94 +#: imipweb/calendar.py:344
34.95 msgid "Show earlier"
34.96 msgstr "Vis tidligere"
34.97
34.98 -#: imipweb/calendar.py:354
34.99 +#: imipweb/calendar.py:353
34.100 msgid "Show later"
34.101 msgstr "Vis senere"
34.102
34.103 -#: imipweb/calendar.py:361
34.104 +#: imipweb/calendar.py:360
34.105 msgid "Show later events"
34.106 msgstr "Vis senere begivenhder"
34.107
34.108 -#: imipweb/calendar.py:440
34.109 +#: imipweb/calendar.py:439
34.110 #, python-format
34.111 msgid "Showing events from %(start)s until %(end)s"
34.112 msgstr "Viser begivenheder fra %(start)s frem til %(end)s"
34.113
34.114 -#: imipweb/calendar.py:444
34.115 +#: imipweb/calendar.py:443
34.116 #, python-format
34.117 msgid "Showing events from %s"
34.118 msgstr "Viser begivenheder fra %s"
34.119
34.120 -#: imipweb/calendar.py:446
34.121 +#: imipweb/calendar.py:445
34.122 #, python-format
34.123 msgid "Showing events until %s"
34.124 msgstr "Viser begivenheder frem til %s"
34.125
34.126 -#: imipweb/calendar.py:470
34.127 +#: imipweb/calendar.py:469
34.128 msgid "Pending requests"
34.129 msgstr "Forespørgsler som venter"
34.130
34.131 -#: imipweb/calendar.py:470
34.132 +#: imipweb/calendar.py:469
34.133 msgid "Your schedule"
34.134 msgstr "Din tidsplan"
34.135
34.136 -#: imipweb/calendar.py:589
34.137 +#: imipweb/calendar.py:588
34.138 msgid "Calendar"
34.139 msgstr "Kalender"
34.140
34.141 -#: imipweb/calendar.py:840
34.142 +#: imipweb/calendar.py:839
34.143 msgid "Summary:"
34.144 msgstr "Opsummering:"
34.145
34.146 -#: imipweb/calendar.py:842
34.147 +#: imipweb/calendar.py:841
34.148 msgid "New event"
34.149 msgstr "Ny begivenhed"
34.150
34.151 -#: imipweb/calendar.py:993
34.152 +#: imipweb/calendar.py:992
34.153 msgid "(Participant is busy)"
34.154 msgstr "(Deltager er optaget)"
34.155
34.156 -#: imipweb/calendar.py:1076
34.157 +#: imipweb/calendar.py:1075
34.158 msgid "Select/deselect period"
34.159 msgstr "Vælg/nulstil periode"
34.160
34.161 -#: imipweb/event.py:44
34.162 +#: imipweb/event.py:43
34.163 msgid "Summary"
34.164 msgstr "Opsummering"
34.165
34.166 -#: imipweb/event.py:45 imipweb/event.py:629 imipweb/event.py:742
34.167 +#: imipweb/event.py:44 imipweb/event.py:628 imipweb/event.py:741
34.168 msgid "Start"
34.169 msgstr "Start"
34.170
34.171 -#: imipweb/event.py:46 imipweb/event.py:630 imipweb/event.py:743
34.172 +#: imipweb/event.py:45 imipweb/event.py:629 imipweb/event.py:742
34.173 msgid "End"
34.174 msgstr "Slut"
34.175
34.176 -#: imipweb/event.py:47
34.177 +#: imipweb/event.py:46
34.178 msgid "Organiser"
34.179 msgstr "Arrangør"
34.180
34.181 -#: imipweb/event.py:48 imipweb/event.py:595
34.182 +#: imipweb/event.py:47 imipweb/event.py:594
34.183 msgid "Attendee"
34.184 msgstr "Deltager"
34.185
34.186 -#: imipweb/event.py:52
34.187 +#: imipweb/event.py:51
34.188 msgid "Not confirmed"
34.189 msgstr "Ikke bekræftet"
34.190
34.191 -#: imipweb/event.py:53
34.192 +#: imipweb/event.py:52
34.193 msgid "Attending"
34.194 msgstr "Deltager"
34.195
34.196 -#: imipweb/event.py:54
34.197 +#: imipweb/event.py:53
34.198 msgid "Tentatively attending"
34.199 msgstr "Deltager måske"
34.200
34.201 -#: imipweb/event.py:55
34.202 +#: imipweb/event.py:54
34.203 msgid "Not attending"
34.204 msgstr "Deltager ikke"
34.205
34.206 -#: imipweb/event.py:56
34.207 +#: imipweb/event.py:55
34.208 msgid "Delegated"
34.209 msgstr "Er delegeret til en anden"
34.210
34.211 -#: imipweb/event.py:57
34.212 +#: imipweb/event.py:56
34.213 msgid "Not indicated"
34.214 msgstr "Ikke angivet"
34.215
34.216 -#: imipweb/event.py:154
34.217 +#: imipweb/event.py:153
34.218 msgid "This event has not been shared."
34.219 msgstr "Denne begivenhed er ikke blevet delt med andre."
34.220
34.221 -#: imipweb/event.py:159
34.222 +#: imipweb/event.py:158
34.223 msgid "An action is required for this request:"
34.224 msgstr "Der skal tages et valg for denne forespørgsel:"
34.225
34.226 -#: imipweb/event.py:162
34.227 +#: imipweb/event.py:161
34.228 msgid "Send reply"
34.229 msgstr "Send svar"
34.230
34.231 -#: imipweb/event.py:164 imipweb/event.py:183
34.232 +#: imipweb/event.py:163 imipweb/event.py:182
34.233 msgid "Discard event"
34.234 msgstr "Drop begivenheden"
34.235
34.236 -#: imipweb/event.py:166 imipweb/event.py:186
34.237 +#: imipweb/event.py:165 imipweb/event.py:185
34.238 msgid "Return to the calendar"
34.239 msgstr "Tilbage til kalenderen"
34.240
34.241 -#: imipweb/event.py:170
34.242 +#: imipweb/event.py:169
34.243 msgid "As organiser, you can perform the following:"
34.244 msgstr "Som arrangør kan du gøre følgende:"
34.245
34.246 -#: imipweb/event.py:173
34.247 +#: imipweb/event.py:172
34.248 msgid "Update event"
34.249 msgstr "Opdatér begivenheden"
34.250
34.251 -#: imipweb/event.py:177
34.252 +#: imipweb/event.py:176
34.253 msgid "Ignore counter-proposals"
34.254 msgstr "Ignorér modforslag"
34.255
34.256 -#: imipweb/event.py:181
34.257 +#: imipweb/event.py:180
34.258 msgid "Cancel event"
34.259 msgstr "Aflys begivenheden"
34.260
34.261 -#: imipweb/event.py:188
34.262 +#: imipweb/event.py:187
34.263 msgid "Save without sending"
34.264 msgstr "Gem uden at sende"
34.265
34.266 -#: imipweb/event.py:227 imipweb/event.py:741 imipweb/event.py:1290
34.267 +#: imipweb/event.py:226 imipweb/event.py:740 imipweb/event.py:1289
34.268 msgid "Event"
34.269 msgstr "Begivenhed"
34.270
34.271 -#: imipweb/event.py:274
34.272 +#: imipweb/event.py:273
34.273 msgid "First occurrence replaced by a separate event"
34.274 msgstr "Den første periode er erstattet af en anden begivenhed"
34.275
34.276 -#: imipweb/event.py:283
34.277 +#: imipweb/event.py:282
34.278 msgid "First occurrence excluded"
34.279 msgstr "Den første periode blev fjernet"
34.280
34.281 -#: imipweb/event.py:294
34.282 +#: imipweb/event.py:293
34.283 msgid "Add a recurrence"
34.284 msgstr "Tilføj en ny periode"
34.285
34.286 -#: imipweb/event.py:335
34.287 +#: imipweb/event.py:334
34.288 msgid "Add attendee"
34.289 msgstr "Tilføj en deltager"
34.290
34.291 -#: imipweb/event.py:428
34.292 +#: imipweb/event.py:427
34.293 msgid "(Uninvited)"
34.294 msgstr "(Ikke inviteret)"
34.295
34.296 -#: imipweb/event.py:429
34.297 +#: imipweb/event.py:428
34.298 msgid "Re-invite"
34.299 msgstr "Invitér påny"
34.300
34.301 -#: imipweb/event.py:453
34.302 +#: imipweb/event.py:452
34.303 msgid "This event modifies a recurring event."
34.304 msgstr "Denne begivenhed ændrer en periode i en begivenhed som gentager sig."
34.305
34.306 -#: imipweb/event.py:463
34.307 +#: imipweb/event.py:462
34.308 #, python-format
34.309 msgid "This event occurs on the following occasions within the next %d days:"
34.310 msgstr "Denne begivenhed forekommer på følgende anledninger de næste %d dage:"
34.311
34.312 -#: imipweb/event.py:494
34.313 +#: imipweb/event.py:493
34.314 msgid "Occurrence"
34.315 msgstr "Forekomst"
34.316
34.317 -#: imipweb/event.py:494
34.318 +#: imipweb/event.py:493
34.319 msgid "Occurrence from rule"
34.320 msgstr "Forekomst fra regel"
34.321
34.322 -#: imipweb/event.py:527
34.323 +#: imipweb/event.py:526
34.324 msgid "(Removed)"
34.325 msgstr "(Fjernet)"
34.326
34.327 -#: imipweb/event.py:528
34.328 +#: imipweb/event.py:527
34.329 msgid "Re-add"
34.330 msgstr "Tilføj påny"
34.331
34.332 -#: imipweb/event.py:590
34.333 +#: imipweb/event.py:589
34.334 msgid "The following attendees have been suggested for this event:"
34.335 msgstr "Følgende deltagere er blevet foreslået for denne begivenhed:"
34.336
34.337 -#: imipweb/event.py:596 imipweb/event.py:626
34.338 +#: imipweb/event.py:595 imipweb/event.py:625
34.339 msgid "Suggested by..."
34.340 msgstr "Foreslået af..."
34.341
34.342 -#: imipweb/event.py:620
34.343 +#: imipweb/event.py:619
34.344 msgid "The following periods have been suggested for this event:"
34.345 msgstr "Følgende perioder er blevet foreslået for denne begivenhed:"
34.346
34.347 -#: imipweb/event.py:625
34.348 +#: imipweb/event.py:624
34.349 msgid "Periods"
34.350 msgstr "Perioder"
34.351
34.352 -#: imipweb/event.py:736
34.353 +#: imipweb/event.py:735
34.354 msgid "This event conflicts with others:"
34.355 msgstr "Begivenheden er i konflikt med andre i tidsplanen:"
34.356
34.357 -#: imipweb/event.py:763
34.358 +#: imipweb/event.py:762
34.359 msgid "(Unspecified event)"
34.360 msgstr "(Ikke angivet begivenhed)"
34.361
36.1 --- a/messages/nb_NO.imip-agent.po Tue Apr 19 21:20:57 2016 +0200
36.2 +++ b/messages/nb_NO.imip-agent.po Fri Apr 22 16:22:58 2016 +0200
36.3 @@ -174,272 +174,272 @@
36.4 msgid "Do not participate"
36.5 msgstr "Ikke delta"
36.6
36.7 -#: imipweb/calendar.py:133
36.8 +#: imipweb/calendar.py:132
36.9 #, python-format
36.10 msgid "New event at %s"
36.11 msgstr "Ny hendelse på %s"
36.12
36.13 -#: imipweb/calendar.py:231
36.14 +#: imipweb/calendar.py:230
36.15 msgid "Pending requests:"
36.16 msgstr "Forespørsler som venter"
36.17
36.18 -#: imipweb/calendar.py:253
36.19 +#: imipweb/calendar.py:252
36.20 msgid "There are no pending requests."
36.21 msgstr "Ingen forespørsler"
36.22
36.23 -#: imipweb/calendar.py:270
36.24 +#: imipweb/calendar.py:269
36.25 msgid "Participants for scheduling:"
36.26 msgstr "Deltakere for gruppeplanlegging:"
36.27
36.28 -#: imipweb/calendar.py:275 imipweb/event.py:426 imipweb/event.py:525
36.29 +#: imipweb/calendar.py:274 imipweb/event.py:425 imipweb/event.py:524
36.30 msgid "Remove"
36.31 msgstr "Fjerne"
36.32
36.33 -#: imipweb/calendar.py:280
36.34 +#: imipweb/calendar.py:279
36.35 msgid "Add"
36.36 msgstr "Legge til"
36.37
36.38 -#: imipweb/calendar.py:304
36.39 +#: imipweb/calendar.py:303
36.40 msgid "Select days or periods for a new event."
36.41 msgstr "Velg dager eller perioder for en ny hendelse."
36.42
36.43 -#: imipweb/calendar.py:305
36.44 +#: imipweb/calendar.py:304
36.45 msgid "Hide busy time periods"
36.46 msgstr "Skjule opptatte perioder"
36.47
36.48 -#: imipweb/calendar.py:306
36.49 +#: imipweb/calendar.py:305
36.50 msgid "Show busy time periods"
36.51 msgstr "Vise opptatte perioder"
36.52
36.53 -#: imipweb/calendar.py:307
36.54 +#: imipweb/calendar.py:306
36.55 msgid "Show empty days"
36.56 msgstr "Vise ledige dager"
36.57
36.58 -#: imipweb/calendar.py:308
36.59 +#: imipweb/calendar.py:307
36.60 msgid "Hide empty days"
36.61 msgstr "Skjule ledige dager"
36.62
36.63 -#: imipweb/calendar.py:309 imipweb/calendar.py:846
36.64 +#: imipweb/calendar.py:308 imipweb/calendar.py:845
36.65 msgid "Clear selections"
36.66 msgstr "Nullstille valgte perioder"
36.67
36.68 -#: imipweb/calendar.py:339
36.69 +#: imipweb/calendar.py:338
36.70 msgid "Show earlier events"
36.71 msgstr "Tidligere hendelser"
36.72
36.73 -#: imipweb/calendar.py:345
36.74 +#: imipweb/calendar.py:344
36.75 msgid "Show earlier"
36.76 msgstr "Tidligere"
36.77
36.78 -#: imipweb/calendar.py:354
36.79 +#: imipweb/calendar.py:353
36.80 msgid "Show later"
36.81 msgstr "Senere"
36.82
36.83 -#: imipweb/calendar.py:361
36.84 +#: imipweb/calendar.py:360
36.85 msgid "Show later events"
36.86 msgstr "Senere hendelser"
36.87
36.88 -#: imipweb/calendar.py:440
36.89 +#: imipweb/calendar.py:439
36.90 #, python-format
36.91 msgid "Showing events from %(start)s until %(end)s"
36.92 msgstr "Viser hendelser fra %(start)s frem til %(end)s"
36.93
36.94 -#: imipweb/calendar.py:444
36.95 +#: imipweb/calendar.py:443
36.96 #, python-format
36.97 msgid "Showing events from %s"
36.98 msgstr "Viser hendelser fra %s"
36.99
36.100 -#: imipweb/calendar.py:446
36.101 +#: imipweb/calendar.py:445
36.102 #, python-format
36.103 msgid "Showing events until %s"
36.104 msgstr "Viser hendelser frem til %s"
36.105
36.106 -#: imipweb/calendar.py:470
36.107 +#: imipweb/calendar.py:469
36.108 msgid "Pending requests"
36.109 msgstr "Forespørsler som venter"
36.110
36.111 -#: imipweb/calendar.py:470
36.112 +#: imipweb/calendar.py:469
36.113 msgid "Your schedule"
36.114 msgstr "Din tidsplan"
36.115
36.116 -#: imipweb/calendar.py:589
36.117 +#: imipweb/calendar.py:588
36.118 msgid "Calendar"
36.119 msgstr "Kalender"
36.120
36.121 -#: imipweb/calendar.py:840
36.122 +#: imipweb/calendar.py:839
36.123 msgid "Summary:"
36.124 msgstr "Sammendrag:"
36.125
36.126 -#: imipweb/calendar.py:842
36.127 +#: imipweb/calendar.py:841
36.128 msgid "New event"
36.129 msgstr "Ny hendelse"
36.130
36.131 -#: imipweb/calendar.py:993
36.132 +#: imipweb/calendar.py:992
36.133 msgid "(Participant is busy)"
36.134 msgstr "(Deltaker er opptatt)"
36.135
36.136 -#: imipweb/calendar.py:1076
36.137 +#: imipweb/calendar.py:1075
36.138 msgid "Select/deselect period"
36.139 msgstr "Velge/nullstille periode"
36.140
36.141 -#: imipweb/event.py:44
36.142 +#: imipweb/event.py:43
36.143 msgid "Summary"
36.144 msgstr "Sammendrag"
36.145
36.146 -#: imipweb/event.py:45 imipweb/event.py:629 imipweb/event.py:742
36.147 +#: imipweb/event.py:44 imipweb/event.py:628 imipweb/event.py:741
36.148 msgid "Start"
36.149 msgstr "Start"
36.150
36.151 -#: imipweb/event.py:46 imipweb/event.py:630 imipweb/event.py:743
36.152 +#: imipweb/event.py:45 imipweb/event.py:629 imipweb/event.py:742
36.153 msgid "End"
36.154 msgstr "Slutt"
36.155
36.156 -#: imipweb/event.py:47
36.157 +#: imipweb/event.py:46
36.158 msgid "Organiser"
36.159 msgstr "Arrangør"
36.160
36.161 -#: imipweb/event.py:48 imipweb/event.py:595
36.162 +#: imipweb/event.py:47 imipweb/event.py:594
36.163 msgid "Attendee"
36.164 msgstr "Deltaker"
36.165
36.166 -#: imipweb/event.py:52
36.167 +#: imipweb/event.py:51
36.168 msgid "Not confirmed"
36.169 msgstr "Ikke bekreftet"
36.170
36.171 -#: imipweb/event.py:53
36.172 +#: imipweb/event.py:52
36.173 msgid "Attending"
36.174 msgstr "Deltar"
36.175
36.176 -#: imipweb/event.py:54
36.177 +#: imipweb/event.py:53
36.178 msgid "Tentatively attending"
36.179 msgstr "Delta kanskje"
36.180
36.181 -#: imipweb/event.py:55
36.182 +#: imipweb/event.py:54
36.183 msgid "Not attending"
36.184 msgstr "Deltar ikke"
36.185
36.186 -#: imipweb/event.py:56
36.187 +#: imipweb/event.py:55
36.188 msgid "Delegated"
36.189 msgstr "Har delegert til en annen"
36.190
36.191 -#: imipweb/event.py:57
36.192 +#: imipweb/event.py:56
36.193 msgid "Not indicated"
36.194 msgstr "Ikke angitt"
36.195
36.196 -#: imipweb/event.py:154
36.197 +#: imipweb/event.py:153
36.198 msgid "This event has not been shared."
36.199 msgstr "Denne hendelsen er ikke blitt delt med andre."
36.200
36.201 -#: imipweb/event.py:159
36.202 +#: imipweb/event.py:158
36.203 msgid "An action is required for this request:"
36.204 msgstr "Et valg må tas med denne forespørselen:"
36.205
36.206 -#: imipweb/event.py:162
36.207 +#: imipweb/event.py:161
36.208 msgid "Send reply"
36.209 msgstr "Send svar"
36.210
36.211 -#: imipweb/event.py:164 imipweb/event.py:183
36.212 +#: imipweb/event.py:163 imipweb/event.py:182
36.213 msgid "Discard event"
36.214 msgstr "Kaste hendelsen"
36.215
36.216 -#: imipweb/event.py:166 imipweb/event.py:186
36.217 +#: imipweb/event.py:165 imipweb/event.py:185
36.218 msgid "Return to the calendar"
36.219 msgstr "Tilbake til kalenderen"
36.220
36.221 -#: imipweb/event.py:170
36.222 +#: imipweb/event.py:169
36.223 msgid "As organiser, you can perform the following:"
36.224 msgstr "Som arrangør kan du utføre følgende:"
36.225
36.226 -#: imipweb/event.py:173
36.227 +#: imipweb/event.py:172
36.228 msgid "Update event"
36.229 msgstr "Oppdatere hendelsen"
36.230
36.231 -#: imipweb/event.py:177
36.232 +#: imipweb/event.py:176
36.233 msgid "Ignore counter-proposals"
36.234 msgstr "Overse motforslag"
36.235
36.236 -#: imipweb/event.py:181
36.237 +#: imipweb/event.py:180
36.238 msgid "Cancel event"
36.239 msgstr "Send avbud for hendelsen"
36.240
36.241 -#: imipweb/event.py:188
36.242 +#: imipweb/event.py:187
36.243 msgid "Save without sending"
36.244 msgstr "Lagre uten å sende"
36.245
36.246 -#: imipweb/event.py:227 imipweb/event.py:741 imipweb/event.py:1290
36.247 +#: imipweb/event.py:226 imipweb/event.py:740 imipweb/event.py:1289
36.248 msgid "Event"
36.249 msgstr "Hendelse"
36.250
36.251 -#: imipweb/event.py:274
36.252 +#: imipweb/event.py:273
36.253 msgid "First occurrence replaced by a separate event"
36.254 msgstr "Den første perioden er erstattet med en annen hendelse"
36.255
36.256 -#: imipweb/event.py:283
36.257 +#: imipweb/event.py:282
36.258 msgid "First occurrence excluded"
36.259 msgstr "Den første perioden ble fjernet"
36.260
36.261 -#: imipweb/event.py:294
36.262 +#: imipweb/event.py:293
36.263 msgid "Add a recurrence"
36.264 msgstr "Legge til en ny periode"
36.265
36.266 -#: imipweb/event.py:335
36.267 +#: imipweb/event.py:334
36.268 msgid "Add attendee"
36.269 msgstr "Legge til en deltaker"
36.270
36.271 -#: imipweb/event.py:428
36.272 +#: imipweb/event.py:427
36.273 msgid "(Uninvited)"
36.274 msgstr "(Ikke invitert)"
36.275
36.276 -#: imipweb/event.py:429
36.277 +#: imipweb/event.py:428
36.278 msgid "Re-invite"
36.279 msgstr "Invitere på nytt"
36.280
36.281 -#: imipweb/event.py:453
36.282 +#: imipweb/event.py:452
36.283 msgid "This event modifies a recurring event."
36.284 msgstr "Denne hendelsen endrer en period i en hendelse som gjentar seg."
36.285
36.286 -#: imipweb/event.py:463
36.287 +#: imipweb/event.py:462
36.288 #, python-format
36.289 msgid "This event occurs on the following occasions within the next %d days:"
36.290 msgstr "Denne hendelsen forekommer på følgende anledninger innen %d dager:"
36.291
36.292 -#: imipweb/event.py:494
36.293 +#: imipweb/event.py:493
36.294 msgid "Occurrence"
36.295 msgstr "Forekomst"
36.296
36.297 -#: imipweb/event.py:494
36.298 +#: imipweb/event.py:493
36.299 msgid "Occurrence from rule"
36.300 msgstr "Forekomst fra regel"
36.301
36.302 -#: imipweb/event.py:527
36.303 +#: imipweb/event.py:526
36.304 msgid "(Removed)"
36.305 msgstr "(Fjernet)"
36.306
36.307 -#: imipweb/event.py:528
36.308 +#: imipweb/event.py:527
36.309 msgid "Re-add"
36.310 msgstr "Legge til på nytt"
36.311
36.312 -#: imipweb/event.py:590
36.313 +#: imipweb/event.py:589
36.314 msgid "The following attendees have been suggested for this event:"
36.315 msgstr "Følgende deltakere er blitt foreslått for denne hendelsen:"
36.316
36.317 -#: imipweb/event.py:596 imipweb/event.py:626
36.318 +#: imipweb/event.py:595 imipweb/event.py:625
36.319 msgid "Suggested by..."
36.320 msgstr "Foreslått av..."
36.321
36.322 -#: imipweb/event.py:620
36.323 +#: imipweb/event.py:619
36.324 msgid "The following periods have been suggested for this event:"
36.325 msgstr "Følgende perioder er blitt foreslått for denne hendelsen:"
36.326
36.327 -#: imipweb/event.py:625
36.328 +#: imipweb/event.py:624
36.329 msgid "Periods"
36.330 msgstr "Perioder"
36.331
36.332 -#: imipweb/event.py:736
36.333 +#: imipweb/event.py:735
36.334 msgid "This event conflicts with others:"
36.335 msgstr "Hendelsen er i konflikt med andre i tidsplanen:"
36.336
36.337 -#: imipweb/event.py:763
36.338 +#: imipweb/event.py:762
36.339 msgid "(Unspecified event)"
36.340 msgstr "(Ikke angitt hendelse)"
36.341
37.1 --- a/test_all.sh Tue Apr 19 21:20:57 2016 +0200
37.2 +++ b/test_all.sh Fri Apr 22 16:22:58 2016 +0200
37.3 @@ -1,3 +1,6 @@
37.4 #!/bin/sh
37.5
37.6 -for FILENAME in tests/test_*.sh ; do echo $FILENAME ; $FILENAME ; done
37.7 +for FILENAME in tests/test_*.sh ; do
37.8 + echo "$FILENAME"
37.9 + "$FILENAME"
37.10 +done
38.1 --- a/tests/common.sh Tue Apr 19 21:20:57 2016 +0200
38.2 +++ b/tests/common.sh Fri Apr 22 16:22:58 2016 +0200
38.3 @@ -1,47 +1,44 @@
38.4 #!/bin/sh
38.5
38.6 -THIS_DIR=`dirname "$0"`
38.7 -BASE_DIR="$THIS_DIR/.."
38.8 -
38.9 -STORE=/tmp/store
38.10 -STATIC=/tmp/static
38.11 -PREFS=/tmp/prefs
38.12 -JOURNAL=/tmp/journal
38.13 -
38.14 -ARGS="-S $STORE -P $STATIC -p $PREFS -j $JOURNAL -d"
38.15 +. "`dirname \"$0\"`/common_minimal.sh"
38.16
38.17 ACCEPT_SCRIPT="$THIS_DIR/test_handle.py"
38.18 -ACCEPT_ARGS="accept $STORE $JOURNAL $PREFS"
38.19 +ACCEPT_ARGS="accept $STORE_TYPE $STORE $JOURNAL $PREFS"
38.20
38.21 COUNTER_SCRIPT="$THIS_DIR/test_handle.py"
38.22 -COUNTER_ARGS="counter $STORE $JOURNAL $PREFS"
38.23 +COUNTER_ARGS="counter $STORE_TYPE $STORE $JOURNAL $PREFS"
38.24
38.25 DECLINE_SCRIPT="$THIS_DIR/test_handle.py"
38.26 -DECLINE_ARGS="decline $STORE $JOURNAL $PREFS"
38.27 +DECLINE_ARGS="decline $STORE_TYPE $STORE $JOURNAL $PREFS"
38.28
38.29 FREEBUSY_SCRIPT="$BASE_DIR/tools/make_freebusy.py"
38.30 FREEBUSY_ARGS="-s -n"
38.31
38.32 +LIST_SCRIPT="$THIS_DIR/list_table.py"
38.33 +LIST_ARGS="$STORE_TYPE $STORE $JOURNAL"
38.34 +
38.35 OUTGOING_SCRIPT="$BASE_DIR/imip_person_outgoing.py"
38.36
38.37 PERSON_SCRIPT="$BASE_DIR/imip_person.py"
38.38
38.39 -RESOURCE_SCRIPT="$BASE_DIR/imip_resource.py"
38.40 -
38.41 -SHOWMAIL="$BASE_DIR/tools/showmail.py"
38.42 +SET_QUOTA_LIMIT="$BASE_DIR/tools/set_quota_limit.py"
38.43 +SET_QUOTA_LIMIT_ARGS="-T $STORE_TYPE -j $JOURNAL"
38.44
38.45 TAB=`printf '\t'`
38.46
38.47 -TEMPLATES="$THIS_DIR/templates"
38.48 -
38.49 -ERROR=err.tmp
38.50 -
38.51 PYTHONPATH="$BASE_DIR"
38.52 export PYTHONPATH
38.53
38.54 -rm -rf "$STORE"
38.55 +if [ "$STORE_TYPE" = "file" ]; then
38.56 + rm -rf "$STORE"
38.57 + rm -rf "$JOURNAL"
38.58 +elif [ "$STORE_TYPE" = "postgresql" ]; then
38.59 + dropdb "$DBNAME"
38.60 + createdb "$DBNAME"
38.61 + psql -q -f "$BASE_DIR/conf/postgresql/schema.sql" "$DBNAME"
38.62 +fi
38.63 +
38.64 rm -rf "$STATIC"
38.65 rm -rf "$PREFS"
38.66 -rm -rf "$JOURNAL"
38.67 rm -f "$ERROR"
38.68 rm -f out*.tmp
39.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
39.2 +++ b/tests/common_minimal.sh Fri Apr 22 16:22:58 2016 +0200
39.3 @@ -0,0 +1,28 @@
39.4 +#!/bin/sh
39.5 +
39.6 +THIS_DIR=`dirname "$0"`
39.7 +BASE_DIR="$THIS_DIR/.."
39.8 +
39.9 +STORE_TYPE=${STORE_TYPE:-file}
39.10 +
39.11 +if [ "$STORE_TYPE" = "file" ]; then
39.12 + STORE=/tmp/store
39.13 + JOURNAL=/tmp/journal
39.14 +elif [ "$STORE_TYPE" = "postgresql" ]; then
39.15 + DBNAME='imip_agent_test'
39.16 + STORE="dbname=$DBNAME"
39.17 + JOURNAL="$STORE"
39.18 +fi
39.19 +
39.20 +STATIC=/tmp/static
39.21 +PREFS=/tmp/prefs
39.22 +
39.23 +ARGS="-T $STORE_TYPE -S $STORE -P $STATIC -p $PREFS -j $JOURNAL -d"
39.24 +
39.25 +RESOURCE_SCRIPT="$BASE_DIR/imip_resource.py"
39.26 +
39.27 +SHOWMAIL="$BASE_DIR/tools/showmail.py"
39.28 +
39.29 +TEMPLATES="$THIS_DIR/templates"
39.30 +
39.31 +ERROR=err.tmp
40.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
40.2 +++ b/tests/list_table.py Fri Apr 22 16:22:58 2016 +0200
40.3 @@ -0,0 +1,110 @@
40.4 +#!/usr/bin/env python
40.5 +
40.6 +"""
40.7 +Show the contents of a table.
40.8 +
40.9 +Copyright (C) 2016 Paul Boddie <paul@boddie.org.uk>
40.10 +
40.11 +This program is free software; you can redistribute it and/or modify it under
40.12 +the terms of the GNU General Public License as published by the Free Software
40.13 +Foundation; either version 3 of the License, or (at your option) any later
40.14 +version.
40.15 +
40.16 +This program is distributed in the hope that it will be useful, but WITHOUT
40.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
40.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
40.19 +details.
40.20 +
40.21 +You should have received a copy of the GNU General Public License along with
40.22 +this program. If not, see <http://www.gnu.org/licenses/>.
40.23 +"""
40.24 +
40.25 +from imiptools.data import Object
40.26 +from imiptools.stores import get_store, get_journal
40.27 +import sys
40.28 +
40.29 +def show_list(data):
40.30 + for row in data:
40.31 + print row or ""
40.32 +
40.33 +def show_object(data):
40.34 + if data:
40.35 + print Object(data).to_string()
40.36 +
40.37 +def show_periods(data):
40.38 + for row in data:
40.39 + print "\t".join(row.as_tuple(strings_only=True))
40.40 +
40.41 +def show_tuples(data):
40.42 + for row in data:
40.43 + print "\t".join([(column or "") for column in row])
40.44 +
40.45 +if __name__ == "__main__":
40.46 + try:
40.47 + store_type, store_dir, journal_dir, user, table = sys.argv[1:6]
40.48 + args = sys.argv[6:]
40.49 + except ValueError:
40.50 + print >>sys.stderr, """\
40.51 +Need a store type, a store directory, a journal directory, a user URI, and a
40.52 +table to show. Other arguments may be needed for certain tables.
40.53 +"""
40.54 + sys.exit(1)
40.55 +
40.56 + store = get_store(store_type, store_dir)
40.57 + journal = get_journal(store_type, journal_dir)
40.58 +
40.59 + # Periods.
40.60 +
40.61 + if table == "entries":
40.62 + group = args[0]
40.63 + data = journal.get_entries(user, group)
40.64 + show_periods(data)
40.65 +
40.66 + elif table == "freebusy":
40.67 + data = store.get_freebusy(user)
40.68 + show_periods(data)
40.69 +
40.70 + elif table == "freebusy_offers":
40.71 + data = store.get_freebusy_offers(user)
40.72 + show_periods(data)
40.73 +
40.74 + elif table == "freebusy_other":
40.75 + other = args[0]
40.76 + data = store.get_freebusy_for_other(user, other)
40.77 + show_periods(data)
40.78 +
40.79 + # Tuples.
40.80 +
40.81 + elif table == "requests":
40.82 + data = store.get_requests(user)
40.83 + show_tuples(data)
40.84 +
40.85 + elif table == "freebusy_providers":
40.86 + data = store.get_freebusy_providers(user)
40.87 + show_tuples(data)
40.88 +
40.89 + # Objects.
40.90 +
40.91 + elif table == "countered_object":
40.92 + uid = args[0]
40.93 + other = args[1]
40.94 + data = store.get_counter(user, other, uid)
40.95 + show_object(data)
40.96 +
40.97 + elif table == "object":
40.98 + uid = args[0]
40.99 + data = store.get_event(user, uid)
40.100 + show_object(data)
40.101 +
40.102 + elif table == "recurrence":
40.103 + uid = args[0]
40.104 + recurrenceid = args[1]
40.105 + data = store.get_event(user, uid, recurrenceid)
40.106 + show_object(data)
40.107 +
40.108 + elif table == "cancelled_recurrences":
40.109 + uid = args[0]
40.110 + data = store.get_cancelled_recurrences(user, uid)
40.111 + show_list(data)
40.112 +
40.113 +# vim: tabstop=4 expandtab shiftwidth=4
41.1 --- a/tests/resource_request.sh Tue Apr 19 21:20:57 2016 +0200
41.2 +++ b/tests/resource_request.sh Fri Apr 22 16:22:58 2016 +0200
41.3 @@ -1,17 +1,6 @@
41.4 #!/bin/sh
41.5
41.6 -THIS_DIR=`dirname $0`
41.7 -
41.8 -TEMPLATES="$THIS_DIR/templates"
41.9 -RESOURCE_SCRIPT="$THIS_DIR/../imip_resource.py"
41.10 -SHOWMAIL="$THIS_DIR/../tools/showmail.py"
41.11 -STORE=/tmp/store
41.12 -STATIC=/tmp/static
41.13 -PREFS=/tmp/prefs
41.14 -JOURNAL=/tmp/journal
41.15 -ARGS="-S $STORE -P $STATIC -p $PREFS -j $JOURNAL -d"
41.16 -
41.17 -ERROR=err.tmp
41.18 +. "`dirname \"$0\"`/common_minimal.sh"
41.19
41.20 export N=$1
41.21 export START=20141126T090000
42.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
42.2 +++ b/tests/templates/event-request-person-recurring-rdate.txt Fri Apr 22 16:22:58 2016 +0200
42.3 @@ -0,0 +1,37 @@
42.4 +Content-Type: multipart/alternative; boundary="===============0047278175=="
42.5 +MIME-Version: 1.0
42.6 +From: paul.boddie@example.com
42.7 +To: vincent.vole@example.com, harvey.horse@example.com
42.8 +Subject: Invitation!
42.9 +
42.10 +--===============0047278175==
42.11 +Content-Type: text/plain; charset="us-ascii"
42.12 +MIME-Version: 1.0
42.13 +Content-Transfer-Encoding: 7bit
42.14 +
42.15 +This message contains an event.
42.16 +
42.17 +--===============0047278175==
42.18 +MIME-Version: 1.0
42.19 +Content-Transfer-Encoding: 7bit
42.20 +Content-Type: text/calendar; charset="us-ascii"; method="REQUEST"
42.21 +
42.22 +BEGIN:VCALENDAR
42.23 +PRODID:-//imip-agent/test//EN
42.24 +METHOD:REQUEST
42.25 +VERSION:2.0
42.26 +BEGIN:VEVENT
42.27 +ORGANIZER:mailto:paul.boddie@example.com
42.28 +ATTENDEE;RSVP=TRUE:mailto:vincent.vole@example.com
42.29 +ATTENDEE;RSVP=TRUE:mailto:harvey.horse@example.com
42.30 +ATTENDEE;RSVP=TRUE:mailto:paul.boddie@example.com
42.31 +DTSTAMP:20141009T182400Z
42.32 +DTSTART;TZID=Europe/Oslo:20141010T100000
42.33 +DTEND;TZID=Europe/Oslo:20141010T110000
42.34 +RDATE;TZID=Europe/Oslo;VALUE=PERIOD:20141011T100000/20141011T110000
42.35 +SUMMARY:Recurring event
42.36 +UID:event26@example.com
42.37 +END:VEVENT
42.38 +END:VCALENDAR
42.39 +
42.40 +--===============0047278175==--
43.1 --- a/tests/test_freebusy_publishing.sh Tue Apr 19 21:20:57 2016 +0200
43.2 +++ b/tests/test_freebusy_publishing.sh Fri Apr 22 16:22:58 2016 +0200
43.3 @@ -4,8 +4,6 @@
43.4
43.5 USER="mailto:paul.boddie@example.com"
43.6 SENDER="mailto:resource-room-confroom@example.com"
43.7 -FBFILE="$STORE/$USER/freebusy"
43.8 -FBOTHERFILE="$STORE/$USER/freebusy-other/$SENDER"
43.9
43.10 mkdir -p "$PREFS/$USER"
43.11 echo 'Europe/Oslo' > "$PREFS/$USER/TZID"
43.12 @@ -15,17 +13,23 @@
43.13 | "$SHOWMAIL" \
43.14 > out0.tmp
43.15
43.16 - grep -q "^20140401T070000Z${TAB}20140401T080000Z" "$FBOTHERFILE" \
43.17 -&& grep -q "^20140401T080000Z${TAB}20140401T100000Z" "$FBOTHERFILE" \
43.18 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \
43.19 +> out1.tmp
43.20 +
43.21 + grep -q "^20140401T070000Z${TAB}20140401T080000Z" "out1.tmp" \
43.22 +&& grep -q "^20140401T080000Z${TAB}20140401T100000Z" "out1.tmp" \
43.23 && echo "Success" \
43.24 || echo "Failed"
43.25
43.26 "$PERSON_SCRIPT" $ARGS < "$TEMPLATES/fb-publish-again.txt" 2>> $ERROR \
43.27 | "$SHOWMAIL" \
43.28 -> out0.tmp
43.29 +> out2.tmp
43.30
43.31 - grep -q "^20140401T070000Z${TAB}20140401T080000Z" "$FBOTHERFILE" \
43.32 -&& ! grep -q "^20140401T080000Z${TAB}20140401T100000Z" "$FBOTHERFILE" \
43.33 -&& grep -q "^20140401T083000Z${TAB}20140401T100000Z" "$FBOTHERFILE" \
43.34 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \
43.35 +> out3.tmp
43.36 +
43.37 + grep -q "^20140401T070000Z${TAB}20140401T080000Z" "out3.tmp" \
43.38 +&& ! grep -q "^20140401T080000Z${TAB}20140401T100000Z" "out3.tmp" \
43.39 +&& grep -q "^20140401T083000Z${TAB}20140401T100000Z" "out3.tmp" \
43.40 && echo "Success" \
43.41 || echo "Failed"
44.1 --- a/tests/test_handle.py Tue Apr 19 21:20:57 2016 +0200
44.2 +++ b/tests/test_handle.py Fri Apr 22 16:22:58 2016 +0200
44.3 @@ -24,7 +24,8 @@
44.4 from imiptools.dates import get_datetime, to_timezone
44.5 from imiptools.mail import Messenger
44.6 from imiptools.period import RecurringPeriod
44.7 -import imiptools.stores.file
44.8 +from imiptools.stores import get_store, get_journal
44.9 +from os.path import split
44.10 import sys
44.11
44.12 class TestClient(ClientForObject):
44.13 @@ -36,7 +37,7 @@
44.14
44.15 # Action methods.
44.16
44.17 - def handle_request(self, action, start=None, end=None):
44.18 + def handle_request(self, action, start=None, end=None, recurrenceid=None):
44.19
44.20 """
44.21 Process the current request for the current user. Return whether the
44.22 @@ -44,18 +45,36 @@
44.23
44.24 If 'start' and 'end' are specified, they will be used in any
44.25 counter-proposal.
44.26 +
44.27 + Where 'recurrenceid' is specified and refers to a new recurrence, the
44.28 + action will apply only to this new recurrence.
44.29 """
44.30
44.31 + have_new_recurrence = self.obj.get_recurrenceid() != recurrenceid
44.32 +
44.33 + if have_new_recurrence:
44.34 + self.obj["RECURRENCE-ID"] = [(recurrenceid, {})]
44.35 + self.obj.remove_all(["RDATE", "RRULE"])
44.36 +
44.37 # Reply only on behalf of this user.
44.38
44.39 if action in ("accept", "decline"):
44.40 attendee_attr = self.update_participation(action == "accept" and "ACCEPTED" or "DECLINED")
44.41 method = "REPLY"
44.42
44.43 - # For counter-proposals, set a new main period for the event.
44.44 -
44.45 elif action == "counter":
44.46 attendee_attr = self.obj.get_value_map("ATTENDEE").get(self.user)
44.47 + method = "COUNTER"
44.48 +
44.49 + # Nothing else is supported.
44.50 +
44.51 + else:
44.52 + return None
44.53 +
44.54 + # For counter-proposals or new recurrences, set a new main period for
44.55 + # the event.
44.56 +
44.57 + if action == "counter" or have_new_recurrence:
44.58 period = self.obj.get_main_period(self.get_tzid())
44.59
44.60 # Use the existing or configured time zone for the specified
44.61 @@ -65,9 +84,8 @@
44.62 end = to_timezone(get_datetime(end), period.tzid)
44.63 period = RecurringPeriod(start, end, period.tzid, period.origin, period.get_start_attr(), period.get_end_attr())
44.64 self.obj.set_period(period)
44.65 - method = "COUNTER"
44.66 - else:
44.67 - return None
44.68 +
44.69 + # Where no attendees remain, no message is generated.
44.70
44.71 if not attendee_attr:
44.72 return None
44.73 @@ -93,39 +111,53 @@
44.74 # response message to standard output.
44.75
44.76 if __name__ == "__main__":
44.77 + progname = split(sys.argv[0])[-1]
44.78 +
44.79 try:
44.80 - action, store_dir, journal_dir, preferences_dir, user = sys.argv[1:6]
44.81 - if action == "counter":
44.82 - start, end = sys.argv[6:8]
44.83 - i = 8
44.84 + action, store_type, store_dir, journal_dir, preferences_dir, user = sys.argv[1:7]
44.85 + if len(sys.argv) >= 10:
44.86 + start, end = sys.argv[7:9]
44.87 + i = 9
44.88 else:
44.89 start, end = None, None
44.90 - i = 6
44.91 + i = 7
44.92 uid, recurrenceid = (sys.argv[i:i+2] + [None] * 2)[:2]
44.93 except ValueError:
44.94 print >>sys.stderr, """\
44.95 -Need 'accept', 'counter' or 'decline', a store directory, a preferences
44.96 -directory, user URI, any counter-proposal datetimes (see below), plus the
44.97 -appropriate event UID and RECURRENCE-ID (if a recurrence is involved).
44.98 +Usage: %s <action> <store type> <store directory> <journal directory>
44.99 + <preferences directory> <user URI> [ <start> <end> ]
44.100 + <uid> <recurrence-id>
44.101 +
44.102 +Need 'accept', 'counter' or 'decline', a store type, a store directory, a
44.103 +journal directory, a preferences directory, user URI, any counter-proposal or
44.104 +new recurrence datetimes (see below), plus the appropriate event UID and
44.105 +RECURRENCE-ID (if a recurrence is involved).
44.106
44.107 The RECURRENCE-ID must be in exactly the form employed by the store, not a
44.108 -different but equivalent representation.
44.109 +different but equivalent representation, if the identifier is to refer to an
44.110 +existing recurrence.
44.111
44.112 Alternatively, omit the UID and RECURRENCE-ID and provide event-only details on
44.113 standard input to force the script to handle an event not already present in the
44.114 store.
44.115
44.116 If 'counter' has been indicated, alternative start and end datetimes are also
44.117 -required.
44.118 +required. If a specific recurrence is being separated from an event, such
44.119 +datetimes are also required in order to set the main period of the recurrence.
44.120 """
44.121 sys.exit(1)
44.122
44.123 - store = imiptools.stores.file.FileStore(store_dir)
44.124 - journal = imiptools.stores.file.FileJournal(journal_dir)
44.125 + store = get_store(store_type, store_dir)
44.126 + journal = get_journal(store_type, journal_dir)
44.127
44.128 if uid is not None:
44.129 fragment = store.get_event(user, uid, recurrenceid)
44.130
44.131 + # Permit new recurrences by getting the parent object.
44.132 +
44.133 + if not fragment:
44.134 + fragment = store.get_event(user, uid)
44.135 +
44.136 if not fragment:
44.137 print >>sys.stderr, "No such event:", uid, recurrenceid
44.138 sys.exit(1)
44.139 @@ -134,7 +166,7 @@
44.140
44.141 obj = Object(fragment)
44.142 handler = TestClient(obj, user, Messenger(), store, None, journal, preferences_dir)
44.143 - response = handler.handle_request(action, start, end)
44.144 + response = handler.handle_request(action, start, end, recurrenceid)
44.145
44.146 if response:
44.147 if uid is not None:
45.1 --- a/tests/test_outgoing_invitation.sh Tue Apr 19 21:20:57 2016 +0200
45.2 +++ b/tests/test_outgoing_invitation.sh Fri Apr 22 16:22:58 2016 +0200
45.3 @@ -3,70 +3,84 @@
45.4 . "`dirname \"$0\"`/common.sh"
45.5
45.6 USER="mailto:paul.boddie@example.com"
45.7 -FBFILE="$STORE/$USER/freebusy"
45.8
45.9 mkdir -p "$PREFS/$USER"
45.10 echo 'Europe/Oslo' > "$PREFS/$USER/TZID"
45.11 echo 'share' > "$PREFS/$USER/freebusy_sharing"
45.12
45.13 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request.txt" 2>> $ERROR
45.14 -cp "$FBFILE" out1.tmp
45.15
45.16 - grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE" \
45.17 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
45.18 +| tee out1.tmp \
45.19 +| grep -q "^20141126T150000Z${TAB}20141126T160000Z" \
45.20 && echo "Success" \
45.21 || echo "Failed"
45.22
45.23 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-cancel.txt" 2>> $ERROR
45.24 echo "Cancel..."
45.25 -cp "$FBFILE" out2.tmp
45.26
45.27 - ! grep -q '^2' "$FBFILE" \
45.28 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
45.29 +> out2.tmp
45.30 +
45.31 + ! grep -q '^2' "out2.tmp" \
45.32 && echo "Success" \
45.33 || echo "Failed"
45.34
45.35 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-recurring.txt" 2>> $ERROR
45.36 -cp "$FBFILE" out3.tmp
45.37
45.38 - [ `cat "$FBFILE" | wc -l` = '3' ] \
45.39 -&& grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \
45.40 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
45.41 +> out3.tmp
45.42 +
45.43 + [ `cat "out3.tmp" | wc -l` = '3' ] \
45.44 +&& grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out3.tmp" \
45.45 && echo "Success" \
45.46 || echo "Failed"
45.47
45.48 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-recurring-instance.txt" 2>> $ERROR
45.49 -cp "$FBFILE" out4.tmp
45.50
45.51 - [ `cat "$FBFILE" | wc -l` = '2' ] \
45.52 -&& ! grep -q "^20141114T090000Z${TAB}20141114T100000Z" "$FBFILE" \
45.53 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
45.54 +> out4.tmp
45.55 +
45.56 + [ `cat "out4.tmp" | wc -l` = '2' ] \
45.57 +&& ! grep -q "^20141114T090000Z${TAB}20141114T100000Z" "out4.tmp" \
45.58 && echo "Success" \
45.59 || echo "Failed"
45.60
45.61 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-recurring-reschedule-instance.txt" 2>> $ERROR
45.62 -cp "$FBFILE" out5.tmp
45.63
45.64 - grep -q "^20141011T080000Z${TAB}20141011T090000Z" "$FBFILE" \
45.65 -&& ! grep -q "^20141010T090000Z${TAB}20141010T100000Z" "$FBFILE" \
45.66 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
45.67 +> out5.tmp
45.68 +
45.69 + grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out5.tmp" \
45.70 +&& ! grep -q "^20141010T090000Z${TAB}20141010T100000Z" "out5.tmp" \
45.71 && echo "Success" \
45.72 || echo "Failed"
45.73
45.74 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-recurring.txt" 2>> $ERROR
45.75 -cp "$FBFILE" out6.tmp
45.76
45.77 - ! grep -q '^2' "$FBFILE" \
45.78 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
45.79 +> out6.tmp
45.80 +
45.81 + ! grep -q '^2' "out6.tmp" \
45.82 && echo "Success" \
45.83 || echo "Failed"
45.84
45.85 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-recurring-day.txt" 2>> $ERROR
45.86 -cp "$FBFILE" out7.tmp
45.87
45.88 - [ `cat "$FBFILE" | wc -l` = '3' ] \
45.89 -&& grep -q "^20141211T230000Z${TAB}20141212T230000Z" "$FBFILE" \
45.90 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
45.91 +> out7.tmp
45.92 +
45.93 + [ `cat "out7.tmp" | wc -l` = '3' ] \
45.94 +&& grep -q "^20141211T230000Z${TAB}20141212T230000Z" "out7.tmp" \
45.95 && echo "Success" \
45.96 || echo "Failed"
45.97
45.98 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-recurring-day.txt" 2>> $ERROR
45.99 -cp "$FBFILE" out8.tmp
45.100
45.101 - ! grep -q '^2' "$FBFILE" \
45.102 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
45.103 +> out8.tmp
45.104 +
45.105 + ! grep -q '^2' "out8.tmp" \
45.106 && echo "Success" \
45.107 || echo "Failed"
45.108
45.109 @@ -75,31 +89,39 @@
45.110 echo 'Europe/Mariehamn' > "$PREFS/$USER/TZID"
45.111
45.112 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-recurring.txt" 2>> $ERROR
45.113 -cp "$FBFILE" out9.tmp
45.114
45.115 - [ `cat "$FBFILE" | wc -l` = '3' ] \
45.116 -&& grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \
45.117 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
45.118 +> out9.tmp
45.119 +
45.120 + [ `cat "out9.tmp" | wc -l` = '3' ] \
45.121 +&& grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out9.tmp" \
45.122 && echo "Success" \
45.123 || echo "Failed"
45.124
45.125 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-recurring.txt" 2>> $ERROR
45.126 -cp "$FBFILE" out10.tmp
45.127
45.128 - ! grep -q '^2' "$FBFILE" \
45.129 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
45.130 +> out10.tmp
45.131 +
45.132 + ! grep -q '^2' "out10.tmp" \
45.133 && echo "Success" \
45.134 || echo "Failed"
45.135
45.136 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-recurring-day.txt" 2>> $ERROR
45.137 -cp "$FBFILE" out11.tmp
45.138
45.139 - [ `cat "$FBFILE" | wc -l` = '3' ] \
45.140 -&& grep -q "^20141211T230000Z${TAB}20141212T230000Z" "$FBFILE" \
45.141 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
45.142 +> out11.tmp
45.143 +
45.144 + [ `cat "out11.tmp" | wc -l` = '3' ] \
45.145 +&& grep -q "^20141211T230000Z${TAB}20141212T230000Z" "out11.tmp" \
45.146 && echo "Success" \
45.147 || echo "Failed"
45.148
45.149 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-recurring-day.txt" 2>> $ERROR
45.150 -cp "$FBFILE" out12.tmp
45.151
45.152 - ! grep -q '^2' "$FBFILE" \
45.153 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
45.154 +> out12.tmp
45.155 +
45.156 + ! grep -q '^2' "out12.tmp" \
45.157 && echo "Success" \
45.158 || echo "Failed"
46.1 --- a/tests/test_person_invitation.sh Tue Apr 19 21:20:57 2016 +0200
46.2 +++ b/tests/test_person_invitation.sh Fri Apr 22 16:22:58 2016 +0200
46.3 @@ -4,8 +4,6 @@
46.4
46.5 USER="mailto:vincent.vole@example.com"
46.6 SENDER="mailto:paul.boddie@example.com"
46.7 -FBFILE="$STORE/$USER/freebusy"
46.8 -FBOTHERFILE="$STORE/$USER/freebusy-other/$SENDER"
46.9
46.10 mkdir -p "$PREFS/$USER"
46.11 echo 'Europe/Oslo' > "$PREFS/$USER/TZID"
46.12 @@ -37,12 +35,17 @@
46.13 && echo "Success" \
46.14 || echo "Failed"
46.15
46.16 - ! [ -e "$FBFILE" ] \
46.17 -|| ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE" \
46.18 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
46.19 +> out2f.tmp
46.20 +
46.21 + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out2f.tmp" \
46.22 && echo "Success" \
46.23 || echo "Failed"
46.24
46.25 - grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBOTHERFILE" \
46.26 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \
46.27 +> out2fo.tmp
46.28 +
46.29 + grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out2fo.tmp" \
46.30 && echo "Success" \
46.31 || echo "Failed"
46.32
46.33 @@ -50,7 +53,10 @@
46.34 | tee out3.tmp \
46.35 | "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR
46.36
46.37 - grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE" \
46.38 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
46.39 +> out3f.tmp
46.40 +
46.41 + grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out3f.tmp" \
46.42 && echo "Success" \
46.43 || echo "Failed"
46.44
46.45 @@ -62,11 +68,17 @@
46.46 && echo "Success" \
46.47 || echo "Failed"
46.48
46.49 - ! grep -q "event7@example.com" "$FBFILE" \
46.50 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
46.51 +> out4f.tmp
46.52 +
46.53 + ! grep -q "event7@example.com" "out4f.tmp" \
46.54 && echo "Success" \
46.55 || echo "Failed"
46.56
46.57 - grep -q "event7@example.com" "$FBOTHERFILE" \
46.58 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \
46.59 +> out4fo.tmp
46.60 +
46.61 + grep -q "event7@example.com" "out4fo.tmp" \
46.62 && echo "Success" \
46.63 || echo "Failed"
46.64
46.65 @@ -74,8 +86,11 @@
46.66 | tee out5.tmp \
46.67 | "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR
46.68
46.69 - grep -q "event6@example.com" "$FBFILE" \
46.70 -&& ! grep -q "event7@example.com" "$FBFILE" \
46.71 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
46.72 +> out5f.tmp
46.73 +
46.74 + grep -q "event6@example.com" "out5f.tmp" \
46.75 +&& ! grep -q "event7@example.com" "out5f.tmp" \
46.76 && echo "Success" \
46.77 || echo "Failed"
46.78
46.79 @@ -87,11 +102,17 @@
46.80 && echo "Success" \
46.81 || echo "Failed"
46.82
46.83 - ! grep -q "event6@example.com" "$FBFILE" \
46.84 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
46.85 +> out6f.tmp
46.86 +
46.87 + ! grep -q "event6@example.com" "out6f.tmp" \
46.88 && echo "Success" \
46.89 || echo "Failed"
46.90
46.91 - grep -q "event6@example.com" "$FBOTHERFILE" \
46.92 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \
46.93 +> out6fo.tmp
46.94 +
46.95 + grep -q "event6@example.com" "out6fo.tmp" \
46.96 && echo "Success" \
46.97 || echo "Failed"
46.98
46.99 @@ -103,11 +124,17 @@
46.100 && echo "Success" \
46.101 || echo "Failed"
46.102
46.103 - ! grep -q "event6@example.com" "$FBFILE" \
46.104 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
46.105 +> out7f.tmp
46.106 +
46.107 + ! grep -q "event6@example.com" "out7f.tmp" \
46.108 && echo "Success" \
46.109 || echo "Failed"
46.110
46.111 - ! grep -q "event6@example.com" "$FBOTHERFILE" \
46.112 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \
46.113 +> out7fo.tmp
46.114 +
46.115 + ! grep -q "event6@example.com" "out7fo.tmp" \
46.116 && echo "Success" \
46.117 || echo "Failed"
46.118
46.119 @@ -119,10 +146,16 @@
46.120 && echo "Success" \
46.121 || echo "Failed"
46.122
46.123 - ! grep -q "spoof2@example.com" "$FBFILE" \
46.124 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
46.125 +> out8f.tmp
46.126 +
46.127 + ! grep -q "spoof2@example.com" "out8f.tmp" \
46.128 && echo "Success" \
46.129 || echo "Failed"
46.130
46.131 - ! grep -q "spoof2@example.com" "$FBOTHERFILE" \
46.132 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \
46.133 +> out8fo.tmp
46.134 +
46.135 + ! grep -q "spoof2@example.com" "out8fo.tmp" \
46.136 && echo "Success" \
46.137 || echo "Failed"
47.1 --- a/tests/test_person_invitation_add.sh Tue Apr 19 21:20:57 2016 +0200
47.2 +++ b/tests/test_person_invitation_add.sh Fri Apr 22 16:22:58 2016 +0200
47.3 @@ -4,10 +4,6 @@
47.4
47.5 USER="mailto:vincent.vole@example.com"
47.6 SENDER="mailto:paul.boddie@example.com"
47.7 -FBFILE="$STORE/$USER/freebusy"
47.8 -FBOTHERFILE="$STORE/$USER/freebusy-other/$SENDER"
47.9 -FBSENDERFILE="$STORE/$SENDER/freebusy"
47.10 -FBSENDEROTHERFILE="$STORE/$SENDER/freebusy-other/$USER"
47.11
47.12 mkdir -p "$PREFS/$USER"
47.13 echo 'Europe/Oslo' > "$PREFS/$USER/TZID"
47.14 @@ -21,7 +17,10 @@
47.15
47.16 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-person-recurring.txt" 2>> $ERROR
47.17
47.18 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBSENDERFILE" \
47.19 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
47.20 +> out1f.tmp
47.21 +
47.22 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out1f.tmp" \
47.23 && echo "Success" \
47.24 || echo "Failed"
47.25
47.26 @@ -35,12 +34,17 @@
47.27 && echo "Success" \
47.28 || echo "Failed"
47.29
47.30 - ! [ -e "$FBFILE" ] \
47.31 -|| ! grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \
47.32 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
47.33 +> out2f.tmp
47.34 +
47.35 + ! grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out2f.tmp" \
47.36 && echo "Success" \
47.37 || echo "Failed"
47.38
47.39 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBOTHERFILE" \
47.40 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \
47.41 +> out2fo.tmp
47.42 +
47.43 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out2fo.tmp" \
47.44 && echo "Success" \
47.45 || echo "Failed"
47.46
47.47 @@ -54,7 +58,10 @@
47.48 && echo "Success" \
47.49 || echo "Failed"
47.50
47.51 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \
47.52 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
47.53 +> out3f.tmp
47.54 +
47.55 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out3f.tmp" \
47.56 && echo "Success" \
47.57 || echo "Failed"
47.58
47.59 @@ -64,7 +71,10 @@
47.60 | "$SHOWMAIL" \
47.61 > out4.tmp
47.62
47.63 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBSENDEROTHERFILE" \
47.64 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy_other" "$USER" \
47.65 +> out4fo.tmp
47.66 +
47.67 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out4fo.tmp" \
47.68 && echo "Success" \
47.69 || echo "Failed"
47.70
47.71 @@ -72,8 +82,11 @@
47.72
47.73 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-add-person-recurring.txt" 2>> $ERROR
47.74
47.75 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBSENDERFILE" \
47.76 -&& grep -q "^20150109T090000Z${TAB}20150109T100000Z" "$FBSENDERFILE" \
47.77 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
47.78 +> out4f.tmp
47.79 +
47.80 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out4f.tmp" \
47.81 +&& grep -q "^20150109T090000Z${TAB}20150109T100000Z" "out4f.tmp" \
47.82 && echo "Success" \
47.83 || echo "Failed"
47.84
47.85 @@ -87,13 +100,19 @@
47.86 && echo "Success" \
47.87 || echo "Failed"
47.88
47.89 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \
47.90 -&& ! grep -q "^20150109T090000Z${TAB}20150109T100000Z" "$FBFILE" \
47.91 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
47.92 +> out5f.tmp
47.93 +
47.94 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out5f.tmp" \
47.95 +&& ! grep -q "^20150109T090000Z${TAB}20150109T100000Z" "out5f.tmp" \
47.96 && echo "Success" \
47.97 || echo "Failed"
47.98
47.99 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBOTHERFILE" \
47.100 -&& ! grep -q "^20150109T090000Z${TAB}20150109T100000Z" "$FBOTHERFILE" \
47.101 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \
47.102 +> out5fo.tmp
47.103 +
47.104 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out5fo.tmp" \
47.105 +&& ! grep -q "^20150109T090000Z${TAB}20150109T100000Z" "out5fo.tmp" \
47.106 && echo "Success" \
47.107 || echo "Failed"
47.108
47.109 @@ -118,13 +137,19 @@
47.110 && echo "Success" \
47.111 || echo "Failed"
47.112
47.113 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \
47.114 -&& ! grep -q "^20150109T090000Z${TAB}20150109T100000Z" "$FBFILE" \
47.115 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
47.116 +> out7f.tmp
47.117 +
47.118 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out7f.tmp" \
47.119 +&& ! grep -q "^20150109T090000Z${TAB}20150109T100000Z" "out7f.tmp" \
47.120 && echo "Success" \
47.121 || echo "Failed"
47.122
47.123 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBOTHERFILE" \
47.124 -&& grep -q "^20150109T090000Z${TAB}20150109T100000Z" "$FBOTHERFILE" \
47.125 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \
47.126 +> out7fo.tmp
47.127 +
47.128 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out7fo.tmp" \
47.129 +&& grep -q "^20150109T090000Z${TAB}20150109T100000Z" "out7fo.tmp" \
47.130 && echo "Success" \
47.131 || echo "Failed"
47.132
47.133 @@ -146,7 +171,10 @@
47.134 && echo "Success" \
47.135 || echo "Failed"
47.136
47.137 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \
47.138 -&& grep -q "^20150109T090000Z${TAB}20150109T100000Z" "$FBFILE" \
47.139 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
47.140 +> out9f.tmp
47.141 +
47.142 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out9f.tmp" \
47.143 +&& grep -q "^20150109T090000Z${TAB}20150109T100000Z" "out9f.tmp" \
47.144 && echo "Success" \
47.145 || echo "Failed"
48.1 --- a/tests/test_person_invitation_counter.sh Tue Apr 19 21:20:57 2016 +0200
48.2 +++ b/tests/test_person_invitation_counter.sh Fri Apr 22 16:22:58 2016 +0200
48.3 @@ -4,11 +4,6 @@
48.4
48.5 USER="mailto:vincent.vole@example.com"
48.6 SENDER="mailto:paul.boddie@example.com"
48.7 -FBFILE="$STORE/$USER/freebusy"
48.8 -FBOFFERFILE="$STORE/$USER/freebusy-offers"
48.9 -FBSENDERFILE="$STORE/$SENDER/freebusy"
48.10 -FBSENDEROTHERFILE="$STORE/$SENDER/freebusy-other/$USER"
48.11 -FBSENDERREQUESTS="$STORE/$SENDER/requests"
48.12
48.13 mkdir -p "$PREFS/$USER"
48.14 echo 'Europe/Oslo' > "$PREFS/$USER/TZID"
48.15 @@ -28,7 +23,10 @@
48.16
48.17 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-person.txt" 2>> $ERROR
48.18
48.19 - grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBSENDERFILE" \
48.20 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
48.21 +> out0f.tmp
48.22 +
48.23 + grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out0f.tmp" \
48.24 && echo "Success" \
48.25 || echo "Failed"
48.26
48.27 @@ -42,8 +40,10 @@
48.28 && echo "Success" \
48.29 || echo "Failed"
48.30
48.31 - ! [ -e "$FBFILE" ] \
48.32 -|| ! grep -q "event6@example.com" "$FBFILE" \
48.33 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
48.34 +> out1f.tmp
48.35 +
48.36 + ! grep -q "event6@example.com" "out1f.tmp" \
48.37 && echo "Success" \
48.38 || echo "Failed"
48.39
48.40 @@ -61,9 +61,11 @@
48.41
48.42 # Note that the invitation has only been prepared, not processed.
48.43
48.44 - ! [ -e "$FBFILE" ] \
48.45 -|| ( ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE" \
48.46 - && ! grep -q "^20141126T160000Z${TAB}20141126T170000Z" "$FBFILE" ) \
48.47 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
48.48 +> out2f.tmp
48.49 +
48.50 + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out2f.tmp" \
48.51 +&& ! grep -q "^20141126T160000Z${TAB}20141126T170000Z" "out2f.tmp" \
48.52 && echo "Success" \
48.53 || echo "Failed"
48.54
48.55 @@ -72,8 +74,10 @@
48.56 && echo "Success" \
48.57 || echo "Failed"
48.58
48.59 - ! [ -e "$FBOFFERFILE" ] \
48.60 -|| ! grep -q "^20141126T160000Z${TAB}20141126T170000Z" "$FBOFFERFILE" \
48.61 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_offers" \
48.62 +> out2o.tmp
48.63 +
48.64 + ! grep -q "^20141126T160000Z${TAB}20141126T170000Z" "out2o.tmp" \
48.65 && echo "Success" \
48.66 || echo "Failed"
48.67
48.68 @@ -81,13 +85,18 @@
48.69
48.70 "$OUTGOING_SCRIPT" $ARGS < out2r.tmp 2>> $ERROR
48.71
48.72 - ! [ -e "$FBFILE" ] \
48.73 -|| ( ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE" \
48.74 - && ! grep -q "^20141126T160000Z${TAB}20141126T170000Z" "$FBFILE" ) \
48.75 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
48.76 +> out2f2.tmp
48.77 +
48.78 + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out2f2.tmp" \
48.79 +&& ! grep -q "^20141126T160000Z${TAB}20141126T170000Z" "out2f2.tmp" \
48.80 && echo "Success" \
48.81 || echo "Failed"
48.82
48.83 - grep -q "^20141126T160000Z${TAB}20141126T170000Z" "$FBOFFERFILE" \
48.84 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_offers" \
48.85 +> out2o2.tmp
48.86 +
48.87 + grep -q "^20141126T160000Z${TAB}20141126T170000Z" "out2o2.tmp" \
48.88 && echo "Success" \
48.89 || echo "Failed"
48.90
48.91 @@ -98,21 +107,32 @@
48.92 | "$SHOWMAIL" \
48.93 > out3.tmp
48.94
48.95 - ! [ -e "$FBSENDEROTHERFILE" ] \
48.96 -|| ( ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBSENDEROTHERFILE" \
48.97 - && ! grep -q "^20141126T160000Z${TAB}20141126T170000Z" "$FBSENDEROTHERFILE" ) \
48.98 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy_other" "$USER" \
48.99 +> out3f.tmp
48.100 +
48.101 + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out3f.tmp" \
48.102 +&& ! grep -q "^20141126T160000Z${TAB}20141126T170000Z" "out3f.tmp" \
48.103 && echo "Success" \
48.104 || echo "Failed"
48.105
48.106 - grep -q 'DTSTART;TZID=Europe/Oslo.*:20141126T160000' "$STORE/$SENDER/objects/event6@example.com" \
48.107 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "object" "event6@example.com" \
48.108 +> out3O.tmp
48.109 +
48.110 + grep -q 'DTSTART;TZID=Europe/Oslo.*:20141126T160000' "out3O.tmp" \
48.111 && echo "Success" \
48.112 || echo "Failed"
48.113
48.114 - grep -q 'DTSTART;TZID=Europe/Oslo.*:20141126T170000' "$STORE/$SENDER/counters/objects/event6@example.com/$USER" \
48.115 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "countered_object" "event6@example.com" "$USER" \
48.116 +> out3C.tmp
48.117 +
48.118 + grep -q 'DTSTART;TZID=Europe/Oslo.*:20141126T170000' "out3C.tmp" \
48.119 && echo "Success" \
48.120 || echo "Failed"
48.121
48.122 - grep -q 'event6@example.com' "$FBSENDERREQUESTS" \
48.123 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "requests" \
48.124 +> out3R.tmp
48.125 +
48.126 + grep -q 'event6@example.com' "out3R.tmp" \
48.127 && echo "Success" \
48.128 || echo "Failed"
48.129
48.130 @@ -137,11 +157,17 @@
48.131
48.132 "$OUTGOING_SCRIPT" $ARGS < out5.tmp 2>> $ERROR
48.133
48.134 - ! [ -e "$STORE/$SENDER/counters/objects/event6@example.com/$USER" ] \
48.135 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "countered_object" "event6@example.com" "$USER" \
48.136 +> out5C.tmp
48.137 +
48.138 + ! grep -q 'event6@example.com' "out5C.tmp" \
48.139 && echo "Success" \
48.140 || echo "Failed"
48.141
48.142 - ! grep -q 'event6@example.com' "$FBSENDERREQUESTS" \
48.143 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "requests" \
48.144 +> out5R.tmp
48.145 +
48.146 + ! grep -q 'event6@example.com' "out5R.tmp" \
48.147 && echo "Success" \
48.148 || echo "Failed"
48.149
48.150 @@ -149,13 +175,18 @@
48.151 | "$SHOWMAIL" \
48.152 > out6.tmp
48.153
48.154 - ! [ -e "$FBFILE" ] \
48.155 -|| ( ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE" \
48.156 - && ! grep -q "^20141126T160000Z${TAB}20141126T170000Z" "$FBFILE" ) \
48.157 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
48.158 +> out6f.tmp
48.159 +
48.160 + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out6f.tmp" \
48.161 +&& ! grep -q "^20141126T160000Z${TAB}20141126T170000Z" "out6f.tmp" \
48.162 && echo "Success" \
48.163 || echo "Failed"
48.164
48.165 - ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBOFFERFILE" \
48.166 -&& ! grep -q "^20141126T160000Z${TAB}20141126T170000Z" "$FBOFFERFILE" \
48.167 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_offers" \
48.168 +> out6o.tmp
48.169 +
48.170 + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out6o.tmp" \
48.171 +&& ! grep -q "^20141126T160000Z${TAB}20141126T170000Z" "out6o.tmp" \
48.172 && echo "Success" \
48.173 || echo "Failed"
49.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
49.2 +++ b/tests/test_person_invitation_decline_instance.sh Fri Apr 22 16:22:58 2016 +0200
49.3 @@ -0,0 +1,392 @@
49.4 +#!/bin/sh
49.5 +
49.6 +. "`dirname \"$0\"`/common.sh"
49.7 +
49.8 +USER1="mailto:vincent.vole@example.com"
49.9 +USER2="mailto:harvey.horse@example.com"
49.10 +SENDER="mailto:paul.boddie@example.com"
49.11 +
49.12 +mkdir -p "$PREFS/$USER1"
49.13 +echo 'Europe/Oslo' > "$PREFS/$USER1/TZID"
49.14 +echo 'share' > "$PREFS/$USER1/freebusy_sharing"
49.15 +
49.16 +mkdir -p "$PREFS/$USER2"
49.17 +echo 'Europe/Oslo' > "$PREFS/$USER2/TZID"
49.18 +echo 'share' > "$PREFS/$USER2/freebusy_sharing"
49.19 +
49.20 +mkdir -p "$PREFS/$SENDER"
49.21 +echo 'Europe/Oslo' > "$PREFS/$SENDER/TZID"
49.22 +
49.23 +# Test free/busy responses.
49.24 +
49.25 + "$PERSON_SCRIPT" $ARGS < "$TEMPLATES/fb-request-person-all.txt" 2>> $ERROR \
49.26 +| "$SHOWMAIL" \
49.27 +> out0.tmp
49.28 +
49.29 + grep -q 'METHOD:REPLY' out0.tmp \
49.30 +&& ! grep -q '^FREEBUSY' out0.tmp \
49.31 +&& echo "Success" \
49.32 +|| echo "Failed"
49.33 +
49.34 + "$PERSON_SCRIPT" $ARGS < "$TEMPLATES/fb-request-person.txt" 2>> $ERROR \
49.35 +| "$SHOWMAIL" \
49.36 +> out1.tmp
49.37 +
49.38 + grep -q 'METHOD:REPLY' out1.tmp \
49.39 +&& ! grep -q '^FREEBUSY' out1.tmp \
49.40 +&& echo "Success" \
49.41 +|| echo "Failed"
49.42 +
49.43 +# Publish an event, testing registration in the outgoing handler.
49.44 +
49.45 +"$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-person-recurring-rdate.txt" 2>> $ERROR
49.46 +
49.47 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
49.48 +> out1f.tmp
49.49 +
49.50 + grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out1f.tmp" \
49.51 +&& echo "Success" \
49.52 +|| echo "Failed"
49.53 +
49.54 +# There should be an event created by the sender.
49.55 +
49.56 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "object" "event26@example.com" \
49.57 +> out1O.tmp
49.58 +
49.59 + grep -q 'event26@example.com' "out1O.tmp" \
49.60 +&& echo "Success" \
49.61 +|| echo "Failed"
49.62 +
49.63 +# Test registration in the incoming handler for the recipients.
49.64 +
49.65 + "$PERSON_SCRIPT" $ARGS < "$TEMPLATES/event-request-person-recurring-rdate.txt" 2>> $ERROR \
49.66 +| "$SHOWMAIL" \
49.67 +> out2.tmp
49.68 +
49.69 + ! grep -q 'METHOD:REPLY' out2.tmp \
49.70 +&& echo "Success" \
49.71 +|| echo "Failed"
49.72 +
49.73 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "freebusy" \
49.74 +> out2f.tmp
49.75 +
49.76 + ! grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out2f.tmp" \
49.77 +&& echo "Success" \
49.78 +|| echo "Failed"
49.79 +
49.80 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "freebusy_other" "$SENDER" \
49.81 +> out2o.tmp
49.82 +
49.83 + grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out2o.tmp" \
49.84 +&& echo "Success" \
49.85 +|| echo "Failed"
49.86 +
49.87 + "$LIST_SCRIPT" $LIST_ARGS "$USER2" "freebusy" \
49.88 +> out2f2.tmp
49.89 +
49.90 + ! grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out2f2.tmp" \
49.91 +&& echo "Success" \
49.92 +|| echo "Failed"
49.93 +
49.94 + "$LIST_SCRIPT" $LIST_ARGS "$USER2" "freebusy_other" "$SENDER" \
49.95 +> out2o2.tmp
49.96 +
49.97 + grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out2o2.tmp" \
49.98 +&& echo "Success" \
49.99 +|| echo "Failed"
49.100 +
49.101 +# There should be an event created by the sender.
49.102 +
49.103 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "object" "event26@example.com" \
49.104 +> out2O.tmp
49.105 +
49.106 + grep -q 'event26@example.com' "out2O.tmp" \
49.107 +&& echo "Success" \
49.108 +|| echo "Failed"
49.109 +
49.110 + "$LIST_SCRIPT" $LIST_ARGS "$USER2" "object" "event26@example.com" \
49.111 +> out2O2.tmp
49.112 +
49.113 + grep -q 'event26@example.com' "out2O2.tmp" \
49.114 +&& echo "Success" \
49.115 +|| echo "Failed"
49.116 +
49.117 +# Test acceptance and registration in the outgoing handler.
49.118 +
49.119 + "$ACCEPT_SCRIPT" $ACCEPT_ARGS "$USER1" "event26@example.com" 2>> $ERROR \
49.120 +| tee out3.tmp \
49.121 +| "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR
49.122 +
49.123 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "freebusy" \
49.124 +> out3f.tmp
49.125 +
49.126 + grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out3f.tmp" \
49.127 +&& echo "Success" \
49.128 +|| echo "Failed"
49.129 +
49.130 + "$ACCEPT_SCRIPT" $ACCEPT_ARGS "$USER2" "event26@example.com" 2>> $ERROR \
49.131 +| tee out32.tmp \
49.132 +| "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR
49.133 +
49.134 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "freebusy" \
49.135 +> out3f2.tmp
49.136 +
49.137 + grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out3f2.tmp" \
49.138 +&& echo "Success" \
49.139 +|| echo "Failed"
49.140 +
49.141 +# Test registration in the incoming handler.
49.142 +
49.143 + "$PERSON_SCRIPT" $ARGS < out3.tmp 2>> $ERROR \
49.144 +| "$SHOWMAIL" \
49.145 +> out4.tmp
49.146 +
49.147 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "freebusy" \
49.148 +> out4f.tmp
49.149 +
49.150 + [ `grep "event26@example.com" "out4f.tmp" | wc -l` = '2' ] \
49.151 +&& grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out4f.tmp" \
49.152 +&& echo "Success" \
49.153 +|| echo "Failed"
49.154 +
49.155 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "freebusy_other" "$SENDER" \
49.156 +> out4o.tmp
49.157 +
49.158 + [ `grep "event26@example.com" "out4o.tmp" | wc -l` = '2' ] \
49.159 +&& grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out4o.tmp" \
49.160 +&& echo "Success" \
49.161 +|| echo "Failed"
49.162 +
49.163 + "$PERSON_SCRIPT" $ARGS < out32.tmp 2>> $ERROR \
49.164 +| "$SHOWMAIL" \
49.165 +> out42.tmp
49.166 +
49.167 + "$LIST_SCRIPT" $LIST_ARGS "$USER2" "freebusy" \
49.168 +> out4f2.tmp
49.169 +
49.170 + [ `grep "event26@example.com" "out4f2.tmp" | wc -l` = '2' ] \
49.171 +&& grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out4f2.tmp" \
49.172 +&& echo "Success" \
49.173 +|| echo "Failed"
49.174 +
49.175 + "$LIST_SCRIPT" $LIST_ARGS "$USER2" "freebusy_other" "$SENDER" \
49.176 +> out4o2.tmp
49.177 +
49.178 + [ `grep "event26@example.com" "out4o2.tmp" | wc -l` = '2' ] \
49.179 +&& grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out4o2.tmp" \
49.180 +&& echo "Success" \
49.181 +|| echo "Failed"
49.182 +
49.183 +# Test recurrence declining in the outgoing handler.
49.184 +# Only the first user declines.
49.185 +
49.186 + "$DECLINE_SCRIPT" $DECLINE_ARGS "$USER1" "20141011T100000" "20141011T110000" "event26@example.com" "20141011T100000" 2>> $ERROR \
49.187 +| tee out5.tmp \
49.188 +| "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR
49.189 +
49.190 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "freebusy" \
49.191 +> out5s.tmp
49.192 +
49.193 + [ `grep "event26@example.com" "out5s.tmp" | wc -l` = '1' ] \
49.194 +&& ! grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out5s.tmp" \
49.195 +&& echo "Success" \
49.196 +|| echo "Failed"
49.197 +
49.198 +# There should be a recurrence created by the user.
49.199 +
49.200 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "object" "event26@example.com" \
49.201 +> out5O.tmp
49.202 +
49.203 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "recurrence" "event26@example.com" "20141011T100000" \
49.204 +> out5R.tmp
49.205 +
49.206 + grep -q 'event26@example.com' "out5O.tmp" \
49.207 +&& grep -q 'event26@example.com' "out5R.tmp" \
49.208 +&& echo "Success" \
49.209 +|| echo "Failed"
49.210 +
49.211 +# Test declining in the incoming handler.
49.212 +
49.213 + "$PERSON_SCRIPT" $ARGS < out5.tmp 2>> $ERROR \
49.214 +| tee out6r.tmp \
49.215 +| "$SHOWMAIL" \
49.216 +> out6.tmp
49.217 +
49.218 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
49.219 +> out6f.tmp
49.220 +
49.221 + [ `grep "event26@example.com" "out6f.tmp" | wc -l` = '2' ] \
49.222 +&& grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out6f.tmp" \
49.223 +&& echo "Success" \
49.224 +|| echo "Failed"
49.225 +
49.226 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy_other" "$USER1" \
49.227 +> out6o.tmp
49.228 +
49.229 + [ `grep "event26@example.com" "out6o.tmp" | wc -l` = '1' ] \
49.230 +&& ! grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out6o.tmp" \
49.231 +&& echo "Success" \
49.232 +|| echo "Failed"
49.233 +
49.234 +# The second user is still attending the original event.
49.235 +
49.236 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy_other" "$USER2" \
49.237 +> out6o2.tmp
49.238 +
49.239 + [ `grep "event26@example.com" "out6o2.tmp" | wc -l` = '2' ] \
49.240 +&& grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out6o2.tmp" \
49.241 +&& echo "Success" \
49.242 +|| echo "Failed"
49.243 +
49.244 +# There should be a recurrence created by the user.
49.245 +
49.246 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "object" "event26@example.com" \
49.247 +> out6O.tmp
49.248 +
49.249 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "recurrence" "event26@example.com" "20141011T100000" \
49.250 +> out6R.tmp
49.251 +
49.252 + grep -q 'event26@example.com' "out6O.tmp" \
49.253 +&& grep -q 'event26@example.com' "out6R.tmp" \
49.254 +&& echo "Success" \
49.255 +|| echo "Failed"
49.256 +
49.257 +# This should cause the organiser to tell the second user about the recurrence.
49.258 +
49.259 + grep -q 'METHOD:REQUEST' out6.tmp \
49.260 +&& echo "Success" \
49.261 +|| echo "Failed"
49.262 +
49.263 + "$PERSON_SCRIPT" $ARGS < out6r.tmp 2>> $ERROR \
49.264 +| "$SHOWMAIL" \
49.265 +> out62.tmp
49.266 +
49.267 + "$LIST_SCRIPT" $LIST_ARGS "$USER2" "object" "event26@example.com" \
49.268 +> out6O2.tmp
49.269 +
49.270 + "$LIST_SCRIPT" $LIST_ARGS "$USER2" "recurrence" "event26@example.com" "20141011T100000" \
49.271 +> out6R2.tmp
49.272 +
49.273 +# The second user's schedule should remain unchanged.
49.274 +# NOTE: The nature of the periods might need to change, with the recurrence
49.275 +# NOTE: taking over the affected period.
49.276 +
49.277 + "$LIST_SCRIPT" $LIST_ARGS "$USER2" "freebusy" \
49.278 +> out6f2.tmp
49.279 +
49.280 + [ `grep "event26@example.com" "out6f2.tmp" | wc -l` = '2' ] \
49.281 +&& grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out6f2.tmp" \
49.282 +&& echo "Success" \
49.283 +|| echo "Failed"
49.284 +
49.285 +# Test recurrence acceptance in the outgoing handler.
49.286 +
49.287 + "$ACCEPT_SCRIPT" $ACCEPT_ARGS "$USER1" "20141011T100000" "20141011T110000" "event26@example.com" "20141011T100000" 2>> $ERROR \
49.288 +| tee out7.tmp \
49.289 +| "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR
49.290 +
49.291 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "freebusy" \
49.292 +> out7s.tmp
49.293 +
49.294 + [ `grep "event26@example.com" "out7s.tmp" | wc -l` = '2' ] \
49.295 +&& grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out7s.tmp" \
49.296 +&& echo "Success" \
49.297 +|| echo "Failed"
49.298 +
49.299 +# There should still be a recurrence created by the user.
49.300 +
49.301 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "object" "event26@example.com" \
49.302 +> out7O.tmp
49.303 +
49.304 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "recurrence" "event26@example.com" "20141011T100000" \
49.305 +> out7R.tmp
49.306 +
49.307 + grep -q 'event26@example.com' "out7O.tmp" \
49.308 +&& grep -q 'event26@example.com' "out7R.tmp" \
49.309 +&& echo "Success" \
49.310 +|| echo "Failed"
49.311 +
49.312 +# Test acceptance in the incoming handler.
49.313 +
49.314 + "$PERSON_SCRIPT" $ARGS < out7.tmp 2>> $ERROR \
49.315 +| "$SHOWMAIL" \
49.316 +> out8.tmp
49.317 +
49.318 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
49.319 +> out8f.tmp
49.320 +
49.321 + [ `grep "event26@example.com" "out8f.tmp" | wc -l` = '2' ] \
49.322 +&& grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out8f.tmp" \
49.323 +&& echo "Success" \
49.324 +|| echo "Failed"
49.325 +
49.326 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy_other" "$USER1" \
49.327 +> out8o.tmp
49.328 +
49.329 + [ `grep "event26@example.com" "out8o.tmp" | wc -l` = '2' ] \
49.330 +&& grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out8o.tmp" \
49.331 +&& echo "Success" \
49.332 +|| echo "Failed"
49.333 +
49.334 +# The second user should not have been affected.
49.335 +
49.336 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy_other" "$USER2" \
49.337 +> out8o2.tmp
49.338 +
49.339 + [ `grep "event26@example.com" "out8o2.tmp" | wc -l` = '2' ] \
49.340 +&& grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out8o2.tmp" \
49.341 +&& echo "Success" \
49.342 +|| echo "Failed"
49.343 +
49.344 +# There should be a recurrence created by the user.
49.345 +
49.346 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "object" "event26@example.com" \
49.347 +> out8O.tmp
49.348 +
49.349 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "recurrence" "event26@example.com" "20141011T100000" \
49.350 +> out8R.tmp
49.351 +
49.352 + grep -q 'event26@example.com' "out8O.tmp" \
49.353 +&& grep -q 'event26@example.com' "out8R.tmp" \
49.354 +&& echo "Success" \
49.355 +|| echo "Failed"
49.356 +
49.357 +# Test recurrence declining in the outgoing handler.
49.358 +# Now the second user declines the parent event.
49.359 +
49.360 + "$DECLINE_SCRIPT" $DECLINE_ARGS "$USER2" "event26@example.com" 2>> $ERROR \
49.361 +| tee out9.tmp \
49.362 +| "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR
49.363 +
49.364 + "$LIST_SCRIPT" $LIST_ARGS "$USER2" "freebusy" \
49.365 +> out9s.tmp
49.366 +
49.367 + [ `grep "event26@example.com" "out9s.tmp" | wc -l` = '0' ] \
49.368 +&& ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "out9s.tmp" \
49.369 +&& ! grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out9s.tmp" \
49.370 +&& echo "Success" \
49.371 +|| echo "Failed"
49.372 +
49.373 +# Test declining in the incoming handler.
49.374 +
49.375 + "$PERSON_SCRIPT" $ARGS < out9.tmp 2>> $ERROR \
49.376 +| "$SHOWMAIL" \
49.377 +> out10.tmp
49.378 +
49.379 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
49.380 +> out10f.tmp
49.381 +
49.382 + [ `grep "event26@example.com" "out10f.tmp" | wc -l` = '2' ] \
49.383 +&& grep -q "^20141010T080000Z${TAB}20141010T090000Z" "out10f.tmp" \
49.384 +&& grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out10f.tmp" \
49.385 +&& echo "Success" \
49.386 +|| echo "Failed"
49.387 +
49.388 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy_other" "$USER2" \
49.389 +> out10o.tmp
49.390 +
49.391 + [ `grep "event26@example.com" "out10o.tmp" | wc -l` = '1' ] \
49.392 +&& ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "out10o.tmp" \
49.393 +&& grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out10o.tmp" \
49.394 +&& echo "Success" \
49.395 +|| echo "Failed"
50.1 --- a/tests/test_person_invitation_recurring.sh Tue Apr 19 21:20:57 2016 +0200
50.2 +++ b/tests/test_person_invitation_recurring.sh Fri Apr 22 16:22:58 2016 +0200
50.3 @@ -4,16 +4,13 @@
50.4
50.5 USER="mailto:vincent.vole@example.com"
50.6 SENDER="mailto:paul.boddie@example.com"
50.7 -FBFILE="$STORE/$USER/freebusy"
50.8 -FBOTHERFILE="$STORE/$USER/freebusy-other/$SENDER"
50.9 -FBSENDERFILE="$STORE/$SENDER/freebusy"
50.10
50.11 mkdir -p "$PREFS/$USER"
50.12 echo 'Europe/Oslo' > "$PREFS/$USER/TZID"
50.13 echo 'share' > "$PREFS/$USER/freebusy_sharing"
50.14
50.15 mkdir -p "$PREFS/$SENDER"
50.16 -echo 'Europe/Oslo' > "$PREFS/$USER/TZID"
50.17 +echo 'Europe/Oslo' > "$PREFS/$SENDER/TZID"
50.18
50.19 # Test free/busy responses.
50.20
50.21 @@ -39,7 +36,10 @@
50.22
50.23 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-person-recurring.txt" 2>> $ERROR
50.24
50.25 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBSENDERFILE" \
50.26 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
50.27 +> out1f.tmp
50.28 +
50.29 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out1f.tmp" \
50.30 && echo "Success" \
50.31 || echo "Failed"
50.32
50.33 @@ -53,12 +53,17 @@
50.34 && echo "Success" \
50.35 || echo "Failed"
50.36
50.37 - ! [ -e "$FBFILE" ] \
50.38 -|| ! grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \
50.39 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
50.40 +> out2f.tmp
50.41 +
50.42 + ! grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out2f.tmp" \
50.43 && echo "Success" \
50.44 || echo "Failed"
50.45
50.46 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBOTHERFILE" \
50.47 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \
50.48 +> out2o.tmp
50.49 +
50.50 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out2o.tmp" \
50.51 && echo "Success" \
50.52 || echo "Failed"
50.53
50.54 @@ -68,7 +73,10 @@
50.55 | tee out3.tmp \
50.56 | "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR
50.57
50.58 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \
50.59 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
50.60 +> out3f.tmp
50.61 +
50.62 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out3f.tmp" \
50.63 && echo "Success" \
50.64 || echo "Failed"
50.65
50.66 @@ -76,8 +84,11 @@
50.67
50.68 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-person-recurring-instance.txt" 2>> $ERROR
50.69
50.70 - [ `grep "event8@example.com" "$FBSENDERFILE" | wc -l` = '2' ] \
50.71 -&& ! grep -q "^20141114T090000Z${TAB}20141114T100000Z" "$FBSENDERFILE" \
50.72 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
50.73 +> out3s.tmp
50.74 +
50.75 + [ `grep "event8@example.com" "out3s.tmp" | wc -l` = '2' ] \
50.76 +&& ! grep -q "^20141114T090000Z${TAB}20141114T100000Z" "out3s.tmp" \
50.77 && echo "Success" \
50.78 || echo "Failed"
50.79
50.80 @@ -91,13 +102,19 @@
50.81 && echo "Success" \
50.82 || echo "Failed"
50.83
50.84 - [ `grep "event8@example.com" "$FBFILE" | wc -l` = '2' ] \
50.85 -&& ! grep -q "^20141114T090000Z${TAB}20141114T100000Z" "$FBFILE" \
50.86 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
50.87 +> out4f.tmp
50.88 +
50.89 + [ `grep "event8@example.com" "out4f.tmp" | wc -l` = '2' ] \
50.90 +&& ! grep -q "^20141114T090000Z${TAB}20141114T100000Z" "out4f.tmp" \
50.91 && echo "Success" \
50.92 || echo "Failed"
50.93
50.94 - [ `grep "event8@example.com" "$FBOTHERFILE" | wc -l` = '2' ] \
50.95 -&& ! grep -q "^20141114T090000Z${TAB}20141114T100000Z" "$FBOTHERFILE" \
50.96 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \
50.97 +> out4o.tmp
50.98 +
50.99 + [ `grep "event8@example.com" "out4o.tmp" | wc -l` = '2' ] \
50.100 +&& ! grep -q "^20141114T090000Z${TAB}20141114T100000Z" "out4o.tmp" \
50.101 && echo "Success" \
50.102 || echo "Failed"
50.103
50.104 @@ -105,8 +122,11 @@
50.105
50.106 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-person-recurring-reschedule-instance.txt" 2>> $ERROR
50.107
50.108 - grep -q "^20141011T080000Z${TAB}20141011T090000Z" "$FBSENDERFILE" \
50.109 -&& ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "$FBSENDERFILE" \
50.110 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
50.111 +> out4s.tmp
50.112 +
50.113 + grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out4s.tmp" \
50.114 +&& ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "out4s.tmp" \
50.115 && echo "Success" \
50.116 || echo "Failed"
50.117
50.118 @@ -120,13 +140,19 @@
50.119 && echo "Success" \
50.120 || echo "Failed"
50.121
50.122 - ! grep -q "^20141011T080000Z${TAB}20141011T090000Z" "$FBFILE" \
50.123 -&& grep -q "^20141010T080000Z${TAB}20141010T090000Z" "$FBFILE" \
50.124 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
50.125 +> out5f.tmp
50.126 +
50.127 + ! grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out5f.tmp" \
50.128 +&& grep -q "^20141010T080000Z${TAB}20141010T090000Z" "out5f.tmp" \
50.129 && echo "Success" \
50.130 || echo "Failed"
50.131
50.132 - grep -q "^20141011T080000Z${TAB}20141011T090000Z" "$FBOTHERFILE" \
50.133 -&& ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "$FBOTHERFILE" \
50.134 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \
50.135 +> out5o.tmp
50.136 +
50.137 + grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out5o.tmp" \
50.138 +&& ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "out5o.tmp" \
50.139 && echo "Success" \
50.140 || echo "Failed"
50.141
50.142 @@ -136,8 +162,11 @@
50.143 | tee out6.tmp \
50.144 | "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR
50.145
50.146 - grep -q "^20141011T080000Z${TAB}20141011T090000Z" "$FBFILE" \
50.147 -&& ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "$FBFILE" \
50.148 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
50.149 +> out6f.tmp
50.150 +
50.151 + grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out6f.tmp" \
50.152 +&& ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "out6f.tmp" \
50.153 && echo "Success" \
50.154 || echo "Failed"
50.155
50.156 @@ -145,7 +174,10 @@
50.157
50.158 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-person-recurring.txt" 2>> $ERROR
50.159
50.160 - ! grep -q "event8@example.com" "$FBSENDERFILE" \
50.161 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
50.162 +> out6s.tmp
50.163 +
50.164 + ! grep -q "event8@example.com" "out6s.tmp" \
50.165 && echo "Success" \
50.166 || echo "Failed"
50.167
50.168 @@ -153,17 +185,23 @@
50.169
50.170 "$PERSON_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-person-recurring.txt" 2>> $ERROR \
50.171 | "$SHOWMAIL" \
50.172 -> out6.tmp
50.173 +> out7.tmp
50.174
50.175 - ! grep -q 'METHOD:REPLY' out6.tmp \
50.176 + ! grep -q 'METHOD:REPLY' out7.tmp \
50.177 && echo "Success" \
50.178 || echo "Failed"
50.179
50.180 - ! grep -q "event8@example.com" "$FBFILE" \
50.181 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
50.182 +> out7f.tmp
50.183 +
50.184 + ! grep -q "event8@example.com" "out7f.tmp" \
50.185 && echo "Success" \
50.186 || echo "Failed"
50.187
50.188 - ! grep -q "event8@example.com" "$FBOTHERFILE" \
50.189 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \
50.190 +> out7o.tmp
50.191 +
50.192 + ! grep -q "event8@example.com" "out7o.tmp" \
50.193 && echo "Success" \
50.194 || echo "Failed"
50.195
50.196 @@ -173,53 +211,74 @@
50.197
50.198 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-person-recurring.txt" 2>> $ERROR
50.199
50.200 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBSENDERFILE" \
50.201 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
50.202 +> out7s.tmp
50.203 +
50.204 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out7s.tmp" \
50.205 && echo "Success" \
50.206 || echo "Failed"
50.207
50.208 "$PERSON_SCRIPT" $ARGS < "$TEMPLATES/event-request-person-recurring.txt" 2>> $ERROR \
50.209 | "$SHOWMAIL" \
50.210 -> out7.tmp
50.211 +> out8.tmp
50.212
50.213 - ! grep -q 'METHOD:REPLY' out7.tmp \
50.214 + ! grep -q 'METHOD:REPLY' out8.tmp \
50.215 && echo "Success" \
50.216 || echo "Failed"
50.217
50.218 - ! grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \
50.219 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
50.220 +> out8f.tmp
50.221 +
50.222 + ! grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out8f.tmp" \
50.223 && echo "Success" \
50.224 || echo "Failed"
50.225
50.226 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBOTHERFILE" \
50.227 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \
50.228 +> out8o.tmp
50.229 +
50.230 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out8o.tmp" \
50.231 && echo "Success" \
50.232 || echo "Failed"
50.233
50.234 "$ACCEPT_SCRIPT" $ACCEPT_ARGS "$USER" "event8@example.com" 2>> $ERROR \
50.235 -| tee out8.tmp \
50.236 +| tee out9.tmp \
50.237 | "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR
50.238
50.239 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \
50.240 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
50.241 +> out9f.tmp
50.242 +
50.243 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out9f.tmp" \
50.244 && echo "Success" \
50.245 || echo "Failed"
50.246
50.247 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-person-recurring.txt" 2>> $ERROR
50.248
50.249 - ! grep -q "event8@example.com" "$FBSENDERFILE" \
50.250 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
50.251 +> out9s.tmp
50.252 +
50.253 + ! grep -q "event8@example.com" "out9s.tmp" \
50.254 && echo "Success" \
50.255 || echo "Failed"
50.256
50.257 "$PERSON_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-person-recurring.txt" 2>> $ERROR \
50.258 | "$SHOWMAIL" \
50.259 -> out9.tmp
50.260 +> out10.tmp
50.261
50.262 - ! grep -q 'METHOD:REPLY' out9.tmp \
50.263 + ! grep -q 'METHOD:REPLY' out10.tmp \
50.264 && echo "Success" \
50.265 || echo "Failed"
50.266
50.267 - ! grep -q "event8@example.com" "$FBFILE" \
50.268 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
50.269 +> out10f.tmp
50.270 +
50.271 + ! grep -q "event8@example.com" "out10f.tmp" \
50.272 && echo "Success" \
50.273 || echo "Failed"
50.274
50.275 - ! grep -q "event8@example.com" "$FBOTHERFILE" \
50.276 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \
50.277 +> out10o.tmp
50.278 +
50.279 + ! grep -q "event8@example.com" "out10o.tmp" \
50.280 && echo "Success" \
50.281 || echo "Failed"
50.282
50.283 @@ -228,32 +287,44 @@
50.284
50.285 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-person-recurring-day-floating.txt" 2>> $ERROR
50.286
50.287 - grep -q "^20141211T230000Z${TAB}20141212T230000Z" "$FBSENDERFILE" \
50.288 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
50.289 +> out10s.tmp
50.290 +
50.291 + grep -q "^20141211T230000Z${TAB}20141212T230000Z" "out10s.tmp" \
50.292 && echo "Success" \
50.293 || echo "Failed"
50.294
50.295 "$PERSON_SCRIPT" $ARGS < "$TEMPLATES/event-request-person-recurring-day-floating.txt" 2>> $ERROR \
50.296 | "$SHOWMAIL" \
50.297 -> out10.tmp
50.298 +> out11.tmp
50.299
50.300 - ! grep -q 'METHOD:REPLY' out10.tmp \
50.301 + ! grep -q 'METHOD:REPLY' out11.tmp \
50.302 && echo "Success" \
50.303 || echo "Failed"
50.304
50.305 - ! grep -q "^20141211T220000Z${TAB}20141212T220000Z" "$FBFILE" \
50.306 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
50.307 +> out11f.tmp
50.308 +
50.309 + ! grep -q "^20141211T220000Z${TAB}20141212T220000Z" "out11f.tmp" \
50.310 && echo "Success" \
50.311 || echo "Failed"
50.312
50.313 # (The organiser is not attending.)
50.314
50.315 - ! grep -q "^20141211T220000Z${TAB}20141212T220000Z" "$FBOTHERFILE" \
50.316 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \
50.317 +> out11o.tmp
50.318 +
50.319 + ! grep -q "^20141211T220000Z${TAB}20141212T220000Z" "out11o.tmp" \
50.320 && echo "Success" \
50.321 || echo "Failed"
50.322
50.323 "$ACCEPT_SCRIPT" $ACCEPT_ARGS "$USER" "event12@example.com" 2>> $ERROR \
50.324 -| tee out11.tmp \
50.325 +| tee out12.tmp \
50.326 | "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR
50.327
50.328 - grep -q "^20141211T220000Z${TAB}20141212T220000Z" "$FBFILE" \
50.329 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
50.330 +> out12f.tmp
50.331 +
50.332 + grep -q "^20141211T220000Z${TAB}20141212T220000Z" "out12f.tmp" \
50.333 && echo "Success" \
50.334 || echo "Failed"
51.1 --- a/tests/test_person_invitation_refresh.sh Tue Apr 19 21:20:57 2016 +0200
51.2 +++ b/tests/test_person_invitation_refresh.sh Fri Apr 22 16:22:58 2016 +0200
51.3 @@ -4,9 +4,6 @@
51.4
51.5 USER="mailto:vincent.vole@example.com"
51.6 SENDER="mailto:paul.boddie@example.com"
51.7 -FBFILE="$STORE/$USER/freebusy"
51.8 -FBOTHERFILE="$STORE/$USER/freebusy-other/$SENDER"
51.9 -FBSENDERFILE="$STORE/$SENDER/freebusy"
51.10
51.11 mkdir -p "$PREFS/$USER"
51.12 echo 'Europe/Oslo' > "$PREFS/$USER/TZID"
51.13 @@ -21,7 +18,10 @@
51.14
51.15 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-person-recurring.txt" 2>> $ERROR
51.16
51.17 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBSENDERFILE" \
51.18 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
51.19 +> out1f.tmp
51.20 +
51.21 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out1f.tmp" \
51.22 && echo "Success" \
51.23 || echo "Failed"
51.24
51.25 @@ -45,12 +45,17 @@
51.26 && echo "Success" \
51.27 || echo "Failed"
51.28
51.29 - ! [ -e "$FBFILE" ] \
51.30 -|| ! grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \
51.31 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
51.32 +> out3f.tmp
51.33 +
51.34 + ! grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out3f.tmp" \
51.35 && echo "Success" \
51.36 || echo "Failed"
51.37
51.38 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBOTHERFILE" \
51.39 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \
51.40 +> out3f.tmp
51.41 +
51.42 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out3f.tmp" \
51.43 && echo "Success" \
51.44 || echo "Failed"
51.45
51.46 @@ -60,7 +65,10 @@
51.47 | tee out4.tmp \
51.48 | "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR
51.49
51.50 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \
51.51 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
51.52 +> out4f.tmp
51.53 +
51.54 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out4f.tmp" \
51.55 && echo "Success" \
51.56 || echo "Failed"
51.57
51.58 @@ -78,8 +86,11 @@
51.59
51.60 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-person-recurring-reschedule-instance.txt" 2>> $ERROR
51.61
51.62 - grep -q "^20141011T080000Z${TAB}20141011T090000Z" "$FBSENDERFILE" \
51.63 -&& ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "$FBSENDERFILE" \
51.64 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
51.65 +> out5f.tmp
51.66 +
51.67 + grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out5f.tmp" \
51.68 +&& ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "out5f.tmp" \
51.69 && echo "Success" \
51.70 || echo "Failed"
51.71
51.72 @@ -102,8 +113,14 @@
51.73 | "$SHOWMAIL" \
51.74 > out6a.tmp
51.75
51.76 - [ -e "$STORE/$USER/objects/event8@example.com" ] \
51.77 -&& [ -e "$STORE/$USER/recurrences/event8@example.com/20141010T080000Z" ] \
51.78 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "object" "event8@example.com" \
51.79 +> out6O.tmp
51.80 +
51.81 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "recurrence" "event8@example.com" "20141010T080000Z" \
51.82 +> out6R.tmp
51.83 +
51.84 + grep -q 'event8@example.com' "out6O.tmp" \
51.85 +&& grep -q 'event8@example.com' "out6R.tmp" \
51.86 && echo "Success" \
51.87 || echo "Failed"
51.88
51.89 @@ -112,14 +129,26 @@
51.90
51.91 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-person-recurring-rescheduled-instance.txt" 2>> $ERROR
51.92
51.93 - [ -e "$STORE/$SENDER/objects/event8@example.com" ] \
51.94 -&& ! [ -e "$STORE/$SENDER/recurrences/event8@example.com/20141010T080000Z" ] \
51.95 -&& [ -e "$STORE/$SENDER/cancellations/recurrences/event8@example.com/20141010T080000Z" ] \
51.96 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "object" "event8@example.com" \
51.97 +> out6O2.tmp
51.98 +
51.99 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "recurrence" "event8@example.com" "20141010T080000Z" \
51.100 +> out6R2.tmp
51.101 +
51.102 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "cancelled_recurrences" "event8@example.com" \
51.103 +> out6C.tmp
51.104 +
51.105 + grep -q 'event8@example.com' "out6O2.tmp" \
51.106 +&& ! grep -q 'event8@example.com' "out6R2.tmp" \
51.107 +&& grep -q '20141010T080000Z' "out6C.tmp" \
51.108 && echo "Success" \
51.109 || echo "Failed"
51.110
51.111 - ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "$FBSENDERFILE" \
51.112 -&& ! grep -q "^20141011T080000Z${TAB}20141011T090000Z" "$FBSENDERFILE" \
51.113 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
51.114 +> out6f.tmp
51.115 +
51.116 + ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "out6f.tmp" \
51.117 +&& ! grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out6f.tmp" \
51.118 && echo "Success" \
51.119 || echo "Failed"
51.120
51.121 @@ -143,9 +172,18 @@
51.122 | "$SHOWMAIL" \
51.123 > out7a.tmp
51.124
51.125 - [ -e "$STORE/$USER/objects/event8@example.com" ] \
51.126 -&& ! [ -e "$STORE/$USER/recurrences/event8@example.com/20141010T080000Z" ] \
51.127 -&& [ -e "$STORE/$USER/cancellations/recurrences/event8@example.com/20141010T080000Z" ] \
51.128 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "object" "event8@example.com" \
51.129 +> out7O.tmp
51.130 +
51.131 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "recurrence" "event8@example.com" "20141010T080000Z" \
51.132 +> out7R.tmp
51.133 +
51.134 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "cancelled_recurrences" "event8@example.com" \
51.135 +> out7C.tmp
51.136 +
51.137 + grep -q 'event8@example.com' "out7O.tmp" \
51.138 +&& ! grep -q 'event8@example.com' "out7R.tmp" \
51.139 +&& grep -q '20141010T080000Z' "out7C.tmp" \
51.140 && echo "Success" \
51.141 || echo "Failed"
51.142
51.143 @@ -153,8 +191,11 @@
51.144
51.145 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-add-person-recurring-rescheduled-instance.txt" 2>> $ERROR
51.146
51.147 - grep -q "^20141010T080000Z${TAB}20141010T090000Z" "$FBSENDERFILE" \
51.148 -&& ! grep -q "^20141011T080000Z${TAB}20141011T090000Z" "$FBSENDERFILE" \
51.149 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
51.150 +> out7f.tmp
51.151 +
51.152 + grep -q "^20141010T080000Z${TAB}20141010T090000Z" "out7f.tmp" \
51.153 +&& ! grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out7f.tmp" \
51.154 && echo "Success" \
51.155 || echo "Failed"
51.156
51.157 @@ -162,17 +203,32 @@
51.158 | "$SHOWMAIL" \
51.159 > out8.tmp
51.160
51.161 - [ -e "$STORE/$USER/objects/event8@example.com" ] \
51.162 -&& [ -e "$STORE/$USER/recurrences/event8@example.com/20141010T080000Z" ] \
51.163 -&& ! [ -e "$STORE/$USER/cancellations/recurrences/event8@example.com/20141010T080000Z" ] \
51.164 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "object" "event8@example.com" \
51.165 +> out8O.tmp
51.166 +
51.167 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "recurrence" "event8@example.com" "20141010T080000Z" \
51.168 +> out8R.tmp
51.169 +
51.170 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "cancelled_recurrences" "event8@example.com" \
51.171 +> out8C.tmp
51.172 +
51.173 + grep -q 'event8@example.com' "out8O.tmp" \
51.174 +&& grep -q 'event8@example.com' "out8R.tmp" \
51.175 +&& ! grep -q '20141010T080000Z' "out8C.tmp" \
51.176 && echo "Success" \
51.177 || echo "Failed"
51.178
51.179 - ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "$FBFILE" \
51.180 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
51.181 +> out8f.tmp
51.182 +
51.183 + ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "out8f.tmp" \
51.184 && echo "Success" \
51.185 || echo "Failed"
51.186
51.187 - grep -q "^20141010T080000Z${TAB}20141010T090000Z" "$FBOTHERFILE" \
51.188 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \
51.189 +> out8f.tmp
51.190 +
51.191 + grep -q "^20141010T080000Z${TAB}20141010T090000Z" "out8f.tmp" \
51.192 && echo "Success" \
51.193 || echo "Failed"
51.194
51.195 @@ -182,7 +238,10 @@
51.196 | tee out9.tmp \
51.197 | "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR
51.198
51.199 - grep -q "^20141010T080000Z${TAB}20141010T090000Z" "$FBFILE" \
51.200 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
51.201 +> out9f.tmp
51.202 +
51.203 + grep -q "^20141010T080000Z${TAB}20141010T090000Z" "out9f.tmp" \
51.204 && echo "Success" \
51.205 || echo "Failed"
51.206
51.207 @@ -205,7 +264,13 @@
51.208 | "$SHOWMAIL" \
51.209 > out11.tmp
51.210
51.211 - [ -e "$STORE/$USER/objects/event8@example.com" ] \
51.212 -&& [ -e "$STORE/$USER/recurrences/event8@example.com/20141010T080000Z" ] \
51.213 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "object" "event8@example.com" \
51.214 +> out11O.tmp
51.215 +
51.216 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "recurrence" "event8@example.com" "20141010T080000Z" \
51.217 +> out11R.tmp
51.218 +
51.219 + grep -q 'event8@example.com' "out11O.tmp" \
51.220 +&& grep -q 'event8@example.com' "out11R.tmp" \
51.221 && echo "Success" \
51.222 || echo "Failed"
52.1 --- a/tests/test_person_non_participation.sh Tue Apr 19 21:20:57 2016 +0200
52.2 +++ b/tests/test_person_non_participation.sh Fri Apr 22 16:22:58 2016 +0200
52.3 @@ -5,9 +5,6 @@
52.4 USER="mailto:vincent.vole@example.com"
52.5 IMPOSTER="mailto:oliver.otter@example.com"
52.6 SENDER="mailto:paul.boddie@example.com"
52.7 -FBFILE="$STORE/$USER/freebusy"
52.8 -FBOTHERFILE="$STORE/$USER/freebusy-other/$SENDER"
52.9 -FBIMPOSTERFILE="$STORE/$SENDER/freebusy-other/$IMPOSTER"
52.10
52.11 mkdir -p "$PREFS/$USER"
52.12 echo 'no' > "$PREFS/$USER/participating"
52.13 @@ -27,7 +24,10 @@
52.14
52.15 "$OUTGOING_SCRIPT" < "$TEMPLATES/event-request-person.txt" $ARGS 2>> $ERROR
52.16
52.17 - [ -e "$STORE/$SENDER/objects/event6@example.com" ] \
52.18 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "object" "event6@example.com" \
52.19 +> out0O.tmp
52.20 +
52.21 + grep -q "event6@example.com" "out0O.tmp" \
52.22 && echo "Success" \
52.23 || echo "Failed"
52.24
52.25 @@ -41,11 +41,17 @@
52.26 && echo "Success" \
52.27 || echo "Failed"
52.28
52.29 - ! [ -e "$FBFILE" ] \
52.30 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
52.31 +> out2f.tmp
52.32 +
52.33 + ! grep -q "event6@example.com" "out2f.tmp" \
52.34 && echo "Success" \
52.35 || echo "Failed"
52.36
52.37 - ! [ -e "$FBOTHERFILE" ] \
52.38 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \
52.39 +> out2s.tmp
52.40 +
52.41 + ! grep -q "event6@example.com" "out2s.tmp" \
52.42 && echo "Success" \
52.43 || echo "Failed"
52.44
52.45 @@ -55,8 +61,10 @@
52.46 | tee out3.tmp \
52.47 | "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR
52.48
52.49 - ! [ -e "$FBFILE" ] \
52.50 -|| ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE" \
52.51 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
52.52 +> out2f.tmp
52.53 +
52.54 + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out2f.tmp" \
52.55 && echo "Success" \
52.56 || echo "Failed"
52.57
52.58 @@ -68,12 +76,16 @@
52.59 | "$SHOWMAIL" \
52.60 > out5.tmp
52.61
52.62 - [ -e "$STORE/$SENDER/objects/event6@example.com" ] \
52.63 -&& ! grep -q "otter" "$STORE/$SENDER/objects/event6@example.com" \
52.64 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "object" "event6@example.com" \
52.65 +> out5O.tmp
52.66 +
52.67 + ! grep -q "otter" "out5O.tmp" \
52.68 && echo "Success" \
52.69 || echo "Failed"
52.70
52.71 - ( ! [ -e "$FBIMPOSTERFILE" ] \
52.72 - || ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBIMPOSTERFILE") \
52.73 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy_other" "$IMPOSTER" \
52.74 +> out5s.tmp
52.75 +
52.76 + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out5s.tmp" \
52.77 && echo "Success" \
52.78 || echo "Failed"
53.1 --- a/tests/test_resource_invitation_add.sh Tue Apr 19 21:20:57 2016 +0200
53.2 +++ b/tests/test_resource_invitation_add.sh Fri Apr 22 16:22:58 2016 +0200
53.3 @@ -4,9 +4,6 @@
53.4
53.5 USER="mailto:resource-room-confroom@example.com"
53.6 SENDER="mailto:paul.boddie@example.com"
53.7 -FBFILE="$STORE/$USER/freebusy"
53.8 -FBSENDERFILE="$STORE/$SENDER/freebusy"
53.9 -FBSENDEROTHERFILE="$STORE/$SENDER/freebusy-other/$USER"
53.10
53.11 mkdir -p "$PREFS/$USER"
53.12 echo 'Europe/Oslo' > "$PREFS/$USER/TZID"
53.13 @@ -20,7 +17,9 @@
53.14
53.15 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-recurring.txt" 2>> $ERROR
53.16
53.17 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBSENDERFILE" \
53.18 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
53.19 +| tee out0f.tmp \
53.20 +| grep -q "^20141212T090000Z${TAB}20141212T100000Z" \
53.21 && echo "Success" \
53.22 || echo "Failed"
53.23
53.24 @@ -34,7 +33,9 @@
53.25 && echo "Success" \
53.26 || echo "Failed"
53.27
53.28 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \
53.29 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
53.30 +| tee out1f.tmp \
53.31 +| grep -q "^20141212T090000Z${TAB}20141212T100000Z" \
53.32 && echo "Success" \
53.33 || echo "Failed"
53.34
53.35 @@ -44,7 +45,9 @@
53.36 | "$SHOWMAIL" \
53.37 > out2.tmp
53.38
53.39 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBSENDEROTHERFILE" \
53.40 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy_other" "$USER" \
53.41 +| tee out2o.tmp \
53.42 +| grep -q "^20141212T090000Z${TAB}20141212T100000Z" \
53.43 && echo "Success" \
53.44 || echo "Failed"
53.45
53.46 @@ -52,8 +55,11 @@
53.47
53.48 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-add-recurring.txt" 2>> $ERROR
53.49
53.50 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBSENDERFILE" \
53.51 -&& grep -q "^20150109T090000Z${TAB}20150109T100000Z" "$FBSENDERFILE" \
53.52 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
53.53 +> out2f.tmp
53.54 +
53.55 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out2f.tmp" \
53.56 +&& grep -q "^20150109T090000Z${TAB}20150109T100000Z" "out2f.tmp" \
53.57 && echo "Success" \
53.58 || echo "Failed"
53.59
53.60 @@ -67,8 +73,11 @@
53.61 && echo "Success" \
53.62 || echo "Failed"
53.63
53.64 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \
53.65 -&& ! grep -q "^20150109T090000Z${TAB}20150109T100000Z" "$FBFILE" \
53.66 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
53.67 +> out3f.tmp
53.68 +
53.69 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out3f.tmp" \
53.70 +&& ! grep -q "^20150109T090000Z${TAB}20150109T100000Z" "out3f.tmp" \
53.71 && echo "Success" \
53.72 || echo "Failed"
53.73
53.74 @@ -94,8 +103,11 @@
53.75 && echo "Success" \
53.76 || echo "Failed"
53.77
53.78 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \
53.79 -&& grep -q "^20150109T090000Z${TAB}20150109T100000Z" "$FBFILE" \
53.80 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
53.81 +> out5f.tmp
53.82 +
53.83 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out5f.tmp" \
53.84 +&& grep -q "^20150109T090000Z${TAB}20150109T100000Z" "out5f.tmp" \
53.85 && echo "Success" \
53.86 || echo "Failed"
53.87
53.88 @@ -105,7 +117,10 @@
53.89 | "$SHOWMAIL" \
53.90 > out6.tmp
53.91
53.92 - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBSENDEROTHERFILE" \
53.93 -&& grep -q "^20150109T090000Z${TAB}20150109T100000Z" "$FBSENDEROTHERFILE" \
53.94 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy_other" "$USER" \
53.95 +> out6o.tmp
53.96 +
53.97 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out6o.tmp" \
53.98 +&& grep -q "^20150109T090000Z${TAB}20150109T100000Z" "out6o.tmp" \
53.99 && echo "Success" \
53.100 || echo "Failed"
54.1 --- a/tests/test_resource_invitation_constraints.sh Tue Apr 19 21:20:57 2016 +0200
54.2 +++ b/tests/test_resource_invitation_constraints.sh Fri Apr 22 16:22:58 2016 +0200
54.3 @@ -5,12 +5,6 @@
54.4 USER="mailto:resource-room-sauna@example.com"
54.5 SENDER="mailto:paul.boddie@example.com"
54.6 RIVALSENDER="mailto:vincent.vole@example.com"
54.7 -FBFILE="$STORE/$USER/freebusy"
54.8 -FBOFFERFILE="$STORE/$USER/freebusy-offers"
54.9 -FBSENDERFILE="$STORE/$SENDER/freebusy"
54.10 -FBSENDEROTHERFILE="$STORE/$SENDER/freebusy-other/$USER"
54.11 -FBSENDERREQUESTS="$STORE/$SENDER/requests"
54.12 -FBRIVALSENDERFILE="$STORE/$RIVALSENDER/freebusy"
54.13
54.14 mkdir -p "$PREFS/$USER"
54.15 echo 'Europe/Oslo' > "$PREFS/$USER/TZID"
54.16 @@ -32,7 +26,9 @@
54.17
54.18 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-sauna-bad.txt" 2>> $ERROR
54.19
54.20 - grep -q "^20141126T151000Z${TAB}20141126T154500Z" "$FBSENDERFILE" \
54.21 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
54.22 +| tee out0f.tmp \
54.23 +| grep -q "^20141126T151000Z${TAB}20141126T154500Z" \
54.24 && echo "Success" \
54.25 || echo "Failed"
54.26
54.27 @@ -48,12 +44,16 @@
54.28 && echo "Success" \
54.29 || echo "Failed"
54.30
54.31 - ! [ -e "$FBFILE" ] \
54.32 -|| ! grep -q "^20141126T151500Z${TAB}20141126T154500Z" "$FBFILE" \
54.33 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
54.34 +> out1f.tmp
54.35 +
54.36 + ! grep -q "^20141126T151500Z${TAB}20141126T154500Z" "out1f.tmp" \
54.37 && echo "Success" \
54.38 || echo "Failed"
54.39
54.40 - grep -q "^20141126T151500Z${TAB}20141126T154500Z" "$FBOFFERFILE" \
54.41 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_offers" \
54.42 +| tee out1o.tmp \
54.43 +| grep -q "^20141126T151500Z${TAB}20141126T154500Z" \
54.44 && echo "Success" \
54.45 || echo "Failed"
54.46
54.47 @@ -64,20 +64,28 @@
54.48 | "$SHOWMAIL" \
54.49 > out2.tmp
54.50
54.51 - ( ! [ -e "$FBSENDEROTHERFILE" ] \
54.52 - || ! grep -q "^20141126T151000Z${TAB}20141126T154500Z" "$FBSENDEROTHERFILE") \
54.53 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy_other" "$USER" \
54.54 +> out2f.tmp
54.55 +
54.56 + ! grep -q "^20141126T151000Z${TAB}20141126T154500Z" "out2f.tmp" \
54.57 && echo "Success" \
54.58 || echo "Failed"
54.59
54.60 - grep -q 'DTSTART;TZID=Europe/Oslo.*:20141126T161000' "$STORE/$SENDER/objects/event13@example.com" \
54.61 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "object" "event13@example.com" \
54.62 +| tee out2O.tmp \
54.63 +| grep -q 'DTSTART;TZID=Europe/Oslo.*:20141126T161000' \
54.64 && echo "Success" \
54.65 || echo "Failed"
54.66
54.67 - grep -q 'DTSTART;TZID=Europe/Oslo.*:20141126T161500' "$STORE/$SENDER/counters/objects/event13@example.com/$USER" \
54.68 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "countered_object" "event13@example.com" "$USER" \
54.69 +| tee out2C.tmp \
54.70 +| grep -q 'DTSTART;TZID=Europe/Oslo.*:20141126T161500' \
54.71 && echo "Success" \
54.72 || echo "Failed"
54.73
54.74 - grep -q 'event13@example.com' "$FBSENDERREQUESTS" \
54.75 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "requests" \
54.76 +| tee out2r.tmp \
54.77 +| grep -q 'event13@example.com' \
54.78 && echo "Success" \
54.79 || echo "Failed"
54.80
54.81 @@ -85,8 +93,11 @@
54.82
54.83 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-sauna-rival.txt" 2>> $ERROR
54.84
54.85 - ! grep -q "^20141126T151000Z${TAB}20141126T154500Z" "$FBRIVALSENDERFILE" \
54.86 -&& grep -q "^20141126T153000Z${TAB}20141126T154500Z" "$FBRIVALSENDERFILE" \
54.87 + "$LIST_SCRIPT" $LIST_ARGS "$RIVALSENDER" "freebusy" \
54.88 +> out2R.tmp
54.89 +
54.90 + ! grep -q "^20141126T151000Z${TAB}20141126T154500Z" "out2R.tmp" \
54.91 +&& grep -q "^20141126T153000Z${TAB}20141126T154500Z" "out2R.tmp" \
54.92 && echo "Success" \
54.93 || echo "Failed"
54.94
54.95 @@ -108,12 +119,17 @@
54.96 | "$SHOWMAIL" \
54.97 > out4.tmp
54.98
54.99 - ! grep -q "^20141126T151000Z${TAB}20141126T154500Z" "$FBRIVALSENDERFILE" \
54.100 -&& grep -q "^20141126T153000Z${TAB}20141126T154500Z" "$FBRIVALSENDERFILE" \
54.101 + "$LIST_SCRIPT" $LIST_ARGS "$RIVALSENDER" "freebusy" \
54.102 +> out4R.tmp
54.103 +
54.104 + ! grep -q "^20141126T151000Z${TAB}20141126T154500Z" "out4R.tmp" \
54.105 +&& grep -q "^20141126T153000Z${TAB}20141126T154500Z" "out4R.tmp" \
54.106 && echo "Success" \
54.107 || echo "Failed"
54.108
54.109 - grep -q 'ATTENDEE.*;PARTSTAT=DECLINED' "$STORE/$RIVALSENDER/objects/event18@example.com" \
54.110 + "$LIST_SCRIPT" $LIST_ARGS "$RIVALSENDER" "object" "event18@example.com" \
54.111 +| tee out4O.tmp \
54.112 +| grep -q 'ATTENDEE.*;PARTSTAT=DECLINED' \
54.113 && echo "Success" \
54.114 || echo "Failed"
54.115
54.116 @@ -134,9 +150,12 @@
54.117
54.118 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-sauna-good.txt" 2>> $ERROR
54.119
54.120 - ! grep -q "^20141126T151000Z${TAB}20141126T154500Z" "$FBSENDERFILE" \
54.121 -&& ! grep -q "^20141126T151500Z${TAB}20141126T154500Z" "$FBSENDERFILE" \
54.122 -&& ! grep -q "^20141126T153000Z${TAB}20141126T154500Z" "$FBSENDERFILE" \
54.123 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
54.124 +> out5f.tmp
54.125 +
54.126 + ! grep -q "^20141126T151000Z${TAB}20141126T154500Z" "out5f.tmp" \
54.127 +&& ! grep -q "^20141126T151500Z${TAB}20141126T154500Z" "out5f.tmp" \
54.128 +&& ! grep -q "^20141126T153000Z${TAB}20141126T154500Z" "out5f.tmp" \
54.129 && echo "Success" \
54.130 || echo "Failed"
54.131
54.132 @@ -144,7 +163,10 @@
54.133 && echo "Success" \
54.134 || echo "Failed"
54.135
54.136 - ! grep -q 'event13@example.com' "$FBSENDERREQUESTS" \
54.137 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "requests" \
54.138 +> out5r.tmp
54.139 +
54.140 + ! grep -q 'event13@example.com' "out5r.tmp" \
54.141 && echo "Success" \
54.142 || echo "Failed"
54.143
54.144 @@ -159,13 +181,18 @@
54.145 && echo "Success" \
54.146 || echo "Failed"
54.147
54.148 - grep -q "^20141126T150000Z${TAB}20141126T154500Z" "$FBFILE" \
54.149 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
54.150 +| tee out6f.tmp \
54.151 +| grep -q "^20141126T150000Z${TAB}20141126T154500Z" \
54.152 && echo "Success" \
54.153 || echo "Failed"
54.154
54.155 - ! grep -q "^20141126T150000Z${TAB}20141126T154500Z" "$FBOFFERFILE" \
54.156 -&& ! grep -q "^20141126T151500Z${TAB}20141126T154500Z" "$FBOFFERFILE" \
54.157 -&& ! grep -q "^20141126T153000Z${TAB}20141126T154500Z" "$FBOFFERFILE" \
54.158 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_offers" \
54.159 +> out6o.tmp
54.160 +
54.161 + ! grep -q "^20141126T150000Z${TAB}20141126T154500Z" "out6o.tmp" \
54.162 +&& ! grep -q "^20141126T151500Z${TAB}20141126T154500Z" "out6o.tmp" \
54.163 +&& ! grep -q "^20141126T153000Z${TAB}20141126T154500Z" "out6o.tmp" \
54.164 && echo "Success" \
54.165 || echo "Failed"
54.166
54.167 @@ -176,10 +203,13 @@
54.168 | "$SHOWMAIL" \
54.169 > out7.tmp
54.170
54.171 - grep -q "^20141126T150000Z${TAB}20141126T154500Z" "$FBSENDERFILE" \
54.172 -&& ! grep -q "^20141126T151000Z${TAB}20141126T154500Z" "$FBSENDERFILE" \
54.173 -&& ! grep -q "^20141126T151500Z${TAB}20141126T154500Z" "$FBSENDERFILE" \
54.174 -&& ! grep -q "^20141126T153000Z${TAB}20141126T154500Z" "$FBSENDERFILE" \
54.175 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
54.176 +> out7f.tmp
54.177 +
54.178 + grep -q "^20141126T150000Z${TAB}20141126T154500Z" "out7f.tmp" \
54.179 +&& ! grep -q "^20141126T151000Z${TAB}20141126T154500Z" "out7f.tmp" \
54.180 +&& ! grep -q "^20141126T151500Z${TAB}20141126T154500Z" "out7f.tmp" \
54.181 +&& ! grep -q "^20141126T153000Z${TAB}20141126T154500Z" "out7f.tmp" \
54.182 && echo "Success" \
54.183 || echo "Failed"
54.184
54.185 @@ -187,7 +217,10 @@
54.186 && echo "Success" \
54.187 || echo "Failed"
54.188
54.189 - ! grep -q 'event13@example.com' "$FBSENDERREQUESTS" \
54.190 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "requests" \
54.191 +> out7r.tmp
54.192 +
54.193 + ! grep -q 'event13@example.com' "out7r.tmp" \
54.194 && echo "Success" \
54.195 || echo "Failed"
54.196
55.1 --- a/tests/test_resource_invitation_constraints_alternative.sh Tue Apr 19 21:20:57 2016 +0200
55.2 +++ b/tests/test_resource_invitation_constraints_alternative.sh Fri Apr 22 16:22:58 2016 +0200
55.3 @@ -5,12 +5,6 @@
55.4 USER="mailto:resource-room-sauna@example.com"
55.5 SENDER="mailto:paul.boddie@example.com"
55.6 RIVALSENDER="mailto:vincent.vole@example.com"
55.7 -FBFILE="$STORE/$USER/freebusy"
55.8 -FBOFFERFILE="$STORE/$USER/freebusy-offers"
55.9 -FBSENDERFILE="$STORE/$SENDER/freebusy"
55.10 -FBSENDEROTHERFILE="$STORE/$SENDER/freebusy-other/$USER"
55.11 -FBSENDERREQUESTS="$STORE/$SENDER/requests"
55.12 -FBRIVALSENDERFILE="$STORE/$RIVALSENDER/freebusy"
55.13
55.14 mkdir -p "$PREFS/$USER"
55.15 echo 'Europe/Oslo' > "$PREFS/$USER/TZID"
55.16 @@ -32,7 +26,9 @@
55.17
55.18 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-sauna-bad.txt" 2>> $ERROR
55.19
55.20 - grep -q "^20141126T151000Z${TAB}20141126T154500Z" "$FBSENDERFILE" \
55.21 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
55.22 +| tee out0f.tmp \
55.23 +| grep -q "^20141126T151000Z${TAB}20141126T154500Z" \
55.24 && echo "Success" \
55.25 || echo "Failed"
55.26
55.27 @@ -47,12 +43,16 @@
55.28 && echo "Success" \
55.29 || echo "Failed"
55.30
55.31 - ! [ -e "$FBFILE" ] \
55.32 -|| ! grep -q "^20141126T151500Z${TAB}20141126T154500Z" "$FBFILE" \
55.33 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
55.34 +> out1f.tmp
55.35 +
55.36 + ! grep -q "^20141126T151500Z${TAB}20141126T154500Z" "out1f.tmp" \
55.37 && echo "Success" \
55.38 || echo "Failed"
55.39
55.40 - grep -q "^20141126T151500Z${TAB}20141126T154500Z" "$FBOFFERFILE" \
55.41 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_offers" \
55.42 +| tee out1o.tmp \
55.43 +| grep -q "^20141126T151500Z${TAB}20141126T154500Z" \
55.44 && echo "Success" \
55.45 || echo "Failed"
55.46
55.47 @@ -63,21 +63,29 @@
55.48 | "$SHOWMAIL" \
55.49 > out2.tmp
55.50
55.51 - [ ! -e "$FBSENDEROTHERFILE" ] \
55.52 -|| ( ! grep -q "^20141126T151000Z${TAB}20141126T154500Z" "$FBSENDEROTHERFILE" \
55.53 - && ! grep -q "^20141126T151500Z${TAB}20141126T154500Z" "$FBSENDEROTHERFILE" ) \
55.54 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy_other" "$USER" \
55.55 +> out2f.tmp
55.56 +
55.57 + ! grep -q "^20141126T151000Z${TAB}20141126T154500Z" "out2f.tmp" \
55.58 +&& ! grep -q "^20141126T151500Z${TAB}20141126T154500Z" "out2f.tmp" \
55.59 && echo "Success" \
55.60 || echo "Failed"
55.61
55.62 - grep -q 'DTSTART;TZID=Europe/Oslo.*:20141126T161000' "$STORE/$SENDER/objects/event13@example.com" \
55.63 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "object" "event13@example.com" \
55.64 +| tee out2O.tmp \
55.65 +| grep -q 'DTSTART;TZID=Europe/Oslo.*:20141126T161000' \
55.66 && echo "Success" \
55.67 || echo "Failed"
55.68
55.69 - grep -q 'DTSTART;TZID=Europe/Oslo.*:20141126T161500' "$STORE/$SENDER/counters/objects/event13@example.com/$USER" \
55.70 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "countered_object" "event13@example.com" "$USER" \
55.71 +| tee out2C.tmp \
55.72 +| grep -q 'DTSTART;TZID=Europe/Oslo.*:20141126T161500' \
55.73 && echo "Success" \
55.74 || echo "Failed"
55.75
55.76 - grep -q 'event13@example.com' "$FBSENDERREQUESTS" \
55.77 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "requests" \
55.78 +| tee out2R.tmp \
55.79 +| grep -q 'event13@example.com' \
55.80 && echo "Success" \
55.81 || echo "Failed"
55.82
55.83 @@ -94,12 +102,17 @@
55.84 | "$SHOWMAIL" \
55.85 > out4.tmp
55.86
55.87 - ! [ -e "$FBFILE" ] \
55.88 -|| ! grep -q "^20141126T151500Z${TAB}20141126T154500Z" "$FBFILE" \
55.89 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
55.90 +> out4f.tmp
55.91 +
55.92 + ! grep -q "^20141126T151500Z${TAB}20141126T154500Z" "out4f.tmp" \
55.93 && echo "Success" \
55.94 || echo "Failed"
55.95
55.96 - ! grep -q "^20141126T151500Z${TAB}20141126T154500Z" "$FBOFFERFILE" \
55.97 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_offers" \
55.98 +> out4o.tmp
55.99 +
55.100 + ! grep -q "^20141126T151500Z${TAB}20141126T154500Z" "out4o.tmp" \
55.101 && echo "Success" \
55.102 || echo "Failed"
55.103
55.104 @@ -107,8 +120,11 @@
55.105
55.106 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-sauna-rival.txt" 2>> $ERROR
55.107
55.108 - ! grep -q "^20141126T151000Z${TAB}20141126T154500Z" "$FBRIVALSENDERFILE" \
55.109 -&& grep -q "^20141126T153000Z${TAB}20141126T154500Z" "$FBRIVALSENDERFILE" \
55.110 + "$LIST_SCRIPT" $LIST_ARGS "$RIVALSENDER" "freebusy" \
55.111 +> out4r.tmp
55.112 +
55.113 + ! grep -q "^20141126T151000Z${TAB}20141126T154500Z" "out4r.tmp" \
55.114 +&& grep -q "^20141126T153000Z${TAB}20141126T154500Z" "out4r.tmp" \
55.115 && echo "Success" \
55.116 || echo "Failed"
55.117
55.118 @@ -123,11 +139,16 @@
55.119 && echo "Success" \
55.120 || echo "Failed"
55.121
55.122 - grep -q "^20141126T153000Z${TAB}20141126T154500Z" "$FBFILE" \
55.123 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
55.124 +| tee out5f.tmp \
55.125 +| grep -q "^20141126T153000Z${TAB}20141126T154500Z" \
55.126 && echo "Success" \
55.127 || echo "Failed"
55.128
55.129 - ! grep -q "^20141126T153000Z${TAB}20141126T154500Z" "$FBOFFERFILE" \
55.130 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_offers" \
55.131 +> out5o.tmp
55.132 +
55.133 + ! grep -q "^20141126T153000Z${TAB}20141126T154500Z" "out5o.tmp" \
55.134 && echo "Success" \
55.135 || echo "Failed"
55.136
55.137 @@ -137,13 +158,18 @@
55.138 | "$SHOWMAIL" \
55.139 > out6.tmp
55.140
55.141 - ! grep -q "^20141126T151000Z${TAB}20141126T154500Z" "$FBRIVALSENDERFILE" \
55.142 -&& ! grep -q "^20141126T151500Z${TAB}20141126T154500Z" "$FBRIVALSENDERFILE" \
55.143 -&& grep -q "^20141126T153000Z${TAB}20141126T154500Z" "$FBRIVALSENDERFILE" \
55.144 + "$LIST_SCRIPT" $LIST_ARGS "$RIVALSENDER" "freebusy" \
55.145 +> out6r.tmp
55.146 +
55.147 + ! grep -q "^20141126T151000Z${TAB}20141126T154500Z" "out6r.tmp" \
55.148 +&& ! grep -q "^20141126T151500Z${TAB}20141126T154500Z" "out6r.tmp" \
55.149 +&& grep -q "^20141126T153000Z${TAB}20141126T154500Z" "out6r.tmp" \
55.150 && echo "Success" \
55.151 || echo "Failed"
55.152
55.153 - grep -q 'ATTENDEE.*;PARTSTAT=ACCEPTED' "$STORE/$RIVALSENDER/objects/event18@example.com" \
55.154 + "$LIST_SCRIPT" $LIST_ARGS "$RIVALSENDER" "object" "event18@example.com" \
55.155 +| tee out6O.tmp \
55.156 +| grep -q 'ATTENDEE.*;PARTSTAT=ACCEPTED' \
55.157 && echo "Success" \
55.158 || echo "Failed"
55.159
56.1 --- a/tests/test_resource_invitation_constraints_multiple.sh Tue Apr 19 21:20:57 2016 +0200
56.2 +++ b/tests/test_resource_invitation_constraints_multiple.sh Fri Apr 22 16:22:58 2016 +0200
56.3 @@ -5,9 +5,6 @@
56.4 USER="mailto:resource-room-sauna@example.com"
56.5 SENDER="mailto:paul.boddie@example.com"
56.6 OUTSIDESENDER="mailto:paul.boddie@example.net"
56.7 -FBFILE="$STORE/$USER/freebusy"
56.8 -FBSENDERFILE="$STORE/$SENDER/freebusy"
56.9 -FBOUTSIDESENDERFILE="$STORE/$OUTSIDESENDER/freebusy"
56.10
56.11 mkdir -p "$PREFS/$USER"
56.12 echo 'Europe/Oslo' > "$PREFS/$USER/TZID"
56.13 @@ -32,7 +29,9 @@
56.14
56.15 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-sauna-outsider.txt" 2>> $ERROR
56.16
56.17 - grep -q "^20141126T150000Z${TAB}20141126T154500Z" "$FBOUTSIDESENDERFILE" \
56.18 + "$LIST_SCRIPT" $LIST_ARGS "$OUTSIDESENDER" "freebusy" \
56.19 +| tee out0f.tmp \
56.20 +| grep -q "^20141126T150000Z${TAB}20141126T154500Z" \
56.21 && echo "Success" \
56.22 || echo "Failed"
56.23
56.24 @@ -48,8 +47,10 @@
56.25 && echo "Success" \
56.26 || echo "Failed"
56.27
56.28 - ! [ -e "$FBFILE" ] \
56.29 -|| ! grep -q "^20141126T150000Z${TAB}20141126T154500Z" "$FBFILE" \
56.30 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
56.31 +> out1f.tmp
56.32 +
56.33 + ! grep -q "^20141126T150000Z${TAB}20141126T154500Z" "out1f.tmp" \
56.34 && echo "Success" \
56.35 || echo "Failed"
56.36
56.37 @@ -71,8 +72,9 @@
56.38 && echo "Success" \
56.39 || echo "Failed"
56.40
56.41 - [ -e "$FBFILE" ] \
56.42 -&& grep -q "^20141126T150000Z${TAB}20141126T154500Z" "$FBFILE" \
56.43 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
56.44 +| tee out2f.tmp \
56.45 +| grep -q "^20141126T150000Z${TAB}20141126T154500Z" \
56.46 && echo "Success" \
56.47 || echo "Failed"
56.48
56.49 @@ -105,7 +107,9 @@
56.50
56.51 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-sauna-acl.txt" 2>> $ERROR
56.52
56.53 - grep -q "^20141126T160000Z${TAB}20141126T164500Z" "$FBSENDERFILE" \
56.54 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
56.55 +| tee out3f.tmp \
56.56 +| grep -q "^20141126T160000Z${TAB}20141126T164500Z" \
56.57 && echo "Success" \
56.58 || echo "Failed"
56.59
56.60 @@ -121,8 +125,10 @@
56.61 && echo "Success" \
56.62 || echo "Failed"
56.63
56.64 - ! [ -e "$FBFILE" ] \
56.65 -|| ! grep -q "^20141126T160000Z${TAB}20141126T164500Z" "$FBFILE" \
56.66 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
56.67 +> out4f.tmp
56.68 +
56.69 + ! grep -q "^20141126T160000Z${TAB}20141126T164500Z" "out4f.tmp" \
56.70 && echo "Success" \
56.71 || echo "Failed"
56.72
56.73 @@ -136,7 +142,9 @@
56.74
56.75 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-sauna-acl.txt" 2>> $ERROR
56.76
56.77 - grep -q "^20141126T160000Z${TAB}20141126T164500Z" "$FBSENDERFILE" \
56.78 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
56.79 +| tee out4s.tmp \
56.80 +| grep -q "^20141126T160000Z${TAB}20141126T164500Z" \
56.81 && echo "Success" \
56.82 || echo "Failed"
56.83
56.84 @@ -152,8 +160,10 @@
56.85 && echo "Success" \
56.86 || echo "Failed"
56.87
56.88 - ! [ -e "$FBFILE" ] \
56.89 -|| ! grep -q "^20141126T160000Z${TAB}20141126T164500Z" "$FBFILE" \
56.90 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
56.91 +> out5f.tmp
56.92 +
56.93 + ! grep -q "^20141126T160000Z${TAB}20141126T164500Z" "out5f.tmp" \
56.94 && echo "Success" \
56.95 || echo "Failed"
56.96
56.97 @@ -168,7 +178,9 @@
56.98
56.99 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-sauna-acl.txt" 2>> $ERROR
56.100
56.101 - grep -q "^20141126T160000Z${TAB}20141126T164500Z" "$FBSENDERFILE" \
56.102 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
56.103 +| tee out5s.tmp \
56.104 +| grep -q "^20141126T160000Z${TAB}20141126T164500Z" \
56.105 && echo "Success" \
56.106 || echo "Failed"
56.107
56.108 @@ -184,8 +196,10 @@
56.109 && echo "Success" \
56.110 || echo "Failed"
56.111
56.112 - ! [ -e "$FBFILE" ] \
56.113 -|| ! grep -q "^20141126T160000Z${TAB}20141126T164500Z" "$FBFILE" \
56.114 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
56.115 +> out6f.tmp
56.116 +
56.117 + ! grep -q "^20141126T160000Z${TAB}20141126T164500Z" "out6f.tmp" \
56.118 && echo "Success" \
56.119 || echo "Failed"
56.120
56.121 @@ -214,8 +228,9 @@
56.122 && echo "Success" \
56.123 || echo "Failed"
56.124
56.125 - ! [ -e "$FBFILE" ] \
56.126 -|| grep -q "^20141126T160000Z${TAB}20141126T164500Z" "$FBFILE" \
56.127 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
56.128 +| tee out7f.tmp \
56.129 +| grep -q "^20141126T160000Z${TAB}20141126T164500Z" \
56.130 && echo "Success" \
56.131 || echo "Failed"
56.132
57.1 --- a/tests/test_resource_invitation_constraints_next_free.sh Tue Apr 19 21:20:57 2016 +0200
57.2 +++ b/tests/test_resource_invitation_constraints_next_free.sh Fri Apr 22 16:22:58 2016 +0200
57.3 @@ -5,13 +5,6 @@
57.4 USER="mailto:resource-room-sauna@example.com"
57.5 SENDER="mailto:paul.boddie@example.com"
57.6 RIVALSENDER="mailto:vincent.vole@example.com"
57.7 -FBFILE="$STORE/$USER/freebusy"
57.8 -FBOTHERFILE="$STORE/$USER/freebusy-other/$SENDER"
57.9 -FBOFFERFILE="$STORE/$USER/freebusy-offers"
57.10 -FBSENDERFILE="$STORE/$SENDER/freebusy"
57.11 -FBSENDEROTHERFILE="$STORE/$SENDER/freebusy-other/$USER"
57.12 -FBSENDERREQUESTS="$STORE/$SENDER/requests"
57.13 -FBRIVALSENDERFILE="$STORE/$RIVALSENDER/freebusy"
57.14
57.15 mkdir -p "$PREFS/$USER"
57.16 echo 'Europe/Oslo' > "$PREFS/$USER/TZID"
57.17 @@ -32,8 +25,11 @@
57.18
57.19 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-sauna-busy.txt" 2>> $ERROR
57.20
57.21 - [ `grep "event19@example.com" "$FBRIVALSENDERFILE" | wc -l` = '5' ] \
57.22 -&& grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBRIVALSENDERFILE" \
57.23 + "$LIST_SCRIPT" $LIST_ARGS "$RIVALSENDER" "freebusy" \
57.24 +> out0s.tmp
57.25 +
57.26 + [ `grep "event19@example.com" "out0s.tmp" | wc -l` = '5' ] \
57.27 +&& grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out0s.tmp" \
57.28 && echo "Success" \
57.29 || echo "Failed"
57.30
57.31 @@ -49,8 +45,11 @@
57.32 && echo "Success" \
57.33 || echo "Failed"
57.34
57.35 - [ `grep "event19@example.com" "$FBFILE" | wc -l` = '5' ] \
57.36 -&& grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE" \
57.37 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
57.38 +> out1f.tmp
57.39 +
57.40 + [ `grep "event19@example.com" "out1f.tmp" | wc -l` = '5' ] \
57.41 +&& grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out1f.tmp" \
57.42 && echo "Success" \
57.43 || echo "Failed"
57.44
57.45 @@ -67,8 +66,11 @@
57.46 | "$SHOWMAIL" \
57.47 > out3.tmp
57.48
57.49 - grep -q "^20141126T160000Z${TAB}20141126T170000Z" "$FBOTHERFILE" \
57.50 -&& grep -q "^20141126T180000Z${TAB}20141126T190000Z" "$FBOTHERFILE" \
57.51 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \
57.52 +> out3f.tmp
57.53 +
57.54 + grep -q "^20141126T160000Z${TAB}20141126T170000Z" "out3f.tmp" \
57.55 +&& grep -q "^20141126T180000Z${TAB}20141126T190000Z" "out3f.tmp" \
57.56 && echo "Success" \
57.57 || echo "Failed"
57.58
57.59 @@ -76,7 +78,9 @@
57.60
57.61 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-sauna-good.txt" 2>> $ERROR
57.62
57.63 - grep -q "^20141126T150000Z${TAB}20141126T154500Z" "$FBSENDERFILE" \
57.64 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
57.65 +| tee out3s.tmp \
57.66 +| grep -q "^20141126T150000Z${TAB}20141126T154500Z" \
57.67 && echo "Success" \
57.68 || echo "Failed"
57.69
57.70 @@ -94,12 +98,15 @@
57.71 && echo "Success" \
57.72 || echo "Failed"
57.73
57.74 - ! grep -q "^20141126T150000Z${TAB}20141126T154500Z" "$FBOFFERFILE" \
57.75 -&& ! grep -q "^20141126T160000Z${TAB}20141126T164500Z" "$FBOFFERFILE" \
57.76 -&& ! grep -q "^20141126T170000Z${TAB}20141126T174500Z" "$FBOFFERFILE" \
57.77 -&& ! grep -q "^20141126T180000Z${TAB}20141126T184500Z" "$FBOFFERFILE" \
57.78 -&& ! grep -q "^20141126T190000Z${TAB}20141126T194500Z" "$FBOFFERFILE" \
57.79 -&& grep -q "^20141126T200000Z${TAB}20141126T204500Z" "$FBOFFERFILE" \
57.80 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_offers" \
57.81 +> out6o.tmp
57.82 +
57.83 + ! grep -q "^20141126T150000Z${TAB}20141126T154500Z" "out6o.tmp" \
57.84 +&& ! grep -q "^20141126T160000Z${TAB}20141126T164500Z" "out6o.tmp" \
57.85 +&& ! grep -q "^20141126T170000Z${TAB}20141126T174500Z" "out6o.tmp" \
57.86 +&& ! grep -q "^20141126T180000Z${TAB}20141126T184500Z" "out6o.tmp" \
57.87 +&& ! grep -q "^20141126T190000Z${TAB}20141126T194500Z" "out6o.tmp" \
57.88 +&& grep -q "^20141126T200000Z${TAB}20141126T204500Z" "out6o.tmp" \
57.89 && echo "Success" \
57.90 || echo "Failed"
57.91
57.92 @@ -110,16 +117,23 @@
57.93 | "$SHOWMAIL" \
57.94 > out7.tmp
57.95
57.96 - grep -q "^20141126T150000Z${TAB}20141126T154500Z" "$FBSENDERFILE" \
57.97 -&& ! grep -q "^20141126T160000Z${TAB}20141126T164500Z" "$FBSENDERFILE" \
57.98 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
57.99 +> out7s.tmp
57.100 +
57.101 + grep -q "^20141126T150000Z${TAB}20141126T154500Z" "out7s.tmp" \
57.102 +&& ! grep -q "^20141126T160000Z${TAB}20141126T164500Z" "out7s.tmp" \
57.103 && echo "Success" \
57.104 || echo "Failed"
57.105
57.106 - [ -e "$STORE/$SENDER/counters/objects/event13@example.com/$USER" ] \
57.107 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "countered_object" "event13@example.com" "$USER" \
57.108 +| tee out7C.tmp \
57.109 +| grep -q "event13@example.com" \
57.110 && echo "Success" \
57.111 || echo "Failed"
57.112
57.113 - grep -q 'event13@example.com' "$FBSENDERREQUESTS" \
57.114 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "requests" \
57.115 +| tee out7R.tmp \
57.116 +| grep -q 'event13@example.com' \
57.117 && echo "Success" \
57.118 || echo "Failed"
57.119
57.120 @@ -132,16 +146,25 @@
57.121
57.122 "$OUTGOING_SCRIPT" $ARGS < out8.tmp 2>> $ERROR
57.123
57.124 - ! grep -q "^20141126T150000Z${TAB}20141126T154500Z" "$FBSENDERFILE" \
57.125 -&& grep -q "^20141126T200000Z${TAB}20141126T204500Z" "$FBSENDERFILE" \
57.126 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
57.127 +> out8s.tmp
57.128 +
57.129 + ! grep -q "^20141126T150000Z${TAB}20141126T154500Z" "out8s.tmp" \
57.130 +&& grep -q "^20141126T200000Z${TAB}20141126T204500Z" "out8s.tmp" \
57.131 && echo "Success" \
57.132 || echo "Failed"
57.133
57.134 - ! [ -e "$STORE/$SENDER/counters/objects/event13@example.com/$USER" ] \
57.135 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "countered_object" "event13@example.com" "$USER" \
57.136 +> out8C.tmp
57.137 +
57.138 + ! grep -q "event13@example.com" "out8C.tmp" \
57.139 && echo "Success" \
57.140 || echo "Failed"
57.141
57.142 - ! grep -q 'event13@example.com' "$FBSENDERREQUESTS" \
57.143 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "requests" \
57.144 +> out8R.tmp
57.145 +
57.146 + ! grep -q 'event13@example.com' "out8R.tmp" \
57.147 && echo "Success" \
57.148 || echo "Failed"
57.149
57.150 @@ -154,18 +177,24 @@
57.151 && echo "Success" \
57.152 || echo "Failed"
57.153
57.154 - ! grep -q "^20141126T150000Z${TAB}20141126T154500Z" "$FBOFFERFILE" \
57.155 -&& ! grep -q "^20141126T160000Z${TAB}20141126T164500Z" "$FBOFFERFILE" \
57.156 -&& ! grep -q "^20141126T170000Z${TAB}20141126T174500Z" "$FBOFFERFILE" \
57.157 -&& ! grep -q "^20141126T180000Z${TAB}20141126T184500Z" "$FBOFFERFILE" \
57.158 -&& ! grep -q "^20141126T190000Z${TAB}20141126T194500Z" "$FBOFFERFILE" \
57.159 -&& ! grep -q "^20141126T200000Z${TAB}20141126T204500Z" "$FBOFFERFILE" \
57.160 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_offers" \
57.161 +> out9o.tmp
57.162 +
57.163 + ! grep -q "^20141126T150000Z${TAB}20141126T154500Z" "out9o.tmp" \
57.164 +&& ! grep -q "^20141126T160000Z${TAB}20141126T164500Z" "out9o.tmp" \
57.165 +&& ! grep -q "^20141126T170000Z${TAB}20141126T174500Z" "out9o.tmp" \
57.166 +&& ! grep -q "^20141126T180000Z${TAB}20141126T184500Z" "out9o.tmp" \
57.167 +&& ! grep -q "^20141126T190000Z${TAB}20141126T194500Z" "out9o.tmp" \
57.168 +&& ! grep -q "^20141126T200000Z${TAB}20141126T204500Z" "out9o.tmp" \
57.169 && echo "Success" \
57.170 || echo "Failed"
57.171
57.172 - [ `grep "event19@example.com" "$FBFILE" | wc -l` = '5' ] \
57.173 -&& [ `grep "event13@example.com" "$FBFILE" | wc -l` = '1' ] \
57.174 -&& grep -q "^20141126T200000Z${TAB}20141126T204500Z" "$FBFILE" \
57.175 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
57.176 +> out9f.tmp
57.177 +
57.178 + [ `grep "event19@example.com" "out9f.tmp" | wc -l` = '5' ] \
57.179 +&& [ `grep "event13@example.com" "out9f.tmp" | wc -l` = '1' ] \
57.180 +&& grep -q "^20141126T200000Z${TAB}20141126T204500Z" "out9f.tmp" \
57.181 && echo "Success" \
57.182 || echo "Failed"
57.183
57.184 @@ -180,18 +209,24 @@
57.185 && echo "Success" \
57.186 || echo "Failed"
57.187
57.188 - ! grep -q "^20141126T150000Z${TAB}20141126T154500Z" "$FBOFFERFILE" \
57.189 -&& ! grep -q "^20141126T160000Z${TAB}20141126T164500Z" "$FBOFFERFILE" \
57.190 -&& ! grep -q "^20141126T170000Z${TAB}20141126T174500Z" "$FBOFFERFILE" \
57.191 -&& ! grep -q "^20141126T180000Z${TAB}20141126T184500Z" "$FBOFFERFILE" \
57.192 -&& ! grep -q "^20141126T190000Z${TAB}20141126T194500Z" "$FBOFFERFILE" \
57.193 -&& ! grep -q "^20141126T200000Z${TAB}20141126T204500Z" "$FBOFFERFILE" \
57.194 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_offers" \
57.195 +> out10o.tmp
57.196 +
57.197 + ! grep -q "^20141126T150000Z${TAB}20141126T154500Z" "out10o.tmp" \
57.198 +&& ! grep -q "^20141126T160000Z${TAB}20141126T164500Z" "out10o.tmp" \
57.199 +&& ! grep -q "^20141126T170000Z${TAB}20141126T174500Z" "out10o.tmp" \
57.200 +&& ! grep -q "^20141126T180000Z${TAB}20141126T184500Z" "out10o.tmp" \
57.201 +&& ! grep -q "^20141126T190000Z${TAB}20141126T194500Z" "out10o.tmp" \
57.202 +&& ! grep -q "^20141126T200000Z${TAB}20141126T204500Z" "out10o.tmp" \
57.203 && echo "Success" \
57.204 || echo "Failed"
57.205
57.206 - [ `grep "event19@example.com" "$FBFILE" | wc -l` = '5' ] \
57.207 -&& [ `grep "event13@example.com" "$FBFILE" | wc -l` = '1' ] \
57.208 -&& grep -q "^20141126T200000Z${TAB}20141126T204500Z" "$FBFILE" \
57.209 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
57.210 +> out10f.tmp
57.211 +
57.212 + [ `grep "event19@example.com" "out10f.tmp" | wc -l` = '5' ] \
57.213 +&& [ `grep "event13@example.com" "out10f.tmp" | wc -l` = '1' ] \
57.214 +&& grep -q "^20141126T200000Z${TAB}20141126T204500Z" "out10f.tmp" \
57.215 && echo "Success" \
57.216 || echo "Failed"
57.217
57.218 @@ -212,17 +247,23 @@
57.219
57.220 # Note that the duration is different now.
57.221
57.222 - ! grep -q "^20141126T150000Z${TAB}20141126T154500Z" "$FBOFFERFILE" \
57.223 -&& ! grep -q "^20141126T160000Z${TAB}20141126T164500Z" "$FBOFFERFILE" \
57.224 -&& ! grep -q "^20141126T170000Z${TAB}20141126T174500Z" "$FBOFFERFILE" \
57.225 -&& ! grep -q "^20141126T180000Z${TAB}20141126T184500Z" "$FBOFFERFILE" \
57.226 -&& ! grep -q "^20141126T190000Z${TAB}20141126T194500Z" "$FBOFFERFILE" \
57.227 -&& grep -q "^20141126T200000Z${TAB}20141126T203000Z" "$FBOFFERFILE" \
57.228 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_offers" \
57.229 +> out12o.tmp
57.230 +
57.231 + ! grep -q "^20141126T150000Z${TAB}20141126T154500Z" "out12o.tmp" \
57.232 +&& ! grep -q "^20141126T160000Z${TAB}20141126T164500Z" "out12o.tmp" \
57.233 +&& ! grep -q "^20141126T170000Z${TAB}20141126T174500Z" "out12o.tmp" \
57.234 +&& ! grep -q "^20141126T180000Z${TAB}20141126T184500Z" "out12o.tmp" \
57.235 +&& ! grep -q "^20141126T190000Z${TAB}20141126T194500Z" "out12o.tmp" \
57.236 +&& grep -q "^20141126T200000Z${TAB}20141126T203000Z" "out12o.tmp" \
57.237 && echo "Success" \
57.238 || echo "Failed"
57.239
57.240 - [ `grep "event19@example.com" "$FBFILE" | wc -l` = '5' ] \
57.241 -&& [ `grep "event13@example.com" "$FBFILE" | wc -l` = '1' ] \
57.242 -&& grep -q "^20141126T200000Z${TAB}20141126T204500Z" "$FBFILE" \
57.243 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
57.244 +> out12f.tmp
57.245 +
57.246 + [ `grep "event19@example.com" "out12f.tmp" | wc -l` = '5' ] \
57.247 +&& [ `grep "event13@example.com" "out12f.tmp" | wc -l` = '1' ] \
57.248 +&& grep -q "^20141126T200000Z${TAB}20141126T204500Z" "out12f.tmp" \
57.249 && echo "Success" \
57.250 || echo "Failed"
58.1 --- a/tests/test_resource_invitation_constraints_quota.sh Tue Apr 19 21:20:57 2016 +0200
58.2 +++ b/tests/test_resource_invitation_constraints_quota.sh Fri Apr 22 16:22:58 2016 +0200
58.3 @@ -5,11 +5,7 @@
58.4 USER1="mailto:resource-car-porsche911@example.com"
58.5 USER2="mailto:resource-car-fiat500@example.com"
58.6 SENDER="mailto:paul.boddie@example.com"
58.7 -FBFILE1="$STORE/$USER1/freebusy"
58.8 -FBFILE2="$STORE/$USER2/freebusy"
58.9 -FBSENDERFILE="$STORE/$SENDER/freebusy"
58.10 QUOTA=cars
58.11 -JOURNALFILE="$JOURNAL/$QUOTA/journal/$SENDER"
58.12
58.13 mkdir -p "$PREFS/$USER1"
58.14 echo 'Europe/Oslo' > "$PREFS/$USER1/TZID"
58.15 @@ -27,8 +23,7 @@
58.16 check_quota $QUOTA
58.17 EOF
58.18
58.19 -mkdir -p "$JOURNAL/$QUOTA"
58.20 -echo '* PT1H' > "$JOURNAL/$QUOTA/limits"
58.21 +"$SET_QUOTA_LIMIT" "$QUOTA" '*' 'PT1H' $SET_QUOTA_LIMIT_ARGS
58.22
58.23 "$RESOURCE_SCRIPT" $ARGS < "$TEMPLATES/fb-request-car.txt" 2>> $ERROR \
58.24 | "$SHOWMAIL" \
58.25 @@ -43,7 +38,9 @@
58.26
58.27 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-car.txt" 2>> $ERROR
58.28
58.29 - grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBSENDERFILE" \
58.30 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
58.31 +| tee out0s.tmp \
58.32 +| grep -q "^20141126T150000Z${TAB}20141126T160000Z" \
58.33 && echo "Success" \
58.34 || echo "Failed"
58.35
58.36 @@ -59,15 +56,17 @@
58.37 && echo "Success" \
58.38 || echo "Failed"
58.39
58.40 - [ -e "$FBFILE1" ] \
58.41 -&& grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE1" \
58.42 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "freebusy" \
58.43 +| tee out1f.tmp \
58.44 +| grep -q "^20141126T150000Z${TAB}20141126T160000Z" \
58.45 && echo "Success" \
58.46 || echo "Failed"
58.47
58.48 # Check the quota (event is confirmed).
58.49
58.50 - [ -e "$JOURNALFILE" ] \
58.51 -&& grep -q "event21@example.com" "$JOURNALFILE" \
58.52 + "$LIST_SCRIPT" $LIST_ARGS "$QUOTA" "entries" "$SENDER" \
58.53 +| tee out1e.tmp \
58.54 +| grep -q "event21@example.com" \
58.55 && echo "Success" \
58.56 || echo "Failed"
58.57
58.58 @@ -75,7 +74,9 @@
58.59
58.60 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-car-conflict.txt" 2>> $ERROR
58.61
58.62 - grep -q "^20141126T153000Z${TAB}20141126T163000Z" "$FBSENDERFILE" \
58.63 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
58.64 +| tee out1s.tmp \
58.65 +| grep -q "^20141126T153000Z${TAB}20141126T163000Z" \
58.66 && echo "Success" \
58.67 || echo "Failed"
58.68
58.69 @@ -91,28 +92,34 @@
58.70 && echo "Success" \
58.71 || echo "Failed"
58.72
58.73 - ! [ -e "$FBFILE2" ] \
58.74 -|| ! grep -q "^20141126T153000Z${TAB}20141126T163000Z" "$FBFILE2" \
58.75 + "$LIST_SCRIPT" $LIST_ARGS "$USER2" "freebusy" \
58.76 +> out2f.tmp
58.77 +
58.78 + ! grep -q "^20141126T153000Z${TAB}20141126T163000Z" "out2f.tmp" \
58.79 && echo "Success" \
58.80 || echo "Failed"
58.81
58.82 # Check the quota (event is not confirmed).
58.83
58.84 - [ -e "$JOURNALFILE" ] \
58.85 -&& grep -q "event21@example.com" "$JOURNALFILE" \
58.86 -&& ! grep -q "event22@example.com" "$JOURNALFILE" \
58.87 + "$LIST_SCRIPT" $LIST_ARGS "$QUOTA" "entries" "$SENDER" \
58.88 +> out2e.tmp
58.89 +
58.90 + grep -q "event21@example.com" "out2e.tmp" \
58.91 +&& ! grep -q "event22@example.com" "out2e.tmp" \
58.92 && echo "Success" \
58.93 || echo "Failed"
58.94
58.95 # Increase the quota.
58.96
58.97 -echo '* PT2H' > "$JOURNAL/$QUOTA/limits"
58.98 +"$SET_QUOTA_LIMIT" "$QUOTA" '*' 'PT2H' $SET_QUOTA_LIMIT_ARGS
58.99
58.100 # Attempt to schedule the event again.
58.101
58.102 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-car-conflict.txt" 2>> $ERROR
58.103
58.104 - grep -q "^20141126T153000Z${TAB}20141126T163000Z" "$FBSENDERFILE" \
58.105 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
58.106 +| tee out2s.tmp \
58.107 +| grep -q "^20141126T153000Z${TAB}20141126T163000Z" \
58.108 && echo "Success" \
58.109 || echo "Failed"
58.110
58.111 @@ -128,16 +135,19 @@
58.112 && echo "Success" \
58.113 || echo "Failed"
58.114
58.115 - [ -e "$FBFILE2" ] \
58.116 -&& grep -q "^20141126T153000Z${TAB}20141126T163000Z" "$FBFILE2" \
58.117 + "$LIST_SCRIPT" $LIST_ARGS "$USER2" "freebusy" \
58.118 +| tee out3f.tmp \
58.119 +| grep -q "^20141126T153000Z${TAB}20141126T163000Z" \
58.120 && echo "Success" \
58.121 || echo "Failed"
58.122
58.123 # Check the quota (event is confirmed).
58.124
58.125 - [ -e "$JOURNALFILE" ] \
58.126 -&& grep -q "event21@example.com" "$JOURNALFILE" \
58.127 -&& grep -q "event22@example.com" "$JOURNALFILE" \
58.128 + "$LIST_SCRIPT" $LIST_ARGS "$QUOTA" "entries" "$SENDER" \
58.129 +> out3e.tmp
58.130 +
58.131 + grep -q "event21@example.com" "out3e.tmp" \
58.132 +&& grep -q "event22@example.com" "out3e.tmp" \
58.133 && echo "Success" \
58.134 || echo "Failed"
58.135
58.136 @@ -145,7 +155,10 @@
58.137
58.138 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-car.txt" 2>> $ERROR
58.139
58.140 - ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBSENDERFILE" \
58.141 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
58.142 +> out3s.tmp
58.143 +
58.144 + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out3s.tmp" \
58.145 && echo "Success" \
58.146 || echo "Failed"
58.147
58.148 @@ -160,15 +173,20 @@
58.149 && echo "Success" \
58.150 || echo "Failed"
58.151
58.152 - ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE1" \
58.153 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "freebusy" \
58.154 +> out4f.tmp
58.155 +
58.156 + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out4f.tmp" \
58.157 && echo "Success" \
58.158 || echo "Failed"
58.159
58.160 # Check the quota (event is retracted).
58.161
58.162 - [ -e "$JOURNALFILE" ] \
58.163 -&& ! grep -q "event21@example.com" "$JOURNALFILE" \
58.164 -&& grep -q "event22@example.com" "$JOURNALFILE" \
58.165 + "$LIST_SCRIPT" $LIST_ARGS "$QUOTA" "entries" "$SENDER" \
58.166 +> out4e.tmp
58.167 +
58.168 + ! grep -q "event21@example.com" "out4e.tmp" \
58.169 +&& grep -q "event22@example.com" "out4e.tmp" \
58.170 && echo "Success" \
58.171 || echo "Failed"
58.172
58.173 @@ -198,16 +216,19 @@
58.174 && echo "Success" \
58.175 || echo "Failed"
58.176
58.177 - [ -e "$FBFILE2" ] \
58.178 -&& grep -q "^20141126T153000Z${TAB}20141126T163000Z" "$FBFILE2" \
58.179 + "$LIST_SCRIPT" $LIST_ARGS "$USER2" "freebusy" \
58.180 +| tee out5f.tmp \
58.181 +| grep -q "^20141126T153000Z${TAB}20141126T163000Z" \
58.182 && echo "Success" \
58.183 || echo "Failed"
58.184
58.185 # Check the quota (event is still confirmed).
58.186
58.187 - [ -e "$JOURNALFILE" ] \
58.188 -&& ! grep -q "event21@example.com" "$JOURNALFILE" \
58.189 -&& grep -q "event22@example.com" "$JOURNALFILE" \
58.190 + "$LIST_SCRIPT" $LIST_ARGS "$QUOTA" "entries" "$SENDER" \
58.191 +> out5e.tmp
58.192 +
58.193 + ! grep -q "event21@example.com" "out5e.tmp" \
58.194 +&& grep -q "event22@example.com" "out5e.tmp" \
58.195 && echo "Success" \
58.196 || echo "Failed"
58.197
58.198 @@ -215,7 +236,9 @@
58.199
58.200 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-car.txt" 2>> $ERROR
58.201
58.202 - grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBSENDERFILE" \
58.203 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
58.204 +| tee out5s.tmp \
58.205 +| grep -q "^20141126T150000Z${TAB}20141126T160000Z" \
58.206 && echo "Success" \
58.207 || echo "Failed"
58.208
58.209 @@ -231,15 +254,20 @@
58.210 && echo "Success" \
58.211 || echo "Failed"
58.212
58.213 - ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE1" \
58.214 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "freebusy" \
58.215 +> out6f.tmp
58.216 +
58.217 + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out6f.tmp" \
58.218 && echo "Success" \
58.219 || echo "Failed"
58.220
58.221 # Check the quota (event is still retracted and not newly confirmed).
58.222
58.223 - [ -e "$JOURNALFILE" ] \
58.224 -&& ! grep -q "event21@example.com" "$JOURNALFILE" \
58.225 -&& grep -q "event22@example.com" "$JOURNALFILE" \
58.226 + "$LIST_SCRIPT" $LIST_ARGS "$QUOTA" "entries" "$SENDER" \
58.227 +> out6e.tmp
58.228 +
58.229 + ! grep -q "event21@example.com" "out6e.tmp" \
58.230 +&& grep -q "event22@example.com" "out6e.tmp" \
58.231 && echo "Success" \
58.232 || echo "Failed"
58.233
58.234 @@ -247,8 +275,11 @@
58.235
58.236 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-car-moved.txt" 2>> $ERROR
58.237
58.238 - ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBSENDERFILE" \
58.239 -&& grep -q "^20141126T143000Z${TAB}20141126T153000Z" "$FBSENDERFILE" \
58.240 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
58.241 +> out6s.tmp
58.242 +
58.243 + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out6s.tmp" \
58.244 +&& grep -q "^20141126T143000Z${TAB}20141126T153000Z" "out6s.tmp" \
58.245 && echo "Success" \
58.246 || echo "Failed"
58.247
58.248 @@ -264,27 +295,33 @@
58.249 && echo "Success" \
58.250 || echo "Failed"
58.251
58.252 - grep -q "^20141126T143000Z${TAB}20141126T153000Z" "$FBFILE1" \
58.253 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "freebusy" \
58.254 +| tee out7f.tmp \
58.255 +| grep -q "^20141126T143000Z${TAB}20141126T153000Z" \
58.256 && echo "Success" \
58.257 || echo "Failed"
58.258
58.259 # Check the quota (event is newly confirmed).
58.260
58.261 - [ -e "$JOURNALFILE" ] \
58.262 -&& grep -q "event21@example.com" "$JOURNALFILE" \
58.263 -&& grep -q "event22@example.com" "$JOURNALFILE" \
58.264 + "$LIST_SCRIPT" $LIST_ARGS "$QUOTA" "entries" "$SENDER" \
58.265 +> out7e.tmp
58.266 +
58.267 + grep -q "event21@example.com" "out7e.tmp" \
58.268 +&& grep -q "event22@example.com" "out7e.tmp" \
58.269 && echo "Success" \
58.270 || echo "Failed"
58.271
58.272 # Increase the quota.
58.273
58.274 -echo '* PT3H' > "$JOURNAL/$QUOTA/limits"
58.275 +"$SET_QUOTA_LIMIT" "$QUOTA" '*' 'PT3H' $SET_QUOTA_LIMIT_ARGS
58.276
58.277 # Attempt to schedule an event involving both resources.
58.278
58.279 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-cars.txt" 2>> $ERROR
58.280
58.281 - grep -q "^20141127T150000Z${TAB}20141127T160000Z" "$FBSENDERFILE" \
58.282 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
58.283 +| tee out7s.tmp \
58.284 +| grep -q "^20141127T150000Z${TAB}20141127T160000Z" \
58.285 && echo "Success" \
58.286 || echo "Failed"
58.287
58.288 @@ -311,16 +348,23 @@
58.289 && echo "Success" \
58.290 || echo "Failed"
58.291
58.292 - ( grep -q "^20141127T150000Z${TAB}20141127T160000Z" "$FBFILE1" \
58.293 - && ! grep -q "^20141127T150000Z${TAB}20141127T160000Z" "$FBFILE2" ) \
58.294 -|| ( ! grep -q "^20141127T150000Z${TAB}20141127T160000Z" "$FBFILE1" \
58.295 - && grep -q "^20141127T150000Z${TAB}20141127T160000Z" "$FBFILE2" ) \
58.296 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "freebusy" \
58.297 +> out8f.tmp
58.298 +
58.299 + "$LIST_SCRIPT" $LIST_ARGS "$USER2" "freebusy" \
58.300 +> out8f2.tmp
58.301 +
58.302 + ( grep -q "^20141127T150000Z${TAB}20141127T160000Z" "out8f.tmp" \
58.303 + && ! grep -q "^20141127T150000Z${TAB}20141127T160000Z" "out8f2.tmp" ) \
58.304 +|| ( ! grep -q "^20141127T150000Z${TAB}20141127T160000Z" "out8f.tmp" \
58.305 + && grep -q "^20141127T150000Z${TAB}20141127T160000Z" "out8f2.tmp" ) \
58.306 && echo "Success" \
58.307 || echo "Failed"
58.308
58.309 # Check the quota (event is confirmed, but only for one resource).
58.310
58.311 - [ -e "$JOURNALFILE" ] \
58.312 -&& grep -q "event23@example.com" "$JOURNALFILE" \
58.313 + "$LIST_SCRIPT" $LIST_ARGS "$QUOTA" "entries" "$SENDER" \
58.314 +| tee out8e.tmp \
58.315 +| grep -q "event23@example.com" \
58.316 && echo "Success" \
58.317 || echo "Failed"
59.1 --- a/tests/test_resource_invitation_constraints_quota_recurring.sh Tue Apr 19 21:20:57 2016 +0200
59.2 +++ b/tests/test_resource_invitation_constraints_quota_recurring.sh Fri Apr 22 16:22:58 2016 +0200
59.3 @@ -4,10 +4,7 @@
59.4
59.5 USER="mailto:resource-car-porsche911@example.com"
59.6 SENDER="mailto:paul.boddie@example.com"
59.7 -FBFILE="$STORE/$USER/freebusy"
59.8 -FBSENDERFILE="$STORE/$SENDER/freebusy"
59.9 QUOTA="$USER"
59.10 -JOURNALFILE="$JOURNAL/$QUOTA/journal/$SENDER"
59.11
59.12 mkdir -p "$PREFS/$USER"
59.13 echo 'Europe/Oslo' > "$PREFS/$USER/TZID"
59.14 @@ -19,8 +16,7 @@
59.15
59.16 # Employ a user-specific quota (no argument with the functions above).
59.17
59.18 -mkdir -p "$JOURNAL/$QUOTA"
59.19 -echo '* PT10H' > "$JOURNAL/$QUOTA/limits"
59.20 +"$SET_QUOTA_LIMIT" "$QUOTA" '*' 'PT10H' $SET_QUOTA_LIMIT_ARGS
59.21
59.22 "$RESOURCE_SCRIPT" $ARGS < "$TEMPLATES/fb-request-car-all.txt" 2>> $ERROR \
59.23 | "$SHOWMAIL" \
59.24 @@ -35,8 +31,11 @@
59.25
59.26 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-car-recurring.txt" 2>> $ERROR
59.27
59.28 - grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBSENDERFILE" \
59.29 -&& grep -q "^20141206T150000Z${TAB}20141206T160000Z" "$FBSENDERFILE" \
59.30 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
59.31 +> out0f.tmp
59.32 +
59.33 + grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out0f.tmp" \
59.34 +&& grep -q "^20141206T150000Z${TAB}20141206T160000Z" "out0f.tmp" \
59.35 && echo "Success" \
59.36 || echo "Failed"
59.37
59.38 @@ -52,16 +51,20 @@
59.39 && echo "Success" \
59.40 || echo "Failed"
59.41
59.42 - ! [ -e "$FBFILE" ] \
59.43 -|| ( ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE" \
59.44 - && ! grep -q "^20141206T150000Z${TAB}20141206T160000Z" "$FBFILE" ) \
59.45 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
59.46 +> out1f.tmp
59.47 +
59.48 + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out1f.tmp" \
59.49 +&& ! grep -q "^20141206T150000Z${TAB}20141206T160000Z" "out1f.tmp" \
59.50 && echo "Success" \
59.51 || echo "Failed"
59.52
59.53 # Check the quota (event is not confirmed).
59.54
59.55 - ! [ -e "$JOURNALFILE" ] \
59.56 -|| ! grep -q "event24@example.com" "$JOURNALFILE" \
59.57 + "$LIST_SCRIPT" $LIST_ARGS "$QUOTA" "entries" "$SENDER" \
59.58 +> out1e.tmp
59.59 +
59.60 + ! grep -q "event24@example.com" "out1e.tmp" \
59.61 && echo "Success" \
59.62 || echo "Failed"
59.63
59.64 @@ -70,9 +73,12 @@
59.65 sed 's/FREQ=DAILY/FREQ=DAILY;COUNT=11/;' "$TEMPLATES/event-request-car-recurring.txt" \
59.66 | "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR
59.67
59.68 - grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBSENDERFILE" \
59.69 -&& grep -q "^20141205T150000Z${TAB}20141205T160000Z" "$FBSENDERFILE" \
59.70 -&& grep -q "^20141206T150000Z${TAB}20141206T160000Z" "$FBSENDERFILE" \
59.71 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
59.72 +> out1s.tmp
59.73 +
59.74 + grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out1s.tmp" \
59.75 +&& grep -q "^20141205T150000Z${TAB}20141205T160000Z" "out1s.tmp" \
59.76 +&& grep -q "^20141206T150000Z${TAB}20141206T160000Z" "out1s.tmp" \
59.77 && echo "Success" \
59.78 || echo "Failed"
59.79
59.80 @@ -89,17 +95,21 @@
59.81 && echo "Success" \
59.82 || echo "Failed"
59.83
59.84 - ! [ -e "$FBFILE" ] \
59.85 -|| ( ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE" \
59.86 - && ! grep -q "^20141205T150000Z${TAB}20141205T160000Z" "$FBFILE" \
59.87 - && ! grep -q "^20141206T150000Z${TAB}20141206T160000Z" "$FBFILE" ) \
59.88 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
59.89 +> out2f.tmp
59.90 +
59.91 + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out2f.tmp" \
59.92 +&& ! grep -q "^20141205T150000Z${TAB}20141205T160000Z" "out2f.tmp" \
59.93 +&& ! grep -q "^20141206T150000Z${TAB}20141206T160000Z" "out2f.tmp" \
59.94 && echo "Success" \
59.95 || echo "Failed"
59.96
59.97 # Check the quota (event is confirmed).
59.98
59.99 - ! [ -e "$JOURNALFILE" ] \
59.100 -|| ! grep -q "event24@example.com" "$JOURNALFILE" \
59.101 + "$LIST_SCRIPT" $LIST_ARGS "$QUOTA" "entries" "$SENDER" \
59.102 +> out2e.tmp
59.103 +
59.104 + ! grep -q "event24@example.com" "out2e.tmp" \
59.105 && echo "Success" \
59.106 || echo "Failed"
59.107
59.108 @@ -108,9 +118,12 @@
59.109 sed 's/FREQ=DAILY/FREQ=DAILY;COUNT=10/;' "$TEMPLATES/event-request-car-recurring.txt" \
59.110 | "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR
59.111
59.112 - grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBSENDERFILE" \
59.113 -&& grep -q "^20141205T150000Z${TAB}20141205T160000Z" "$FBSENDERFILE" \
59.114 -&& ! grep -q "^20141206T150000Z${TAB}20141206T160000Z" "$FBSENDERFILE" \
59.115 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \
59.116 +> out2s.tmp
59.117 +
59.118 + grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out2s.tmp" \
59.119 +&& grep -q "^20141205T150000Z${TAB}20141205T160000Z" "out2s.tmp" \
59.120 +&& ! grep -q "^20141206T150000Z${TAB}20141206T160000Z" "out2s.tmp" \
59.121 && echo "Success" \
59.122 || echo "Failed"
59.123
59.124 @@ -118,25 +131,29 @@
59.125
59.126 sed 's/FREQ=DAILY/FREQ=DAILY;COUNT=10/;' "$TEMPLATES/event-request-car-recurring.txt" \
59.127 | "$RESOURCE_SCRIPT" $ARGS 2>> $ERROR \
59.128 -| tee out2r.tmp \
59.129 +| tee out3r.tmp \
59.130 | "$SHOWMAIL" \
59.131 -> out2.tmp
59.132 +> out3.tmp
59.133
59.134 - grep -q 'METHOD:REPLY' out2.tmp \
59.135 -&& grep -q 'ATTENDEE.*;PARTSTAT=ACCEPTED' out2.tmp \
59.136 + grep -q 'METHOD:REPLY' out3.tmp \
59.137 +&& grep -q 'ATTENDEE.*;PARTSTAT=ACCEPTED' out3.tmp \
59.138 && echo "Success" \
59.139 || echo "Failed"
59.140
59.141 - [ -e "$FBFILE" ] \
59.142 -&& grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE" \
59.143 -&& grep -q "^20141205T150000Z${TAB}20141205T160000Z" "$FBFILE" \
59.144 -&& ! grep -q "^20141206T150000Z${TAB}20141206T160000Z" "$FBFILE" \
59.145 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \
59.146 +> out3f.tmp
59.147 +
59.148 + grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out3f.tmp" \
59.149 +&& grep -q "^20141205T150000Z${TAB}20141205T160000Z" "out3f.tmp" \
59.150 +&& ! grep -q "^20141206T150000Z${TAB}20141206T160000Z" "out3f.tmp" \
59.151 && echo "Success" \
59.152 || echo "Failed"
59.153
59.154 # Check the quota (event is confirmed).
59.155
59.156 - [ -e "$JOURNALFILE" ] \
59.157 -&& grep -q "event24@example.com" "$JOURNALFILE" \
59.158 + "$LIST_SCRIPT" $LIST_ARGS "$QUOTA" "entries" "$SENDER" \
59.159 +> out3e.tmp
59.160 +
59.161 + grep -q "event24@example.com" "out3e.tmp" \
59.162 && echo "Success" \
59.163 || echo "Failed"
60.1 --- a/tests/test_resource_invitation_constraints_quota_recurring_limits.sh Tue Apr 19 21:20:57 2016 +0200
60.2 +++ b/tests/test_resource_invitation_constraints_quota_recurring_limits.sh Fri Apr 22 16:22:58 2016 +0200
60.3 @@ -8,13 +8,7 @@
60.4 SENDER2="mailto:vincent.vole@example.com"
60.5 SENDERADDRESS1="paul.boddie@example.com"
60.6 SENDERADDRESS2="vincent.vole@example.com"
60.7 -FBFILE1="$STORE/$USER1/freebusy"
60.8 -FBFILE2="$STORE/$USER2/freebusy"
60.9 -FBSENDERFILE1="$STORE/$SENDER1/freebusy"
60.10 -FBSENDERFILE2="$STORE/$SENDER2/freebusy"
60.11 QUOTA=cars
60.12 -JOURNALFILE1="$JOURNAL/$QUOTA/journal/$SENDER1"
60.13 -JOURNALFILE2="$JOURNAL/$QUOTA/journal/$SENDER2"
60.14
60.15 mkdir -p "$PREFS/$USER1"
60.16 echo 'Europe/Oslo' > "$PREFS/$USER1/TZID"
60.17 @@ -32,11 +26,8 @@
60.18 check_quota $QUOTA
60.19 EOF
60.20
60.21 -mkdir -p "$JOURNAL/$QUOTA"
60.22 -cat > "$JOURNAL/$QUOTA/limits" <<EOF
60.23 -mailto:vincent.vole@example.com PT10H
60.24 -* PT5H
60.25 -EOF
60.26 +"$SET_QUOTA_LIMIT" "$QUOTA" 'mailto:vincent.vole@example.com' 'PT10H' $SET_QUOTA_LIMIT_ARGS
60.27 +"$SET_QUOTA_LIMIT" "$QUOTA" '*' 'PT5H' $SET_QUOTA_LIMIT_ARGS
60.28
60.29 "$RESOURCE_SCRIPT" $ARGS < "$TEMPLATES/fb-request-car-all.txt" 2>> $ERROR \
60.30 | "$SHOWMAIL" \
60.31 @@ -51,8 +42,11 @@
60.32
60.33 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-cars-recurring.txt" 2>> $ERROR
60.34
60.35 - grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBSENDERFILE1" \
60.36 -&& grep -q "^20141206T150000Z${TAB}20141206T160000Z" "$FBSENDERFILE1" \
60.37 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER1" "freebusy" \
60.38 +> out0f.tmp
60.39 +
60.40 + grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out0f.tmp" \
60.41 +&& grep -q "^20141206T150000Z${TAB}20141206T160000Z" "out0f.tmp" \
60.42 && echo "Success" \
60.43 || echo "Failed"
60.44
60.45 @@ -68,19 +62,25 @@
60.46 && echo "Success" \
60.47 || echo "Failed"
60.48
60.49 - ( ! [ -e "$FBFILE1" ] \
60.50 - || ( ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE1" \
60.51 - && ! grep -q "^20141206T150000Z${TAB}20141206T160000Z" "$FBFILE1" )) \
60.52 -&& ( ! [ -e "$FBFILE2" ] \
60.53 - || ( ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE2" \
60.54 - && ! grep -q "^20141206T150000Z${TAB}20141206T160000Z" "$FBFILE2" )) \
60.55 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "freebusy" \
60.56 +> out1f.tmp
60.57 +
60.58 + "$LIST_SCRIPT" $LIST_ARGS "$USER2" "freebusy" \
60.59 +> out1f2.tmp
60.60 +
60.61 + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out1f.tmp" \
60.62 +&& ! grep -q "^20141206T150000Z${TAB}20141206T160000Z" "out1f.tmp" \
60.63 +&& ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out1f2.tmp" \
60.64 +&& ! grep -q "^20141206T150000Z${TAB}20141206T160000Z" "out1f2.tmp" \
60.65 && echo "Success" \
60.66 || echo "Failed"
60.67
60.68 # Check the quota (event is not confirmed).
60.69
60.70 - ! [ -e "$JOURNALFILE1" ] \
60.71 -|| ! grep -q "event25@example.com" "$JOURNALFILE1" \
60.72 + "$LIST_SCRIPT" $LIST_ARGS "$QUOTA" "entries" "$SENDER1" \
60.73 +> out1e.tmp
60.74 +
60.75 + ! grep -q "event25@example.com" "out1e.tmp" \
60.76 && echo "Success" \
60.77 || echo "Failed"
60.78
60.79 @@ -89,8 +89,11 @@
60.80 sed 's/FREQ=DAILY/FREQ=DAILY;COUNT=5/;' "$TEMPLATES/event-request-cars-recurring.txt" \
60.81 | "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR
60.82
60.83 - grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBSENDERFILE1" \
60.84 -&& grep -q "^20141130T150000Z${TAB}20141130T160000Z" "$FBSENDERFILE1" \
60.85 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER1" "freebusy" \
60.86 +> out1s.tmp
60.87 +
60.88 + grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out1s.tmp" \
60.89 +&& grep -q "^20141130T150000Z${TAB}20141130T160000Z" "out1s.tmp" \
60.90 && echo "Success" \
60.91 || echo "Failed"
60.92
60.93 @@ -118,25 +121,29 @@
60.94 && echo "Success" \
60.95 || echo "Failed"
60.96
60.97 - (( ! [ -e "$FBFILE1" ] \
60.98 - || ( ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE1" \
60.99 - && ! grep -q "^20141130T150000Z${TAB}20141130T160000Z" "$FBFILE1" )) \
60.100 - && [ -e "$FBFILE2" ] \
60.101 - && grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE2" \
60.102 - && grep -q "^20141130T150000Z${TAB}20141130T160000Z" "$FBFILE2" ) \
60.103 -|| (( ! [ -e "$FBFILE2" ] \
60.104 - || ( ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE2" \
60.105 - && ! grep -q "^20141130T150000Z${TAB}20141130T160000Z" "$FBFILE2" )) \
60.106 - && [ -e "$FBFILE1" ] \
60.107 - && grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE1" \
60.108 - && grep -q "^20141130T150000Z${TAB}20141130T160000Z" "$FBFILE1" ) \
60.109 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "freebusy" \
60.110 +> out2f.tmp
60.111 +
60.112 + "$LIST_SCRIPT" $LIST_ARGS "$USER2" "freebusy" \
60.113 +> out2f2.tmp
60.114 +
60.115 + ( ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out2f.tmp" \
60.116 + && ! grep -q "^20141130T150000Z${TAB}20141130T160000Z" "out2f.tmp" \
60.117 + && grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out2f2.tmp" \
60.118 + && grep -q "^20141130T150000Z${TAB}20141130T160000Z" "out2f2.tmp" ) \
60.119 +|| ( ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out2f2.tmp" \
60.120 + && ! grep -q "^20141130T150000Z${TAB}20141130T160000Z" "out2f2.tmp" \
60.121 + && grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out2f.tmp" \
60.122 + && grep -q "^20141130T150000Z${TAB}20141130T160000Z" "out2f.tmp" ) \
60.123 && echo "Success" \
60.124 || echo "Failed"
60.125
60.126 # Check the quota (event is confirmed for one resource).
60.127
60.128 - ! [ -e "$JOURNALFILE1" ] \
60.129 -|| grep -q "event25@example.com" "$JOURNALFILE1" \
60.130 + "$LIST_SCRIPT" $LIST_ARGS "$QUOTA" "entries" "$SENDER1" \
60.131 +> out2e.tmp
60.132 +
60.133 + grep -q "event25@example.com" "out2e.tmp" \
60.134 && echo "Success" \
60.135 || echo "Failed"
60.136
60.137 @@ -144,8 +151,11 @@
60.138
60.139 "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-cars-recurring.txt" 2>> $ERROR
60.140
60.141 - ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBSENDERFILE1" \
60.142 -&& ! grep -q "^20141130T150000Z${TAB}20141130T160000Z" "$FBSENDERFILE1" \
60.143 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER1" "freebusy" \
60.144 +> out2s.tmp
60.145 +
60.146 + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out2s.tmp" \
60.147 +&& ! grep -q "^20141130T150000Z${TAB}20141130T160000Z" "out2s.tmp" \
60.148 && echo "Success" \
60.149 || echo "Failed"
60.150
60.151 @@ -160,19 +170,25 @@
60.152 && echo "Success" \
60.153 || echo "Failed"
60.154
60.155 - ( ! [ -e "$FBFILE1" ] \
60.156 - || ( ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE1" \
60.157 - && ! grep -q "^20141206T150000Z${TAB}20141206T160000Z" "$FBFILE1" )) \
60.158 -&& ( ! [ -e "$FBFILE2" ] \
60.159 - || ( ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE2" \
60.160 - && ! grep -q "^20141206T150000Z${TAB}20141206T160000Z" "$FBFILE2" )) \
60.161 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "freebusy" \
60.162 +> out3f.tmp
60.163 +
60.164 + "$LIST_SCRIPT" $LIST_ARGS "$USER2" "freebusy" \
60.165 +> out3f2.tmp
60.166 +
60.167 + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out3f.tmp" \
60.168 +&& ! grep -q "^20141206T150000Z${TAB}20141206T160000Z" "out3f.tmp" \
60.169 +&& ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out3f2.tmp" \
60.170 +&& ! grep -q "^20141206T150000Z${TAB}20141206T160000Z" "out3f2.tmp" \
60.171 && echo "Success" \
60.172 || echo "Failed"
60.173
60.174 # Check the quota (event is retracted).
60.175
60.176 - ! [ -e "$JOURNALFILE1" ] \
60.177 -|| ! grep -q "event25@example.com" "$JOURNALFILE1" \
60.178 + "$LIST_SCRIPT" $LIST_ARGS "$QUOTA" "entries" "$SENDER1" \
60.179 +> out3e.tmp
60.180 +
60.181 + ! grep -q "event25@example.com" "out3e.tmp" \
60.182 && echo "Success" \
60.183 || echo "Failed"
60.184
60.185 @@ -182,8 +198,11 @@
60.186 | sed "s/$SENDERADDRESS1/$SENDERADDRESS2/;" \
60.187 | "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR
60.188
60.189 - grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBSENDERFILE2" \
60.190 -&& grep -q "^20141130T150000Z${TAB}20141130T160000Z" "$FBSENDERFILE2" \
60.191 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER2" "freebusy" \
60.192 +> out3s.tmp
60.193 +
60.194 + grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out3s.tmp" \
60.195 +&& grep -q "^20141130T150000Z${TAB}20141130T160000Z" "out3s.tmp" \
60.196 && echo "Success" \
60.197 || echo "Failed"
60.198
60.199 @@ -211,18 +230,24 @@
60.200 && echo "Success" \
60.201 || echo "Failed"
60.202
60.203 - [ -e "$FBFILE1" ] \
60.204 -&& grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE1" \
60.205 -&& grep -q "^20141130T150000Z${TAB}20141130T160000Z" "$FBFILE1" \
60.206 -&& [ -e "$FBFILE2" ] \
60.207 -&& grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE2" \
60.208 -&& grep -q "^20141130T150000Z${TAB}20141130T160000Z" "$FBFILE2" \
60.209 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "freebusy" \
60.210 +> out4f.tmp
60.211 +
60.212 + "$LIST_SCRIPT" $LIST_ARGS "$USER2" "freebusy" \
60.213 +> out4f2.tmp
60.214 +
60.215 + grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out4f.tmp" \
60.216 +&& grep -q "^20141130T150000Z${TAB}20141130T160000Z" "out4f.tmp" \
60.217 +&& grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out4f2.tmp" \
60.218 +&& grep -q "^20141130T150000Z${TAB}20141130T160000Z" "out4f2.tmp" \
60.219 && echo "Success" \
60.220 || echo "Failed"
60.221
60.222 # Check the quota (event is confirmed for both resources).
60.223
60.224 - [ -e "$JOURNALFILE2" ] \
60.225 -&& grep -q "event25@example.com" "$JOURNALFILE2" \
60.226 + "$LIST_SCRIPT" $LIST_ARGS "$QUOTA" "entries" "$SENDER2" \
60.227 +> out4e.tmp
60.228 +
60.229 + grep -q "event25@example.com" "out4e.tmp" \
60.230 && echo "Success" \
60.231 || echo "Failed"
61.1 --- a/tests/test_resource_invitation_recurring_indefinitely.sh Tue Apr 19 21:20:57 2016 +0200
61.2 +++ b/tests/test_resource_invitation_recurring_indefinitely.sh Fri Apr 22 16:22:58 2016 +0200
61.3 @@ -39,7 +39,9 @@
61.4
61.5 "$FREEBUSY_SCRIPT" "$USER" $FREEBUSY_ARGS $ARGS 2>> $ERROR
61.6
61.7 - grep -q 'event14@example.com' "$STORE/$USER/freebusy-providers" \
61.8 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_providers" \
61.9 +| tee out3p.tmp \
61.10 +| grep -q 'event14@example.com' \
61.11 && echo "Success" \
61.12 || echo "Failed"
61.13
61.14 @@ -57,7 +59,10 @@
61.15 && echo "Success" \
61.16 || echo "Failed"
61.17
61.18 - ! grep -q 'event14@example.com' "$STORE/$USER/freebusy-providers" \
61.19 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_providers" \
61.20 +> out4p.tmp
61.21 +
61.22 + ! grep -q 'event14@example.com' "out4p.tmp" \
61.23 && echo "Success" \
61.24 || echo "Failed"
61.25
61.26 @@ -83,6 +88,8 @@
61.27 && echo "Success" \
61.28 || echo "Failed"
61.29
61.30 - grep -q 'event14@example.com' "$STORE/$USER/freebusy-providers" \
61.31 + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_providers" \
61.32 +| tee out6p.tmp \
61.33 +| grep -q 'event14@example.com' \
61.34 && echo "Success" \
61.35 || echo "Failed"
62.1 --- a/tools/config.sh Tue Apr 19 21:20:57 2016 +0200
62.2 +++ b/tools/config.sh Fri Apr 22 16:22:58 2016 +0200
62.3 @@ -1,7 +1,16 @@
62.4 #!/bin/sh
62.5
62.6 +STORE_TYPE=file
62.7 IMIP_AGENT_USER=imip-agent
62.8 IMIP_AGENT_GROUP=lmtp
62.9 INSTALL_DIR=/var/lib/imip-agent
62.10 WEB_INSTALL_DIR=/var/www/imip-agent
62.11 CONFIG_DIR=/etc/imip-agent
62.12 +
62.13 +# Store-specific settings.
62.14 +
62.15 +# For STORE_TYPE=postgresql...
62.16 +
62.17 +POSTGRESQL_DB=imip_agent
62.18 +POSTGRESQL_USERS="imip-agent www-data"
62.19 +AS_POSTGRES="sudo -u postgres"
63.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
63.2 +++ b/tools/copy_store.py Fri Apr 22 16:22:58 2016 +0200
63.3 @@ -0,0 +1,177 @@
63.4 +#!/usr/bin/env python
63.5 +
63.6 +"""
63.7 +Copy store information into another store.
63.8 +
63.9 +Copyright (C) 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk>
63.10 +
63.11 +This program is free software; you can redistribute it and/or modify it under
63.12 +the terms of the GNU General Public License as published by the Free Software
63.13 +Foundation; either version 3 of the License, or (at your option) any later
63.14 +version.
63.15 +
63.16 +This program is distributed in the hope that it will be useful, but WITHOUT
63.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
63.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
63.19 +details.
63.20 +
63.21 +You should have received a copy of the GNU General Public License along with
63.22 +this program. If not, see <http://www.gnu.org/licenses/>.
63.23 +"""
63.24 +
63.25 +from os.path import abspath, split
63.26 +import sys
63.27 +
63.28 +# Find the modules.
63.29 +
63.30 +try:
63.31 + import imiptools
63.32 +except ImportError:
63.33 + parent = abspath(split(split(__file__)[0])[0])
63.34 + if split(parent)[1] == "imip-agent":
63.35 + sys.path.append(parent)
63.36 +
63.37 +from imiptools import config
63.38 +from imiptools.data import Object
63.39 +from imiptools.stores import get_store, get_publisher, get_journal
63.40 +
63.41 +def copy_store(from_store, from_journal, to_store, to_journal):
63.42 +
63.43 + """
63.44 + Copy stored information from the specified 'from_store' and 'from_journal'
63.45 + to the specified 'to_store' and 'to_journal' respectively.
63.46 + """
63.47 +
63.48 + # For each user...
63.49 +
63.50 + for user in from_store.get_users():
63.51 +
63.52 + # Copy requests.
63.53 +
63.54 + requests = from_store.get_requests(user)
63.55 + if requests:
63.56 + to_store.set_requests(user, requests)
63.57 +
63.58 + # Copy events, both active and cancellations.
63.59 +
63.60 + for dirname in (None, "cancellations"):
63.61 +
63.62 + # Get event, recurrence information.
63.63 +
63.64 + for uid, recurrenceid in from_store.get_all_events(user, dirname=dirname):
63.65 + d = from_store.get_event(user, uid, recurrenceid, dirname=dirname)
63.66 + if d:
63.67 + to_store.set_event(user, uid, recurrenceid, Object(d).to_node())
63.68 + if dirname == "cancellations":
63.69 + to_store.cancel_event(user, uid, recurrenceid)
63.70 + else:
63.71 + print >>sys.stderr, "Event for %s with UID %s and RECURRENCE-ID %s not found in %s" % (
63.72 + (user, uid, recurrenceid or "null", dirname or "active events"))
63.73 +
63.74 + # Copy counter-proposals.
63.75 +
63.76 + if dirname is None:
63.77 + for other in from_store.get_counters(user, uid, recurrenceid):
63.78 + d = from_store.get_counter(user, other, uid, recurrenceid)
63.79 + if d:
63.80 + to_store.set_counter(user, other, Object(d).to_node(), uid, recurrenceid)
63.81 + else:
63.82 + print >>sys.stderr, "Counter-proposal for %s with UID %s and RECURRENCE-ID %s not found in %s" % (
63.83 + (user, uid, recurrenceid or "null", dirname or "active events"))
63.84 +
63.85 + # Copy free/busy information for the user.
63.86 +
63.87 + freebusy = from_store.get_freebusy(user)
63.88 + if freebusy:
63.89 + to_store.set_freebusy(user, freebusy)
63.90 +
63.91 + # Copy free/busy information for other users.
63.92 +
63.93 + for other in from_store.get_freebusy_others(user):
63.94 + freebusy = from_store.get_freebusy_for_other(user, other)
63.95 + if freebusy:
63.96 + to_store.set_freebusy_for_other(user, freebusy, other)
63.97 +
63.98 + # Copy free/busy offers.
63.99 +
63.100 + offers = from_store.get_freebusy_offers(user)
63.101 + if offers:
63.102 + to_store.set_freebusy_offers(user, offers)
63.103 +
63.104 + # For each quota group...
63.105 +
63.106 + for quota in from_journal.get_quotas():
63.107 +
63.108 + # Copy quota limits.
63.109 +
63.110 + for user_group, limit in from_journal.get_limits(quota).items():
63.111 + to_journal.set_limit(quota, user_group, limit)
63.112 +
63.113 + # Copy group mappings.
63.114 +
63.115 + for store_user, user_group in from_journal.get_groups(quota).items():
63.116 + to_journal.set_group(quota, store_user, user_group)
63.117 +
63.118 + # Copy journal details.
63.119 +
63.120 + for group in from_journal.get_quota_users(quota):
63.121 + to_journal.set_entries(quota, group, from_journal.get_entries(quota, group))
63.122 +
63.123 + # Copy individual free/busy details.
63.124 +
63.125 + for store_user in from_journal.get_freebusy_users(quota):
63.126 + to_journal.set_freebusy(store_user, from_journal.get_freebusy(store_user))
63.127 +
63.128 +# Main program.
63.129 +
63.130 +if __name__ == "__main__":
63.131 +
63.132 + # Interpret the command line arguments.
63.133 +
63.134 + from_store_args = []
63.135 + to_store_args = []
63.136 + l = ignored = []
63.137 +
63.138 + for arg in sys.argv[1:]:
63.139 + if arg in ("-t", "--to"):
63.140 + l = to_store_args
63.141 + elif arg in ("-f", "--from"):
63.142 + l = from_store_args
63.143 + else:
63.144 + l.append(arg)
63.145 +
63.146 + if len(from_store_args) not in (0, 3) or len(to_store_args) != 3:
63.147 + print >>sys.stderr, """\
63.148 +Usage: %s \\
63.149 + [ ( -f | --from ) <store type> <store directory> <journal directory> ] \\
63.150 + ( -t | --to ) <store type> <store directory> <journal directory>
63.151 +
63.152 +Need details of a destination store indicated by the -t or --to option.
63.153 +In addition, details of a source store may be indicated by the -f or --from
63.154 +option; otherwise, the currently-configured store is used.
63.155 +""" % split(sys.argv[0])[1]
63.156 + sys.exit(1)
63.157 +
63.158 + # Override defaults if indicated.
63.159 +
63.160 + getvalue = lambda value, pos=0, default=None: value and value[pos] or default
63.161 +
63.162 + from_store_type = getvalue(from_store_args, 0, config.STORE_TYPE)
63.163 + from_store_dir = getvalue(from_store_args, 1)
63.164 + from_journal_dir = getvalue(from_store_args, 2)
63.165 +
63.166 + to_store_type, to_store_dir, to_journal_dir = to_store_args
63.167 +
63.168 + # Obtain store-related objects.
63.169 +
63.170 + from_store = get_store(from_store_type, from_store_dir)
63.171 + from_journal = get_journal(from_store_type, from_journal_dir)
63.172 +
63.173 + to_store = get_store(to_store_type, to_store_dir)
63.174 + to_journal = get_journal(to_store_type, to_journal_dir)
63.175 +
63.176 + # Process the store.
63.177 +
63.178 + copy_store(from_store, from_journal, to_store, to_journal)
63.179 +
63.180 +# vim: tabstop=4 expandtab shiftwidth=4
64.1 --- a/tools/fix.sh Tue Apr 19 21:20:57 2016 +0200
64.2 +++ b/tools/fix.sh Fri Apr 22 16:22:58 2016 +0200
64.3 @@ -3,38 +3,45 @@
64.4 DIRNAME=`dirname "$0"`
64.5
64.6 if [ -e "$DIRNAME/config.sh" ]; then
64.7 - . "$DIRNAME/config.sh"
64.8 + CONFIG="$DIRNAME/config.sh"
64.9 + . "$CONFIG"
64.10 else
64.11 - . /etc/imip-agent/config.sh
64.12 + CONFIG=/etc/imip-agent/config.sh
64.13 + . "$CONFIG"
64.14 fi
64.15
64.16 PROGNAME=`basename "$0"`
64.17
64.18 if [ "$1" = "--help" ]; then
64.19 cat 1>&2 <<EOF
64.20 -Usage: $PROGNAME [ <stored data directory> [ <published data directory> [ <user> [ <group> ] ] ] ]
64.21 +Usage: $PROGNAME
64.22 +
64.23 +Fix permissions for the stored and published data directories, operating on...
64.24
64.25 -Fix permissions for the stored and published data directories, operating on the
64.26 -given stored data and published data directories (or, respectively,
64.27 -$INSTALL_DIR and $WEB_INSTALL_DIR if omitted).
64.28 + * $INSTALL_DIR
64.29 + * $WEB_INSTALL_DIR
64.30
64.31 -Set ownership and membership using the given user and group (or, respectively,
64.32 -$IMIP_AGENT_USER and $IMIP_AGENT_GROUP if omitted).
64.33 +...respectively.
64.34 +
64.35 +Set ownership and membership to the user and group respectively given as
64.36 +$IMIP_AGENT_USER and $IMIP_AGENT_GROUP.
64.37 EOF
64.38 exit 1
64.39 fi
64.40
64.41 -INSTALL_DIR=${1:-$INSTALL_DIR}
64.42 -WEB_INSTALL_DIR=${2:-$WEB_INSTALL_DIR}
64.43 -USER=${3:-$IMIP_AGENT_USER}
64.44 -GROUP=${4:-$IMIP_AGENT_GROUP}
64.45 +chown -R "$IMIP_AGENT_USER" "$INSTALL_DIR"
64.46 +chgrp -R "$IMIP_AGENT_GROUP" "$INSTALL_DIR"
64.47
64.48 -chown -R "$USER" "$INSTALL_DIR"
64.49 -chgrp -R "$GROUP" "$INSTALL_DIR"
64.50 -
64.51 -for DIR in "$INSTALL_DIR"/store "$INSTALL_DIR"/preferences "$WEB_INSTALL_DIR"/static \
64.52 - "$INSTALL_DIR"/journal ; do
64.53 - chown -R "$USER" "$DIR"
64.54 - chgrp -R "$GROUP" "$DIR"
64.55 +for DIR in "$INSTALL_DIR"/preferences "$WEB_INSTALL_DIR"/static ; do
64.56 + chown -R "$IMIP_AGENT_USER" "$DIR"
64.57 + chgrp -R "$IMIP_AGENT_GROUP" "$DIR"
64.58 chmod -R g+w "$DIR"
64.59 done
64.60 +
64.61 +if [ "$STORE_TYPE" = "file" ]; then
64.62 + for DIR in "$INSTALL_DIR"/store "$INSTALL_DIR"/journal ; do
64.63 + chown -R "$IMIP_AGENT_USER" "$DIR"
64.64 + chgrp -R "$IMIP_AGENT_GROUP" "$DIR"
64.65 + chmod -R g+w "$DIR"
64.66 + done
64.67 +fi
65.1 --- a/tools/init.sh Tue Apr 19 21:20:57 2016 +0200
65.2 +++ b/tools/init.sh Fri Apr 22 16:22:58 2016 +0200
65.3 @@ -1,49 +1,169 @@
65.4 #!/bin/sh
65.5
65.6 DIRNAME=`dirname "$0"`
65.7 +CONFIG="$DIRNAME/config.sh"
65.8
65.9 -if [ -e "$DIRNAME/config.sh" ]; then
65.10 - . "$DIRNAME/config.sh"
65.11 +if [ -e "$CONFIG" ]; then
65.12 + . "$CONFIG"
65.13 else
65.14 - . /etc/imip-agent/config.sh
65.15 + CONFIG=/etc/imip-agent/config.sh
65.16 + . "$CONFIG"
65.17 +fi
65.18 +
65.19 +SCHEMA="$DIRNAME/../conf/postgresql/schema.sql"
65.20 +
65.21 +if [ ! -e "$SCHEMA" ]; then
65.22 + SCHEMA=/etc/imip-agent/postgresql/schema.sql
65.23 fi
65.24
65.25 PROGNAME=`basename "$0"`
65.26
65.27 if [ "$1" = "--help" ]; then
65.28 cat 1>&2 <<EOF
65.29 -Usage: $PROGNAME [ <stored data directory> [ <published data directory> [ <user> [ <group> ] ] ] ]
65.30 +Usage: $PROGNAME
65.31 +
65.32 +Initialise stored and published data directories at...
65.33
65.34 -Initialise stored and published data directories either at any specified
65.35 -locations or, respectively, at $INSTALL_DIR and $WEB_INSTALL_DIR.
65.36 + * $INSTALL_DIR
65.37 + * $WEB_INSTALL_DIR
65.38
65.39 -Set permissions to the given user and group or, respectively, to $IMIP_AGENT_USER
65.40 +...respectively.
65.41 +
65.42 +Set permissions to the user and group respectively given as $IMIP_AGENT_USER
65.43 and $IMIP_AGENT_GROUP.
65.44
65.45 -Within the stored data directory (using $INSTALL_DIR as an example), the
65.46 -following directories are created:
65.47 +Within the stored data directory, the following directories will be created
65.48 +(with STORE_TYPE currently set as "$STORE_TYPE"):
65.49
65.50 - * $INSTALL_DIR/journal
65.51 * $INSTALL_DIR/preferences
65.52 - * $INSTALL_DIR/store
65.53 +EOF
65.54
65.55 -Within the published data directory (using $WEB_INSTALL_DIR as an example), the
65.56 -following directory is created:
65.57 + if [ "$STORE_TYPE" = "file" ]; then
65.58 + cat 1>&2 <<EOF
65.59 + * $INSTALL_DIR/journal (if STORE_TYPE is "file")
65.60 + * $INSTALL_DIR/store (if STORE_TYPE is "file")
65.61 +EOF
65.62 + fi
65.63 +
65.64 + cat 1>&2 <<EOF
65.65 +
65.66 +Within the published data directory the following directory will be created:
65.67
65.68 * $WEB_INSTALL_DIR/static
65.69 EOF
65.70 +
65.71 + if [ "$STORE_TYPE" = "postgresql" ]; then
65.72 + cat 1>&2 <<EOF
65.73 +
65.74 +With STORE_TYPE set as "database", a database schema will be initialised for the
65.75 +following database:
65.76 +
65.77 + * $POSTGRESQL_DB
65.78 +EOF
65.79 + fi
65.80 +
65.81 + cat 1>&2 <<EOF
65.82 +
65.83 +See $CONFIG for the settings used as described above.
65.84 +EOF
65.85 + exit 1
65.86 +fi
65.87 +
65.88 +# Test for a privileged user.
65.89 +
65.90 +if [ `whoami` != 'root' ]; then
65.91 + cat 1>&2 <<EOF
65.92 +You will need to become a privileged user using su or sudo to run this program
65.93 +because it changes file ownership and may also switch users to run database
65.94 +administration commands.
65.95 +EOF
65.96 exit 1
65.97 fi
65.98
65.99 -INSTALL_DIR=${1:-$INSTALL_DIR}
65.100 -WEB_INSTALL_DIR=${2:-$WEB_INSTALL_DIR}
65.101 -USER=${3:-$IMIP_AGENT_USER}
65.102 -GROUP=${4:-$IMIP_AGENT_GROUP}
65.103 +# Create necessary directories regardless of store type.
65.104 +
65.105 +echo "Creating preferences and static Web directories..." 1>&2
65.106
65.107 -for DIR in "$INSTALL_DIR"/store "$INSTALL_DIR"/preferences "$WEB_INSTALL_DIR"/static \
65.108 - "$INSTALL_DIR"/journal ; do
65.109 +for DIR in "$INSTALL_DIR"/preferences "$WEB_INSTALL_DIR"/static ; do
65.110 mkdir -p "$DIR"
65.111 - chown "$USER" "$DIR"
65.112 - chgrp "$GROUP" "$DIR"
65.113 + chown "$IMIP_AGENT_USER" "$DIR"
65.114 + chgrp "$IMIP_AGENT_GROUP" "$DIR"
65.115 chmod g+ws "$DIR"
65.116 done
65.117 +
65.118 +# Initialise a file store.
65.119 +
65.120 +if [ "$STORE_TYPE" = "file" ]; then
65.121 +
65.122 + echo "Creating store and journal directories..." 1>&2
65.123 +
65.124 + for DIR in "$INSTALL_DIR"/store "$INSTALL_DIR"/journal ; do
65.125 + mkdir -p "$DIR"
65.126 + chown "$IMIP_AGENT_USER" "$DIR"
65.127 + chgrp "$IMIP_AGENT_GROUP" "$DIR"
65.128 + chmod g+ws "$DIR"
65.129 + done
65.130 +
65.131 +# Initialise a PostgreSQL store.
65.132 +
65.133 +elif [ "$STORE_TYPE" = "postgresql" ]; then
65.134 +
65.135 + # Check for the database.
65.136 +
65.137 + echo "Checking for the database ${POSTGRESQL_DB}..." 1>&2
65.138 +
65.139 + if $AS_POSTGRES psql -tA -c 'select datname from pg_database' postgres | grep -q ^"$POSTGRESQL_DB"$ ; then
65.140 + cat 1>&2 <<EOF
65.141 +Database $POSTGRESQL_DB already exists.
65.142 +EOF
65.143 + exit 1
65.144 + fi
65.145 +
65.146 + # Attempt to create the database.
65.147 +
65.148 + echo "Creating database ${POSTGRESQL_DB}..." 1>&2
65.149 +
65.150 + if ! $AS_POSTGRES createdb "$POSTGRESQL_DB" ; then
65.151 + cat 1>&2 <<EOF
65.152 +Could not create database $POSTGRESQL_DB using createdb.
65.153 +EOF
65.154 + exit 1
65.155 + fi
65.156 +
65.157 + # Attempt to initialise the schema.
65.158 +
65.159 + echo "Initialising the schema for database ${POSTGRESQL_DB}..." 1>&2
65.160 +
65.161 + if ! $AS_POSTGRES psql -q -f "$SCHEMA" "$POSTGRESQL_DB" ; then
65.162 + cat 1>&2 <<EOF
65.163 +Could not initialise schema in database $POSTGRESQL_DB using psql.
65.164 +EOF
65.165 + exit 1
65.166 + fi
65.167 +
65.168 + # For each user needing to connect, attempt to create a role and grant it
65.169 + # privileges on the tables.
65.170 +
65.171 + for USER in $POSTGRESQL_USERS ; do
65.172 +
65.173 + echo "Creating a database user for ${USER}..." 1>&2
65.174 +
65.175 + if ! $AS_POSTGRES createuser -D -R -S "$USER" ; then
65.176 + cat 1>&2 <<EOF
65.177 +Could not create database user $USER using createuser.
65.178 +EOF
65.179 + fi
65.180 +
65.181 + echo "Granting privileges to database user for ${USER}..." 1>&2
65.182 +
65.183 + if ! $AS_POSTGRES psql -Atc '\dt' "$POSTGRESQL_DB" \
65.184 + | cut -d '|' -f 2 \
65.185 + | xargs -I{} $AS_POSTGRES psql -q -c "grant all privileges on table {} to \"$USER\"" "$POSTGRESQL_DB" ; then
65.186 +
65.187 + cat 1>&2 <<EOF
65.188 +Could not grant permissions for schema in database $POSTGRESQL_DB to $USER
65.189 +using psql.
65.190 +EOF
65.191 + fi
65.192 + done
65.193 +fi
66.1 --- a/tools/init_user.sh Tue Apr 19 21:20:57 2016 +0200
66.2 +++ b/tools/init_user.sh Fri Apr 22 16:22:58 2016 +0200
66.3 @@ -3,36 +3,76 @@
66.4 DIRNAME=`dirname "$0"`
66.5
66.6 if [ -e "$DIRNAME/config.sh" ]; then
66.7 - . "$DIRNAME/config.sh"
66.8 + CONFIG="$DIRNAME/config.sh"
66.9 + . "$CONFIG"
66.10 else
66.11 - . /etc/imip-agent/config.sh
66.12 + CONFIG=/etc/imip-agent/config.sh
66.13 + . "$CONFIG"
66.14 fi
66.15
66.16 PROGNAME=`basename "$0"`
66.17
66.18 if [ "$1" = "--help" ] || [ ! "$1" ]; then
66.19 cat 1>&2 <<EOF
66.20 -Usage: $PROGNAME <calendar user> [ <stored data directory> [ <published data directory> [ <user> ] ] ]
66.21 +Usage: $PROGNAME <calendar user>
66.22
66.23 Initialise a given calendar user within an existing installation, creating
66.24 -resources within the given stored data and published data directories or,
66.25 -respectively, within $INSTALL_DIR and $WEB_INSTALL_DIR.
66.26 +resources within the given stored data and published data directories...
66.27 +
66.28 + * $INSTALL_DIR
66.29 + * $WEB_INSTALL_DIR
66.30 +
66.31 +...respectively.
66.32
66.33 -The resources will be defined as having the given system user as owner or,
66.34 -if the user is omitted, the $IMIP_AGENT_USER as owner.
66.35 +The resources will be defined as having $IMIP_AGENT_USER as owner.
66.36 +
66.37 +See $CONFIG for the settings used as described above.
66.38 +
66.39 +Example:
66.40 +
66.41 +$PROGNAME mailto:vincent.vole@example.com
66.42 EOF
66.43 exit 1
66.44 fi
66.45
66.46 CALENDAR_USER=$1
66.47 -INSTALL_DIR=${2:-$INSTALL_DIR}
66.48 -WEB_INSTALL_DIR=${3:-$WEB_INSTALL_DIR}
66.49 -USER=${4:-$IMIP_AGENT_USER}
66.50 +
66.51 +if [ ! "$CALENDAR_USER" ]; then
66.52 + cat 1>&2 <<EOF
66.53 +Need a calendar user to initialise.
66.54 +EOF
66.55 + exit 1
66.56 +fi
66.57 +
66.58 +# Test for a privileged user.
66.59
66.60 -for DIR in "$INSTALL_DIR"/store "$INSTALL_DIR"/preferences "$WEB_INSTALL_DIR"/static \
66.61 - "$INSTALL_DIR"/journal ; do
66.62 +if [ `whoami` != 'root' ]; then
66.63 + cat 1>&2 <<EOF
66.64 +You will need to become a privileged user using su or sudo to run this program
66.65 +because it changes file ownership.
66.66 +EOF
66.67 + exit 1
66.68 +fi
66.69 +
66.70 +# Initialise the directories.
66.71 +
66.72 +echo "Creating preferences and static Web directories..." 1>&2
66.73 +
66.74 +for DIR in "$INSTALL_DIR"/preferences "$WEB_INSTALL_DIR"/static ; do
66.75 mkdir -p "$DIR/$CALENDAR_USER"
66.76 - chown "$USER" "$DIR/$CALENDAR_USER"
66.77 + chown "$IMIP_AGENT_USER" "$DIR/$CALENDAR_USER"
66.78 chmod g+ws "$DIR/$CALENDAR_USER"
66.79 # Group privileges should already be set.
66.80 done
66.81 +
66.82 +if [ "$STORE_TYPE" = "file" ]; then
66.83 +
66.84 + echo "Creating store and journal directories..." 1>&2
66.85 +
66.86 + for DIR in "$INSTALL_DIR"/store "$INSTALL_DIR"/journal ; do
66.87 + mkdir -p "$DIR/$CALENDAR_USER"
66.88 + chown "$IMIP_AGENT_USER" "$DIR/$CALENDAR_USER"
66.89 + chmod g+ws "$DIR/$CALENDAR_USER"
66.90 + # Group privileges should already be set.
66.91 + done
66.92 +fi
67.1 --- a/tools/install.sh Tue Apr 19 21:20:57 2016 +0200
67.2 +++ b/tools/install.sh Fri Apr 22 16:22:58 2016 +0200
67.3 @@ -28,6 +28,7 @@
67.4
67.5 for DIR in "$INSTALL_DIR/imiptools" \
67.6 "$INSTALL_DIR/imiptools/stores" \
67.7 + "$INSTALL_DIR/imiptools/stores/database" \
67.8 "$INSTALL_DIR/imiptools/handlers" \
67.9 "$INSTALL_DIR/imiptools/handlers/scheduling" ; do
67.10 if [ ! -e "$DIR" ]; then
67.11 @@ -45,6 +46,7 @@
67.12
67.13 cp imiptools/*.py "$INSTALL_DIR/imiptools/"
67.14 cp imiptools/stores/*.py "$INSTALL_DIR/imiptools/stores/"
67.15 +cp imiptools/stores/database/*.py "$INSTALL_DIR/imiptools/stores/database/"
67.16 cp imiptools/handlers/*.py "$INSTALL_DIR/imiptools/handlers/"
67.17 cp imiptools/handlers/scheduling/*.py "$INSTALL_DIR/imiptools/handlers/scheduling/"
67.18
67.19 @@ -85,9 +87,23 @@
67.20
67.21 ln -s "$CONFIG_DIR/config.py" "$INSTALL_DIR/imiptools/config.py"
67.22
67.23 +# Copy related configuration files.
67.24 +
67.25 +if [ ! -e "$CONFIG_DIR/postgresql" ]; then
67.26 + mkdir -p "$CONFIG_DIR/postgresql"
67.27 +fi
67.28 +
67.29 +if [ -e "$CONFIG_DIR/postgresql/schema.sql" ]; then
67.30 + if ! cmp "conf/postgresql/schema.sql" "$CONFIG_DIR/postgresql/schema.sql" > /dev/null 2>&1 ; then
67.31 + cp "conf/postgresql/schema.sql" "$CONFIG_DIR/postgresql/schema.sql.new"
67.32 + fi
67.33 +else
67.34 + cp "conf/postgresql/schema.sql" "$CONFIG_DIR/postgresql/schema.sql"
67.35 +fi
67.36 +
67.37 # Tools
67.38
67.39 -TOOLS="fix.sh init.sh init_user.sh make_freebusy.py update_quotas.py update_scheduling_modules.py"
67.40 +TOOLS="copy_store.py fix.sh init.sh init_user.sh make_freebusy.py set_quota_limit.py update_quotas.py update_scheduling_modules.py"
67.41
67.42 if [ ! -e "$INSTALL_DIR/tools" ]; then
67.43 mkdir -p "$INSTALL_DIR/tools"
68.1 --- a/tools/make_freebusy.py Tue Apr 19 21:20:57 2016 +0200
68.2 +++ b/tools/make_freebusy.py Fri Apr 22 16:22:58 2016 +0200
68.3 @@ -21,7 +21,7 @@
68.4 this program. If not, see <http://www.gnu.org/licenses/>.
68.5 """
68.6
68.7 -from os.path import split
68.8 +from os.path import abspath, split
68.9 import sys
68.10
68.11 # Find the modules.
68.12 @@ -29,16 +29,17 @@
68.13 try:
68.14 import imiptools
68.15 except ImportError:
68.16 - parent = split(split(__file__)[0])[0]
68.17 + parent = abspath(split(split(__file__)[0])[0])
68.18 if split(parent)[1] == "imip-agent":
68.19 sys.path.append(parent)
68.20
68.21 from codecs import getwriter
68.22 +from imiptools import config
68.23 from imiptools.client import Client
68.24 from imiptools.data import get_window_end, Object
68.25 from imiptools.dates import get_default_timezone, to_utc_datetime
68.26 -from imiptools.period import insert_period
68.27 -from imiptools.stores.file import FileStore, FilePublisher, FileJournal
68.28 +from imiptools.period import FreeBusyCollection
68.29 +from imiptools.stores import get_store, get_publisher, get_journal
68.30
68.31 def make_freebusy(client, participant, store_and_publish, include_needs_action,
68.32 reset_updated_list, verbose):
68.33 @@ -86,7 +87,7 @@
68.34
68.35 if not all_events:
68.36 all_events = store.get_all_events(user)
68.37 - fb = []
68.38 + fb = FreeBusyCollection()
68.39
68.40 # With providers of additional periods, append to the existing collection.
68.41
68.42 @@ -115,7 +116,7 @@
68.43 if obj.get_participation(partstat, include_needs_action):
68.44 for p in obj.get_active_periods(recurrenceids, tzid, window_end):
68.45 fbp = obj.get_freebusy_period(p, partstat == "ORG")
68.46 - insert_period(fb, fbp)
68.47 + fb.insert_period(fbp)
68.48
68.49 # Store and publish the free/busy collection.
68.50
68.51 @@ -148,6 +149,7 @@
68.52
68.53 participants = []
68.54 args = []
68.55 + store_type = []
68.56 store_dir = []
68.57 publishing_dir = []
68.58 journal_dir = []
68.59 @@ -163,6 +165,8 @@
68.60 if arg in ("-n", "-s", "-v", "-r"):
68.61 args.append(arg)
68.62 l = ignored
68.63 + elif arg == "-T":
68.64 + l = store_type
68.65 elif arg == "-S":
68.66 l = store_dir
68.67 elif arg == "-P":
68.68 @@ -178,20 +182,26 @@
68.69 user = participants[0]
68.70 except IndexError:
68.71 print >>sys.stderr, """\
68.72 -Usage: %s <user> [ <other user> ] <options>
68.73 +Usage: %s <user> [ <other user> ] [ <options> ]
68.74
68.75 Need a user and an optional participant (if different from the user),
68.76 along with the -s option if updating the store and the published details.
68.77 -Specify -n to include objects with PARTSTAT of NEEDS-ACTION.
68.78 -Specify -r to inspect all objects, not just those expected to provide details.
68.79 -Specify -v for additional messages on standard error.
68.80 +
68.81 +Specific options:
68.82 +
68.83 +-s Update the store and published details (write details to standard output
68.84 + otherwise)
68.85 +-n Include objects with PARTSTAT of NEEDS-ACTION
68.86 +-r Inspect all objects, not just those expected to provide details
68.87 +-v Show additional messages on standard error
68.88
68.89 General options:
68.90
68.91 --j indicate the journal directory location
68.92 --p indicate the preferences directory location
68.93 --P indicate the publishing directory location
68.94 --S indicate the store directory location
68.95 +-j Indicates the journal directory location
68.96 +-p Indicates the preferences directory location
68.97 +-P Indicates the publishing directory location
68.98 +-S Indicates the store directory location
68.99 +-T Indicates the store type (the configured value if omitted)
68.100 """ % split(sys.argv[0])[1]
68.101 sys.exit(1)
68.102
68.103 @@ -205,16 +215,19 @@
68.104
68.105 # Override defaults if indicated.
68.106
68.107 - store_dir = store_dir and store_dir[0] or None
68.108 - publishing_dir = publishing_dir and publishing_dir[0] or None
68.109 - journal_dir = journal_dir and journal_dir[0] or None
68.110 - preferences_dir = preferences_dir and preferences_dir[0] or None
68.111 + getvalue = lambda value, default=None: value and value[0] or default
68.112
68.113 - # Obtain store-related objects.
68.114 + store_type = getvalue(store_type, config.STORE_TYPE)
68.115 + store_dir = getvalue(store_dir)
68.116 + publishing_dir = getvalue(publishing_dir)
68.117 + journal_dir = getvalue(journal_dir)
68.118 + preferences_dir = getvalue(preferences_dir)
68.119
68.120 - store = FileStore(store_dir)
68.121 - publisher = FilePublisher(publishing_dir)
68.122 - journal = FileJournal(journal_dir)
68.123 + # Obtain store-related objects or delegate this to the Client initialiser.
68.124 +
68.125 + store = get_store(store_type, store_dir)
68.126 + publisher = get_publisher(publishing_dir)
68.127 + journal = get_journal(store_type, journal_dir)
68.128
68.129 # Obtain a list of users for processing.
68.130
69.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
69.2 +++ b/tools/sendmail.py Fri Apr 22 16:22:58 2016 +0200
69.3 @@ -0,0 +1,9 @@
69.4 +#!/usr/bin/env python
69.5 +
69.6 +import smtplib
69.7 +import sys
69.8 +
69.9 +sender, recipients = sys.argv[1], sys.argv[2:]
69.10 +
69.11 +s = smtplib.SMTP("localhost")
69.12 +print s.sendmail(sender, recipients, sys.stdin.read())
70.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
70.2 +++ b/tools/set_quota_limit.py Fri Apr 22 16:22:58 2016 +0200
70.3 @@ -0,0 +1,85 @@
70.4 +#!/usr/bin/env python
70.5 +
70.6 +"""
70.7 +Set a quota limit for a user group.
70.8 +
70.9 +Copyright (C) 2016 Paul Boddie <paul@boddie.org.uk>
70.10 +
70.11 +This program is free software; you can redistribute it and/or modify it under
70.12 +the terms of the GNU General Public License as published by the Free Software
70.13 +Foundation; either version 3 of the License, or (at your option) any later
70.14 +version.
70.15 +
70.16 +This program is distributed in the hope that it will be useful, but WITHOUT
70.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
70.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
70.19 +details.
70.20 +
70.21 +You should have received a copy of the GNU General Public License along with
70.22 +this program. If not, see <http://www.gnu.org/licenses/>.
70.23 +"""
70.24 +
70.25 +from os.path import abspath, split
70.26 +import sys
70.27 +
70.28 +# Find the modules.
70.29 +
70.30 +try:
70.31 + import imiptools
70.32 +except ImportError:
70.33 + parent = abspath(split(split(__file__)[0])[0])
70.34 + if split(parent)[1] == "imip-agent":
70.35 + sys.path.append(parent)
70.36 +
70.37 +from imiptools import config
70.38 +from imiptools.stores import get_journal
70.39 +
70.40 +# Main program.
70.41 +
70.42 +if __name__ == "__main__":
70.43 +
70.44 + # Interpret the command line arguments.
70.45 +
70.46 + args = []
70.47 + store_type = []
70.48 + journal_dir = []
70.49 +
70.50 + # Collect quota details first, switching to other arguments when encountering
70.51 + # switches.
70.52 +
70.53 + l = args
70.54 +
70.55 + for arg in sys.argv[1:]:
70.56 + if arg == "-T":
70.57 + l = store_type
70.58 + elif arg == "-j":
70.59 + l = journal_dir
70.60 + else:
70.61 + l.append(arg)
70.62 +
70.63 + try:
70.64 + quota, group, limit = args
70.65 + except ValueError:
70.66 + print >>sys.stderr, """\
70.67 +Usage: %s <quota> <group> <limit> [ <options> ]
70.68 +
70.69 +General options:
70.70 +
70.71 +-j Indicates the journal directory location
70.72 +-T Indicates the store type (the configured value if omitted)
70.73 +""" % split(sys.argv[0])[1]
70.74 + sys.exit(1)
70.75 +
70.76 + # Override defaults if indicated.
70.77 +
70.78 + getvalue = lambda value, default=None: value and value[0] or default
70.79 +
70.80 + store_type = getvalue(store_type, config.STORE_TYPE)
70.81 + journal_dir = getvalue(journal_dir)
70.82 +
70.83 + # Obtain store-related objects.
70.84 +
70.85 + journal = get_journal(store_type, journal_dir)
70.86 + journal.set_limit(quota, group, limit)
70.87 +
70.88 +# vim: tabstop=4 expandtab shiftwidth=4
71.1 --- a/tools/update_quotas.py Tue Apr 19 21:20:57 2016 +0200
71.2 +++ b/tools/update_quotas.py Fri Apr 22 16:22:58 2016 +0200
71.3 @@ -19,7 +19,7 @@
71.4 this program. If not, see <http://www.gnu.org/licenses/>.
71.5 """
71.6
71.7 -from os.path import split
71.8 +from os.path import abspath, split
71.9 import sys
71.10
71.11 # Find the modules.
71.12 @@ -27,14 +27,15 @@
71.13 try:
71.14 import imiptools
71.15 except ImportError:
71.16 - parent = split(split(__file__)[0])[0]
71.17 + parent = abspath(split(split(__file__)[0])[0])
71.18 if split(parent)[1] == "imip-agent":
71.19 sys.path.append(parent)
71.20
71.21 from codecs import getwriter
71.22 +from imiptools import config
71.23 from imiptools.dates import get_datetime, get_default_timezone, get_time, \
71.24 to_utc_datetime
71.25 -from imiptools.stores.file import FileJournal
71.26 +from imiptools.stores import get_journal
71.27
71.28 def remove_expired_entries(entries, expiry):
71.29
71.30 @@ -110,6 +111,7 @@
71.31
71.32 quotas = []
71.33 args = []
71.34 + store_type = []
71.35 journal_dir = []
71.36 expiry = []
71.37 ignored = []
71.38 @@ -123,6 +125,8 @@
71.39 if arg in ("-s", "-v"):
71.40 args.append(arg)
71.41 l = ignored
71.42 + elif arg == "-T":
71.43 + l = store_type
71.44 elif arg == "-j":
71.45 l = journal_dir
71.46 elif arg == "-e":
71.47 @@ -141,8 +145,9 @@
71.48
71.49 General options:
71.50
71.51 --e indicate an expiry time for events (default is now)
71.52 --j indicate the journal directory location
71.53 +-e Indicates an expiry time for events (default is now)
71.54 +-j Indicates the journal directory location
71.55 +-T Indicates the store type (the configured value if omitted)
71.56 """ % split(sys.argv[0])[1]
71.57 sys.exit(1)
71.58
71.59 @@ -153,8 +158,11 @@
71.60
71.61 # Override defaults if indicated.
71.62
71.63 - journal_dir = journal_dir and journal_dir[0] or None
71.64 - expiry = expiry and expiry[0] or None
71.65 + getvalue = lambda value, default=None: value and value[0] or default
71.66 +
71.67 + store_type = getvalue(store_type, config.STORE_TYPE)
71.68 + journal_dir = getvalue(journal_dir)
71.69 + expiry = getvalue(expiry)
71.70
71.71 if expiry:
71.72 expiry = to_utc_datetime(get_datetime(expiry), get_default_timezone())
71.73 @@ -164,7 +172,7 @@
71.74
71.75 # Obtain store-related objects.
71.76
71.77 - journal = FileJournal(journal_dir)
71.78 + journal = get_journal(store_type, journal_dir)
71.79
71.80 # Obtain a list of users for processing.
71.81