summaryrefslogtreecommitdiffstats
path: root/core/l10n/ast.js
blob: 9e845c2d0fbaa003c5661f344b3668019744c7be (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
OC.L10N.register(
    "core",
    {
    "Please select a file." : "Esbilla un ficheru, por favor.",
    "File is too big" : "El ficheru ye pergrande",
    "The selected file is not an image." : "El ficheru esbilláu nun ye una imaxe.",
    "The selected file cannot be read." : "El ficheru esbilláu nun pue lleese.",
    "Invalid file provided" : "Apurrióse un ficheru non válidu",
    "No image or file provided" : "Nun s'apurrieron imáxenes o ficheros",
    "Unknown filetype" : "Triba desconocida de ficheru",
    "Invalid image" : "Imaxe non válida",
    "An error occurred. Please contact your admin." : "Asocedió un fallu. Contauta col to alministrador, por favor.",
    "No temporary profile picture available, try again" : "Nun hai disponible imaxe de perfil temporal dala, volvi tentalo",
    "No crop data provided" : "Nun s'apurrió'l retayu de datos",
    "State token does not match" : "El pase d'estáu nun concasa",
    "Password reset is disabled" : "Ta desactiváu'l reaniciu de contraseñes",
    "Couldn't reset password because the token is invalid" : "Nun pudo reaniciase la contraseña porque'l pase nun ye válidu",
    "Couldn't reset password because the token is expired" : "Nun pudo reaniciase la contraseña porque'l caducó'l pase",
    "%s password reset" : "%s restablecer contraseña",
    "Password reset" : "Reaniciu de contrseña",
    "Click the following button to reset your password. If you have not requested the password reset, then ignore this email." : "Primi nel botón de darréu pa reaniciar la to contraseña. Si nun solicitesti esto, entós inora esti corréu.",
    "Click the following link to reset your password. If you have not requested the password reset, then ignore this email." : "Primi nel botón de darréu pa reaniciar la to contraseña. Si nun solicitesti esto, entós inora esti corréu.",
    "Preparing update" : "Tresnando anovamientu",
    "[%d / %d]: %s" : "[%d / %d]: %s",
    "Please use the command line updater because automatic updating is disabled in the config.php." : "Usa l'anovador en llinia de comandos porque l'anovamientu automáticu ta deshabilitáu nel config.php.",
    "[%d / %d]: Checking table %s" : "[%d / %d]: Comprobando tabla %s",
    "Turned on maintenance mode" : "Activóse'l mou de caltenimientu",
    "Turned off maintenance mode" : "Desactivóse'l mou de caltenimientu",
    "Maintenance mode is kept active" : "El mou caltenimientu sigue activu",
    "Updating database schema" : "Anovando esquema de base de datos",
    "Updated database" : "Base de datos anovada",
    "Checked database schema update" : "Anovamientu del esquema de base de datos revisáu",
    "Checking updates of apps" : "Comprobando anovamientu d'aplicaciones",
    "Checked database schema update for apps" : "Anovamientu del esquema de base de datos p'aplicaciones revisáu",
    "Starting code integrity check" : "Aniciando comprobación integridá del códigu",
    "Finished code integrity check" : "Finó la comprobación d'integridá del códigu",
    "%s (incompatible)" : "%s (incompatible)",
    "Following apps have been disabled: %s" : "Deshabilitáronse les aplicaciones de darréu: %s",
    "Already up to date" : "Yá s'anovó",
    "PHP does not seem to be setup properly to query system environment variables. The test with getenv(\"PATH\") only returns an empty response." : "PHP nun paez tar configuráu de mou afayadizu pa solicitar les variables del sistema. La prueba con getenv(\"PATH\") namái devuelve una rempuesta balera.",
    "This is particularly recommended when using the desktop client for file synchronisation." : "Esto aconséyase particularmente al usar el veceru d'escritoriu pa la sincronización de ficheros.",
    "Error occurred while checking server setup" : "Fallu entrín se comprobaba la configruación del sirvidor",
    "Non-existing tag #{tag}" : "Etiqueta inesistente #{tag}",
    "invisible" : "invisible",
    "Delete" : "Desaniciar",
    "Rename" : "Renomar",
    "Collaborative tags" : "Etiquetes collaboratives",
    "No tags found" : "Nun s'alcontraron etiquetes",
    "unknown text" : "testu desconocíu",
    "Hello world!" : "¡Hola mundu!",
    "sunny" : "soleyero",
    "Hello {name}, the weather is {weather}" : "Hola {name}, el tiempu ta {weather}",
    "Hello {name}" : "Hola {name}",
    "<strong>These are your search results<script>alert(1)</script></strong>" : "<strong>Estos son los tos resultaos de gueta<script>alert(1)</script></strong>",
    "_download %n file_::_download %n files_" : ["baxar %n ficheru","baxar %n ficheros"],
    "The update is in progress, leaving this page might interrupt the process in some environments." : "L'anovamientu ta en cursu, dexar esta páxina quiciabes inerrumpa'l procesu en dellos entornos.",
    "An error occurred." : "Asocedió un fallu",
    "Please reload the page." : "Por favor, recarga la páxina",
    "Continue to Nextcloud" : "Siguir con Nextcloud",
    "Searching other places" : "Guetando otros llugares",
    "No search results in other folders for {tag}{filter}{endtag}" : "Nun hai resultaos de gueta n'otres carpetes pa {tag}{filter}{endtag}",
    "_{count} search result in another folder_::_{count} search results in other folders_" : ["{count} resultáu de gueta n'otres carpetes","{count} resultaos de gueta n'otres carpetes"],
    "Log in" : "Aniciar sesión",
    "Logging in …" : "Aniciando sesión...",
    "Server side authentication failed!" : "Falló l'autenticación nel sirvidor!",
    "Please contact your administrator." : "Por favor, contauta col to alministrador",
    "An internal error occurred." : "Asocedió un fallu internu.",
    "Please try again or contact your administrator." : "Volvi tentalo o contauta col to alministrador, por favor.",
    "Username or email" : "Nome d'usuariu o corréu",
    "Password" : "Contraseña",
    "Wrong username or password." : "Nome d'usuariu y contraseña incorreutos",
    "User disabled" : "Usuariu desactiváu",
    "Reset password" : "Restablecer contraseña",
    "Couldn't send reset email. Please contact your administrator." : "Nun pudo unviase'l corréu de reaniciu. Contauta col to alministrador, por favor.",
    "Password can not be changed. Please contact your administrator." : "Nun pue camudase la contraseña. Contauta col alministrador, por favor.",
    "New password" : "Contraseña nueva",
    "I know what I'm doing" : "Sé lo que toi faciendo",
    "Cancel" : "Encaboxar",
    "Back" : "Atrás",
    "Settings" : "Axustes",
    "Search contacts …" : "Guetar contautos...",
    "No contacts found" : "Nun s'alcontraron contautos",
    "Show all contacts …" : "Amosar tolos contautos...",
    "Loading your contacts …" : "Cargando los tos contautos...",
    "Looking for {term} …" : "Guetando {term}...",
    "No" : "Non",
    "Yes" : "Sí",
    "No files in here" : "Equí nun hai ficheros",
    "New folder" : "Carpeta nueva",
    "Name" : "Nome",
    "Size" : "Tamañu",
    "Modified" : "Modificóse'l",
    "\"{name}\" is an invalid file name." : "\"{name}\" ye un nome de ficheru inválidu.",
    "File name cannot be empty." : "El nome de ficheru nun pue quedar baleru.",
    "\"{name}\" is not an allowed filetype" : "«{name}» nun ye una triba de ficheru permitida",
    "{newName} already exists" : "{newName} yá esiste",
    "Choose" : "Escoyer",
    "Copy" : "Copiar",
    "Move" : "Mover",
    "Error loading file picker template: {error}" : "Fallu cargando'l ficheru de plantía d'escoyeta: {error}",
    "OK" : "Aceutar",
    "Error loading message template: {error}" : "Fallu cargando'l mensaxe de la plantía: {error}",
    "read-only" : "namái llectura",
    "_{count} file conflict_::_{count} file conflicts_" : ["{count} conflictu de ficheru","{count} conflictos de ficheru "],
    "One file conflict" : "Conflictu nun ficheru",
    "New Files" : "Ficheros nuevos",
    "Already existing files" : "Ficheros yá esistentes",
    "Which files do you want to keep?" : "¿Qué ficheros quies caltener?",
    "If you select both versions, the copied file will have a number added to its name." : "Si seleiciones dambes versiones, el ficheru copiáu va tener un númberu amestáu al so nome",
    "Continue" : "Continuar",
    "(all selected)" : "(esbillao too)",
    "({count} selected)" : "(esbillaos {count})",
    "Error loading file exists template" : "Falu cargando plantía de ficheru esistente",
    "Pending" : "Pendiente",
    "Saving …" : "Guardando...",
    "Authentication required" : "Ríquese l'autenticación",
    "This action requires you to confirm your password" : "Esta aición rique que confirmes la to contraseña",
    "Confirm" : "Confirmar",
    "Failed to authenticate, try again" : "Falu al autenticar, volvi tentalo",
    "seconds ago" : "hai segundos",
    "Connection to server lost" : "Perdióse la conexón del sirvidor",
    "_Problem loading page, reloading in %n second_::_Problem loading page, reloading in %n seconds_" : ["Fallu cargando la páxina, recargando en %n segundu","Fallu cargando la páxina, recargando en %n segundos"],
    "Hide details" : "Anubrir detalles",
    "Very weak password" : "Contraseña mui feble",
    "Weak password" : "Contraseña feble",
    "So-so password" : "Contraseña pasable",
    "Good password" : "Contraseña bona",
    "Strong password" : "Contraseña mui bona",
    "No action available" : "Nun hai aiciones disponibles",
    "Error fetching contact actions" : "Fallu diendo en cata de les aiciones de contautos",
    "Personal" : "Personal",
    "Users" : "Usuarios",
    "Apps" : "Aplicaciones",
    "Admin" : "Alministrador",
    "Help" : "Ayuda",
    "Access forbidden" : "Accesu denegáu",
    "File not found" : "Nun s'alcontró'l ficheru",
    "Error" : "Fallu",
    "Internal Server Error" : "Fallu internu del sirvidor",
    "More details can be found in the server log." : "Puen alcontrase más detalles nel rexistru del sirvidor.",
    "Technical details" : "Detalle téunicos",
    "Remote Address: %s" : "Direición reota: %s",
    "Request ID: %s" : "ID de solicitú: %s",
    "Type: %s" : "Triba: %s",
    "Code: %s" : "Códigu: %s",
    "Message: %s" : "Mensaxe: %s",
    "File: %s" : "Ficheru: %s",
    "Line: %s" : "Llinia: %s",
    "Security warning" : "Alvertencia de seguranza",
    "Your data directory and files are probably accessible from the internet because the .htaccess file does not work." : "El to direutoriu de datos y ficheros seique ye accesible dende internet por mor qu'el ficheru .htaccess nun furrula.",
    "Create an <strong>admin account</strong>" : "Crea una <strong>cuenta d'alministrador</strong>",
    "Username" : "Nome d'usuariu",
    "Storage & database" : "Almacenamientu y Base de datos",
    "Data folder" : "Carpeta de datos",
    "Configure the database" : "Configura la base de datos",
    "Only %s is available." : "Namái ta disponible %s",
    "For more details check out the documentation." : "Pa más detalles comprueba la documentación.",
    "Database user" : "Usuariu de la base de datos",
    "Database password" : "Contraseña de la base de datos",
    "Database name" : "Nome de la base de datos",
    "Database tablespace" : "Espaciu de tables de la base de datos",
    "Database host" : "Agospiador de la base de datos",
    "Performance warning" : "Alvertencia de rindimientu",
    "Finish setup" : "Finar la configuración ",
    "Finishing …" : "Finando...",
    "Need help?" : "¿Precises ayuda?",
    "See the documentation" : "Mira la documentación",
    "More apps" : "Más aplicaciones",
    "More" : "Más",
    "Search" : "Guetar",
    "Contacts" : "Contautos",
    "Confirm your password" : "Confirma la to contraseña",
    "App token" : "Pase d'aplicación",
    "Grant access" : "Conceder accesu",
    "This share is password-protected" : "Esta compartición tien contraseña protexida",
    "The password is wrong. Try again." : "La contraseña ye incorreuta. Inténtalo otra vegada.",
    "Two-factor authentication" : "Autenticación en dos pasos",
    "Use backup code" : "Usar códigu de respaldu",
    "Error while validating your second factor" : "Fallu al validar el to pasu segundu",
    "These apps will be updated:" : "Anovaránse estes aplicaciones:",
    "These incompatible apps will be disabled:" : "Deshabilitaránse estes aplicaciones incompletes:",
    "The theme %s has been disabled." : "Deshabilitóse'l tema %s.",
    "Please make sure that the database, the config folder and the data folder have been backed up before proceeding." : "Enantes de siguir, asegúrate de que se fizo una copia de seguridá de la base de datos, la carpeta de configuración y la carpeta de datos.",
    "Start update" : "Aniciar anovamientu",
    "Detailed logs" : "Rexistros detallaos",
    "Please use the command line updater because you have a big instance with more than 50 users." : "Usa l'anovador en llinia de comandos porque tienes una instancia grande con más de 50 usuarios.",
    "I know that if I continue doing the update via web UI has the risk, that the request runs into a timeout and could cause data loss, but I have a backup and know how to restore my instance in case of a failure." : "Sé que si sigo faciendo l'anovamientu pela IU web pue escosar el tiempu de la solicitú y causar una perda de datos, pero teo un respaldu y sé cómo restaurar la mio instancia en casu de fallu.",
    "Contact your system administrator if this message persists or appeared unexpectedly." : "Contauta col alministrador si esti problema sigui apaeciendo.",
    "Shared" : "Compartíu",
    "Shared by" : "Compartíos por",
    "Choose a password for the public link" : "Escueyi una contraseña pal enllaz públicu",
    "Copied!" : "¡Copióse!",
    "Copy link" : "Copiar enllaz",
    "Not supported!" : "¡Nun se sofita!",
    "Press ⌘-C to copy." : "Primi ⌘-C pa copiar.",
    "Press Ctrl-C to copy." : "Primi Ctrl-C pa copiar.",
    "Resharing is not allowed" : "Recompartir nun ta permitíu",
    "Link" : "Enllaz",
    "Password protect" : "Protexer con contraseña",
    "Allow editing" : "Permitir edición",
    "Email link to person" : "Enllaz de corréu-e a la persona",
    "Send" : "Unviar",
    "Read only" : "Namái llectura",
    "Set expiration date" : "Afitar la data de caducidá",
    "Expiration" : "Caducidá",
    "Expiration date" : "Data de caducidá",
    "Unshare" : "Dexar de compartir",
    "Share to {name}" : "Compartir con {name}",
    "Share link" : "Compartir enllaz",
    "Could not unshare" : "Nun pudo dexar de compartise",
    "Shared with you and the group {group} by {owner}" : "Compartíu contigo y col grupu {group} por {owner}",
    "Shared with you by {owner}" : "Compartíu contigo por {owner}",
    "group" : "grupu",
    "remote" : "remotu",
    "email" : "corréu",
    "Can edit" : "Pue editar",
    "Can create" : "Pue crear",
    "Can change" : "Pue camudar",
    "Can delete" : "Pue desaniciar",
    "Access control" : "Control d'accesu",
    "Error while sharing" : "Fallu mientres la compartición",
    "Share details could not be loaded for this item." : "Nun pudieron cargase los detalles de la compartición pa esti elementu.",
    "_At least {count} character is needed for autocompletion_::_At least {count} characters are needed for autocompletion_" : ["Precísase polo menos {count} caráuter pal auto-completáu","Precísense polo menos {count} caráuteres pal auto-completáu"],
    "No users or groups found for {search}" : "Nun s'alcontraron usuarios o grupos pa {search}",
    "No users found for {search}" : "Nun s'alcontraron usuarios pa {search}",
    "An error occurred. Please try again" : "Asocedió un fallu. Volvi tentalo, por favor",
    "Home" : "Aniciu",
    "Work" : "Trabayu",
    "Other" : "Otru",
    "{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
    "Share" : "Compartir",
    "Name or email address..." : "Nome o direición de corréu...",
    "Name or federated cloud ID..." : "Nome o ID de ñube federada...",
    "Name, federated cloud ID or email address..." : "Nome, ID de ñube federada o direición de corréu...",
    "Name..." : "Nome...",
    "Error removing share" : "Fallu desaniciando la compartición",
    "({scope})" : "({scope})",
    "Saving..." : "Guardando..."
},
"nplurals=2; plural=(n != 1);");
x">Sabre_DAV_Property_HrefList($addresses, false); } // These two properties are shortcuts for ical to easily find // other principals this principal has access to. $propRead = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for'; $propWrite = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for'; if (in_array($propRead,$requestedProperties) || in_array($propWrite,$requestedProperties)) { $membership = $node->getGroupMembership(); $readList = array(); $writeList = array(); foreach($membership as $group) { $groupNode = $this->server->tree->getNodeForPath($group); // If the node is either ap proxy-read or proxy-write // group, we grab the parent principal and add it to the // list. if ($groupNode instanceof Sabre_CalDAV_Principal_ProxyRead) { list($readList[]) = Sabre_DAV_URLUtil::splitPath($group); } if ($groupNode instanceof Sabre_CalDAV_Principal_ProxyWrite) { list($writeList[]) = Sabre_DAV_URLUtil::splitPath($group); } } if (in_array($propRead,$requestedProperties)) { unset($requestedProperties[$propRead]); $returnedProperties[200][$propRead] = new Sabre_DAV_Property_HrefList($readList); } if (in_array($propWrite,$requestedProperties)) { unset($requestedProperties[$propWrite]); $returnedProperties[200][$propWrite] = new Sabre_DAV_Property_HrefList($writeList); } } } // instanceof IPrincipal if ($node instanceof Sabre_CalDAV_ICalendarObject) { // The calendar-data property is not supposed to be a 'real' // property, but in large chunks of the spec it does act as such. // Therefore we simply expose it as a property. $calDataProp = '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data'; if (in_array($calDataProp, $requestedProperties)) { unset($requestedProperties[$calDataProp]); $val = $node->get(); if (is_resource($val)) $val = stream_get_contents($val); // Taking out \r to not screw up the xml output $returnedProperties[200][$calDataProp] = str_replace("\r","", $val); } } } /** * This function handles the calendar-multiget REPORT. * * This report is used by the client to fetch the content of a series * of urls. Effectively avoiding a lot of redundant requests. * * @param DOMNode $dom * @return void */ public function calendarMultiGetReport($dom) { $properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild)); $hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href'); $xpath = new DOMXPath($dom); $xpath->registerNameSpace('cal',Sabre_CalDAV_Plugin::NS_CALDAV); $xpath->registerNameSpace('dav','urn:DAV'); $expand = $xpath->query('/cal:calendar-multiget/dav:prop/cal:calendar-data/cal:expand'); if ($expand->length>0) { $expandElem = $expand->item(0); $start = $expandElem->getAttribute('start'); $end = $expandElem->getAttribute('end'); if(!$start || !$end) { throw new Sabre_DAV_Exception_BadRequest('The "start" and "end" attributes are required for the CALDAV:expand element'); } $start = Sabre_VObject_DateTimeParser::parseDateTime($start); $end = Sabre_VObject_DateTimeParser::parseDateTime($end); if ($end <= $start) { throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the expand element.'); } $expand = true; } else { $expand = false; } foreach($hrefElems as $elem) { $uri = $this->server->calculateUri($elem->nodeValue); list($objProps) = $this->server->getPropertiesForPath($uri,$properties); if ($expand && isset($objProps[200]['{' . self::NS_CALDAV . '}calendar-data'])) { $vObject = Sabre_VObject_Reader::read($objProps[200]['{' . self::NS_CALDAV . '}calendar-data']); $vObject->expand($start, $end); $objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); } $propertyList[]=$objProps; } $this->server->httpResponse->sendStatus(207); $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); $this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList)); } /** * This function handles the calendar-query REPORT * * This report is used by clients to request calendar objects based on * complex conditions. * * @param DOMNode $dom * @return void */ public function calendarQueryReport($dom) { $parser = new Sabre_CalDAV_CalendarQueryParser($dom); $parser->parse(); $requestedCalendarData = true; $requestedProperties = $parser->requestedProperties; if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar-data', $requestedProperties)) { // We always retrieve calendar-data, as we need it for filtering. $requestedProperties[] = '{urn:ietf:params:xml:ns:caldav}calendar-data'; // If calendar-data wasn't explicitly requested, we need to remove // it after processing. $requestedCalendarData = false; } // These are the list of nodes that potentially match the requirement $candidateNodes = $this->server->getPropertiesForPath( $this->server->getRequestUri(), $requestedProperties, $this->server->getHTTPDepth(0) ); $verifiedNodes = array(); $validator = new Sabre_CalDAV_CalendarQueryValidator(); foreach($candidateNodes as $node) { // If the node didn't have a calendar-data property, it must not be a calendar object if (!isset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'])) continue; $vObject = Sabre_VObject_Reader::read($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); if ($validator->validate($vObject,$parser->filters)) { if (!$requestedCalendarData) { unset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); } if ($parser->expand) { $vObject->expand($parser->expand['start'], $parser->expand['end']); $node[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); } $verifiedNodes[] = $node; } } $this->server->httpResponse->sendStatus(207); $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); $this->server->httpResponse->sendBody($this->server->generateMultiStatus($verifiedNodes)); } /** * This method is responsible for parsing the request and generating the * response for the CALDAV:free-busy-query REPORT. * * @param DOMNode $dom * @return void */ protected function freeBusyQueryReport(DOMNode $dom) { $start = null; $end = null; foreach($dom->firstChild->childNodes as $childNode) { $clark = Sabre_DAV_XMLUtil::toClarkNotation($childNode); if ($clark == '{' . self::NS_CALDAV . '}time-range') { $start = $childNode->getAttribute('start'); $end = $childNode->getAttribute('end'); break; } } if ($start) { $start = Sabre_VObject_DateTimeParser::parseDateTime($start); } if ($end) { $end = Sabre_VObject_DateTimeParser::parseDateTime($end); } if (!$start && !$end) { throw new Sabre_DAV_Exception_BadRequest('The freebusy report must have a time-range filter'); } $acl = $this->server->getPlugin('acl'); if (!$acl) { throw new Sabre_DAV_Exception('The ACL plugin must be loaded for free-busy queries to work'); } $uri = $this->server->getRequestUri(); $acl->checkPrivileges($uri,'{' . self::NS_CALDAV . '}read-free-busy'); $calendar = $this->server->tree->getNodeForPath($uri); if (!$calendar instanceof Sabre_CalDAV_ICalendar) { throw new Sabre_DAV_Exception_NotImplemented('The free-busy-query REPORT is only implemented on calendars'); } $objects = array_map(function($child) { $obj = $child->get(); if (is_resource($obj)) { $obj = stream_get_contents($obj); } return $obj; }, $calendar->getChildren()); $generator = new Sabre_VObject_FreeBusyGenerator(); $generator->setObjects($objects); $generator->setTimeRange($start, $end); $result = $generator->getResult(); $result = $result->serialize(); $this->server->httpResponse->sendStatus(200); $this->server->httpResponse->setHeader('Content-Type', 'text/calendar'); $this->server->httpResponse->setHeader('Content-Length', strlen($result)); $this->server->httpResponse->sendBody($result); } /** * This method is triggered before a file gets updated with new content. * * This plugin uses this method to ensure that CalDAV objects receive * valid calendar data. * * @param string $path * @param Sabre_DAV_IFile $node * @param resource $data * @return void */ public function beforeWriteContent($path, Sabre_DAV_IFile $node, &$data) { if (!$node instanceof Sabre_CalDAV_ICalendarObject) return; $this->validateICalendar($data); } /** * This method is triggered before a new file is created. * * This plugin uses this method to ensure that newly created calendar * objects contain valid calendar data. * * @param string $path * @param resource $data * @param Sabre_DAV_ICollection $parentNode * @return void */ public function beforeCreateFile($path, &$data, Sabre_DAV_ICollection $parentNode) { if (!$parentNode instanceof Sabre_CalDAV_Calendar) return; $this->validateICalendar($data); } /** * Checks if the submitted iCalendar data is in fact, valid. * * An exception is thrown if it's not. * * @param resource|string $data * @return void */ protected function validateICalendar(&$data) { // If it's a stream, we convert it to a string first. if (is_resource($data)) { $data = stream_get_contents($data); } // Converting the data to unicode, if needed. $data = Sabre_DAV_StringUtil::ensureUTF8($data); try { $vobj = Sabre_VObject_Reader::read($data); } catch (Sabre_VObject_ParseException $e) { throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid iCalendar 2.0 data. Parse error: ' . $e->getMessage()); } } /** * This method handles POST requests to the schedule-outbox * * @param Sabre_CalDAV_Schedule_IOutbox $outboxNode * @return void */ public function outboxRequest(Sabre_CalDAV_Schedule_IOutbox $outboxNode) { $originator = $this->server->httpRequest->getHeader('Originator'); $recipients = $this->server->httpRequest->getHeader('Recipient'); if (!$originator) { throw new Sabre_DAV_Exception_BadRequest('The Originator: header must be specified when making POST requests'); } if (!$recipients) { throw new Sabre_DAV_Exception_BadRequest('The Recipient: header must be specified when making POST requests'); } if (!preg_match('/^mailto:(.*)@(.*)$/', $originator)) { throw new Sabre_DAV_Exception_BadRequest('Originator must start with mailto: and must be valid email address'); } $originator = substr($originator,7); $recipients = explode(',',$recipients); foreach($recipients as $k=>$recipient) { $recipient = trim($recipient); if (!preg_match('/^mailto:(.*)@(.*)$/', $recipient)) { throw new Sabre_DAV_Exception_BadRequest('Recipients must start with mailto: and must be valid email address'); } $recipient = substr($recipient, 7); $recipients[$k] = $recipient; } // We need to make sure that 'originator' matches one of the email // addresses of the selected principal. $principal = $outboxNode->getOwner(); $props = $this->server->getProperties($principal,array( '{' . self::NS_CALDAV . '}calendar-user-address-set', )); $addresses = array(); if (isset($props['{' . self::NS_CALDAV . '}calendar-user-address-set'])) { $addresses = $props['{' . self::NS_CALDAV . '}calendar-user-address-set']->getHrefs(); } if (!in_array('mailto:' . $originator, $addresses)) { throw new Sabre_DAV_Exception_Forbidden('The addresses specified in the Originator header did not match any addresses in the owners calendar-user-address-set header'); } try { $vObject = Sabre_VObject_Reader::read($this->server->httpRequest->getBody(true)); } catch (Sabre_VObject_ParseException $e) { throw new Sabre_DAV_Exception_BadRequest('The request body must be a valid iCalendar object. Parse error: ' . $e->getMessage()); } // Checking for the object type $componentType = null; foreach($vObject->getComponents() as $component) { if ($component->name !== 'VTIMEZONE') { $componentType = $component->name; break; } } if (is_null($componentType)) { throw new Sabre_DAV_Exception_BadRequest('We expected at least one VTODO, VJOURNAL, VFREEBUSY or VEVENT component'); } // Validating the METHOD $method = strtoupper((string)$vObject->METHOD); if (!$method) { throw new Sabre_DAV_Exception_BadRequest('A METHOD property must be specified in iTIP messages'); } if (in_array($method, array('REQUEST','REPLY','ADD','CANCEL')) && $componentType==='VEVENT') { $this->iMIPMessage($originator, $recipients, $vObject); $this->server->httpResponse->sendStatus(200); $this->server->httpResponse->sendBody('Messages sent'); } else { throw new Sabre_DAV_Exception_NotImplemented('This iTIP method is currently not implemented'); } } /** * Sends an iMIP message by email. * * @param string $originator * @param array $recipients * @param Sabre_VObject_Component $vObject * @return void */ protected function iMIPMessage($originator, array $recipients, Sabre_VObject_Component $vObject) { if (!$this->imipHandler) { throw new Sabre_DAV_Exception_NotImplemented('No iMIP handler is setup on this server.'); } $this->imipHandler->sendMessage($originator, $recipients, $vObject); } /** * This method is used to generate HTML output for the * Sabre_DAV_Browser_Plugin. This allows us to generate an interface users * can use to create new calendars. * * @param Sabre_DAV_INode $node * @param string $output * @return bool */ public function htmlActionsPanel(Sabre_DAV_INode $node, &$output) { if (!$node instanceof Sabre_CalDAV_UserCalendars) return; $output.= '<tr><td colspan="2"><form method="post" action=""> <h3>Create new calendar</h3> <input type="hidden" name="sabreAction" value="mkcalendar" /> <label>Name (uri):</label> <input type="text" name="name" /><br /> <label>Display name:</label> <input type="text" name="{DAV:}displayname" /><br /> <input type="submit" value="create" /> </form> </td></tr>'; return false; } /** * This method allows us to intercept the 'mkcalendar' sabreAction. This * action enables the user to create new calendars from the browser plugin. * * @param string $uri * @param string $action * @param array $postVars * @return bool */ public function browserPostAction($uri, $action, array $postVars) { if ($action!=='mkcalendar') return; $resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar'); $properties = array(); if (isset($postVars['{DAV:}displayname'])) { $properties['{DAV:}displayname'] = $postVars['{DAV:}displayname']; } $this->server->createCollection($uri . '/' . $postVars['name'],$resourceType,$properties); return false; } }