├── .gitignore ├── COPYING ├── COPYING_GPL ├── README.md ├── radicale_infcloud ├── __init__.py └── web │ ├── .htaccess │ ├── addressbook.js │ ├── auth │ ├── .htaccess │ ├── common.inc │ ├── config.inc │ ├── cross_domain.inc │ ├── doc │ │ ├── example_config_response.xml │ │ └── readme.txt │ ├── index.php │ └── plugins │ │ ├── generic.inc │ │ ├── generic_conf.inc │ │ ├── ldap.inc │ │ └── ldap_conf.inc │ ├── cache.manifest │ ├── cache_handler.js │ ├── cache_update.sh │ ├── changelog.txt │ ├── changelog_caldavzap.txt │ ├── changelog_carddavmate.txt │ ├── common.js │ ├── config.js │ ├── css │ ├── default.css │ ├── default_ie.css │ ├── default_integration.css │ ├── fullcalendar.css │ ├── jquery-ui.custom.css │ ├── jquery.tagsinput.css │ └── spectrum.custom.css │ ├── data_process.js │ ├── fonts │ ├── Roboto-Bold-webfont.eot │ ├── Roboto-Bold-webfont.svg │ ├── Roboto-Bold-webfont.ttf │ ├── Roboto-Bold-webfont.woff │ ├── Roboto-BoldItalic-webfont.eot │ ├── Roboto-BoldItalic-webfont.svg │ ├── Roboto-BoldItalic-webfont.ttf │ ├── Roboto-BoldItalic-webfont.woff │ ├── Roboto-Italic-webfont.eot │ ├── Roboto-Italic-webfont.svg │ ├── Roboto-Italic-webfont.ttf │ ├── Roboto-Italic-webfont.woff │ ├── Roboto-Light-webfont.eot │ ├── Roboto-Light-webfont.svg │ ├── Roboto-Light-webfont.ttf │ ├── Roboto-Light-webfont.woff │ ├── Roboto-LightItalic-webfont.eot │ ├── Roboto-LightItalic-webfont.svg │ ├── Roboto-LightItalic-webfont.ttf │ ├── Roboto-LightItalic-webfont.woff │ ├── Roboto-Medium-webfont.eot │ ├── Roboto-Medium-webfont.svg │ ├── Roboto-Medium-webfont.ttf │ ├── Roboto-Medium-webfont.woff │ ├── Roboto-MediumItalic-webfont.eot │ ├── Roboto-MediumItalic-webfont.svg │ ├── Roboto-MediumItalic-webfont.ttf │ ├── Roboto-MediumItalic-webfont.woff │ ├── Roboto-Regular-webfont.eot │ ├── Roboto-Regular-webfont.svg │ ├── Roboto-Regular-webfont.ttf │ ├── Roboto-Regular-webfont.woff │ └── license.txt │ ├── forms.js │ ├── images │ ├── add_cal.svg │ ├── add_cal_white.svg │ ├── arrow.svg │ ├── arrow_next.svg │ ├── arrow_next_red.svg │ ├── arrow_prev.svg │ ├── arrow_prev_red.svg │ ├── banner_addressbook.svg │ ├── banner_calendar.svg │ ├── banner_logout.svg │ ├── banner_refresh.svg │ ├── banner_todo.svg │ ├── calendarB.svg │ ├── cdm_logo.svg │ ├── cdz_logo.svg │ ├── cloud.svg │ ├── company.svg │ ├── company_s_b.svg │ ├── company_s_w.svg │ ├── delegation.svg │ ├── dp_left.svg │ ├── dp_right.svg │ ├── drag.svg │ ├── error_b.svg │ ├── error_badge.svg │ ├── error_w.svg │ ├── in_progress_b.svg │ ├── in_progress_dr.svg │ ├── in_progress_r.svg │ ├── in_progress_w.svg │ ├── infcloud_logo.svg │ ├── jumper_bottom_b.svg │ ├── jumper_bottom_w.svg │ ├── jumper_top_b.svg │ ├── jumper_top_w.svg │ ├── loadinfo.gif │ ├── loadinfo_s1.gif │ ├── loadinfo_s2.gif │ ├── loadinfo_s3.gif │ ├── loadinfo_s4.gif │ ├── login.svg │ ├── logout.svg │ ├── needs_action_b.svg │ ├── needs_action_dr.svg │ ├── needs_action_r.svg │ ├── needs_action_w.svg │ ├── new_item.svg │ ├── op_add.svg │ ├── op_del.svg │ ├── popupArrow.svg │ ├── priority-1-dr.svg │ ├── priority-1-r.svg │ ├── priority-1-w.svg │ ├── priority-1.svg │ ├── priority-2-dr.svg │ ├── priority-2-r.svg │ ├── priority-2-w.svg │ ├── priority-2.svg │ ├── priority-3-dr.svg │ ├── priority-3-r.svg │ ├── priority-3-w.svg │ ├── priority-3.svg │ ├── read_only_b.svg │ ├── read_only_w.svg │ ├── remove_cal.svg │ ├── remove_cal_white.svg │ ├── reset_b.svg │ ├── reset_dr.svg │ ├── reset_drw.svg │ ├── reset_r.svg │ ├── reset_rw.svg │ ├── reset_w.svg │ ├── resource_arrow_down.svg │ ├── resource_arrow_right.svg │ ├── resource_arrow_up.svg │ ├── resources.svg │ ├── search.svg │ ├── searchWhiteNew.svg │ ├── select.svg │ ├── select_bg.svg │ ├── select_bg_black.svg │ ├── select_bg_dis.svg │ ├── select_black.svg │ ├── select_dis.svg │ ├── select_inv.svg │ ├── select_login.svg │ ├── success_b.svg │ ├── success_dr.svg │ ├── success_drw.svg │ ├── success_r.svg │ ├── success_rw.svg │ ├── success_w.svg │ ├── todoB.svg │ └── user.svg │ ├── index.html │ ├── interface.js │ ├── lib │ ├── fullcalendar.js │ ├── ie_base64.js │ ├── jquery-2.1.4.min.js │ ├── jquery-ui-1.11.4.custom.js │ ├── jquery.autosize.js │ ├── jquery.browser.js │ ├── jquery.placeholder-1.1.9.js │ ├── jquery.quicksearch.js │ ├── jquery.tagsinput.js │ ├── rrule.js │ ├── sha256.js │ └── spectrum.js │ ├── license.txt │ ├── localization.js │ ├── main.js │ ├── misc │ ├── baikal-flat-0.2.7.diff │ ├── calendarserver.diff │ ├── config_davical.txt │ ├── readme_baikal_sabredav.txt │ └── readme_osx.txt │ ├── readme.txt │ ├── resource.js │ ├── timezones.js │ ├── vcalendar.js │ ├── vcalendar_rfc_regex.js │ ├── vcard_rfc_regex.js │ ├── vtodo.js │ └── webdav_protocol.js └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .*.swp 3 | *.pyc 4 | __pycache__ 5 | 6 | /MANIFEST 7 | /build 8 | /dist 9 | /*.egg-info 10 | 11 | coverage.xml 12 | .pytest_cache 13 | .cache 14 | .coverage 15 | .coverage.* 16 | .eggs 17 | .project 18 | .pydevproject 19 | .settings 20 | .tox 21 | .vscode 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # InfCloud for Radicale 2 | 3 | Integrate [InfCloud](https://www.inf-it.com/open-source/clients/infcloud/) into 4 | [Radicale](http://radicale.org/)'s web interface. 5 | 6 | InfCloud allows you to to manage appointments, tasks and contacts in the browser. 7 | 8 | **Bug:** At least one calendar (appointments + tasks) and one addressbook must exist 9 | for InfCloud to work properly. Otherwise the "Refresh" button is broken. 10 | 11 | ## Installation 12 | 13 | ```shell 14 | python3 -m pip install --upgrade https://github.com/Unrud/RadicaleInfCloud/archive/master.tar.gz 15 | ``` 16 | 17 | ## Configuration 18 | 19 | Set the configuration option `type` in the `web` section to `radicale_infcloud`. 20 | 21 | The value of `infcloud_config` is appended to InfCloud's `config.js`. 22 | 23 | ```ini 24 | [web] 25 | type = radicale_infcloud 26 | # Optional InfCloud configuration 27 | infcloud_config = globalInterfaceLanguage='de_DE'; 28 | globalTimeZone='Europe/Berlin'; 29 | ``` 30 | 31 | ## License 32 | 33 | [AGPL-3.0](https://github.com/Unrud/RadicaleWeb/blob/master/COPYING) because 34 | [InfCloud](https://github.com/Unrud/RadicaleInfCloud/blob/master/radicale_infcloud/web/) 35 | is included. 36 | 37 | [GPL-3.0](https://github.com/Unrud/RadicaleWeb/blob/master/COPYING_GPL) 38 | for `radicale_infcloud/__init__.py` 39 | -------------------------------------------------------------------------------- /radicale_infcloud/__init__.py: -------------------------------------------------------------------------------- 1 | # RadicaleWeb web interface for Radicale. 2 | # Copyright © 2017-2018, 2020, 2022 Unrud 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | from http import client 18 | from radicale import httputils 19 | from radicale.web import internal 20 | 21 | PLUGIN_CONFIG_SCHEMA = {"web": { 22 | "infcloud_config": { 23 | "value": "", 24 | "type": str}}} 25 | 26 | 27 | class Web(internal.Web): 28 | 29 | def __init__(self, configuration): 30 | super().__init__(configuration.copy(PLUGIN_CONFIG_SCHEMA)) 31 | 32 | def get(self, environ, base_prefix, path, user): 33 | if path == "/.web/infcloud/" or path.startswith("/.web/infcloud"): 34 | status, headers, answer = httputils.serve_resource( 35 | "radicale_infcloud", "web", base_prefix, path, 36 | "/.web/infcloud") 37 | else: 38 | status, headers, answer = super().get( 39 | environ, base_prefix, path, user) 40 | if status == client.OK and path in ("/.web/", "/.web/index.html"): 41 | answer = answer.replace(b"""\ 42 |