Commit ec7faa7d authored by Frank Bergmann's avatar Frank Bergmann

Initial Import

parents
Pipeline #552 failed with stages
<?xml version="1.0" encoding="utf-8"?>
<message_catalog package_key="oacs-dav" locale="ar_LB" charset="utf-8">
<msg key="Disable">غير مفعل</msg>
<msg key="Disable_Folders"> عدم تفعيل الحافظات المختارة</msg>
<msg key="Disabled">غير مفعل</msg>
<msg key="Enable">تفعيل</msg>
<msg key="Enable_Folders">تفعيل الحافظات المختارة</msg>
<msg key="Enabled">مفعل</msg>
<msg key="Folder_Name">اسم الحافظة</msg>
<msg key="Folder_URL">حافظة العناوين</msg>
<msg key="Folders_Disabled">عدم تفعيل الحافظات المختارة لدعم WebDAV </msg>
<msg key="Folders_Enabled">تفعيل الحافظات المختارة لدعم WebDAV </msg>
<msg key="Package_Name">اسم الحزمة</msg>
<msg key="Package_Type">نوع الحزمة</msg>
<msg key="Status">الحالة</msg>
<msg key="WebDAV_Folder_Administration">WebDAV حافظة الادارة ل</msg>
</message_catalog>
<?xml version="1.0" encoding="ISO-8859-1"?>
<message_catalog package_key="oacs-dav" locale="ca_ES" charset="ISO-8859-1">
<msg key="Disable">Deshabitar </msg>
<msg key="Disable_Folders">Deshabitar les carpetes seleccionades </msg>
<msg key="Disabled">Deshabilitat </msg>
<msg key="Enable">Habilitar </msg>
<msg key="Enable_Folders">Habilitar les carpetes seleccionades </msg>
<msg key="Enabled">Habilitat</msg>
<msg key="Folder_Name">Nom de la carpeta </msg>
<msg key="Folder_URL">Carpeta URL </msg>
<msg key="Folders_Disabled">Carpetes seleccionades deshabilitades per a suport WebDav. </msg>
<msg key="Folders_Enabled">Carpetes seleccionades habilitades per a suport WebDAV. </msg>
<msg key="Package_Name">Nom del paquet </msg>
<msg key="Package_Type">Tipus de paquet </msg>
<msg key="Status">Estat </msg>
<msg key="WebDAV_Folder_Administration">Administraci WebDAV de la carpeta </msg>
</message_catalog>
<?xml version="1.0" encoding="ISO-8859-1"?>
<message_catalog package_key="oacs-dav" locale="da_DK" charset="ISO-8859-1">
<msg key="Disable">Deaktiver</msg>
<msg key="Disable_Folders">Deaktiver Valgte Mapper</msg>
<msg key="Disabled">Deaktiveret</msg>
<msg key="Enable">Aktiver</msg>
<msg key="Enable_Folders">Aktiver Valgte Mapper</msg>
<msg key="Enabled">Aktiveret</msg>
<msg key="Folder_Name">Mappenavn</msg>
<msg key="Folder_URL">Mappe URL</msg>
<msg key="Folders_Disabled">Valgte Mapper deaktiveret for WebDAV-understttelse.</msg>
<msg key="Folders_Enabled">Valgte Mapper aktiveret for WebDAV-understttelse.</msg>
<msg key="Package_Name">Pakke Navn</msg>
<msg key="Package_Type">Pakke Type</msg>
<msg key="Status">Status</msg>
<msg key="WebDAV_Folder_Administration">WebDAV Mappeadministration</msg>
</message_catalog>
<?xml version="1.0" encoding="ISO-8859-1"?>
<message_catalog package_key="oacs-dav" locale="de_DE" charset="ISO-8859-1">
<msg key="Disable">Deaktivieren</msg>
<msg key="Disable_Folders">Die ausgewhlten Ordner deaktivieren</msg>
<msg key="Disabled">Deaktiviert</msg>
<msg key="Enable">Aktivieren</msg>
<msg key="Enable_Folders">Die ausgewhlten Ordner aktivieren</msg>
<msg key="Enabled">Aktiviert</msg>
<msg key="Folder_Name">Ordnername</msg>
<msg key="Folder_URL">Ordner-URL</msg>
<msg key="Folders_Disabled">Die ausgewhlten Ordner wurden fr die WebDAV-Untersttzung deaktiviert</msg>
<msg key="Folders_Enabled">Die ausgewhlten Ordner wurden fr die WebDAV-Untersttzung aktiviert.</msg>
<msg key="Package_Name">Paket-Bezeichnung</msg>
<msg key="Package_Type">Paket-Typ</msg>
<msg key="Status">Status</msg>
<msg key="WebDAV_Folder_Administration">WebDAV-Ordnerverwaltung</msg>
</message_catalog>
<?xml version="1.0" encoding="utf-8"?>
<message_catalog package_key="oacs-dav" locale="el_GR" charset="utf-8">
<msg key="Disable">Απενεργοποίηση</msg>
<msg key="Disable_Folders">Απενεργοποίηση επιλεγμένων φακέλων</msg>
<msg key="Disabled">Απενεργοποιημένο</msg>
<msg key="Enable">Ενεργοποίηση</msg>
<msg key="Enable_Folders">Ενεργοποίηση επιλεγμένων φακέλων</msg>
<msg key="Enabled">Ενεργοποιημένο</msg>
<msg key="Folder_Name">Όνομα φακέλου</msg>
<msg key="Folder_URL">URL φακέλου</msg>
<msg key="Folders_Disabled">Επιλεγμένοι φάκελοι ανενεργοί για υποστήριξη WebDAV </msg>
<msg key="Folders_Enabled">Επιλεγμένοι φάκελοι ενεργοί για υποστήριξη WebDAV </msg>
<msg key="Package_Name">Όνομα πακέτου</msg>
<msg key="Package_Type">Τύπος πακέτου</msg>
<msg key="Status">Κατάσταση</msg>
<msg key="WebDAV_Folder_Administration">Διαχείριση WebDAV φακέλου</msg>
</message_catalog>
<?xml version="1.0" encoding="ISO-8859-1"?>
<message_catalog package_key="oacs-dav" locale="en_AU" charset="ISO-8859-1">
<msg key="Disable">Disable</msg>
<msg key="Disable_Folders">Disable Selected Folders</msg>
<msg key="Disabled">Disabled</msg>
<msg key="Enable">Enable</msg>
<msg key="Enable_Folders">Enable Selected Folders</msg>
<msg key="Enabled">Enabled</msg>
<msg key="Folder_Name">Folder Name</msg>
<msg key="Folder_URL">Folder URL</msg>
<msg key="Folders_Disabled">Selected Folders disabled for WebDAV support.</msg>
<msg key="Folders_Enabled">Selected Folders enabled for WebDAV support.</msg>
<msg key="Package_Name">Package Name</msg>
<msg key="Package_Type">Package Type</msg>
<msg key="Status">Status</msg>
<msg key="WebDAV_Folder_Administration">WebDAV Folder Administration</msg>
</message_catalog>
<?xml version="1.0" encoding="ISO-8859-1"?>
<message_catalog package_key="oacs-dav" locale="en_US" charset="ISO-8859-1">
<msg key="Disable">Disable</msg>
<msg key="Disable_Folders">Disable Selected Folders</msg>
<msg key="Disabled">Disabled</msg>
<msg key="Enable">Enable</msg>
<msg key="Enable_Folders">Enable Selected Folders</msg>
<msg key="Enabled">Enabled</msg>
<msg key="Folder_Name">Folder Name</msg>
<msg key="Folder_URL">Folder URL</msg>
<msg key="Folders_Disabled">Selected Folders disabled for WebDAV support.</msg>
<msg key="Folders_Enabled">Selected Folders enabled for WebDAV support.</msg>
<msg key="Package_Name">Package Name</msg>
<msg key="Package_Type">Package Type</msg>
<msg key="Status">Status</msg>
<msg key="WebDAV_Folder_Administration">WebDAV Folder Administration</msg>
</message_catalog>
<?xml version="1.0" encoding="ISO-8859-1"?>
<message_catalog package_key="oacs-dav" locale="es_CO" charset="ISO-8859-1">
<msg key="Disable">Deshabitar</msg>
<msg key="Disable_Folders">Deshabitar Carpetas Selecciondas </msg>
<msg key="Disabled">Deshabilitado</msg>
<msg key="Enable">Habilitar</msg>
<msg key="Enable_Folders">Habilitar Carpetas Seleccionadas</msg>
<msg key="Enabled">Habilitar</msg>
<msg key="Folder_Name">Nombre de la Carpeta</msg>
<msg key="Folder_URL">Carpeta URL</msg>
<msg key="Folders_Disabled">Carpetas Seleccionadas deshabilitadas para soporte WebDav.</msg>
<msg key="Folders_Enabled">Carpetas Seleccionadas habilitadas para soporte WebDAV.</msg>
<msg key="Package_Name">Nombre del Paquete</msg>
<msg key="Package_Type">Tipo del Paquete</msg>
<msg key="Status">Estado</msg>
<msg key="WebDAV_Folder_Administration">Administracin de la capeta WebDAV</msg>
</message_catalog>
<?xml version="1.0" encoding="ISO-8859-1"?>
<message_catalog package_key="oacs-dav" locale="es_ES" charset="ISO-8859-1">
<msg key="Disable">Deshabitar</msg>
<msg key="Disable_Folders">Deshabitar Carpetas Selecciondas </msg>
<msg key="Disabled">Deshabilitado</msg>
<msg key="Enable">Habilitar</msg>
<msg key="Enable_Folders">Habilitar Carpetas Seleccionadas</msg>
<msg key="Enabled">Habilitar</msg>
<msg key="Folder_Name">Nombre de la Carpeta</msg>
<msg key="Folder_URL">Carpeta URL</msg>
<msg key="Folders_Disabled">Carpetas Seleccionadas deshabilitadas para soporte WebDav.</msg>
<msg key="Folders_Enabled">Carpetas Seleccionadas habilitadas para soporte WebDAV.</msg>
<msg key="Package_Name">Nombre del Paquete</msg>
<msg key="Package_Type">Tipo del paquete</msg>
<msg key="Status">Estado</msg>
<msg key="WebDAV_Folder_Administration">Administracin WebDAV de la carpeta</msg>
</message_catalog>
<?xml version="1.0" encoding="ISO-8859-1"?>
<message_catalog package_key="oacs-dav" locale="es_GT" charset="ISO-8859-1">
<msg key="Disable">Deshabitar</msg>
<msg key="Disable_Folders">Deshabitar Carpetas Selecciondas </msg>
<msg key="Disabled">Deshabilitado</msg>
<msg key="Enable">Habilitar</msg>
<msg key="Enable_Folders">Habilitar Carpetas Seleccionadas</msg>
<msg key="Enabled">Habilitar</msg>
<msg key="Folder_Name">Nombre de la Carpeta</msg>
<msg key="Folder_URL">Carpeta URL</msg>
<msg key="Folders_Disabled">Carpetas Seleccionadas deshabilitadas para soporte WebDav.</msg>
<msg key="Folders_Enabled">Carpetas Seleccionadas habilitadas para soporte WebDAV.</msg>
<msg key="Package_Name">Nombre del Paquete</msg>
<msg key="Package_Type">Tipo del Paquete</msg>
<msg key="Status">Estado</msg>
<msg key="WebDAV_Folder_Administration">Administracin de la capeta WebDAV</msg>
</message_catalog>
<?xml version="1.0" encoding="ISO-8859-1"?>
<message_catalog package_key="oacs-dav" locale="eu_ES" charset="ISO-8859-1">
<msg key="Disable">Desaktibatu</msg>
<msg key="Disable_Folders">Desaktibatu aukeratutako karpetak</msg>
<msg key="Disabled">Desaktibatuta</msg>
<msg key="Enable">Aktibatu</msg>
<msg key="Enable_Folders">Aktibatu aukeratutako karpetak</msg>
<msg key="Enabled">Aktibatuta</msg>
<msg key="Folder_Name">Karpetaren Izena</msg>
<msg key="Folder_URL">Karpetaren URLa</msg>
<msg key="Folders_Disabled">Aukeratutako karpetak desaktibatuta WebDAV euskarrirako.</msg>
<msg key="Folders_Enabled">Aukeratutako karpetak aktibatuta WebDAV euskarrirako.</msg>
<msg key="Package_Name">Paketearen Izena</msg>
<msg key="Package_Type">Pakete-mota</msg>
<msg key="Status">Egoera</msg>
<msg key="WebDAV_Folder_Administration">WebDAV karpetaren kudeaketa</msg>
</message_catalog>
<?xml version="1.0" encoding="utf-8"?>
<message_catalog package_key="oacs-dav" locale="hi_IN" charset="utf-8">
<msg key="Disable">अशकतता </msg>
<msg key="Disable_Folders">अशकतता चुनना पुस्तिका</msg>
<msg key="Disabled">अशकतता </msg>
<msg key="Enable">समर्थ करना </msg>
<msg key="Enable_Folders">समर्थ चुनना पुस्तिका </msg>
<msg key="Enabled">समर्थ करना </msg>
<msg key="Folder_Name">पुस्तिका नाम </msg>
<msg key="Folder_URL">पुस्तिका URL</msg>
<msg key="Folders_Disabled">अशकतता चुनना पुस्तिका का WebDAV सहारा । </msg>
<msg key="Folders_Enabled">समर्थ चुनना पुस्तिका का WebDAV सहारा । </msg>
<msg key="Package_Name">पैकेज नाम </msg>
<msg key="Package_Type">पैकेज प्रकार </msg>
<msg key="Status">स्थिति </msg>
<msg key="WebDAV_Folder_Administration">WebDAV पुस्तिका शासन</msg>
</message_catalog>
<?xml version="1.0" encoding="ISO-8859-1"?>
<message_catalog package_key="oacs-dav" locale="it_IT" charset="ISO-8859-1">
<msg key="Disable">Disabilita</msg>
<msg key="Disable_Folders">Disabilita le cartelle selezionate</msg>
<msg key="Disabled">Disabilitata</msg>
<msg key="Enable">Abilita</msg>
<msg key="Enable_Folders">Abilita le cartelle selezionate</msg>
<msg key="Enabled">Abilitata</msg>
<msg key="Folder_Name">Nome cartella</msg>
<msg key="Folder_URL">URL della cartella</msg>
<msg key="Folders_Disabled">Le cartelle selezionate sono disabilitate per WebDAV</msg>
<msg key="Folders_Enabled">Le cartelle selezionate sono abilitate per WebDAV</msg>
<msg key="Package_Name">Nome package</msg>
<msg key="Package_Type">Tipo package</msg>
<msg key="Status">Stato</msg>
<msg key="WebDAV_Folder_Administration">Amministrazione delle cartelle WebDAV</msg>
</message_catalog>
<?xml version="1.0" encoding="utf-8"?>
<message_catalog package_key="oacs-dav" locale="ms_MY" charset="utf-8">
<msg key="Disable">Lumpuh</msg>
<msg key="Disable_Folders">Folder-folder Terpilih Dilumpuhkan</msg>
<msg key="Disabled">Dilumpuh</msg>
<msg key="Enable">Pulih</msg>
<msg key="Enable_Folders">Folder-folder Terpilih Dipulihkan</msg>
<msg key="Enabled">Dipulih</msg>
<msg key="Folder_Name">Nama Folder</msg>
<msg key="Folder_URL">Folder URL</msg>
<msg key="Folders_Disabled">Folder-folder Terpilih dilumpuhkan untuk sokongan WebDAV.</msg>
<msg key="Folders_Enabled">Folder-folder Terpilih dipulihkan untuk sokongan WebDAV.</msg>
<msg key="Package_Name">Nama Pakej</msg>
<msg key="Package_Type">Jenis Pakej</msg>
<msg key="Status">Status</msg>
<msg key="WebDAV_Folder_Administration">Pentadbiran Folder WebDAV</msg>
</message_catalog>
<?xml version="1.0" encoding="ISO-8859-1"?>
<message_catalog package_key="oacs-dav" locale="nl_NL" charset="ISO-8859-1">
<msg key="Disable">Uitschakelen</msg>
<msg key="Disable_Folders">Geselecteerde mappen uitschakelen</msg>
<msg key="Disabled">Uitgeschakeld</msg>
<msg key="Enable">Inschakelen</msg>
<msg key="Enable_Folders">Geselecteerde mappen inschakelen</msg>
<msg key="Enabled">Ingeschakeld</msg>
<msg key="Folder_Name">Mapnaam</msg>
<msg key="Folder_URL">Map-URL</msg>
<msg key="Folders_Disabled">WebDAV ondersteuning voor geselecteerde mappen is uitgeschakeld</msg>
<msg key="Folders_Enabled">WebDAV ondersteuning voor geselecteerde mappen is ingeschakeld</msg>
<msg key="Package_Name">Modulenaam</msg>
<msg key="Package_Type">Moduletype</msg>
<msg key="Status">Status</msg>
<msg key="WebDAV_Folder_Administration">Administratie WebDAV mappen</msg>
</message_catalog>
<?xml version="1.0" encoding="ISO-8859-1"?>
<message_catalog package_key="oacs-dav" locale="nn_NO" charset="ISO-8859-1">
<msg key="Disable">Gjer utilgjengeleg</msg>
<msg key="Disable_Folders">Gjer utvalde mapper utilgjengelege</msg>
<msg key="Disabled">Gjort utilgjengeleg</msg>
<msg key="Enable">Gjer tilgjengeleg</msg>
<msg key="Enable_Folders">Gjer utvalde mapper tilgjengelege</msg>
<msg key="Enabled">Gjort tilgjengeleg</msg>
<msg key="Folder_Name">Mappenavn</msg>
<msg key="Folder_URL">URL for mappe</msg>
<msg key="Folders_Disabled">Utvalde mapper som er utilgjengelege for WebDAV support.</msg>
<msg key="Folders_Enabled">Utvalde mapper som er tilgjengelege for WebDAV support.</msg>
<msg key="Package_Name">Pakkenavn</msg>
<msg key="Package_Type">Pakketype</msg>
<msg key="Status">Status</msg>
<msg key="WebDAV_Folder_Administration">Administrasjon av WebDAV mappe</msg>
</message_catalog>
<?xml version="1.0" encoding="ISO-8859-1"?>
<message_catalog package_key="oacs-dav" locale="no_NO" charset="ISO-8859-1">
<msg key="Disable">Gjr utilgjengelig</msg>
<msg key="Disable_Folders">Gjr utvalgte mapper utilgjengelige</msg>
<msg key="Disabled">Utilgjengelig</msg>
<msg key="Enable">Gjr tilgjengelig</msg>
<msg key="Enable_Folders">Gjr utvalgte mapper tilgjengelig</msg>
<msg key="Enabled">Tilgjengeliggjort</msg>
<msg key="Folder_Name">Navn p mappe</msg>
<msg key="Folder_URL">URL for mappe</msg>
<msg key="Folders_Disabled">Utvalgte mapper er ikke tilgjengelige for WebDAV-sttte.</msg>
<msg key="Folders_Enabled">Utvalgte mapper er tilgjengelige for WebDAV-sttte</msg>
<msg key="Package_Name">Navn p pakke</msg>
<msg key="Package_Type">Pakketype</msg>
<msg key="Status">Status</msg>
<msg key="WebDAV_Folder_Administration">Administrasjon for WebDAV-mappe</msg>
</message_catalog>
<?xml version="1.0" encoding="utf-8"?>
<message_catalog package_key="oacs-dav" locale="pa_IN" charset="utf-8">
<msg key="Disable">ਅਯੋਗ</msg>
<msg key="Disable_Folders">ਚੁਣੇ ਫੋਲਡਰ ਅਯੋਗ</msg>
<msg key="Disabled">ਅਯੋਗ</msg>
<msg key="Enable">ਯੋਗ</msg>
<msg key="Enable_Folders">ਚੁਣੇ ਫੋਲਡਰ ਯੋਗ</msg>
<msg key="Enabled">ਯੋਗ ਕੀਤਾ</msg>
<msg key="Folder_Name">ਫੋਲਡਰ ਨਾਂ</msg>
<msg key="Folder_URL">ਫੋਲਡਰ URL</msg>
<msg key="Package_Name">ਪੈਕੇਜ ਨਾਂ</msg>
<msg key="Package_Type">ਪੈਕੇਜ ਕਿਸਮ</msg>
<msg key="Status">ਹਾਲਤ</msg>
<msg key="WebDAV_Folder_Administration">WebDAV ਫੋਲਡਰ ਪਰਬੰਧਨ</msg>
</message_catalog>
<?xml version="1.0" encoding="ISO-8859-1"?>
<message_catalog package_key="oacs-dav" locale="pt_BR" charset="ISO-8859-1">
<msg key="Disable">Desabilitar</msg>
<msg key="Disable_Folders">Desabilitar Pastas Selecionadas</msg>
<msg key="Disabled">Disabilitado</msg>
<msg key="Enable">Habilitar</msg>
<msg key="Enable_Folders">Habilitar Pastas Selecionadas</msg>
<msg key="Enabled">Habilitado</msg>
<msg key="Folder_Name">Nome da Pasta</msg>
<msg key="Folder_URL">URL da Pasta</msg>
<msg key="Folders_Disabled">Pastas Selecionadas desabilitadas para suporte WebDAV.</msg>
<msg key="Folders_Enabled">Pastas selecionadas habilitadas para suporte WebDAV.</msg>
<msg key="Package_Name">Nome do Pacote</msg>
<msg key="Package_Type">Tipo de Pacote</msg>
<msg key="Status">Status</msg>
<msg key="WebDAV_Folder_Administration">Administrao de Pastas WebDAV</msg>
</message_catalog>
<?xml version="1.0" encoding="utf-8"?>
<message_catalog package_key="oacs-dav" locale="ro_RO" charset="utf-8">
<msg key="Disable">Dezactivează</msg>
<msg key="Disable_Folders">Dezactivează Folderele Selectate</msg>
<msg key="Disabled">Dezactivat</msg>
<msg key="Enable">Activează</msg>
<msg key="Enable_Folders">Activează Folderele Selectate</msg>
<msg key="Enabled">Activează</msg>
<msg key="Folder_Name">Numele Folder-ului</msg>
<msg key="Folder_URL">URL pentru Folder</msg>
<msg key="Folders_Disabled">Folderele Selectate au fost deactivate pentru WebDAV support</msg>
<msg key="Folders_Enabled">Foldere Selectate activate pentru support WebDAV</msg>
<msg key="Package_Name">Numele Programului</msg>
<msg key="Package_Type">Tipul de Program</msg>
<msg key="Status">Statut</msg>
<msg key="WebDAV_Folder_Administration">Administrarea Folderului WebDAV</msg>
</message_catalog>
<?xml version="1.0" encoding="utf-8"?>
<message_catalog package_key="oacs-dav" locale="ru_RU" charset="utf-8">
<msg key="Disable">Запретить</msg>
<msg key="Disable_Folders">Запретить для выбранных папок</msg>
<msg key="Disabled">Запрещено</msg>
<msg key="Enable">Разрешить</msg>
<msg key="Enable_Folders">Разрешить для выбранных папок</msg>
<msg key="Enabled">Разрешено</msg>
<msg key="Folder_Name">Название папки</msg>
<msg key="Folder_URL">URL папки</msg>
<msg key="Folders_Disabled">Для выбранных папок запрещена поддержка WebDAV.</msg>
<msg key="Folders_Enabled">Для выбранных папок разрешена поддержка WebDAV.</msg>
<msg key="Package_Name">Название пакета</msg>
<msg key="Package_Type">Тип пакета</msg>
<msg key="Status">Состояние</msg>
<msg key="WebDAV_Folder_Administration">Управление папкой WebDAV</msg>
</message_catalog>
<?xml version="1.0" encoding="utf-8"?>
<message_catalog package_key="oacs-dav" locale="zh_CN" charset="utf-8">
<msg key="Disable">不能</msg>
<msg key="Disable_Folders">不可选择的文件夹</msg>
<msg key="Disabled">不能</msg>
<msg key="Enable"></msg>
<msg key="Enable_Folders">可选择的文件夹</msg>
<msg key="Enabled"></msg>
<msg key="Folder_Name">文件夹名</msg>
<msg key="Folder_URL">文件夹URL</msg>
<msg key="Folders_Disabled">所选文件夹不支持WebDAV。</msg>
<msg key="Folders_Enabled">所选文件夹支持WebDAV。</msg>
<msg key="Package_Name">包名称</msg>
<msg key="Package_Type">包类型</msg>
<msg key="Status">状态</msg>
<msg key="WebDAV_Folder_Administration">WebDAV文件夹管理</msg>
</message_catalog>
<?xml version="1.0"?>
<!-- Generated by the OpenACS Package Manager -->
<package key="oacs-dav" url="http://openacs.org/repository/apm/packages/dav-support" type="apm_service">
<package-name>webDAV Support</package-name>
<pretty-plural></pretty-plural>
<initial-install-p>f</initial-install-p>
<singleton-p>t</singleton-p>
<auto-mount>webdav-support</auto-mount>
<version name="1.2.0d2" url="http://openacs.org/repository/download/apm/oacs-dav-1.2.0d2.apm">
<owner url="mailto:dave@thedesignexperience.org">Dave Bauer</owner>
<summary>Provides services to enable webDAV access to content repository items.</summary>
<release-date>2004-09-27</release-date>
<vendor>OpenACS</vendor>
<description format="text/html">An interface to the tDAV webDAV package. oacs-dav provides services to offer webDAV access to content repository data.</description>
<provides url="oacs-dav" version="1.2.0d2"/>
<callbacks>
<callback type="after-install" proc="oacs_dav::install::package_install"/>
<callback type="before-uninstall" proc="oacs_dav::install::package_uninstall"/>
<callback type="after-upgrade" proc="oacs_dav::install::upgrade"/>
</callbacks>
<parameters>
<parameter datatype="number" min_n_values="1" max_n_values="1" name="DefaultLockTimeout" default="300" description="Length of time in seconds for locks if WebDAV client does not specify a time. Locks will expire after this length of time."/>
<parameter datatype="number" min_n_values="1" max_n_values="1" name="RequireAuthForPropfind" default="1" description="Require authorization for PROPFIND requests. Enable this to suuport Microsoft Web Folders clients which may not respond correctly to an authentication request. Set to 1 for enabled, 0 for disabled."/>
<parameter datatype="string" min_n_values="1" max_n_values="1" name="WebDAVURLPrefix" default="/dav" description="Prefix to be added to the urls for WebDAV requests."/>
</parameters>
</version>
</package>
--
-- @author Dave Bauer (dave@thedesignexperience.org)
-- @creation-date 2003-10-18
-- @cvs-id $Id$
--
-- create a table to map site node_ids to cr_folders
create table dav_site_node_folder_map (
node_id integer
constraint dav_sn_folder_map_node_id_un
unique
constraint dav_sn_folder_map_node_id_fk
references site_nodes on delete cascade,
folder_id integer
constraint dav_impls_folder_id_fk
references cr_folders on delete cascade,
enabled_p char(1)
constraint dav_sn_folder_map_enbld_p_bl
check (enabled_p in ('t','f'))
);
--
-- @author Dave Bauer (dave@thedesignexperience.org)
-- @creation-date 2003-10-19
-- @cvs-id $Id$
--
drop table dav_site_node_folder_map
\ No newline at end of file
--
--
--
-- @author Dave Bauer (dave@thedesignexperience.org)
-- @creation-date 2005-01-29
-- @arch-tag: 938abc15-f59c-4397-b882-f5a89884be62
-- @cvs-id $Id$
--
alter table dav_site_node_folder_map drop constraint dav_side_node_folder_map_node_id_fk;
alter table dav_site_node_folder_map add constraint dav_side_node_folder_map_node_id_fk foreign key (node_id) references site_nodes (node_id) on delete cascade;
alter table
dav_site_node_folder_map
drop constraint
dav_impls_folder_id_fk;
alter table
dav_site_node_folder_map
add constraint
dav_impls_folder_id_fk
foreign key (folder_id)
references cr_folders
on delete cascade;
--
-- @author Dave Bauer (dave@thedesignexperience.org)
-- @creation-date 2003-09-14
-- @cvs-id $Id$
--
-- create a table to map site node_ids to cr_folders
create table dav_site_node_folder_map (
node_id integer
constraint dav_site_node_folder_map_node_id_un
unique
constraint dav_side_node_folder_map_node_id_fk
references site_nodes on delete cascade,
folder_id integer
constraint dav_impls_folder_id_fk
references cr_folders on delete cascade,
enabled_p boolean
);
\ No newline at end of file
--
-- @author Dave Bauer (dave@thedesignexperience.org)
-- @creation-date 2003-09-28
-- @cvs-id $Id:
--
drop table dav_site_node_folder_map;
\ No newline at end of file
--
--
--
-- @author Dave Bauer (dave@thedesignexperience.org)
-- @creation-date 2005-01-29
-- @arch-tag: 938abc15-f59c-4397-b882-f5a89884be62
-- @cvs-id $Id$
--
alter table dav_site_node_folder_map drop constraint dav_side_node_folder_map_node_id_fk;
alter table dav_site_node_folder_map add constraint dav_side_node_folder_map_node_id_fk foreign key (node_id) references site_nodes (node_id) on delete cascade;
alter table
dav_site_node_folder_map
drop constraint
dav_impls_folder_id_fk;
alter table
dav_site_node_folder_map
add constraint
dav_impls_folder_id_fk
foreign key (folder_id)
references cr_folders
on delete cascade;
#
ad_library {
setup filters
@author Dave Bauer (dave@thedesignexperience.org)
@creation-date 2003-12-18
@cvs-id $Id$
}
set prefix [parameter::get \
-package_id [apm_package_id_from_key "oacs-dav"] \
-parameter "WebDAVURLPrefix" \
-default "/dav"]
set url "${prefix}/*"
set filter_url "${prefix}/*"
ns_register_filter preauth GET ${filter_url} oacs_dav::authorize
ns_register_filter preauth HEAD ${filter_url} oacs_dav::authorize
ns_register_filter preauth PUT ${filter_url} oacs_dav::authorize
ns_register_filter preauth MKCOL ${filter_url} oacs_dav::authorize
ns_register_filter preauth COPY ${filter_url} oacs_dav::authorize
ns_register_filter preauth MOVE ${filter_url} oacs_dav::authorize
ns_register_filter preauth PROPFIND ${filter_url} oacs_dav::authorize
ns_register_filter preauth PROPPATCH ${filter_url} oacs_dav::authorize
ns_register_filter preauth DELETE ${filter_url} oacs_dav::authorize
ns_register_filter preauth LOCK ${filter_url} oacs_dav::authorize
ns_register_filter preauth UNLOCK ${filter_url} oacs_dav::authorize
ns_log notice "OACS-DAV preauth filters loaded on $filter_url"
ns_register_proc GET ${url} oacs_dav::handle_request
ns_register_proc HEAD ${url} oacs_dav::handle_request
ns_register_proc COPY ${url} oacs_dav::handle_request
ns_register_proc PUT ${url} oacs_dav::handle_request
ns_register_proc DELETE ${url} oacs_dav::handle_request
ns_register_proc PROPFIND ${url} oacs_dav::handle_request
ns_register_proc PROPPATCH ${url} oacs_dav::handle_request
ns_register_proc MKCOL ${url} oacs_dav::handle_request
ns_register_proc MOVE ${url} oacs_dav::handle_request
ns_register_proc LOCK ${url} oacs_dav::handle_request
ns_register_proc UNLOCK ${url} oacs_dav::handle_request
#
ad_library {
Setup procs to run at package install, should be run only once.
@author Dave Bauer (dave@thedesignexperience.org)
@creation-date 2003-09-11
@cvs-id $Id$
}
namespace eval oacs_dav::install {}
ad_proc -private oacs_dav::install::package_install {} {
setup DAV service contracts
} {
db_transaction {
create_service_contracts
register_implementation
}
}
ad_proc -private oacs_dav::install::package_uninstall {} {
clean up for package uninstall
} {
db_transaction {
delete_service_contracts
unregister_implementation
}
}
# this is far from complete or even known to be going in the
# right direction
# somehow we need to get identication information from the
# user and send back status of permission allowed or denied
# look at the DAV spec to get an idea of what inputs and
# outputs these methods have
ad_proc -private oacs_dav::install::create_service_contracts {
} {
create service contract for DAV methods
} {
oacs_dav::install::create_dav_sc
oacs_dav::install::create_dav_put_type_sc
oacs_dav::install::create_dav_mkcol_type_sc
}
ad_proc -private oacs_dav::install::create_dav_sc {
} {
create dav service contract spec
} {
set contract_name "dav"
set dav_spec {
description "implements DAV methods"
operations {
get {
description "DAV GET Method"
output { content:string }
}
put {
description "DAV PUT Method"
output { response:string }
}
propfind {
description "DAV PROPFIND Method"
output {
response:string
}
}
delete {
description "DAV DELETE Method"
output {
response:string
}
}
mkcol {
description "DAV MKCOL Method"
output {
response:string
}
}
copy {
description "DAV Copy Method"
output {
response:string
}
}
move {
description "DAV Move Method"
output {
response:string
}
}
proppatch {
description "DAV PROPATCH Method"
output {
response:string
}
}
lock {
description "DAV LOCK Method"
output {
response:string
}
}
unlock {
description "DAV UNLOCK Method"
output {
response:string
}
}
head {
description "DAV HEAD Method"
output {
response:string
}
}
}
}
acs_sc::contract::new_from_spec \
-spec [concat [list name $contract_name] $dav_spec ]
}
ad_proc -private oacs_dav::install::create_dav_put_type_sc {
} {
create dav_put_type service contract
} {
set contract_name "dav_put_type"
set dav_spec {
description "returns content type to use for PUT operation"
operations {
get_type {
description "DAV PUT Content Type"
output { content_type:string }
}
}
}
acs_sc::contract::new_from_spec \
-spec [concat [list name $contract_name] $dav_spec ]
}
ad_proc -private oacs_dav::install::create_dav_mkcol_type_sc {
} {
create dav_mkcol_type service contract
} {
set contract_name "dav_mkcol_type"
set spec {
description "returns content type to use for MKCOL operation"
operations {
get_type {
description "DAV MKCOL Content Type"
output { content_type:string }
}
}
}
acs_sc::contract::new_from_spec \
-spec [concat [list name $contract_name] $spec ]
}
ad_proc -private oacs_dav::install::delete_service_contracts {
} {
remove service contracts on uninstall
} {
acs_sc::contract::delete -name dav
acs_sc::contract::delete -name dav_put_type
acs_sc::contract::delete -name dav_mkcol_type
}
ad_proc -private oacs_dav::install::register_implementation {
} {
add default content repository service contract
implementation
} {
set spec {
name "content_revision"
aliases {
get oacs_dav::impl::content_revision::get
head oacs_dav::impl::content_revision::head
put oacs_dav::impl::content_revision::put
propfind oacs_dav::impl::content_revision::propfind
delete oacs_dav::impl::content_revision::delete
mkcol oacs_dav::impl::content_revision::mkcol
proppatch oacs_dav::impl::content_revision::proppatch
copy oacs_dav::impl::content_revision::copy
move oacs_dav::impl::content_revision::move
lock oacs_dav::impl::content_revision::lock
unlock oacs_dav::impl::content_revision::unlock
}
contract_name {dav}
owner [oacs_dav::package_key]
}
acs_sc::impl::new_from_spec -spec $spec
set spec {
name "content_folder"
aliases {
get oacs_dav::impl::content_folder::get
head oacs_dav::impl::content_revision::head
put oacs_dav::impl::content_folder::put
propfind oacs_dav::impl::content_folder::propfind
delete oacs_dav::impl::content_folder::delete
mkcol oacs_dav::impl::content_folder::mkcol
proppatch oacs_dav::impl::content_folder::proppatch
copy oacs_dav::impl::content_folder::copy
move oacs_dav::impl::content_folder::move
lock oacs_dav::impl::content_folder::lock
unlock oacs_dav::impl::content_folder::unlock
}
contract_name {dav}
owner [oacs_dav::package_key]
}
acs_sc::impl::new_from_spec -spec $spec
}
ad_proc -private oacs_dav::install::unregister_implementation {
} {
remove default service contract implementation
} {
acs_sc::impl::delete -contract_name dav -impl_name content_folder
acs_sc::impl::delete -contract_name dav -impl_name content_revision
}
ad_proc -private oacs_dav::install::upgrade {
-from_version_name
-to_version_name
} {
Install new DAV service contracts
} {
apm_upgrade_logic \
-from_version_name $from_version_name \
-to_version_name $to_version_name \
-spec {
1.0b1 1.0b2 {
oacs_dav::install::create_dav_mkcol_type_sc
}
}
}
\ No newline at end of file
<?xml version="1.0"?>
<queryset>
<rdbms><type>oracle</type><version>8.1.6</version></rdbms>
<fullquery name="oacs_dav::conn_setup.get_item_id">
<querytext>
begin
:1 := content_item.get_id(
item_path => :item_name,
root_folder_id => :parent_id,
resolve_index => 'f');
end;
</querytext>
</fullquery>
<fullquery name="oacs_dav::children_have_permission_p.child_perms">
<querytext>
select count(*)
from (select item_id
from cr_items
connect by prior item_id = parent_id
start with item_id = :item_id)
where not exists (select 1
from acs_object_party_privilege_map m
where m.object_id = cr_items.item_id
and m.party_id = :user_id
and m.privilege = :privilege)
</querytext>
</fullquery>
<fullquery
name="oacs_dav::impl::content_folder::propfind.get_properties">
<querytext>
select nvl (cr.content_length,0) as content_length,
nvl (cr.mime_type,'*/*') as mime_type,
to_char(o.creation_date, 'YYYY-MM-DD"T"HH:MI:SS."000"') as creation_date,
to_char(o.last_modified, 'Dy, Dd Mon YYYY HH:MI:SS "${os_time_zone}"') as last_modified,
ci1.item_id,
case when ci1.item_id=:folder_id then '' else ci1.name end as name,
content_item.get_path(ci1.item_id,:folder_id) as item_uri,
case when o.object_type='content_folder' then 1 else 0 end
as collection_p
from (
select * from cr_items
where (parent_id=:folder_id
or item_id=:folder_id)
and level <= :depth + 1
connect by prior item_id=parent_id
start with item_id=:folder_id
) ci1,
cr_revisions cr,
acs_objects o
where
ci1.item_id=o.object_id
and ci1.live_revision = cr.revision_id(+)
and exists (select 1
from acs_object_party_privilege_map m
where m.object_id = ci1.item_id
and m.party_id = :user_id
and m.privilege = 'read')
</querytext>
</fullquery>
<fullquery
name="oacs_dav::impl::content_revision::propfind.get_properties">
<querytext>
select
ci.item_id,
ci.name,
content_item.get_path(ci.item_id,:folder_id) as item_uri,
nvl(cr.mime_type,'*/*') as mime_type,
nvl(cr.content_length,0) as content_length,
to_char(o.creation_date, 'YYYY-MM-DD"T"HH:MI:SS."000"') as creation_date,
to_char(o.last_modified, 'Dy, Dd Mon YYYY HH:MI:SS "${os_time_zone}"') as last_modified
from cr_items ci,
acs_objects o,
cr_revisions cr
where
ci.item_id=:item_id
and ci.item_id = o.object_id
and cr.revision_id=ci.live_revision
</querytext>
</fullquery>
<fullquery
name="oacs_dav::impl::content_folder::mkcol.create_folder">
<querytext>
begin
:1 := content_folder.new(
name => :new_folder_name,
label => :label,
description => :description,
parent_id => :parent_id,
context_id => :parent_id,
folder_id => NULL,
creation_date => sysdate,
creation_user => :user_id,
creation_ip => :peer_addr
);
end;
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_folder::copy.copy_folder">
<querytext>
begin
content_folder.copy (
folder_id => :copy_folder_id,
target_folder_id => :new_parent_folder_id,
creation_user => :user_id,
creation_ip => :peer_addr,
name => :new_name
);
end;
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_folder::copy.update_child_revisions">
<querytext>
update cr_items
set live_revision=latest_revision
where exists (
select 1
from
(select ci1.item_id as child_item_id
from cr_items ci1
connect by prior item_id = parent_id
start with item_id = :folder_id
) children
where item_id=children.child_item_id
)
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_folder::move.move_folder">
<querytext>
begin
:1 := content_folder.move (
folder_id => :move_folder_id,
target_folder_id => :new_parent_folder_id,
name => :new_name
);
end;
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_folder::move.rename_folder">
<querytext>
begin
content_folder.edit_name (
folder_id => :move_folder_id,
name => :new_name,
label => :new_name,
description => NULL
);
end;
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_revision::move.move_item">
<querytext>
begin
content_item.move (
item_id => :item_id,
target_folder_id => :new_parent_folder_id,
name => :new_name
);
end;
</querytext>
</fullquery>
<fullquery
name="oacs_dav::impl::content_revision::move.rename_item">
<querytext>
begin
content_item.edit_name (
item_id => :item_id,
name => :new_name
);
end;
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_revision::copy.copy_item">
<querytext>
begin
:1 := content_item.copy2 (
item_id => :copy_item_id,
target_folder_id => :new_parent_folder_id,
creation_user => :user_id,
creation_ip => :peer_addr,
name => :new_name
);
end;
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_revision::move.delete_for_move">
<querytext>
begin
content_item.del(
item_id => :dest_item_id
);
end;
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_revision::copy.delete_for_copy">
<querytext>
begin
content_item.del(
item_id => :dest_item_id
);
end;
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_revision::delete.delete_item">
<querytext>
begin
content_item.del (
item_id => :item_id
);
end;
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_folder::delete.delete_folder">
<querytext>
begin
content_folder.del (
folder_id => :item_id,
cascade_p => 't'
);
end;
</querytext>
</fullquery>
<fullquery name="oacs_dav::item_parent_folder_id.get_parent_folder_id">
<querytext>
begin
:1 := content_item.get_id(
item_path => :parent_name,
root_folder_id => :root_folder_id,
resolve_index => 'f');
end;
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_folder::copy.get_dest_id">
<querytext>
select content_item.get_id(:new_name,:new_parent_folder_id,'f') from dual
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_folder::move.get_dest_id">
<querytext>
select content_item.get_id(:new_name,:new_parent_folder_id,'f') from dual
</querytext>
</fullquery>
<fullquery
name="oacs_dav::impl::content_folder::move.delete_for_move">
<querytext>
begin
content_folder.del(
folder_id => :dest_item_id,
cascade_p => 't');
end;
</querytext>
</fullquery>
<fullquery
name="oacs_dav::impl::content_folder::copy.delete_for_copy">
<querytext>
begin
content_folder.del(
folder_id => :dest_item_id,
cascade_p => 't');
end;
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_revision::copy.get_dest_id">
<querytext>
select content_item.get_id(:new_name,:new_parent_folder_id,'f') from dual
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_revision::move.get_dest_id">
<querytext>
select content_item.get_id(:new_name,:new_parent_folder_id,'f') from dual
</querytext>
</fullquery>
</queryset>
<?xml version="1.0"?>
<queryset>
<rdbms><type>postgresql</type><version>7.1</version></rdbms>
<fullquery name="oacs_dav::conn_setup.get_item_id">
<querytext>
select content_item__get_id(:item_name,:parent_id,'f')
</querytext>
</fullquery>
<fullquery name="oacs_dav::children_have_permission_p.child_perms">
<querytext>
select count(*)
from cr_items c1, cr_items c2
where c2.item_id = :item_id
and c1.tree_sortkey between c2.tree_sortkey and tree_right(c2.tree_sortkey)
and not exists (select 1
from acs_object_party_privilege_map m
where m.object_id = cr_items.item_id
and m.party_id = :user_id
and m.privilege = :privilege)
</querytext>
</fullquery>
<fullquery
name="oacs_dav::impl::content_folder::propfind.get_properties">
<querytext>
select
coalesce (cr.content_length,0) as content_length,
coalesce(cr.mime_type,'*/*') as mime_type,
to_char(timezone('GMT',o.creation_date) :: timestamptz ,'YYYY-MM-DD"T"HH:MM:SS.MS"Z"') as creation_date,
to_char(timezone('GMT',o.last_modified) :: timestamptz ,'Dy, DD Mon YYYY HH:MM:SS TZ') as last_modified,
ci1.item_id,
case when ci1.item_id=ci2.item_id then '' else ci1.name end as name,
content_item__get_path(ci1.item_id,:folder_id) as item_uri,
case when o.object_type='content_folder' then 1 else 0 end
as collection_p
from
cr_items ci1,
cr_revisions cr,
cr_items ci2,
acs_objects o
where
ci1.live_revision = cr.revision_id and
ci1.tree_sortkey between ci2.tree_sortkey and tree_right(ci2.tree_sortkey) and
ci2.item_id=:folder_id and
ci1.item_id = o.object_id and
(tree_level(ci1.tree_sortkey) - tree_level(ci2.tree_sortkey)) <= :depth :: integer and
exists (select 1
from acs_object_party_privilege_map m
where m.object_id = ci1.item_id
and m.party_id = :user_id
and m.privilege = 'read')
union
select 0 as content_length,
'*/*' as mime_type,
to_char(timezone('GMT',o.creation_date) :: timestamptz ,'YYYY-MM-DD"T"HH:MM:SS.MS"Z"') as creation_date,
to_char(timezone('GMT',o.last_modified) :: timestamptz ,'Dy, DD Mon YYYY HH:MM:SS TZ') as last_modified,
ci1.item_id,
case when ci1.item_id=ci2.item_id then '' else ci1.name end as name,
content_item__get_path(ci1.item_id,:folder_id) as item_uri,
case when o.object_type='content_folder' then 1 else 0 end
as collection_p
from
cr_items ci1,
cr_items ci2,
acs_objects o
where
ci1.tree_sortkey between ci2.tree_sortkey and tree_right(ci2.tree_sortkey) and
ci2.item_id=:folder_id and
ci1.item_id = o.object_id and
(tree_level(ci1.tree_sortkey) - tree_level(ci2.tree_sortkey)) <= :depth :: integer and
exists (select 1
from acs_object_party_privilege_map m
where m.object_id = ci1.item_id
and m.party_id = :user_id
and m.privilege = 'read') and
not exists (select 1
from cr_revisions cr
where cr.revision_id = ci1.live_revision)
</querytext>
</fullquery>
<fullquery
name="oacs_dav::impl::content_revision::propfind.get_properties">
<querytext>
select
ci.item_id,
ci.name,
content_item__get_path(ci.item_id,:folder_id) as item_uri,
coalesce(cr.mime_type,'*/*') as mime_type,
coalesce(cr.content_length,0) as content_length,
to_char(timezone('GMT',o.creation_date) :: timestamptz ,'YYYY-MM-DD"T"HH:MM:SS.MS"Z"') as creation_date,
to_char(timezone('GMT',o.last_modified) :: timestamptz ,'Dy, DD Mon YYYY HH:MM:SS TZ') as last_modified
from cr_items ci,
acs_objects o,
cr_revisions cr
where
ci.item_id=:item_id
and ci.item_id = o.object_id
and cr.revision_id=ci.live_revision
and exists (select 1
from acs_object_party_privilege_map m
where m.object_id = ci.item_id
and m.party_id = :user_id
and m.privilege = 'read')
</querytext>
</fullquery>
<fullquery
name="oacs_dav::impl::content_folder::mkcol.create_folder">
<querytext>
select content_folder__new(
:new_folder_name,
:label,
:description,
:parent_id,
:parent_id,
NULL,
current_timestamp,
:user_id,
:peer_addr
)
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_folder::copy.copy_folder">
<querytext>
select content_folder__copy (
:copy_folder_id,
:new_parent_folder_id,
:user_id,
:peer_addr,
:new_name
)
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_folder::copy.update_child_revisions">
<querytext>
update cr_items
set live_revision = latest_revision
where exists (
select 1
from
(select ci1.item_id as child_item_id
from cr_items ci1, cr_items ci2
where ci2.item_id=:new_folder_id
and ci1.tree_sortkey
between ci2.tree_sortkey and tree_right(ci2.tree_sortkey)
) children
where item_id=children.child_item_id
)
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_folder::move.move_folder">
<querytext>
select content_folder__move (
:move_folder_id,
:new_parent_folder_id,
:new_name
)
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_folder::move.rename_folder">
<querytext>
select content_folder__edit_name (
:move_folder_id,
:new_name,
:new_name,
NULL
)
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_revision::move.move_item">
<querytext>
select content_item__move (
:item_id,
:new_parent_folder_id,
:new_name
)
</querytext>
</fullquery>
<fullquery
name="oacs_dav::impl::content_revision::move.rename_item">
<querytext>
select content_item__edit_name (
:item_id,
:new_name
)
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_revision::copy.copy_item">
<querytext>
select content_item__copy (
:copy_item_id,
:new_parent_folder_id,
:user_id,
:peer_addr,
:new_name
)
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_revision::copy.delete_for_copy">
<querytext>
select content_item__delete(:dest_item_id)
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_revision::move.delete_for_move">
<querytext>
select content_item__delete(:dest_item_id)
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_revision::delete.delete_item">
<querytext>
select content_item__delete (
:item_id
)
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_folder::delete.delete_folder">
<querytext>
select content_folder__delete (
:item_id,
't'
)
</querytext>
</fullquery>
<fullquery name="oacs_dav::item_parent_folder_id.get_parent_folder_id">
<querytext>
select content_item__get_id(:parent_name,:root_folder_id,'f')
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_folder::copy.get_dest_id">
<querytext>
select content_item__get_id(:new_name,:new_parent_folder_id,'f')
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_folder::move.get_dest_id">
<querytext>
select content_item__get_id(:new_name,:new_parent_folder_id,'f')
</querytext>
</fullquery>
<fullquery
name="oacs_dav::impl::content_folder::move.delete_for_move">
<querytext>
select content_folder__delete(:dest_item_id,'t');
</querytext>
</fullquery>
<fullquery
name="oacs_dav::impl::content_folder::copy.delete_for_copy">
<querytext>
select content_folder__delete(:dest_item_id,'t');
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_revision::copy.get_dest_id">
<querytext>
select content_item__get_id(:new_name,:new_parent_folder_id,'f')
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_revision::move.get_dest_id">
<querytext>
select content_item__get_id(:new_name,:new_parent_folder_id,'f')
</querytext>
</fullquery>
</queryset>
# /packages/oacs-dav/tcl/oacs-dav-procs.tcl
ns_log debug "\nLoading oacs-dav-procs.tcl"
ad_library {
Support for tDAV tcl webDAV implemenation
@author Dave Bauer (dave@thedesignexperience.org)
@creation-date 2003-09-11
@cvs-id $Id$
}
namespace eval oacs_dav {}
ad_proc oacs_dav::urlencode { string } {
urlencode allowing characters according to rfc 1738
http://www.w3.org/Addressing/rfc1738.txt
"Thus, only alphanumerics, the special characters "$-_.+!*'(),", and
reserved characters used for their reserved purposes may be used
unencoded within a URL."
ignore + used to encode spaces in query strings
This is mainly to support MS Web Folders which do not follow the
spec which states that any character may be urlencoded. Web Folders
rejects the entire collection as invalid if a filename contains
one of these characters encoded.
} {
set encoded_string [ns_urlencode $string]
set encoded_string [string map -nocase \
{+ %20 %2d - %5f _ %24 $ %2e . %21 ! %28 ( %29 ) %27 ' %2c ,} $encoded_string]
return $encoded_string
}
ad_proc oacs_dav::folder_enabled {
-folder_id
} {
@param folder_id
@return t if folder is webdav enabled, f if not
} {
return [db_string enabled_p "" -default "f"]
}
ad_proc oacs_dav::set_user_id {} {
set user_id based on authentication header
} {
# should be something like "Basic 29234k3j49a"
set a [ns_set get [ns_conn headers] Authorization]
if {[string length $a]} {
ns_log debug "\nTDAV auth_check authentication info $a"
# get the second bit, the base64 encoded bit
set up [lindex [split $a " "] 1]
# after decoding, it should be user:password; get the username
set user [lindex [split [ns_uudecode $up] ":"] 0]
set password [lindex [split [ns_uudecode $up] ":"] 1]
ns_log debug "\nACS VERSION [ad_acs_version]"
ns_log debug "\nTDAV 5.0 authentication"
# check all authorities
foreach authority [auth::authority::get_authority_options] {
set authority_id [lindex $authority 1]
array set auth [auth::authenticate \
-username $user \
-password $password \
-authority_id $authority_id \
-no_cookie]
if {![string equal $auth(auth_status) "ok"]} {
array set auth [auth::authenticate \
-email $user \
-password $password \
-authority_id $authority_id \
-no_cookie]
}
if {[string equal $auth(auth_status) "ok"]} {
# we can stop checking
break
}
}
if {![string equal $auth(auth_status) "ok"]} {
ns_log debug "\nTDAV 5.0 auth status $auth(auth_status)"
ns_returnunauthorized
return 0
}
ns_log debug "\nTDAV: auth_check openacs 5.0 user_id= $auth(user_id)"
ad_conn -set user_id $auth(user_id)
} else {
# no authenticate header, anonymous visitor
ad_conn -set user_id 0
ad_conn -set untrusted_user_id 0
}
}
ad_proc oacs_dav::authorize { args } {
check is user_id has permission to perform the WebDAV method on
the URI
} {
ns_log debug "\nOACS-DAV running oacs_dav::authorize"
# Restrict to SSL if required
if { [security::RestrictLoginToSSLP] && ![security::secure_conn_p] } {
ns_returnunauthorized
return filter_return
}
# set common data for all requests
oacs_dav::conn_setup
set method [string tolower [oacs_dav::conn method]]
set item_id [oacs_dav::conn item_id]
set user_id [oacs_dav::conn user_id]
set folder_id [oacs_dav::conn folder_id]
ns_log debug "\nOACS-DAV oacs_dav::authorize user_id $user_id method $method item_id $item_id"
set authorized_p 0
# if item doesn't exist don't bother checking....
if {[empty_string_p $item_id]} {
if {![string equal "put" $method] && ![string equal "mkcol" $method] && ![string equal "lock" $method]} {
ns_log debug "\noacs_dav::authorize file not found"
ns_return 404 text/plain "File Not Found"
return filter_return
}
}
switch $method {
put -
mkcol {
set authorized_p [permission::permission_p \
-object_id $folder_id \
-party_id $user_id \
-privilege "create"]
}
delete {
set authorized_p [permission::permission_p \
-object_id $item_id \
-party_id $user_id \
-privilege "delete"]
}
lock {
if {![empty_string_p $item_id]} {
set authorized_p [permission::permission_p \
-object_id $item_id \
-party_id $user_id \
-privilege "write"]
} else {
# if item does not exist yet check for create on
# the collection and create a null lock
set authorized_p [permission::permission_p \
-object_id $folder_id \
-party_id $user_id \
-privilege "create"]
}
}
unlock -
proppatch {
set authorized_p [permission::permission_p \
-object_id $item_id \
-party_id $user_id \
-privilege "write"]
}
copy -
move {
set authorized_p [expr [permission::permission_p \
-object_id $item_id \
-party_id $user_id \
-privilege "read"] \
&& [permission::permission_p \
-object_id [oacs_dav::conn dest_parent_id ] \
-party_id $user_id \
-privilege "create"]\
|| [permission::permission_p \
-object_id [oacs_dav::conn dest_parent_id ] \
-party_id $user_id \
-privilege "write"]]
}
propfind {
if {[empty_string_p $user_id]} {
ns_returnunauthorized
} else {
set authorized_p [permission::permission_p \
-object_id $item_id \
-party_id $user_id \
-privilege "read"]
}
}
head -
get {
# default for GET PROPFIND
set authorized_p [permission::permission_p \
-object_id $item_id \
-party_id $user_id \
-privilege "read"]
}
}
if {![string equal $authorized_p 1]} {
ns_returnunauthorized
return filter_return
}
return filter_ok
}
ad_proc -public oacs_dav::conn {
args
} {
shared data for WebDAV requests
} {
global tdav_conn
set flag [lindex $args 0]
if { [string index $flag 0] != "-" } {
set var $flag
set flag "-get"
} else {
set var [lindex $args 1]
}
switch -- $flag {
-set {
set value [lindex $args 2]
set tdav_conn($var) $value
return $value
}
-get {
if { [info exists tdav_conn($var)] } {
return $tdav_conn($var)
} else {
return [ad_conn $var]
}
}
}
}
ad_proc -public oacs_dav::register_folder {
{-enabled_p "t"}
folder_id
node_id
} {
add a uri to dav support
@param folder_id
@param node_id
Register a root WebDAV enabled folder for a site node_id
All requests that resolve to this site node id will be checked for
WebDAV content using this folder as the root. Only one folder per
node_id can be registered.
} {
db_transaction {
db_dml add_folder ""
} on_error {
ns_log error "OACS-DAV Failed attempt to add folder_id $folder_id as a WebDAV enabled folder for node_id $node_id. One folder is already registered"
error "Only one folder per node_id may be registered."
}
}
ad_proc -public oacs_dav::unregister_folder {
folder_id
node_id
} {
remove a uri from dav support
@param folder_id
@param node_id
} {
db_dml remove_folder ""
}
ad_proc -public oacs_dav::item_parent_folder_id {
uri
} {
get the folder_id of the parent of an item
from the uri
@param uri
@returns parent_folder_id or empty string if folder does not exist
} {
array set sn [oacs_dav::request_site_node $uri]
set node_id $sn(node_id)
set root_folder_id [oacs_dav::request_folder_id $node_id]
set urlv [split [string trimright [string range $uri [string length $sn(url)] end] "/"] "/"]
if {[llength $urlv] >1} {
set parent_name [join [lrange $urlv 0 [expr [llength $urlv] -2 ] ] "/" ]
} else {
set parent_name "/"
}
ns_log debug "\nparent_folder_id urlv $urlv parent_name $parent_name uri $uri"
if {[string equal [string trimright $parent_name "/"] [string trimright $sn(url) "/"]]} {
# content_item__get_id can't resolve "/"
# because it strips the leading and trailing /
# from the url you pass in, and cr_items.name of the folder
# is not and empty string
set parent_id $root_folder_id
} else {
set parent_id [db_exec_plsql get_parent_folder_id ""]
}
return $parent_id
}
ad_proc -public oacs_dav::uri_prefix {
} {
@return URI prefix to use for WebDAV requests
} {
set oacs_dav_package_id [apm_package_id_from_key "oacs-dav"]
return [parameter::get -package_id $oacs_dav_package_id -parameter "WebDAVURLPrefix" -default "/dav"]
}
ad_proc -public oacs_dav::conn_setup {} {
Setup oacs_dav::conn, authenticate user
} {
ad_conn -reset
set uri [ns_urldecode [ns_conn url]]
ns_log debug "\nconn_setp uri \"$uri\" "
set dav_url_regexp "^[oacs_dav::uri_prefix]"
regsub $dav_url_regexp $uri {} uri
if {[empty_string_p $uri]} {
set uri "/"
}
oacs_dav::conn -set uri $uri
set method [ns_conn method]
ns_log debug "\noacs_dav::conn_setup: uri \"$uri\" method $method"
oacs_dav::set_user_id
ns_log debug "\noacs_dav::conn_setup: uri \"$uri\" method $method user_id [oacs_dav::conn user_id]"
array set sn [oacs_dav::request_site_node $uri]
set node_id [oacs_dav::conn -set node_id $sn(node_id)]
set package_id [oacs_dav::conn -set package_id $sn(package_id)]
set folder_id [oacs_dav::conn -set folder_id [oacs_dav::request_folder_id [oacs_dav::conn node_id]]]
set urlv [oacs_dav::conn -set urlv [split [string trimright $uri "/"] "/"]]
set destination [ns_urldecode [ns_set iget [ns_conn headers] Destination]]
regsub {https?://[^/]+/} $destination {/} dest
regsub $dav_url_regexp $dest {} dest
oacs_dav::conn -set oacs_destination $dest
if {![empty_string_p $dest]} {
oacs_dav::conn -set dest_parent_id [oacs_dav::item_parent_folder_id $dest]
}
# we need item_id and content_type
# we should use content::init but that has caching and I don't
# have time to resolve the issues that raises right now
# a full-featured, consistently used tcl api for CR will fix that
if {[llength $urlv] > 2} {
set parent_url [join [lrange $urlv 0 [expr [llength $urlv] -2 ] ] "/" ]
} else {
set parent_url "/"
}
ns_log debug "\noacs_dav::conn_setup: handle request parent_url $parent_url length urlv [llength $urlv] urlv $urlv"
set item_name [lindex $urlv end]
if {[empty_string_p $item_name]} {
# for propget etc we need the name of the folder
# the last element in urlv for a folder is an empty string
set item_name [lindex [split [string trimleft $parent_url "/"] "/"] end]
}
oacs_dav::conn -set item_name $item_name
ns_log debug "\noacs_dav::conn_setup: handle request parent_url $parent_url length urlv [llength $urlv] urlv $urlv item_name $item_name"
set parent_id [oacs_dav::item_parent_folder_id $uri]
set item_id [oacs_dav::conn -set item_id [db_exec_plsql get_item_id ""]]
ns_log debug "\noacs_dav::conn_setup: uri $uri parent_url $parent_url folder_id $folder_id"
if {[string equal [string trimright $uri "/"] [string trimright $sn(url) "/"]]} {
set item_id [oacs_dav::conn -set item_id $folder_id]
}
ns_log debug "\noacs_dav::conn_setup: item_id $item_id"
}
ad_proc -public oacs_dav::children_have_permission_p {
-user_id
-item_id
-privilege
} {
Check permission on child items of item_id for user_id with privilege
@param user_id
@param item_id
@param privilege
@return retursn 0 if user does not have privilege over all childern otherwise return 1
} {
set child_count [db_string child_perms ""]
ns_log notice "\n ----- \n oacs_dav::children_have_permission_p \n child_count = $child_count \n ----- \n"
incr child_count [db_string revision_perms ""]
ns_log notice "\n ----- \n oacs_dav::children_have_permission_p \n child_count = $child_count \n ----- \n"
ns_log notice "\n ----- \n oacs_dav::children_have_permission_p \n return [expr $child_count == 0] \n ----- \n"
return [expr $child_count == 0]
}
ad_proc -public oacs_dav::handle_request { uri method args } {
dispatch request to the proper service contract implmentation
} {
set uri [oacs_dav::conn uri]
set method [string tolower [ns_conn method]]
ns_log debug "\noacs_dav::handle_request method=$method uri=$uri"
set item_id [oacs_dav::conn item_id]
set folder_id [oacs_dav::conn folder_id]
set package_id [oacs_dav::conn package_id]
set node_id [oacs_dav::conn node_id]
set package_key [apm_package_key_from_id $package_id]
ns_log debug "\noacs_dav::handle_request item_id is $item_id"
if {[empty_string_p $item_id]} {
ns_log debug "\noacs_dav::handle_request item_id is empty"
# set this to null if nothing exists, only valid on PUT or MKCOL
# to create a new item, otherwise we bail
# item for URI does not exist
# ask package what content type to use
switch -- $method {
mkcol {
if {![acs_sc_binding_exists_p dav_mkcol_type $package_key]} {
set content_type "content_folder"
} else {
set content_type [acs_sc_call dav_mkcol_type get_type "" $package_key]
}
}
put {
if {![acs_sc_binding_exists_p dav_put_type $package_key]} {
set content_type "content_revision"
} else {
set content_type [acs_sc_call dav_put_type get_type "" $package_key]
}
}
lock {
# asssume resource on NULL LOCK
set content_type "content_revision"
}
default {
# return a 404 or other error
ns_log debug "\noacs_dav::handle_request: 404 handle request Item not found method $method URI $uri"
ns_return 404 text/html "File Not Found"
return
}
}
} else {
# get content type of existing item
set content_type \
[oacs_dav::conn -set content_type \
[db_string get_content_type "" -default "content_revision"]]
}
# use content type
# i think we should walk up the object type hierarchy up to
# content_revision if we don't find an implementation
# implementation name is content_type
set real_content_type [oacs_dav::conn -set real_content_type $content_type]
while {![acs_sc_binding_exists_p dav $content_type]} {
# go up content_type hierarchy
# we do the query here to avoid running the query
# when the implementation for the content_type does
# exist
set content_type [db_string supertype "select supertype from acs_object_types where object_type = :content_type" -default ""]
if { $content_type eq "content_revision"} {break}
if { $content_type eq ""} { error "no dav implementation found for content_type $real_content_type" }
ns_log Notice "now looking for a dav implementation for content_type $content_type"
}
oacs_dav::conn -set content_type $content_type
# probably should catch this
ns_log debug "\noacs_dav::handle_request method $method uri $uri item_id $item_id folder_id $folder_id package_id $package_id node_id $node_id content_type $content_type args $args"
set response [acs_sc_call dav $method "" $content_type]
# here the sc impl might return us some data,
# then we would probably have to send that to tDAV for processing
ns_log debug "\nDAV: response is \"$response\""
if {![string equal -nocase "get" $method]
&& ![string equal -nocase "head" $method]} {
tdav::respond $response
}
}
ad_proc -public oacs_dav::request_site_node { uri } {
resolves uri to a site node_id
} {
# if you want to serve up DAV content at a different URL
# you still need to mount a package in the site-map
# might change later when we figure out how to actually use it
ns_log debug "\nOACS-DAV!! uri $uri"
set sn [site_node::get -url $uri]
return $sn
}
ad_proc -public oacs_dav::request_folder_id { node_id } {
resolves a node_id to a DAV enabled folder_id
@param node_id site node_id of request
@returns folder_id, or empty string if no folder exists
in dav_package_folder_map for this node_id
} {
return [db_string get_folder_id "" -default ""]
}
namespace eval oacs_dav::impl::content_folder {}
# this is probably going away, is there such thing as "source"
# of a folder/collection?
ad_proc oacs_dav::impl::content_folder::get {} {
GET DAV method for content folders
can't get a folder
} {
# return something
# if its just a plain file, and a GET then do we need to send anything
# extra or just the file?
return [list 409]
}
ad_proc oacs_dav::impl::content_folder::head {} {
HEAD DAV method for content folders
can't get a folder
} {
# I am not sure what the behavior is, but the client
# should be smart enough to do a propfind on a folder/collection
return [list 409]
}
ad_proc oacs_dav::impl::content_folder::mkcol {} {
MKCOL DAV method for generic content folder
@author Dave Bauer
} {
set uri [oacs_dav::conn uri]
set user_id [oacs_dav::conn user_id]
set peer_addr [oacs_dav::conn peeraddr]
set item_id [oacs_dav::conn item_id]
set fname [oacs_dav::conn item_name]
set parent_id [oacs_dav::item_parent_folder_id $uri]
if {[empty_string_p $parent_id]} {
return [list 409]
}
if { ![empty_string_p $item_id]} {
return [list 405]
}
# probably have to revisit setting content_types allowed
# and permissions, but inheriting from the parent seems
# reasonable
db_transaction {
set new_folder_name $fname
set label $fname
set description $fname
set new_folder_id [db_exec_plsql create_folder ""]
set response [list 201]
} on_error {
set response [list 500]
}
return $response
}
ad_proc oacs_dav::impl::content_folder::copy {} {
COPY DAV method for generic content folder
} {
set package_id [oacs_dav::conn package_id]
set user_id [oacs_dav::conn user_id]
set peer_addr [oacs_dav::conn peeraddr]
set copy_folder_id [oacs_dav::conn item_id]
set overwrite [oacs_dav::conn overwrite]
set target_uri [oacs_dav::conn oacs_destination]
set new_parent_folder_id [oacs_dav::conn dest_parent_id]
set durlv [split [string trimright $target_uri "/"] "/"]
set new_name [lindex $durlv end]
set uri [oacs_dav::conn uri]
# check that destination exists and is WebDAV enabled
# when depth is 0 copy just the folder
# when depth is 1 copy contents
ns_log debug "\nDAV Folder Copy dest $target_uri parent_id $new_parent_folder_id"
if {[empty_string_p $new_parent_folder_id]} {
return [list 409]
}
set dest_item_id [db_string get_dest_id "" -default ""]
if {![empty_string_p $dest_item_id]} {
ns_log debug "\n ----- \n DAV Folder Copy Folder Exists item_id $dest_item_id overwrite $overwrite \n ----- \n"
if {![string equal -nocase $overwrite "T"]} {
return [list 412]
} elseif {![permission::permission_p \
-object_id $dest_item_id \
-party_id $user_id \
-privilege "write"]} {
ns_returnunauthorized
}
# according to the spec copy with overwrite means
# delete then copy
set children_permission_p [oacs_dav::children_have_permission_p -item_id $copy_folder_id -user_id $user_id -privilege "delete"]
if {!$children_permission_p} {
return [list 409]
}
if {![string equal "unlocked" [tdav::check_lock $target_uri]]} {
return [list 423]
}
db_exec_plsql delete_for_copy ""
set response [list 204]
ns_log debug "\n ----- \n CONTENT_FOLDER::COPY OVERWRITING RETURNING 204 \n ----- \n"
} else {
set response [list 201]
}
set err_p 0
db_transaction {
db_exec_plsql copy_folder ""
# we need to do this because in oracle content_folder__copy
# is a procedure and does not return the new folder_id
set new_folder_id [db_string get_new_folder_id ""]
# update all child items revisions to live revision
db_dml update_child_revisions ""
} on_error {
set err_p 1
}
if { $err_p } {
return [list 500]
}
tdav::copy_props $uri $target_uri
return $response
}
ad_proc oacs_dav::impl::content_folder::move {} {
MOVE DAV method for generic content folder
} {
set package_id [oacs_dav::conn package_id]
set user_id [oacs_dav::conn user_id]
set peer_addr [oacs_dav::conn peeraddr]
set uri [oacs_dav::conn uri]
set target_uri [oacs_dav::conn oacs_destination]
set move_folder_id [oacs_dav::conn item_id]
set item_name [oacs_dav::conn item_name]
set new_parent_folder_id [oacs_dav::conn dest_parent_id]
set cur_parent_folder_id [oacs_dav::item_parent_folder_id $uri]
set turlv [split [string trimright $target_uri "/"] "/"]
set new_name [lindex $turlv end]
set overwrite [oacs_dav::conn overwrite]
if {![string equal "unlocked" [tdav::check_lock $uri]]} {
return [list 423]
}
if {[empty_string_p $new_parent_folder_id]} {
set response [list 412]
return $response
}
set dest_item_id [db_string get_dest_id "" -default ""]
ns_log debug "\n@DAV@@ folder move new_name $new_name dest_id $dest_item_id new_folder_id $new_parent_folder_id \n"
if {![empty_string_p $dest_item_id]} {
if {![string equal -nocase $overwrite "T"]} {
return [list 412]
} elseif {![permission::permission_p \
-object_id $dest_item_id \
-party_id $user_id \
-privilege "write"]} {
ns_returnunauthorized
}
# according to the spec move with overwrite means
# delete then move
if {![string equal "unlocked" [tdav::check_lock $target_uri]]} {
return [list 423]
}
# TODO check if we have permission over everything inside
set children_permission_p [oacs_dav::children_have_permission_p -item_id $move_folder_id -user_id $user_id -privilege "delete"]
if {!$children_permission_p} {
return [list 409]
}
db_exec_plsql delete_for_move ""
set response [list 204]
ns_log debug "\n ----- \n CONTENT_FOLDER::MOVE OVERWRITING RETURNING 204 \n ----- \n"
} else {
set response [list 201]
}
# don't let anyone move root DAV folders in the
# dav_site_node_folder_map
if {![string equal [db_string site_node_folder ""] 0]} {
return [list 403]
}
set err_p 0
db_transaction {
if {![string equal $cur_parent_folder_id $new_parent_folder_id]} {
ns_log debug "\n@@DAV@@ move folder $move_folder_id"
db_exec_plsql move_folder ""
# change label if name is different
if {![string equal $new_name $item_name]} {
db_dml update_label ""
}
} elseif {![empty_string_p $new_name]} {
ns_log debug "\n@@DAV@@ move folder rename $move_folder_id to $new_name"
db_exec_plsql rename_folder ""
}
} on_error {
set err_p 1
}
if { $err_p } {
return [list 500]
}
tdav::copy_props $uri $target_uri
tdav::delete_props $uri
tdav::remove_lock $uri
return $response
}
ad_proc oacs_dav::impl::content_folder::delete {} {
DELETE DAV method for generic content folder
} {
set package_id [oacs_dav::conn package_id]
set user_id [oacs_dav::conn user_id]
set peer_addr [oacs_dav::conn peeraddr]
set item_id [oacs_dav::conn item_id]
set uri [oacs_dav::conn uri]
if {![string equal "unlocked" [tdav::check_lock $uri]]} {
return [list 423]
}
set children_permission_p [oacs_dav::children_have_permission_p -item_id $item_id -user_id $user_id -privilege "delete"]
if {!$children_permission_p} {
return [list 403]
}
if {[catch {db_exec_plsql delete_folder ""} errmsg]} {
ns_log error "content_folder::delete $errmsg"
set response [list 500]
# ns_log debug "\nCONTEXT IDS [db_list get_ids "select object_id from acs_objects where context_id=:item_id"]"
} else {
set response [list 204]
tdav::delete_props $uri
tdav::remove_lock $uri
}
return $response
}
ad_proc oacs_dav::impl::content_folder::propfind {} {
PROPFIND DAV method for generic content folder
} {
set user_id [oacs_dav::conn user_id]
set depth [oacs_dav::conn depth]
set encoded_uri [list]
foreach fragment [split [ad_conn url] "/"] {
lappend encoded_uri [oacs_dav::urlencode $fragment]
}
set folder_uri "[ad_conn location][join $encoded_uri "/"]"
# this is wacky, but MS Web Folders usually (but not always)
# requests a collection without a trailing slash
# if you return a propfind with the href for the collection
# with a trailing slash, sometimes (but not always) it will
# get confused and show the collection as a member of itself
regsub {/$} $folder_uri {} folder_uri
if {[empty_string_p $depth]} {
set depth 0
}
set prop_req [oacs_dav::conn prop_req]
set folder_id [oacs_dav::conn item_id]
# append the properties into response
set all_properties [list]
# hack to get the OS time zone to tack on the end of oracle timestamps
# until we stop supporting oracle 8i
set os_time_zone [clock format [clock seconds] -format %Z]
db_foreach get_properties "" {
set name $name
set etag "1f9a-400-3948d0f5"
set properties [list]
# is "D" the namespace??
lappend properties [list "D" "getcontentlength"] $content_length
# ns_log debug "\nDAVEB item_id $item_id folder_id $folder_id $item_uri"
if {$item_id == $folder_id} {
set item_uri "/"
} else {
set encoded_uri [list]
foreach fragment [split $item_uri "/"] {
lappend encoded_uri [oacs_dav::urlencode $fragment]
# ns_log debug "\npropfind: fragment \"$fragment\" encoded_uri \"$encoded_uri\" "
}
set item_uri "/[join $encoded_uri "/"]"
}
lappend properties [list "D" "getcontenttype"] $mime_type
# where do we get an etag from?
lappend properties [list "D" "getetag"] $etag
lappend properties [list "D" "getlastmodified"] $last_modified
lappend properties [list "D" "creationdate"] $creation_date
if {$collection_p} {
lappend properties [list "D" "resourcetype"] "D:collection"
} else {
lappend properties [list "D" "resourcetype"] ""
}
# according to Todd's example
# resourcetype for a folder(collection) is <D:collection/>
# and getcontenttype is */*
foreach i [tdav::get_user_props ${folder_uri}${item_uri} $depth $prop_req] {
lappend properties $i
}
lappend all_properties [list ${folder_uri}${item_uri} $collection_p $properties]
}
set response [list 207 $all_properties]
return $response
}
ad_proc oacs_dav::impl::content_folder::proppatch {} {
PROPPATCH DAV method for generic content folder
user-properties are stored in the filesystem by tDAV
this doesn't do anything until tDAV allows storage of
user properties in the database
} {
set uri [oacs_dav::conn uri]
if {![string equal "unlocked" [tdav::check_lock $uri]]} {
return [list 423]
}
set response [tdav::update_user_props $uri [oacs_dav::conn prop_req]]
return [list 207 $response]
}
ad_proc oacs_dav::impl::content_folder::lock {} {
LOCK DAV method for generic content folder
} {
set uri [oacs_dav::conn uri]
set owner [oacs_dav::conn lock_owner]
set scope [oacs_dav::conn lock_scope]
set type [oacs_dav::conn lock_type]
if {![string equal "unlocked" [tdav::check_lock $uri]]} {
set ret_code 423
set response [list $ret_code]
} else {
set depth [tdav::conn depth]
set timeout [tdav::conn lock_timeout]
if {[empty_string_p $timeout]} {
set timeout [parameter::get_from_package_key -parameter "DefaultLockTimeout" -package_key "oacs-dav" -default "300"]
}
set token [tdav::set_lock $uri $depth $type $scope $owner $timeout]
set ret_code 200
set response [list $ret_code [list depth $depth token $token timeout $timeout owner $owner scope $scope type $type]]
}
return $response
}
ad_proc oacs_dav::impl::content_folder::unlock {} {
UNLOCK DAV method for generic content folder
} {
set uri [oacs_dav::conn uri]
if {![string equal unlocked [tdav::check_lock_for_unlock $uri]]} {
set ret_code 423
set body "Resource is locked."
} else {
ns_log debug "\ntdav::check_lock_for_unlock = [tdav::check_lock_for_unlock $uri]]"
tdav::remove_lock $uri
set ret_code 204
set body ""
}
return [list $ret_code $body]
}
namespace eval oacs_dav::impl::content_revision {}
ad_proc oacs_dav::impl::content_revision::get {} {
GET DAV method for generic content revision
@author Dave Bauer
@param uri
} {
set item_id [oacs_dav::conn item_id]
#should return the DAV content for the content item
#for now we always get live/latest revision
cr_write_content -item_id $item_id
}
ad_proc oacs_dav::impl::content_revision::head {} {
GET DAV method for generic content revision
@author Dave Bauer
@param uri
} {
set item_id [oacs_dav::conn item_id]
# cr_write_content works correctly for HEAD requests
# with filesystem storage, it sends out the content
# on lob storage. that needs to be fixed.
cr_write_content -item_id $item_id
}
ad_proc oacs_dav::impl::content_revision::put {} {
PUT DAV method for generic content revision
@author Dave Bauer
} {
set user_id [oacs_dav::conn user_id]
set item_id [oacs_dav::conn item_id]
set root_folder_id [oacs_dav::conn folder_id]
set uri [oacs_dav::conn uri]
if {![string equal "unlocked" [tdav::check_lock $uri]]} {
return [list 423]
}
set tmp_filename [oacs_dav::conn tmpfile]
set tmp_size [file size $tmp_filename]
# authenticate that user has write privilege
# we need to calculate parent_id from the URI
# it might not be the root DAV folder for the package
# check for folder or not
set urlv [split [oacs_dav::conn uri] "/"]
set name [oacs_dav::conn item_name]
set parent_id [oacs_dav::item_parent_folder_id $uri]
if {[empty_string_p $parent_id]} {
set response [list 409]
return $response
}
# create new item if necessary
db_transaction {
set mime_type [cr_filename_to_mime_type $name]
if {[empty_string_p $item_id]} {
# this won't really work very nicely if we support
# abstract url type names... maybe chop off the extension
# when we name the object?
set revision_id [cr_import_content \
-storage_type file \
$parent_id \
$tmp_filename \
$tmp_size \
$mime_type \
$name]
if {[file exists [tdav::get_lock_file $uri]]} {
# if there is a null lock use 204
set response [list 204]
} else {
set response [list 201]
}
} else {
set revision_id [cr_import_content \
-item_id $item_id \
-storage_type file \
$parent_id \
$tmp_filename \
$tmp_size \
$mime_type \
$name]
set response [list 204]
}
db_dml set_live_revision ""
} on_error {
set response [list 500]
ns_log error "oacs_dav::impl::content_revision::put: $errmsg"
}
file delete $tmp_filename
# at least we need to return the http_status
return $response
}
ad_proc oacs_dav::impl::content_revision::propfind {} {
PROPFIND DAV method for generic content revision
@author Dave Bauer
} {
set user_id [oacs_dav::conn user_id]
set item_id [oacs_dav::conn item_id]
set folder_id [oacs_dav::conn folder_id]
set uri [oacs_dav::conn uri]
set depth [oacs_dav::conn depth]
set prop_req [oacs_dav::conn prop_req]
set os_time_zone [clock format [clock seconds] -format %Z]
# find the values
db_1row get_properties ""
set etag "1f9a-400-3948d0f5"
set properties [list]
# is "D" the namespace??
lappend properties [list "D" "getcontentlength"] $content_length
# lappend properties [list "D" "uri"] $item_uri
lappend properties [list "D" "getcontenttype"] $mime_type
# where do we get an etag from?
lappend properties [list "D" "getetag"] $etag
lappend properties [list "D" "getlastmodified"] $last_modified
lappend properties [list "D" "creationdate"] $creation_date
lappend properties [list "D" "resourcetype"] ""
foreach i [tdav::get_user_props ${uri} $depth $prop_req] {
lappend properties $i
}
set response [list 207 [list [list $uri "" $properties]]]
return $response
}
ad_proc oacs_dav::impl::content_revision::proppatch {} {
PROPPATCH DAV method for generic content revision
We store all user properties in the filesystem using tDAV for now
So this is just a stub until we can get everything stored in the
database.
@author Dave Bauer
} {
# get the properties out of the list
set uri [oacs_dav::conn uri]
if {![string equal "unlocked" [tdav::check_lock $uri]]} {
return [list 423]
}
# set the values
set response [tdav::update_user_props $uri [oacs_dav::conn prop_req]]
# return results
return [list 207 $response]
}
ad_proc oacs_dav::impl::content_revision::delete {} {
DELETE DAV method for generic content revision
@author Dave Bauer
} {
set package_id [oacs_dav::conn package_id]
set user_id [oacs_dav::conn user_id]
set peer_addr [oacs_dav::conn peeraddr]
set item_id [oacs_dav::conn item_id]
set uri [oacs_dav::conn uri]
if {![string equal "unlocked" [tdav::check_lock $uri]]} {
return [list 423]
}
if {[catch {db_exec_plsql delete_item ""} errmsg]} {
set response [list 500]
} else {
set response [list 204]
tdav::delete_props $uri
tdav::remove_lock $uri
}
return $response
}
ad_proc oacs_dav::impl::content_revision::copy {} {
COPY DAV method for generic content revision
@author Dave Bauer
} {
set package_id [oacs_dav::conn package_id]
set user_id [oacs_dav::conn user_id]
set peer_addr [oacs_dav::conn peeraddr]
set uri [oacs_dav::conn uri]
# check for write permission on target folder
set target_uri [oacs_dav::conn oacs_destination]
set copy_item_id [oacs_dav::conn item_id]
set overwrite [oacs_dav::conn overwrite]
set turlv [split $target_uri "/"]
set new_name [lindex $turlv end]
set new_parent_folder_id [oacs_dav::conn dest_parent_id]
if {[empty_string_p $new_parent_folder_id]} {
return [list 409]
}
set dest_item_id [db_string get_dest_id "" -default ""]
ns_log debug "\nDAV Revision Copy dest $target_uri parent_id $new_parent_folder_id"
if {![empty_string_p $dest_item_id]} {
ns_log debug "\n ----- \n DAV Revision Copy Folder Exists item_id $dest_item_id overwrite $overwrite \n ----- \n"
if {![string equal -nocase $overwrite "T"]} {
return [list 412]
} elseif {![permission::permission_p \
-object_id $dest_item_id \
-party_id $user_id \
-privilege "write"]} {
ns_returnunauthorized
}
# according to the spec copy with overwrite means
# delete then copy
ns_log debug "\noacs_dav::revision::copy checking for lock on target"
if {![string equal "unlocked" [tdav::check_lock $target_uri]]} {
return [list 423]
}
db_exec_plsql delete_for_copy ""
set response [list 204]
ns_log debug "\n ----- \n CONTENT_REVISION::COPY OVERWRITING RETURNING 204 \n ----- \n"
} else {
set response [list 201]
}
set err_p 0
db_transaction {
set item_id [db_exec_plsql copy_item ""]
db_dml set_live_revision ""
} on_error {
set err_p 1
}
if { $err_p } {
return [list 500]
}
tdav::copy_props $uri $target_uri
return $response
}
ad_proc oacs_dav::impl::content_revision::move {} {
MOVE DAV method for generic content revision
@author Dave Bauer
} {
set package_id [oacs_dav::conn package_id]
set user_id [oacs_dav::conn user_id]
set peer_addr [oacs_dav::conn peeraddr]
set item_id [oacs_dav::conn item_id]
set item_name [oacs_dav::conn item_name]
set uri [oacs_dav::conn uri]
set target_uri [oacs_dav::conn oacs_destination]
set cur_parent_folder_id [oacs_dav::conn folder_id]
set new_parent_folder_id [oacs_dav::conn dest_parent_id]
set turlv [split $target_uri "/"]
set new_name [lindex $turlv end]
set overwrite [oacs_dav::conn overwrite]
if {[empty_string_p $new_parent_folder_id]} {
return [list 409]
}
if {![string equal "unlocked" [tdav::check_lock $uri]]} {
return [list 423]
}
ns_log debug "\nDAV Revision move dest $target_uri parent_id $new_parent_folder_id"
set dest_item_id [db_string get_dest_id "" -default ""]
if {![empty_string_p $dest_item_id]} {
ns_log debug "\n ----- \n DAV Revision move Folder Exists item_id $dest_item_id overwrite $overwrite \n ----- \n"
if {![string equal -nocase $overwrite "T"]} {
return [list 412]
} elseif {![permission::permission_p \
-object_id $dest_item_id \
-party_id $user_id \
-privilege "write"]} {
return [list 401]
}
if {![string equal "unlocked" [tdav::check_lock $target_uri]]} {
return [list 423]
}
db_exec_plsql delete_for_move ""
set response [list 204]
ns_log debug "\n ----- \n CONTENT_REVISION::MOVE OVERWRITING RETURNING 204 \n ----- \n"
} else {
set response [list 201]
}
set err_p 0
db_transaction {
if {![string equal $cur_parent_folder_id $new_parent_folder_id]} {
db_exec_plsql move_item ""
} elseif {![empty_string_p $new_name] } {
db_exec_plsql rename_item ""
}
if {![string equal $item_name $new_name]} {
db_dml update_title ""
}
} on_error {
set err_p 1
}
if { $err_p } {
return [list 500]
}
tdav::copy_props $uri $target_uri
tdav::delete_props $uri
tdav::remove_lock $uri
return $response
}
ad_proc oacs_dav::impl::content_revision::mkcol {} {
MKCOL DAV method for generic content revision
@author Dave Bauer
} {
# not allowed to create a collection inside a resource
# return some sort of error
set response [list 405]
return $response
}
ad_proc oacs_dav::impl::content_revision::lock {} {
LOCK DAV method for generic content revision
} {
set uri [oacs_dav::conn uri]
set owner [oacs_dav::conn lock_owner]
set scope [oacs_dav::conn lock_scope]
set type [oacs_dav::conn lock_type]
if {![string equal "unlocked" [tdav::check_lock $uri]]} {
set ret_code 423
set response [list $ret_code]
} else {
set depth [tdav::conn depth]
set timeout [tdav::conn lock_timeout]
if {[empty_string_p $timeout]} {
set timeout 300
}
set token [tdav::set_lock $uri $depth $type $scope $owner $timeout]
set ret_code 200
set response [list $ret_code [list depth $depth token $token timeout $timeout owner $owner scope $scope type $type]]
}
return $response
}
ad_proc oacs_dav::impl::content_revision::unlock {} {
UNLOCK DAV method for generic content revision
} {
set uri [oacs_dav::conn uri]
if {![string equal unlocked [tdav::check_lock_for_unlock $uri]]} {
set ret_code 423
set body "Resource is locked."
} else {
ns_log debug "\ntdav::check_lock_for_unlock = [tdav::check_lock_for_unlock $uri]]"
tdav::remove_lock $uri
set ret_code 204
set body ""
}
return [list $ret_code $body]
}
<?xml version="1.0"?>
<queryset>
<fullquery name="oacs_dav::folder_enabled.enabled_p">
<querytext>
select enabled_p
from dav_site_node_folder_map
where folder_id=:folder_id
</querytext>
</fullquery>
<fullquery name="oacs_dav::register_folder.add_folder">
<querytext>
insert into dav_site_node_folder_map
(node_id, folder_id, enabled_p)
values
(:node_id, :folder_id, :enabled_p)
</querytext>
</fullquery>
<fullquery name="oacs_dav::unregister_folder.remove_folder">
<querytext>
delete from dav_site_node_folder_map
where folder_id=:folder_id
and node_id=:node_id
</querytext>
</fullquery>
<fullquery name="oacs_dav::request_folder_id.get_folder_id">
<querytext>
select folder_id from dav_site_node_folder_map
where node_id=:node_id and enabled_p = 't'
</querytext>
</fullquery>
<fullquery name="oacs_dav::handle_request.get_content_type">
<querytext>
select content_type from cr_items where item_id=:item_id
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_revision::put.set_live_revision">
<querytext>
update cr_items set live_revision=:revision_id
where item_id=(select item_id from cr_revisions
where revision_id=:revision_id)
</querytext>
</fullquery>
<fullquery
name="oacs_dav::impl::content_folder::move.site_node_folder">
<querytext>
select count(*) from dav_site_node_folder_map
where folder_id=:move_folder_id
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_folder::move.update_label">
<querytext>
update cr_folders
set label = :new_name
where folder_id=:move_folder_id
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_folder::copy.get_new_folder_id">
<querytext>
select item_id
from cr_items
where name = :new_name
and parent_id = :new_parent_folder_id
</querytext>
</fullquery>
<fullquery
name="oacs_dav::impl::content_revision::copy.set_live_revision">
<querytext>
update cr_items set live_revision=latest_revision
where item_id=:item_id
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_revision::move.update_title">
<querytext>
update cr_revisions
set title = :new_name
where revision_id = (select latest_revision from cr_items
where item_id=:item_id)
</querytext>
</fullquery>
<fullquery name="oacs_dav::children_have_permission_p.revision_perms">
<querytext>
select count(*)
from cr_revisions
where item_id = :item_id
and not exists (select 1
from acs_object_party_privilege_map m
where m.object_id = revision_id
and m.party_id = :user_id
and m.privilege = 'delete')
</querytext>
</fullquery>
<fullquery name="oacs_dav::impl::content_folder::copy.update_child_revisions">
<querytext>
update cr_items
set live_revision=latest_revision
where exists (
select 1
from
(select ci1.item_id as child_item_id
from cr_items ci1, cr_items ci2
where ci2.item_id=:new_folder_id
and ci1.tree_sortkey
between ci2.tree_sortkey and tree_right(ci2.tree_sortkey)
) children
where item_id=child_item_id
)
</querytext>
</fullquery>
</queryset>
#
# tDAV.tcl
#
# Copyright 2003 Musea Technologies
#
# http://www.museatech.net
#
# $Id
#
# bugs to:
# toddg@tdav.museatech.net
#
# Authors: Todd Gillespie
# Dave Bauer
#
# Based upon sources from:
#
# webdav.tcl
#
# A WebDAV implementation for AOLserver 3.x.
#
# Copyright (c) 2000-2001 Panoptic Computer Network.
# All rights reserved.
#
# http://www.panoptic.com/
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# ------------------------------------------------------------
# Silly workaround so that AOLserver can find scripts via "package require".
# set tcl_library [file join $tcl_pkgPath tcl${tcl_version}]
# source [file join $tcl_library init.tcl]
# ------------------------------------------------------------
package require tdom
namespace eval tdav {}
# workaround if not installed in OACS
# tdav::filter_webdav_options
#
# Handles OPTIONS HTTP requests
#
# Arguments:
# none
#
# Results:
# returns an HTTP response containing WebDAV options supported
#
# TODO Make this smart to return options based on URI
# We still need to pretend that the site root supports DAV
# methods or some clients get confused.
proc tdav::filter_webdav_options {args} {
set dav_level {1,2}
ns_set put [ns_conn outputheaders] DAV $dav_level
# The allowed webdav options for the share that the requested
# URL belongs to.
foreach {uri options} [nsv_array get tdav_options] {
if {[regexp $uri [ns_conn url]]} {
ns_set put [ns_conn outputheaders] Allow [join $options {, }]
break
}
}
# This tells MSFT products to skip looking for FrontPage extensions.
ns_set put [ns_conn outputheaders] MS-Author-Via DAV
ns_return 200 text/plain {}
return filter_return
}
# ------------------------------------------------------------
# first check XML validity
# add PROPPATCH
# split into prop error function?
# get body
proc tdav::xml_valid_p {xml_doc} {
# TODO use tnc with tDOM to vaildate the xml request
return 1
}
# tdav::read_xml
#
# reads xml from connection
#
# Arguments:
# none
#
# Results:
#
# returns xml text of request
proc tdav::read_xml {} {
set fp ""
while {$fp == ""} {
set tmpfile [ns_tmpnam]
set fp [ns_openexcl $tmpfile]
}
#fconfigure $fp -translation binary -encoding binary
# fconfigure $fp -encoding utf-8
ns_conncptofp $fp
seek $fp 0
set xml [read $fp]
close $fp
ns_unlink -nocomplain $tmpfile
ns_log debug "\n-----tdav::read_xml XML = -----\n $xml \n ----- end ----- \n "
return $xml
}
# tdav::dbm_write_list
#
# helper fxns for dbm-like props
# Writes a list to a properties file
#
# Arguments:
# uri URI of the request being handled
# list properties formatted in a Tcl list as
# propertyname value
#
# Results:
# file written including contents of list
proc tdav::dbm_write_list {uri list} {
set file [tdav::get_prop_file $uri]
if {[catch {set f [open $file w]} err]} {
# probably no parent dir, create it:
file mkdir [file dirname $file]
# open again:
set f [open $file w]
}
fconfigure $f -encoding utf-8
puts $f $list
close $f
}
# tdav::get_prop_file
#
# Get the filename that contains user properties.
#
# Arguments:
# uri URI to get properties filename for
#
# Results:
# Returns the filename containing user properties.
proc tdav::get_prop_file {uri} {
# just in case. I hate that 'file join' fails on this
regsub {^/} $uri {} uri
# log this for failed config section
set name [ns_config "ns/server/[ns_info server]/tdav" propdir]
if {[string equal "" $name]} {
set name [file join [ns_info pageroot] "../propdir/${uri}"]
} else {
set name [file join $name $uri]
}
# catch uncreated parent dirs here:
if {![file exists [file dirname $name]]} {
# no parent dir, create it:
file mkdir [file dirname $name]
# safe for public consumption?
}
return "${name}.prop"
}
# tdav::get_lock_file
#
# Get the filename of the lock file
#
# Arguments:
# uri URI to get the lock filename for
#
# Results:
# Returns the filename containing the lock information for URI
proc tdav::get_lock_file {uri} {
# just in case. I hate that 'file join' fails on this
regsub {^/} $uri {} uri
# log this for failed config section
set name [ns_config "ns/server/[ns_info server]/tdav" lockdir]
if {[string equal "" $name]} {
set name [file join [ns_info pageroot] "../lockdir/${uri}"]
} else {
set name [file join $name $uri]
}
if {![file exists [file dirname $name]]} {
# no parent dir, create it:
file mkdir [file dirname $name]
# safe for public consumption?
}
return "${name}.lock"
}
# tdav::delete_props
#
# Delete the properties file for a URI
#
# Arguments:
# uri URI of properties file to delete
#
# Results:
# File containing user properties for URI is deleted
proc tdav::delete_props {uri} {
set entry [tdav::get_prop_file $uri]
catch {[file delete -force $entry]} err
return err
}
# tdav::move_props
#
# Move the properties file for a URI
#
# Arguments:
# uri Original URI
# newuri New URI after move
#
# Results:
# Properties file is moved under the properties directory
# to the relative location for newuri
proc tdav::move_props {uri newuri} {
set entry [tdav::get_prop_file $uri]
set dest [tdav::get_prop_file $newuri]
catch {[file copy -force $entry $dest]}
}
# tdav::copy_props
#
# Copy properties file for a URI to another URI
#
# Arguments:
# uri source URI to copy
# newuri destination URI of copy
#
# Results:
# Contents of properties file for URI is copied
# under the properties directory to the relative
# location corresponding to newuri.
proc tdav::copy_props {uri newuri} {
set entry [tdav::get_prop_file $uri]
set dest [tdav::get_prop_file $newuri]
catch {[file copy -force $entry $dest]}
}
proc tdav::write_lock {uri list} {
set f [open [tdav::get_lock_file $uri] w]
puts $f $list
close $f
}
proc tdav::dbm_read_list {uri} {
set f [open [tdav::get_prop_file $uri] {CREAT RDONLY}]
fconfigure $f -encoding utf-8
set s [read $f]
close $f
return $s
}
# tdav::read_lock
#
# Read lock file for a URI
#
# Arguments:
# uri URI to retrieve lock
#
# Results:
# Returns the contents of the lock file. Contents will
# be evaluated before being returned.
proc tdav::read_lock {uri} {
set f [open [tdav::get_lock_file $uri] {CREAT RDONLY}]
set s [read $f]
set e "list ${s}"
set l [eval $e]
close $f
return $l
}
# tdav::remove_lock
#
# Delete lock file, effectively also removing the lock
#
# Arguments:
# uri URI to remove lock from
#
# Results:
# Lock file for URI is deleted
proc tdav::remove_lock {uri} {
ns_unlink -nocomplain [tdav::get_lock_file $uri]
}
# tdav::dbm_write_array
#
# Write array into user properties file
#
# UNUSED
proc tdav::dbm_write_array {uri arr} {
# extract list from array
tdav::dbm_write_list($uri,[array get arr])
# throw errors
}
# tdav::lock_timeout_left
#
# timeout
# total length of timeout set in seconds
#
# locktime
# time lock was created in any format clock scan can accept
#
proc tdav::lock_timeout_left { timeout locktime } {
set locktime [clock scan $locktime]
set lockexpiretime [clock scan "$timeout seconds" -base $locktime]
set timeout_left [expr $lockexpiretime - [clock seconds]]
if {$timeout_left < 0} {
set timeout_left 0
}
return $timeout_left
}
# tdav::check_lock
#
# Compare existing lock to lock token provided
# by the client
#
# Arguments:
# uri URI of request
#
# Results:
# If the lock token in the Lock-Token header matches
# an existing lock return "unlocked". Processing of
# transction from the caller should continure. If
# the lock doesn't match return "filter_return". Generally
# this means either no Lock-Token header was provided or
# the Lock-Token header does not match the existing lock
# on URI. In this case the caller should return an HTTP
# status of 423 or otherwise treat the file as locked.
proc tdav::check_lock {uri} {
regsub {^/} $uri {} uri
# if lock exists, work. if not, just return.
if {[file exists [tdav::get_lock_file $uri]]} {
set lockinfo [tdav::read_lock $uri]
# check if lock is expired
if {[tdav::lock_timeout_left [lindex $lockinfo 4] [lindex $lockinfo 6]] == 0 } {
tdav::remove_lock $uri
return "unlocked"
}
set hdr [ns_set iget [ns_conn headers] If]
# the If header exists, work, otherwise 423
if {[info exists hdr] && [string length $hdr]} {
set token ""
# add ? in the token re in case there is a conditional ()
# in the header
regexp {(<https?://[^/]+([^>]+)>\s+)?\(<([^>]+)>\)} $hdr nil maybe hdr_uri token
set ftk [lindex $lockinfo 3]
if {![info exists token] || ![string equal $token $ftk]} {
ns_log Debug "tdav::check_lock: token mismatch $ftk expected hdr: $hdr token: $token"
ns_return 423 {text/plain} {}
return filter_return
}
} else {
ns_log Debug "tdav::check_lock: no \"If\" header found for request of $uri"
ns_return 423 {text/plain} {}
return filter_return
}
# also check for uri == hdr_uri
}
return unlocked
}
# tdav::check_lock_for_unlock
#
# Compare existing lock with client provided lock token.
#
# Arguments:
# uri URI of the request
#
# Results:
# If the client provided lock token matches the existing lock the
# lock is removed and "unlocked" is returned. Otherwise no action
# is taken on the lock and "filter_return" is returned.
proc tdav::check_lock_for_unlock {uri} {
regsub {^/} $uri {} uri
# if lock exists, work. if not, just return.
if {[file exists [tdav::get_lock_file $uri]]} {
set hdr [ns_set iget [ns_conn headers] {Lock-Token}]
# the If header exists, work, otherwise 423
if {[info exists hdr] && [string length $hdr]} {
regexp {<([^>]+)>} $hdr nil token
set ftk [lindex [tdav::read_lock $uri] 3]
if {[info exists token] && [string equal $token $ftk]} {
# it's good, the tokens match. carry on.
} else {
return filter_return
}
} else {
return filter_return
}
# also check for uri == hdr_uri
}
return unlocked
}
# tdav::get_fs_props
#
# Generate a list of filesystem properties
#
# Arguments:
# none
#
# Results:
# Returns a list of standard DAV properties for
# the request uri as ns_conn url
# The list is formatted as
# {namespace propertyname} value pairs. The results
# should be evaluated in the caller.
proc tdav::get_fs_props {} {
# global fs_props
set fs_props [list]
# lappend fs_props [list ns0 supportlock] {subst {"<none/>"}}
lappend fs_props [list ns0 getcontenttype] {subst {[ns_guesstype $filename]}}
lappend fs_props [list D getcontentlength] {subst {[file size $entry]}}
lappend fs_props [list D creationdate] {subst {[clock format $file_stat(mtime) -format "%Y-%m-%dT%H:%M:%SZ" -gmt 1]}}
lappend fs_props [list D getlastmodified] {subst {[clock format $file_stat(mtime) -format "%a, %d %b %Y %H:%M:%S %Z" -gmt 1]}}
lappend fs_props [list D getetag] {subst {"1f9a-400-3948d0f5"}}
lappend fs_props [list D resourcetype] {if {[file isdirectory $entry]} {
subst {D:collection}
} else {
subst {[ns_guesstype $filename]}
}}
return $fs_props
}
# tdav::extract_propertyupdate_remove
#
# I am guessing this should return a list of properties
# to be removed. It isn't used anywhere.
proc tdav::extract_propertyupdate_remove {proplist} {
# ht
# ACTION
foreach c $proplist {
# extraneous, then name
set p [[$c childNodes] childNodes]
set name [$p nodeName]
# DATA:
set ht($name) [[$p childNodes] nodeValue]
}
return $ht
}
# tdav::extract_propertyupdate_set
#
# I am guessing this should return a list of properties
# to be removed. It isn't used anywhere.
proc tdav::extract_propertyupdate_set {proplist} {
# ht
# ACTION
foreach c $proplist {
# extraneous, then name
set p [[$c childNodes] childNodes]
set name [$p nodeName]
# DATA:
set ht($name) [[$p childNodes] nodeValue]
}
return $ht
}
# tdav::filter_webdav_proppatch
#
# Prepare request data for PROPPATCH method
#
# Arguments:
# none
#
# Results:
# Parses XML body and puts the formatted result in tdav_conn(prop_req)
# global variable. Accessed from tdav::conn prop_req command.
# Sets tdav_conn(depth) from HTTP Depth header
proc tdav::filter_webdav_proppatch {args} {
set depth [tdav::conn -set depth [ns_set iget [ns_conn headers] Depth]]
set xml [tdav::read_xml]
if {[catch {dom parse $xml} xd]} {
# xml body is not well formed
ns_returnbadrequest
return filter_return
}
set setl [$xd getElementsByTagName "*set"]
set rml [$xd getElementsByTagName "*remove"]
set prop_req [list]
foreach node $rml {
set p [[$node childNodes] childNodes]
# we use localname because we always resolve the URI namespace
# for the tag name
set ns [$p namespaceURI]
if {[string equal "" $ns]} {
set name [$p nodeName]
} else {
set name [$p localName]
}
if {[catch {set value [[$p childNodes] nodeValue]}]} {
set value ""
}
lappend prop_req remove [list [list $ns $name] $value]
}
foreach node $setl {
set p [[$node childNodes] childNodes]
# we use localname because we always resolve the URI namespace
# for the tag name
set ns [$p namespaceURI]
if {[string equal "" $ns]} {
set name [$p nodeName]
} else {
set name [$p localName]
}
if {[catch {set value [[$p childNodes] nodeValue]}]} {
set value ""
}
lappend prop_req set [list [list $ns $name] $value]
}
tdav::conn -set prop_req $prop_req
return filter_ok
}
# tdav::webdav_proppatch
#
# Handle proppatch method for tDAV filesystem storage
#
# Arguments:
# none
#
# Results:
# Attempts to set or unset properties based on the request
# contained in tdav_conn(prop_req).
#
# Returns a list containing the HTTP status code and
# the status of each property set/unset. The status is a list
# of HTTP status code and text for each property.
proc tdav::webdav_proppatch {} {
set uri [ns_conn url]
regsub {^/} $uri {} uri
set filename [file join [ns_info pageroot] $uri]
set body ""
set ret_code 200
if {![file exists $filename]} {
set ret_code 404
} else {
if {![string equal unlocked [tdav::check_lock $uri]]} {
set ret_code 423
set response "The resource is locked"
} else {
set prop_req [tdav::conn prop_req]
set response [tdav::update_user_props $uri $prop_req]
}
set ret_code 207
}
tdav::respond [list $ret_code $response]
}
# tdav::webdav_propfind
#
# Handle propfind request for tDAV filesystem storage
#
# Arguments:
# none
#
# Results:
# Returns a list of HTTP status for the request, and if sucessful a
# list of properties in the format of
# {href collection_p {properies_list}}
# where properties list is a list of pairs
# {namespace name} value.
proc tdav::webdav_propfind {} {
set props [list]
set uri [ns_conn url]
set depth [tdav::conn depth]
set prop_req [tdav::conn prop_req]
regsub {^/} $uri {} uri
regsub -all -- (\{|\}) $uri \\\\& uri
# decide on file or directory
# why doesn't tcl handle this?
# otoh, it lets us handle the notfound error here
# wait, no, this is right as long as the DAV request is correct
# so fuck it
if {$depth > 0} {
set entries [glob -nocomplain [file join [ns_info pageroot] $uri *]]
} else {
set entries [glob -nocomplain [file join [ns_info pageroot] $uri]]
}
foreach entry $entries {
set entry_props [list]
set filename [lindex [file split $entry] end]
# Tcl befuddles me:
set href [string replace $entry 1 [string length [ns_info pageroot]] ""]
file stat $entry file_stat
set collection_p [string equal "directory" $file_stat(type)]
foreach {i j} [tdav::get_fs_props] {
lappend entry_props [list [lindex $i 0] [lindex $i 1]] [eval $j]
}
foreach {i j} [tdav::get_user_props $uri $depth $prop_req] {
lappend entry_props [list [lindex $i 0] [lindex $i 1]] $j
}
lappend props [list $href $collection_p $entry_props]
}
tdav::respond [list 207 $props]
}
# tdav::get_user_props
#
# Retreive user properties from tDAV filesystem storage
#
# Arguments:
# uri URI of the request
# depth valid for collections (directories) can be 0 1 or infinity
# 0 is the directory only
# 1 is the directory and direct descendants
# infinity is all decendants, this is the default if depth
# is not specified
# prop_req should contain a list of name/value pairs of properties
# to return. Right now it is unsupported and all properties
# are always returned
#
# Results:
# returns a list of name/value pairs
proc tdav::get_user_props { uri depth prop_req } {
regsub {^/} $uri {} luri
return [tdav::dbm_read_list $luri]
}
proc tdav::update_user_props {uri prop_req} {
array set props [tdav::dbm_read_list $uri]
set status [list]
foreach {action i} $prop_req {
set k [lindex $i 0]
set value [lindex $i 1]
switch -- $action {
set {
if {[catch {set props($k) $value} err]} {
lappend status [list "HTTP/1.1 409 Conflict" $k]
} else {
lappend status [list "HTTP/1.1 200 OK" $k]
}
}
remove {
#according to WebDAV spec removing a nonexistent
# property is not an error, if it's there
# remove it, otherwise, continue.
if {[info exists props($k)]} {
unset props($k)
}
lappend status [list "HTTP/1.1 200 OK" $k]
}
}
#filter out filesystem sets
# DAVEB where is this filtering occuring?
#write the props back out to disc:
tdav::dbm_write_list $uri [array get props]
}
return $status
}
# tdav::filter_webdav_propfind
#
# Prepare incoming PROPFIND request
#
# Arguments:
# none
#
# Results:
# sets global values in tdav_conn array for
# depth, and prop_req
# prop_req is a list of lists of namespace/name pairs
proc tdav::filter_webdav_propfind {args} {
set prop_req [list]
set depth [ns_set iget [ns_conn headers] Depth]
tdav::conn -set depth $depth
set body ""
set ret_code 207
set xml [tdav::read_xml]
# test for xml req
# test for url existence
regsub {^/} [ns_conn url] {} uri
set entry [file join [ns_info pageroot] $uri]
# parse the xml body to check if its valid
if {![string equal "" $xml] && [catch {dom parse $xml} xd]} {
ns_return 400 text/plain "XML request not well-formed."
return filter_return
}
set xml_prop_list [list]
if {[info exists xd] && ![string equal "" $xd]} {
set prop [$xd getElementsByTagNameNS "DAV:" "prop"]
# if <prop> element doesn't exist we return all properties
if {![string equal "" $prop]} {
set xml_prop_list [$prop childNodes]
}
foreach node $xml_prop_list {
set ns [$node namespaceURI]
if {[string equal $ns ""]} {
set name [$node nodeName]
} else {
set name [$node localName]
}
lappend prop_req [list $ns $name]
}
}
tdav::conn -set prop_req $prop_req
# this should be the end of the filter.
return filter_ok
}
# tdav::filter_webdav_put
#
# Prepare incoming PUT request
#
# Arguments:
# none
#
# Results
# Copies content to a temporary file and sets tdav_conn(tmpfile)
proc tdav::filter_webdav_put {args} {
set tmpfile [ns_tmpnam]
set fd [open $tmpfile w+]
ns_writecontent $fd
close $fd
tdav::conn -set tmpfile $tmpfile
return filter_ok
}
# tdav::webdav_put
#
# Handle PUT for tDAV filesystem storage
#
# Arguments:
# none
#
# Results:
# If sucessful file is created under AOLserver pageroot
# that corresponds to the URI of the request.
# Calls tdav::respond with a list containing HTTP status
# and response body to return the results to the client.
proc tdav::webdav_put {} {
set uri [ns_conn url]
set uri [string trimleft $uri "/"]
set entry [file join [ns_info pageroot] $uri]
set filename [lindex [file split $entry] end]
set tmpfile [tdav::conn tmpfile]
set ret_code 500
set body ""
if {[file exists $entry]} {
if {![string equal "unlocked" [tdav::check_lock $uri]]} {
set ret_code 423
set body "Resource is locked."
} else {
file rename -force -- $tmpfile $entry
set ret_code 204
}
} else {
file rename -- $tmpfile $entry
set ret_code 201
}
tdav::respond [list $ret_code ""]
}
# tdav::filter_webdav_delete
#
# Prepare incoming DELETE request
#
# Arguments:
# none
#
# Results:
# There isn't anything to set so this doesn't do anything
# right now
proc tdav::filter_webdav_delete {args} {
# not sure there is anything we need to set here
return filter_ok
}
# tdav::webdav_delete
#
# Handle DELETE method for tDAV filesystem storage
#
# Arguments:
# none
#
# Results:
# If sucessful file corresponding to URI is removed from
# the filesystem. In addition properties and lock files
# are also removed. Calls tdav::respond to return the results
# to the client.
proc tdav::webdav_delete {} {
set uri [ns_conn url]
regsub {^/} $uri {} uri
set entry [file join [ns_info pageroot] $uri]
set filename [lindex [file split $entry] end]
set ret_code 500
set body ""
if {[file exists $entry]} {
# 423's and returns:
if {[string equal unlocked [tdav::check_lock $uri]]} {
file delete -force -- $entry
ns_unlink -nocomplain $entry
tdav::delete_props $uri
tdav::remove_lock $uri
set ret_code 204
} else {
set ret_code 423
set body "Resource is locked."
}
} else {
# file exists will fail on urls created by urlencode. do a decode here & test
# ?
set ret_code 404
}
tdav::respond [list $ret_code $body]
}
# tdav::filter_webdav_mkcol
#
# Prepares MKCOL request
#
# Arguments:
# none
#
# Results:
# This handles the invalid request with
# a content body. Otherwise it passes on to the
# registered procedure.
proc tdav::filter_webdav_mkcol {args} {
if [ns_conn contentlength] {
set ret_code 415
set html_response ""
tdav::respond [list 415]
return filter_return
}
return filter_ok
}
# tdav::webdav_mkcol
#
# Handles MKCOL method for tDAV filesystem storage
#
# Arguments:
# none
#
# Results:
# Creates a directory under the AOLserver pageroot
# corresponding to the URI. Calls tdav::respond to
# return the results to the client.
proc tdav::webdav_mkcol {} {
set uri [ns_conn url]
regsub {^/} $uri {} uri
set entry [file join [ns_info pageroot] $uri]
set filename [lindex [file split $entry] end]
regsub {/[^/]*/*$} $entry {} parent_dir
if ![file exists $parent_dir] {
set ret_code 409
} elseif ![file exists $entry] {
file mkdir $entry
file mkdir [file join [ns_info pageroot] "../props/" $uri]
set ret_code 201
} else {
set ret_code 405
}
tdav::respond [list $ret_code]
}
# ------------------------------------------------------------
proc tdav::filter_webdav_copy {args} {
set overwrite [tdav::conn -set overwrite [ns_set iget [ns_conn headers] Overwrite]]
set destination [encoding convertto utf-8 [ns_urldecode [ns_set iget [ns_conn headers] Destination]]]
regsub {https?://[^/]+/} $destination {/} dest
tdav::conn -set destination $dest
return filter_ok
}
proc tdav::webdav_copy {} {
set overwrite [tdav::conn overwrite]
set dest [tdav::conn destination]
set local_dest [ns_info pageroot]
append local_dest $dest
set newuri [string replace $local_dest 1 [string length [ns_info pageroot]] ""]
regsub {^/} $newuri {} newuri
set uri [ns_conn url]
regsub {^/} $uri {} uri
set entry [file join [ns_info pageroot] $uri]
set filename [lindex [file split $entry] end]
regsub {^/} [ns_conn url] {} uri
set entry [file join [ns_info pageroot] $uri]
if {![file exists $entry]} {
set ret_code 404
} else {
if {[file exists $local_dest]} {
if {![string equal "unlocked" [tdav::check_lock $dest]]} {
# ns_return 423 {text/plain} {Resource is locked.}
set ret_code 423
set body "Resource is locked."
} else {
if [string equal -nocase $overwrite "F"] {
set ret_code 412
} else {
set ret_code 204
file copy -force $entry $local_dest
tdav::copy_props $uri $newuri
}
}
} else {
set ret_code 201
file copy $entry $local_dest
tdav::copy_props $uri $newuri
}
}
ns_return $ret_code {text/html} {}
tdav::respond [list $ret_code]
}
# ------------------------------------------------------------
proc tdav::filter_webdav_move {args} {
set overwrite [tdav::conn -set overwrite [ns_set iget [ns_conn headers] Overwrite]]
set destination [encoding convertto utf-8 [ns_urldecode [ns_set iget [ns_conn headers] Destination]]]
regsub {https?://[^/]+/} $destination {/} dest
tdav::conn -set destination $dest
return filter_ok
}
proc tdav::webdav_move { args } {
set overwrite [tdav::conn overwrite]
set dest [tdav::conn destination]
set uri [ns_conn url]
set local_dest [ns_info pageroot]
append local_dest $dest
set newuri [string replace $local_dest 1 [string length [ns_info pageroot]] ""]
regsub {^/} $newuri {} newuri
set uri [ns_conn url]
regsub {^/} $uri {} uri
set entry [file join [ns_info pageroot] $uri]
set filename [lindex [file split $entry] end]
set ret_code 500
set body {}
if {![file exists $entry]} {
set ret_code 404
} else {
if {![string equal "unlocked" [tdav::check_lock $uri]]} {
# ns_return 423 {text/plain} {Resource is locked.}
set ret_code 423
set body "Resource is locked."
} elseif [file exists $local_dest] {
if [string equal -nocase $overwrite "F"] {
set ret_code 412
} else {
set ret_code 204
file delete -force $local_dest
file copy -force $entry $local_dest
file delete -force $entry
tdav::copy_props $uri $newuri
tdav::delete_props $uri
}
} else {
set ret_code 201
file copy $entry $local_dest
tdav::copy_props $uri $newuri
file delete -force $entry
tdav::delete_props $uri
}
}
ns_return $ret_code {text/html} $body
return filter_return
}
proc tdav::filter_webdav_lock {args} {
set ret_code 500
set body {}
set xml [tdav::read_xml]
set d [[dom parse $xml] documentElement]
set l [$d childNodes]
set scope [[[lindex $l 0] childNodes] nodeName]
set type [[[lindex $l 1] childNodes] nodeName]
if {[catch {set owner [[[lindex $l 2] childNodes] nodeValue]} err]} {
set owner ""
}
set depth [ns_set iget [ns_conn headers] Depth]
set timeout [ns_set iget [ns_conn headers] Timeout]
regsub {^Second-} $timeout {} timeout
tdav::conn -set lock_timeout $timeout
if {![string length $depth]} {
set depth 0
}
tdav::conn -set depth $depth
tdav::conn -set lock_scope $scope
tdav::conn -set lock_type $type
tdav::conn -set lock_owner $owner
set lock_token [ns_set iget [ns_conn headers] Lock-Token]
tdav::conn -set lock_token $lock_token
return filter_ok
}
proc tdav::set_lock {uri depth type scope owner {timeout ""} {locktime ""} } {
if {[string equal "" $timeout]} {
set timeout [ns_config "ns/server/[ns_info server]/tdav" "defaultlocktimeout" "300"]
}
if {[string equal "" $locktime]} {
set locktime [clock format [clock seconds] -format "%T %D"]
}
set token "opaquelocktoken:[ns_rand 2147483647]"
set lock [list $type $scope $owner $token $timeout $depth $locktime]
tdav::write_lock $uri $lock
return $token
}
proc tdav::webdav_lock {} {
set scope [tdav::conn lock_scope]
set type [tdav::conn lock_type]
set owner [tdav::conn lock_owner]
set uri [ns_conn url]
regsub {^/} $uri {} uri
set entry [file join [ns_info pageroot] $uri]
set filename [lindex [file split $entry] end]
set existing_lock_token [tdav::conn lock_token]
# if {![file exists $entry]} {
# set ret_code 404
# } else
if {![string equal "unlocked" [tdav::check_lock $uri]]} {
set ret_code 423
tdav::respond [list $ret_code]
} else {
set depth [tdav::conn depth]
set timeout [tdav::conn lock_timeout]
if {[string equal "" $timeout]} {
#probably make this a paramter?
set timeout 180
}
if {![string equal "" $existing_lock_token] && [file exists [tdav::get_lock_file $uri]} {
set old_lock [tdav::read_lock $uri]
set new_lock [list [lindex $old_lock 0] [lindex $old_lock 1] [lindex $old_lock 2] [lindex $old_lock 3] $timeout [clock format [clock seconds]]]
tdav::write_lock $uri $new_lock
} else {
set token [tdav::set_lock $uri $depth $type $scope $owner $timeout [clock format [clock seconds]]]
}
set ret_code 200
tdav::respond [list $ret_code [list depth $depth token $token timeout $timeout owner $owner scope $scope type $type]]
}
}
proc tdav::filter_webdav_unlock {args} {
set ret_code 500
set body {}
set lock_token [ns_set iget [ns_conn headers] Lock-Token]
tdav::conn -set lock_token $lock_token
return filter_ok
}
proc tdav::webdav_unlock {} {
set uri [ns_conn url]
regsub {^/} $uri {} uri
set entry [file join [ns_info pageroot] $uri]
set filename [lindex [file split $entry] end]
if {![file exists $entry]} {
set ret_code 404
set body {}
} elseif {![string equal unlocked [tdav::check_lock_for_unlock $uri]]} {
set ret_code 423
set body "Resource is locked."
} else {
tdav::remove_lock $uri
set ret_code 204
set body ""
}
tdav::respond [list $ret_code $body]
}
proc tdav::filter_stuff_nsperm {args} {
# should be something like "Basic 29234k3j49a"
set a [ns_set get [ns_conn headers] Authorization]
# get the second bit, the base64 encoded bit
set up [lindex [split $a " "] 1]
# after decoding, it should be user:password; get the username
set user [lindex [split [ns_uudecode $up] ":"] 0]
return filter_ok
}
proc tdav::return_unauthorized { {realm ""} } {
ns_set put [ns_conn outputheaders] "WWW-Authenticate" [subst {Basic realm="$realm"}]
ns_return 401 {text/plain} "Unauthorized\n"
}
# so this will take what's returned and if necessary format an
# XML response body
proc tdav::respond { response } {
set response_code [lindex $response 0]
if {[string equal "423" $response_code]} {
set response_body "The resource is locked"
set mime_type "text/plain"
} else {
set response_list [tdav::respond::[string tolower [ns_conn method]] $response]
set response_body [lindex $response_list 0]
set mime_type [lindex $response_list 1]
if {[string equal "" $mime_type]} {
set mime_type "text/plain"
}
if {[string match "text/xml*" $mime_type]} {
set response_body [encoding convertto utf-8 $response_body]
}
}
ns_log debug "\n ----- tdav litmus headers ----- \n [ns_set iget [ns_conn headers] X-Litmus] \n -----\n"
ns_log debug "\n ----- tdav::response response_body ----- \n $response_body \n ----- end ----- \n"
ns_return $response_code $mime_type $response_body
}
namespace eval tdav::respond {}
proc tdav::respond::delete { response } {
set body ""
set mime_type text/plain
set body [lindex $response 1]
return [list $body $mime_type]
}
proc tdav::respond::lock { response } {
array set lock [lindex $response 1]
set body [subst {<?xml version="1.0" encoding="utf-8"?>
<prop xmlns="DAV:">
<lockdiscovery>
<activelock>
<locktype><${lock(type)}/></locktype>
<lockscope><${lock(scope)}/></lockscope>
<depth>${lock(depth)}</depth>
<owner>${lock(owner)}</owner><timeout>Second-${lock(timeout)}</timeout>
<locktoken>
<href>${lock(token)}</href>
</locktoken>
</activelock>
</lockdiscovery>
</prop>}]
ns_set put [ns_conn outputheaders] "Lock-Token" "<${lock(token)}>"
set ret_code 200
return [list $body text/html]
}
proc tdav::respond::unlock { response } {
# probably should be doing something here
set body ""
return [list $body]
}
proc tdav::respond::put { response } {
return $response
}
proc tdav::respond::proppatch { response } {
set resp_code [lindex $response 0]
set href ""
set body [subst {<?xml version="1.0" encoding="utf-8" ?>
<D:multistatus xmlns:D="DAV:">
<D:response xmlns:ns0="DAV:">
<D:href>[ns_conn location]${href}</D:href>
}]
foreach res [lindex $response 1] {
set status [lindex $res 0]
set ns [lindex [lindex $res 1] 0]
set name [lindex [lindex $res 1] 1]
append body [subst {<D:propstat>
<D:prop><$name xmlns='$ns'/></D:prop>
<D:status>$status</D:status>
</D:propstat>
}]
}
append body {</D:response>
</D:multistatus>}
return [list $body {text/xml charset="utf-8"}]
}
proc tdav::respond::copy { response } {
return $response
}
proc tdav::respond::move { response } {
return $response
}
proc tdav::respond::mkcol { response } {
set body ""
switch -- [lindex $response 0] {
415 {
# set body "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">"
}
490 {
# set body "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">"
}
201 {
# set body "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">
# <html><head>
# <title>201 Created</title>
# </head><body>
# <h1>Created</h1>
# <p>Collection [ns_conn url] has been created.</p>
# <hr>
# <address></address>
# </body></html>"
}
405 {
set body "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">
<html><head>
<title>405 Method Not Allowed</title>
</head><body>
<h1>Method not allowed</h1>
</body></html>"
}
}
return [list $body text/html]
}
proc tdav::respond::propfind { response } {
# this proc requires that all properties to be returned are in the
# response lindex 1
# we don't have to check the tdav fs props or lock properties
# they should already be there
set d [dom createDocumentNS "DAV:" "D:multistatus"]
set n [$d documentElement]
$n setAttribute "xmlns:b" "urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/"
set mst_body ""
foreach res [lindex $response 1] {
set href [lindex $res 0]
set props [lindex $res 2]
set r [$d createElementNS DAV: ns0:response]
$n appendChild $r
set h [$d createElement D:href]
$h appendChild [$d createTextNode ${href}]
set propstat [$d createElement D:propstat]
set prop [$d createElement D:prop]
$r appendChild $h
$r appendChild $propstat
foreach {i j} $props {
# interestingly enough, adding the namespace here to the prop is fine
set name [lindex $i 1]
set ns [lindex $i 0]
if {![string equal "D" $ns] && ![string equal "ns0" $ns]} {
# for user properties set the namespace explicitly in
# the tag
if {![string equal "" $ns]} {
set pnode [$d createElementNS $ns $name]
} else {
set pnode [$d createElement $name]
}
} else {
set pnode [$d createElement ${ns}:${name}]
}
if {[string equal "creationdate" $name]} {
$pnode setAttribute "b:dt" "dateTime.tz"
}
if {[string equal "getlastmodified" $name]} {
$pnode setAttribute "b:dt" "dateTime.rfc1123"
}
if {[string equal "D:collection" $j]} {
$pnode appendChild [$d createElement $j]
} else {
$pnode appendChild [$d createTextNode $j]
}
$prop appendChild $pnode
}
set supportedlock [$d createElement D:supportedlock]
set lockentry [$d createElement D:lockentry]
set lockscope [$d createElement D:lockscope]
set exclusive [$d createElement D:exclusive]
set locktype [$d createElement D:locktype]
set write_type [$d createElement D:write]
$supportedlock appendChild $lockentry
$locktype appendChild $write_type
$lockscope appendChild $exclusive
$lockentry appendChild $lockscope
$lockentry appendChild $locktype
$prop appendChild $supportedlock
set lockdiscovery [$d createElement D:lockdiscovery]
regsub {https?://[^/]+/} $href {/} local_uri
if {[file exists [tdav::get_lock_file $local_uri]]} {
# check for timeout
set lockinfo [tdav::read_lock $local_uri]
set lock_timeout_left [tdav::lock_timeout_left [lindex $lockinfo 4] [lindex $lockinfo 6]]
if {$lock_timeout_left > 0} {
set activelock [$d createElement D:activelock]
set locktype [$d createElement D:locktype]
set lockscope [$d createElement D:lockscope]
set depth [$d createElement D:depth]
set owner [$d createElement D:owner]
set timeout [$d createElement D:timeout]
set locktoken [$d createElement D:locktoken]
set locktokenhref [$d createElement D:href]
$locktype appendChild [$d createElement D:[lindex $lockinfo 0]]
$lockscope appendChild [$d createElement D:[lindex $lockinfo 1]]
$depth appendChild [$d createTextNode [lindex $lockinfo 5]]
$timeout appendChild [$d createTextNode Second-$lock_timeout_left]
$owner appendChild [$d createTextNode [lindex $lockinfo 2]]
$locktokenhref appendChild [$d createTextNode [lindex $lockinfo 3]]
$locktoken appendChild $locktokenhref
$activelock appendChild $locktype
$activelock appendChild $lockscope
$activelock appendChild $depth
$activelock appendChild $timeout
$activelock appendChild $owner
$activelock appendChild $locktoken
$lockdiscovery appendChild $activelock
}
}
$prop appendChild $lockdiscovery
$propstat appendChild $prop
set status [$d createElement D:status]
set status_text [$d createTextNode "HTTP/1.1 200 OK"]
$status appendChild $status_text
$propstat appendChild $status
}
set body [$d asXML -escapeNonASCII]
set body "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n${body}"
set response [list $body {text/xml charset="utf-8"}]
return $response
}
proc tdav::conn {args} {
global tdav_conn
set flag [lindex $args 0]
if { [string index $flag 0] != "-" } {
set var $flag
set flag "-get"
} else {
set var [lindex $args 1]
}
switch -- $flag {
-set {
set value [lindex $args 2]
set tdav_conn($var) $value
return $value
}
-get {
if { [info exists tdav_conn($var)] } {
return $tdav_conn($var)
} else {
return [ns_conn $var]
}
}
}
}
proc tdav::apply_filters {{uri "/*"} {options "OPTIONS GET HEAD POST DELETE TRACE PROPFIND PROPPATCH COPY MOVE MKCOL LOCK UNLOCK"} {enable_filesystem "f"}} {
# Verify that the options are valid options. Webdav requires
# support for a minimum set of options. And offers support for a
# limited set of options. (See RFC 2518)
set required_options [list OPTIONS PROPFIND PROPPATCH MKCOL GET HEAD POST]
foreach required_option $required_options {
if {[lsearch -exact [string toupper $options] $required_option] < 0} {
ns_log error "Required option $required_option missing from tDAV options for URI '$uri'.
Required web dav options are: '$required_options'."
return
}
}
set allowed_options [list OPTIONS COPY DELETE GET HEAD MKCOL MOVE LOCK POST PROPFIND PROPPATCH PUT TRACE UNLOCK]
foreach option $options {
if {[lsearch -exact $allowed_options [string toupper $option]] < 0} {
ns_log error "Option $option is not an allowed tDAV option for URI '$uri'.
Allowed web dav options are: '$allowed_options'."
return
}
}
# Register filters for selected tDAV options. Do not register a
# filter for GET, POST or HEAD.
# change /example/* to /example* to accomodate the
# url matching for registered filters
set filter_uri "[string trimright $uri /*]*"
foreach option $options {
if {[lsearch -exact [list GET POST HEAD] $option] < 0} {
ns_log debug "tDAV registering filter for $filter_uri on $option"
ns_register_filter postauth [string toupper $option] "${filter_uri}" tdav::filter_webdav_[string tolower $option]
}
}
ns_log notice "tDAV: Registered filters on $filter_uri"
# Register procedures for selected tDAV options. Do not register a
# proc for OPTIONS, GET, POST or HEAD.
if {[string equal "true" $enable_filesystem]} {
foreach option $options {
if {[lsearch -exact [list OPTIONS GET POST HEAD] $option] < 0} {
ns_log debug "tDAV registering proc for $uri on $option"
ns_register_proc [string toupper $option] "${uri}" tdav::webdav_[string tolower $option]
}
}
ns_log notice "tDAV: Registered procedures on $uri"
} else {
ns_log notice "tDAV: Filesystem access by WebDAV disabled"
}
# Store the tDAV properties in an nsv set so that the registerd
# filters and procedures don't have to read the config file
# anymore.
nsv_set tdav_options $uri $options
}
proc tdav::add_user {user encpass} {
ns_perm adduser $user $encpass ""
}
proc tdav::setpass {user encpass} {
ns_perm setpass $user $encpass
}
proc tdav::remove_user {user} {
# no corresponding ns_perm function.
# ns_perm setpass
# ns_perm denyuser /*
# might work
}
proc tdav::allow_user {uri user} {
foreach {share_uri options} [nsv_array get tdav_options] {
if {[regexp $share_uri $uri]} {
foreach option $options {
ns_perm allowuser [string toupper $option] ${uri} $user
}
break
}
}
}
proc tdav::deny_user {uri user} {
foreach {share_uri options} [nsv_array get tdav_options] {
if {[regexp $share_uri $uri]} {
foreach option $options {
ns_perm denyuser [string toupper $option] ${uri} $user
}
break
}
}
}
proc tdav::allow_group {uri group} {
foreach {share_uri options} [nsv_array get tdav_options] {
if {[regexp $share_uri $uri]} {
foreach option $options {
ns_perm allowgroup [string toupper $option] ${uri} $group
}
break
}
}
}
proc tdav::deny_group {uri group} {
foreach {share_uri options} [nsv_array get tdav_options] {
if {[regexp $share_uri $uri]} {
foreach option $options {
ns_perm denygroup [string toupper $option] ${uri} $group
}
break
}
}
}
# and finally, install all that.
if {![nsv_exists tdav_filters_installed filters_installed]} {
nsv_set tdav_filters_installed filters_installed 1
# Uncomment the default user and password for testing. The
# application of permissions will be application specific. To use
# ns_perm your application will need to fill the ns_perm data
# every time the server is loaded and when anything changes in a
# running server. SkipLocks must be set to On in the AOLserver
# config file and ns_perm module must be loaded.
# The alternative is to define preauth filters on the WebDAV
# methods and write your own code to handle authentication. This
# is how the OpenACS implementation that uses tDAV works.
# ns_perm adduser tdav [ns_crypt tdav salt] userfield
# ns_perm adduser tdav1 [ns_crypt tdav1 salt] userfield
# ns_perm addgroup tdav tdav tdav1
set tdav_shares [ns_configsection "ns/server/[ns_info server]/tdav/shares"]
if { ![string equal "" $tdav_shares] } {
for {set i 0} {$i < [ns_set size $tdav_shares]} {incr i} {
set tdav_share [ns_configsection "ns/server/[ns_info server]/tdav/share/[ns_set key $tdav_shares $i]"]
tdav::apply_filters [ns_set get $tdav_share uri] [ns_set get $tdav_share options] [ns_set get $tdav_share enablefilesystem]
# uncomment the next line if you are using ns_perm authentication
# tdav::allow_group [ns_set get $tdav_share uri] tdav
}
}
}
TEST
\ No newline at end of file
<html>
<head>
<title>Test Dav File</title>
</head>
<body>
<p>This is the test of OpenACS WebDAV support.</p>
</body>
</html>
\ No newline at end of file
<?xml version="1.0"?>
<!DOCTYPE queryset PUBLIC "-//OpenACS//DTD XQL 1.0//EN" "xql.dtd">
<!-- @author Dave Bauer (dave@thedesignexperience.org) -->
<!-- @creation-date 2003-09-14 -->
<!-- @cvs-id $Id$ -->
<queryset>
<rdbms><type>postgresql</type><version>7.1</version></rdbms>
<fullquery name="_oacs-dav__oacs_dav_put.create_test_folder">
<querytext>
select content_folder__new (
'__test_folder',
'__test_folder',
NULL,
NULL
)
</querytext>
</fullquery>
<fullquery name="_oacs-dav__oacs_dav_put.register_content_type">
<querytext>
select
content_folder__register_content_type(:folder_id,'content_revision','t')
</querytext>
</fullquery>
</queryset>
\ No newline at end of file
#
ad_library {
Test procedures for oacs-dav
@author Dave Bauer (dave@thedesignexperience.org)
@creation-date 2003-09-14
@cvs-id $Id$
}
aa_register_case oacs_dav_sc_create {
test creation of DAV service contract
} {
aa_run_with_teardown \
-rollback \
-test_code {
aa_true "DAV Service contract created" [expr [db_0or1row get_dav_sc ""]]
set sc_ops [db_list get_dav_ops ""]
set valid_ops [list get put mkcol copy propfind proppatch move delete]
foreach op_name $valid_ops {
aa_true "$op_name operation created" [expr [lsearch $sc_ops $op_name] > -1]
}
aa_true "DAV put_type Service contract created" [expr [db_0or1row get_dav_pt_sc ""]]
aa_true "get_type operation created" [expr [db_0or1row get_dav_pt_op ""]]
}
}
aa_register_case oacs_dav_put {
test generic cr_revision PUT
} {
aa_run_with_teardown \
-rollback \
-test_code {
array set sn [site_node::get -url "/"]
set package_id $sn(package_id)
set name "__test_file.html"
oacs_dav::conn -set item_name $name
set uri "/${name}"
set item_id ""
oacs_dav::conn -set method "PUT"
oacs_dav::conn -set item_id $item_id
oacs_dav::conn -set uri $uri
oacs_dav::conn -set urlv $name
oacs_dav::conn -set tmpfile "[acs_root_dir]/packages/oacs-dav/tcl/test/$name"
# we probably want to create a bunch of files in the filesystem
# and test mime type and other attributes to make sure the
# content gets in the database
set fd [open [oacs_dav::conn tmpfile] r]
set orig_content [read $fd]
close $fd
set folder_id [db_exec_plsql create_test_folder ""]
aa_log "Folder Created $folder_id package_id $package_id"
oacs_dav::conn -set folder_id $folder_id
db_exec_plsql register_content_type ""
oacs_dav::register_folder $folder_id $sn(node_id)
set response [oacs_dav::impl::content_revision::put]
aa_log "Response was $response"
set new_item_id [db_string item_exists "" -default ""]
aa_log "Item_id=$new_item_id"
aa_true "Content Item Created" [expr ![empty_string_p $new_item_id]]
set revision_id [db_string revision_exists "" -default ""]
aa_true "Content Revision Created" [expr ![empty_string_p $revision_id]]
set cr_filename "[cr_fs_path]/[db_string get_content_filename ""]"
aa_true "Content Attribute Set" [string equal [file size [oacs_dav::conn tmpfile]] [file size $cr_filename]]
}
}
aa_register_case oacs_dav_mkcol {
test generic content folder creation
} {
aa_run_with_teardown \
-rollback \
-test_code {
array set sn [site_node::get -url "/"]
set package_id $sn(package_id)
set name "__test_folder1/__test_folder2"
set uri "/"
oacs_dav::conn -set item_id ""
oacs_dav::conn -set uri $uri
oacs_dav::conn -set extra_url $name
oacs_dav::conn -set urlv [split $uri "/"]
oacs_dav::conn -set package_id $package_id
set parent_folder_id [db_string get_parent_folder "" -default "-100"]
oacs_dav::conn -set folder_id $parent_folder_id
oacs_dav::register_folder $parent_folder_id $sn(node_id)
foreach fname [split $name "/"] {
set uri "$uri${fname}/"
oacs_dav::conn -set item_name $fname
oacs_dav::conn -set uri $uri
oacs_dav::conn -set extra_url $fname
oacs_dav::conn -set urlv [split $uri "/"]
aa_log "name $fname uri $uri"
set response [oacs_dav::impl::content_folder::mkcol]
set new_folder_id [db_string folder_exists "" -default ""]
aa_true "Content Folder $fname created" [expr ![empty_string_p $new_folder_id]]
}
}
}
\ No newline at end of file
<?xml version="1.0"?>
<!DOCTYPE queryset PUBLIC "-//OpenACS//DTD XQL 1.0//EN" "xql.dtd">
<!-- @author Dave Bauer (dave@thedesignexperience.org) -->
<!-- @creation-date 2003-09-14 -->
<!-- @cvs-id $Id$ -->
<queryset>
<fullquery name="_oacs-dav__oacs_dav_sc_create.get_dav_sc">
<querytext>
select * from acs_sc_contracts where contract_name='dav'
</querytext>
</fullquery>
<fullquery name="_oacs-dav__oacs_dav_sc_create.get_dav_pt_sc">
<querytext>
select * from acs_sc_contracts where contract_name='dav_put_type'
</querytext>
</fullquery>
<fullquery name="_oacs-dav__oacs_dav_sc_create.get_dav_ops">
<querytext>
select operation_name from acs_sc_operations where contract_name='dav'
</querytext>
</fullquery>
<fullquery name="_oacs-dav__oacs_dav_sc_create.get_dav_pt_op">
<querytext>
select operation_name from acs_sc_operations where
contract_name='dav_put_type'
and operation_name='get_type'
</querytext>
</fullquery>
<fullquery name="_oacs-dav__oacs_dav_put.item_exists">
<querytext>
select item_id from cr_items where name=:name
and parent_id=:folder_id
</querytext>
</fullquery>
<fullquery name="_oacs-dav__oacs_dav_put.revision_exists">
<querytext>
select revision_id from cr_revisions
where item_id=:new_item_id
</querytext>
</fullquery>
<fullquery name="_oacs-dav__oacs_dav_put.get_content_filename">
<querytext>
select content from cr_revisions where revision_id=:revision_id
</querytext>
</fullquery>
<fullquery name="_oacs-dav__oacs_dav_put.item_exists">
<querytext>
select item_id from cr_items where name=:name
and parent_id=:folder_id
</querytext>
</fullquery>
<fullquery name="_oacs-dav__oacs_dav_mkcol.get_parent_folder">
<querytext>
select folder_id from cr_folders where package_id=:package_id
</querytext>
</fullquery>
<fullquery name="_oacs-dav__oacs_dav_mkcol.folder_exists">
<querytext>
select item_id
from cr_items
where name=:fname
and content_type='content_folder'
</querytext>
</fullquery>
</queryset>
\ No newline at end of file
TEST
\ No newline at end of file
#
ad_page_contract {
WebDAV disable folders
@author Dave Bauer (dave@thedesignexperience.org)
@creation-date 2004-02-15
@cvs-id $Id$
} {
folder_id:integer,multiple
} -properties {
} -validate {
} -errors {
}
permission::require_permission \
-party_id [ad_conn user_id] \
-object_id [ad_conn package_id ] \
-privilege "admin"
foreach id $folder_id {
db_dml disable_folder ""
}
util_user_message -message [_ oacs-dav.Folders_Disabled]
ad_returnredirect "."
\ No newline at end of file
<?xml version="1.0"?>
<!DOCTYPE queryset PUBLIC "-//OpenACS//DTD XQL 1.0//EN"
"http://www.thecodemill.biz/repository/xql.dtd">
<!-- @author Dave Bauer (dave@thedesignexperience.org) -->
<!-- @creation-date 2004-02-15 -->
<!-- @cvs-id $Id$ -->
<queryset>
<fullquery name="disable_folder">
<querytext>
update dav_site_node_folder_map
set enabled_p = 'f'
where folder_id=:id
</querytext>
</fullquery>
</queryset>
\ No newline at end of file
#
ad_page_contract {
WebDAV enable folders
@author Dave Bauer (dave@thedesignexperience.org)
@creation-date 2004-02-15
@cvs-id $Id$
} {
folder_id:integer,multiple
} -properties {
} -validate {
} -errors {
}
permission::require_permission \
-party_id [ad_conn user_id] \
-object_id [ad_conn package_id ] \
-privilege "admin"
foreach id $folder_id {
db_dml enable_folder ""
}
util_user_message -message [_ oacs-dav.Folders_Enabled]
ad_returnredirect "."
\ No newline at end of file
<?xml version="1.0"?>
<!DOCTYPE queryset PUBLIC "-//OpenACS//DTD XQL 1.0//EN"
"http://www.thecodemill.biz/repository/xql.dtd">
<!-- @author Dave Bauer (dave@thedesignexperience.org) -->
<!-- @creation-date 2004-02-15 -->
<!-- @cvs-id $Id$ -->
<queryset>
<fullquery name="enable_folder">
<querytext>
update dav_site_node_folder_map
set enabled_p = 't'
where folder_id=:id
</querytext>
</fullquery>
</queryset>
\ No newline at end of file
<master>
<property name="title">@title@</property>
<property name="context">@context@</property>
<listtemplate name="folders"></listtemplate>
\ No newline at end of file
# packages/oacs-dav/www/admin/index.tcl
ad_page_contract {
Administer webdav enabled folders
@author Dave Bauer (dave@thedesignexperience.org)
@creation-date 2004-02-15
@cvs-id $Id$
} {
} -properties {
title
context
} -validate {
} -errors {
}
permission::require_permission \
-party_id [ad_conn user_id] \
-object_id [ad_conn package_id ] \
-privilege "admin"
set bulk_actions [list "[_ oacs-dav.Enable]" "enable" "[_ oacs-dav.Enable_Folders]" "[_ oacs-dav.Disable]" "disable" "[_ oacs-dav.Disable_Folders]" ]
template::list::create \
-name folders \
-multirow folders \
-key folder_id \
-bulk_actions $bulk_actions \
-elements {
package_key {label {[_ oacs-dav.Package_Type]}}
package_name { label {[_ oacs-dav.Package_Name]} }
label { label {[_ oacs-dav.Folder_Name]} }
folder_url { label {[_ oacs-dav.Folder_URL]} }
status { label {[_ oacs-dav.Status]} }
}
db_multirow -extend {folder_url package_key package_name status} folders get_folders {} {
array set sn [site_node::get -node_id $node_id]
set folder_url $sn(url)
set package_key $sn(package_key)
set package_name $sn(instance_name)
set status [string map -nocase [list t [_ oacs-dav.Enabled] f [_ oacs-dav.Disabled] ] $enabled_p]
}
set title [_ oacs-dav.WebDAV_Folder_Administration]
set context $title
ad_return_template
<?xml version="1.0"?>
<!DOCTYPE queryset PUBLIC "-//OpenACS//DTD XQL 1.0//EN"
"http://www.thecodemill.biz/repository/xql.dtd">
<!-- @author Dave Bauer (dave@thedesignexperience.org) -->
<!-- @creation-date 2004-02-15 -->
<!-- @cvs-id $Id$ -->
<queryset>
<fullquery name="get_folders">
<querytext>
select cf.folder_id,
cf.label,
sn.node_id,
sn.enabled_p
from cr_folders cf,
dav_site_node_folder_map sn
where cf.folder_id=sn.folder_id
</querytext>
</fullquery>
</queryset>
\ No newline at end of file
<html>
<head>
<title>OpenACS WebDAV Support</title>
</head>
<body>
<h2>OpenACS WebDAV Support</h2>
<h3>Introduction</h3>
<p>This package implements a WebDAV interface to the OpenACS
Content Repository. In addition to generic access to content
items, there is a service-contract interface so packages can
define custom handlers for WebDAV methods for objects that belong
to that package.
</p>
<h3>Installation</h3> <p>Install through the APM. If you install
file-storage, WebDAV support is installed automatically. In
addtion you should check the tDAV specific configuration
parameters to the AOLserver configuration file. The default parameters work fine, they will create webdav URLs like <i>yoursite/</i>dav/*
<p>
You can visit the <a href="/webdav-support/">/webdav-support/</a> page to control webdav access on a per-folder basis. Packages that support WebDAV will add folders to this list and an administrator can then activate or deactivate the folders.
</p>
<h3>How
it Works</h3> <p>OpenACS WebDAV Support requires the tDAV
AOLserver module to implement most of the WebDAV protocol. OpenACS
WebDAV Support just provides and interface between tDAV and the
Content Repository
</p>
<p>Each content_type that requires a custom handler much implement
the <code>dav</code> service contract. Each content type should
implement the <code>dav</code> service contract with the implementation name the same as the content_type. This includes operations
for every WebDAV method. Some operations do not make sense for
certian object types. Specifically, content_items, which are
mapped to WebDAV resources, should not perform a MKCOL (make
collection) method. Likewise, a content_folder, or WebDAV
collection, should not allow a PUT method. In addition to the
<code>dav</code> service contract is a helper contract to allow
packages to set the initial content_type for new items created
through WebDAV. Each package should implement the
<code>dav_put_type</code> service contract with the implementation
named the same as the package key.</p>
<p>Each package instance that will allow WebDAV access should
register a package_id and folder_id for the root content_folder
that corresponds with the URI of the package's mount point using <code>oacs_dav::register_folder</code>.</p>
<h3>Dispatching Requests</h3>
<p>A preauth filter is registered for all WebDAV methods. This
calls oacs_dav::authorize which will set oacs_dav::conn user_id to
the OpenACS user_id or 0 is the request is not authenticated. This
filter also calls oacs_dav::setup_conn sets up the basic
information needed to authorize the request. If authorization
fails a 401 HTTP response is returned requesting authentication
information. If authorization is successful the filter returns
filter_ok and the tdav::filter* filter for the method is called.</p>
<p>The tdav::filter* commands setup global information for each
method that is independent of the storage type. After this filter
runs, the request is handled by the registered procedure for
OpenACS oacs_dav::handle_request.</p>
<p>oacs_dav::handle_request determines the package_id that should
handle the URI. This is based on the standard OpenACS site_node
Tcl API. After the package is found, the root folder for that
package is retreived from the dav_package_folder_map table. Using
the folder_id, and the URI of the request, the
<code>content_item__get_id</code> pl/sql(plpgsql) procedure is
called to find the item_id for the request. If no item_id is found
and the requested method is PUT, a new item should be created. If
the method is not PUT, a 404 error should be returned.
</p>
<p>oacs_dav::handle_request will call the service contract
implemenation for the content_type of the item. If the request is a
PUT, first the dav_put_type service contract for the package_key of
the request is called. For file-storage this returns
"file_storage_object" so items created by PUT are created as
file_storage_objects instead of generic content_revisions. </p>
<p>The service contract implementation for each operation must return
the response data in the format required by tDAV. The documentation
for the tdav::respond::* procedures named for each method describe
what is required.</p>
#
#
# Redirect to administration
#
# @author Dave Bauer (dave@thedesignexperience.org)
# @creation-date 2004-02-16
# @cvs-id $Id$
ad_returnredirect "admin/"
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment