diff options
author | Jörn Friedrich Dreyer <jfd@butonic.de> | 2012-08-25 00:05:07 +0200 |
---|---|---|
committer | Jörn Friedrich Dreyer <jfd@butonic.de> | 2012-08-25 00:05:07 +0200 |
commit | c8255a170c2d7449b4e7728edd2237eea71dca80 (patch) | |
tree | e5a3854ec472be9700064bc7b689b8adf7c7f692 /apps | |
parent | 0c0ae928dcd483211a92303eb2b202453d02a86e (diff) | |
parent | 46d6fd15e4cc02d45079ffc688be0684e61f1434 (diff) | |
download | nextcloud-server-c8255a170c2d7449b4e7728edd2237eea71dca80.tar.gz nextcloud-server-c8255a170c2d7449b4e7728edd2237eea71dca80.zip |
Merge branch 'master' of git://gitorious.org/owncloud/owncloud into oracle-support
Conflicts:
3rdparty/Sabre/CardDAV/Plugin.php
3rdparty/smb4php/smb.php
apps/bookmarks/ajax/addBookmark.php
apps/bookmarks/ajax/editBookmark.php
apps/bookmarks/appinfo/migrate.php
apps/calendar/ajax/calendar/edit.form.php
apps/calendar/ajax/changeview.php
apps/calendar/ajax/import/import.php
apps/calendar/ajax/settings/guesstimezone.php
apps/calendar/ajax/settings/setfirstday.php
apps/calendar/ajax/settings/settimeformat.php
apps/calendar/ajax/share/changepermission.php
apps/calendar/ajax/share/share.php
apps/calendar/ajax/share/unshare.php
apps/calendar/appinfo/app.php
apps/calendar/appinfo/remote.php
apps/calendar/appinfo/update.php
apps/calendar/appinfo/version
apps/calendar/js/calendar.js
apps/calendar/l10n/da.php
apps/calendar/l10n/de.php
apps/calendar/l10n/fi_FI.php
apps/calendar/l10n/gl.php
apps/calendar/l10n/he.php
apps/calendar/l10n/hr.php
apps/calendar/l10n/ja_JP.php
apps/calendar/l10n/lb.php
apps/calendar/l10n/lt_LT.php
apps/calendar/l10n/nb_NO.php
apps/calendar/l10n/pl.php
apps/calendar/l10n/pt_PT.php
apps/calendar/l10n/ro.php
apps/calendar/l10n/ru.php
apps/calendar/l10n/sv.php
apps/calendar/l10n/zh_CN.php
apps/calendar/l10n/zh_TW.php
apps/calendar/lib/app.php
apps/calendar/lib/calendar.php
apps/calendar/lib/object.php
apps/calendar/lib/share.php
apps/calendar/templates/part.choosecalendar.rowfields.php
apps/calendar/templates/part.import.php
apps/calendar/templates/settings.php
apps/contacts/ajax/activation.php
apps/contacts/ajax/addressbook/delete.php
apps/contacts/ajax/contact/add.php
apps/contacts/ajax/contact/addproperty.php
apps/contacts/ajax/contact/delete.php
apps/contacts/ajax/contact/deleteproperty.php
apps/contacts/ajax/contact/saveproperty.php
apps/contacts/ajax/createaddressbook.php
apps/contacts/ajax/cropphoto.php
apps/contacts/ajax/currentphoto.php
apps/contacts/ajax/importaddressbook.php
apps/contacts/ajax/oc_photo.php
apps/contacts/ajax/savecrop.php
apps/contacts/ajax/selectaddressbook.php
apps/contacts/ajax/updateaddressbook.php
apps/contacts/ajax/uploadimport.php
apps/contacts/ajax/uploadphoto.php
apps/contacts/appinfo/migrate.php
apps/contacts/appinfo/remote.php
apps/contacts/css/contacts.css
apps/contacts/import.php
apps/contacts/index.php
apps/contacts/js/contacts.js
apps/contacts/l10n/ca.php
apps/contacts/l10n/cs_CZ.php
apps/contacts/l10n/da.php
apps/contacts/l10n/de.php
apps/contacts/l10n/el.php
apps/contacts/l10n/eo.php
apps/contacts/l10n/es.php
apps/contacts/l10n/et_EE.php
apps/contacts/l10n/eu.php
apps/contacts/l10n/fa.php
apps/contacts/l10n/fi_FI.php
apps/contacts/l10n/fr.php
apps/contacts/l10n/he.php
apps/contacts/l10n/hr.php
apps/contacts/l10n/hu_HU.php
apps/contacts/l10n/ia.php
apps/contacts/l10n/it.php
apps/contacts/l10n/ja_JP.php
apps/contacts/l10n/ko.php
apps/contacts/l10n/lb.php
apps/contacts/l10n/mk.php
apps/contacts/l10n/nb_NO.php
apps/contacts/l10n/nl.php
apps/contacts/l10n/pl.php
apps/contacts/l10n/pt_BR.php
apps/contacts/l10n/pt_PT.php
apps/contacts/l10n/ro.php
apps/contacts/l10n/ru.php
apps/contacts/l10n/sk_SK.php
apps/contacts/l10n/sl.php
apps/contacts/l10n/sv.php
apps/contacts/l10n/th_TH.php
apps/contacts/l10n/tr.php
apps/contacts/l10n/zh_CN.php
apps/contacts/l10n/zh_TW.php
apps/contacts/lib/addressbook.php
apps/contacts/lib/hooks.php
apps/contacts/lib/vcard.php
apps/contacts/photo.php
apps/contacts/templates/part.contact.php
apps/contacts/templates/part.contacts.php
apps/contacts/templates/part.cropphoto.php
apps/contacts/templates/part.importaddressbook.php
apps/contacts/templates/part.selectaddressbook.php
apps/contacts/thumbnail.php
apps/files/ajax/download.php
apps/files/ajax/newfile.php
apps/files/ajax/timezone.php
apps/files/appinfo/update.php
apps/files/appinfo/version
apps/files/index.php
apps/files/js/fileactions.js
apps/files/js/filelist.js
apps/files/js/files.js
apps/files/l10n/ar.php
apps/files/l10n/bg_BG.php
apps/files/l10n/ca.php
apps/files/l10n/cs_CZ.php
apps/files/l10n/da.php
apps/files/l10n/de.php
apps/files/l10n/el.php
apps/files/l10n/eo.php
apps/files/l10n/es.php
apps/files/l10n/et_EE.php
apps/files/l10n/eu.php
apps/files/l10n/fa.php
apps/files/l10n/fi_FI.php
apps/files/l10n/fr.php
apps/files/l10n/gl.php
apps/files/l10n/he.php
apps/files/l10n/hr.php
apps/files/l10n/hu_HU.php
apps/files/l10n/ia.php
apps/files/l10n/id.php
apps/files/l10n/it.php
apps/files/l10n/ja_JP.php
apps/files/l10n/ko.php
apps/files/l10n/lb.php
apps/files/l10n/lt_LT.php
apps/files/l10n/mk.php
apps/files/l10n/ms_MY.php
apps/files/l10n/nb_NO.php
apps/files/l10n/nl.php
apps/files/l10n/nn_NO.php
apps/files/l10n/pl.php
apps/files/l10n/pt_BR.php
apps/files/l10n/pt_PT.php
apps/files/l10n/ro.php
apps/files/l10n/ru.php
apps/files/l10n/sk_SK.php
apps/files/l10n/sl.php
apps/files/l10n/sr.php
apps/files/l10n/sr@latin.php
apps/files/l10n/sv.php
apps/files/l10n/th_TH.php
apps/files/l10n/tr.php
apps/files/l10n/uk.php
apps/files/l10n/zh_CN.php
apps/files/l10n/zh_TW.php
apps/files_archive/js/archive.js
apps/files_encryption/lib/cryptstream.php
apps/files_encryption/lib/proxy.php
apps/files_encryption/tests/proxy.php
apps/files_external/appinfo/app.php
apps/files_external/lib/smb.php
apps/files_external/lib/streamwrapper.php
apps/files_external/tests/config.php
apps/files_external/tests/smb.php
apps/files_sharing/ajax/email.php
apps/files_sharing/ajax/getitem.php
apps/files_sharing/ajax/setpermissions.php
apps/files_sharing/ajax/share.php
apps/files_sharing/ajax/toggleresharing.php
apps/files_sharing/ajax/unshare.php
apps/files_sharing/ajax/userautocomplete.php
apps/files_sharing/js/settings.js
apps/files_sharing/js/share.js
apps/files_sharing/lib_share.php
apps/files_sharing/settings.php
apps/files_sharing/sharedstorage.php
apps/files_sharing/templates/settings.php
apps/files_versions/ajax/rollbackVersion.php
apps/files_versions/versions.php
apps/gallery/ajax/thumbnail.php
apps/gallery/appinfo/app.php
apps/gallery/appinfo/update.php
apps/gallery/appinfo/version
apps/gallery/css/styles.css
apps/gallery/index.php
apps/gallery/js/pictures.js
apps/gallery/l10n/ca.php
apps/gallery/l10n/cs_CZ.php
apps/gallery/l10n/de.php
apps/gallery/l10n/el.php
apps/gallery/l10n/es.php
apps/gallery/l10n/fi_FI.php
apps/gallery/l10n/fr.php
apps/gallery/l10n/it.php
apps/gallery/l10n/pl.php
apps/gallery/l10n/pt_PT.php
apps/gallery/l10n/ru.php
apps/gallery/l10n/sl.php
apps/gallery/l10n/sv.php
apps/gallery/l10n/th_TH.php
apps/gallery/l10n/tr.php
apps/gallery/l10n/zh_CN.php
apps/gallery/lib/album.php
apps/gallery/lib/hooks_handlers.php
apps/gallery/lib/managers.php
apps/gallery/lib/photo.php
apps/gallery/lib/tiles.php
apps/gallery/lib/tiles_test.php
apps/gallery/templates/index.php
apps/media/lib_ampache.php
apps/media/lib_collection.php
apps/media/lib_media.php
apps/remoteStorage/lib_remoteStorage.php
apps/tasks/ajax/addtaskform.php
apps/tasks/ajax/edittask.php
apps/user_ldap/appinfo/update.php
apps/user_ldap/group_ldap.php
apps/user_ldap/lib_ldap.php
apps/user_ldap/settings.php
apps/user_ldap/templates/settings.php
apps/user_ldap/user_ldap.php
apps/user_migrate/appinfo/app.php
apps/user_migrate/templates/settings.php
apps/user_webfinger/host-meta.php
config/config.sample.php
core/js/js.js
core/l10n/da.php
core/l10n/de.php
core/l10n/fi_FI.php
core/l10n/gl.php
core/l10n/he.php
core/l10n/hr.php
core/l10n/id.php
core/l10n/ja_JP.php
core/l10n/lb.php
core/l10n/lt_LT.php
core/l10n/nb_NO.php
core/l10n/pl.php
core/l10n/pt_PT.php
core/l10n/ro.php
core/l10n/ru.php
core/l10n/sv.php
core/lostpassword/index.php
core/templates/layout.user.php
core/templates/login.php
db_structure.xml
index.php
l10n/af/calendar.po
l10n/af/contacts.po
l10n/af/core.po
l10n/af/files.po
l10n/af/settings.po
l10n/ar/calendar.po
l10n/ar/contacts.po
l10n/ar/core.po
l10n/ar/files.po
l10n/ar/media.po
l10n/ar/settings.po
l10n/bg_BG/calendar.po
l10n/bg_BG/contacts.po
l10n/bg_BG/core.po
l10n/bg_BG/files.po
l10n/bg_BG/media.po
l10n/bg_BG/settings.po
l10n/ca/calendar.po
l10n/ca/contacts.po
l10n/ca/core.po
l10n/ca/files.po
l10n/ca/gallery.po
l10n/ca/settings.po
l10n/cs_CZ/calendar.po
l10n/cs_CZ/contacts.po
l10n/cs_CZ/core.po
l10n/cs_CZ/files.po
l10n/cs_CZ/gallery.po
l10n/cs_CZ/settings.po
l10n/da/calendar.po
l10n/da/contacts.po
l10n/da/core.po
l10n/da/files.po
l10n/da/settings.po
l10n/de/calendar.po
l10n/de/contacts.po
l10n/de/core.po
l10n/de/files.po
l10n/de/gallery.po
l10n/de/settings.po
l10n/el/calendar.po
l10n/el/contacts.po
l10n/el/core.po
l10n/el/files.po
l10n/el/gallery.po
l10n/el/settings.po
l10n/eo/calendar.po
l10n/eo/contacts.po
l10n/eo/core.po
l10n/eo/files.po
l10n/eo/media.po
l10n/eo/settings.po
l10n/es/calendar.po
l10n/es/contacts.po
l10n/es/core.po
l10n/es/files.po
l10n/es/gallery.po
l10n/es/settings.po
l10n/et_EE/calendar.po
l10n/et_EE/contacts.po
l10n/et_EE/core.po
l10n/et_EE/files.po
l10n/et_EE/settings.po
l10n/eu/calendar.po
l10n/eu/contacts.po
l10n/eu/core.po
l10n/eu/files.po
l10n/eu/settings.po
l10n/fa/calendar.po
l10n/fa/contacts.po
l10n/fa/core.po
l10n/fa/files.po
l10n/fa/settings.po
l10n/fi_FI/calendar.po
l10n/fi_FI/contacts.po
l10n/fi_FI/core.po
l10n/fi_FI/files.po
l10n/fi_FI/gallery.po
l10n/fi_FI/settings.po
l10n/fr/calendar.po
l10n/fr/contacts.po
l10n/fr/core.po
l10n/fr/files.po
l10n/fr/gallery.po
l10n/fr/media.po
l10n/fr/settings.po
l10n/gl/calendar.po
l10n/gl/contacts.po
l10n/gl/core.po
l10n/gl/files.po
l10n/gl/settings.po
l10n/he/calendar.po
l10n/he/contacts.po
l10n/he/core.po
l10n/he/files.po
l10n/he/settings.po
l10n/hr/calendar.po
l10n/hr/contacts.po
l10n/hr/core.po
l10n/hr/files.po
l10n/hr/settings.po
l10n/hu_HU/calendar.po
l10n/hu_HU/contacts.po
l10n/hu_HU/core.po
l10n/hu_HU/files.po
l10n/hu_HU/settings.po
l10n/hy/calendar.po
l10n/hy/contacts.po
l10n/hy/core.po
l10n/hy/files.po
l10n/hy/settings.po
l10n/ia/calendar.po
l10n/ia/contacts.po
l10n/ia/core.po
l10n/ia/files.po
l10n/ia/settings.po
l10n/id/calendar.po
l10n/id/contacts.po
l10n/id/core.po
l10n/id/files.po
l10n/id/settings.po
l10n/it/calendar.po
l10n/it/contacts.po
l10n/it/core.po
l10n/it/files.po
l10n/it/gallery.po
l10n/it/settings.po
l10n/ja_JP/calendar.po
l10n/ja_JP/contacts.po
l10n/ja_JP/core.po
l10n/ja_JP/files.po
l10n/ja_JP/settings.po
l10n/ko/calendar.po
l10n/ko/contacts.po
l10n/ko/core.po
l10n/ko/files.po
l10n/ko/settings.po
l10n/lb/calendar.po
l10n/lb/contacts.po
l10n/lb/core.po
l10n/lb/files.po
l10n/lb/settings.po
l10n/lt_LT/calendar.po
l10n/lt_LT/contacts.po
l10n/lt_LT/core.po
l10n/lt_LT/files.po
l10n/lt_LT/settings.po
l10n/mk/calendar.po
l10n/mk/contacts.po
l10n/mk/core.po
l10n/mk/files.po
l10n/mk/settings.po
l10n/ms_MY/calendar.po
l10n/ms_MY/contacts.po
l10n/ms_MY/core.po
l10n/ms_MY/files.po
l10n/ms_MY/settings.po
l10n/nb_NO/calendar.po
l10n/nb_NO/contacts.po
l10n/nb_NO/core.po
l10n/nb_NO/files.po
l10n/nb_NO/settings.po
l10n/nl/calendar.po
l10n/nl/contacts.po
l10n/nl/core.po
l10n/nl/files.po
l10n/nl/settings.po
l10n/nn_NO/calendar.po
l10n/nn_NO/contacts.po
l10n/nn_NO/core.po
l10n/nn_NO/files.po
l10n/nn_NO/settings.po
l10n/pl/calendar.po
l10n/pl/contacts.po
l10n/pl/core.po
l10n/pl/files.po
l10n/pl/gallery.po
l10n/pl/settings.po
l10n/pt_BR/calendar.po
l10n/pt_BR/contacts.po
l10n/pt_BR/core.po
l10n/pt_BR/files.po
l10n/pt_BR/settings.po
l10n/pt_PT/calendar.po
l10n/pt_PT/contacts.po
l10n/pt_PT/core.po
l10n/pt_PT/files.po
l10n/pt_PT/gallery.po
l10n/pt_PT/settings.po
l10n/ro/calendar.po
l10n/ro/contacts.po
l10n/ro/core.po
l10n/ro/files.po
l10n/ro/settings.po
l10n/ru/calendar.po
l10n/ru/contacts.po
l10n/ru/core.po
l10n/ru/files.po
l10n/ru/gallery.po
l10n/ru/settings.po
l10n/sk_SK/calendar.po
l10n/sk_SK/contacts.po
l10n/sk_SK/core.po
l10n/sk_SK/files.po
l10n/sk_SK/settings.po
l10n/sl/calendar.po
l10n/sl/contacts.po
l10n/sl/core.po
l10n/sl/files.po
l10n/sl/gallery.po
l10n/sl/settings.po
l10n/sr/calendar.po
l10n/sr/contacts.po
l10n/sr/core.po
l10n/sr/files.po
l10n/sr/settings.po
l10n/sr@latin/calendar.po
l10n/sr@latin/contacts.po
l10n/sr@latin/core.po
l10n/sr@latin/files.po
l10n/sr@latin/settings.po
l10n/sv/calendar.po
l10n/sv/contacts.po
l10n/sv/core.po
l10n/sv/files.po
l10n/sv/gallery.po
l10n/sv/media.po
l10n/sv/settings.po
l10n/templates/bookmarks.pot
l10n/templates/calendar.pot
l10n/templates/contacts.pot
l10n/templates/core.pot
l10n/templates/files.pot
l10n/templates/gallery.pot
l10n/templates/media.pot
l10n/templates/settings.pot
l10n/th_TH/calendar.po
l10n/th_TH/contacts.po
l10n/th_TH/core.po
l10n/th_TH/files.po
l10n/th_TH/gallery.po
l10n/th_TH/settings.po
l10n/tr/calendar.po
l10n/tr/contacts.po
l10n/tr/core.po
l10n/tr/files.po
l10n/tr/gallery.po
l10n/tr/settings.po
l10n/uk/calendar.po
l10n/uk/contacts.po
l10n/uk/core.po
l10n/uk/files.po
l10n/uk/media.po
l10n/uk/settings.po
l10n/zh_CN/calendar.po
l10n/zh_CN/contacts.po
l10n/zh_CN/core.po
l10n/zh_CN/files.po
l10n/zh_CN/gallery.po
l10n/zh_CN/settings.po
l10n/zh_TW/calendar.po
l10n/zh_TW/contacts.po
l10n/zh_TW/core.po
l10n/zh_TW/files.po
l10n/zh_TW/settings.po
lib/app.php
lib/base.php
lib/connector/sabre/file.php
lib/connector/sabre/locks.php
lib/connector/sabre/node.php
lib/db.php
lib/filecache.php
lib/fileproxy/quota.php
lib/files.php
lib/filestorage/local.php
lib/filesystemview.php
lib/group/database.php
lib/helper.php
lib/installer.php
lib/json.php
lib/l10n.php
lib/migrate.php
lib/mimetypes.fixlist.php
lib/ocs.php
lib/preferences.php
lib/public/json.php
lib/public/util.php
lib/template.php
lib/user.php
lib/user/database.php
lib/util.php
lib/vcategories.php
ocs/providers.php
settings/admin.php
settings/ajax/lostpassword.php
settings/ajax/removeuser.php
settings/ajax/setbackgroundjobsmode.php
settings/ajax/setlanguage.php
settings/ajax/setquota.php
settings/ajax/togglegroups.php
settings/apps.php
settings/css/settings.css
settings/js/apps.js
settings/js/users.js
settings/l10n/bg_BG.php
settings/l10n/ca.php
settings/l10n/cs_CZ.php
settings/l10n/da.php
settings/l10n/de.php
settings/l10n/el.php
settings/l10n/eo.php
settings/l10n/es.php
settings/l10n/et_EE.php
settings/l10n/eu.php
settings/l10n/fa.php
settings/l10n/fi_FI.php
settings/l10n/fr.php
settings/l10n/gl.php
settings/l10n/he.php
settings/l10n/hr.php
settings/l10n/hu_HU.php
settings/l10n/it.php
settings/l10n/ja_JP.php
settings/l10n/ko.php
settings/l10n/lt_LT.php
settings/l10n/mk.php
settings/l10n/ms_MY.php
settings/l10n/nb_NO.php
settings/l10n/nl.php
settings/l10n/nn_NO.php
settings/l10n/pl.php
settings/l10n/pt_BR.php
settings/l10n/pt_PT.php
settings/l10n/ru.php
settings/l10n/sk_SK.php
settings/l10n/sl.php
settings/l10n/sv.php
settings/l10n/th_TH.php
settings/l10n/tr.php
settings/l10n/zh_CN.php
settings/personal.php
settings/templates/admin.php
settings/templates/users.php
Diffstat (limited to 'apps')
846 files changed, 33628 insertions, 13156 deletions
diff --git a/apps/admin_audit/appinfo/app.php b/apps/admin_audit/appinfo/app.php new file mode 100644 index 00000000000..e52f633cf14 --- /dev/null +++ b/apps/admin_audit/appinfo/app.php @@ -0,0 +1,18 @@ +<?php + +OC::$CLASSPATH['OC_Admin_Audit_Hooks_Handlers'] = 'apps/admin_audit/lib/hooks_handlers.php'; + +OCP\Util::connectHook('OCP\User', 'pre_login', 'OC_Admin_Audit_Hooks_Handlers', 'pre_login'); +OCP\Util::connectHook('OCP\User', 'post_login', 'OC_Admin_Audit_Hooks_Handlers', 'post_login'); +OCP\Util::connectHook('OCP\User', 'logout', 'OC_Admin_Audit_Hooks_Handlers', 'logout'); + +OCP\Util::connectHook(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_rename, 'OC_Admin_Audit_Hooks_Handlers', 'rename'); +OCP\Util::connectHook(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_create, 'OC_Admin_Audit_Hooks_Handlers', 'create'); +OCP\Util::connectHook(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_copy, 'OC_Admin_Audit_Hooks_Handlers', 'copy'); +OCP\Util::connectHook(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_write, 'OC_Admin_Audit_Hooks_Handlers', 'write'); +OCP\Util::connectHook(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_read, 'OC_Admin_Audit_Hooks_Handlers', 'read'); +OCP\Util::connectHook(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_delete, 'OC_Admin_Audit_Hooks_Handlers', 'delete'); + +OCP\Util::connectHook('OC_Share', 'public', 'OC_Admin_Audit_Hooks_Handlers', 'share_public'); +OCP\Util::connectHook('OC_Share', 'public-download', 'OC_Admin_Audit_Hooks_Handlers', 'share_public_download'); +OCP\Util::connectHook('OC_Share', 'user', 'OC_Admin_Audit_Hooks_Handlers', 'share_user'); diff --git a/apps/admin_audit/appinfo/info.xml b/apps/admin_audit/appinfo/info.xml new file mode 100644 index 00000000000..4fa013162f3 --- /dev/null +++ b/apps/admin_audit/appinfo/info.xml @@ -0,0 +1,11 @@ +<?xml version="1.0"?> +<info> + <id>admin_audit</id> + <name>Log audit info</name> + <version>0.1</version> + <licence>AGPL</licence> + <author>Bart Visscher</author> + <require>4</require> + <description>Audit user actions in Owncloud</description> + <shipped>true</shipped> +</info> diff --git a/apps/admin_audit/lib/hooks_handlers.php b/apps/admin_audit/lib/hooks_handlers.php new file mode 100644 index 00000000000..c5aec97d939 --- /dev/null +++ b/apps/admin_audit/lib/hooks_handlers.php @@ -0,0 +1,72 @@ +<?php + +class OC_Admin_Audit_Hooks_Handlers { + static public function pre_login($params) { + $path = $params['uid']; + self::log('Trying login '.$user); + } + static public function post_login($params) { + $path = $params['uid']; + self::log('Login '.$user); + } + static public function logout($params) { + $user = OCP\User::getUser(); + self::log('Logout '.$user); + } + + static public function rename($params) { + $oldpath = $params[OC_Filesystem::signal_param_oldpath]; + $newpath = $params[OC_Filesystem::signal_param_newpath]; + $user = OCP\User::getUser(); + self::log('Rename "'.$oldpath.'" to "'.$newpath.'" by '.$user); + } + static public function create($params) { + $path = $params[OC_Filesystem::signal_param_path]; + $user = OCP\User::getUser(); + self::log('Create "'.$path.'" by '.$user); + } + static public function copy($params) { + $oldpath = $params[OC_Filesystem::signal_param_oldpath]; + $newpath = $params[OC_Filesystem::signal_param_newpath]; + $user = OCP\User::getUser(); + self::log('Copy "'.$oldpath.'" to "'.$newpath.'" by '.$user); + } + static public function write($params) { + $path = $params[OC_Filesystem::signal_param_path]; + $user = OCP\User::getUser(); + self::log('Write "'.$path.'" by '.$user); + } + static public function read($params) { + $path = $params[OC_Filesystem::signal_param_path]; + $user = OCP\User::getUser(); + self::log('Read "'.$path.'" by '.$user); + } + static public function delete($params) { + $path = $params[OC_Filesystem::signal_param_path]; + $user = OCP\User::getUser(); + self::log('Delete "'.$path.'" by '.$user); + } + static public function share_public($params) { + $path = $params['source']; + $token = $params['token']; + $user = OCP\User::getUser(); + self::log('Shared "'.$path.'" with public, token="'.$token.'" by '.$user); + } + static public function share_public_download($params) { + $path = $params['source']; + $token = $params['token']; + $user = $_SERVER['REMOTE_ADDR']; + self::log('Download of shared "'.$path.'" token="'.$token.'" by '.$user); + } + static public function share_user($params) { + $path = $params['source']; + $permissions = $params['permissions']; + $with = $params['with']; + $user = OCP\User::getUser(); + $rw = $permissions & OC_Share::WRITE ? 'w' : 'o'; + self::log('Shared "'.$path.'" (r'.$rw.') with user "'.$with.'" by '.$user); + } + static protected function log($msg) { + OCP\Util::writeLog('admin_audit', $msg, OCP\Util::INFO); + } +} diff --git a/apps/admin_dependencies_chk/appinfo/app.php b/apps/admin_dependencies_chk/appinfo/app.php index 72d368a085e..62b26342d23 100644 --- a/apps/admin_dependencies_chk/appinfo/app.php +++ b/apps/admin_dependencies_chk/appinfo/app.php @@ -1,9 +1,4 @@ <?php $l=OC_L10N::get('admin_dependencies_chk'); -OCP\App::register( array( - 'order' => 14, - 'id' => 'admin_dependencies_chk', - 'name' => 'Owncloud Install Info' )); - OCP\App::registerAdmin('admin_dependencies_chk','settings'); diff --git a/apps/admin_dependencies_chk/l10n/.gitkeep b/apps/admin_dependencies_chk/l10n/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/.gitkeep diff --git a/apps/admin_dependencies_chk/l10n/ca.php b/apps/admin_dependencies_chk/l10n/ca.php new file mode 100644 index 00000000000..08f4ec80781 --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/ca.php @@ -0,0 +1,14 @@ +<?php $TRANSLATIONS = array( +"The php-json module is needed by the many applications for inter communications" => "El mòdul php-json és necessari per moltes aplicacions per comunicacions internes", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "El mòdul php-curl és necessari per mostrar el tÃtol de la pà gina quan s'afegeixen adreces d'interès", +"The php-gd module is needed to create thumbnails of your images" => "El mòdul php-gd és necessari per generar miniatures d'imatges", +"The php-ldap module is needed connect to your ldap server" => "El mòdul php-ldap és necessari per connectar amb el servidor ldap", +"The php-zip module is needed download multiple files at once" => "El mòdul php-zip és necessari per baixar múltiples fitxers de cop", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "El mòdul php-mb_multibyte és necessari per gestionar correctament la codificació.", +"The php-ctype module is needed validate data." => "El mòdul php-ctype és necessari per validar dades.", +"The php-xml module is needed to share files with webdav." => "El mòdul php-xml és necessari per compatir els fitxers amb webdav.", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "La directiva allow_url_fopen de php.ini hauria d'establir-se en 1 per accedir a la base de coneixements dels servidors OCS", +"The php-pdo module is needed to store owncloud data into a database." => "El mòdul php-pdo és necessari per desar les dades d'ownCloud en una base de dades.", +"Dependencies status" => "Estat de dependències", +"Used by :" => "Usat per:" +); diff --git a/apps/admin_dependencies_chk/l10n/cs_CZ.php b/apps/admin_dependencies_chk/l10n/cs_CZ.php new file mode 100644 index 00000000000..f750f13aefc --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/cs_CZ.php @@ -0,0 +1,14 @@ +<?php $TRANSLATIONS = array( +"The php-json module is needed by the many applications for inter communications" => "Modul php-json je tÅ™eba pro vzájemnou komunikaci mnoha aplikacÃ", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "Modul php-curl je tÅ™eba pro zobrazenà titulu strany v okamžiku pÅ™idánà záložky", +"The php-gd module is needed to create thumbnails of your images" => "Modul php-gd je tÅ™eba pro tvorbu náhledů VaÅ¡ich obrázků", +"The php-ldap module is needed connect to your ldap server" => "Modul php-ldap je tÅ™eba pro pÅ™ipojenà na Váš ldap server", +"The php-zip module is needed download multiple files at once" => "Modul php-zip je tÅ™eba pro souběžné stahovánà souborů", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "Modul php-mb_multibyte je tÅ™eba pro správnou funkci kódovánÃ.", +"The php-ctype module is needed validate data." => "Modul php-ctype je tÅ™eba k ověřovánà dat.", +"The php-xml module is needed to share files with webdav." => "Modul php-xml je tÅ™eba ke sdÃlenà souborů prostÅ™ednictvÃm WebDAV.", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "PÅ™Ãkaz allow_url_fopen ve VaÅ¡em php.ini souboru by mÄ›l být nastaven na 1 kvůli zÃskávánà informacà z OCS serverů", +"The php-pdo module is needed to store owncloud data into a database." => "Modul php-pdo je tÅ™eba pro ukládánà dat ownCloud do databáze", +"Dependencies status" => "Status závislostÃ", +"Used by :" => "PoužÃváno:" +); diff --git a/apps/admin_dependencies_chk/l10n/de.php b/apps/admin_dependencies_chk/l10n/de.php new file mode 100644 index 00000000000..7877e7d679b --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/de.php @@ -0,0 +1,14 @@ +<?php $TRANSLATIONS = array( +"The php-json module is needed by the many applications for inter communications" => "Das Modul php-json wird von vielen Anwendungen zur internen Kommunikation benötigt.", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "Das Modul php-curl wird benötigt, um den Titel der Seite für die Lesezeichen hinzuzufügen.", +"The php-gd module is needed to create thumbnails of your images" => "Das Modul php-gd wird für die Erzeugung der Vorschaubilder benötigt.", +"The php-ldap module is needed connect to your ldap server" => "Das Modul php-ldap wird für die Verbindung mit dem LDAP-Server benötigt.", +"The php-zip module is needed download multiple files at once" => "Das Modul php-zip wird für den gleichzeitigen Download mehrerer Dateien benötigt.", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "Das Modul php_mb_multibyte wird benötigt, um das Encoding richtig zu handhaben.", +"The php-ctype module is needed validate data." => "Das Modul php-ctype wird benötigt, um Daten zu prüfen.", +"The php-xml module is needed to share files with webdav." => "Das Modul php-xml wird benötigt, um Dateien über WebDAV zu teilen.", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "Die Richtlinie allow_url_fopen in Ihrer php.ini sollte auf 1 gesetzt werden, um die Wissensbasis vom OCS-Server abrufen.", +"The php-pdo module is needed to store owncloud data into a database." => "Das Modul php-pdo wird benötigt, um Daten in der Datenbank zu speichern.", +"Dependencies status" => "Status der Abhängigkeiten", +"Used by :" => "Benutzt von:" +); diff --git a/apps/admin_dependencies_chk/l10n/el.php b/apps/admin_dependencies_chk/l10n/el.php new file mode 100644 index 00000000000..9dd455b670e --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/el.php @@ -0,0 +1,4 @@ +<?php $TRANSLATIONS = array( +"Dependencies status" => "Κατάσταση εξαÏτήσεων", +"Used by :" => "ΧÏησιμοποιήθηκε από:" +); diff --git a/apps/admin_dependencies_chk/l10n/eo.php b/apps/admin_dependencies_chk/l10n/eo.php new file mode 100644 index 00000000000..65896831f7b --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/eo.php @@ -0,0 +1,14 @@ +<?php $TRANSLATIONS = array( +"The php-json module is needed by the many applications for inter communications" => "La modulo php-json necesas por komuniko inter la multaj aplikaĵoj", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "La modulo php-curl necesas por venigi la paÄotitolon dum aldono de legosigno", +"The php-gd module is needed to create thumbnails of your images" => "La modulo php-gd necesas por krei bildetojn.", +"The php-ldap module is needed connect to your ldap server" => "La modulo php-ldap necesas por konekti al via LDAP-servilo.", +"The php-zip module is needed download multiple files at once" => "La modulo php-zip necesas por elÅuti plurajn dosierojn per unu fojo.", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "La modulo php-mb_multibyte necesas por Äuste administri la kodprezenton.", +"The php-ctype module is needed validate data." => "La modulo php-ctype necesas por validkontroli datumojn.", +"The php-xml module is needed to share files with webdav." => "La modulo php-xml necesas por kunhavigi dosierojn per WebDAV.", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "La ordono allow_url_fopen de via php.ini devus valori 1 por ricevi scibazon el OCS-serviloj", +"The php-pdo module is needed to store owncloud data into a database." => "La modulo php-pdo necesas por konservi datumojn de ownCloud en datumbazo.", +"Dependencies status" => "Stato de dependoj", +"Used by :" => "Uzata de:" +); diff --git a/apps/admin_dependencies_chk/l10n/es.php b/apps/admin_dependencies_chk/l10n/es.php new file mode 100644 index 00000000000..cbf13951ac1 --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/es.php @@ -0,0 +1,4 @@ +<?php $TRANSLATIONS = array( +"Dependencies status" => "Estado de las dependencias", +"Used by :" => "Usado por:" +); diff --git a/apps/admin_dependencies_chk/l10n/et_EE.php b/apps/admin_dependencies_chk/l10n/et_EE.php new file mode 100644 index 00000000000..529c8e78d1a --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/et_EE.php @@ -0,0 +1,14 @@ +<?php $TRANSLATIONS = array( +"The php-json module is needed by the many applications for inter communications" => "php-json moodul on vajalik paljude rakenduse poolt omvahelise suhtlemise jaoks", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "php-curl moodul on vajalik lehe pealkirja tõmbamiseks järjehoidja lisamisel", +"The php-gd module is needed to create thumbnails of your images" => "php-gd moodul on vajalik sinu piltidest pisipiltide loomiseks", +"The php-ldap module is needed connect to your ldap server" => "php-ldap moodul on vajalik sinu ldap serveriga ühendumiseks", +"The php-zip module is needed download multiple files at once" => "php-zip moodul on vajalik mitme faili korraga alla laadimiseks", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "php-mb_multibyte moodul on vajalik kodeerimise korrektseks haldamiseks.", +"The php-ctype module is needed validate data." => "php-ctype moodul on vajalik andmete kontrollimiseks.", +"The php-xml module is needed to share files with webdav." => "php-xml moodul on vajalik failide jagamiseks webdav-iga.", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "Sinu php.ini failis oleva direktiivi allow_url_fopen väärtuseks peaks määrama 1, et saaks tõmmata teadmistebaasi OCS-i serveritest", +"The php-pdo module is needed to store owncloud data into a database." => "php-pdo moodul on vajalik owncloudi andmete salvestamiseks andmebaasi.", +"Dependencies status" => "Sõltuvuse staatus", +"Used by :" => "Kasutab :" +); diff --git a/apps/admin_dependencies_chk/l10n/fi_FI.php b/apps/admin_dependencies_chk/l10n/fi_FI.php new file mode 100644 index 00000000000..85e33cfe8c5 --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/fi_FI.php @@ -0,0 +1,9 @@ +<?php $TRANSLATIONS = array( +"The php-gd module is needed to create thumbnails of your images" => "php-gd-moduuli vaaditaan, jotta kuvista on mahdollista luoda esikatselukuvia", +"The php-ldap module is needed connect to your ldap server" => "php-ldap-moduuli vaaditaan, jotta yhteys ldap-palvelimeen on mahdollista", +"The php-zip module is needed download multiple files at once" => "php-zip-moduuli vaaditaan, jotta useiden tiedostojen samanaikainen lataus on mahdollista", +"The php-xml module is needed to share files with webdav." => "php-xml-moduuli vaaditaan, jotta tiedostojen jako webdavia käyttäen on mahdollista", +"The php-pdo module is needed to store owncloud data into a database." => "php-pdo-moduuli tarvitaan, jotta ownCloud-tietojen tallennus tietokantaan on mahdollista", +"Dependencies status" => "Riippuvuuksien tila", +"Used by :" => "Käyttökohde:" +); diff --git a/apps/admin_dependencies_chk/l10n/fr.php b/apps/admin_dependencies_chk/l10n/fr.php new file mode 100644 index 00000000000..1b195b78bb4 --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/fr.php @@ -0,0 +1,14 @@ +<?php $TRANSLATIONS = array( +"The php-json module is needed by the many applications for inter communications" => "Le module php-json est requis pour l'inter-communication de nombreux modules.", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "Le module php-curl est requis afin de rapatrier le titre des pages lorsque vous ajoutez un marque-pages.", +"The php-gd module is needed to create thumbnails of your images" => "Le module php-gd est requis afin de permettre la création d'aperçus pour vos images.", +"The php-ldap module is needed connect to your ldap server" => "Le module php-ldap est requis afin de permettre la connexion à votre serveur ldap.", +"The php-zip module is needed download multiple files at once" => "Le module php-zip est requis pour le téléchargement simultané de plusieurs fichiers.", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "Le module php-mb_multibyte est requis pour une gestion correcte des encodages.", +"The php-ctype module is needed validate data." => "Le module php-ctype est requis pour la validation des données.", +"The php-xml module is needed to share files with webdav." => "Le module php-xml est requis pour le partage de fichiers via webdav.", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "La directive allow_url_fopen de votre fichier php.ini doit être à la valeur 1 afin de permettre le rapatriement de la base de connaissance depuis les serveurs OCS.", +"The php-pdo module is needed to store owncloud data into a database." => "le module php-pdo est requis pour le stockage des données ownCloud en base de données.", +"Dependencies status" => "Statut des dépendances", +"Used by :" => "Utilisé par :" +); diff --git a/apps/admin_dependencies_chk/l10n/it.php b/apps/admin_dependencies_chk/l10n/it.php new file mode 100644 index 00000000000..f51286966e1 --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/it.php @@ -0,0 +1,14 @@ +<?php $TRANSLATIONS = array( +"The php-json module is needed by the many applications for inter communications" => "Il modulo php-json è richiesto per l'intercomunicazione di diverse applicazioni", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "Il modulo php-curl è richiesto per scaricare il titolo della pagina quando si aggiunge un segnalibro", +"The php-gd module is needed to create thumbnails of your images" => "Il modulo php-gd è richiesto per creare miniature delle tue immagini", +"The php-ldap module is needed connect to your ldap server" => "Il modulo php-ldap è richiesto per collegarsi a un server ldap", +"The php-zip module is needed download multiple files at once" => "Il modulo php-zip è richiesto per scaricare diversi file contemporaneamente", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "Il modulo php-mb_multibyte è richiesto per gestire correttamente la codifica.", +"The php-ctype module is needed validate data." => "Il modulo php-ctype è richiesto per la validazione dei dati.", +"The php-xml module is needed to share files with webdav." => "Il modulo php-xml è richiesto per condividere i file con webdav.", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "La direttiva allow_url_fopen del tuo php.ini deve essere impostata a 1 per recuperare la base di conoscenza dai server di OCS", +"The php-pdo module is needed to store owncloud data into a database." => "Il modulo php-pdo è richiesto per archiviare i dati di ownCloud in un database.", +"Dependencies status" => "Stato delle dipendenze", +"Used by :" => "Usato da:" +); diff --git a/apps/admin_dependencies_chk/l10n/ja_JP.php b/apps/admin_dependencies_chk/l10n/ja_JP.php new file mode 100644 index 00000000000..0770b92db15 --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/ja_JP.php @@ -0,0 +1,14 @@ +<?php $TRANSLATIONS = array( +"The php-json module is needed by the many applications for inter communications" => "php-jsonモジュールã¯ã‚¢ãƒ—リケーション間ã®å†…部通信ã«å¿…è¦ã§ã™", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "php-curlモジュールã¯ãƒ–ãƒƒã‚¯ãƒžãƒ¼ã‚¯è¿½åŠ æ™‚ã®ãƒšãƒ¼ã‚¸ã‚¿ã‚¤ãƒˆãƒ«å–å¾—ã«å¿…è¦ã§ã™", +"The php-gd module is needed to create thumbnails of your images" => "php-gdモジュールã¯ã‚µãƒ ãƒã‚¤ãƒ«ç”»åƒã®ç”Ÿæˆã«å¿…è¦ã§ã™", +"The php-ldap module is needed connect to your ldap server" => "php-ldapモジュールã¯LDAPサーãƒã¸ã®æŽ¥ç¶šã«å¿…è¦ã§ã™", +"The php-zip module is needed download multiple files at once" => "php-zipモジュールã¯è¤‡æ•°ãƒ•ã‚¡ã‚¤ãƒ«ã®åŒæ™‚ダウンãƒãƒ¼ãƒ‰ã«å¿…è¦ã§ã™", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "php-mb_multibyteモジュールã¯ã‚¨ãƒ³ã‚³ãƒ¼ãƒ‰ã‚’æ£ã—ã扱ã†ãŸã‚ã«å¿…è¦ã§ã™", +"The php-ctype module is needed validate data." => "php-ctypeモジュールã¯ãƒ‡ãƒ¼ã‚¿ã®ãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ã«å¿…è¦ã§ã™", +"The php-xml module is needed to share files with webdav." => "php-xmlモジュールã¯WebDAVã§ã®ãƒ•ã‚¡ã‚¤ãƒ«å…±æœ‰ã«å¿…è¦ã§ã™", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "php.iniã®allow_url_fopenã¯OCSサーãƒã‹ã‚‰çŸ¥è˜ãƒ™ãƒ¼ã‚¹ã‚’å–å¾—ã™ã‚‹ãŸã‚ã«1ã«è¨å®šã—ãªãã¦ã¯ãªã‚Šã¾ã›ã‚“", +"The php-pdo module is needed to store owncloud data into a database." => "php-pdoモジュールã¯ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã«ownCloudã®ãƒ‡ãƒ¼ã‚¿ã‚’æ ¼ç´ã™ã‚‹ãŸã‚ã«å¿…è¦ã§ã™", +"Dependencies status" => "ä¾å˜é–¢ä¿‚ã®çŠ¶æ³", +"Used by :" => "利用先 :" +); diff --git a/apps/admin_dependencies_chk/l10n/lt_LT.php b/apps/admin_dependencies_chk/l10n/lt_LT.php new file mode 100644 index 00000000000..4fed2ab93d5 --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/lt_LT.php @@ -0,0 +1,14 @@ +<?php $TRANSLATIONS = array( +"The php-json module is needed by the many applications for inter communications" => "Php-json modulis yra reikalingas duomenų keitimuisi tarp programų", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "Php-curl modulis automatiÅ¡kai nuskaito tinklapio pavadinimÄ… kuomet iÅ¡saugoma žymelÄ—.", +"The php-gd module is needed to create thumbnails of your images" => "Php-gd modulis yra naudojamas paveikslÄ—lių miniatiÅ«roms kurti.", +"The php-ldap module is needed connect to your ldap server" => "Php-ldap modulis yra reikalingas prisijungimui prie jÅ«sų ldap serverio", +"The php-zip module is needed download multiple files at once" => "Php-zip modulis yra reikalingas kelių failų atsiuntimui iÅ¡ karto.", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "Php-mb_multibyte modulis yra naudojamas apdoroti įvairius teksto kodavimo formatus.", +"The php-ctype module is needed validate data." => "Php-ctype modulis yra reikalingas duomenų tikrinimui.", +"The php-xml module is needed to share files with webdav." => "Php-xml modulis yra reikalingas failų dalinimuisi naudojant webdav.", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "allow_url_fopen direktyva turÄ—tų bÅ«ti nustatyta į \"1\" jei norite automatiÅ¡kai gauti žinių bazÄ—s informacijÄ… iÅ¡ OCS serverių.", +"The php-pdo module is needed to store owncloud data into a database." => "Php-pdo modulis yra reikalingas duomenų saugojimui į owncloud duomenų bazÄ™.", +"Dependencies status" => "PriklausomybÄ—s", +"Used by :" => "Naudojama:" +); diff --git a/apps/admin_dependencies_chk/l10n/pl.php b/apps/admin_dependencies_chk/l10n/pl.php new file mode 100644 index 00000000000..e40d23cb7e1 --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/pl.php @@ -0,0 +1,14 @@ +<?php $TRANSLATIONS = array( +"The php-json module is needed by the many applications for inter communications" => "ModuÅ‚ php-json jest wymagane przez wiele aplikacji do wewnÄ™trznej Å‚Ä…cznoÅ›ci", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "Modude php-curl jest wymagany do pobrania tytuÅ‚u strony podczas dodawania zakÅ‚adki", +"The php-gd module is needed to create thumbnails of your images" => "ModuÅ‚ php-gd jest wymagany do tworzenia miniatury obrazów", +"The php-ldap module is needed connect to your ldap server" => "ModuÅ‚ php-ldap jest wymagany aby poÅ‚Ä…czyć siÄ™ z serwerem ldap", +"The php-zip module is needed download multiple files at once" => "ModuÅ‚ php-zip jest wymagany aby pobrać wiele plików na raz", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "ModuÅ‚ php-mb_multibyte jest wymagany do poprawnego zarzÄ…dzania kodowaniem.", +"The php-ctype module is needed validate data." => "ModuÅ‚ php-ctype jest wymagany do sprawdzania poprawnoÅ›ci danych.", +"The php-xml module is needed to share files with webdav." => "ModuÅ‚ php-xml jest wymagany do udostÄ™pniania plików przy użyciu protokoÅ‚u webdav.", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "Dyrektywy allow_url_fopen użytkownika php.ini powinna być ustawiona na 1 do pobierania bazy wiedzy z serwerów OCS", +"The php-pdo module is needed to store owncloud data into a database." => "ModuÅ‚ php-pdo jest wymagany do przechowywania danych owncloud w bazie danych.", +"Dependencies status" => "Stan zależnoÅ›ci", +"Used by :" => "Używane przez:" +); diff --git a/apps/admin_dependencies_chk/l10n/sl.php b/apps/admin_dependencies_chk/l10n/sl.php new file mode 100644 index 00000000000..0d36aa379ce --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/sl.php @@ -0,0 +1,14 @@ +<?php $TRANSLATIONS = array( +"The php-json module is needed by the many applications for inter communications" => "Modul php-json je potreben za medsebojno komunikacijo veliko aplikacij.", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "Modul php-curl je potreben za pridobivanje naslova strani pri dodajanju zaznamkov.", +"The php-gd module is needed to create thumbnails of your images" => "Modul php-gd je potreben za ustvarjanje sliÄic za predogled.", +"The php-ldap module is needed connect to your ldap server" => "Modul php-ldap je potreben za povezavo z vaÅ¡im ldap strežnikom.", +"The php-zip module is needed download multiple files at once" => "Modul php-zip je potreben za prenaÅ¡anje veÄih datotek hkrati.", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "Modul php-mb_multibyte je potreben za pravilno upravljanje kodiranja.", +"The php-ctype module is needed validate data." => "Modul php-ctype je potreben za preverjanje veljavnosti podatkov.", +"The php-xml module is needed to share files with webdav." => "Modul php-xml je potreben za izmenjavo datotek preko protokola WebDAV.", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "Direktiva allow_url_fopen v vaÅ¡i php.ini datoteki mora biti nastavljena na 1, Äe želite omogoÄiti dostop do zbirke znanja na strežnikih OCS.", +"The php-pdo module is needed to store owncloud data into a database." => "Modul php-pdo je potreben za shranjevanje ownCloud podatkov v podatkovno zbirko.", +"Dependencies status" => "Stanje odvisnosti", +"Used by :" => "Uporablja:" +); diff --git a/apps/admin_dependencies_chk/l10n/sv.php b/apps/admin_dependencies_chk/l10n/sv.php new file mode 100644 index 00000000000..07868f3c03c --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/sv.php @@ -0,0 +1,14 @@ +<?php $TRANSLATIONS = array( +"The php-json module is needed by the many applications for inter communications" => "Modulen php-json behövs av mÃ¥nga applikationer som interagerar.", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "Modulen php-curl behövs för att hämta sidans titel när du lägger till bokmärken.", +"The php-gd module is needed to create thumbnails of your images" => "Modulen php-gd behövs för att skapa miniatyrer av dina bilder.", +"The php-ldap module is needed connect to your ldap server" => "Modulen php-ldap behövs för att ansluta mot din ldapserver.", +"The php-zip module is needed download multiple files at once" => "Modulen php-zip behövs för att kunna ladda ner flera filer pÃ¥ en gÃ¥ng.", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "Modulen php-mb_multibyte behövs för att hantera korrekt teckenkodning.", +"The php-ctype module is needed validate data." => "Modulen php-ctype behövs för att validera data.", +"The php-xml module is needed to share files with webdav." => "Modulen php-xml behövs för att kunna dela filer med webdav.", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "Direktivet allow_url_fopen i php.ini bör sättas till 1 för att kunna hämta kunskapsbasen frÃ¥n OCS-servrar.", +"The php-pdo module is needed to store owncloud data into a database." => "Modulen php-pdo behövs för att kunna lagra ownCloud data i en databas.", +"Dependencies status" => "Beroenden status", +"Used by :" => "Används av:" +); diff --git a/apps/admin_dependencies_chk/l10n/th_TH.php b/apps/admin_dependencies_chk/l10n/th_TH.php new file mode 100644 index 00000000000..01d5c36366d --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/th_TH.php @@ -0,0 +1,14 @@ +<?php $TRANSLATIONS = array( +"The php-json module is needed by the many applications for inter communications" => "โมดูล php-json จำเป็นต้à¸à¸‡à¹ƒà¸Šà¹‰à¸ªà¸³à¸«à¸£à¸±à¸šà¹à¸à¸žà¸žà¸¥à¸´à¹€à¸„ชั่นหลายๆตัวเพื่à¸à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸à¸¡à¸•à¹ˆà¸à¸ªà¸²à¸à¸¥", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "โมดูล php-curl จำเป็นต้à¸à¸‡à¹ƒà¸Šà¹‰à¸ªà¸³à¸«à¸£à¸±à¸šà¸”ึงข้à¸à¸¡à¸¹à¸¥à¸Šà¸·à¹ˆà¸à¸«à¸±à¸§à¹€à¸§à¹‡à¸šà¹€à¸¡à¸·à¹ˆà¸à¹€à¸žà¸´à¹ˆà¸¡à¹€à¸‚้าไปยังรายà¸à¸²à¸£à¹‚ปรด", +"The php-gd module is needed to create thumbnails of your images" => "โมดูล php-gd จำเป็นต้à¸à¸‡à¹ƒà¸Šà¹‰à¸ªà¸³à¸«à¸£à¸±à¸šà¸ªà¸£à¹‰à¸²à¸‡à¸£à¸¹à¸›à¸ าพขนาดย่à¸à¸‚à¸à¸‡à¸£à¸¹à¸›à¸ าพขà¸à¸‡à¸„ุณ", +"The php-ldap module is needed connect to your ldap server" => "โมดูล php-ldap จำเป็นต้à¸à¸‡à¹ƒà¸Šà¹‰à¸ªà¸³à¸«à¸£à¸±à¸šà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸à¸¡à¸•à¹ˆà¸à¸à¸±à¸šà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ ldap ขà¸à¸‡à¸„ุณ", +"The php-zip module is needed download multiple files at once" => "โมดูล php-zip จำเป็นต้à¸à¸‡à¹ƒà¸Šà¹‰à¸ªà¸³à¸«à¸£à¸±à¸šà¸”าวน์โหลดไฟล์พร้à¸à¸¡à¸à¸±à¸™à¸«à¸¥à¸²à¸¢à¹†à¹„ฟล์ในครั้งเดียว", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "โมดูล php-mb_multibyte จำเป็นต้à¸à¸‡à¹ƒà¸Šà¹‰à¸ªà¸³à¸«à¸£à¸±à¸šà¸à¸²à¸£à¸ˆà¸±à¸”à¸à¸²à¸£à¸à¸²à¸£à¹à¸›à¸¥à¸‡à¸£à¸«à¸±à¸ªà¹„ฟล์à¸à¸¢à¹ˆà¸²à¸‡à¸–ูà¸à¸•à¹‰à¸à¸‡", +"The php-ctype module is needed validate data." => "โมดูล php-ctype จำเป็นต้à¸à¸‡à¹ƒà¸Šà¹‰à¸ªà¸³à¸«à¸£à¸±à¸šà¸•à¸£à¸§à¸ˆà¸ªà¸à¸šà¸„วามถูà¸à¸•à¹‰à¸à¸‡à¸‚à¸à¸‡à¸‚้à¸à¸¡à¸¹à¸¥", +"The php-xml module is needed to share files with webdav." => "โมดูล php-xml จำเป็นต้à¸à¸‡à¹ƒà¸Šà¹‰à¸ªà¸³à¸«à¸£à¸±à¸šà¹à¸Šà¸£à¹Œà¹„ฟล์ด้วย webdav", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "คำสั่ง allow_url_fopen ที่à¸à¸¢à¸¹à¹ˆà¹ƒà¸™à¹„ฟล์ php.ini ขà¸à¸‡à¸„ุณ ควรà¸à¸³à¸«à¸™à¸”เป็น 1 เพื่à¸à¸”ึงข้à¸à¸¡à¸¹à¸¥à¸‚à¸à¸‡à¸à¸²à¸™à¸„วามรู้ต่างๆจาà¸à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¸‚à¸à¸‡ OCS", +"The php-pdo module is needed to store owncloud data into a database." => "โมดูล php-pdo จำเป็นต้à¸à¸‡à¹ƒà¸Šà¹‰à¸ªà¸³à¸«à¸£à¸±à¸šà¸ˆà¸±à¸”เà¸à¹‡à¸šà¸‚้à¸à¸¡à¸¹à¸¥à¹ƒà¸™ owncloud เข้าไปไว้ยังà¸à¸²à¸™à¸‚้à¸à¸¡à¸¹à¸¥", +"Dependencies status" => "สถานะà¸à¸²à¸£à¸à¹‰à¸²à¸‡à¸à¸´à¸‡", +"Used by :" => "ใช้งานโดย:" +); diff --git a/apps/admin_migrate/l10n/.gitkeep b/apps/admin_migrate/l10n/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/apps/admin_migrate/l10n/.gitkeep diff --git a/apps/admin_migrate/l10n/ca.php b/apps/admin_migrate/l10n/ca.php new file mode 100644 index 00000000000..8743b397601 --- /dev/null +++ b/apps/admin_migrate/l10n/ca.php @@ -0,0 +1,5 @@ +<?php $TRANSLATIONS = array( +"Export this ownCloud instance" => "Exporta aquesta instà ncia de ownCloud", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Això crearà un fitxer comprimit amb les dades d'aquesta instà ncia ownCloud.\n Escolliu el tipus d'exportació:", +"Export" => "Exporta" +); diff --git a/apps/admin_migrate/l10n/cs_CZ.php b/apps/admin_migrate/l10n/cs_CZ.php new file mode 100644 index 00000000000..0dc1a61d5d8 --- /dev/null +++ b/apps/admin_migrate/l10n/cs_CZ.php @@ -0,0 +1,5 @@ +<?php $TRANSLATIONS = array( +"Export this ownCloud instance" => "Export této instance ownCloud", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Bude vytvoÅ™en komprimovaný soubor obsahujÃcà data této instance ownCloud.⎠Zvolte typ exportu:", +"Export" => "Export" +); diff --git a/apps/admin_migrate/l10n/da.php b/apps/admin_migrate/l10n/da.php new file mode 100644 index 00000000000..a33635cdc14 --- /dev/null +++ b/apps/admin_migrate/l10n/da.php @@ -0,0 +1,4 @@ +<?php $TRANSLATIONS = array( +"Export this ownCloud instance" => "Eksporter ownCloud instans", +"Export" => "Eksporter" +); diff --git a/apps/admin_migrate/l10n/de.php b/apps/admin_migrate/l10n/de.php new file mode 100644 index 00000000000..d42179b75ad --- /dev/null +++ b/apps/admin_migrate/l10n/de.php @@ -0,0 +1,5 @@ +<?php $TRANSLATIONS = array( +"Export this ownCloud instance" => "Diese ownCloud-Instanz exportieren.", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Dies wird eine komprimierte Datei erzeugen, welche die Daten dieser ownCloud-Instanz enthält.\n Bitte wählen Sie den Exporttyp:", +"Export" => "Exportieren" +); diff --git a/apps/admin_migrate/l10n/el.php b/apps/admin_migrate/l10n/el.php new file mode 100644 index 00000000000..95034c46a4c --- /dev/null +++ b/apps/admin_migrate/l10n/el.php @@ -0,0 +1,4 @@ +<?php $TRANSLATIONS = array( +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Αυτό θα δημιουÏγήσει Îνα συμπιεσμÎνο αÏχείο που θα πεÏιÎχει τα δεδομÎνα από αυτό το ownCloud.\n ΠαÏακαλώ επιλÎξτε τον Ï„Ïπο εξαγωγής:", +"Export" => "Εξαγωγή" +); diff --git a/apps/admin_migrate/l10n/eo.php b/apps/admin_migrate/l10n/eo.php new file mode 100644 index 00000000000..a37be76425b --- /dev/null +++ b/apps/admin_migrate/l10n/eo.php @@ -0,0 +1,5 @@ +<?php $TRANSLATIONS = array( +"Export this ownCloud instance" => "Malenporti ĉi tiun aperon de ownCloud", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Ĉi tio kreos densigitan dosieron, kiu enhavos la datumojn de ĉi tiu apero de ownCloud.\nBonvolu elekti la tipon de malenportado:", +"Export" => "Malenporti" +); diff --git a/apps/admin_migrate/l10n/es.php b/apps/admin_migrate/l10n/es.php new file mode 100644 index 00000000000..cb6699b1d94 --- /dev/null +++ b/apps/admin_migrate/l10n/es.php @@ -0,0 +1,5 @@ +<?php $TRANSLATIONS = array( +"Export this ownCloud instance" => "Exportar esta instancia de ownCloud", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Se creará un archivo comprimido que contendrá los datos de esta instancia de owncloud.\n Por favor elegir el tipo de exportación:", +"Export" => "Exportar" +); diff --git a/apps/admin_migrate/l10n/et_EE.php b/apps/admin_migrate/l10n/et_EE.php new file mode 100644 index 00000000000..ee92f9fe217 --- /dev/null +++ b/apps/admin_migrate/l10n/et_EE.php @@ -0,0 +1,5 @@ +<?php $TRANSLATIONS = array( +"Export this ownCloud instance" => "Ekspordi see ownCloudi paigaldus", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "See loob pakitud faili, milles on sinu owncloudi paigalduse andmed.\n Palun vali eksporditava faili tüüp:", +"Export" => "Ekspordi" +); diff --git a/apps/admin_migrate/l10n/fi_FI.php b/apps/admin_migrate/l10n/fi_FI.php new file mode 100644 index 00000000000..2484eab7d33 --- /dev/null +++ b/apps/admin_migrate/l10n/fi_FI.php @@ -0,0 +1,4 @@ +<?php $TRANSLATIONS = array( +"Export this ownCloud instance" => "Vie tämä ownCloud-istanssi", +"Export" => "Vie" +); diff --git a/apps/admin_migrate/l10n/fr.php b/apps/admin_migrate/l10n/fr.php new file mode 100644 index 00000000000..0cf0a444b0e --- /dev/null +++ b/apps/admin_migrate/l10n/fr.php @@ -0,0 +1,5 @@ +<?php $TRANSLATIONS = array( +"Export this ownCloud instance" => "Exporter cette instance ownCloud", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Ceci va créer une archive compressée contenant les données de cette instance ownCloud.\n Veuillez choisir le type d'export :", +"Export" => "Exporter" +); diff --git a/apps/admin_migrate/l10n/gl.php b/apps/admin_migrate/l10n/gl.php new file mode 100644 index 00000000000..9d18e134938 --- /dev/null +++ b/apps/admin_migrate/l10n/gl.php @@ -0,0 +1,5 @@ +<?php $TRANSLATIONS = array( +"Export this ownCloud instance" => "Exporta esta instancia de ownCloud", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Esto creará un ficheiro comprimido que contén os datos de esta instancia de ownCloud.\nPor favor escolla o modo de exportación:", +"Export" => "Exportar" +); diff --git a/apps/admin_migrate/l10n/it.php b/apps/admin_migrate/l10n/it.php new file mode 100644 index 00000000000..94ba191ba6e --- /dev/null +++ b/apps/admin_migrate/l10n/it.php @@ -0,0 +1,5 @@ +<?php $TRANSLATIONS = array( +"Export this ownCloud instance" => "Esporta questa istanza di ownCloud", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Questa operazione creerà un file compresso che contiene i dati dell'istanza di ownCloud. Scegli il tipo di esportazione:", +"Export" => "Esporta" +); diff --git a/apps/admin_migrate/l10n/ja_JP.php b/apps/admin_migrate/l10n/ja_JP.php new file mode 100644 index 00000000000..f8e5944a6a4 --- /dev/null +++ b/apps/admin_migrate/l10n/ja_JP.php @@ -0,0 +1,5 @@ +<?php $TRANSLATIONS = array( +"Export this ownCloud instance" => "ownCloudをエクスãƒãƒ¼ãƒˆ", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "ã“ã®ownCloudã®ãƒ‡ãƒ¼ã‚¿ã‚’å«ã‚€åœ§ç¸®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’生æˆã—ã¾ã™ã€‚\nエクスãƒãƒ¼ãƒˆã®ç¨®é¡žã‚’é¸æŠžã—ã¦ãã ã•ã„:", +"Export" => "エクスãƒãƒ¼ãƒˆ" +); diff --git a/apps/admin_migrate/l10n/lt_LT.php b/apps/admin_migrate/l10n/lt_LT.php new file mode 100644 index 00000000000..f78263da75f --- /dev/null +++ b/apps/admin_migrate/l10n/lt_LT.php @@ -0,0 +1,5 @@ +<?php $TRANSLATIONS = array( +"Export this ownCloud instance" => "Eksportuoti Å¡iÄ… ownCloud instaliacijÄ…", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Bus sukurtas archyvas su visais owncloud duomenimis ir failais.\n Pasirinkite eksportavimo tipÄ…:", +"Export" => "Eksportuoti" +); diff --git a/apps/admin_migrate/l10n/nb_NO.php b/apps/admin_migrate/l10n/nb_NO.php new file mode 100644 index 00000000000..31f4c030bd3 --- /dev/null +++ b/apps/admin_migrate/l10n/nb_NO.php @@ -0,0 +1,5 @@ +<?php $TRANSLATIONS = array( +"Export this ownCloud instance" => "Eksporter denne ownCloud forekomsten", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Dette vil opprette en komprimert fil som inneholder dataene fra denne ownCloud forekomsten.⎠Vennligst velg eksporttype:", +"Export" => "Eksport" +); diff --git a/apps/admin_migrate/l10n/pl.php b/apps/admin_migrate/l10n/pl.php new file mode 100644 index 00000000000..292601daa2b --- /dev/null +++ b/apps/admin_migrate/l10n/pl.php @@ -0,0 +1,5 @@ +<?php $TRANSLATIONS = array( +"Export this ownCloud instance" => "Eksportuj instancjÄ™ ownCloud", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Spowoduje to utworzenie pliku skompresowanego, który zawiera dane tej instancji ownCloud.⎠proszÄ™ wybrać typ eksportu:", +"Export" => "Eksport" +); diff --git a/apps/admin_migrate/l10n/sl.php b/apps/admin_migrate/l10n/sl.php new file mode 100644 index 00000000000..b8d1118bfe2 --- /dev/null +++ b/apps/admin_migrate/l10n/sl.php @@ -0,0 +1,5 @@ +<?php $TRANSLATIONS = array( +"Export this ownCloud instance" => "Izvozi to ownCloud namestitev", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Ustvarjena bo stisnjena datoteka s podatki te ownCloud namestitve.\n Prosimo, Äe izberete vrsto izvoza:", +"Export" => "Izvozi" +); diff --git a/apps/admin_migrate/l10n/sv.php b/apps/admin_migrate/l10n/sv.php new file mode 100644 index 00000000000..57866e897e6 --- /dev/null +++ b/apps/admin_migrate/l10n/sv.php @@ -0,0 +1,5 @@ +<?php $TRANSLATIONS = array( +"Export this ownCloud instance" => "Exportera denna instans av ownCloud", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Detta kommer att skapa en komprimerad fil som innehÃ¥ller all data frÃ¥n denna instans av ownCloud.\n Välj exporttyp:", +"Export" => "Exportera" +); diff --git a/apps/admin_migrate/l10n/th_TH.php b/apps/admin_migrate/l10n/th_TH.php new file mode 100644 index 00000000000..9dfaca15b5e --- /dev/null +++ b/apps/admin_migrate/l10n/th_TH.php @@ -0,0 +1,5 @@ +<?php $TRANSLATIONS = array( +"Export this ownCloud instance" => "ส่งà¸à¸à¸à¸‚้à¸à¸¡à¸¹à¸¥à¸„่าสมมุติขà¸à¸‡ ownCloud นี้", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "ส่วนนี้จะเป็นà¸à¸²à¸£à¸ªà¸£à¹‰à¸²à¸‡à¹„ฟล์บีบà¸à¸±à¸”ที่บรรจุข้à¸à¸¡à¸¹à¸¥à¸„่าสมมุติขà¸à¸‡ ownCloud.\n à¸à¸£à¸¸à¸“าเลืà¸à¸à¸Šà¸™à¸´à¸”ขà¸à¸‡à¸à¸²à¸£à¸ªà¹ˆà¸‡à¸à¸à¸à¸‚้à¸à¸¡à¸¹à¸¥:", +"Export" => "ส่งà¸à¸à¸" +); diff --git a/apps/bookmarks/ajax/addBookmark.php b/apps/bookmarks/ajax/addBookmark.php index b4d0f33d721..baf3a288c17 100644 --- a/apps/bookmarks/ajax/addBookmark.php +++ b/apps/bookmarks/ajax/addBookmark.php @@ -28,9 +28,10 @@ $RUNTIME_NOSETUPFS=true; // Check if we are a user OCP\JSON::checkLoggedIn(); -OCP\JSON::checkAppEnabled('bookmarks'); OCP\JSON::callCheck(); -require_once(OC::$APPSROOT . '/apps/bookmarks/bookmarksHelper.php'); +OCP\JSON::checkAppEnabled('bookmarks'); + +require_once(OC_App::getAppPath('bookmarks').'/bookmarksHelper.php'); $id = addBookmark($_POST['url'], $_POST['title'], $_POST['tags']); -OCP\JSON::success(array('data' => $id));
\ No newline at end of file +OCP\JSON::success(array('data' => $id)); diff --git a/apps/bookmarks/ajax/delBookmark.php b/apps/bookmarks/ajax/delBookmark.php index 140da2a37d1..26437ea0c8c 100644 --- a/apps/bookmarks/ajax/delBookmark.php +++ b/apps/bookmarks/ajax/delBookmark.php @@ -21,13 +21,10 @@ * */ -//no apps or filesystem -$RUNTIME_NOSETUPFS=true; - - - // Check if we are a user OCP\JSON::checkLoggedIn(); +OCP\JSON::callCheck(); + OCP\JSON::checkAppEnabled('bookmarks'); OCP\JSON::callCheck(); diff --git a/apps/bookmarks/ajax/editBookmark.php b/apps/bookmarks/ajax/editBookmark.php index 8c1b19cf0c6..617021e412b 100644 --- a/apps/bookmarks/ajax/editBookmark.php +++ b/apps/bookmarks/ajax/editBookmark.php @@ -21,16 +21,12 @@ * */ -//no apps or filesystem -$RUNTIME_NOSETUPFS=true; - - - // Check if we are a user OCP\JSON::checkLoggedIn(); -OCP\JSON::checkAppEnabled('bookmarks'); OCP\JSON::callCheck(); +OCP\JSON::checkAppEnabled('bookmarks'); + $CONFIG_DBTYPE = OCP\Config::getSystemValue( "dbtype", "sqlite" ); if( $CONFIG_DBTYPE == 'sqlite' or $CONFIG_DBTYPE == 'sqlite3' ){ $_ut = "strftime('%s','now')"; diff --git a/apps/bookmarks/ajax/recordClick.php b/apps/bookmarks/ajax/recordClick.php index 332d58262ee..785056dc11c 100644 --- a/apps/bookmarks/ajax/recordClick.php +++ b/apps/bookmarks/ajax/recordClick.php @@ -21,11 +21,6 @@ * */ -//no apps or filesystem -$RUNTIME_NOSETUPFS=true; - - - // Check if we are a user OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('bookmarks'); diff --git a/apps/bookmarks/ajax/updateList.php b/apps/bookmarks/ajax/updateList.php index 4de2475d067..cf9a2cf9183 100644 --- a/apps/bookmarks/ajax/updateList.php +++ b/apps/bookmarks/ajax/updateList.php @@ -22,11 +22,6 @@ * */ -//no apps or filesystem -$RUNTIME_NOSETUPFS=true; - - - // Check if we are a user OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('bookmarks'); diff --git a/apps/bookmarks/appinfo/app.php b/apps/bookmarks/appinfo/app.php index 8a8f443891c..f4bca9df700 100644 --- a/apps/bookmarks/appinfo/app.php +++ b/apps/bookmarks/appinfo/app.php @@ -10,8 +10,6 @@ OC::$CLASSPATH['OC_Bookmarks_Bookmarks'] = 'apps/bookmarks/lib/bookmarks.php'; OC::$CLASSPATH['OC_Search_Provider_Bookmarks'] = 'apps/bookmarks/lib/search.php'; -OCP\App::register( array( 'order' => 70, 'id' => 'bookmark', 'name' => 'Bookmarks' )); - $l = new OC_l10n('bookmarks'); OCP\App::addNavigationEntry( array( 'id' => 'bookmarks_index', 'order' => 70, 'href' => OCP\Util::linkTo( 'bookmarks', 'index.php' ), 'icon' => OCP\Util::imagePath( 'bookmarks', 'bookmarks.png' ), 'name' => $l->t('Bookmarks'))); diff --git a/apps/bookmarks/bookmarksHelper.php b/apps/bookmarks/bookmarksHelper.php index cb0ca06c728..988042fc0e5 100644 --- a/apps/bookmarks/bookmarksHelper.php +++ b/apps/bookmarks/bookmarksHelper.php @@ -90,7 +90,8 @@ function addBookmark($url, $title, $tags='') { if(empty($title)) { $metadata = getURLMetadata($url); - $title = $metadata['title']; + if(isset($metadata['title'])) // Check for problems fetching the title + $title = $metadata['title']; } if(empty($title)) { diff --git a/apps/bookmarks/img/bookmarks.png b/apps/bookmarks/img/bookmarks.png Binary files differindex b92e4f50a42..3e8eed2380f 100644 --- a/apps/bookmarks/img/bookmarks.png +++ b/apps/bookmarks/img/bookmarks.png diff --git a/apps/bookmarks/l10n/bg_BG.php b/apps/bookmarks/l10n/bg_BG.php new file mode 100644 index 00000000000..04d731b1074 --- /dev/null +++ b/apps/bookmarks/l10n/bg_BG.php @@ -0,0 +1,12 @@ +<?php $TRANSLATIONS = array( +"Bookmarks" => "Отметки", +"unnamed" => "неозаглавено", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "Завлачете това в лентата Ñ Ð¾Ñ‚Ð¼ÐµÑ‚ÐºÐ¸ на браузъра Ñи и го натиÑкайте, когато иÑкате да отметнете бързо нÑÐºÐ¾Ñ Ñтраница:", +"Read later" => "ОтмÑтане", +"Address" => "ÐдреÑ", +"Title" => "Заглавие", +"Tags" => "Етикети", +"Save bookmark" => "Ð—Ð°Ð¿Ð¸Ñ Ð½Ð° отметката", +"You have no bookmarks" => "ÐÑмате отметки", +"Bookmarklet <br />" => "Бутон за отметки <br />" +); diff --git a/apps/bookmarks/l10n/ca.php b/apps/bookmarks/l10n/ca.php new file mode 100644 index 00000000000..cf90d9a8874 --- /dev/null +++ b/apps/bookmarks/l10n/ca.php @@ -0,0 +1,12 @@ +<?php $TRANSLATIONS = array( +"Bookmarks" => "Adreces d'interès", +"unnamed" => "sense nom", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "Arrossegueu-ho al navegador i feu-hi un clic quan volgueu marcar rà pidament una adreça d'interès:", +"Read later" => "Llegeix més tard", +"Address" => "Adreça", +"Title" => "TÃtol", +"Tags" => "Etiquetes", +"Save bookmark" => "Desa l'adreça d'interès", +"You have no bookmarks" => "No teniu adreces d'interès", +"Bookmarklet <br />" => "Bookmarklet <br />" +); diff --git a/apps/bookmarks/l10n/cs_CZ.php b/apps/bookmarks/l10n/cs_CZ.php new file mode 100644 index 00000000000..2251969a26c --- /dev/null +++ b/apps/bookmarks/l10n/cs_CZ.php @@ -0,0 +1,12 @@ +<?php $TRANSLATIONS = array( +"Bookmarks" => "Záložky", +"unnamed" => "nepojmenovaný", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "PÅ™etáhnÄ›te do VaÅ¡eho prohlÞeÄe a klinÄ›te, pokud si pÅ™ejete rychle uložit stranu do záložek:", +"Read later" => "PÅ™eÄÃst pozdÄ›ji", +"Address" => "Adresa", +"Title" => "Název", +"Tags" => "Tagy", +"Save bookmark" => "Uložit záložku", +"You have no bookmarks" => "Nemáte žádné záložky", +"Bookmarklet <br />" => "Záložky <br />" +); diff --git a/apps/bookmarks/l10n/de.php b/apps/bookmarks/l10n/de.php new file mode 100644 index 00000000000..14a54f1ccee --- /dev/null +++ b/apps/bookmarks/l10n/de.php @@ -0,0 +1,12 @@ +<?php $TRANSLATIONS = array( +"Bookmarks" => "Lesezeichen", +"unnamed" => "unbenannt", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "Ziehen Sie dies zu Ihren Browser-Lesezeichen und klicken Sie darauf, wenn Sie eine Website schnell den Lesezeichen hinzufügen wollen.", +"Read later" => "Später lesen", +"Address" => "Adresse", +"Title" => "Titel", +"Tags" => "Tags", +"Save bookmark" => "Lesezeichen speichern", +"You have no bookmarks" => "Sie haben keine Lesezeichen", +"Bookmarklet <br />" => "Bookmarklet <br />" +); diff --git a/apps/bookmarks/l10n/el.php b/apps/bookmarks/l10n/el.php new file mode 100644 index 00000000000..f282a1bbf85 --- /dev/null +++ b/apps/bookmarks/l10n/el.php @@ -0,0 +1,12 @@ +<?php $TRANSLATIONS = array( +"Bookmarks" => "Σελιδοδείκτες", +"unnamed" => "ανώνυμο", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "ΣÏÏετε αυτό στους σελιδοδείκτες του πεÏιηγητή σας και κάντε κλικ επάνω του, όταν θÎλετε να Ï€ÏοσθÎσετε σÏντομα μια ιστοσελίδα ως σελιδοδείκτη:", +"Read later" => "Ανάγνωση αÏγότεÏα", +"Address" => "ΔιεÏθυνση", +"Title" => "Τίτλος", +"Tags" => "ΕτικÎτες", +"Save bookmark" => "Αποθήκευση σελιδοδείκτη", +"You have no bookmarks" => "Δεν Îχετε σελιδοδείκτες", +"Bookmarklet <br />" => "ΕφαÏμογίδιο Σελιδοδεικτών <br />" +); diff --git a/apps/bookmarks/l10n/eo.php b/apps/bookmarks/l10n/eo.php new file mode 100644 index 00000000000..808cda8a041 --- /dev/null +++ b/apps/bookmarks/l10n/eo.php @@ -0,0 +1,11 @@ +<?php $TRANSLATIONS = array( +"Bookmarks" => "Legosignoj", +"unnamed" => "nenomita", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "Åœovu tion ĉi al la legosignoj de via TTT-legilo kaj klaku Äin, se vi volas rapide legosignigi TTT-paÄon:", +"Read later" => "Legi poste", +"Address" => "Adreso", +"Title" => "Titolo", +"Tags" => "Etikedoj", +"Save bookmark" => "Konservi legosignon", +"You have no bookmarks" => "Vi havas neniun legosignon" +); diff --git a/apps/bookmarks/l10n/es.php b/apps/bookmarks/l10n/es.php new file mode 100644 index 00000000000..071b0dda707 --- /dev/null +++ b/apps/bookmarks/l10n/es.php @@ -0,0 +1,12 @@ +<?php $TRANSLATIONS = array( +"Bookmarks" => "Marcadores", +"unnamed" => "sin nombre", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "Arrastra desde aquà a los marcadores de tu navegador, y haz clic cuando quieras marcar una página web rápidamente:", +"Read later" => "Leer después", +"Address" => "Dirección", +"Title" => "TÃtulo", +"Tags" => "Etiquetas", +"Save bookmark" => "Guardar marcador", +"You have no bookmarks" => "No tienes marcadores", +"Bookmarklet <br />" => "Bookmarklet <br />" +); diff --git a/apps/bookmarks/l10n/et_EE.php b/apps/bookmarks/l10n/et_EE.php new file mode 100644 index 00000000000..da9e4d92a6f --- /dev/null +++ b/apps/bookmarks/l10n/et_EE.php @@ -0,0 +1,10 @@ +<?php $TRANSLATIONS = array( +"Bookmarks" => "Järjehoidjad", +"unnamed" => "nimetu", +"Read later" => "Loe hiljem", +"Address" => "Aadress", +"Title" => "Pealkiri", +"Tags" => "Sildid", +"Save bookmark" => "Salvesta järjehoidja", +"You have no bookmarks" => "Sul pole järjehoidjaid" +); diff --git a/apps/bookmarks/l10n/fa.php b/apps/bookmarks/l10n/fa.php new file mode 100644 index 00000000000..b46ce911d41 --- /dev/null +++ b/apps/bookmarks/l10n/fa.php @@ -0,0 +1,8 @@ +<?php $TRANSLATIONS = array( +"Bookmarks" => "نشانک‌ها", +"unnamed" => "بدون‌نام", +"Address" => "آدرس", +"Title" => "عنوان", +"Save bookmark" => "ذخیره نشانک", +"You have no bookmarks" => "شما هیچ نشانکی ندارید" +); diff --git a/apps/bookmarks/l10n/fi_FI.php b/apps/bookmarks/l10n/fi_FI.php new file mode 100644 index 00000000000..c814747411b --- /dev/null +++ b/apps/bookmarks/l10n/fi_FI.php @@ -0,0 +1,12 @@ +<?php $TRANSLATIONS = array( +"Bookmarks" => "Kirjanmerkit", +"unnamed" => "nimetön", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "Vedä tämä selaimesi kirjanmerkkipalkkiin ja napsauta sitä, kun haluat lisätä kirjanmerkin nopeasti:", +"Read later" => "Lue myöhemmin", +"Address" => "Osoite", +"Title" => "Otsikko", +"Tags" => "Tunnisteet", +"Save bookmark" => "Tallenna kirjanmerkki", +"You have no bookmarks" => "Sinulla ei ole kirjanmerkkejä", +"Bookmarklet <br />" => "Kirjanmerkitsin <br />" +); diff --git a/apps/bookmarks/l10n/fr.php b/apps/bookmarks/l10n/fr.php new file mode 100644 index 00000000000..508c82369fb --- /dev/null +++ b/apps/bookmarks/l10n/fr.php @@ -0,0 +1,12 @@ +<?php $TRANSLATIONS = array( +"Bookmarks" => "Favoris", +"unnamed" => "sans titre", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "Glissez ceci dans les favoris de votre navigateur, et cliquer dessus lorsque vous souhaitez ajouter la page en cours à vos marques-pages :", +"Read later" => "Lire plus tard", +"Address" => "Adresse", +"Title" => "Titre", +"Tags" => "Étiquettes", +"Save bookmark" => "Sauvegarder le favori", +"You have no bookmarks" => "Vous n'avez aucun favori", +"Bookmarklet <br />" => "Gestionnaire de favoris <br />" +); diff --git a/apps/bookmarks/l10n/it.php b/apps/bookmarks/l10n/it.php new file mode 100644 index 00000000000..862d75bde45 --- /dev/null +++ b/apps/bookmarks/l10n/it.php @@ -0,0 +1,12 @@ +<?php $TRANSLATIONS = array( +"Bookmarks" => "Segnalibri", +"unnamed" => "senza nome", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "Quando vuoi creare rapidamente un segnalibro, trascinalo sui segnalibri del browser e fai clic su di esso:", +"Read later" => "Leggi dopo", +"Address" => "Indirizzo", +"Title" => "Titolo", +"Tags" => "Tag", +"Save bookmark" => "Salva segnalibro", +"You have no bookmarks" => "Non hai segnalibri", +"Bookmarklet <br />" => "Bookmarklet <br />" +); diff --git a/apps/bookmarks/l10n/lt_LT.php b/apps/bookmarks/l10n/lt_LT.php new file mode 100644 index 00000000000..58faddc2331 --- /dev/null +++ b/apps/bookmarks/l10n/lt_LT.php @@ -0,0 +1,3 @@ +<?php $TRANSLATIONS = array( +"unnamed" => "be pavadinimo" +); diff --git a/apps/bookmarks/l10n/nb_NO.php b/apps/bookmarks/l10n/nb_NO.php new file mode 100644 index 00000000000..12e63887d24 --- /dev/null +++ b/apps/bookmarks/l10n/nb_NO.php @@ -0,0 +1,11 @@ +<?php $TRANSLATIONS = array( +"Bookmarks" => "Bokmerker", +"unnamed" => "uten navn", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "Dra denne over din nettlesers bokmerker og klikk den, hvis du ønsker Ã¥ hurtig legge til bokmerke for en nettside", +"Read later" => "Les senere", +"Address" => "Adresse", +"Title" => "Tittel", +"Tags" => "Etikett", +"Save bookmark" => "Lagre bokmerke", +"You have no bookmarks" => "Du har ingen bokmerker" +); diff --git a/apps/bookmarks/l10n/sl.php b/apps/bookmarks/l10n/sl.php new file mode 100644 index 00000000000..32a41629082 --- /dev/null +++ b/apps/bookmarks/l10n/sl.php @@ -0,0 +1,12 @@ +<?php $TRANSLATIONS = array( +"Bookmarks" => "Zaznamki", +"unnamed" => "neimenovano", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "Povlecite to povezavo med zaznamke v vaÅ¡em brskalniku in jo, ko želite ustvariti zaznamek trenutne strani, preprosto kliknite:", +"Read later" => "Preberi kasneje", +"Address" => "Naslov", +"Title" => "Ime", +"Tags" => "Oznake", +"Save bookmark" => "Shrani zaznamek", +"You have no bookmarks" => "Nimate zaznamkov", +"Bookmarklet <br />" => "Bookmarklet <br />" +); diff --git a/apps/bookmarks/l10n/sv.php b/apps/bookmarks/l10n/sv.php new file mode 100644 index 00000000000..689bd452f12 --- /dev/null +++ b/apps/bookmarks/l10n/sv.php @@ -0,0 +1,12 @@ +<?php $TRANSLATIONS = array( +"Bookmarks" => "Bokmärken", +"unnamed" => "namnlös", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "Dra till din webbläsares bokmärken och klicka pÃ¥ det när du vill bokmärka en webbsida snabbt:", +"Read later" => "Läs senare", +"Address" => "Adress", +"Title" => "Titel", +"Tags" => "Taggar", +"Save bookmark" => "Spara bokmärke", +"You have no bookmarks" => "Du har inga bokmärken", +"Bookmarklet <br />" => "Skriptbokmärke <br />" +); diff --git a/apps/bookmarks/l10n/th_TH.php b/apps/bookmarks/l10n/th_TH.php new file mode 100644 index 00000000000..64a148efabc --- /dev/null +++ b/apps/bookmarks/l10n/th_TH.php @@ -0,0 +1,12 @@ +<?php $TRANSLATIONS = array( +"Bookmarks" => "รายà¸à¸²à¸£à¹‚ปรด", +"unnamed" => "ยังไม่มีชื่à¸", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "ลาà¸à¸ªà¸´à¹ˆà¸‡à¸™à¸µà¹‰à¹„ปไว้ที่รายà¸à¸²à¸£à¹‚ปรดในโปรà¹à¸à¸£à¸¡à¸šà¸£à¸²à¸§à¹€à¸‹à¸à¸£à¹Œà¸‚à¸à¸‡à¸„ุณ à¹à¸¥à¹‰à¸§à¸„ลิà¸à¸—ี่นั่น, เมื่à¸à¸„ุณต้à¸à¸‡à¸à¸²à¸£à¹€à¸à¹‡à¸šà¸«à¸™à¹‰à¸²à¹€à¸§à¹‡à¸šà¹€à¸žà¸ˆà¹€à¸‚้าไปไว้ในรายà¸à¸²à¸£à¹‚ปรดà¸à¸¢à¹ˆà¸²à¸‡à¸£à¸§à¸”เร็ว", +"Read later" => "à¸à¹ˆà¸²à¸™à¸ ายหลัง", +"Address" => "ที่à¸à¸¢à¸¹à¹ˆ", +"Title" => "ชื่à¸", +"Tags" => "ป้ายà¸à¸³à¸à¸±à¸š", +"Save bookmark" => "บันทึà¸à¸£à¸²à¸¢à¸à¸²à¸£à¹‚ปรด", +"You have no bookmarks" => "คุณยังไม่มีรายà¸à¸²à¸£à¹‚ปรด", +"Bookmarklet <br />" => "Bookmarklet <br />" +); diff --git a/apps/bookmarks/lib/bookmarks.php b/apps/bookmarks/lib/bookmarks.php index d569bf528a1..e1e13388902 100644 --- a/apps/bookmarks/lib/bookmarks.php +++ b/apps/bookmarks/lib/bookmarks.php @@ -145,4 +145,3 @@ class OC_Bookmarks_Bookmarks{ return true; } } -?> diff --git a/apps/bookmarks/templates/addBm.php b/apps/bookmarks/templates/addBm.php index dbe673f53a8..357e0a18f25 100644 --- a/apps/bookmarks/templates/addBm.php +++ b/apps/bookmarks/templates/addBm.php @@ -3,9 +3,9 @@ <head> <meta charset="utf-8"> <title>Read later - ownCloud</title> - <link rel="stylesheet" href="css/readlater.css"> </head> <body> <div class="message"><h1>Saved!</h1></div> + <a href="javascript:self.close()" >Close the window</a> </body> </html>
\ No newline at end of file diff --git a/apps/bookmarks/templates/list.php b/apps/bookmarks/templates/list.php index fdd2b19f79a..4b84b438900 100644 --- a/apps/bookmarks/templates/list.php +++ b/apps/bookmarks/templates/list.php @@ -7,7 +7,7 @@ * See the COPYING-README file. */ ?> -<input type="hidden" id="bookmarkFilterTag" value="<?php if(isset($_GET['tag'])) echo htmlentities($_GET['tag'],ENT_COMPAT,'utf-8'); ?>" /> +<input type="hidden" id="bookmarkFilterTag" value="<?php if(isset($_GET['tag'])) echo OCP\Util::sanitizeHTML($_GET['tag']); ?>" /> <div id="controls"> <input type="hidden" id="bookmark_add_id" value="0" /> <input type="text" id="bookmark_add_url" placeholder="<?php echo $l->t('Address'); ?>" class="bookmarks_input" /> @@ -20,7 +20,7 @@ <div id="firstrun" style="display: none;"> <?php echo $l->t('You have no bookmarks'); - require_once(OC::$APPSROOT . '/apps/bookmarks/templates/bookmarklet.php'); + require_once(OC_App::getAppPath('bookmarks') .'/templates/bookmarklet.php'); createBookmarklet(); ?> </div> diff --git a/apps/calendar/ajax/cache/rescan.php b/apps/calendar/ajax/cache/rescan.php new file mode 100644 index 00000000000..3417f1ae4b4 --- /dev/null +++ b/apps/calendar/ajax/cache/rescan.php @@ -0,0 +1,15 @@ +<?php +/** + * Copyright (c) 2012 Georg Ehrke <georg@ownCloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +OCP\JSON::checkLoggedIn(); +OCP\JSON::checkAppEnabled('calendar'); +$calendars = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser()); +foreach($calendars as $calendar){ + OC_Calendar_Repeat::cleancalendar($calendar['id']); + OC_Calendar_Repeat::generatecalendar($calendar['id']); +} +OCP\JSON::success();
\ No newline at end of file diff --git a/apps/calendar/ajax/cache/status.php b/apps/calendar/ajax/cache/status.php new file mode 100644 index 00000000000..d2806d47895 --- /dev/null +++ b/apps/calendar/ajax/cache/status.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright (c) 2012 Georg Ehrke <georg@ownCloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +OCP\JSON::checkLoggedIn(); +OCP\JSON::checkAppEnabled('calendar'); +$calendars = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser()); +$allcached = true; +foreach($calendars as $calendar){ + if(!OC_Calendar_Repeat::is_calendar_cached($calendar['id'])){ + $allcached = false; + } +} +$l = new OC_L10N('calendar'); +if(!$allcached){ + OCP\JSON::error(array('message'=>'Not all calendars are completely cached', 'l10n'=>$l->t('Not all calendars are completely cached'))); +}else{ + OCP\JSON::success(array('message'=>'Everything seems to be completely cached', 'l10n'=>$l->t('Everything seems to be completely cached'))); +}
\ No newline at end of file diff --git a/apps/calendar/ajax/calendar/activation.php b/apps/calendar/ajax/calendar/activation.php index e31908beb14..f4aadc5b017 100644 --- a/apps/calendar/ajax/calendar/activation.php +++ b/apps/calendar/ajax/calendar/activation.php @@ -9,6 +9,8 @@ OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('calendar'); +OCP\JSON::callCheck(); + $calendarid = $_POST['calendarid']; $calendar = OC_Calendar_App::getCalendar($calendarid, true); if(!$calendar){ diff --git a/apps/calendar/ajax/calendar/edit.form.php b/apps/calendar/ajax/calendar/edit.form.php index ae056a524bd..3916c527637 100644 --- a/apps/calendar/ajax/calendar/edit.form.php +++ b/apps/calendar/ajax/calendar/edit.form.php @@ -11,9 +11,9 @@ OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('calendar'); $calendarcolor_options = OC_Calendar_Calendar::getCalendarColorOptions(); -$calendar = OC_Calendar_App::getCalendar($_GET['calendarid']); +$calendar = OC_Calendar_App::getCalendar($_POST['calendarid']); $tmpl = new OCP\Template("calendar", "part.editcalendar"); $tmpl->assign('new', false); $tmpl->assign('calendarcolor_options', $calendarcolor_options); $tmpl->assign('calendar', $calendar); -$tmpl->printPage(); +$tmpl->printPage();
\ No newline at end of file diff --git a/apps/calendar/ajax/calendar/new.php b/apps/calendar/ajax/calendar/new.php index 34b056abe8f..67d12822378 100644 --- a/apps/calendar/ajax/calendar/new.php +++ b/apps/calendar/ajax/calendar/new.php @@ -6,8 +6,6 @@ * See the COPYING-README file. */ - - // Check if we are a user OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('calendar'); @@ -32,7 +30,13 @@ OC_Calendar_Calendar::setCalendarActive($calendarid, 1); $calendar = OC_Calendar_Calendar::find($calendarid); $tmpl = new OCP\Template('calendar', 'part.choosecalendar.rowfields'); $tmpl->assign('calendar', $calendar); +if(OC_Calendar_Share::allUsersSharedwith($calendarid, OC_Calendar_Share::CALENDAR) == array()){ + $shared = false; +}else{ + $shared = true; +} +$tmpl->assign('shared', $shared); OCP\JSON::success(array( 'page' => $tmpl->fetchPage(), 'eventSource' => OC_Calendar_Calendar::getEventSourceInfo($calendar), -));
\ No newline at end of file +)); diff --git a/apps/calendar/ajax/calendar/overview.php b/apps/calendar/ajax/calendar/overview.php index 9d43364ffbf..1d8e49ea5f2 100644 --- a/apps/calendar/ajax/calendar/overview.php +++ b/apps/calendar/ajax/calendar/overview.php @@ -4,9 +4,7 @@ * This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. - */ - - + */ $l10n = OC_L10N::get('calendar'); OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('calendar'); diff --git a/apps/calendar/ajax/calendar/update.php b/apps/calendar/ajax/calendar/update.php index 740094775f5..c09b1008c9c 100644 --- a/apps/calendar/ajax/calendar/update.php +++ b/apps/calendar/ajax/calendar/update.php @@ -37,7 +37,13 @@ OC_Calendar_Calendar::setCalendarActive($calendarid, $_POST['active']); $calendar = OC_Calendar_App::getCalendar($calendarid); $tmpl = new OCP\Template('calendar', 'part.choosecalendar.rowfields'); $tmpl->assign('calendar', $calendar); +if(OC_Calendar_Share::allUsersSharedwith($calendarid, OC_Calendar_Share::CALENDAR) == array()){ + $shared = false; +}else{ + $shared = true; +} +$tmpl->assign('shared', $shared); OCP\JSON::success(array( 'page' => $tmpl->fetchPage(), 'eventSource' => OC_Calendar_Calendar::getEventSourceInfo($calendar), -));
\ No newline at end of file +)); diff --git a/apps/calendar/ajax/categories/rescan.php b/apps/calendar/ajax/categories/rescan.php index f0060cb23b2..08c32865b6f 100644 --- a/apps/calendar/ajax/categories/rescan.php +++ b/apps/calendar/ajax/categories/rescan.php @@ -9,6 +9,7 @@ OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('calendar'); +OCP\JSON::callCheck(); foreach ($_POST as $key=>$element) { debug('_POST: '.$key.'=>'.print_r($element, true)); diff --git a/apps/calendar/ajax/changeview.php b/apps/calendar/ajax/changeview.php index 951f603ce85..819025543a5 100644 --- a/apps/calendar/ajax/changeview.php +++ b/apps/calendar/ajax/changeview.php @@ -7,15 +7,15 @@ */ OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('calendar'); -$view = $_GET['v']; +$view = $_POST['v']; switch($view){ case 'agendaWeek': case 'month'; case 'list': break; default: - OCP\JSON::error(array('message'=>'unexspected parameter: ' . $view)); + OCP\JSON::error(array('message'=>'unexpected parameter: ' . $view)); exit; } OCP\Config::setUserValue(OCP\USER::getUser(), 'calendar', 'currentview', $view); -OCP\JSON::success(); +OCP\JSON::success();
\ No newline at end of file diff --git a/apps/calendar/ajax/event/edit.form.php b/apps/calendar/ajax/event/edit.form.php index e5cf573c718..27512481538 100644 --- a/apps/calendar/ajax/event/edit.form.php +++ b/apps/calendar/ajax/event/edit.form.php @@ -13,7 +13,7 @@ if(!OCP\User::isLoggedIn()) { } OCP\JSON::checkAppEnabled('calendar'); -$id = $_GET['id']; +$id = $_POST['id']; $data = OC_Calendar_App::getEventObject($id, true, true); if(!$data){ diff --git a/apps/calendar/ajax/event/new.form.php b/apps/calendar/ajax/event/new.form.php index 0b19e7e92f9..db04cdf2d49 100644 --- a/apps/calendar/ajax/event/new.form.php +++ b/apps/calendar/ajax/event/new.form.php @@ -27,7 +27,7 @@ if (!$end){ } $start = new DateTime('@'.$start); $end = new DateTime('@'.$end); -$timezone = OCP\Config::getUserValue(OCP\USER::getUser(), 'calendar', 'timezone', date_default_timezone_get()); +$timezone = OC_Calendar_App::getTimezone(); $start->setTimezone(new DateTimeZone($timezone)); $end->setTimezone(new DateTimeZone($timezone)); diff --git a/apps/calendar/ajax/event/resize.php b/apps/calendar/ajax/event/resize.php index 56b83205e85..15b687b55da 100644 --- a/apps/calendar/ajax/event/resize.php +++ b/apps/calendar/ajax/event/resize.php @@ -7,6 +7,7 @@ */ OCP\JSON::checkLoggedIn(); +OCP\JSON::callCheck(); $id = $_POST['id']; diff --git a/apps/calendar/ajax/events.php b/apps/calendar/ajax/events.php index e00e0cfeb1e..ae55cbc02db 100644 --- a/apps/calendar/ajax/events.php +++ b/apps/calendar/ajax/events.php @@ -5,25 +5,20 @@ * later. * See the COPYING-README file. */ - - -require_once('when/When.php'); - OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('calendar'); session_write_close(); // Look for the calendar id -$calendar_id = OC_Calendar_App::getCalendar($_GET['calendar_id'], false, false); -if($calendar_id !== false){ - if(! is_numeric($calendar_id['userid']) && $calendar_id['userid'] != OCP\User::getUser()){ - OCP\JSON::error(); - exit; +$calendar_id = null; +if (strval(intval($_GET['calendar_id'])) == strval($_GET['calendar_id'])) { // integer for sure. + $id = intval($_GET['calendar_id']); + $calendarrow = OC_Calendar_App::getCalendar($id, true, false); // Let's at least security check otherwise we might as well use OC_Calendar_Calendar::find() + if($calendarrow !== false && is_int($calendar_id['userid']) && $id == $calendar_id['userid']) { + $calendar_id = $id; } } -else { - $calendar_id = $_GET['calendar_id']; -} +$calendar_id = (is_null($calendar_id)?strip_tags($_GET['calendar_id']):$calendar_id); $start = (version_compare(PHP_VERSION, '5.3.0', '>='))?DateTime::createFromFormat('U', $_GET['start']):new DateTime('@' . $_GET['start']); $end = (version_compare(PHP_VERSION, '5.3.0', '>='))?DateTime::createFromFormat('U', $_GET['end']):new DateTime('@' . $_GET['end']); diff --git a/apps/calendar/ajax/import/calendarcheck.php b/apps/calendar/ajax/import/calendarcheck.php new file mode 100644 index 00000000000..a91bab70573 --- /dev/null +++ b/apps/calendar/ajax/import/calendarcheck.php @@ -0,0 +1,18 @@ +<?php +/** + * Copyright (c) 2012 Georg Ehrke <ownclouddev at georgswebsite dot de> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +OCP\JSON::checkLoggedIn(); +OCP\App::checkAppEnabled('calendar'); +$calname = strip_tags($_POST['calname']); +$calendars = OC_Calendar_Calendar::allCalendars(OCP\User::getUser()); +foreach($calendars as $calendar){ + if($calendar['displayname'] == $calname){ + OCP\JSON::success(array('message'=>'exists')); + exit; + } +} +OCP\JSON::error();
\ No newline at end of file diff --git a/apps/calendar/ajax/import/dialog.php b/apps/calendar/ajax/import/dialog.php index b99c32278c4..18fe226172c 100644 --- a/apps/calendar/ajax/import/dialog.php +++ b/apps/calendar/ajax/import/dialog.php @@ -5,8 +5,6 @@ * later. * See the COPYING-README file. */ - - OCP\JSON::checkLoggedIn(); OCP\App::checkAppEnabled('calendar'); $tmpl = new OCP\Template('calendar', 'part.import'); diff --git a/apps/calendar/ajax/import/dropimport.php b/apps/calendar/ajax/import/dropimport.php new file mode 100644 index 00000000000..f46e7314098 --- /dev/null +++ b/apps/calendar/ajax/import/dropimport.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright (c) 2012 Georg Ehrke <ownclouddev@georgswebsite.de> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +$data = $_POST['data']; +$data = explode(',', $data); +$data = end($data); +$data = base64_decode($data); +OCP\JSON::checkLoggedIn(); +OCP\App::checkAppEnabled('calendar'); +$import = new OC_Calendar_Import($data); +$import->setUserID(OCP\User::getUser()); +$import->setTimeZone(OC_Calendar_App::$tz); +$import->disableProgressCache(); +if(!$import->isValid()){ + OCP\JSON::error(); + exit; +} +$newcalendarname = strip_tags($import->createCalendarName()); +$newid = OC_Calendar_Calendar::addCalendar(OCP\User::getUser(),$newcalendarname,'VEVENT,VTODO,VJOURNAL',null,0,$import->createCalendarColor()); +$import->setCalendarID($newid); +$import->import(); +$count = $import->getCount(); +if($count == 0){ + OC_Calendar_Calendar::deleteCalendar($newid); + OCP\JSON::error(array('message'=>OC_Calendar_App::$l10n->t('The file contained either no events or all events are already saved in your calendar.'))); +}else{ + OCP\JSON::success(array('message'=>$count . ' ' . OC_Calendar_App::$l10n->t('events has been saved in the new calendar') . ' ' . $newcalendarname, 'eventSource'=>OC_Calendar_Calendar::getEventSourceInfo(OC_Calendar_Calendar::find($newid)))); +}
\ No newline at end of file diff --git a/apps/calendar/ajax/import/import.php b/apps/calendar/ajax/import/import.php index c0cd1403763..b1dfc464d00 100644 --- a/apps/calendar/ajax/import/import.php +++ b/apps/calendar/ajax/import/import.php @@ -5,45 +5,77 @@ * later. * See the COPYING-README file. */ -//check for calendar rights or create new one -ob_start(); OCP\JSON::checkLoggedIn(); -OCP\JSON::callCheck(); OCP\App::checkAppEnabled('calendar'); -$nl="\r\n"; -$comps = array('VEVENT'=>true, 'VTODO'=>true, 'VJOURNAL'=>true); -$progressfile = 'import_tmp/' . md5(session_id()) . '.txt'; -if(is_writable('import_tmp/')){ - $progressfopen = fopen($progressfile, 'w'); - fwrite($progressfopen, '10'); - fclose($progressfopen); +OCP\JSON::callCheck(); +session_write_close(); +if (isset($_POST['progresskey']) && isset($_POST['getprogress'])) { + echo OCP\JSON::success(array('percent'=>OC_Cache::get($_POST['progresskey']))); + exit; } $file = OC_Filesystem::file_get_contents($_POST['path'] . '/' . $_POST['file']); +if(!$file){ + OCP\JSON::error(array('error'=>'404')); +} +$import = new OC_Calendar_Import($file); +$import->setUserID(OCP\User::getUser()); +$import->setTimeZone(OC_Calendar_App::$tz); +$import->enableProgressCache(); +$import->setProgresskey($_POST['progresskey']); +if(!$import->isValid()){ + OCP\JSON::error(array('error'=>'notvalid')); + exit; +} +$newcal = false; if($_POST['method'] == 'new'){ - $id = OC_Calendar_Calendar::addCalendar(OCP\USER::getUser(), $_POST['calname']); - OC_Calendar_Calendar::setCalendarActive($id, 1); + $calendars = OC_Calendar_Calendar::allCalendars(OCP\User::getUser()); + foreach($calendars as $calendar){ + if($calendar['displayname'] == $_POST['calname']){ + $id = $calendar['id']; + $newcal = false; + break; + } + $newcal = true; + } + if($newcal){ + $id = OC_Calendar_Calendar::addCalendar(OCP\USER::getUser(), strip_tags($_POST['calname']),'VEVENT,VTODO,VJOURNAL',null,0,strip_tags($_POST['calcolor'])); + OC_Calendar_Calendar::setCalendarActive($id, 1); + } }else{ $calendar = OC_Calendar_App::getCalendar($_POST['id']); if($calendar['userid'] != OCP\USER::getUser()){ - OCP\JSON::error(); + OCP\JSON::error(array('error'=>'missingcalendarrights')); exit(); } $id = $_POST['id']; } -if(is_writable('import_tmp/')){ - $progressfopen = fopen($progressfile, 'w'); - fwrite($progressfopen, '20'); - fclose($progressfopen); +$import->setCalendarID($id); +try{ + $import->import(); +}catch (Exception $e) { + OCP\JSON::error(array('message'=>OC_Calendar_App::$l10n->t('Import failed'), 'debug'=>$e->getMessage())); + //write some log } +$count = $import->getCount(); +if($count == 0){ + if($newcal){ + OC_Calendar_Calendar::deleteCalendar($id); + } + OCP\JSON::error(array('message'=>OC_Calendar_App::$l10n->t('The file contained either no events or all events are already saved in your calendar.'))); +}else{ + if($newcal){ + OCP\JSON::success(array('message'=>$count . ' ' . OC_Calendar_App::$l10n->t('events has been saved in the new calendar') . ' ' . strip_tags($_POST['calname']))); + }else{ + OCP\JSON::success(array('message'=>$count . ' ' . OC_Calendar_App::$l10n->t('events has been saved in your calendar'))); + } +} +/* //////////////////////////// Attention: following code is quite painfull !!! /////////////////////// +writeProgress('20'); // normalize the newlines $file = str_replace(array("\r","\n\n"), array("\n","\n"), $file); $lines = explode("\n", $file); unset($file); -if(is_writable('import_tmp/')){ - $progressfopen = fopen($progressfile, 'w'); - fwrite($progressfopen, '30'); - fclose($progressfopen); -} +writeProgress('30'); // analyze the file, group components by uid, and keep refs to originating calendar object // $cals is array calendar objects, keys are 1st line# $cal, ie array( $cal => $caldata ) // $caldata is array( 'first' => 1st component line#, 'last' => last comp line#, 'end' => end line# ) @@ -87,13 +119,8 @@ foreach($lines as $line) { $i++; } // import the calendar -if(is_writable('import_tmp/')){ - $progressfopen = fopen($progressfile, 'w'); - fwrite($progressfopen, '60'); - fclose($progressfopen); -} +writeProgress('60'); foreach($uids as $uid) { - $prefix=$suffix=$content=array(); foreach($uid as $begin=>$details) { @@ -118,13 +145,7 @@ foreach($uids as $uid) { } } // finished import -if(is_writable('import_tmp/')){ - $progressfopen = fopen($progressfile, 'w'); - fwrite($progressfopen, '100'); - fclose($progressfopen); -} +writeProgress('100'); sleep(3); -if(is_writable('import_tmp/')){ - unlink($progressfile); -} -OCP\JSON::success();
\ No newline at end of file +OC_Cache::remove($progresskey); +OCP\JSON::success();*/ diff --git a/apps/calendar/ajax/settings/guesstimezone.php b/apps/calendar/ajax/settings/guesstimezone.php index 11c74631d44..6b6b8bef9c1 100644 --- a/apps/calendar/ajax/settings/guesstimezone.php +++ b/apps/calendar/ajax/settings/guesstimezone.php @@ -12,8 +12,8 @@ OCP\JSON::checkAppEnabled('calendar'); $l = OC_L10N::get('calendar'); -$lat = $_GET['lat']; -$lng = $_GET['long']; +$lat = $_POST['lat']; +$lng = $_POST['lng']; $timezone = OC_Geo::timezone($lat, $lng); @@ -23,4 +23,4 @@ if($timezone == OCP\Config::getUserValue(OCP\USER::getUser(), 'calendar', 'timez } OCP\Config::setUserValue(OCP\USER::getUser(), 'calendar', 'timezone', $timezone); $message = array('message'=> $l->t('New Timezone:') . $timezone); -OCP\JSON::success($message); +OCP\JSON::success($message);
\ No newline at end of file diff --git a/apps/calendar/ajax/settings/setfirstday.php b/apps/calendar/ajax/settings/setfirstday.php index 97c24882939..73cf0c19b78 100644 --- a/apps/calendar/ajax/settings/setfirstday.php +++ b/apps/calendar/ajax/settings/setfirstday.php @@ -8,7 +8,6 @@ OCP\JSON::checkLoggedIn(); OCP\JSON::callCheck(); - if(isset($_POST["firstday"])){ OCP\Config::setUserValue(OCP\USER::getUser(), 'calendar', 'firstday', $_POST["firstday"]); OCP\JSON::success(); diff --git a/apps/calendar/ajax/settings/settimeformat.php b/apps/calendar/ajax/settings/settimeformat.php index d09679b9270..6136857e2fe 100644 --- a/apps/calendar/ajax/settings/settimeformat.php +++ b/apps/calendar/ajax/settings/settimeformat.php @@ -8,7 +8,6 @@ OCP\JSON::checkLoggedIn(); OCP\JSON::callCheck(); - if(isset($_POST["timeformat"])){ OCP\Config::setUserValue(OCP\USER::getUser(), 'calendar', 'timeformat', $_POST["timeformat"]); OCP\JSON::success(); diff --git a/apps/calendar/ajax/settings/settimezone.php b/apps/calendar/ajax/settings/settimezone.php index 6d029a6643a..06db66d578e 100644 --- a/apps/calendar/ajax/settings/settimezone.php +++ b/apps/calendar/ajax/settings/settimezone.php @@ -14,6 +14,7 @@ $l=OC_L10N::get('calendar'); // Check if we are a user OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('calendar'); +OCP\JSON::callCheck(); // Get data if( isset( $_POST['timezone'] ) ){ diff --git a/apps/calendar/ajax/share/activation.php b/apps/calendar/ajax/share/activation.php index 7d6b8fcb16e..bce8693577b 100644 --- a/apps/calendar/ajax/share/activation.php +++ b/apps/calendar/ajax/share/activation.php @@ -5,7 +5,7 @@ * later. * See the COPYING-README file. */ -$id = strip_tags($_GET['id']); -$activation = strip_tags($_GET['activation']); +$id = strip_tags($_POST['id']); +$activation = strip_tags($_POST['activation']); OC_Calendar_Share::set_active(OCP\USER::getUser(), $id, $activation); OCP\JSON::success(); diff --git a/apps/calendar/ajax/share/changepermission.php b/apps/calendar/ajax/share/changepermission.php index f3c628e8477..5aff7666f79 100644 --- a/apps/calendar/ajax/share/changepermission.php +++ b/apps/calendar/ajax/share/changepermission.php @@ -5,11 +5,12 @@ * later. * See the COPYING-README file. */ - OCP\JSON::callCheck(); -$id = strip_tags($_GET['id']); -$idtype = strip_tags($_GET['idtype']); -$permission = (int) strip_tags($_GET['permission']); +OCP\JSON::callCheck(); + +$id = strip_tags($_POST['id']); +$idtype = strip_tags($_POST['idtype']); +$permission = (int) strip_tags($_POST['permission']); switch($idtype){ case 'calendar': case 'event': @@ -26,8 +27,8 @@ if($idtype == 'event' && !OC_Calendar_App::getEventObject($id)){ OCP\JSON::error(array('message'=>'permission denied')); exit; } -$sharewith = $_GET['sharewith']; -$sharetype = strip_tags($_GET['sharetype']); +$sharewith = $_POST['sharewith']; +$sharetype = strip_tags($_POST['sharetype']); switch($sharetype){ case 'user': case 'group': diff --git a/apps/calendar/ajax/share/dropdown.php b/apps/calendar/ajax/share/dropdown.php index a3b0faca4bf..86cf4ac090e 100644 --- a/apps/calendar/ajax/share/dropdown.php +++ b/apps/calendar/ajax/share/dropdown.php @@ -7,7 +7,7 @@ */ $user = OCP\USER::getUser(); -$calid = $_GET['calid']; +$calid = $_POST['calid']; $calendar = OC_Calendar_Calendar::find($calid); if($calendar['userid'] != $user){ OCP\JSON::error(); diff --git a/apps/calendar/ajax/share/share.php b/apps/calendar/ajax/share/share.php index babb8ce3f13..77e1ab9d657 100644 --- a/apps/calendar/ajax/share/share.php +++ b/apps/calendar/ajax/share/share.php @@ -5,10 +5,11 @@ * later. * See the COPYING-README file. */ - OCP\JSON::callCheck(); + +OCP\JSON::callCheck(); -$id = strip_tags($_GET['id']); -$idtype = strip_tags($_GET['idtype']); +$id = strip_tags($_POST['id']); +$idtype = strip_tags($_POST['idtype']); switch($idtype){ case 'calendar': case 'event': @@ -25,8 +26,8 @@ if($idtype == 'event' && !OC_Calendar_App::getEventObject($id)){ OCP\JSON::error(array('message'=>'permission denied')); exit; } -$sharewith = $_GET['sharewith']; -$sharetype = strip_tags($_GET['sharetype']); +$sharewith = $_POST['sharewith']; +$sharetype = strip_tags($_POST['sharetype']); switch($sharetype){ case 'user': case 'group': diff --git a/apps/calendar/ajax/share/unshare.php b/apps/calendar/ajax/share/unshare.php index 09264070dde..c7c06113189 100644 --- a/apps/calendar/ajax/share/unshare.php +++ b/apps/calendar/ajax/share/unshare.php @@ -5,10 +5,11 @@ * later. * See the COPYING-README file. */ - OCP\JSON::callCheck(); -$id = strip_tags($_GET['id']); -$idtype = strip_tags($_GET['idtype']); +OCP\JSON::callCheck(); + +$id = strip_tags($_POST['id']); +$idtype = strip_tags($_POST['idtype']); switch($idtype){ case 'calendar': case 'event': @@ -25,8 +26,8 @@ if($idtype == 'event' && !OC_Calendar_App::getEventObject($id)){ OCP\JSON::error(array('message'=>'permission denied')); exit; } -$sharewith = $_GET['sharewith']; -$sharetype = strip_tags($_GET['sharetype']); +$sharewith = $_POST['sharewith']; +$sharetype = strip_tags($_POST['sharetype']); switch($sharetype){ case 'user': case 'group': diff --git a/apps/calendar/appinfo/app.php b/apps/calendar/appinfo/app.php index 69b556af314..00787055787 100644 --- a/apps/calendar/appinfo/app.php +++ b/apps/calendar/appinfo/app.php @@ -4,29 +4,41 @@ OC::$CLASSPATH['OC_Calendar_App'] = 'apps/calendar/lib/app.php'; OC::$CLASSPATH['OC_Calendar_Calendar'] = 'apps/calendar/lib/calendar.php'; OC::$CLASSPATH['OC_Calendar_Object'] = 'apps/calendar/lib/object.php'; OC::$CLASSPATH['OC_Calendar_Hooks'] = 'apps/calendar/lib/hooks.php'; -OC::$CLASSPATH['OC_Connector_Sabre_CalDAV'] = 'apps/calendar/lib/connector_sabre.php'; +OC::$CLASSPATH['OC_Connector_Sabre_CalDAV'] = 'apps/calendar/lib/sabre/backend.php'; +OC::$CLASSPATH['OC_Connector_Sabre_CalDAV_CalendarRoot'] = 'apps/calendar/lib/sabre/calendarroot.php'; +OC::$CLASSPATH['OC_Connector_Sabre_CalDAV_UserCalendars'] = 'apps/calendar/lib/sabre/usercalendars.php'; +OC::$CLASSPATH['OC_Connector_Sabre_CalDAV_Calendar'] = 'apps/calendar/lib/sabre/calendar.php'; +OC::$CLASSPATH['OC_Connector_Sabre_CalDAV_CalendarObject'] = 'apps/calendar/lib/sabre/object.php'; +OC::$CLASSPATH['OC_Calendar_Repeat'] = 'apps/calendar/lib/repeat.php'; OC::$CLASSPATH['OC_Calendar_Share'] = 'apps/calendar/lib/share.php'; OC::$CLASSPATH['OC_Search_Provider_Calendar'] = 'apps/calendar/lib/search.php'; - +OC::$CLASSPATH['OC_Calendar_Export'] = 'apps/calendar/lib/export.php'; +OC::$CLASSPATH['OC_Calendar_Import'] = 'apps/calendar/lib/import.php'; +OC::$CLASSPATH['OC_Share_Backend_Calendar'] = 'apps/calendar/lib/share/calendar.php'; +OC::$CLASSPATH['OC_Share_Backend_Event'] = 'apps/calendar/lib/share/event.php'; //General Hooks OCP\Util::connectHook('OC_User', 'post_createUser', 'OC_Calendar_Hooks', 'createUser'); OCP\Util::connectHook('OC_User', 'post_deleteUser', 'OC_Calendar_Hooks', 'deleteUser'); +//Repeating Events Hooks +OCP\Util::connectHook('OC_Calendar', 'addEvent', 'OC_Calendar_Repeat', 'generate'); +OCP\Util::connectHook('OC_Calendar', 'editEvent', 'OC_Calendar_Repeat', 'update'); +OCP\Util::connectHook('OC_Calendar', 'deleteEvent', 'OC_Calendar_Repeat', 'clean'); +OCP\Util::connectHook('OC_Calendar', 'moveEvent', 'OC_Calendar_Repeat', 'update'); +OCP\Util::connectHook('OC_Calendar', 'deleteCalendar', 'OC_Calendar_Repeat', 'cleanCalendar'); //Sharing Hooks OCP\Util::connectHook('OC_Calendar', 'deleteEvent', 'OC_Calendar_Share', 'post_eventdelete'); OCP\Util::connectHook('OC_Calendar', 'deleteCalendar', 'OC_Calendar_Share', 'post_caldelete'); - OCP\Util::addscript('calendar','loader'); OCP\Util::addscript("3rdparty", "chosen/chosen.jquery.min"); OCP\Util::addStyle("3rdparty", "chosen/chosen"); -OCP\App::register( array( - 'order' => 10, - 'id' => 'calendar', - 'name' => 'Calendar' )); +OCP\Util::addStyle('3rdparty/miniColors', 'jquery.miniColors'); +OCP\Util::addscript('3rdparty/miniColors', 'jquery.miniColors.min'); OCP\App::addNavigationEntry( array( 'id' => 'calendar_index', 'order' => 10, 'href' => OCP\Util::linkTo( 'calendar', 'index.php' ), 'icon' => OCP\Util::imagePath( 'calendar', 'icon.svg' ), 'name' => $l->t('Calendar'))); -OCP\App::registerPersonal('calendar', 'settings'); OC_Search::registerProvider('OC_Search_Provider_Calendar'); +OCP\Share::registerBackend('calendar', 'OC_Share_Backend_Calendar'); +OCP\Share::registerBackend('event', 'OC_Share_Backend_Event'); diff --git a/apps/calendar/appinfo/database.xml b/apps/calendar/appinfo/database.xml index f60319ad432..16e10010d5a 100644 --- a/apps/calendar/appinfo/database.xml +++ b/apps/calendar/appinfo/database.xml @@ -290,4 +290,56 @@ </table> + <table> + + <name>*dbprefix*calendar_repeat</name> + + <declaration> + + <field> + <name>id</name> + <type>integer</type> + <default>0</default> + <notnull>true</notnull> + <autoincrement>1</autoincrement> + <unsigned>true</unsigned> + <length>4</length> + </field> + + <field> + <name>eventid</name> + <type>integer</type> + <default>0</default> + <notnull>true</notnull> + <unsigned>true</unsigned> + <length>4</length> + </field> + + <field> + <name>calid</name> + <type>integer</type> + <default>0</default> + <notnull>true</notnull> + <unsigned>true</unsigned> + <length>4</length> + </field> + + <field> + <name>startdate</name> + <type>timestamp</type> + <default>0000-00-00 00:00:00</default> + <notnull>false</notnull> + </field> + + <field> + <name>enddate</name> + <type>timestamp</type> + <default>0000-00-00 00:00:00</default> + <notnull>false</notnull> + </field> + + </declaration> + + </table> + </database> diff --git a/apps/calendar/appinfo/remote.php b/apps/calendar/appinfo/remote.php index d500ec1080a..f499d909666 100644 --- a/apps/calendar/appinfo/remote.php +++ b/apps/calendar/appinfo/remote.php @@ -7,8 +7,8 @@ */ OCP\App::checkAppEnabled('calendar'); -if(substr($_SERVER["REQUEST_URI"],0,strlen(OC::$APPSWEBROOT . '/apps/calendar/caldav.php')) == OC::$APPSWEBROOT . '/apps/calendar/caldav.php'){ - $baseuri = OC::$APPSWEBROOT . '/apps/calendar/caldav.php'; +if(substr($_SERVER["REQUEST_URI"],0,strlen(OC_App::getAppWebPath('calendar').'/caldav.php')) == OC_App::getAppWebPath('calendar'). '/caldav.php'){ + $baseuri = OC_App::getAppWebPath('calendar').'/caldav.php'; } // only need authentication apps @@ -21,18 +21,17 @@ $principalBackend = new OC_Connector_Sabre_Principal(); $caldavBackend = new OC_Connector_Sabre_CalDAV(); // Root nodes -$Sabre_CalDAV_Principal_Collection = new Sabre_CalDAV_Principal_Collection($principalBackend); +$Sabre_CalDAV_Principal_Collection = new Sabre_CalDAV_Principal_Collection($principalBackend); $Sabre_CalDAV_Principal_Collection->disableListing = true; // Disable listening -$Sabre_CalDAV_CalendarRootNode = new Sabre_CalDAV_CalendarRootNode($principalBackend, $caldavBackend); -$Sabre_CalDAV_CalendarRootNode->disableListing = true; // Disable listening +$calendarRoot = new OC_Connector_Sabre_CalDAV_CalendarRoot($principalBackend, $caldavBackend); +$calendarRoot->disableListing = true; // Disable listening -$nodes = array( - $Sabre_CalDAV_Principal_Collection, - $Sabre_CalDAV_CalendarRootNode, +$nodes = array( + $Sabre_CalDAV_Principal_Collection, + $calendarRoot, ); - // Fire up server $server = new Sabre_DAV_Server($nodes); $server->setBaseUri($baseuri); @@ -41,6 +40,7 @@ $server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend,'ownCloud')); $server->addPlugin(new Sabre_CalDAV_Plugin()); $server->addPlugin(new Sabre_DAVACL_Plugin()); $server->addPlugin(new Sabre_DAV_Browser_Plugin(false)); // Show something in the Browser, but no upload +$server->addPlugin(new Sabre_CalDAV_ICSExportPlugin()); // And off we go! $server->exec(); diff --git a/apps/calendar/appinfo/update.php b/apps/calendar/appinfo/update.php index 3b5998d9987..0e11c998841 100644 --- a/apps/calendar/appinfo/update.php +++ b/apps/calendar/appinfo/update.php @@ -15,3 +15,10 @@ if (version_compare($installedVersion, '0.2.1', '<')) { $r = $stmt->execute(array($color,$id)); } } +if (version_compare($installedVersion, '0.5', '<')) { + $calendars = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser()); + foreach($calendars as $calendar){ + OC_Calendar_Repeat::cleanCalendar($calendar['id']); + OC_Calendar_Repeat::generateCalendar($calendar['id']); + } +}
\ No newline at end of file diff --git a/apps/calendar/appinfo/version b/apps/calendar/appinfo/version index 267577d47e4..cb0c939a936 100644 --- a/apps/calendar/appinfo/version +++ b/apps/calendar/appinfo/version @@ -1 +1 @@ -0.4.1 +0.5.2 diff --git a/apps/calendar/calendar.php b/apps/calendar/calendar.php new file mode 100644 index 00000000000..2c0bee9d233 --- /dev/null +++ b/apps/calendar/calendar.php @@ -0,0 +1,12 @@ +<?php +/** + * Copyright (c) 2012 Georg Ehrke <ownClouddev at georgswebsite.de> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +$l10n = OC_L10N::get('calendar'); +OCP\JSON::checkLoggedIn(); +OCP\JSON::checkAppEnabled('calendar'); +$tmpl = new OCP\Template('calendar', 'part.choosecalendar'); +$tmpl->printpage();
\ No newline at end of file diff --git a/apps/calendar/css/import.css b/apps/calendar/css/import.css new file mode 100644 index 00000000000..fd82006072c --- /dev/null +++ b/apps/calendar/css/import.css @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2012 Georg Ehrke <ownclouddev at georgswebsite dot de> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +#calendar_import_newcalform, #calendar_import_mergewarning, #calendar_import_process, #calendar_import_done{display:none;} +#calendar_import_process_message, #calendar_import_status, #calendar_import_form_message, #calendar_import_mergewarning{text-align:center;} +#calendar_import_form_message{font-weight: bold;} +#calendar_import_newcalendar{width:415px;float:right;} +#calendar_import_mergewarning{clear: both;} +#calendar_import_defaultcolors{clear:both;margin: 0 auto;text-align: center;} +.calendar_import_warning{border-color: #fc3333;} +.calendar-colorpicker-color{display:inline-block;width:20px;height:5px;margin: 0 auto;cursor:pointer;border:2px solid transparent;margin-top: 5px;}
\ No newline at end of file diff --git a/apps/calendar/css/style.css b/apps/calendar/css/style.css index 373a4565638..64a779b9a9f 100644 --- a/apps/calendar/css/style.css +++ b/apps/calendar/css/style.css @@ -19,7 +19,7 @@ #loading { display: none;margin: 0;padding:0;margin-top:5px;} -#calendar_holder {position: relative;bottom: 0; right: 0; left: 0; top: 3em;} +#fullcalendar {position: relative;bottom: 0; right: 0; left: 0; top: 3em;} .fc-content{padding:2px 4px;} #listview {margin: 0; padding: 10px; background: #EEEEEE;} #listview #more_before, #listview #more_after {border: 1px solid #1a1a1a; width:25em;padding: 3px;text-align: center;} @@ -40,8 +40,6 @@ .thisday{background: #FFFABC;} .event {position:relative;} .event.colored {border-bottom: 1px solid white;} -.popup {display: none; position: absolute; z-index: 1000; background: #eeeeee; color: #000000; border: 1px solid #1a1a1a; font-size: 90%;} -.event_popup {width: 280px; height: 40px; padding: 10px;} input[type="button"].active {color: #6193CF} #fromtime, #totime { @@ -133,3 +131,12 @@ padding:0 8px 2px; line-height:1.2; margin-bottom:4px; } + +#choosecalendar a.settings{ + margin-top: 25px; + margin-right: 10px; +} + +#fullcalendar{ + overflow: scroll; +}
\ No newline at end of file diff --git a/apps/calendar/export.php b/apps/calendar/export.php index 5780d191a57..1374c49cc0d 100644 --- a/apps/calendar/export.php +++ b/apps/calendar/export.php @@ -5,35 +5,26 @@ * later. * See the COPYING-README file. */ - - OCP\User::checkLoggedIn(); OCP\App::checkAppEnabled('calendar'); $cal = isset($_GET['calid']) ? $_GET['calid'] : NULL; $event = isset($_GET['eventid']) ? $_GET['eventid'] : NULL; -$nl = "\r\n"; if(isset($cal)){ $calendar = OC_Calendar_App::getCalendar($cal, true); if(!$calendar){ header('HTTP/1.0 404 Not Found'); exit; } - $calobjects = OC_Calendar_Object::all($cal); header('Content-Type: text/Calendar'); - header('Content-Disposition: inline; filename=' . $calendar['displayname'] . '.ics'); - foreach($calobjects as $calobject){ - echo $calobject['calendardata'] . $nl; - } + header('Content-Disposition: inline; filename=' . str_replace(' ', '-', $calendar['displayname']) . '.ics'); + echo OC_Calendar_Export::export($cal, OC_Calendar_Export::CALENDAR); }elseif(isset($event)){ $data = OC_Calendar_App::getEventObject($_GET['eventid'], true); if(!$data){ header('HTTP/1.0 404 Not Found'); exit; } - $calendarid = $data['calendarid']; - $calendar = OC_Calendar_App::getCalendar($calendarid); header('Content-Type: text/Calendar'); - header('Content-Disposition: inline; filename=' . $data['summary'] . '.ics'); - echo $data['calendardata']; -} -?> + header('Content-Disposition: inline; filename=' . str_replace(' ', '-', $data['summary']) . '.ics'); + echo OC_Calendar_Export::export($event, OC_Calendar_Export::EVENT); +}
\ No newline at end of file diff --git a/apps/calendar/img/icon.png b/apps/calendar/img/icon.png Binary files differindex eb9e07cbb10..267efd997f3 100644 --- a/apps/calendar/img/icon.png +++ b/apps/calendar/img/icon.png diff --git a/apps/calendar/import_tmp/Info b/apps/calendar/import_tmp/Info deleted file mode 100644 index abafbce435c..00000000000 --- a/apps/calendar/import_tmp/Info +++ /dev/null @@ -1,2 +0,0 @@ -This folder contains static files with the percentage of the import. -Requires write permission diff --git a/apps/calendar/index.php b/apps/calendar/index.php index cf03a7a3cd3..a8ad4ab3356 100644 --- a/apps/calendar/index.php +++ b/apps/calendar/index.php @@ -5,25 +5,34 @@ * later. * See the COPYING-README file. */ - - OCP\User::checkLoggedIn(); OCP\App::checkAppEnabled('calendar'); // Create default calendar ... -$calendars = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser(), 1); +$calendars = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser(), false); if( count($calendars) == 0){ OC_Calendar_Calendar::addCalendar(OCP\USER::getUser(),'Default calendar'); - $calendars = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser(), 1); + $calendars = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser(), true); } $eventSources = array(); foreach($calendars as $calendar){ - $eventSources[] = OC_Calendar_Calendar::getEventSourceInfo($calendar); + if($calendar['active'] == 1) { + $eventSources[] = OC_Calendar_Calendar::getEventSourceInfo($calendar); + } } -$eventSources[] = array('url' => '?app=calendar&getfile=ajax/events.php?calendar_id=shared_rw', 'backgroundColor' => '#1D2D44', 'borderColor' => '#888', 'textColor' => 'white', 'editable'=>'true'); -$eventSources[] = array('url' => '?app=calendar&getfile=ajax/events.php?calendar_id=shared_r', 'backgroundColor' => '#1D2D44', 'borderColor' => '#888', 'textColor' => 'white', 'editable' => 'false'); +$events_baseURL = OCP\Util::linkTo('calendar', 'ajax/events.php'); +$eventSources[] = array('url' => $events_baseURL.'?calendar_id=shared_rw', + 'backgroundColor' => '#1D2D44', + 'borderColor' => '#888', + 'textColor' => 'white', + 'editable'=>'true'); +$eventSources[] = array('url' => $events_baseURL.'?calendar_id=shared_r', + 'backgroundColor' => '#1D2D44', + 'borderColor' => '#888', + 'textColor' => 'white', + 'editable' => 'false'); OCP\Util::emitHook('OC_Calendar', 'getSources', array('sources' => &$eventSources)); $categories = OC_Calendar_App::getCategoryOptions(); @@ -54,9 +63,9 @@ OCP\Util::addscript('contacts','jquery.multi-autocomplete'); OCP\Util::addscript('','oc-vcategories'); OCP\App::setActiveNavigationEntry('calendar_index'); $tmpl = new OCP\Template('calendar', 'calendar', 'user'); -$tmpl->assign('eventSources', $eventSources); +$tmpl->assign('eventSources', $eventSources,false); $tmpl->assign('categories', $categories); if(array_key_exists('showevent', $_GET)){ - $tmpl->assign('showevent', $_GET['showevent']); + $tmpl->assign('showevent', $_GET['showevent'], false); } $tmpl->printPage(); diff --git a/apps/calendar/js/calendar.js b/apps/calendar/js/calendar.js index efdff52998c..23846c89b84 100644 --- a/apps/calendar/js/calendar.js +++ b/apps/calendar/js/calendar.js @@ -18,7 +18,7 @@ Calendar={ startEventDialog:function(){ Calendar.UI.loading(false); $('.tipsy').remove(); - $('#calendar_holder').fullCalendar('unselect'); + $('#fullcalendar').fullCalendar('unselect'); Calendar.UI.lockTime(); $( "#from" ).datepicker({ dateFormat : 'dd-mm-yy' @@ -78,7 +78,7 @@ Calendar={ $('#event').dialog('destroy').remove(); }else{ Calendar.UI.loading(true); - $('#dialog_holder').load(OC.filePath('calendar', 'ajax/event', 'edit.form.php') + '?id=' + id, Calendar.UI.startEventDialog); + $('#dialog_holder').load(OC.filePath('calendar', 'ajax/event', 'edit.form.php'), {id: id}, Calendar.UI.startEventDialog); } }, submitDeleteEventForm:function(url){ @@ -88,7 +88,7 @@ Calendar={ $.post(url, post, function(data){ Calendar.UI.loading(false); if(data.status == 'success'){ - $('#calendar_holder').fullCalendar('removeEvents', $('#event_form input[name=id]').val()); + $('#fullcalendar').fullCalendar('removeEvents', $('#event_form input[name=id]').val()); $('#event').dialog('destroy').remove(); } else { $('#errorbox').html(t('calendar', 'Deletion failed')); @@ -133,7 +133,7 @@ Calendar={ } else if(data.status == 'success'){ $('#event').dialog('destroy').remove(); - $('#calendar_holder').fullCalendar('refetchEvents'); + $('#fullcalendar').fullCalendar('refetchEvents'); } },"json"); }, @@ -148,7 +148,7 @@ Calendar={ console.log("Event moved successfully"); }else{ revertFunc(); - $('#calendar_holder').fullCalendar('refetchEvents'); + $('#fullcalendar').fullCalendar('refetchEvents'); } }); }, @@ -163,7 +163,7 @@ Calendar={ console.log("Event resized successfully"); }else{ revertFunc(); - $('#calendar_holder').fullCalendar('refetchEvents'); + $('#fullcalendar').fullCalendar('refetchEvents'); } }); }, @@ -207,8 +207,7 @@ Calendar={ } }, showCalDAVUrl:function(username, calname){ - $('#caldav_url').val(totalurl + '/' + username + '/' + calname); - $('#caldav_url').val(encodeURI($('#caldav_url').val())); + $('#caldav_url').val(totalurl + '/' + username + '/' + decodeURIComponent(calname)); $('#caldav_url').show(); $("#caldav_url_close").show(); }, @@ -240,11 +239,11 @@ Calendar={ doc_height = $(document).height(), win_height = $(window).height(); if(direction == 'down' && win_height == (doc_height - scroll)){ - $('#calendar_holder').fullCalendar('next'); + $('#fullcalendar').fullCalendar('next'); $(document).scrollTop(0); event.preventDefault(); }else if (direction == 'top' && scroll == 0) { - $('#calendar_holder').fullCalendar('prev'); + $('#fullcalendar').fullCalendar('prev'); $(document).scrollTop(win_height); event.preventDefault(); } @@ -399,9 +398,9 @@ Calendar={ if (data.status == 'success'){ checkbox.checked = data.active == 1; if (data.active == 1){ - $('#calendar_holder').fullCalendar('addEventSource', data.eventSource); + $('#fullcalendar').fullCalendar('addEventSource', data.eventSource); }else{ - $('#calendar_holder').fullCalendar('removeEventSource', data.eventSource.url); + $('#fullcalendar').fullCalendar('removeEventSource', data.eventSource.url); } } }); @@ -414,7 +413,7 @@ Calendar={ }, edit:function(object, calendarid){ var tr = $(document.createElement('tr')) - .load(OC.filePath('calendar', 'ajax/calendar', 'edit.form.php') + "?calendarid="+calendarid, + .load(OC.filePath('calendar', 'ajax/calendar', 'edit.form.php'), {calendarid: calendarid}, function(){Calendar.UI.Calendar.colorPicker(this)}); $(object).closest('tr').after(tr).hide(); }, @@ -427,9 +426,10 @@ Calendar={ function(data) { if (data.status == 'success'){ var url = 'ajax/events.php?calendar_id='+calid; - $('#calendar_holder').fullCalendar('removeEventSource', url); + $('#fullcalendar').fullCalendar('removeEventSource', url); $('#choosecalendar_dialog').dialog('destroy').remove(); Calendar.UI.Calendar.overview(); + $('#fullcalendar').fullCalendar('refetchEvents'); } }); } @@ -456,8 +456,8 @@ Calendar={ function(data){ if(data.status == 'success'){ $(button).closest('tr').prev().html(data.page).show().next().remove(); - $('#calendar_holder').fullCalendar('removeEventSource', data.eventSource.url); - $('#calendar_holder').fullCalendar('addEventSource', data.eventSource); + $('#fullcalendar').fullCalendar('removeEventSource', data.eventSource.url); + $('#fullcalendar').fullCalendar('addEventSource', data.eventSource); if (calendarid == 'new'){ $('#choosecalendar_dialog > table:first').append('<tr><td colspan="6"><a href="#" onclick="Calendar.UI.Calendar.newCalendar(this);"><input type="button" value="' + newcalendar + '"></a></td></tr>'); } @@ -503,14 +503,14 @@ Calendar={ currentid: 'false', idtype: '', activation:function(object,owner,id){ - $.getJSON(OC.filePath('calendar', 'ajax/share', 'activation.php'),{id:id, idtype:'calendar', activation:object.checked?1:0}); - $('#calendar_holder').fullCalendar('refetchEvents'); + $.post(OC.filePath('calendar', 'ajax/share', 'activation.php'),{id:id, idtype:'calendar', activation:object.checked?1:0}); + $('#fullcalendar').fullCalendar('refetchEvents'); }, dropdown:function(userid, calid){ $('.calendar_share_dropdown').remove(); var element = document.getElementById(userid+'_'+calid); $('<div class="calendar_share_dropdown"></div>').appendTo(element); - $.get(OC.filePath('calendar', 'ajax/share', 'dropdown.php') + '?calid=' + calid, function(data){ + $.post(OC.filePath('calendar', 'ajax/share', 'dropdown.php'), {calid: calid}, function(data){ $('.calendar_share_dropdown').html(data); $('.calendar_share_dropdown').show('blind'); $('#share_user').chosen(); @@ -520,7 +520,7 @@ Calendar={ Calendar.UI.Share.idtype = 'calendar'; }, share:function(id, idtype, sharewith, sharetype){ - $.getJSON(OC.filePath('calendar', 'ajax/share', 'share.php'),{id:id, idtype:idtype, sharewith:sharewith, sharetype:sharetype}, function(data){ + $.post(OC.filePath('calendar', 'ajax/share', 'share.php'),{id:id, idtype:idtype, sharewith:sharewith, sharetype:sharetype}, function(data){ if(sharetype == 'public'){ $('#public_token').val(parent.location.protocol+'//'+location.host+OC.linkTo('', 'public.php')+'?service=calendar&t='+data.message); $('#public_token').css('display', 'block'); @@ -528,7 +528,7 @@ Calendar={ }); }, unshare:function(id, idtype, sharewith, sharetype){ - $.getJSON(OC.filePath('calendar', 'ajax/share', 'unshare.php'),{id:id, idtype:idtype, sharewith:sharewith, sharetype:sharetype}, function(){ + $.post(OC.filePath('calendar', 'ajax/share', 'unshare.php'),{id:id, idtype:idtype, sharewith:sharewith, sharetype:sharetype}, function(){ if(sharetype == 'public'){ $('#public_token').val(''); $('#public_token').css('display', 'none'); @@ -536,7 +536,7 @@ Calendar={ }); }, changepermission:function(id, idtype, sharewith, sharetype, permission){ - $.getJSON(OC.filePath('calendar', 'ajax/share', 'changepermission.php'),{id:id, idtype:idtype, sharewith: sharewith, sharetype:sharetype, permission: (permission?1:0)}); + $.post(OC.filePath('calendar', 'ajax/share', 'changepermission.php'),{id:id, idtype:idtype, sharewith: sharewith, sharetype:sharetype, permission: (permission?1:0)}); }, init:function(){ $('.calendar_share_dropdown').live('mouseleave', function(){ @@ -547,7 +547,7 @@ Calendar={ $('#share_user').live('change', function(){ if($('#sharewithuser_' + $('#share_user option:selected').text()).length == 0){ Calendar.UI.Share.share(Calendar.UI.Share.currentid, Calendar.UI.Share.idtype, $('#share_user option:selected').text(), 'user'); - var newitem = '<li id="sharewithuser_' + $('#share_user option:selected').text() +'"><input type="checkbox" width="12px" style="visibility:hidden;" title="' + $('#share_user option:selected').text() + '">' + $('#share_user option:selected').text() + '<img src="' + oc_webroot + '/core/img/actions/delete.svg" class="svg action" style="display:none;float:right;"></li>'; + var newitem = '<li id="sharewithuser_' + $('#share_user option:selected').text() +'"><input type="checkbox" width="12px" style="visibility:hidden;" title="' + $('#share_user option:selected').text() + '">' + $('#share_user option:selected').text() + '<img src="' + OC.imagePath('core', 'actions/delete.svg') + '" class="svg action" style="display:none;float:right;"></li>'; $('#sharewithuser_list').append(newitem); $('#sharewithuser_' + $('#share_user option:selected').text() + ' > img').click(function(){ $('#share_user option[value="' + $(this).parent().text() + '"]').removeAttr('disabled'); @@ -562,7 +562,7 @@ Calendar={ $('#share_group').live('change', function(){ if($('#sharewithgroup_' + $('#share_group option:selected').text()).length == 0){ Calendar.UI.Share.share(Calendar.UI.Share.currentid, Calendar.UI.Share.idtype, $('#share_group option:selected').text(), 'group'); - var newitem = '<li id="sharewithgroup_' + $('#share_group option:selected').text() +'"><input type="checkbox" width="12px" style="visibility:hidden;" title="' + $('#share_group option:selected').text() + '">' + $('#share_group option:selected').text() + '<img src="' + oc_webroot + '/core/img/actions/delete.svg" class="svg action" style="display:none;float:right;"></li>'; + var newitem = '<li id="sharewithgroup_' + $('#share_group option:selected').text() +'"><input type="checkbox" width="12px" style="visibility:hidden;" title="' + $('#share_group option:selected').text() + '">' + $('#share_group option:selected').text() + '<img src="' + OC.imagePath('core', 'actions/delete.svg') + '" class="svg action" style="display:none;float:right;"></li>'; $('#sharewithgroup_list').append(newitem); $('#sharewithgroup_' + $('#share_group option:selected').text() + ' > img').click(function(){ $('#share_group option[value="' + $(this).parent().text() + '"]').removeAttr('disabled'); @@ -605,8 +605,53 @@ Calendar={ }); /*var permissions = (this.checked) ? 1 : 0;*/ } + }, + Drop:{ + init:function(){ + if (typeof window.FileReader === 'undefined') { + console.log('The drop-import feature is not supported in your browser :('); + return false; + } + droparea = document.getElementById('fullcalendar'); + droparea.ondrop = function(e){ + e.preventDefault(); + Calendar.UI.Drop.drop(e); + } + console.log('Drop initialized successfully'); + }, + drop:function(e){ + var files = e.dataTransfer.files; + for(var i = 0;i < files.length;i++){ + var file = files[i]; + reader = new FileReader(); + reader.onload = function(event){ + Calendar.UI.Drop.import(event.target.result); + $('#fullcalendar').fullCalendar('refetchEvents'); + } + reader.readAsDataURL(file); + } + }, + import:function(data){ + $.post(OC.filePath('calendar', 'ajax/import', 'dropimport.php'), {'data':data},function(result) { + if(result.status == 'success'){ + $('#fullcalendar').fullCalendar('addEventSource', result.eventSource); + $('#notification').html(result.message); + $('#notification').slideDown(); + window.setTimeout(function(){$('#notification').slideUp();}, 5000); + return true; + }else{ + $('#notification').html(result.message); + $('#notification').slideDown(); + window.setTimeout(function(){$('#notification').slideUp();}, 5000); + } + }); + } } - } + }, + Settings:{ + // + }, + } $.fullCalendar.views.list = ListView; function ListView(element, calendar) { @@ -774,7 +819,7 @@ function ListView(element, calendar) { } $(document).ready(function(){ Calendar.UI.initScroll(); - $('#calendar_holder').fullCalendar({ + $('#fullcalendar').fullCalendar({ header: false, firstDay: firstDay, editable: true, @@ -803,17 +848,17 @@ $(document).ready(function(){ dayNamesShort: dayNamesShort, allDayText: allDayText, viewDisplay: function(view) { - $('#datecontrol_date').html(view.title); + $('#datecontrol_date').val($('<p>').html(view.title).text()); if (view.name != defaultView) { - $.get(OC.filePath('calendar', 'ajax', 'changeview.php') + "?v="+view.name); + $.post(OC.filePath('calendar', 'ajax', 'changeview.php'), {v:view.name}); defaultView = view.name; } Calendar.UI.setViewActive(view.name); if (view.name == 'agendaWeek') { - $('#calendar_holder').fullCalendar('option', 'aspectRatio', 0.1); + $('#fullcalendar').fullCalendar('option', 'aspectRatio', 0.1); } else { - $('#calendar_holder').fullCalendar('option', 'aspectRatio', 1.35); + $('#fullcalendar').fullCalendar('option', 'aspectRatio', 1.35); } }, columnFormat: { @@ -842,26 +887,49 @@ $(document).ready(function(){ loading: Calendar.UI.loading, eventSources: eventSources }); + $('#datecontrol_date').datepicker({ + changeMonth: true, + changeYear: true, + showButtonPanel: true, + beforeShow: function(input, inst) { + var calendar_holder = $('#fullcalendar'); + var date = calendar_holder.fullCalendar('getDate'); + inst.input.datepicker('setDate', date); + inst.input.val(calendar_holder.fullCalendar('getView').title); + return inst; + }, + onSelect: function(value, inst) { + var date = inst.input.datepicker('getDate'); + $('#fullcalendar').fullCalendar('gotoDate', date); + } + }); fillWindow($('#content')); OCCategories.changed = Calendar.UI.categoriesChanged; OCCategories.app = 'calendar'; $('#oneweekview_radio').click(function(){ - $('#calendar_holder').fullCalendar('changeView', 'agendaWeek'); + $('#fullcalendar').fullCalendar('changeView', 'agendaWeek'); }); $('#onemonthview_radio').click(function(){ - $('#calendar_holder').fullCalendar('changeView', 'month'); + $('#fullcalendar').fullCalendar('changeView', 'month'); }); $('#listview_radio').click(function(){ - $('#calendar_holder').fullCalendar('changeView', 'list'); + $('#fullcalendar').fullCalendar('changeView', 'list'); }); $('#today_input').click(function(){ - $('#calendar_holder').fullCalendar('today'); + $('#fullcalendar').fullCalendar('today'); }); $('#datecontrol_left').click(function(){ - $('#calendar_holder').fullCalendar('prev'); + $('#fullcalendar').fullCalendar('prev'); }); $('#datecontrol_right').click(function(){ - $('#calendar_holder').fullCalendar('next'); + $('#fullcalendar').fullCalendar('next'); }); Calendar.UI.Share.init(); + Calendar.UI.Drop.init(); + $('#choosecalendar .generalsettings').on('click keydown', function() { + OC.appSettings({appid:'calendar', loadJS:true, cache:false}); + }); + $('#choosecalendar .calendarsettings').on('click keydown', function() { + OC.appSettings({appid:'calendar', loadJS:true, cache:false, scriptName:'calendar.php'}); + }); }); diff --git a/apps/calendar/js/geo.js b/apps/calendar/js/geo.js index 092d8547469..99290d940e3 100644 --- a/apps/calendar/js/geo.js +++ b/apps/calendar/js/geo.js @@ -6,7 +6,7 @@ */ if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(function(position) { - $.getJSON(OC.filePath('calendar', 'ajax/settings', 'guesstimezone.php') + '?lat=' + position.coords.latitude + '&long=' + position.coords.longitude, + $.post(OC.filePath('calendar', 'ajax/settings', 'guesstimezone.php'), {lat: position.coords.latitude, lng: position.coords.longitude}, function(data){ if (data.status == 'success' && typeof(data.message) != 'undefined'){ $('#notification').html(data.message); diff --git a/apps/calendar/js/loader.js b/apps/calendar/js/loader.js index 60d92f448ee..253abafc427 100644 --- a/apps/calendar/js/loader.js +++ b/apps/calendar/js/loader.js @@ -5,77 +5,175 @@ * See the COPYING-README file. */ Calendar_Import={ - importdialog: function(filename){ - var path = $('#dir').val(); - $('body').append('<div id="calendar_import"></div>'); - $('#calendar_import').load(OC.filePath('calendar', 'ajax/import', 'dialog.php'), {filename:filename, path:path}, function(){Calendar_Import.initdialog(filename);}); + Store:{ + file: '', + path: '', + id: 0, + method: '', + calname: '', + calcolor: '', + progresskey: '', + percentage: 0 }, - initdialog: function(filename){ - $('#calendar_import_dialog').dialog({ - width : 500, - close : function() { - $(this).dialog('destroy').remove(); - $('#calendar_import').remove(); + Dialog:{ + open: function(filename){ + OC.addStyle('calendar', 'import'); + Calendar_Import.Store.file = filename; + Calendar_Import.Store.path = $('#dir').val(); + $('body').append('<div id="calendar_import"></div>'); + $('#calendar_import').load(OC.filePath('calendar', 'ajax/import', 'dialog.php'), {filename:Calendar_Import.Store.file, path:Calendar_Import.Store.path},function(){ + Calendar_Import.Dialog.init(); + }); + }, + close: function(){ + Calendar_Import.reset(); + $(this).dialog('destroy').remove(); + $('#calendar_import_dialog').remove(); + }, + init: function(){ + //init dialog + $('#calendar_import_dialog').dialog({ + width : 500, + resizable: false, + close : function() { + Calendar_Import.Dialog.close(); + } + }); + //init buttons + $('#calendar_import_done').click(function(){ + Calendar_Import.Dialog.close(); + }); + $('#calendar_import_submit').click(function(){ + Calendar_Import.Core.process(); + }); + $('#calendar_import_mergewarning').click(function(){ + $('#calendar_import_newcalendar').attr('value', $('#calendar_import_availablename').val()); + Calendar_Import.Dialog.mergewarning($('#calendar_import_newcalendar').val()); + }); + $('#calendar_import_calendar').change(function(){ + if($('#calendar_import_calendar option:selected').val() == 'newcal'){ + $('#calendar_import_newcalform').slideDown('slow'); + Calendar_Import.Dialog.mergewarning($('#calendar_import_newcalendar').val()); + }else{ + $('#calendar_import_newcalform').slideUp('slow'); + $('#calendar_import_mergewarning').slideUp('slow'); + } + }); + $('#calendar_import_newcalendar').keyup(function(){ + Calendar_Import.Dialog.mergewarning($.trim($('#calendar_import_newcalendar').val())); + }); + $('#calendar_import_newcalendar_color').miniColors({ + letterCase: 'uppercase' + }); + $('.calendar-colorpicker-color').click(function(){ + var str = $(this).attr('rel'); + str = str.substr(1); + $('#calendar_import_newcalendar_color').attr('value', str); + $(".color-picker").miniColors('value', '#' + str); + }); + //init progressbar + $('#calendar_import_progressbar').progressbar({value: Calendar_Import.Store.percentage}); + Calendar_Import.Store.progresskey = $('#calendar_import_progresskey').val(); + }, + mergewarning: function(newcalname){ + $.post(OC.filePath('calendar', 'ajax/import', 'calendarcheck.php'), {calname: newcalname}, function(data){ + if(data.message == 'exists'){ + $('#calendar_import_mergewarning').slideDown('slow'); + }else{ + $('#calendar_import_mergewarning').slideUp('slow'); + } + }); + }, + update: function(){ + if(Calendar_Import.Store.percentage == 100){ + return false; } - }); - $('#import_done_button').click(function(){ - $('#calendar_import_dialog').dialog('destroy').remove(); - $('#calendar_import').remove(); - }); - $('#progressbar').progressbar({value: 0}); - $('#startimport').click(function(){ - var filename = $('#filename').val(); - var path = $('#path').val(); - var calid = $('#calendar option:selected').val(); - if($('#calendar option:selected').val() == 'newcal'){ - var method = 'new'; - var calname = $('#newcalendar').val(); - var calname = $.trim(calname); - if(calname == ''){ - $('#newcalendar').css('background-color', '#FF2626'); - $('#newcalendar').focus(function(){ - $('#newcalendar').css('background-color', '#F8F8F8'); - }); - return false; + $.post(OC.filePath('calendar', 'ajax/import', 'import.php'), {progresskey: Calendar_Import.Store.progresskey, getprogress: true}, function(data){ + if(data.status == 'success'){ + if(data.percent == null){ + return false; + } + Calendar_Import.Store.percentage = parseInt(data.percent); + $('#calendar_import_progressbar').progressbar('option', 'value', parseInt(data.percent)); + if(data.percent < 100 ){ + window.setTimeout('Calendar_Import.Dialog.update()', 250); + }else{ + $('#calendar_import_done').css('display', 'block'); + } + }else{ + $('#calendar_import_progressbar').progressbar('option', 'value', 100); + $('#calendar_import_progressbar > div').css('background-color', '#FF2626'); + $('#calendar_import_status').html(data.message); } - }else{ - var method = 'old'; + }); + return 0; + }, + warning: function(selector){ + $(selector).addClass('calendar_import_warning'); + $(selector).focus(function(){ + $(selector).removeClass('calendar_import_warning'); + }); + } + }, + Core:{ + process: function(){ + var validation = Calendar_Import.Core.prepare(); + if(validation){ + $('#calendar_import_form').css('display', 'none'); + $('#calendar_import_process').css('display', 'block'); + $('#calendar_import_newcalendar').attr('readonly', 'readonly'); + $('#calendar_import_calendar').attr('disabled', 'disabled'); + Calendar_Import.Core.send(); + window.setTimeout('Calendar_Import.Dialog.update()', 250); } - $('#newcalendar').attr('readonly', 'readonly'); - $('#calendar').attr('disabled', 'disabled'); - var progressfile = $('#progressfile').val(); - $.post(OC.filePath('calendar', 'ajax/import', 'import.php'), {method: String (method), calname: String (calname), path: String (path), file: String (filename), id: String (calid)}, function(data){ + }, + send: function(){ + $.post(OC.filePath('calendar', 'ajax/import', 'import.php'), + {progresskey: Calendar_Import.Store.progresskey, method: String (Calendar_Import.Store.method), calname: String (Calendar_Import.Store.calname), path: String (Calendar_Import.Store.path), file: String (Calendar_Import.Store.file), id: String (Calendar_Import.Store.id), calcolor: String (Calendar_Import.Store.calcolor)}, function(data){ if(data.status == 'success'){ - $('#progressbar').progressbar('option', 'value', 100); - $('#import_done').css('display', 'block'); + $('#calendar_import_progressbar').progressbar('option', 'value', 100); + Calendar_Import.Store.percentage = 100; + $('#calendar_import_done').css('display', 'block'); + $('#calendar_import_status').html(data.message); + }else{ + $('#calendar_import_progressbar').progressbar('option', 'value', 100); + $('#calendar_import_progressbar > div').css('background-color', '#FF2626'); + $('#calendar_import_status').html(data.message); } }); - $('#form_container').css('display', 'none'); - $('#progressbar_container').css('display', 'block'); - window.setTimeout('Calendar_Import.getimportstatus(\'' + progressfile + '\')', 500); - }); - $('#calendar').change(function(){ - if($('#calendar option:selected').val() == 'newcal'){ - $('#newcalform').slideDown('slow'); + }, + prepare: function(){ + Calendar_Import.Store.id = $('#calendar_import_calendar option:selected').val(); + if($('#calendar_import_calendar option:selected').val() == 'newcal'){ + Calendar_Import.Store.method = 'new'; + Calendar_Import.Store.calname = $.trim($('#calendar_import_newcalendar').val()); + if(Calendar_Import.Store.calname == ''){ + Calendar_Import.Dialog.warning('#calendar_import_newcalendar'); + return false; + } + Calendar_Import.Store.calcolor = $.trim($('#calendar_import_newcalendar_color').val()); + if(Calendar_Import.Store.calcolor == ''){ + Calendar_Import.Store.calcolor = $('.calendar-colorpicker-color:first').attr('rel'); + } }else{ - $('#newcalform').slideUp('slow'); + Calendar_Import.Store.method = 'old'; } - }); + return true; + } }, - getimportstatus: function(progressfile){ - $.get(OC.filePath('calendar', 'import_tmp', progressfile), function(percent){ - $('#progressbar').progressbar('option', 'value', parseInt(percent)); - if(percent < 100){ - window.setTimeout('Calendar_Import.getimportstatus(\'' + progressfile + '\')', 500); - }else{ - $('#import_done').css('display', 'block'); - } - }); + reset: function(){ + Calendar_Import.Store.file = ''; + Calendar_Import.Store.path = ''; + Calendar_Import.Store.id = 0; + Calendar_Import.Store.method = ''; + Calendar_Import.Store.calname = ''; + Calendar_Import.Store.progresskey = ''; + Calendar_Import.Store.percentage = 0; } } $(document).ready(function(){ if(typeof FileActions !== 'undefined'){ - FileActions.register('text/calendar','importcal', '', Calendar_Import.importdialog); - FileActions.setDefault('text/calendar','importcal'); + FileActions.register('text/calendar','importCalendar', FileActions.PERMISSION_READ, '', Calendar_Import.Dialog.open); + FileActions.setDefault('text/calendar','importCalendar'); }; }); diff --git a/apps/calendar/js/settings.js b/apps/calendar/js/settings.js index c768a47a797..20753a7b8fe 100644 --- a/apps/calendar/js/settings.js +++ b/apps/calendar/js/settings.js @@ -1,11 +1,7 @@ $(document).ready(function(){ $('#timezone').change( function(){ - OC.msg.startSaving('#calendar .msg') - // Serialize the data var post = $( '#timezone' ).serialize(); - $.post( OC.filePath('calendar', 'ajax/settings', 'settimezone.php'), post, function(data){ - //OC.msg.finishedSaving('#calendar .msg', data); - }); + $.post( OC.filePath('calendar', 'ajax/settings', 'settimezone.php'), post, function(data){return;}); return false; }); $('#timezone').chosen(); @@ -34,6 +30,7 @@ $(document).ready(function(){ $.getJSON(OC.filePath('calendar', 'ajax/settings', 'timeformat.php'), function(jsondata, status) { $('#' + jsondata.timeformat).attr('selected',true); $('#timeformat').chosen(); + $('#timeformat_chzn').css('width', '100px'); }); $.getJSON(OC.filePath('calendar', 'ajax/settings', 'gettimezonedetection.php'), function(jsondata, status){ if(jsondata.detection == 'true'){ @@ -43,5 +40,27 @@ $(document).ready(function(){ $.getJSON(OC.filePath('calendar', 'ajax/settings', 'getfirstday.php'), function(jsondata, status) { $('#' + jsondata.firstday).attr('selected',true); $('#firstday').chosen(); + $('#firstday_chzn').css('width', '100px'); + }); + $('#cleancalendarcache').click(function(){ + $.getJSON(OC.filePath('calendar', 'ajax/cache', 'rescan.php'), function(){ + calendarcachecheck(); + }); }); + calendarcachecheck(); + }); +function calendarcachecheck(){ + $.getJSON(OC.filePath('calendar', 'ajax/cache', 'status.php'), function(jsondata, status) { + $('#cleancalendarcache').attr('title', jsondata.l10n.text); + if(jsondata.status == 'success'){ + $('#cleancalendarcache').css('background', '#F8F8F8'); + $('#cleancalendarcache').css('color', '#333'); + $('#cleancalendarcache').css('text-shadow', '#fff 0 1px 0'); + }else{ + $('#cleancalendarcache').css('background', '#DC143C'); + $('#cleancalendarcache').css('color', '#FFFFFF'); + $('#cleancalendarcache').css('text-shadow', '0px 0px 0px #fff, 0px 0px #fff'); + } + }); +}
\ No newline at end of file diff --git a/apps/calendar/l10n/ar.php b/apps/calendar/l10n/ar.php index 679f1102853..1ca5e0ead5d 100644 --- a/apps/calendar/l10n/ar.php +++ b/apps/calendar/l10n/ar.php @@ -1,9 +1,19 @@ <?php $TRANSLATIONS = array( +"Not all calendars are completely cached" => "ليس جميع الجداول الزمنيه Ù…ØÙوضه مؤقة", +"Everything seems to be completely cached" => "كل شيء Ù…ØÙوض مؤقة", +"No calendars found." => "لم يتم العثور على جدول الزمني", +"No events found." => "لم يتم العثور على اØداث", "Wrong calendar" => "جدول زمني خاطئ", "New Timezone:" => "التوقيت الجديد", "Timezone changed" => "تم تغيير المنطقة الزمنية", "Invalid request" => "طلب غير Ù…Ùهوم", "Calendar" => "الجدول الزمني", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "ddd M/d", +"MMMM yyyy" => "ddd M/d", +"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", "Birthday" => "عيد ميلاد", "Business" => "عمل", "Call" => "إتصال", @@ -19,6 +29,9 @@ "Projects" => "مشاريع", "Questions" => "اسئلة", "Work" => "العمل", +"by" => "من قبل", +"unnamed" => "غير مسمى", +"New Calendar" => "جدول زمني جديد", "Does not repeat" => "لا يعاد", "Daily" => "يومي", "Weekly" => "أسبوعي", @@ -63,8 +76,19 @@ "by day and month" => "Øسب اليوم Ùˆ الشهر", "Date" => "تاريخ", "Cal." => "تقويم", +"Sun." => "Ø£Øد", +"Mon." => "أثن.", +"Tue." => "ثلا.", +"Wed." => "أرب.", +"Thu." => "خمي.", +"Fri." => "جمع.", +"Sat." => "سبت", +"Jan." => "Ùƒ2", +"Feb." => "شبا.", +"Mar." => "آذا.", +"Apr." => "نيس.", +"May." => "أيا.", "All day" => "كل النهار", -"New Calendar" => "جدول زمني جديد", "Missing fields" => "خانات خالية من المعلومات", "Title" => "عنوان", "From Date" => "من تاريخ", @@ -77,13 +101,15 @@ "Month" => "شهر", "List" => "قائمة", "Today" => "اليوم", -"Calendars" => "الجداول الزمنية", -"There was a fail, while parsing the file." => "لم يتم قراءة المل٠بنجاØ.", -"Choose active calendars" => "إختر الجدول الزمني الرئيسي", +"Your calendars" => "جداولك الزمنيه", "CalDav Link" => "وصلة CalDav", +"Shared calendars" => "جداول زمنيه مشتركه", +"No shared calendars" => "لا يوجد جداول زمنيه مشتركه", +"Share Calendar" => "شارك الجدول الزمني", "Download" => "تØميل", "Edit" => "تعديل", "Delete" => "ØØ°Ù", +"shared with you by" => "مشاركه من قبل", "New calendar" => "جدول زمني جديد", "Edit calendar" => "عادل الجدول الزمني", "Displayname" => "الاسم المرئي", @@ -94,8 +120,15 @@ "Cancel" => "إلغاء", "Edit an event" => "عادل Øدث", "Export" => "تصدير المعلومات", +"Eventinfo" => "تÙاصيل الØدث", +"Repeating" => "يعاد", +"Alarm" => "تنبيه", +"Attendees" => "الØضور", +"Share" => "شارك", "Title of the Event" => "عنوان الØدث", "Category" => "Ùئة", +"Separate categories with commas" => "اÙصل الÙئات بالÙواصل", +"Edit categories" => "عدل الÙئات", "All Day Event" => "Øدث ÙÙŠ يوم كامل", "From" => "من", "To" => "إلى", @@ -116,20 +149,23 @@ "Interval" => "المده الÙاصله", "End" => "نهايه", "occurrences" => "الاØداث", -"Import a calendar file" => "أدخل مل٠التقويم", -"Please choose the calendar" => "الرجاء إختر الجدول الزمني", "create a new calendar" => "انشاء جدول زمني جديد", +"Import a calendar file" => "أدخل مل٠التقويم", "Name of new calendar" => "أسم الجدول الزمني الجديد", "Import" => "إدخال", -"Importing calendar" => "يتم ادخال الجدول الزمني", -"Calendar imported successfully" => "تم ادخال الجدول الزمني بنجاØ", "Close Dialog" => "أغلق الØوار", "Create a new event" => "إضاÙØ© Øدث جديد", -"Select category" => "اختر الÙئة", +"View an event" => "شاهد الØدث", +"No categories selected" => "لم يتم اختيار الÙئات", +"of" => "من", +"at" => "ÙÙŠ", "Timezone" => "المنطقة الزمنية", -"Check always for changes of the timezone" => "راقب دائما تغير التقويم الزمني", -"Timeformat" => "شكل الوقت", "24h" => "24 ساعة", "12h" => "12 ساعة", -"Calendar CalDAV syncing address:" => "عنوان لتØديث ال CalDAV الجدول الزمني" +"Users" => "المستخدمين", +"select users" => "اختر المستخدمين", +"Editable" => "يمكن تعديله", +"Groups" => "مجموعات", +"select groups" => "اختر المجموعات", +"make public" => "Øدث عام" ); diff --git a/apps/calendar/l10n/bg_BG.php b/apps/calendar/l10n/bg_BG.php index e4f73d24a9a..fc353ebef95 100644 --- a/apps/calendar/l10n/bg_BG.php +++ b/apps/calendar/l10n/bg_BG.php @@ -1,7 +1,23 @@ <?php $TRANSLATIONS = array( +"No calendars found." => "Ðе Ñа открити календари.", +"No events found." => "Ðе Ñа открити ÑъбитиÑ.", +"Import failed" => "Грешка при внаÑÑне", +"New Timezone:" => "Ðов чаÑови поÑÑ:", "Timezone changed" => "ЧаÑовата зона е Ñменена", "Invalid request" => "Ðевалидна заÑвка", "Calendar" => "Календар", +"Birthday" => "Роджен ден", +"Clients" => "Клиенти", +"Holidays" => "Празници", +"Ideas" => "Идеи", +"Journey" => "Пътуване", +"Meeting" => "Среща", +"Other" => "Друго", +"Personal" => "Лично", +"Projects" => "Проекти", +"Questions" => "ВъпроÑи", +"Work" => "Работа", +"New Calendar" => "Ðов календар", "Does not repeat" => "Ðе Ñе повтарÑ", "Daily" => "Дневно", "Weekly" => "Седмично", @@ -9,32 +25,60 @@ "Bi-Weekly" => "ДвуÑедмично", "Monthly" => "МеÑечно", "Yearly" => "Годишно", +"never" => "никога", +"Monday" => "Понеделник", +"Tuesday" => "Вторник", +"Wednesday" => "СрÑда", +"Thursday" => "Четвъртък", +"Friday" => "Петък", +"Saturday" => "Събота", +"Sunday" => "ÐеделÑ", "All day" => "Ð’Ñички дни", +"Missing fields" => "ЛипÑват полета", "Title" => "Заглавие", "Week" => "Седмица", "Month" => "МеÑец", +"List" => "СпиÑък", "Today" => "ДнеÑ", -"Calendars" => "Календари", -"There was a fail, while parsing the file." => "Възникна проблем Ñ Ñ€Ð°Ð·Ð»Ð¸Ñтването на файла.", -"Choose active calendars" => "Изберете активен календар", +"Your calendars" => "Вашите календари", +"Shared calendars" => "Споделени календари", +"No shared calendars" => "ÐÑма Ñподелени календари", +"Share Calendar" => "СподелÑне на календар", "Download" => "ИзтеглÑне", "Edit" => "ПромÑна", +"Delete" => "Изтриване", +"New calendar" => "Ðов календар", "Edit calendar" => "Промени календар", "Displayname" => "Екранно име", "Active" => "Ðктивен", "Calendar color" => "ЦвÑÑ‚ на календара", +"Save" => "ЗапиÑ", "Submit" => "Продължи", +"Cancel" => "Отказ", "Edit an event" => "ПромÑна на Ñъбитие", +"Export" => "ИзнаÑÑне", +"Share" => "СподелÑне", "Title of the Event" => "Ðаименование", "Category" => "КатегориÑ", +"Separate categories with commas" => "Отделете категориите ÑÑŠÑ Ð·Ð°Ð¿ÐµÑ‚Ð°Ð¸", +"Edit categories" => "Редактиране на категориите", "All Day Event" => "Целодневно Ñъбитие", "From" => "От", "To" => "До", +"Advanced options" => "Разширени наÑтройки", "Location" => "ЛокациÑ", "Location of the Event" => "ЛокациÑ", "Description" => "ОпиÑание", "Description of the Event" => "ОпиÑание", "Repeat" => "Повтори", +"create a new calendar" => "Ñъздаване на нов календар", +"Please choose a calendar" => "Изберете календар", +"Name of new calendar" => "Име на Ð½Ð¾Ð²Ð¸Ñ ÐºÐ°Ð»ÐµÐ½Ð´Ð°Ñ€", +"Import" => "ВнаÑÑне", +"Close Dialog" => "ЗатварÑне на прозореца", "Create a new event" => "Ðово Ñъбитие", -"Timezone" => "ЧаÑова зона" +"View an event" => "Преглед на Ñъбитие", +"No categories selected" => "ÐÑма избрани категории", +"Timezone" => "ЧаÑова зона", +"Groups" => "Групи" ); diff --git a/apps/calendar/l10n/ca.php b/apps/calendar/l10n/ca.php index afb1c799d93..9e267604e62 100644 --- a/apps/calendar/l10n/ca.php +++ b/apps/calendar/l10n/ca.php @@ -1,12 +1,23 @@ <?php $TRANSLATIONS = array( +"Not all calendars are completely cached" => "No tots els calendaris estan en memòria", +"Everything seems to be completely cached" => "Sembla que tot està en memòria", "No calendars found." => "No s'han trobat calendaris.", "No events found." => "No s'han trobat events.", "Wrong calendar" => "Calendari erroni", +"The file contained either no events or all events are already saved in your calendar." => "El fitxer no contenia esdeveniments o aquests ja estaven desats en el vostre caledari", +"events has been saved in the new calendar" => "els esdeveniments s'han desat en el calendari nou", +"Import failed" => "Ha fallat la importació", +"events has been saved in your calendar" => "els esdveniments s'han desat en el calendari", "New Timezone:" => "Nova zona horà ria:", "Timezone changed" => "La zona horà ria ha canviat", "Invalid request" => "Sol.licitud no và lida", "Calendar" => "Calendari", -"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"ddd" => "ddd", +"ddd M/d" => "ddd d/M", +"dddd M/d" => "dddd d/M", +"MMMM yyyy" => "MMMM yyyy", +"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "d [MMM ][yyyy ]{'—' d MMM yyyy}", +"dddd, MMM d, yyyy" => "dddd, d MMM, yyyy", "Birthday" => "Aniversari", "Business" => "Feina", "Call" => "Trucada", @@ -22,7 +33,9 @@ "Projects" => "Projectes", "Questions" => "Preguntes", "Work" => "Feina", +"by" => "per", "unnamed" => "sense nom", +"New Calendar" => "Calendari nou", "Does not repeat" => "No es repeteix", "Daily" => "Diari", "Weekly" => "Mensual", @@ -67,8 +80,26 @@ "by day and month" => "per dia del mes", "Date" => "Data", "Cal." => "Cal.", +"Sun." => "Dg.", +"Mon." => "Dl.", +"Tue." => "Dm.", +"Wed." => "Dc.", +"Thu." => "Dj.", +"Fri." => "Dv.", +"Sat." => "Ds.", +"Jan." => "Gen.", +"Feb." => "Febr.", +"Mar." => "Març", +"Apr." => "Abr.", +"May." => "Maig", +"Jun." => "Juny", +"Jul." => "Jul.", +"Aug." => "Ag.", +"Sep." => "Set.", +"Oct." => "Oct.", +"Nov." => "Nov.", +"Dec." => "Des.", "All day" => "Tot el dia", -"New Calendar" => "Calendari nou", "Missing fields" => "Els camps que falten", "Title" => "TÃtol", "From Date" => "Des de la data", @@ -81,9 +112,7 @@ "Month" => "Mes", "List" => "Llista", "Today" => "Avui", -"Calendars" => "Calendaris", -"There was a fail, while parsing the file." => "S'ha produït un error en analitzar el fitxer.", -"Choose active calendars" => "Seleccioneu calendaris actius", +"Settings" => "Configuració", "Your calendars" => "Els vostres calendaris", "CalDav Link" => "Enllaç CalDav", "Shared calendars" => "Calendaris compartits", @@ -132,27 +161,34 @@ "Interval" => "Interval", "End" => "Final", "occurrences" => "aparicions", -"Import a calendar file" => "Importa un fitxer de calendari", -"Please choose the calendar" => "Escolliu el calendari", "create a new calendar" => "crea un nou calendari", +"Import a calendar file" => "Importa un fitxer de calendari", +"Please choose a calendar" => "Escolliu un calendari", "Name of new calendar" => "Nom del nou calendari", +"Take an available name!" => "Escolliu un nom disponible!", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Ja hi ha un calendari amb aquest nom. Si continueu, els calendaris es combinaran.", "Import" => "Importa", -"Importing calendar" => "S'està important el calendari", -"Calendar imported successfully" => "El calendari s'ha importat amb èxit", "Close Dialog" => "Tanca el dià leg", "Create a new event" => "Crea un nou esdeveniment", "View an event" => "Mostra un event", "No categories selected" => "No hi ha categories seleccionades", -"Select category" => "Seleccioneu categoria", "of" => "de", "at" => "a", +"General" => "General", "Timezone" => "Zona horà ria", -"Check always for changes of the timezone" => "Comprova sempre en els canvis de zona horà ria", -"Timeformat" => "Format de temps", +"Update timezone automatically" => "Actualitza la zona horà ria automà ticament", +"Time format" => "Format horari", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Primer dia de la setmana", -"Calendar CalDAV syncing address:" => "Adreça de sincronització del calendari CalDAV:", +"Start week on" => "Comença la setmana en ", +"Cache" => "Memòria de cau", +"Clear cache for repeating events" => "Neteja la memòria de cau pels esdeveniments amb repetició", +"URLs" => "URLs", +"Calendar CalDAV syncing addresses" => "Adreça de sincronització del calendari CalDAV", +"more info" => "més informació", +"Primary address (Kontact et al)" => "Adreça primà ria (Kontact et al)", +"iOS/OS X" => "IOS/OS X", +"Read only iCalendar link(s)" => "Enllaç(os) iCalendar només de lectura", "Users" => "Usuaris", "select users" => "seleccioneu usuaris", "Editable" => "Editable", diff --git a/apps/calendar/l10n/cs_CZ.php b/apps/calendar/l10n/cs_CZ.php index 05d286d82c1..ab76cc49d1e 100644 --- a/apps/calendar/l10n/cs_CZ.php +++ b/apps/calendar/l10n/cs_CZ.php @@ -1,12 +1,23 @@ <?php $TRANSLATIONS = array( +"Not all calendars are completely cached" => "V pamÄ›ti nejsou uloženy kompletnÄ› vÅ¡echny kalendáře", +"Everything seems to be completely cached" => "Zdá se, že vÅ¡e je kompletnÄ› uloženo v pamÄ›ti", "No calendars found." => "Žádné kalendáře nenalezeny.", "No events found." => "Žádné události nenalezeny.", "Wrong calendar" => "Nesprávný kalendář", +"The file contained either no events or all events are already saved in your calendar." => "Soubor, obsahujÃcà vÅ¡echny záznamy nebo je prázdný, je již uložen ve VaÅ¡em kalendáři.", +"events has been saved in the new calendar" => "Záznam byl uložen v novém kalendáři", +"Import failed" => "Import selhal", +"events has been saved in your calendar" => "záznamů bylo uloženo ve VaÅ¡em kalendáři", "New Timezone:" => "Nová Äasová zóna:", "Timezone changed" => "ÄŒasová zóna byla zmÄ›nÄ›na", "Invalid request" => "Chybný požadavek", "Calendar" => "Kalendář", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM rrrr", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "d. MMM[ yyyy]{ '—' d.[ MMM] yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, rrrr", "Birthday" => "Narozeniny", "Business" => "ObchodnÃ", "Call" => "Hovor", @@ -22,7 +33,9 @@ "Projects" => "Projekty", "Questions" => "Dotazy", "Work" => "PracovnÃ", +"by" => "od", "unnamed" => "nepojmenováno", +"New Calendar" => "Nový kalendář", "Does not repeat" => "Neopakuje se", "Daily" => "DennÄ›", "Weekly" => "TýdnÄ›", @@ -67,8 +80,26 @@ "by day and month" => "podle dne a mÄ›sÃce", "Date" => "Datum", "Cal." => "Kal.", +"Sun." => "Ne", +"Mon." => "Po", +"Tue." => "Út", +"Wed." => "St", +"Thu." => "ÄŒt", +"Fri." => "Pá", +"Sat." => "So", +"Jan." => "Ne", +"Feb." => "únor", +"Mar." => "bÅ™ezen", +"Apr." => "duben", +"May." => "kvÄ›ten", +"Jun." => "Äerven", +"Jul." => "Äervenec", +"Aug." => "srpen", +"Sep." => "zářÃ", +"Oct." => "Å™Ãjen", +"Nov." => "listopad", +"Dec." => "prosinec", "All day" => "Celý den", -"New Calendar" => "Nový kalendář", "Missing fields" => "ChybÄ›jÃcà pole", "Title" => "Název", "From Date" => "Od data", @@ -81,9 +112,7 @@ "Month" => "mÄ›sÃc", "List" => "Seznam", "Today" => "dnes", -"Calendars" => "Kalendáře", -"There was a fail, while parsing the file." => "Chyba pÅ™i pÅ™evodu souboru", -"Choose active calendars" => "Vybrat aktivnà kalendář", +"Settings" => "NastavenÃ", "Your calendars" => "VaÅ¡e kalendáře", "CalDav Link" => "CalDav odkaz", "Shared calendars" => "SdÃlené kalendáře", @@ -132,27 +161,34 @@ "Interval" => "Interval", "End" => "Konec", "occurrences" => "výskyty", -"Import a calendar file" => "Importovat soubor kalendáře", -"Please choose the calendar" => "Zvolte prosÃm kalendář", "create a new calendar" => "vytvoÅ™it nový kalendář", +"Import a calendar file" => "Importovat soubor kalendáře", +"Please choose a calendar" => "Vyberte prosÃm kalendář", "Name of new calendar" => "Název nového kalendáře", +"Take an available name!" => "Použijte volné jméno!", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Kalendář s trÃmto názvem již existuje. Pokud název použijete, stejnojmenné kalendáře budou slouÄeny.", "Import" => "Import", -"Importing calendar" => "Kalendář se importuje", -"Calendar imported successfully" => "Kalendář byl úspěšnÄ› importován", "Close Dialog" => "ZavÅ™Ãt dialog", "Create a new event" => "VytvoÅ™it novou událost", "View an event" => "Zobrazit událost", "No categories selected" => "Žádné kategorie nevybrány", -"Select category" => "Vyberte kategorii", "of" => "z", "at" => "v", +"General" => "HlavnÃ", "Timezone" => "ÄŒasové pásmo", -"Check always for changes of the timezone" => "Vždy kontrolavat, zda nedoÅ¡lo ke zmÄ›nÄ› Äasového pásma", -"Timeformat" => "Formát Äasu", +"Update timezone automatically" => "Obnovit auronaricky Äasovou zónu.", +"Time format" => "Formát Äasu", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Týden zaÄÃna v", -"Calendar CalDAV syncing address:" => "Adresa pro synchronizaci kalendáře pomocà CalDAV:", +"Start week on" => "Týden zaÄÃna v", +"Cache" => "Paměť", +"Clear cache for repeating events" => "Vymazat paměť pro opakuijÃsà se záznamy", +"URLs" => "URLs", +"Calendar CalDAV syncing addresses" => "Kalendář CalDAV synchronizuje adresy", +"more info" => "podrobnosti", +"Primary address (Kontact et al)" => "Primárnà adresa (veÅ™ejná)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "Odkaz(y) kalendáře pouze pro ÄtenÃ", "Users" => "Uživatelé", "select users" => "vybrat uživatele", "Editable" => "Upravovatelné", diff --git a/apps/calendar/l10n/da.php b/apps/calendar/l10n/da.php index 36551a2a93a..a193d5e1568 100644 --- a/apps/calendar/l10n/da.php +++ b/apps/calendar/l10n/da.php @@ -1,12 +1,22 @@ <?php $TRANSLATIONS = array( +"Not all calendars are completely cached" => "Ikke alle kalendere er fuldstændig cached", "No calendars found." => "Der blev ikke fundet nogen kalendere.", "No events found." => "Der blev ikke fundet nogen begivenheder.", "Wrong calendar" => "Forkert kalender", +"The file contained either no events or all events are already saved in your calendar." => "Filen indeholdt enten ingen begivenheder eller alle begivenheder er allerede gemt i din kalender.", +"events has been saved in the new calendar" => "begivenheder er gemt i den nye kalender", +"Import failed" => "import mislykkedes", +"events has been saved in your calendar" => "begivenheder er gemt i din kalender", "New Timezone:" => "Ny tidszone:", "Timezone changed" => "Tidszone ændret", "Invalid request" => "Ugyldig forespørgsel", "Calendar" => "Kalender", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM åååå", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ åååå]{ '—'[ MMM] d åååå}", +"dddd, MMM d, yyyy" => "dddd, MMM d, åååå", "Birthday" => "Fødselsdag", "Business" => "Forretning", "Call" => "Ring", @@ -22,7 +32,9 @@ "Projects" => "Projekter", "Questions" => "SpørgsmÃ¥l", "Work" => "Arbejde", +"by" => "af", "unnamed" => "unavngivet", +"New Calendar" => "Ny Kalender", "Does not repeat" => "Gentages ikke", "Daily" => "Daglig", "Weekly" => "Ugentlig", @@ -67,8 +79,26 @@ "by day and month" => "efter dag og mÃ¥ned", "Date" => "Dato", "Cal." => "Kal.", +"Sun." => "Søn.", +"Mon." => "Man.", +"Tue." => "Tir.", +"Wed." => "Ons.", +"Thu." => "Tor.", +"Fri." => "Fre.", +"Sat." => "Lør.", +"Jan." => "Jan.", +"Feb." => "Feb.", +"Mar." => "Mar.", +"Apr." => "Apr.", +"May." => "Maj", +"Jun." => "Jun.", +"Jul." => "Jul.", +"Aug." => "Aug.", +"Sep." => "Sep.", +"Oct." => "Okt.", +"Nov." => "Nov.", +"Dec." => "Dec.", "All day" => "Hele dagen", -"New Calendar" => "Ny Kalender", "Missing fields" => "Manglende felter", "Title" => "Titel", "From Date" => "Fra dato", @@ -81,9 +111,7 @@ "Month" => "MÃ¥ned", "List" => "Liste", "Today" => "I dag", -"Calendars" => "Kalendere", -"There was a fail, while parsing the file." => "Der opstod en fejl under gennemlæsning af filen.", -"Choose active calendars" => "Vælg aktive kalendere", +"Settings" => "Indstillinger", "Your calendars" => "Dine kalendere", "CalDav Link" => "CalDav-link", "Shared calendars" => "Delte kalendere", @@ -132,10 +160,11 @@ "Interval" => "Interval", "End" => "Afslutning", "occurrences" => "forekomster", -"Import a calendar file" => "Importer en kalenderfil", -"Please choose the calendar" => "Vælg venligst kalender", "create a new calendar" => "opret en ny kalender", +"Import a calendar file" => "Importer en kalenderfil", +"Please choose a calendar" => "Vælg en kalender", "Name of new calendar" => "Navn pÃ¥ ny kalender", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "En kalender med dette navn findes allerede. Hvis du fortsætter alligevel, vil disse kalendere blive sammenlagt.", "Import" => "Importer", "Importing calendar" => "Importerer kalender", "Calendar imported successfully" => "Kalender importeret korrekt", @@ -143,16 +172,15 @@ "Create a new event" => "Opret en ny begivenhed", "View an event" => "Vis en begivenhed", "No categories selected" => "Ingen categorier valgt", -"Select category" => "Vælg kategori", "of" => "fra", "at" => "kl.", +"General" => "Generel", "Timezone" => "Tidszone", -"Check always for changes of the timezone" => "Check altid efter ændringer i tidszone", -"Timeformat" => "Tidsformat", +"Update timezone automatically" => "Opdater tidszone automatisk", "24h" => "24T", "12h" => "12T", -"First day of the week" => "Ugens første dag", -"Calendar CalDAV syncing address:" => "Synkroniseringsadresse til CalDAV:", +"more info" => "flere oplysninger", +"iOS/OS X" => "iOS/OS X", "Users" => "Brugere", "select users" => "Vælg brugere", "Editable" => "Redigerbar", diff --git a/apps/calendar/l10n/de.php b/apps/calendar/l10n/de.php index f12a18baad0..d91753ff74a 100644 --- a/apps/calendar/l10n/de.php +++ b/apps/calendar/l10n/de.php @@ -1,12 +1,23 @@ <?php $TRANSLATIONS = array( -"No calendars found." => "Keine Kalender gefunden", -"No events found." => "Keine Termine gefunden", +"Not all calendars are completely cached" => "Noch sind nicht alle Kalender zwischengespeichert.", +"Everything seems to be completely cached" => "Es sieht so aus, als wäre alles vollständig zwischengespeichert.", +"No calendars found." => "Keine Kalender gefunden.", +"No events found." => "Keine Termine gefunden.", "Wrong calendar" => "Falscher Kalender", +"The file contained either no events or all events are already saved in your calendar." => "Entweder enthielt die Datei keine Termine oder alle Termine waren bereits im Kalender gespeichert.", +"events has been saved in the new calendar" => "Der Termin wurde im neuen Kalender gespeichert.", +"Import failed" => "Import fehlgeschlagen", +"events has been saved in your calendar" => "Der Termin wurde im Kalender gespeichert.", "New Timezone:" => "Neue Zeitzone:", "Timezone changed" => "Zeitzone geändert", "Invalid request" => "Fehlerhafte Anfrage", "Calendar" => "Kalender", -"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "ddd d MMMM[ yyyy]{ -[ddd d] MMMM yyyy}", +"ddd" => "ddd", +"ddd M/d" => "ddd d.M", +"dddd M/d" => "dddd d.M", +"MMMM yyyy" => "MMMM yyyy", +"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, d. MMM yyyy", "Birthday" => "Geburtstag", "Business" => "Geschäftlich", "Call" => "Anruf", @@ -22,7 +33,9 @@ "Projects" => "Projekte", "Questions" => "Fragen", "Work" => "Arbeit", +"by" => "von", "unnamed" => "unbenannt", +"New Calendar" => "Neuer Kalender", "Does not repeat" => "einmalig", "Daily" => "täglich", "Weekly" => "wöchentlich", @@ -67,8 +80,26 @@ "by day and month" => "nach Tag und Monat", "Date" => "Datum", "Cal." => "Kal.", +"Sun." => "So", +"Mon." => "Mo", +"Tue." => "Di", +"Wed." => "Mi", +"Thu." => "Do", +"Fri." => "Fr", +"Sat." => "Sa", +"Jan." => "Jan.", +"Feb." => "Feb.", +"Mar." => "Mär.", +"Apr." => "Apr.", +"May." => "Mai", +"Jun." => "Jun.", +"Jul." => "Jul.", +"Aug." => "Aug.", +"Sep." => "Sep.", +"Oct." => "Okt.", +"Nov." => "Nov.", +"Dec." => "Dez.", "All day" => "Ganztags", -"New Calendar" => "Neuer Kalender", "Missing fields" => "fehlende Felder", "Title" => "Titel", "From Date" => "Startdatum", @@ -81,12 +112,10 @@ "Month" => "Monat", "List" => "Liste", "Today" => "Heute", -"Calendars" => "Kalender", -"There was a fail, while parsing the file." => "Fehler beim Einlesen der Datei.", -"Choose active calendars" => "Aktive Kalender wählen", +"Settings" => "Einstellungen", "Your calendars" => "Deine Kalender", "CalDav Link" => "CalDAV-Link", -"Shared calendars" => "geteilte Kalender", +"Shared calendars" => "Geteilte Kalender", "No shared calendars" => "Keine geteilten Kalender", "Share Calendar" => "Kalender teilen", "Download" => "Herunterladen", @@ -132,10 +161,12 @@ "Interval" => "Intervall", "End" => "Ende", "occurrences" => "Termine", -"Import a calendar file" => "Kalenderdatei Importieren", -"Please choose the calendar" => "Bitte wählen Sie den Kalender.", "create a new calendar" => "Neuen Kalender anlegen", +"Import a calendar file" => "Kalenderdatei importieren", +"Please choose a calendar" => "Wählen Sie bitte einen Kalender.", "Name of new calendar" => "Kalendername", +"Take an available name!" => "Wählen Sie einen verfügbaren Namen.", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Ein Kalender mit diesem Namen existiert bereits. Sollten Sie fortfahren, werden die beiden Kalender zusammengeführt.", "Import" => "Importieren", "Importing calendar" => "Kalender wird importiert.", "Calendar imported successfully" => "Kalender erfolgreich importiert", @@ -146,15 +177,23 @@ "Select category" => "Kategorie auswählen", "of" => "von", "at" => "um", +"General" => "Allgemein", "Timezone" => "Zeitzone", -"Check always for changes of the timezone" => "immer die Zeitzone überprüfen", -"Timeformat" => "Zeitformat", -"24h" => "24h", -"12h" => "12h", -"First day of the week" => "erster Wochentag", -"Calendar CalDAV syncing address:" => "Kalender CalDAV Synchronisationsadresse:", -"Users" => "Nutzer", -"select users" => "Nutzer auswählen", +"Update timezone automatically" => "Zeitzone automatisch aktualisieren", +"Time format" => "Zeitformat", +"24h" => "24 Stunden", +"12h" => "12 Stunden", +"Start week on" => "Erster Wochentag", +"Cache" => "Zwischenspeicher", +"Clear cache for repeating events" => "Lösche den Zwischenspeicher für wiederholende Veranstaltungen", +"URLs" => "URLs", +"Calendar CalDAV syncing addresses" => "CalDAV-Kalender gleicht Adressen ab", +"more info" => "weitere Informationen", +"Primary address (Kontact et al)" => "Primäre Adresse (Kontakt u.a.)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "Nur lesende(r) iCalender-Link(s)", +"Users" => "Benutzer", +"select users" => "Benutzer auswählen", "Editable" => "editierbar", "Groups" => "Gruppen", "select groups" => "Gruppen auswählen", diff --git a/apps/calendar/l10n/el.php b/apps/calendar/l10n/el.php index 0b289fbcf68..ad07d7b5855 100644 --- a/apps/calendar/l10n/el.php +++ b/apps/calendar/l10n/el.php @@ -1,17 +1,28 @@ <?php $TRANSLATIONS = array( +"Not all calendars are completely cached" => "Δεν Îχει δημιουÏγηθεί λανθάνουσα μνήμη για όλα τα ημεÏολόγια", +"Everything seems to be completely cached" => "Όλα Îχουν αποθηκευτεί στη cache", "No calendars found." => "Δε βÏÎθηκαν ημεÏολόγια.", "No events found." => "Δε βÏÎθηκαν γεγονότα.", "Wrong calendar" => "Λάθος ημεÏολόγιο", +"The file contained either no events or all events are already saved in your calendar." => "Το αÏχείο που πεÏιÎχει είτε κανÎνα γεγονός είτε όλα τα γεγονότα Îχουν ήδη αποθηκευτεί στο ημεÏολόγιό σας.", +"events has been saved in the new calendar" => "τα συμβάντα αποθηκεÏτηκαν σε Îνα νÎο ημεÏολόγιο", +"Import failed" => "Η εισαγωγή απÎτυχε", +"events has been saved in your calendar" => "το συμβάν αποθηκεÏτηκε στο ημεÏολογιό σου", "New Timezone:" => "ÎÎα ζώνη ÏŽÏας:", "Timezone changed" => "Η ζώνη ÏŽÏας άλλαξε", "Invalid request" => "Μη ÎγκυÏο αίτημα", "Calendar" => "ΗμεÏολόγιο", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", "Birthday" => "ΓενÎθλια", "Business" => "ΕπιχείÏηση", "Call" => "Κλήση", "Clients" => "Πελάτες", -"Deliverer" => "ΠαÏαδώσας", +"Deliverer" => "Î Ïομηθευτής", "Holidays" => "ΔιακοπÎÏ‚", "Ideas" => "ΙδÎες", "Journey" => "Ταξίδι", @@ -22,7 +33,9 @@ "Projects" => "ΈÏγα", "Questions" => "ΕÏωτήσεις", "Work" => "ΕÏγασία", +"by" => "από", "unnamed" => "ανώνυμο", +"New Calendar" => "ÎÎα ΗμεÏολόγιο", "Does not repeat" => "Μη επαναλαμβανόμενο", "Daily" => "ΚαθημεÏινά", "Weekly" => "Εβδομαδιαία", @@ -67,8 +80,26 @@ "by day and month" => "κατά ημÎÏα και μήνα", "Date" => "ΗμεÏομηνία", "Cal." => "ΗμεÏ.", +"Sun." => "ΚυÏ.", +"Mon." => "Δευ.", +"Tue." => "ΤÏί.", +"Wed." => "Τετ.", +"Thu." => "Î Îμ.", +"Fri." => "ΠαÏ.", +"Sat." => "Σάβ.", +"Jan." => "Ιαν.", +"Feb." => "Φεβ.", +"Mar." => "ΜάÏ.", +"Apr." => "ΑπÏ.", +"May." => "ΜαÎ.", +"Jun." => "ΙοÏν.", +"Jul." => "ΙοÏλ.", +"Aug." => "ΑÏγ.", +"Sep." => "Σεπ.", +"Oct." => "Οκτ.", +"Nov." => "ÎοÎ.", +"Dec." => "Δεκ.", "All day" => "ΟλοήμεÏο", -"New Calendar" => "ÎÎα ΗμεÏολόγιο", "Missing fields" => "Πεδία που λείπουν", "Title" => "Τίτλος", "From Date" => "Από ΗμεÏομηνία", @@ -81,9 +112,6 @@ "Month" => "Μήνας", "List" => "Λίστα", "Today" => "ΣήμεÏα", -"Calendars" => "ΗμεÏολόγια", -"There was a fail, while parsing the file." => "ΥπήÏξε μια αποτυχία, κατά την σάÏωση του αÏχείου.", -"Choose active calendars" => "ΕπιλÎξτε τα ενεÏγά ημεÏολόγια", "Your calendars" => "Τα ημεÏολόγια σου", "CalDav Link" => "ΣÏνδεση CalDAV", "Shared calendars" => "ΚοινόχÏηστα ημεÏολόγια", @@ -132,27 +160,29 @@ "Interval" => "Διάστημα", "End" => "ΤÎλος", "occurrences" => "πεÏιστατικά", -"Import a calendar file" => "Εισαγωγή αÏχείου ημεÏολογίου", -"Please choose the calendar" => "ΠαÏακαλώ επιλÎξτε το ημεÏολόγιο", "create a new calendar" => "δημιουÏγία νÎου ημεÏολογίου", +"Import a calendar file" => "Εισαγωγή αÏχείου ημεÏολογίου", +"Please choose a calendar" => "ΠαÏακαλώ επÎλεξε Îνα ημεÏολόγιο", "Name of new calendar" => "Όνομα νÎου ημεÏολογίου", +"Take an available name!" => "ΕπÎλεξε Îνα διαθÎσιμο όνομα!", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Ένα ημεÏολόγιο με αυτό το όνομα υπάÏχει ήδη. Εάν θÎλετε να συνεχίσετε, αυτά τα 2 ημεÏολόγια θα συγχωνευθοÏν.", "Import" => "Εισαγωγή", -"Importing calendar" => "Εισαγωγή ημεÏολογίου", -"Calendar imported successfully" => "Το ημεÏολόγιο εισήχθει επιτυχώς", "Close Dialog" => "Κλείσιμο Διαλόγου", "Create a new event" => "ΔημιουÏγήστε Îνα νÎο συμβάν", "View an event" => "Εμφάνισε Îνα γεγονός", "No categories selected" => "Δεν επελÎγησαν κατηγοÏίες", -"Select category" => "ΕπιλÎξτε κατηγοÏία", "of" => "του", "at" => "στο", "Timezone" => "Ζώνη ÏŽÏας", -"Check always for changes of the timezone" => "Έλεγχος πάντα για τις αλλαγÎÏ‚ της ζώνης ÏŽÏας", -"Timeformat" => "ΜοÏφή ÏŽÏας", "24h" => "24ω", "12h" => "12ω", -"First day of the week" => "Î Ïώτη μÎÏα της εβδομάδας", -"Calendar CalDAV syncing address:" => "ΔιεÏθυνση για το συγχÏÎ¿Î½Î¹ÏƒÎ¼Î¿Ï Ï„Î¿Ï… ημεÏολογίου CalDAV:", +"Cache" => "Cache", +"Clear cache for repeating events" => "ΕκκαθάÏιση λανθάνουσας μνήμης για επανάληψη γεγονότων", +"Calendar CalDAV syncing addresses" => "ΔιευθÏνσεις συγχÏÎ¿Î½Î¹ÏƒÎ¼Î¿Ï Î·Î¼ÎµÏολογίου CalDAV", +"more info" => "πεÏισσότεÏες πλÏοφοÏίες", +"Primary address (Kontact et al)" => "ΚÏÏια ΔιεÏθυνση(Επαφή και άλλα)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => " iCalendar link(s) μόνο για ανάγνωση", "Users" => "ΧÏήστες", "select users" => "επÎλεξε χÏήστες", "Editable" => "ΕπεξεÏγάσιμο", diff --git a/apps/calendar/l10n/eo.php b/apps/calendar/l10n/eo.php index b1127d59ca9..be3db9bfaeb 100644 --- a/apps/calendar/l10n/eo.php +++ b/apps/calendar/l10n/eo.php @@ -1,12 +1,23 @@ <?php $TRANSLATIONS = array( +"Not all calendars are completely cached" => "Ne ĉiuj kalendaroj estas tute kaÅmemorigitaj", +"Everything seems to be completely cached" => "Ĉio Åajnas tute kaÅmemorigita", "No calendars found." => "Neniu kalendaro troviÄis.", "No events found." => "Neniu okazaĵo troviÄis.", "Wrong calendar" => "MalÄusta kalendaro", -"New Timezone:" => "Nova horzono:", +"The file contained either no events or all events are already saved in your calendar." => "AÅ la dosiero enhavas neniun okazaĵon aŠĉiuj okazaĵoj jam estas konservitaj en via kalendaro.", +"events has been saved in the new calendar" => "okazaĵoj estas konservitaj en la nova kalendaro", +"Import failed" => "Enporto malsukcesis", +"events has been saved in your calendar" => "okazaĵoj estas konservitaj en via kalendaro", +"New Timezone:" => "Nova horozono:", "Timezone changed" => "La horozono estas ÅanÄita", "Invalid request" => "Nevalida peto", "Calendar" => "Kalendaro", +"ddd" => "ddd", +"ddd M/d" => "ddd d/M", +"dddd M/d" => "dddd d/M", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "d MMM[ yyyy]{ '—'d[ MMM] yyyy}", +"dddd, MMM d, yyyy" => "dddd, d-a de MMM yyyy", "Birthday" => "NaskiÄotago", "Business" => "Negoco", "Call" => "Voko", @@ -22,7 +33,9 @@ "Projects" => "Projektoj", "Questions" => "Demandoj", "Work" => "Laboro", +"by" => "de", "unnamed" => "nenomita", +"New Calendar" => "Nova kalendaro", "Does not repeat" => "Ĉi tio ne ripetiÄas", "Daily" => "Tage", "Weekly" => "Semajne", @@ -67,8 +80,26 @@ "by day and month" => "laÅ tago kaj monato", "Date" => "Dato", "Cal." => "Kal.", +"Sun." => "dim.", +"Mon." => "lun.", +"Tue." => "mar.", +"Wed." => "mer.", +"Thu." => "ĵaÅ.", +"Fri." => "ven.", +"Sat." => "sab.", +"Jan." => "Jan.", +"Feb." => "Feb.", +"Mar." => "Mar.", +"Apr." => "Apr.", +"May." => "Maj.", +"Jun." => "Jun.", +"Jul." => "Jul.", +"Aug." => "AÅg.", +"Sep." => "Sep.", +"Oct." => "Okt.", +"Nov." => "Nov.", +"Dec." => "Dec.", "All day" => "La tuta tago", -"New Calendar" => "Nova kalendaro", "Missing fields" => "Mankas iuj kampoj", "Title" => "Titolo", "From Date" => "ekde la dato", @@ -81,9 +112,7 @@ "Month" => "Monato", "List" => "Listo", "Today" => "HodiaÅ", -"Calendars" => "Kalendaroj", -"There was a fail, while parsing the file." => "Malsukceso okazis dum analizo de la dosiero.", -"Choose active calendars" => "Elektu aktivajn kalendarojn", +"Settings" => "Agordo", "Your calendars" => "Viaj kalendaroj", "CalDav Link" => "CalDav-a ligilo", "Shared calendars" => "Kunhavigitaj kalendaroj", @@ -132,27 +161,34 @@ "Interval" => "Intervalo", "End" => "Fino", "occurrences" => "aperoj", -"Import a calendar file" => "Enporti kalendarodosieron", -"Please choose the calendar" => "Bonvolu elekti kalendaron", "create a new calendar" => "Krei novan kalendaron", +"Import a calendar file" => "Enporti kalendarodosieron", +"Please choose a calendar" => "Bonvolu elekti kalendaron", "Name of new calendar" => "Nomo de la nova kalendaro", +"Take an available name!" => "Prenu haveblan nomon!", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Kalendaro kun ĉi tiu nomo jam ekzastas. Se vi malgraÅe daÅros, ĉi tiuj kalendaroj kunfandiÄos.", "Import" => "Enporti", -"Importing calendar" => "Kalendaro estas enportata", -"Calendar imported successfully" => "La kalendaro enportiÄis sukcese", "Close Dialog" => "Fermi la dialogon", "Create a new event" => "Krei okazaĵon", "View an event" => "Vidi okazaĵon", "No categories selected" => "Neniu kategorio elektita", -"Select category" => "Elekti kategorion", "of" => "de", "at" => "ĉe", +"General" => "Äœenerala", "Timezone" => "Horozono", -"Check always for changes of the timezone" => "Ĉiam kontroli ĉu la horzono ÅanÄiÄis", -"Timeformat" => "Tempoformo", +"Update timezone automatically" => "AÅtomate Äisdatigi la horozonon", +"Time format" => "Horoformo", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Unua tago de la semajno", -"Calendar CalDAV syncing address:" => "Adreso de kalendarosinkronigo per CalDAV:", +"Start week on" => "Komenci semajnon je", +"Cache" => "KaÅmemoro", +"Clear cache for repeating events" => "ForviÅi kaÅmemoron por ripeto de okazaĵoj", +"URLs" => "URL-oj", +"Calendar CalDAV syncing addresses" => "sinkronigaj adresoj por CalDAV-kalendaroj", +"more info" => "pli da informo", +"Primary address (Kontact et al)" => "Ĉefa adreso (Kontact kaj aliaj)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "Nurlegebla(j) iCalendar-ligilo(j)", "Users" => "Uzantoj", "select users" => "elekti uzantojn", "Editable" => "Redaktebla", diff --git a/apps/calendar/l10n/es.php b/apps/calendar/l10n/es.php index 4cd9e3202bf..3ebcd2e9430 100644 --- a/apps/calendar/l10n/es.php +++ b/apps/calendar/l10n/es.php @@ -1,12 +1,23 @@ <?php $TRANSLATIONS = array( +"Not all calendars are completely cached" => "Aún no se han guardado en caché todos los calendarios", +"Everything seems to be completely cached" => "Parece que se ha guardado todo en caché", "No calendars found." => "No se encontraron calendarios.", "No events found." => "No se encontraron eventos.", "Wrong calendar" => "Calendario incorrecto", +"The file contained either no events or all events are already saved in your calendar." => "El archivo no contiene eventos o ya existen en tu calendario.", +"events has been saved in the new calendar" => "Los eventos han sido guardados en el nuevo calendario", +"Import failed" => "Fallo en la importación", +"events has been saved in your calendar" => "eventos se han guardado en tu calendario", "New Timezone:" => "Nueva zona horaria:", "Timezone changed" => "Zona horaria cambiada", "Invalid request" => "Petición no válida", "Calendar" => "Calendario", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", "Birthday" => "Cumpleaños", "Business" => "Negocios", "Call" => "Llamada", @@ -22,7 +33,9 @@ "Projects" => "Proyectos", "Questions" => "Preguntas", "Work" => "Trabajo", +"by" => "por", "unnamed" => "Sin nombre", +"New Calendar" => "Nuevo calendario", "Does not repeat" => "No se repite", "Daily" => "Diariamente", "Weekly" => "Semanalmente", @@ -67,8 +80,26 @@ "by day and month" => "por dÃa y mes", "Date" => "Fecha", "Cal." => "Cal.", +"Sun." => "Dom.", +"Mon." => "Lun.", +"Tue." => "Mar.", +"Wed." => "Mier.", +"Thu." => "Jue.", +"Fri." => "Vie.", +"Sat." => "Sab.", +"Jan." => "Ene.", +"Feb." => "Feb.", +"Mar." => "Mar.", +"Apr." => "Abr.", +"May." => "May.", +"Jun." => "Jun.", +"Jul." => "Jul.", +"Aug." => "Ago.", +"Sep." => "Sep.", +"Oct." => "Oct.", +"Nov." => "Nov.", +"Dec." => "Dic.", "All day" => "Todo el dÃa", -"New Calendar" => "Nuevo calendario", "Missing fields" => "Los campos que faltan", "Title" => "TÃtulo", "From Date" => "Desde la fecha", @@ -81,9 +112,6 @@ "Month" => "Mes", "List" => "Lista", "Today" => "Hoy", -"Calendars" => "Calendarios", -"There was a fail, while parsing the file." => "Se ha producido un fallo al analizar el archivo.", -"Choose active calendars" => "Elige los calendarios activos", "Your calendars" => "Tus calendarios", "CalDav Link" => "Enlace a CalDav", "Shared calendars" => "Calendarios compartidos", @@ -132,27 +160,29 @@ "Interval" => "Intervalo", "End" => "Fin", "occurrences" => "ocurrencias", -"Import a calendar file" => "Importar un archivo de calendario", -"Please choose the calendar" => "Por favor elige el calendario", "create a new calendar" => "Crear un nuevo calendario", +"Import a calendar file" => "Importar un archivo de calendario", +"Please choose a calendar" => "Por favor, escoge un calendario", "Name of new calendar" => "Nombre del nuevo calendario", +"Take an available name!" => "¡Elige un nombre disponible!", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Ya existe un calendario con este nombre. Si continúas, se combinarán los calendarios.", "Import" => "Importar", -"Importing calendar" => "Importando calendario", -"Calendar imported successfully" => "Calendario importado exitosamente", "Close Dialog" => "Cerrar diálogo", "Create a new event" => "Crear un nuevo evento", "View an event" => "Ver un evento", "No categories selected" => "Ninguna categorÃa seleccionada", -"Select category" => "Seleccionar categorÃa", "of" => "de", "at" => "a las", "Timezone" => "Zona horaria", -"Check always for changes of the timezone" => "Comprobar siempre por cambios en la zona horaria", -"Timeformat" => "Formato de hora", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Primer dÃa de la semana", -"Calendar CalDAV syncing address:" => "Dirección de sincronización de calendario CalDAV:", +"Cache" => "Caché", +"Clear cache for repeating events" => "Limpiar caché de eventos recurrentes", +"Calendar CalDAV syncing addresses" => "Direcciones de sincronización de calendario CalDAV:", +"more info" => "Más información", +"Primary address (Kontact et al)" => "Dirección principal (Kontact y otros)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "Enlace(s) iCalendar de sólo lectura", "Users" => "Usuarios", "select users" => "seleccionar usuarios", "Editable" => "Editable", diff --git a/apps/calendar/l10n/et_EE.php b/apps/calendar/l10n/et_EE.php index 931ca56f5fd..59f494f8ab9 100644 --- a/apps/calendar/l10n/et_EE.php +++ b/apps/calendar/l10n/et_EE.php @@ -6,7 +6,12 @@ "Timezone changed" => "Ajavöönd on muudetud", "Invalid request" => "Vigane päring", "Calendar" => "Kalender", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", "Birthday" => "Sünnipäev", "Business" => "Äri", "Call" => "Helista", @@ -23,6 +28,7 @@ "Questions" => "Küsimused", "Work" => "Töö", "unnamed" => "nimetu", +"New Calendar" => "Uus kalender", "Does not repeat" => "Ei kordu", "Daily" => "Iga päev", "Weekly" => "Iga nädal", @@ -68,7 +74,6 @@ "Date" => "Kuupäev", "Cal." => "Kal.", "All day" => "Kogu päev", -"New Calendar" => "Uus kalender", "Missing fields" => "Puuduvad väljad", "Title" => "Pealkiri", "From Date" => "Alates kuupäevast", @@ -81,9 +86,6 @@ "Month" => "Kuu", "List" => "Nimekiri", "Today" => "Täna", -"Calendars" => "Kalendrid", -"There was a fail, while parsing the file." => "Faili parsimisel tekkis viga.", -"Choose active calendars" => "Vali aktiivsed kalendrid", "Your calendars" => "Sinu kalendrid", "CalDav Link" => "CalDav Link", "Shared calendars" => "Jagatud kalendrid", @@ -132,27 +134,19 @@ "Interval" => "Intervall", "End" => "Lõpp", "occurrences" => "toimumiskordi", -"Import a calendar file" => "Impordi kalendrifail", -"Please choose the calendar" => "Palun vali kalender", "create a new calendar" => "loo uus kalender", +"Import a calendar file" => "Impordi kalendrifail", "Name of new calendar" => "Uue kalendri nimi", "Import" => "Impordi", -"Importing calendar" => "Kalendri importimine", -"Calendar imported successfully" => "Kalender on imporditud", "Close Dialog" => "Sulge dialoogiaken", "Create a new event" => "Loo sündmus", "View an event" => "Vaata üritust", "No categories selected" => "Ãœhtegi kategooriat pole valitud", -"Select category" => "Salvesta kategooria", "of" => "/", "at" => "kell", "Timezone" => "Ajavöönd", -"Check always for changes of the timezone" => "Kontrolli alati muudatusi ajavööndis", -"Timeformat" => "Aja vorming", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Nädala esimene päev", -"Calendar CalDAV syncing address:" => "Kalendri CalDAV sünkroniseerimise aadress:", "Users" => "Kasutajad", "select users" => "valitud kasutajad", "Editable" => "Muudetav", diff --git a/apps/calendar/l10n/eu.php b/apps/calendar/l10n/eu.php index 9e1300032f8..5ebce09c58b 100644 --- a/apps/calendar/l10n/eu.php +++ b/apps/calendar/l10n/eu.php @@ -1,11 +1,18 @@ <?php $TRANSLATIONS = array( +"Not all calendars are completely cached" => "Egutegi guztiak ez daude guztiz cacheatuta", +"Everything seems to be completely cached" => "Dena guztiz cacheatuta dagoela dirudi", "No calendars found." => "Ez da egutegirik aurkitu.", "No events found." => "Ez da gertaerarik aurkitu.", "Wrong calendar" => "Egutegi okerra", +"The file contained either no events or all events are already saved in your calendar." => "Fitxategiak ez zuen gertaerarik edo gertaera guztiak dagoeneko egutegian gordeta zeuden.", +"events has been saved in the new calendar" => "gertaerak egutegi berrian gorde dira", +"Import failed" => "Inportazioak huts egin du", +"events has been saved in your calendar" => "gertaerak zure egutegian gorde dira", "New Timezone:" => "Ordu-zonalde berria", "Timezone changed" => "Ordu-zonaldea aldatuta", "Invalid request" => "Baliogabeko eskaera", "Calendar" => "Egutegia", +"MMMM yyyy" => "yyyy MMMM", "Birthday" => "Jaioteguna", "Business" => "Negozioa", "Call" => "Deia", @@ -22,6 +29,7 @@ "Questions" => "Galderak", "Work" => "Lana", "unnamed" => "izengabea", +"New Calendar" => "Egutegi berria", "Does not repeat" => "Ez da errepikatzen", "Daily" => "Egunero", "Weekly" => "Astero", @@ -66,8 +74,26 @@ "by day and month" => "eguna eta hilabetearen arabera", "Date" => "Data", "Cal." => "Eg.", +"Sun." => "ig.", +"Mon." => "al.", +"Tue." => "ar.", +"Wed." => "az.", +"Thu." => "og.", +"Fri." => "ol.", +"Sat." => "lr.", +"Jan." => "urt.", +"Feb." => "ots.", +"Mar." => "mar.", +"Apr." => "api.", +"May." => "mai.", +"Jun." => "eka.", +"Jul." => "uzt.", +"Aug." => "abu.", +"Sep." => "ira.", +"Oct." => "urr.", +"Nov." => "aza.", +"Dec." => "abe.", "All day" => "Egun guztia", -"New Calendar" => "Egutegi berria", "Missing fields" => "Eremuak faltan", "Title" => "Izenburua", "From Date" => "Hasierako Data", @@ -80,9 +106,6 @@ "Month" => "Hilabetea", "List" => "Zerrenda", "Today" => "Gaur", -"Calendars" => "Egutegiak", -"There was a fail, while parsing the file." => "Huts bat egon da, fitxategia aztertzen zen bitartea.", -"Choose active calendars" => "Aukeratu egutegi aktiboak", "Your calendars" => "Zure egutegiak", "CalDav Link" => "CalDav lotura", "Shared calendars" => "Elkarbanatutako egutegiak", @@ -131,25 +154,26 @@ "Interval" => "Tartea", "End" => "Amaiera", "occurrences" => "errepikapenak", -"Import a calendar file" => "Inportatu egutegi fitxategi bat", -"Please choose the calendar" => "Mesedez aukeratu egutegia", "create a new calendar" => "sortu egutegi berria", +"Import a calendar file" => "Inportatu egutegi fitxategi bat", +"Please choose a calendar" => "Mesedez aukeratu egutegi bat.", "Name of new calendar" => "Egutegi berriaren izena", +"Take an available name!" => "Hartu eskuragarri dagoen izen bat!", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Izen hau duen egutegi bat dagoeneko existitzen da. Hala ere jarraitzen baduzu, egutegi hauek elkartuko dira.", "Import" => "Importatu", -"Importing calendar" => "Egutegia inportatzen", -"Calendar imported successfully" => "Egutegia ongi inportatu da", "Close Dialog" => "Itxi lehioa", "Create a new event" => "Sortu gertaera berria", "View an event" => "Ikusi gertaera bat", "No categories selected" => "Ez da kategoriarik hautatu", -"Select category" => "Aukeratu kategoria", "Timezone" => "Ordu-zonaldea", -"Check always for changes of the timezone" => "Egiaztatu beti ordu-zonalde aldaketen bila", -"Timeformat" => "Ordu formatua", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Asteko lehenego eguna", -"Calendar CalDAV syncing address:" => "Egutegiaren CalDAV sinkronizazio helbidea", +"Cache" => "Cache", +"Clear cache for repeating events" => "Ezabatu gertaera errepikakorren cachea", +"Calendar CalDAV syncing addresses" => "Egutegiaren CalDAV sinkronizazio helbideak", +"more info" => "informazio gehiago", +"Primary address (Kontact et al)" => "Helbide nagusia", +"iOS/OS X" => "iOS/OS X", "Users" => "Erabiltzaileak", "select users" => "hautatutako erabiltzaileak", "Editable" => "Editagarria", diff --git a/apps/calendar/l10n/fa.php b/apps/calendar/l10n/fa.php index cd2bb9c2e5a..9235460834b 100644 --- a/apps/calendar/l10n/fa.php +++ b/apps/calendar/l10n/fa.php @@ -6,7 +6,12 @@ "Timezone changed" => "زمان Ù…ØÙ„ÛŒ تغییر یاÙت", "Invalid request" => "درخواست نامعتبر", "Calendar" => "تقویم", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "DDD m[ yyyy]{ '—'[ DDD] m yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", "Birthday" => "روزتولد", "Business" => "تجارت", "Call" => "تماس گرÙتن", @@ -23,6 +28,7 @@ "Questions" => "سوالات", "Work" => "کار", "unnamed" => "نام گذاری نشده", +"New Calendar" => "تقویم جدید", "Does not repeat" => "تکرار نکنید", "Daily" => "روزانه", "Weekly" => "Ù‡Ùتهگی", @@ -68,7 +74,6 @@ "Date" => "تاریخ", "Cal." => "تقویم.", "All day" => "هرروز", -"New Calendar" => "تقویم جدید", "Missing fields" => "Ùیلد های Ú¯Ù… شده", "Title" => "عنوان", "From Date" => "از تاریخ", @@ -81,9 +86,6 @@ "Month" => "ماه", "List" => "Ùهرست", "Today" => "امروز", -"Calendars" => "تقویم ها", -"There was a fail, while parsing the file." => "ناتوان در تجزیه پرونده", -"Choose active calendars" => "تقویم Ùعال را انتخاب کنید", "Your calendars" => "تقویم های شما", "CalDav Link" => "CalDav Link", "Shared calendars" => "تقویمهای به اشترک گذاری شده", @@ -132,27 +134,19 @@ "Interval" => "Ùاصله", "End" => "پایان", "occurrences" => "ظهور", -"Import a calendar file" => "یک پرونده Øاوی تقویم وارد کنید", -"Please choose the calendar" => "لطÙا تقویم را انتخاب کنید", "create a new calendar" => "یک تقویم جدید ایجاد کنید", +"Import a calendar file" => "یک پرونده Øاوی تقویم وارد کنید", "Name of new calendar" => "نام تقویم جدید", "Import" => "ورودی دادن", -"Importing calendar" => "درØال اÙزودن تقویم", -"Calendar imported successfully" => "اÙزودن تقویم موÙقیت آمیز بود", "Close Dialog" => "بستن دیالوگ", "Create a new event" => "یک رویداد ایجاد کنید", "View an event" => "دیدن یک رویداد", "No categories selected" => "هیچ گروهی انتخاب نشده", -"Select category" => "انتخاب گروه", "of" => "از", "at" => "در", "Timezone" => "زمان Ù…ØÙ„ÛŒ", -"Check always for changes of the timezone" => "همیشه بررسی کنید برای تغییر زمان Ù…ØÙ„ÛŒ", -"Timeformat" => "نوع زمان", "24h" => "24 ساعت", "12h" => "12 ساعت", -"First day of the week" => "یکمین روز Ù‡Ùته", -"Calendar CalDAV syncing address:" => "Calendar CalDAV syncing address :", "Users" => "کاربرها", "select users" => "انتخاب شناسه ها", "Editable" => "قابل ویرایش", diff --git a/apps/calendar/l10n/fi_FI.php b/apps/calendar/l10n/fi_FI.php index 4de94b7b7b7..c4c9df3588a 100644 --- a/apps/calendar/l10n/fi_FI.php +++ b/apps/calendar/l10n/fi_FI.php @@ -2,6 +2,9 @@ "No calendars found." => "Kalentereita ei löytynyt", "No events found." => "Tapahtumia ei löytynyt.", "Wrong calendar" => "Väärä kalenteri", +"The file contained either no events or all events are already saved in your calendar." => "Tiedosto ei joko sisältänyt tapahtumia tai vaihtoehtoisesti kaikki tapahtumat on jo tallennettu kalenteriisi.", +"Import failed" => "Tuonti epäonnistui", +"events has been saved in your calendar" => "tapahtumaa on tallennettu kalenteriisi", "New Timezone:" => "Uusi aikavyöhyke:", "Timezone changed" => "Aikavyöhyke vaihdettu", "Invalid request" => "Virheellinen pyyntö", @@ -21,6 +24,7 @@ "Questions" => "Kysymykset", "Work" => "Työ", "unnamed" => "nimetön", +"New Calendar" => "Uusi kalenteri", "Does not repeat" => "Ei toistoa", "Daily" => "Päivittäin", "Weekly" => "Viikottain", @@ -55,8 +59,26 @@ "November" => "Marraskuu", "December" => "Joulukuu", "Date" => "Päivämäärä", +"Sun." => "Su", +"Mon." => "Ma", +"Tue." => "Ti", +"Wed." => "Ke", +"Thu." => "To", +"Fri." => "Pe", +"Sat." => "La", +"Jan." => "Tammi", +"Feb." => "Helmi", +"Mar." => "Maalis", +"Apr." => "Huhti", +"May." => "Touko", +"Jun." => "Kesä", +"Jul." => "Heinä", +"Aug." => "Elo", +"Sep." => "Syys", +"Oct." => "Loka", +"Nov." => "Marras", +"Dec." => "Joulu", "All day" => "Koko päivä", -"New Calendar" => "Uusi kalenteri", "Missing fields" => "Puuttuvat kentät", "Title" => "Otsikko", "The event ends before it starts" => "Tapahtuma päättyy ennen alkamistaan", @@ -65,9 +87,7 @@ "Month" => "Kuukausi", "List" => "Lista", "Today" => "Tänään", -"Calendars" => "Kalenterit", -"There was a fail, while parsing the file." => "Tiedostoa jäsennettäessä tapahtui virhe.", -"Choose active calendars" => "Valitse aktiiviset kalenterit", +"Settings" => "Asetukset", "Your calendars" => "Omat kalenterisi", "CalDav Link" => "CalDav-linkki", "Shared calendars" => "Jaetut kalenterit", @@ -110,25 +130,26 @@ "Select months" => "Valitse kuukaudet", "Select weeks" => "Valitse viikot", "Interval" => "Intervalli", -"Import a calendar file" => "Tuo kalenteritiedosto", -"Please choose the calendar" => "Valitse kalenteri", "create a new calendar" => "luo uusi kalenteri", +"Import a calendar file" => "Tuo kalenteritiedosto", +"Please choose a calendar" => "Valitse kalenteri", "Name of new calendar" => "Uuden kalenterin nimi", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Samalla nimellä on jo olemassa kalenteri. Jos jatkat kaikesta huolimatta, kalenterit yhdistetään.", "Import" => "Tuo", -"Importing calendar" => "Tuodaan kalenteria", -"Calendar imported successfully" => "Kalenteri tuotu onnistuneesti", "Close Dialog" => "Sulje ikkuna", "Create a new event" => "Luo uusi tapahtuma", "View an event" => "Avaa tapahtuma", "No categories selected" => "Luokkia ei ole valittu", -"Select category" => "Valitse luokka", +"General" => "Yleiset", "Timezone" => "Aikavyöhyke", -"Check always for changes of the timezone" => "Tarkista aina aikavyöhykkeen muutokset", -"Timeformat" => "Ajan esitysmuoto", +"Update timezone automatically" => "Päivitä aikavyöhykkeet automaattisesti", +"Time format" => "Ajan näyttömuoto", "24h" => "24 tuntia", "12h" => "12 tuntia", -"First day of the week" => "Viikon ensimmäinen päivä", -"Calendar CalDAV syncing address:" => "Kalenterin CalDAV-synkronointiosoite:", +"Start week on" => "Viikon alkamispäivä", +"Calendar CalDAV syncing addresses" => "Kalenterin CalDAV-synkronointiosoitteet", +"Primary address (Kontact et al)" => "Ensisijainen osoite (Kontact ja muut vastaavat)", +"iOS/OS X" => "iOS/OS X", "Users" => "Käyttäjät", "select users" => "valitse käyttäjät", "Editable" => "Muoktattava", diff --git a/apps/calendar/l10n/fr.php b/apps/calendar/l10n/fr.php index 506453af428..90ba903b876 100644 --- a/apps/calendar/l10n/fr.php +++ b/apps/calendar/l10n/fr.php @@ -1,12 +1,23 @@ <?php $TRANSLATIONS = array( +"Not all calendars are completely cached" => "Tous les calendriers ne sont pas mis en cache", +"Everything seems to be completely cached" => "Tout semble être en cache", "No calendars found." => "Aucun calendrier n'a été trouvé.", "No events found." => "Aucun événement n'a été trouvé.", "Wrong calendar" => "Mauvais calendrier", +"The file contained either no events or all events are already saved in your calendar." => "Soit le fichier ne contient aucun événement soit tous les événements sont déjà enregistrés dans votre calendrier.", +"events has been saved in the new calendar" => "Les événements ont été enregistrés dans le nouveau calendrier", +"Import failed" => "Échec de l'import", +"events has been saved in your calendar" => "Les événements ont été enregistrés dans votre calendrier", "New Timezone:" => "Nouveau fuseau horaire :", "Timezone changed" => "Fuseau horaire modifié", "Invalid request" => "Requête invalide", "Calendar" => "Calendrier", +"ddd" => "ddd", +"ddd M/d" => "ddd d/M", +"dddd M/d" => "dddd d/M", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, d MMM, yyyy", "Birthday" => "Anniversaire", "Business" => "Professionnel", "Call" => "Appel", @@ -22,7 +33,9 @@ "Projects" => "Projets", "Questions" => "Questions", "Work" => "Travail", +"by" => "par", "unnamed" => "sans-nom", +"New Calendar" => "Nouveau Calendrier", "Does not repeat" => "Pas de répétition", "Daily" => "Tous les jours", "Weekly" => "Hebdomadaire", @@ -67,8 +80,26 @@ "by day and month" => "par jour et mois", "Date" => "Date", "Cal." => "Cal.", +"Sun." => "Dim.", +"Mon." => "Lun.", +"Tue." => "Mar.", +"Wed." => "Mer.", +"Thu." => "Jeu", +"Fri." => "Ven.", +"Sat." => "Sam.", +"Jan." => "Jan.", +"Feb." => "Fév.", +"Mar." => "Mars", +"Apr." => "Avr.", +"May." => "Mai", +"Jun." => "Juin", +"Jul." => "Juil.", +"Aug." => "Août", +"Sep." => "Sep.", +"Oct." => "Oct.", +"Nov." => "Nov.", +"Dec." => "Déc.", "All day" => "Journée entière", -"New Calendar" => "Nouveau Calendrier", "Missing fields" => "Champs manquants", "Title" => "Titre", "From Date" => "De la date", @@ -81,9 +112,6 @@ "Month" => "Mois", "List" => "Liste", "Today" => "Aujourd'hui", -"Calendars" => "Calendriers", -"There was a fail, while parsing the file." => "Une erreur est survenue pendant la lecture du fichier.", -"Choose active calendars" => "Choix des calendriers actifs", "Your calendars" => "Vos calendriers", "CalDav Link" => "Lien CalDav", "Shared calendars" => "Calendriers partagés", @@ -132,27 +160,29 @@ "Interval" => "Intervalle", "End" => "Fin", "occurrences" => "occurrences", -"Import a calendar file" => "Importer un fichier de calendriers", -"Please choose the calendar" => "Choisissez le calendrier svp", "create a new calendar" => "Créer un nouveau calendrier", +"Import a calendar file" => "Importer un fichier de calendriers", +"Please choose a calendar" => "Veuillez sélectionner un calendrier", "Name of new calendar" => "Nom pour le nouveau calendrier", +"Take an available name!" => "Choisissez un nom disponible !", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Un calendrier de ce nom existe déjà . Si vous choisissez de continuer les calendriers seront fusionnés.", "Import" => "Importer", -"Importing calendar" => "Import du calendrier", -"Calendar imported successfully" => "Calendrier importé avec succès", "Close Dialog" => "Fermer la fenêtre", "Create a new event" => "Créer un nouvel événement", "View an event" => "Voir un événement", "No categories selected" => "Aucune catégorie sélectionnée", -"Select category" => "Sélectionner une catégorie", "of" => "de", "at" => "à ", "Timezone" => "Fuseau horaire", -"Check always for changes of the timezone" => "Toujours vérifier d'éventuels changements de fuseau horaire", -"Timeformat" => "Format de l'heure", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Premier jour de la semaine", -"Calendar CalDAV syncing address:" => "Adresse de synchronisation du calendrier CalDAV :", +"Cache" => "Cache", +"Clear cache for repeating events" => "Nettoyer le cache des événements répétitifs", +"Calendar CalDAV syncing addresses" => "Adresses de synchronisation des calendriers CalDAV", +"more info" => "plus d'infos", +"Primary address (Kontact et al)" => "Adresses principales (Kontact et assimilés)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "lien(s) iCalendar en lecture seule", "Users" => "Utilisateurs", "select users" => "sélectionner les utilisateurs", "Editable" => "Modifiable", diff --git a/apps/calendar/l10n/gl.php b/apps/calendar/l10n/gl.php index 3178b1819ec..00a28cc70f4 100644 --- a/apps/calendar/l10n/gl.php +++ b/apps/calendar/l10n/gl.php @@ -6,7 +6,12 @@ "Timezone changed" => "Fuso horario trocado", "Invalid request" => "Petición non válida", "Calendar" => "Calendario", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "d MMM[ yyyy]{ '—'d [ MMM] yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d,yyyy", "Birthday" => "Aniversario", "Business" => "Traballo", "Call" => "Chamada", @@ -23,6 +28,7 @@ "Questions" => "Preguntas", "Work" => "Traballo", "unnamed" => "sen nome", +"New Calendar" => "Novo calendario", "Does not repeat" => "Non se repite", "Daily" => "A diario", "Weekly" => "Semanalmente", @@ -68,7 +74,6 @@ "Date" => "Data", "Cal." => "Cal.", "All day" => "Todo o dia", -"New Calendar" => "Novo calendario", "Missing fields" => "Faltan campos", "Title" => "TÃtulo", "From Date" => "Desde a data", @@ -132,6 +137,7 @@ "Interval" => "Intervalo", "End" => "Fin", "occurrences" => "acontecementos", +"create a new calendar" => "crear un novo calendario", "Import a calendar file" => "Importar un ficheiro de calendario", "Please choose the calendar" => "Por favor, seleccione o calendario", "create a new calendar" => "crear un novo calendario", @@ -143,7 +149,6 @@ "Create a new event" => "Crear un novo evento", "View an event" => "Ver un evento", "No categories selected" => "Non seleccionou as categorÃas", -"Select category" => "Seleccionar categorÃa", "of" => "de", "at" => "a", "Timezone" => "Fuso horario", @@ -151,8 +156,6 @@ "Timeformat" => "Formato de hora", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Primeiro dÃa da semana", -"Calendar CalDAV syncing address:" => "Enderezo de sincronización do calendario CalDAV:", "Users" => "Usuarios", "select users" => "escoller usuarios", "Editable" => "Editable", diff --git a/apps/calendar/l10n/he.php b/apps/calendar/l10n/he.php index c161d3be2ef..d5c0b2b2e53 100644 --- a/apps/calendar/l10n/he.php +++ b/apps/calendar/l10n/he.php @@ -6,7 +6,12 @@ "Timezone changed" => "×זור זמן ×”×©×ª× ×”", "Invalid request" => "בקשה ×œ× ×—×•×§×™×ª", "Calendar" => "×— ×©× ×”", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "d MMM [ yyyy]{ '—'d[ MMM] yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", "Birthday" => "×™×•× ×”×•×œ×“×ª", "Business" => "עסקי×", "Call" => "שיחה", @@ -23,6 +28,7 @@ "Questions" => "ש×לות", "Work" => "עבודה", "unnamed" => "×œ×œ× ×©×", +"New Calendar" => "לוח ×©× ×” חדש", "Does not repeat" => "×œ×œ× ×—×–×¨×”", "Daily" => "יומי", "Weekly" => "שבועי", @@ -68,7 +74,6 @@ "Date" => "ת×ריך", "Cal." => "לוח ×©× ×”", "All day" => "היו×", -"New Calendar" => "לוח ×©× ×” חדש", "Missing fields" => "שדות חסרי×", "Title" => "כותרת", "From Date" => "מת×ריך", @@ -81,9 +86,6 @@ "Month" => "חודש", "List" => "רשימה", "Today" => "היו×", -"Calendars" => "לוחות ×©× ×”", -"There was a fail, while parsing the file." => "×ירעה שגי××” בעת ×¤×¢× ×•×— הקובץ.", -"Choose active calendars" => "בחר לוחות ×©× ×” פעילי×", "Your calendars" => "לוחות ×”×©× ×” שלך", "CalDav Link" => "קישור CalDav", "Shared calendars" => "לוחות ×©× ×” מושתפי×", @@ -132,27 +134,19 @@ "Interval" => "משך", "End" => "סיו×", "occurrences" => "מופעי×", -"Import a calendar file" => "×™×‘×•× ×§×•×‘×¥ לוח ×©× ×”", -"Please choose the calendar" => "× × ×œ×‘×—×•×¨ ×ת לוח ×”×©× ×”", "create a new calendar" => "יצירת לוח ×©× ×” חדש", +"Import a calendar file" => "×™×‘×•× ×§×•×‘×¥ לוח ×©× ×”", "Name of new calendar" => "×©× ×œ×•×— ×”×©× ×” החדש", "Import" => "יבו×", -"Importing calendar" => "היומן מייוב×", -"Calendar imported successfully" => "היומן ×™×™×•×‘× ×‘×”×¦×œ×—×”", "Close Dialog" => "סגירת הדו־שיח", "Create a new event" => "יצירת ×ירוע חדש", "View an event" => "צפייה ב×ירוע", "No categories selected" => "×œ× × ×‘×—×¨×• קטגוריות", -"Select category" => "בחר קטגוריה", "of" => "מתוך", "at" => "×‘×©× ×”", "Timezone" => "×זור זמן", -"Check always for changes of the timezone" => "יש לבדוק תמיד ×× ×™×© ×”×‘×“×œ×™× ×‘×זורי הזמן", -"Timeformat" => "×ž×‘× ×” הת×ריך", "24h" => "24 שעות", "12h" => "12 שעות", -"First day of the week" => "×”×™×•× ×”×¨×שון בשבוע", -"Calendar CalDAV syncing address:" => "כתובת ×”×¡× ×›×¨×•×Ÿ ללוח ×©× ×” מסוג CalDAV:", "Users" => "משתמשי×", "select users" => "× × ×œ×‘×—×•×¨ במשתמשי×", "Editable" => "× ×™×ª×Ÿ לעריכה", diff --git a/apps/calendar/l10n/hr.php b/apps/calendar/l10n/hr.php index 551bb4abbcb..07512b96051 100644 --- a/apps/calendar/l10n/hr.php +++ b/apps/calendar/l10n/hr.php @@ -23,6 +23,7 @@ "Questions" => "Pitanja", "Work" => "Posao", "unnamed" => "bezimeno", +"New Calendar" => "Novi kalendar", "Does not repeat" => "Ne ponavlja se", "Daily" => "Dnevno", "Weekly" => "Tjedno", @@ -67,7 +68,6 @@ "Date" => "datum", "Cal." => "Kal.", "All day" => "Cijeli dan", -"New Calendar" => "Novi kalendar", "Missing fields" => "Nedostaju polja", "Title" => "Naslov", "From Date" => "Datum od", @@ -80,9 +80,6 @@ "Month" => "Mjesec", "List" => "Lista", "Today" => "Danas", -"Calendars" => "Kalendari", -"There was a fail, while parsing the file." => "PogreÅ¡ka pri Äitanju datoteke.", -"Choose active calendars" => "Odabir aktivnih kalendara", "Your calendars" => "VaÅ¡i kalendari", "CalDav Link" => "CalDav poveznica", "Shared calendars" => "Podijeljeni kalendari", @@ -128,27 +125,20 @@ "Interval" => "Interval", "End" => "Kraj", "occurrences" => "pojave", -"Import a calendar file" => "Uvozite datoteku kalendara", -"Please choose the calendar" => "Odaberi kalendar", "create a new calendar" => "stvori novi kalendar", +"Import a calendar file" => "Uvozite datoteku kalendara", "Name of new calendar" => "Ime novog kalendara", "Import" => "Uvoz", -"Importing calendar" => "Uvoz kalendara", -"Calendar imported successfully" => "Kalendar je uspjeÅ¡no uvezen", "Close Dialog" => "Zatvori dijalog", "Create a new event" => "Unesi novi dogaÄ‘aj", "View an event" => "Vidjeti dogaÄ‘aj", "No categories selected" => "Nema odabranih kategorija", -"Select category" => "Odabir kategorije", "of" => "od", "at" => "na", "Timezone" => "Vremenska zona", -"Check always for changes of the timezone" => "Provjerite uvijek za promjene vremenske zone", "Timeformat" => "Format vremena", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Prvi dan tjedna", -"Calendar CalDAV syncing address:" => "Adresa za CalDAV sinkronizaciju kalendara:", "Users" => "Korisnici", "select users" => "odaberi korisnike", "Editable" => "Može se ureÄ‘ivati", diff --git a/apps/calendar/l10n/hu_HU.php b/apps/calendar/l10n/hu_HU.php index d97887aac7a..3ef4b9675be 100644 --- a/apps/calendar/l10n/hu_HU.php +++ b/apps/calendar/l10n/hu_HU.php @@ -6,7 +6,12 @@ "Timezone changed" => "IdÅ‘zóna megváltozott", "Invalid request" => "Érvénytelen kérés", "Calendar" => "Naptár", +"ddd" => "nnn", +"ddd M/d" => "nnn H/n", +"dddd M/d" => "nnnn H/n", +"MMMM yyyy" => "HHHH éééé", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "nnnn, HHH n, éééé", "Birthday" => "Születésap", "Business" => "Ãœzlet", "Call" => "HÃvás", @@ -23,6 +28,7 @@ "Questions" => "Kérdések", "Work" => "Munka", "unnamed" => "névtelen", +"New Calendar" => "Új naptár", "Does not repeat" => "Nem ismétlÅ‘dik", "Daily" => "Napi", "Weekly" => "Heti", @@ -68,7 +74,6 @@ "Date" => "Dátum", "Cal." => "Naptár", "All day" => "Egész nap", -"New Calendar" => "Új naptár", "Missing fields" => "Hiányzó mezÅ‘k", "Title" => "CÃm", "From Date" => "Napjától", @@ -81,9 +86,6 @@ "Month" => "Hónap", "List" => "Lista", "Today" => "Ma", -"Calendars" => "Naptárak", -"There was a fail, while parsing the file." => "Probléma volt a fájl elemzése közben.", -"Choose active calendars" => "AktÃv naptár kiválasztása", "Your calendars" => "Naptárjaid", "CalDav Link" => "CalDAV link", "Shared calendars" => "Megosztott naptárak", @@ -132,27 +134,19 @@ "Interval" => "IdÅ‘köz", "End" => "Vége", "occurrences" => "elÅ‘fordulások", -"Import a calendar file" => "Naptár-fájl importálása", -"Please choose the calendar" => "Válassz naptárat", "create a new calendar" => "új naptár létrehozása", +"Import a calendar file" => "Naptár-fájl importálása", "Name of new calendar" => "Új naptár neve", "Import" => "Importálás", -"Importing calendar" => "Naptár importálása", -"Calendar imported successfully" => "Naptár sikeresen importálva", "Close Dialog" => "Párbeszédablak bezárása", "Create a new event" => "Új esemény létrehozása", "View an event" => "Esemény megtekintése", "No categories selected" => "Nincs kiválasztott kategória", -"Select category" => "Kategória kiválasztása", "of" => ", tulaj ", "at" => ", ", "Timezone" => "IdÅ‘zóna", -"Check always for changes of the timezone" => "Mindig ellenÅ‘rizze az idÅ‘zóna-változásokat", -"Timeformat" => "IdÅ‘formátum", "24h" => "24h", "12h" => "12h", -"First day of the week" => "A hét elsÅ‘ napja", -"Calendar CalDAV syncing address:" => "Naptár CalDAV szinkronizálási cÃm:", "Users" => "Felhasználók", "select users" => "válassz felhasználókat", "Editable" => "SzerkeszthetÅ‘", diff --git a/apps/calendar/l10n/ia.php b/apps/calendar/l10n/ia.php index a346e4de5b7..84c36536b95 100644 --- a/apps/calendar/l10n/ia.php +++ b/apps/calendar/l10n/ia.php @@ -16,6 +16,7 @@ "Questions" => "Demandas", "Work" => "Travalio", "unnamed" => "sin nomine", +"New Calendar" => "Nove calendario", "Does not repeat" => "Non repite", "Daily" => "Quotidian", "Weekly" => "Septimanal", @@ -51,7 +52,6 @@ "by day and month" => "per dia e mense", "Date" => "Data", "All day" => "Omne die", -"New Calendar" => "Nove calendario", "Missing fields" => "Campos incomplete", "Title" => "Titulo", "From Date" => "Data de initio", @@ -62,8 +62,6 @@ "Month" => "Mense", "List" => "Lista", "Today" => "Hodie", -"Calendars" => "Calendarios", -"Choose active calendars" => "Selectionar calendarios active", "Your calendars" => "Tu calendarios", "Download" => "Discarga", "Edit" => "Modificar", @@ -96,20 +94,17 @@ "Select weeks" => "Seliger septimanas", "Interval" => "Intervallo", "End" => "Fin", -"Import a calendar file" => "Importar un file de calendario", -"Please choose the calendar" => "Selige el calendario", "create a new calendar" => "crear un nove calendario", +"Import a calendar file" => "Importar un file de calendario", "Name of new calendar" => "Nomine del calendario", "Import" => "Importar", "Close Dialog" => "Clauder dialogo", "Create a new event" => "Crear un nove evento", "View an event" => "Vide un evento", "No categories selected" => "Nulle categorias seligite", -"Select category" => "Selectionar categoria", "of" => "de", "at" => "in", "Timezone" => "Fuso horari", -"First day of the week" => "Prime die del septimana", "Users" => "Usatores", "Groups" => "Gruppos" ); diff --git a/apps/calendar/l10n/id.php b/apps/calendar/l10n/id.php index ac0734abba4..865c2118fac 100644 --- a/apps/calendar/l10n/id.php +++ b/apps/calendar/l10n/id.php @@ -14,9 +14,6 @@ "Week" => "Minggu", "Month" => "Bulan", "Today" => "Hari ini", -"Calendars" => "Kalender", -"There was a fail, while parsing the file." => "Terjadi kesalahan, saat mengurai berkas.", -"Choose active calendars" => "Pilih kalender aktif", "Download" => "Unduh", "Edit" => "Sunting", "Edit calendar" => "Sunting kalender", diff --git a/apps/calendar/l10n/it.php b/apps/calendar/l10n/it.php index cdb2d99c82e..04e10b582bf 100644 --- a/apps/calendar/l10n/it.php +++ b/apps/calendar/l10n/it.php @@ -1,12 +1,23 @@ <?php $TRANSLATIONS = array( +"Not all calendars are completely cached" => "Non tutti i calendari sono mantenuti completamente in cache", +"Everything seems to be completely cached" => "Tutto sembra essere mantenuto completamente in cache", "No calendars found." => "Nessun calendario trovato.", "No events found." => "Nessun evento trovato.", "Wrong calendar" => "Calendario sbagliato", +"The file contained either no events or all events are already saved in your calendar." => "Il file non conteneva alcun evento o tutti gli eventi erano già salvati nel tuo calendario.", +"events has been saved in the new calendar" => "gli eventi sono stati salvati nel nuovo calendario", +"Import failed" => "Importazione non riuscita", +"events has been saved in your calendar" => "gli eventi sono stati salvati nel tuo calendario", "New Timezone:" => "Nuovo fuso orario:", "Timezone changed" => "Fuso orario cambiato", "Invalid request" => "Richiesta non valida", "Calendar" => "Calendario", +"ddd" => "ddd", +"ddd M/d" => "ddd d/M", +"dddd M/d" => "dddd d/M", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, d MMM yyyy", "Birthday" => "Compleanno", "Business" => "Azienda", "Call" => "Chiama", @@ -22,7 +33,9 @@ "Projects" => "Progetti", "Questions" => "Domande", "Work" => "Lavoro", +"by" => "da", "unnamed" => "senza nome", +"New Calendar" => "Nuovo calendario", "Does not repeat" => "Non ripetere", "Daily" => "Giornaliero", "Weekly" => "Settimanale", @@ -67,8 +80,26 @@ "by day and month" => "per giorno e mese", "Date" => "Data", "Cal." => "Cal.", +"Sun." => "Dom.", +"Mon." => "Lun.", +"Tue." => "Mar.", +"Wed." => "Mer.", +"Thu." => "Gio.", +"Fri." => "Ven.", +"Sat." => "Sab.", +"Jan." => "Gen.", +"Feb." => "Feb.", +"Mar." => "Mar.", +"Apr." => "Apr.", +"May." => "Mag.", +"Jun." => "Giu.", +"Jul." => "Lug.", +"Aug." => "Ago.", +"Sep." => "Set.", +"Oct." => "Ott.", +"Nov." => "Nov.", +"Dec." => "Dic.", "All day" => "Tutti il giorno", -"New Calendar" => "Nuovo calendario", "Missing fields" => "Campi mancanti", "Title" => "Titolo", "From Date" => "Dal giorno", @@ -81,9 +112,7 @@ "Month" => "Mese", "List" => "Elenco", "Today" => "Oggi", -"Calendars" => "Calendari", -"There was a fail, while parsing the file." => "Si è verificato un errore durante l'analisi del file.", -"Choose active calendars" => "Scegli i calendari attivi", +"Settings" => "Impostazioni", "Your calendars" => "I tuoi calendari", "CalDav Link" => "Collegamento CalDav", "Shared calendars" => "Calendari condivisi", @@ -110,7 +139,7 @@ "Share" => "Condividi", "Title of the Event" => "Titolo dell'evento", "Category" => "Categoria", -"Separate categories with commas" => "Categorie separate con virgole", +"Separate categories with commas" => "Categorie separate da virgole", "Edit categories" => "Modifica le categorie", "All Day Event" => "Evento che occupa tutta la giornata", "From" => "Da", @@ -132,27 +161,34 @@ "Interval" => "Intervallo", "End" => "Fine", "occurrences" => "occorrenze", -"Import a calendar file" => "Importa un file di calendario", -"Please choose the calendar" => "Scegli il calendario", "create a new calendar" => "Crea un nuovo calendario", +"Import a calendar file" => "Importa un file di calendario", +"Please choose a calendar" => "Scegli un calendario", "Name of new calendar" => "Nome del nuovo calendario", +"Take an available name!" => "Usa un nome disponibile!", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Un calendario con questo nome esiste già . Se continui, i due calendari saranno uniti.", "Import" => "Importa", -"Importing calendar" => "Importazione del calendario in corso", -"Calendar imported successfully" => "Calendario importato correttamente", "Close Dialog" => "Chiudi la finestra di dialogo", "Create a new event" => "Crea un nuovo evento", "View an event" => "Visualizza un evento", "No categories selected" => "Nessuna categoria selezionata", -"Select category" => "Seleziona una categoria", "of" => "di", "at" => "alle", +"General" => "Generale", "Timezone" => "Fuso orario", -"Check always for changes of the timezone" => "Controlla sempre i cambiamenti di fuso orario", -"Timeformat" => "Formato orario", +"Update timezone automatically" => "Aggiorna automaticamente il fuso orario", +"Time format" => "Formato orario", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Primo giorno della settimana", -"Calendar CalDAV syncing address:" => "Indirizzo sincronizzazione calendario CalDAV:", +"Start week on" => "La settimana inizia il", +"Cache" => "Cache", +"Clear cache for repeating events" => "Cancella gli eventi che si ripetono dalla cache", +"URLs" => "URL", +"Calendar CalDAV syncing addresses" => "Indirizzi di sincronizzazione calendari CalDAV", +"more info" => "ulteriori informazioni", +"Primary address (Kontact et al)" => "Indirizzo principale (Kontact e altri)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "Collegamento(i) iCalendar sola lettura", "Users" => "Utenti", "select users" => "seleziona utenti", "Editable" => "Modificabile", diff --git a/apps/calendar/l10n/ja_JP.php b/apps/calendar/l10n/ja_JP.php index c533a9bd1a7..e59f186a0b2 100644 --- a/apps/calendar/l10n/ja_JP.php +++ b/apps/calendar/l10n/ja_JP.php @@ -1,12 +1,23 @@ <?php $TRANSLATIONS = array( +"Not all calendars are completely cached" => "ã™ã¹ã¦ã®ã‚«ãƒ¬ãƒ³ãƒ€ãƒ¼ã¯å®Œå…¨ã«ã‚ャッシュã•ã‚Œã¦ã„ã¾ã›ã‚“", +"Everything seems to be completely cached" => "ã™ã¹ã¦å®Œå…¨ã«ã‚ャッシュã•ã‚Œã¦ã„ã‚‹ã¨æ€ã‚ã‚Œã¾ã™", "No calendars found." => "カレンダーãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚", "No events found." => "イベントãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚", "Wrong calendar" => "誤ã£ãŸã‚«ãƒ¬ãƒ³ãƒ€ãƒ¼ã§ã™", +"The file contained either no events or all events are already saved in your calendar." => "イベントã®ç„¡ã„ã‚‚ã—ãã¯ã™ã¹ã¦ã®ã‚¤ãƒ™ãƒ³ãƒˆã‚’å«ã‚€ãƒ•ã‚¡ã‚¤ãƒ«ã¯æ—¢ã«ã‚ãªãŸã®ã‚«ãƒ¬ãƒ³ãƒ€ãƒ¼ã«ä¿å˜ã•ã‚Œã¦ã„ã¾ã™ã€‚", +"events has been saved in the new calendar" => "イベントã¯æ–°ã—ã„カレンダーã«ä¿å˜ã•ã‚Œã¾ã—ãŸ", +"Import failed" => "インãƒãƒ¼ãƒˆã«å¤±æ•—", +"events has been saved in your calendar" => "イベントã¯ã‚ãªãŸã®ã‚«ãƒ¬ãƒ³ãƒ€ãƒ¼ã«ä¿å˜ã•ã‚Œã¾ã—ãŸ", "New Timezone:" => "æ–°ã—ã„タイムゾーン:", "Timezone changed" => "タイムゾーンãŒå¤‰æ›´ã•ã‚Œã¾ã—ãŸ", "Invalid request" => "無効ãªãƒªã‚¯ã‚¨ã‚¹ãƒˆã§ã™", "Calendar" => "カレンダー", -"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"ddd" => "dddd", +"ddd M/d" => "M月dæ—¥ (dddd)", +"dddd M/d" => "M月dæ—¥ (dddd)", +"MMMM yyyy" => "yyyyå¹´M月", +"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "yyyyå¹´M月dæ—¥{ '~' [yyyyå¹´][M月]dæ—¥}", +"dddd, MMM d, yyyy" => "yyyyå¹´M月dæ—¥ (dddd)", "Birthday" => "誕生日", "Business" => "ビジãƒã‚¹", "Call" => "電話をã‹ã‘ã‚‹", @@ -22,6 +33,9 @@ "Projects" => "プãƒã‚¸ã‚§ã‚¯ãƒˆ", "Questions" => "質å•äº‹é …", "Work" => "週ã®å§‹ã¾ã‚Š", +"by" => "ã«ã‚ˆã‚‹", +"unnamed" => "ç„¡å", +"New Calendar" => "æ–°ã—ãカレンダーを作æˆ", "Does not repeat" => "ç¹°ã‚Šè¿”ã•ãªã„", "Daily" => "毎日", "Weekly" => "毎週", @@ -34,13 +48,13 @@ "by date" => "日付ã§æŒ‡å®š", "by monthday" => "æ—¥ã«ã¡ã§æŒ‡å®š", "by weekday" => "曜日ã§æŒ‡å®š", -"Monday" => "月曜", -"Tuesday" => "ç«æ›œ", -"Wednesday" => "水曜", -"Thursday" => "木曜", -"Friday" => "金曜", -"Saturday" => "土曜", -"Sunday" => "日曜", +"Monday" => "月", +"Tuesday" => "ç«", +"Wednesday" => "æ°´", +"Thursday" => "木", +"Friday" => "金", +"Saturday" => "土", +"Sunday" => "æ—¥", "events week of month" => "予定ã®ã‚る週を指定", "first" => "1週目", "second" => "2週目", @@ -48,26 +62,45 @@ "fourth" => "4週目", "fifth" => "5週目", "last" => "最終週", -"January" => "1月", -"February" => "2月", -"March" => "3月", -"April" => "4月", -"May" => "5月", -"June" => "6月", -"July" => "7月", -"August" => "8月", -"September" => "9月", -"October" => "1ï¼æœˆ", -"November" => "11月", -"December" => "12月", +"January" => "1月", +"February" => "2月", +"March" => "3月", +"April" => "4月", +"May" => "5月", +"June" => "6月", +"July" => "7月", +"August" => "8月", +"September" => "9月", +"October" => "10月", +"November" => "11月", +"December" => "12月", "by events date" => "日付ã§æŒ‡å®š", "by yearday(s)" => "日番å·ã§æŒ‡å®š", "by weeknumber(s)" => "週番å·ã§æŒ‡å®š", "by day and month" => "月ã¨æ—¥ã§æŒ‡å®š", "Date" => "日付", "Cal." => "カレンダー", +"Sun." => "æ—¥", +"Mon." => "月", +"Tue." => "ç«", +"Wed." => "æ°´", +"Thu." => "木", +"Fri." => "金", +"Sat." => "土", +"Jan." => "1月", +"Feb." => "2月", +"Mar." => "3月", +"Apr." => "4月", +"May." => "5月", +"Jun." => "6月", +"Jul." => "7月", +"Aug." => "8月", +"Sep." => "9月", +"Oct." => "10月", +"Nov." => "11月", +"Dec." => "12月", "All day" => "終日", -"New Calendar" => "æ–°ã—ãカレンダーを作æˆ", +"New Calendar" => "æ–°ã—ãカレンダーを作æˆã™ã‚‹", "Missing fields" => "é …ç›®ãŒã‚ã‚Šã¾ã›ã‚“", "Title" => "タイトル", "From Date" => "開始日", @@ -78,11 +111,9 @@ "There was a database fail" => "データベースã®ã‚¨ãƒ©ãƒ¼ãŒã‚ã‚Šã¾ã—ãŸ", "Week" => "週", "Month" => "月", -"List" => "リスト", +"List" => "予定リスト", "Today" => "今日", -"Calendars" => "カレンダー", -"There was a fail, while parsing the file." => "ファイルã®æ§‹æ–‡è§£æžã«å¤±æ•—ã—ã¾ã—ãŸã€‚", -"Choose active calendars" => "アクティブãªã‚«ãƒ¬ãƒ³ãƒ€ãƒ¼ã‚’é¸æŠž", +"Settings" => "è¨å®š", "Your calendars" => "ã‚ãªãŸã®ã‚«ãƒ¬ãƒ³ãƒ€ãƒ¼", "CalDav Link" => "CalDavã¸ã®ãƒªãƒ³ã‚¯", "Shared calendars" => "共有カレンダー", @@ -91,6 +122,7 @@ "Download" => "ダウンãƒãƒ¼ãƒ‰", "Edit" => "編集", "Delete" => "削除", +"shared with you by" => "共有者", "New calendar" => "æ–°ã—ã„カレンダー", "Edit calendar" => "カレンダーを編集", "Displayname" => "表示å", @@ -130,27 +162,34 @@ "Interval" => "é–“éš”", "End" => "ç¹°ã‚Šè¿”ã™æœŸé–“", "occurrences" => "回繰り返ã™", -"Import a calendar file" => "カレンダーファイルをインãƒãƒ¼ãƒˆ", -"Please choose the calendar" => "カレンダーをé¸æŠžã—ã¦ãã ã•ã„", "create a new calendar" => "æ–°è¦ã‚«ãƒ¬ãƒ³ãƒ€ãƒ¼ã®ä½œæˆ", +"Import a calendar file" => "カレンダーファイルをインãƒãƒ¼ãƒˆ", +"Please choose a calendar" => "カレンダーをé¸æŠžã—ã¦ãã ã•ã„", "Name of new calendar" => "æ–°è¦ã‚«ãƒ¬ãƒ³ãƒ€ãƒ¼ã®å称", +"Take an available name!" => "利用å¯èƒ½ãªåå‰ã‚’指定ã—ã¦ãã ã•ã„ï¼", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "ã“ã®ã‚«ãƒ¬ãƒ³ãƒ€ãƒ¼åã¯ã™ã§ã«ä½¿ã‚ã‚Œã¦ã„ã¾ã™ã€‚ã‚‚ã—続行ã™ã‚‹å ´åˆã¯ã€ã“れらã®ã‚«ãƒ¬ãƒ³ãƒ€ãƒ¼ã¯ãƒžãƒ¼ã‚¸ã•ã‚Œã¾ã™ã€‚", "Import" => "インãƒãƒ¼ãƒˆ", -"Importing calendar" => "カレンダーをå–ã‚Šè¾¼ã¿ä¸", -"Calendar imported successfully" => "カレンダーã®å–ã‚Šè¾¼ã¿ã«æˆåŠŸã—ã¾ã—ãŸ", "Close Dialog" => "ダイアãƒã‚°ã‚’é–‰ã˜ã‚‹", "Create a new event" => "æ–°ã—ã„イベントを作æˆ", "View an event" => "イベントを閲覧", "No categories selected" => "カテゴリãŒé¸æŠžã•ã‚Œã¦ã„ã¾ã›ã‚“", -"Select category" => "カテゴリーをé¸æŠžã—ã¦ãã ã•ã„", "of" => "of", "at" => "at", +"General" => "一般", "Timezone" => "タイムゾーン", -"Check always for changes of the timezone" => "タイムゾーンã®å¤‰æ›´ã‚’常ã«ç¢ºèª", -"Timeformat" => "時刻ã®ãƒ•ã‚©ãƒ¼ãƒžãƒƒãƒˆ", +"Update timezone automatically" => "自動的ã«ã‚¿ã‚¤ãƒ ゾーンを更新", +"Time format" => "時刻ã®è¡¨ç¤ºå½¢å¼", "24h" => "24h", "12h" => "12h", -"First day of the week" => "週ã®å§‹ã¾ã‚Š", -"Calendar CalDAV syncing address:" => "CalDAVカレンダーã®åŒæœŸã‚¢ãƒ‰ãƒ¬ã‚¹:", +"Start week on" => "1週間ã®åˆã‚ã®æ›œæ—¥", +"Cache" => "ã‚ャッシュ", +"Clear cache for repeating events" => "ç¹°ã‚Šè¿”ã—イベントã®ã‚ャッシュをクリア", +"URLs" => "URL", +"Calendar CalDAV syncing addresses" => "CalDAVカレンダーã®åŒæœŸç”¨ã‚¢ãƒ‰ãƒ¬ã‚¹", +"more info" => "ã•ã‚‰ã«", +"Primary address (Kontact et al)" => "プライマリアドレス(コンタクトç‰ï¼‰", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "èªã¿å–り専用ã®iCalendarリンク", "Users" => "ユーザ", "select users" => "ユーザをé¸æŠž", "Editable" => "編集å¯èƒ½", diff --git a/apps/calendar/l10n/ko.php b/apps/calendar/l10n/ko.php index 181bfa4378f..77e421d4aab 100644 --- a/apps/calendar/l10n/ko.php +++ b/apps/calendar/l10n/ko.php @@ -6,6 +6,12 @@ "Timezone changed" => "시간대 변경ë¨", "Invalid request" => "ìž˜ëª»ëœ ìš”ì²", "Calendar" => "ë‹¬ë ¥", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", +"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", "Birthday" => "ìƒì¼", "Business" => "사업", "Call" => "통화", @@ -22,6 +28,7 @@ "Questions" => "질문", "Work" => "ìž‘ì—…", "unnamed" => "ìµëª…ì˜", +"New Calendar" => "새로운 ë‹¬ë ¥", "Does not repeat" => "반복 ì—†ìŒ", "Daily" => "매ì¼", "Weekly" => "매주", @@ -67,7 +74,6 @@ "Date" => "ë‚ ì§œ", "Cal." => "ë‹¬ë ¥", "All day" => "매ì¼", -"New Calendar" => "새로운 ë‹¬ë ¥", "Missing fields" => "ê¸°ìž…ëž€ì´ ë¹„ì–´ìžˆìŠµë‹ˆë‹¤", "Title" => "ì œëª©", "From Date" => "ì‹œìž‘ë‚ ì§œ", @@ -80,9 +86,6 @@ "Month" => "달", "List" => "목ë¡", "Today" => "오늘", -"Calendars" => "ë‹¬ë ¥", -"There was a fail, while parsing the file." => "파ì¼ì„ 처리하는 중 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤.", -"Choose active calendars" => "활성 ë‹¬ë ¥ ì„ íƒ", "Your calendars" => "ë‚´ ë‹¬ë ¥", "CalDav Link" => "CalDav ë§í¬", "Shared calendars" => "ê³µìœ ë‹¬ë ¥", @@ -131,27 +134,19 @@ "Interval" => "간격", "End" => "ë", "occurrences" => "번 ì´í›„", -"Import a calendar file" => "ë‹¬ë ¥ íŒŒì¼ ê°€ì ¸ì˜¤ê¸°", -"Please choose the calendar" => "ë‹¬ë ¥ì„ ì„ íƒí•´ 주세요", "create a new calendar" => "새 ë‹¬ë ¥ 만들기", +"Import a calendar file" => "ë‹¬ë ¥ íŒŒì¼ ê°€ì ¸ì˜¤ê¸°", "Name of new calendar" => "새 ë‹¬ë ¥ ì´ë¦„", "Import" => "ìž…ë ¥", -"Importing calendar" => "ë‹¬ë ¥ ìž…ë ¥", -"Calendar imported successfully" => "ë‹¬ë ¥ ìž…ë ¥ì„ ì„±ê³µì 으로 마쳤습니다.", "Close Dialog" => "대화 마침", "Create a new event" => "새 ì´ë²¤íŠ¸ 만들기", "View an event" => "ì¼ì • 보기", "No categories selected" => "ì„ íƒëœ ì¹´í…Œê³ ë¦¬ ì—†ìŒ", -"Select category" => "ì„ íƒ ì¹´í…Œê³ ë¦¬", "of" => "ì˜", "at" => "ì—ì„œ", "Timezone" => "시간대", -"Check always for changes of the timezone" => "í•ìƒ 시간대 변경 확ì¸í•˜ê¸°", -"Timeformat" => "시간 í˜•ì‹ ì„¤ì •", "24h" => "24시간", "12h" => "12시간", -"First day of the week" => "ê·¸ ì£¼ì˜ ì²«ì§¸ë‚ ", -"Calendar CalDAV syncing address:" => "ë‹¬ë ¥ CalDav ë™ê¸°í™” 주소", "Users" => "사용ìž", "select users" => "ì‚¬ìš©ìž ì„ íƒ", "Editable" => "편집 가능", diff --git a/apps/calendar/l10n/lb.php b/apps/calendar/l10n/lb.php index b40f652cc50..6e96b5df18d 100644 --- a/apps/calendar/l10n/lb.php +++ b/apps/calendar/l10n/lb.php @@ -22,6 +22,7 @@ "Projects" => "Projeten", "Questions" => "Froen", "Work" => "Aarbecht", +"New Calendar" => "Neien Kalenner", "Does not repeat" => "Widderhëlt sech net", "Daily" => "Deeglech", "Weekly" => "All Woch", @@ -62,7 +63,6 @@ "Date" => "Datum", "Cal." => "Cal.", "All day" => "All Dag", -"New Calendar" => "Neien Kalenner", "Missing fields" => "Felder déi feelen", "Title" => "Titel", "From Date" => "Vun Datum", @@ -75,9 +75,6 @@ "Month" => "Mount", "List" => "Lescht", "Today" => "Haut", -"Calendars" => "Kalenneren", -"There was a fail, while parsing the file." => "Feeler beim lueden vum Fichier.", -"Choose active calendars" => "Wiel aktiv Kalenneren aus", "Your calendars" => "Deng Kalenneren", "CalDav Link" => "CalDav Link", "Shared calendars" => "Gedeelte Kalenneren", diff --git a/apps/calendar/l10n/lt_LT.php b/apps/calendar/l10n/lt_LT.php index d7e15fb438f..408718071e2 100644 --- a/apps/calendar/l10n/lt_LT.php +++ b/apps/calendar/l10n/lt_LT.php @@ -23,6 +23,7 @@ "Questions" => "Klausimai", "Work" => "Darbas", "unnamed" => "be pavadinimo", +"New Calendar" => "Naujas kalendorius", "Does not repeat" => "Nekartoti", "Daily" => "Kasdien", "Weekly" => "KiekvienÄ… savaitÄ™", @@ -56,7 +57,6 @@ "Date" => "Data", "Cal." => "Kal.", "All day" => "Visa diena", -"New Calendar" => "Naujas kalendorius", "Missing fields" => "TrÅ«kstami laukai", "Title" => "Pavadinimas", "From Date" => "Nuo datos", @@ -69,9 +69,6 @@ "Month" => "MÄ—nuo", "List" => "SÄ…raÅ¡as", "Today" => "Å iandien", -"Calendars" => "Kalendoriai", -"There was a fail, while parsing the file." => "Apdorojant failÄ… įvyko klaida.", -"Choose active calendars" => "Pasirinkite naudojamus kalendorius", "Your calendars" => "JÅ«sų kalendoriai", "CalDav Link" => "CalDav adresas", "Shared calendars" => "Bendri kalendoriai", @@ -92,6 +89,7 @@ "Export" => "Eksportuoti", "Eventinfo" => "Informacija", "Repeating" => "Pasikartojantis", +"Alarm" => "Priminimas", "Attendees" => "Dalyviai", "Share" => "Dalintis", "Title of the Event" => "Ä®vykio pavadinimas", @@ -113,6 +111,7 @@ "Select weeks" => "Pasirinkite savaites", "Interval" => "Intervalas", "End" => "Pabaiga", +"create a new calendar" => "sukurti naujÄ… kalendorių", "Import a calendar file" => "Importuoti kalendoriaus failÄ…", "Please choose the calendar" => "Pasirinkite kalendorių", "create a new calendar" => "sukurti naujÄ… kalendorių", @@ -124,13 +123,11 @@ "Create a new event" => "Sukurti naujÄ… įvykį", "View an event" => "PeržiÅ«rÄ—ti įvykį", "No categories selected" => "Nepasirinktos jokios katagorijos", -"Select category" => "Pasirinkite kategorijÄ…", "Timezone" => "Laiko juosta", "Check always for changes of the timezone" => "Visada tikrinti laiko zonos pasikeitimus", "Timeformat" => "Laiko formatas", "24h" => "24val", "12h" => "12val", -"Calendar CalDAV syncing address:" => "CalDAV kalendoriaus synchronizavimo adresas:", "Users" => "Vartotojai", "select users" => "pasirinkti vartotojus", "Editable" => "Redaguojamas", diff --git a/apps/calendar/l10n/mk.php b/apps/calendar/l10n/mk.php index 41df376dfa6..1a03101fe51 100644 --- a/apps/calendar/l10n/mk.php +++ b/apps/calendar/l10n/mk.php @@ -6,7 +6,12 @@ "Timezone changed" => "ВременÑката зона е променета", "Invalid request" => "Ðеправилно барање", "Calendar" => "Календар", +"ddd" => "ддд", +"ddd M/d" => "ддд Ðœ/д", +"dddd M/d" => "дддд Ðœ/д", +"MMMM yyyy" => "ММММ гггг", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "дддд, МММ д, гггг", "Birthday" => "Роденден", "Business" => "Деловно", "Call" => "Повикај", @@ -23,6 +28,7 @@ "Questions" => "Прашања", "Work" => "Работа", "unnamed" => "неименувано", +"New Calendar" => "Ðов календар", "Does not repeat" => "Ðе Ñе повторува", "Daily" => "Дневно", "Weekly" => "Седмично", @@ -68,7 +74,6 @@ "Date" => "Датум", "Cal." => "Кал.", "All day" => "Цел ден", -"New Calendar" => "Ðов календар", "Missing fields" => "Полиња кои недоÑтаÑуваат", "Title" => "ÐаÑлов", "From Date" => "Од датум", @@ -81,9 +86,6 @@ "Month" => "МеÑец", "List" => "ЛиÑта", "Today" => "ДенеÑка", -"Calendars" => "Календари", -"There was a fail, while parsing the file." => "Имаше проблем при парÑирање на датотеката.", -"Choose active calendars" => "Избери активни календари", "Your calendars" => "Ваши календари", "CalDav Link" => "Ð’Ñ€Ñка за CalDav", "Shared calendars" => "Споделени календари", @@ -132,27 +134,19 @@ "Interval" => "интервал", "End" => "Крај", "occurrences" => "повторувања", -"Import a calendar file" => "ВнеÑи календар од датотека ", -"Please choose the calendar" => "Ве молам изберете го календарот", "create a new calendar" => "Ñоздади нов календар", +"Import a calendar file" => "ВнеÑи календар од датотека ", "Name of new calendar" => "Име на новиот календар", "Import" => "Увези", -"Importing calendar" => "Увезување на календар", -"Calendar imported successfully" => "Календарот беше уÑпешно увезен", "Close Dialog" => "Затвори дијалог", "Create a new event" => "Создади нов наÑтан", "View an event" => "Погледај наÑтан", "No categories selected" => "Ðема избрано категории", -"Select category" => "Избери категорија", "of" => "од", "at" => "на", "Timezone" => "ВременÑка зона", -"Check always for changes of the timezone" => "Секогаш провери за промени на временÑката зона", -"Timeformat" => "Формат на времето", "24h" => "24ч", "12h" => "12ч", -"First day of the week" => "Прв ден од Ñедмицата", -"Calendar CalDAV syncing address:" => "CalDAV календар адреÑата за Ñинхронизација:", "Users" => "КориÑници", "select users" => "избери кориÑници", "Editable" => "Изменливо", diff --git a/apps/calendar/l10n/ms_MY.php b/apps/calendar/l10n/ms_MY.php index 2cb3ac41c30..4be91a40194 100644 --- a/apps/calendar/l10n/ms_MY.php +++ b/apps/calendar/l10n/ms_MY.php @@ -1,9 +1,17 @@ <?php $TRANSLATIONS = array( +"No calendars found." => "Tiada kalendar dijumpai.", +"No events found." => "Tiada agenda dijumpai.", "Wrong calendar" => "Silap kalendar", "New Timezone:" => "Timezone Baru", "Timezone changed" => "Zon waktu diubah", "Invalid request" => "Permintaan tidak sah", "Calendar" => "Kalendar", +"ddd" => "ddd", +"ddd M/d" => "dd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", +"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyy", "Birthday" => "Hari lahir", "Business" => "Perniagaan", "Call" => "Panggilan", @@ -19,6 +27,8 @@ "Projects" => "Projek", "Questions" => "Soalan", "Work" => "Kerja", +"unnamed" => "tiada nama", +"New Calendar" => "Kalendar baru", "Does not repeat" => "Tidak berulang", "Daily" => "Harian", "Weekly" => "Mingguan", @@ -64,7 +74,6 @@ "Date" => "Tarikh", "Cal." => "Kalendar", "All day" => "Sepanjang hari", -"New Calendar" => "Kalendar baru", "Missing fields" => "Ruangan tertinggal", "Title" => "Tajuk", "From Date" => "Dari tarikh", @@ -77,13 +86,15 @@ "Month" => "Bulan", "List" => "Senarai", "Today" => "Hari ini", -"Calendars" => "Kalendar", -"There was a fail, while parsing the file." => "Berlaku kegagalan ketika penguraian fail. ", -"Choose active calendars" => "Pilih kalendar yang aktif", +"Your calendars" => "Kalendar anda", "CalDav Link" => "Pautan CalDav", +"Shared calendars" => "Kalendar Kongsian", +"No shared calendars" => "Tiada kalendar kongsian", +"Share Calendar" => "Kongsi Kalendar", "Download" => "Muat turun", "Edit" => "Edit", "Delete" => "Hapus", +"shared with you by" => "dikongsi dengan kamu oleh", "New calendar" => "Kalendar baru", "Edit calendar" => "Edit kalendar", "Displayname" => "Paparan nama", @@ -94,8 +105,15 @@ "Cancel" => "Batal", "Edit an event" => "Edit agenda", "Export" => "Export", +"Eventinfo" => "Maklumat agenda", +"Repeating" => "Pengulangan", +"Alarm" => "Penggera", +"Attendees" => "Jemputan", +"Share" => "Berkongsi", "Title of the Event" => "Tajuk agenda", "Category" => "kategori", +"Separate categories with commas" => "Asingkan kategori dengan koma", +"Edit categories" => "Sunting Kategori", "All Day Event" => "Agenda di sepanjang hari ", "From" => "Dari", "To" => "ke", @@ -116,20 +134,23 @@ "Interval" => "Tempoh", "End" => "Tamat", "occurrences" => "Peristiwa", -"Import a calendar file" => "Import fail kalendar", -"Please choose the calendar" => "Sila pilih kalendar", "create a new calendar" => "Cipta kalendar baru", +"Import a calendar file" => "Import fail kalendar", "Name of new calendar" => "Nama kalendar baru", "Import" => "Import", -"Importing calendar" => "Import kalendar", -"Calendar imported successfully" => "Kalendar berjaya diimport", "Close Dialog" => "Tutup dialog", "Create a new event" => "Buat agenda baru", -"Select category" => "Pilih kategori", +"View an event" => "Papar peristiwa", +"No categories selected" => "Tiada kategori dipilih", +"of" => "dari", +"at" => "di", "Timezone" => "Zon waktu", -"Check always for changes of the timezone" => "Sentiasa mengemaskini perubahan zon masa", -"Timeformat" => "Timeformat", "24h" => "24h", "12h" => "12h", -"Calendar CalDAV syncing address:" => "Kelendar CalDAV mengemaskini alamat:" +"Users" => "Pengguna", +"select users" => "Pilih pengguna", +"Editable" => "Boleh disunting", +"Groups" => "Kumpulan-kumpulan", +"select groups" => "pilih kumpulan-kumpulan", +"make public" => "jadikan tontonan awam" ); diff --git a/apps/calendar/l10n/nb_NO.php b/apps/calendar/l10n/nb_NO.php index 95ba5a9dba2..8f736869de1 100644 --- a/apps/calendar/l10n/nb_NO.php +++ b/apps/calendar/l10n/nb_NO.php @@ -8,6 +8,7 @@ "Calendar" => "Kalender", "Birthday" => "Bursdag", "Business" => "Forretninger", +"Call" => "Ring", "Clients" => "Kunder", "Holidays" => "Ferie", "Ideas" => "Ideér", @@ -20,6 +21,7 @@ "Questions" => "SpørsmÃ¥l", "Work" => "Arbeid", "unnamed" => "uten navn", +"New Calendar" => "Ny kalender", "Does not repeat" => "Gjentas ikke", "Daily" => "Daglig", "Weekly" => "Ukentlig", @@ -129,6 +131,7 @@ "Interval" => "Intervall", "End" => "Slutt", "occurrences" => "forekomster", +"create a new calendar" => "Lag en ny kalender", "Import a calendar file" => "Importer en kalenderfil", "Please choose the calendar" => "Vennligst velg kalenderen", "create a new calendar" => "Lag en ny kalender", @@ -147,7 +150,7 @@ "24h" => "24 t", "12h" => "12 t", "First day of the week" => "Ukens første dag", -"Calendar CalDAV syncing address:" => "Synkroniseringsadresse fo kalender CalDAV:", +"Calendar CalDAV syncing address:" => "Kalender CalDAV synkroniseringsadresse", "Users" => "Brukere", "select users" => "valgte brukere", "Editable" => "Redigerbar", diff --git a/apps/calendar/l10n/nl.php b/apps/calendar/l10n/nl.php index d141a1cc08c..834c0ad9054 100644 --- a/apps/calendar/l10n/nl.php +++ b/apps/calendar/l10n/nl.php @@ -6,7 +6,12 @@ "Timezone changed" => "Tijdzone is veranderd", "Invalid request" => "Ongeldige aanvraag", "Calendar" => "Kalender", -"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "d MMM[ yyyy]{ '—' d[ MMM] yyyy}", +"ddd" => "ddd", +"ddd M/d" => "ddd d.M", +"dddd M/d" => "dddd d.M", +"MMMM yyyy" => "MMMM yyyy", +"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "d[ MMM][ yyyy]{ '—' d MMM yyyy}", +"dddd, MMM d, yyyy" => "dddd, d. MMM yyyy", "Birthday" => "Verjaardag", "Business" => "Zakelijk", "Call" => "Bellen", @@ -23,6 +28,7 @@ "Questions" => "Vragen", "Work" => "Werk", "unnamed" => "onbekend", +"New Calendar" => "Nieuwe Kalender", "Does not repeat" => "Wordt niet herhaald", "Daily" => "Dagelijks", "Weekly" => "Wekelijks", @@ -68,7 +74,6 @@ "Date" => "Datum", "Cal." => "Cal.", "All day" => "Hele dag", -"New Calendar" => "Nieuwe Kalender", "Missing fields" => "missende velden", "Title" => "Titel", "From Date" => "Begindatum", @@ -81,9 +86,6 @@ "Month" => "Maand", "List" => "Lijst", "Today" => "Vandaag", -"Calendars" => "Kalenders", -"There was a fail, while parsing the file." => "Er is een fout opgetreden bij het verwerken van het bestand.", -"Choose active calendars" => "Kies actieve kalenders", "Your calendars" => "Je kalenders", "CalDav Link" => "CalDav Link", "Shared calendars" => "Gedeelde kalenders", @@ -132,27 +134,19 @@ "Interval" => "Interval", "End" => "Einde", "occurrences" => "gebeurtenissen", -"Import a calendar file" => "Importeer een agenda bestand", -"Please choose the calendar" => "Kies de kalender", "create a new calendar" => "Maak een nieuw agenda", +"Import a calendar file" => "Importeer een agenda bestand", "Name of new calendar" => "Naam van de nieuwe agenda", "Import" => "Importeer", -"Importing calendar" => "Importeer agenda", -"Calendar imported successfully" => "Agenda succesvol geïmporteerd", "Close Dialog" => "Sluit venster", "Create a new event" => "Maak een nieuwe afspraak", "View an event" => "Bekijk een gebeurtenis", "No categories selected" => "Geen categorieën geselecteerd", -"Select category" => "Kies een categorie", "of" => "van", "at" => "op", "Timezone" => "Tijdzone", -"Check always for changes of the timezone" => "Controleer altijd op aanpassingen van de tijdszone", -"Timeformat" => "Tijdformaat", "24h" => "24uur", "12h" => "12uur", -"First day of the week" => "Eerste dag van de week", -"Calendar CalDAV syncing address:" => "CalDAV kalender synchronisatie adres:", "Users" => "Gebruikers", "select users" => "kies gebruikers", "Editable" => "Te wijzigen", diff --git a/apps/calendar/l10n/nn_NO.php b/apps/calendar/l10n/nn_NO.php index 79119e8100b..3330cc562bc 100644 --- a/apps/calendar/l10n/nn_NO.php +++ b/apps/calendar/l10n/nn_NO.php @@ -19,6 +19,7 @@ "Projects" => "Prosjekt", "Questions" => "SpørsmÃ¥l", "Work" => "Arbeid", +"New Calendar" => "Ny kalender", "Does not repeat" => "Ikkje gjenta", "Daily" => "Kvar dag", "Weekly" => "Kvar veke", @@ -64,7 +65,6 @@ "Date" => "Dato", "Cal." => "Kal.", "All day" => "Heile dagen", -"New Calendar" => "Ny kalender", "Missing fields" => "Manglande felt", "Title" => "Tittel", "From Date" => "FrÃ¥ dato", @@ -77,9 +77,6 @@ "Month" => "MÃ¥nad", "List" => "Liste", "Today" => "I dag", -"Calendars" => "Kalendarar", -"There was a fail, while parsing the file." => "Feil ved tolking av fila.", -"Choose active calendars" => "Vel aktive kalendarar", "CalDav Link" => "CalDav-lenkje", "Download" => "Last ned", "Edit" => "Endra", @@ -116,20 +113,13 @@ "Interval" => "Intervall", "End" => "Ende", "occurrences" => "førekomstar", -"Import a calendar file" => "Importer ei kalenderfil", -"Please choose the calendar" => "Venlegast vel kalenderen", "create a new calendar" => "Lag ny kalender", +"Import a calendar file" => "Importer ei kalenderfil", "Name of new calendar" => "Namn for ny kalender", "Import" => "Importer", -"Importing calendar" => "Importerar kalender", -"Calendar imported successfully" => "Kalender importert utan feil", "Close Dialog" => "Steng dialog", "Create a new event" => "Opprett ei ny hending", -"Select category" => "Vel kategori", "Timezone" => "Tidssone", -"Check always for changes of the timezone" => "Sjekk alltid for endringar i tidssona", -"Timeformat" => "Tidsformat", "24h" => "24t", -"12h" => "12t", -"Calendar CalDAV syncing address:" => "Kalender CalDAV synkroniseringsadresse:" +"12h" => "12t" ); diff --git a/apps/calendar/l10n/pl.php b/apps/calendar/l10n/pl.php index e582cdbb9b3..0174bef6fc2 100644 --- a/apps/calendar/l10n/pl.php +++ b/apps/calendar/l10n/pl.php @@ -2,11 +2,18 @@ "No calendars found." => "Brak kalendarzy", "No events found." => "Brak wydzarzeÅ„", "Wrong calendar" => "NieprawidÅ‚owy kalendarz", +"Import failed" => "Import nieudany", +"events has been saved in your calendar" => "zdarzenie zostaÅ‚o zapisane w twoim kalendarzu", "New Timezone:" => "Nowa strefa czasowa:", "Timezone changed" => "Zmieniono strefÄ™ czasowÄ…", "Invalid request" => "NieprawidÅ‚owe żądanie", "Calendar" => "Kalendarz", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM rrrr", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, rrrr", "Birthday" => "Urodziny", "Business" => "Interesy", "Call" => "Rozmowy", @@ -22,7 +29,9 @@ "Projects" => "Projekty", "Questions" => "Pytania", "Work" => "Zawodowe", +"by" => "przez", "unnamed" => "nienazwany", +"New Calendar" => "Nowy kalendarz", "Does not repeat" => "Brak", "Daily" => "Codziennie", "Weekly" => "Tygodniowo", @@ -67,8 +76,26 @@ "by day and month" => "przez dzieÅ„ i miesiÄ…c", "Date" => "Data", "Cal." => "Kal.", +"Sun." => "N.", +"Mon." => "Pn.", +"Tue." => "Wt.", +"Wed." => "Åšr.", +"Thu." => "Cz.", +"Fri." => "Pt.", +"Sat." => "S.", +"Jan." => "Sty.", +"Feb." => "Lut.", +"Mar." => "Mar.", +"Apr." => "Kwi.", +"May." => "Maj.", +"Jun." => "Cze.", +"Jul." => "Lip.", +"Aug." => "Sie.", +"Sep." => "Wrz.", +"Oct." => "Paź.", +"Nov." => "Lis.", +"Dec." => "Gru.", "All day" => "CaÅ‚y dzieÅ„", -"New Calendar" => "Nowy kalendarz", "Missing fields" => "BrakujÄ…ce pola", "Title" => "Nazwa", "From Date" => "Od daty", @@ -81,9 +108,6 @@ "Month" => "MiesiÄ…c", "List" => "Lista", "Today" => "Dzisiaj", -"Calendars" => "Kalendarze", -"There was a fail, while parsing the file." => "Nie udaÅ‚o siÄ™ przetworzyć pliku.", -"Choose active calendars" => "Wybór aktywnych kalendarzy", "Your calendars" => "Twoje kalendarze", "CalDav Link" => "WyÅ›wietla odnoÅ›nik CalDAV", "Shared calendars" => "Współdzielone kalendarze", @@ -132,9 +156,9 @@ "Interval" => "InterwaÅ‚", "End" => "Koniec", "occurrences" => "wystÄ…pienia", -"Import a calendar file" => "Zaimportuj plik kalendarza", -"Please choose the calendar" => "ProszÄ™ wybrać kalendarz", "create a new calendar" => "stwórz nowy kalendarz", +"Import a calendar file" => "Zaimportuj plik kalendarza", +"Please choose a calendar" => "ProszÄ™ wybierz kalendarz", "Name of new calendar" => "Nazwa kalendarza", "Import" => "Import", "Importing calendar" => "Importuje kalendarz", @@ -143,7 +167,6 @@ "Create a new event" => "Tworzenie nowego wydarzenia", "View an event" => "Zobacz wydarzenie", "No categories selected" => "nie zaznaczono kategorii", -"Select category" => "Wybierz kategoriÄ™", "of" => "z", "at" => "w", "Timezone" => "Strefa czasowa", @@ -151,8 +174,9 @@ "Timeformat" => "Format czasu", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Pierwszy dzieÅ„ tygodnia", -"Calendar CalDAV syncing address:" => "Adres synchronizacji kalendarza CalDAV:", +"more info" => "wiÄ™cej informacji", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "Odczytać tylko linki iCalendar", "Users" => "Użytkownicy", "select users" => "wybierz użytkowników", "Editable" => "Edytowalne", diff --git a/apps/calendar/l10n/pt_BR.php b/apps/calendar/l10n/pt_BR.php index 95317eea0f7..b636c19bfe7 100644 --- a/apps/calendar/l10n/pt_BR.php +++ b/apps/calendar/l10n/pt_BR.php @@ -6,7 +6,12 @@ "Timezone changed" => "Fuso horário alterado", "Invalid request" => "Pedido inválido", "Calendar" => "Calendário", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", "Birthday" => "Aniversário", "Business" => "Negócio", "Call" => "Chamada", @@ -23,6 +28,7 @@ "Questions" => "Perguntas", "Work" => "Trabalho", "unnamed" => "sem nome", +"New Calendar" => "Novo Calendário", "Does not repeat" => "Não repetir", "Daily" => "Diariamente", "Weekly" => "Semanal", @@ -68,7 +74,6 @@ "Date" => "Data", "Cal." => "Cal.", "All day" => "Todo o dia", -"New Calendar" => "Novo Calendário", "Missing fields" => "Campos incompletos", "Title" => "TÃtulo", "From Date" => "Desde a Data", @@ -81,9 +86,6 @@ "Month" => "Mês", "List" => "Lista", "Today" => "Hoje", -"Calendars" => "Calendários", -"There was a fail, while parsing the file." => "Houve uma falha, ao analisar o arquivo.", -"Choose active calendars" => "Escolha calendários ativos", "Your calendars" => "Meus Calendários", "CalDav Link" => "Link para CalDav", "Shared calendars" => "Calendários Compartilhados", @@ -132,27 +134,19 @@ "Interval" => "Intervalo", "End" => "Final", "occurrences" => "ocorrências", -"Import a calendar file" => "Importar um arquivo de calendário", -"Please choose the calendar" => "Por favor, escolha o calendário", "create a new calendar" => "criar um novo calendário", +"Import a calendar file" => "Importar um arquivo de calendário", "Name of new calendar" => "Nome do novo calendário", "Import" => "Importar", -"Importing calendar" => "Importar calendário", -"Calendar imported successfully" => "Calendário importado com sucesso", "Close Dialog" => "Fechar caixa de diálogo", "Create a new event" => "Criar um novo evento", "View an event" => "Visualizar evento", "No categories selected" => "Nenhuma categoria selecionada", -"Select category" => "Selecionar categoria", "of" => "de", "at" => "para", "Timezone" => "Fuso horário", -"Check always for changes of the timezone" => "Verificar sempre mudanças no fuso horário", -"Timeformat" => "Formato da Hora", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Primeiro dia da semana", -"Calendar CalDAV syncing address:" => "Sincronização de endereço do calendário CalDAV :", "Users" => "Usuários", "select users" => "Selecione usuários", "Editable" => "Editável", diff --git a/apps/calendar/l10n/pt_PT.php b/apps/calendar/l10n/pt_PT.php index 33f85569cca..81bab52e593 100644 --- a/apps/calendar/l10n/pt_PT.php +++ b/apps/calendar/l10n/pt_PT.php @@ -1,12 +1,23 @@ <?php $TRANSLATIONS = array( +"Not all calendars are completely cached" => "Nem todos os calendários estão completamente pré-carregados", +"Everything seems to be completely cached" => "Parece que tudo está completamente pré-carregado", "No calendars found." => "Nenhum calendário encontrado.", "No events found." => "Nenhum evento encontrado.", "Wrong calendar" => "Calendário errado", +"The file contained either no events or all events are already saved in your calendar." => "O ficheiro não continha nenhuns eventos ou então todos os eventos já estavam carregados no seu calendário", +"events has been saved in the new calendar" => "Os eventos foram guardados no novo calendário", +"Import failed" => "Falha na importação", +"events has been saved in your calendar" => "Os eventos foram guardados no seu calendário", "New Timezone:" => "Nova zona horária", "Timezone changed" => "Zona horária alterada", "Invalid request" => "Pedido inválido", "Calendar" => "Calendário", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM aaaa", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, aaaa", "Birthday" => "Dia de anos", "Business" => "Negócio", "Call" => "Telefonar", @@ -22,7 +33,9 @@ "Projects" => "Projetos", "Questions" => "Perguntas", "Work" => "Trabalho", +"by" => "por", "unnamed" => "não definido", +"New Calendar" => "Novo calendário", "Does not repeat" => "Não repete", "Daily" => "Diário", "Weekly" => "Semanal", @@ -67,8 +80,26 @@ "by day and month" => "por dia e mês", "Date" => "Data", "Cal." => "Cal.", +"Sun." => "Dom.", +"Mon." => "Seg.", +"Tue." => "ter.", +"Wed." => "Qua.", +"Thu." => "Qui.", +"Fri." => "Sex.", +"Sat." => "Sáb.", +"Jan." => "Jan.", +"Feb." => "Fev,", +"Mar." => "Mar.", +"Apr." => "Abr.", +"May." => "Mai.", +"Jun." => "Jun.", +"Jul." => "Jul.", +"Aug." => "Ago.", +"Sep." => "Set.", +"Oct." => "Out.", +"Nov." => "Nov.", +"Dec." => "Dez.", "All day" => "Todo o dia", -"New Calendar" => "Novo calendário", "Missing fields" => "Falta campos", "Title" => "TÃtulo", "From Date" => "Da data", @@ -81,9 +112,7 @@ "Month" => "Mês", "List" => "Lista", "Today" => "Hoje", -"Calendars" => "Calendários", -"There was a fail, while parsing the file." => "Houve uma falha durante a análise do ficheiro", -"Choose active calendars" => "Escolhe calendários ativos", +"Settings" => "Configurações", "Your calendars" => "Os seus calendários", "CalDav Link" => "Endereço CalDav", "Shared calendars" => "Calendários partilhados", @@ -132,10 +161,12 @@ "Interval" => "Intervalo", "End" => "Fim", "occurrences" => "ocorrências", -"Import a calendar file" => "Importar um ficheiro de calendário", -"Please choose the calendar" => "Por favor escolhe o calendário", "create a new calendar" => "criar novo calendário", +"Import a calendar file" => "Importar um ficheiro de calendário", +"Please choose a calendar" => "Escolha um calendário por favor", "Name of new calendar" => "Nome do novo calendário", +"Take an available name!" => "Escolha um nome disponÃvel!", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Já existe um Calendário com esse nome. Se mesmo assim continuar, esses calendários serão fundidos.", "Import" => "Importar", "Importing calendar" => "A importar calendário", "Calendar imported successfully" => "Calendário importado com sucesso", @@ -143,16 +174,23 @@ "Create a new event" => "Criar novo evento", "View an event" => "Ver um evento", "No categories selected" => "Nenhuma categoria seleccionada", -"Select category" => "Selecionar categoria", "of" => "de", "at" => "em", +"General" => "Geral", "Timezone" => "Zona horária", -"Check always for changes of the timezone" => "Verificar sempre por alterações na zona horária", -"Timeformat" => "Formato da hora", +"Update timezone automatically" => "Actualizar automaticamente o fuso horário", +"Time format" => "Formato da hora", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Primeiro dia da semana", -"Calendar CalDAV syncing address:" => "Endereço de sincronização CalDav do calendário", +"Start week on" => "Começar semana em", +"Cache" => "Memória de pré-carregamento", +"Clear cache for repeating events" => "Limpar a memória de pré carregamento para eventos recorrentes", +"URLs" => "Endereço(s) web", +"Calendar CalDAV syncing addresses" => "Endereços de sincronização de calendários CalDAV", +"more info" => "mais informação", +"Primary address (Kontact et al)" => "Endereço principal (contactos et al.)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "Ligaç(ão/ões) só de leitura do iCalendar", "Users" => "Utilizadores", "select users" => "Selecione utilizadores", "Editable" => "Editavel", diff --git a/apps/calendar/l10n/ro.php b/apps/calendar/l10n/ro.php index 550afcd102f..696322dd730 100644 --- a/apps/calendar/l10n/ro.php +++ b/apps/calendar/l10n/ro.php @@ -23,6 +23,7 @@ "Questions" => "ÃŽntrebări", "Work" => "Servici", "unnamed" => "fără nume", +"New Calendar" => "Calendar nou", "Does not repeat" => "Nerepetabil", "Daily" => "Zilnic", "Weekly" => "Săptămânal", @@ -68,7 +69,6 @@ "Date" => "Data", "Cal." => "Cal.", "All day" => "Toată ziua", -"New Calendar" => "Calendar nou", "Missing fields" => "Câmpuri lipsă", "Title" => "Titlu", "From Date" => "ÃŽncepând cu", @@ -81,9 +81,6 @@ "Month" => "Luna", "List" => "Listă", "Today" => "Astăzi", -"Calendars" => "Calendare", -"There was a fail, while parsing the file." => "A fost întâmpinată o eroare în procesarea fiÈ™ierului", -"Choose active calendars" => "Alege calendarele active", "Your calendars" => "Calendarele tale", "CalDav Link" => "Legătură CalDav", "Shared calendars" => "Calendare partajate", @@ -132,6 +129,7 @@ "Interval" => "Interval", "End" => "SfârÈ™it", "occurrences" => "repetiÈ›ii", +"create a new calendar" => "crează un calendar nou", "Import a calendar file" => "Importă un calendar", "Please choose the calendar" => "AlegeÈ›i calendarul", "create a new calendar" => "crează un calendar nou", @@ -143,7 +141,6 @@ "Create a new event" => "Crează un eveniment nou", "View an event" => "Vizualizează un eveniment", "No categories selected" => "Nici o categorie selectată", -"Select category" => "Selecteză categoria", "of" => "din", "at" => "la", "Timezone" => "Fus orar", @@ -151,8 +148,6 @@ "Timeformat" => "Forma de afiÈ™are a orei", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Prima zi a săptămînii", -"Calendar CalDAV syncing address:" => "Adresa pentru sincronizarea calendarului CalDAV", "Users" => "Utilizatori", "select users" => "utilizatori selectaÈ›i", "Editable" => "Editabil", diff --git a/apps/calendar/l10n/ru.php b/apps/calendar/l10n/ru.php index af40b06b9ff..fbf5ec5ff74 100644 --- a/apps/calendar/l10n/ru.php +++ b/apps/calendar/l10n/ru.php @@ -1,11 +1,23 @@ <?php $TRANSLATIONS = array( +"Not all calendars are completely cached" => "Ðе вÑе календари полноÑтью кешированы", +"Everything seems to be completely cached" => "Ð’Ñе, вроде бы, закешировано", "No calendars found." => "Календари не найдены.", "No events found." => "Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð½Ðµ найдены.", "Wrong calendar" => "Ðеверный календарь", +"The file contained either no events or all events are already saved in your calendar." => "Файл либо не Ñобержит Ñобытий, либо вÑе ÑÐ¾Ð±Ñ‹Ñ‚Ð¸Ñ ÑƒÐ¶Ðµ еÑÑ‚ÑŒ в календаре", +"events has been saved in the new calendar" => "ÑÐ¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð±Ñ‹Ð»Ð¸ Ñохранены в новый календарь", +"Import failed" => "Ошибка импорта", +"events has been saved in your calendar" => "ÑÐ¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð±Ñ‹Ð»Ð¸ Ñохранены в вашем календаре", "New Timezone:" => "Ðовый чаÑовой поÑÑ:", "Timezone changed" => "ЧаÑовой поÑÑ Ð¸Ð·Ð¼ÐµÐ½Ñ‘Ð½", "Invalid request" => "Ðеверный запроÑ", "Calendar" => "Календарь", +"ddd" => "ддд", +"ddd M/d" => "ддд Ðœ/д", +"dddd M/d" => "дддд Ðœ/д", +"MMMM yyyy" => "ММММ гггг", +"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "дддд, МММ д, гггг", "Birthday" => "День рождениÑ", "Business" => "БизнеÑ", "Call" => "Звонить", @@ -21,7 +33,9 @@ "Projects" => "Проекты", "Questions" => "ВопроÑÑ‹", "Work" => "Работа", +"by" => "до ÑвиданиÑ", "unnamed" => "без имени", +"New Calendar" => "Ðовый Календарь", "Does not repeat" => "Ðе повторÑетÑÑ", "Daily" => "Ежедневно", "Weekly" => "Еженедельно", @@ -66,8 +80,26 @@ "by day and month" => "по дню и меÑÑцу", "Date" => "Дата", "Cal." => "Кал.", +"Sun." => "Ð’Ñ.", +"Mon." => "Пн.", +"Tue." => "Ð’Ñ‚.", +"Wed." => "Ср.", +"Thu." => "Чт.", +"Fri." => "Пт.", +"Sat." => "Сб.", +"Jan." => "Янв.", +"Feb." => "Фев.", +"Mar." => "Мар.", +"Apr." => "Ðпр.", +"May." => "Май.", +"Jun." => "Июн.", +"Jul." => "Июл.", +"Aug." => "Ðвг.", +"Sep." => "Сен.", +"Oct." => "Окт.", +"Nov." => "ÐоÑ.", +"Dec." => "Дек.", "All day" => "ВеÑÑŒ день", -"New Calendar" => "Ðовый Календарь", "Missing fields" => "Ðезаполненные полÑ", "Title" => "Ðазвание", "From Date" => "Дата начала", @@ -80,9 +112,7 @@ "Month" => "МеÑÑц", "List" => "СпиÑок", "Today" => "СегоднÑ", -"Calendars" => "Календари", -"There was a fail, while parsing the file." => "Ðе удалоÑÑŒ обработать файл.", -"Choose active calendars" => "Выберите активные календари", +"Settings" => "Параметры", "Your calendars" => "Ваши календари", "CalDav Link" => "СÑылка Ð´Ð»Ñ CalDav", "Shared calendars" => "Общие календари", @@ -131,10 +161,12 @@ "Interval" => "Интервал", "End" => "Окончание", "occurrences" => "повторений", -"Import a calendar file" => "Импортировать календарь из файла", -"Please choose the calendar" => "ПожалуйÑта, выберите календарь", "create a new calendar" => "Создать новый календарь", +"Import a calendar file" => "Импортировать календарь из файла", +"Please choose a calendar" => "ПожалуйÑта, выберите календарь", "Name of new calendar" => "Ðазвание нового календарÑ", +"Take an available name!" => "Возьмите разрешенное имÑ!", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Календарь Ñ Ñ‚Ð°ÐºÐ¸Ð¼ именем уже ÑущеÑтвует. ЕÑли вы продолжите, одноименный календарь будет удален.", "Import" => "Импортировать", "Importing calendar" => "ИмпортируетÑÑ ÐºÐ°Ð»ÐµÐ½Ð´Ð°Ñ€ÑŒ", "Calendar imported successfully" => "Календарь уÑпешно импортирован", @@ -142,14 +174,23 @@ "Create a new event" => "Создать новое Ñобытие", "View an event" => "Показать Ñобытие", "No categories selected" => "Категории не выбраны", -"Select category" => "Выбрать категорию", +"of" => "из", +"at" => "на", +"General" => "ОÑновные", "Timezone" => "ЧаÑовой поÑÑ", -"Check always for changes of the timezone" => "Ð’Ñегда проверÑйте изменение чаÑового поÑÑа", -"Timeformat" => "Формат времени", +"Update timezone automatically" => "ÐвтоматичеÑкое обновление временной зоны", +"Time format" => "Формат времени", "24h" => "24ч", "12h" => "12ч", -"First day of the week" => "Первый день недели", -"Calendar CalDAV syncing address:" => "ÐÐ´Ñ€ÐµÑ Ñинхронизации ÐºÐ°Ð»ÐµÐ½Ð´Ð°Ñ€Ñ CalDAV:", +"Start week on" => "Ðачало недели", +"Cache" => "КÑш", +"Clear cache for repeating events" => "ОчиÑтить кÑш повторÑющихÑÑ Ñобытий", +"URLs" => "URLs", +"Calendar CalDAV syncing addresses" => "ÐÐ´Ñ€ÐµÑ Ñинхронизации CalDAV", +"more info" => "подробнее", +"Primary address (Kontact et al)" => "ОÑновной Ð°Ð´Ñ€ÐµÑ (Контакта)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "Читать только ÑÑылки iCalendar", "Users" => "Пользователи", "select users" => "выбрать пользователей", "Editable" => "Редактируемо", diff --git a/apps/calendar/l10n/sk_SK.php b/apps/calendar/l10n/sk_SK.php index e182a9d3ea4..65400c496d7 100644 --- a/apps/calendar/l10n/sk_SK.php +++ b/apps/calendar/l10n/sk_SK.php @@ -6,7 +6,12 @@ "Timezone changed" => "ÄŒasové pásmo zmenené", "Invalid request" => "Neplatná požiadavka", "Calendar" => "Kalendár", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM rrrr", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "d. MMM[ yyyy]{ '—' d.[ MMM] yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, rrrr", "Birthday" => "Narodeniny", "Business" => "Podnikanie", "Call" => "Hovor", @@ -23,6 +28,7 @@ "Questions" => "Otázky", "Work" => "Práca", "unnamed" => "nepomenovaný", +"New Calendar" => "Nový kalendár", "Does not repeat" => "NeopakovaÅ¥", "Daily" => "Denne", "Weekly" => "Týždenne", @@ -68,7 +74,6 @@ "Date" => "Dátum", "Cal." => "Kal.", "All day" => "Celý deň", -"New Calendar" => "Nový kalendár", "Missing fields" => "Nevyplnené položky", "Title" => "Nadpis", "From Date" => "Od dátumu", @@ -81,9 +86,6 @@ "Month" => "Mesiac", "List" => "Zoznam", "Today" => "Dnes", -"Calendars" => "Kalendáre", -"There was a fail, while parsing the file." => "Nastala chyba poÄas parsovania súboru.", -"Choose active calendars" => "Zvoľte aktÃvne kalendáre", "Your calendars" => "VaÅ¡e kalendáre", "CalDav Link" => "CalDav odkaz", "Shared calendars" => "Zdielané kalendáre", @@ -132,27 +134,19 @@ "Interval" => "Interval", "End" => "Koniec", "occurrences" => "výskyty", -"Import a calendar file" => "ImportovaÅ¥ súbor kalendára", -"Please choose the calendar" => "ProsÃm zvoľte kalendár", "create a new calendar" => "vytvoriÅ¥ nový kalendár", +"Import a calendar file" => "ImportovaÅ¥ súbor kalendára", "Name of new calendar" => "Meno nového kalendára", "Import" => "ImportovaÅ¥", -"Importing calendar" => "Importujem kalendár", -"Calendar imported successfully" => "Kalendár úspeÅ¡ne importovaný", "Close Dialog" => "ZatvoriÅ¥ dialóg", "Create a new event" => "VytvoriÅ¥ udalosÅ¥", "View an event" => "ZobraziÅ¥ udalosÅ¥", "No categories selected" => "Žiadne vybraté kategórie", -"Select category" => "VybraÅ¥ kategóriu", "of" => "z", "at" => "v", "Timezone" => "ÄŒasová zóna", -"Check always for changes of the timezone" => "Vždy kontroluj zmeny Äasového pásma", -"Timeformat" => "Formát Äasu", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Prvý deň v týždni", -"Calendar CalDAV syncing address:" => "SynchronizaÄná adresa kalendára CalDAV: ", "Users" => "PoužÃvatelia", "select users" => "vybraÅ¥ použÃvateľov", "Editable" => "Upravovateľné", diff --git a/apps/calendar/l10n/sl.php b/apps/calendar/l10n/sl.php index 3bf03ede127..585132314bb 100644 --- a/apps/calendar/l10n/sl.php +++ b/apps/calendar/l10n/sl.php @@ -1,12 +1,23 @@ <?php $TRANSLATIONS = array( +"Not all calendars are completely cached" => "Vsi koledarji niso povsem predpomnjeni", +"Everything seems to be completely cached" => "Izgleda, da je vse v predpomnilniku", "No calendars found." => "Ni bilo najdenih koledarjev.", "No events found." => "Ni bilo najdenih dogodkov.", "Wrong calendar" => "NapaÄen koledar", +"The file contained either no events or all events are already saved in your calendar." => "Datoteka ni vsebovala dogodkov ali pa so vsi dogodki že shranjeni v koledarju.", +"events has been saved in the new calendar" => "dogodki so bili shranjeni v nov koledar", +"Import failed" => "Uvoz je spodletel", +"events has been saved in your calendar" => "dogodki so bili shranjeni v vaÅ¡ koledar", "New Timezone:" => "Nov Äasovni pas:", "Timezone changed" => "ÄŒasovni pas je bil spremenjen", "Invalid request" => "Neveljaven zahtevek", "Calendar" => "Koledar", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", "Birthday" => "Rojstni dan", "Business" => "Poslovno", "Call" => "PokliÄi", @@ -22,7 +33,9 @@ "Projects" => "Projekt", "Questions" => "VpraÅ¡anja", "Work" => "Delo", +"by" => "od", "unnamed" => "neimenovan", +"New Calendar" => "Nov koledar", "Does not repeat" => "Se ne ponavlja", "Daily" => "Dnevno", "Weekly" => "Tedensko", @@ -67,8 +80,26 @@ "by day and month" => "po dnevu in mesecu", "Date" => "Datum", "Cal." => "Kol.", +"Sun." => "ned.", +"Mon." => "pon.", +"Tue." => "tor.", +"Wed." => "sre.", +"Thu." => "Äet.", +"Fri." => "pet.", +"Sat." => "sob.", +"Jan." => "jan.", +"Feb." => "feb.", +"Mar." => "mar.", +"Apr." => "apr.", +"May." => "maj", +"Jun." => "jun.", +"Jul." => "jul.", +"Aug." => "avg.", +"Sep." => "sep.", +"Oct." => "okt.", +"Nov." => "nov.", +"Dec." => "dec.", "All day" => "Cel dan", -"New Calendar" => "Nov koledar", "Missing fields" => "MankajoÄa polja", "Title" => "Naslov", "From Date" => "od Datum", @@ -81,9 +112,7 @@ "Month" => "Mesec", "List" => "Seznam", "Today" => "Danes", -"Calendars" => "Koledarji", -"There was a fail, while parsing the file." => "Pri razÄlenjevanju datoteke je priÅ¡lo do napake.", -"Choose active calendars" => "Izberite aktivne koledarje", +"Settings" => "Nastavitve", "Your calendars" => "VaÅ¡i koledarji", "CalDav Link" => "CalDav povezava", "Shared calendars" => "Koledarji v souporabi", @@ -132,27 +161,34 @@ "Interval" => "ÄŒasovni razmik", "End" => "Konec", "occurrences" => "ponovitev", -"Import a calendar file" => "Uvozi datoteko koledarja", -"Please choose the calendar" => "Izberi koledar", "create a new calendar" => "Ustvari nov koledar", +"Import a calendar file" => "Uvozi datoteko koledarja", +"Please choose a calendar" => "Prosimo, Äe izberete koledar", "Name of new calendar" => "Ime novega koledarja", +"Take an available name!" => "Izberite prosto ime!", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Koledar s tem imenom že obstaja. ÄŒe nadaljujete, bosta koledarja združena.", "Import" => "Uvozi", -"Importing calendar" => "Uvažam koledar", -"Calendar imported successfully" => "Koledar je bil uspeÅ¡no uvožen", "Close Dialog" => "Zapri dialog", "Create a new event" => "Ustvari nov dogodek", "View an event" => "Poglej dogodek", "No categories selected" => "Nobena kategorija ni izbrana", -"Select category" => "Izberi kategorijo", "of" => "od", "at" => "pri", +"General" => "SploÅ¡no", "Timezone" => "ÄŒasovni pas", -"Check always for changes of the timezone" => "Vedno preveri za spremembe Äasovnega pasu", -"Timeformat" => "Zapis Äasa", +"Update timezone automatically" => "Samodejno posodobi Äasovni pas", +"Time format" => "Oblika zapisa Äasa", "24h" => "24ur", "12h" => "12ur", -"First day of the week" => "Prvi dan v tednu", -"Calendar CalDAV syncing address:" => "CalDAV sinhronizacijski naslov koledarja:", +"Start week on" => "ZaÄni teden z", +"Cache" => "Predpomnilnik", +"Clear cache for repeating events" => "PoÄisti predpomnilnik za ponavljajoÄe dogodke", +"URLs" => "URLji", +"Calendar CalDAV syncing addresses" => "CalDAV naslov za usklajevanje koledarjev", +"more info" => "dodatne informacije", +"Primary address (Kontact et al)" => "Glavni naslov (Kontakt et al)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "iCalendar povezava/e samo za branje", "Users" => "Uporabniki", "select users" => "izberite uporabnike", "Editable" => "Možno urejanje", diff --git a/apps/calendar/l10n/sr.php b/apps/calendar/l10n/sr.php index 5798c66e0ab..4ec60e20cbe 100644 --- a/apps/calendar/l10n/sr.php +++ b/apps/calendar/l10n/sr.php @@ -18,6 +18,7 @@ "Projects" => "Пројекти", "Questions" => "Питања", "Work" => "ПоÑао", +"New Calendar" => "Ðови календар", "Does not repeat" => "Ðе понавља Ñе", "Daily" => "дневно", "Weekly" => "недељно", @@ -26,15 +27,11 @@ "Monthly" => "меÑечно", "Yearly" => "годишње", "All day" => "Цео дан", -"New Calendar" => "Ðови календар", "Title" => "ÐаÑлов", "Week" => "Ðедеља", "Month" => "МеÑец", "List" => "СпиÑак", "Today" => "ДанаÑ", -"Calendars" => "Календари", -"There was a fail, while parsing the file." => "дошло је до грешке при раÑчлањивању фајла.", -"Choose active calendars" => "Изаберите активне календаре", "CalDav Link" => "КалДав веза", "Download" => "Преузми", "Edit" => "Уреди", @@ -59,6 +56,5 @@ "Description of the Event" => "ÐžÐ¿Ð¸Ñ Ð´Ð¾Ð³Ð°Ñ’Ð°Ñ˜Ð°", "Repeat" => "Понављај", "Create a new event" => "Ðаправи нови догађај", -"Select category" => "Изаберите категорију", "Timezone" => "ВременÑка зона" ); diff --git a/apps/calendar/l10n/sr@latin.php b/apps/calendar/l10n/sr@latin.php index c261f903f76..4ceabcbae59 100644 --- a/apps/calendar/l10n/sr@latin.php +++ b/apps/calendar/l10n/sr@latin.php @@ -18,6 +18,7 @@ "Projects" => "Projekti", "Questions" => "Pitanja", "Work" => "Posao", +"New Calendar" => "Novi kalendar", "Does not repeat" => "Ne ponavlja se", "Daily" => "dnevno", "Weekly" => "nedeljno", @@ -26,15 +27,11 @@ "Monthly" => "meseÄno", "Yearly" => "godiÅ¡nje", "All day" => "Ceo dan", -"New Calendar" => "Novi kalendar", "Title" => "Naslov", "Week" => "Nedelja", "Month" => "Mesec", "List" => "Spisak", "Today" => "Danas", -"Calendars" => "Kalendari", -"There was a fail, while parsing the file." => "doÅ¡lo je do greÅ¡ke pri rasÄlanjivanju fajla.", -"Choose active calendars" => "Izaberite aktivne kalendare", "CalDav Link" => "KalDav veza", "Download" => "Preuzmi", "Edit" => "Uredi", @@ -59,6 +56,5 @@ "Description of the Event" => "Opis dogaÄ‘aja", "Repeat" => "Ponavljaj", "Create a new event" => "Napravi novi dogaÄ‘aj", -"Select category" => "Izaberite kategoriju", "Timezone" => "Vremenska zona" ); diff --git a/apps/calendar/l10n/sv.php b/apps/calendar/l10n/sv.php index 59f8c6e6b5d..4cea9073a26 100644 --- a/apps/calendar/l10n/sv.php +++ b/apps/calendar/l10n/sv.php @@ -1,12 +1,23 @@ <?php $TRANSLATIONS = array( +"Not all calendars are completely cached" => "Alla kalendrar är inte fullständigt sparade i cache", +"Everything seems to be completely cached" => "Allt verkar vara fullständigt sparat i cache", "No calendars found." => "Inga kalendrar funna", "No events found." => "Inga händelser funna.", "Wrong calendar" => "Fel kalender", +"The file contained either no events or all events are already saved in your calendar." => "Filen innehöll inga händelser eller sÃ¥ är alla händelser redan sparade i kalendern.", +"events has been saved in the new calendar" => "händelser har sparats i den nya kalendern", +"Import failed" => "Misslyckad import", +"events has been saved in your calendar" => "händelse har sparats i din kalender", "New Timezone:" => "Ny tidszon:", "Timezone changed" => "Tidszon ändrad", "Invalid request" => "Ogiltig begäran", "Calendar" => "Kalender", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM åååå", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "ddd, MMM d, åååå", "Birthday" => "Födelsedag", "Business" => "Företag", "Call" => "Ringa", @@ -22,7 +33,9 @@ "Projects" => "Projekt", "Questions" => "FrÃ¥gor", "Work" => "Arbetet", +"by" => "av", "unnamed" => "Namn saknas", +"New Calendar" => "Ny kalender", "Does not repeat" => "Upprepas inte", "Daily" => "Dagligen", "Weekly" => "Varje vecka", @@ -67,8 +80,26 @@ "by day and month" => "efter dag och mÃ¥nad", "Date" => "Datum", "Cal." => "Kal.", +"Sun." => "Sön.", +"Mon." => "MÃ¥n.", +"Tue." => "Tis.", +"Wed." => "Ons.", +"Thu." => "Tor.", +"Fri." => "Fre.", +"Sat." => "Lör.", +"Jan." => "Jan.", +"Feb." => "Feb.", +"Mar." => "Mar.", +"Apr." => "Apr.", +"May." => "Maj.", +"Jun." => "Jun.", +"Jul." => "Jul.", +"Aug." => "Aug.", +"Sep." => "Sep.", +"Oct." => "Okt.", +"Nov." => "Nov.", +"Dec." => "Dec.", "All day" => "Hela dagen", -"New Calendar" => "Ny kalender", "Missing fields" => "Saknade fält", "Title" => "Rubrik", "From Date" => "FrÃ¥n datum", @@ -81,9 +112,6 @@ "Month" => "MÃ¥nad", "List" => "Lista", "Today" => "Idag", -"Calendars" => "Kalendrar", -"There was a fail, while parsing the file." => "Det blev ett fel medan filen analyserades.", -"Choose active calendars" => "Välj aktiva kalendrar", "Your calendars" => "Dina kalendrar", "CalDav Link" => "CalDAV-länk", "Shared calendars" => "Delade kalendrar", @@ -132,10 +160,12 @@ "Interval" => "Hur ofta", "End" => "Slut", "occurrences" => "Händelser", -"Import a calendar file" => "Importera en kalenderfil", -"Please choose the calendar" => "Välj kalender", "create a new calendar" => "skapa en ny kalender", +"Import a calendar file" => "Importera en kalenderfil", +"Please choose a calendar" => "Välj en kalender", "Name of new calendar" => "Namn pÃ¥ ny kalender", +"Take an available name!" => "Ta ett ledigt namn!", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "En kalender med detta namn finns redan. Om du fortsätter ändÃ¥ sÃ¥ kommer dessa kalendrar att slÃ¥s samman.", "Import" => "Importera", "Importing calendar" => "Importerar kalender", "Calendar imported successfully" => "Kalender importerades utan problem", @@ -143,7 +173,6 @@ "Create a new event" => "Skapa en ny händelse", "View an event" => "Visa en händelse", "No categories selected" => "Inga kategorier valda", -"Select category" => "Välj kategori", "of" => "av", "at" => "pÃ¥", "Timezone" => "Tidszon", @@ -151,8 +180,13 @@ "Timeformat" => "Tidsformat", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Första dagen av veckan", -"Calendar CalDAV syncing address:" => "Synkroniseringsadress för CalDAV kalender:", +"Cache" => "Cache", +"Clear cache for repeating events" => "Töm cache för upprepade händelser", +"Calendar CalDAV syncing addresses" => "Kalender CalDAV synkroniserar adresser", +"more info" => "mer info", +"Primary address (Kontact et al)" => "Primary address (Kontact et al)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "Read only iCalendar link(s)", "Users" => "Användare", "select users" => "välj användare", "Editable" => "Redigerbar", diff --git a/apps/calendar/l10n/th_TH.php b/apps/calendar/l10n/th_TH.php index 8aaa7ae756a..0b92a623d40 100644 --- a/apps/calendar/l10n/th_TH.php +++ b/apps/calendar/l10n/th_TH.php @@ -1,12 +1,23 @@ <?php $TRANSLATIONS = array( +"Not all calendars are completely cached" => "ไม่ใช่ปà¸à¸´à¸—ินทั้งหมดที่จะถูà¸à¸ˆà¸±à¸”เà¸à¹‡à¸šà¸‚้à¸à¸¡à¸¹à¸¥à¹„ว้ในหน่วยความจำà¹à¸„ชà¸à¸¢à¹ˆà¸²à¸‡à¸ªà¸¡à¸šà¸¹à¸£à¸“์", +"Everything seems to be completely cached" => "ทุà¸à¸ªà¸´à¹ˆà¸‡à¸—ุà¸à¸à¸¢à¹ˆà¸²à¸‡à¹„ด้ถูà¸à¹€à¸à¹‡à¸šà¹€à¸‚้าไปไว้ในหน่วยความจำà¹à¸„ชà¸à¸¢à¹ˆà¸²à¸‡à¸ªà¸¡à¸šà¸¹à¸£à¸“์à¹à¸¥à¹‰à¸§", "No calendars found." => "ไม่พบปà¸à¸´à¸—ินที่ต้à¸à¸‡à¸à¸²à¸£", "No events found." => "ไม่พบà¸à¸´à¸ˆà¸à¸£à¸£à¸¡à¸—ี่ต้à¸à¸‡à¸à¸²à¸£", "Wrong calendar" => "ปà¸à¸´à¸—ินไม่ถูà¸à¸•à¹‰à¸à¸‡", +"The file contained either no events or all events are already saved in your calendar." => "ไฟล์ดังà¸à¸¥à¹ˆà¸²à¸§à¸šà¸£à¸£à¸ˆà¸¸à¸‚้à¸à¸¡à¸¹à¸¥à¸à¸´à¸ˆà¸à¸£à¸£à¸¡à¸—ี่มีà¸à¸¢à¸¹à¹ˆà¹à¸¥à¹‰à¸§à¹ƒà¸™à¸›à¸à¸´à¸—ินขà¸à¸‡à¸„ุณ", +"events has been saved in the new calendar" => "à¸à¸´à¸ˆà¸à¸£à¸£à¸¡à¹„ด้ถูà¸à¸šà¸±à¸™à¸—ึà¸à¹„ปไว้ในปà¸à¸´à¸—ินที่สร้างขึ้นใหม่à¹à¸¥à¹‰à¸§", +"Import failed" => "à¸à¸²à¸£à¸™à¸³à¹€à¸‚้าข้à¸à¸¡à¸¹à¸¥à¸¥à¹‰à¸¡à¹€à¸«à¸¥à¸§", +"events has been saved in your calendar" => "à¸à¸´à¸ˆà¸à¸£à¸£à¸¡à¹„ด้ถูà¸à¸šà¸±à¸™à¸—ึà¸à¹€à¸‚้าไปไว้ในปà¸à¸´à¸—ินขà¸à¸‡à¸„ุณà¹à¸¥à¹‰à¸§", "New Timezone:" => "สร้างโซนเวลาใหม่:", "Timezone changed" => "โซนเวลาถูà¸à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸¥à¹‰à¸§", "Invalid request" => "คำร้à¸à¸‡à¸‚à¸à¹„ม่ถูà¸à¸•à¹‰à¸à¸‡", "Calendar" => "ปà¸à¸´à¸—ิน", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", "Birthday" => "วันเà¸à¸´à¸”", "Business" => "ธุรà¸à¸´à¸ˆ", "Call" => "โทรติดต่à¸", @@ -22,7 +33,9 @@ "Projects" => "โครงà¸à¸²à¸£", "Questions" => "คำถาม", "Work" => "งาน", +"by" => "โดย", "unnamed" => "ไม่มีชื่à¸", +"New Calendar" => "สร้างปà¸à¸´à¸—ินใหม่", "Does not repeat" => "ไม่ต้à¸à¸‡à¸—ำซ้ำ", "Daily" => "รายวัน", "Weekly" => "รายสัปดาห์", @@ -67,8 +80,26 @@ "by day and month" => "ตามวันà¹à¸¥à¸°à¹€à¸”ืà¸à¸™", "Date" => "วันที่", "Cal." => "คำนวณ", +"Sun." => "à¸à¸².", +"Mon." => "จ.", +"Tue." => "à¸.", +"Wed." => "พ.", +"Thu." => "พฤ.", +"Fri." => "ศ.", +"Sat." => "ส.", +"Jan." => "ม.ค.", +"Feb." => "à¸.พ.", +"Mar." => "มี.ค.", +"Apr." => "เม.ย.", +"May." => "พ.ค.", +"Jun." => "มิ.ย.", +"Jul." => "à¸.ค.", +"Aug." => "ส.ค.", +"Sep." => "à¸.ย.", +"Oct." => "ต.ค.", +"Nov." => "พ.ย.", +"Dec." => "ธ.ค.", "All day" => "ทั้งวัน", -"New Calendar" => "สร้างปà¸à¸´à¸—ินใหม่", "Missing fields" => "ช่à¸à¸‡à¸Ÿà¸´à¸¥à¸”์เà¸à¸´à¸”à¸à¸²à¸£à¸ªà¸¹à¸à¸«à¸²à¸¢", "Title" => "ชื่à¸à¸à¸´à¸ˆà¸à¸£à¸£à¸¡", "From Date" => "จาà¸à¸§à¸±à¸™à¸—ี่", @@ -81,9 +112,7 @@ "Month" => "เดืà¸à¸™", "List" => "รายà¸à¸²à¸£", "Today" => "วันนี้", -"Calendars" => "ปà¸à¸´à¸—ิน", -"There was a fail, while parsing the file." => "เà¸à¸´à¸”ความล้มเหลวในà¸à¸²à¸£à¹à¸¢à¸à¹„ฟล์", -"Choose active calendars" => "เลืà¸à¸à¸›à¸à¸´à¸—ินที่ต้à¸à¸‡à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™", +"Settings" => "ตั้งค่า", "Your calendars" => "ปà¸à¸´à¸—ินขà¸à¸‡à¸„ุณ", "CalDav Link" => "ลิงค์ CalDav", "Shared calendars" => "ปà¸à¸´à¸—ินที่เปิดà¹à¸Šà¸£à¹Œ", @@ -132,27 +161,34 @@ "Interval" => "ช่วงเวลา", "End" => "สิ้นสุด", "occurrences" => "จำนวนที่ปราà¸à¸", -"Import a calendar file" => "นำเข้าไฟล์ปà¸à¸´à¸—ิน", -"Please choose the calendar" => "à¸à¸£à¸“าเลืà¸à¸à¸›à¸à¸´à¸—ิน", "create a new calendar" => "สร้างปà¸à¸´à¸—ินใหม่", +"Import a calendar file" => "นำเข้าไฟล์ปà¸à¸´à¸—ิน", +"Please choose a calendar" => "à¸à¸£à¸¸à¸“าเลืà¸à¸à¸›à¸à¸´à¸—ิน", "Name of new calendar" => "ชื่à¸à¸‚à¸à¸‡à¸›à¸à¸´à¸—ิน", +"Take an available name!" => "เลืà¸à¸à¸Šà¸·à¹ˆà¸à¸—ี่ต้à¸à¸‡à¸à¸²à¸£", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "ปà¸à¸´à¸—ินชื่à¸à¸”ังà¸à¸¥à¹ˆà¸²à¸§à¸–ูà¸à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¹„ปà¹à¸¥à¹‰à¸§ หาà¸à¸„ุณยังดำเนินà¸à¸²à¸£à¸•à¹ˆà¸à¹„ป ปà¸à¸´à¸—ินดังà¸à¸¥à¹ˆà¸²à¸§à¸™à¸µà¹‰à¸ˆà¸°à¸–ูà¸à¸œà¸ªà¸²à¸™à¸‚้à¸à¸¡à¸¹à¸¥à¹€à¸‚้าด้วยà¸à¸±à¸™", "Import" => "นำเข้าข้à¸à¸¡à¸¹à¸¥", -"Importing calendar" => "นำเข้าข้à¸à¸¡à¸¹à¸¥à¸›à¸à¸´à¸—ิน", -"Calendar imported successfully" => "ปà¸à¸´à¸—ินถูà¸à¸™à¸³à¹€à¸‚้าข้à¸à¸¡à¸¹à¸¥à¹€à¸£à¸µà¸¢à¸šà¸£à¹‰à¸à¸¢à¹à¸¥à¹‰à¸§", "Close Dialog" => "ปิดà¸à¸¥à¹ˆà¸à¸‡à¸‚้à¸à¸„วามโต้ตà¸à¸š", "Create a new event" => "สร้างà¸à¸´à¸ˆà¸à¸£à¸£à¸¡à¹ƒà¸«à¸¡à¹ˆ", "View an event" => "ดูà¸à¸´à¸ˆà¸à¸£à¸£à¸¡", "No categories selected" => "ยังไม่ได้เลืà¸à¸à¸«à¸¡à¸§à¸”หมู่", -"Select category" => "เลืà¸à¸à¸«à¸¡à¸§à¸”หมู่", "of" => "ขà¸à¸‡", "at" => "ที่", +"General" => "ทั่วไป", "Timezone" => "โซนเวลา", -"Check always for changes of the timezone" => "ตรวจสà¸à¸šà¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡à¹‚ซนเวลาà¸à¸¢à¸¹à¹ˆà¹€à¸ªà¸¡à¸", -"Timeformat" => "รูปà¹à¸šà¸šà¸à¸²à¸£à¹à¸ªà¸”งเวลา", +"Update timezone automatically" => "à¸à¸±à¸žà¹€à¸”ทโซนเวลาà¸à¸±à¸•à¹‚นมัติ", +"Time format" => "รูปà¹à¸šà¸šà¹€à¸§à¸¥à¸²", "24h" => "24 ช.ม.", "12h" => "12 ช.ม.", -"First day of the week" => "วันà¹à¸£à¸à¸‚à¸à¸‡à¸ªà¸±à¸›à¸”าห์", -"Calendar CalDAV syncing address:" => "ที่à¸à¸¢à¸¹à¹ˆà¹ƒà¸™à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸à¸¡à¸‚้à¸à¸¡à¸¹à¸¥à¸à¸±à¸šà¸›à¸à¸´à¸—ิน CalDav:", +"Start week on" => "เริ่มต้นสัปดาห์ด้วย", +"Cache" => "หน่วยความจำà¹à¸„ช", +"Clear cache for repeating events" => "ล้างข้à¸à¸¡à¸¹à¸¥à¹ƒà¸™à¸«à¸™à¹ˆà¸§à¸¢à¸„วามจำà¹à¸„ชสำหรับà¸à¸´à¸ˆà¸à¸£à¸£à¸¡à¸—ี่ซ้ำซ้à¸à¸™", +"URLs" => "URLs", +"Calendar CalDAV syncing addresses" => "ที่à¸à¸¢à¸¹à¹ˆà¸—ี่ใช้สำหรับเชื่à¸à¸¡à¸‚้à¸à¸¡à¸¹à¸¥à¸›à¸à¸´à¸—ิน CalDAV", +"more info" => "ข้à¸à¸¡à¸¹à¸¥à¹€à¸žà¸´à¹ˆà¸¡à¹€à¸•à¸´à¸¡", +"Primary address (Kontact et al)" => "ที่à¸à¸¢à¸¹à¹ˆà¸«à¸¥à¸±à¸ (Kontact et al)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "à¸à¹ˆà¸²à¸™à¹€à¸‰à¸žà¸²à¸°à¸¥à¸´à¸‡à¸à¹Œ iCalendar เท่านั้น", "Users" => "ผู้ใช้งาน", "select users" => "เลืà¸à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™", "Editable" => "สามารถà¹à¸à¹‰à¹„ขได้", diff --git a/apps/calendar/l10n/tr.php b/apps/calendar/l10n/tr.php index a72e4c39f6d..b9256eb619e 100644 --- a/apps/calendar/l10n/tr.php +++ b/apps/calendar/l10n/tr.php @@ -1,12 +1,23 @@ <?php $TRANSLATIONS = array( +"Not all calendars are completely cached" => "Bütün takvimler tamamen ön belleÄŸe alınmadı", +"Everything seems to be completely cached" => "Bütün herÅŸey tamamen ön belleÄŸe alınmış görünüyor", "No calendars found." => "Takvim yok.", "No events found." => "Etkinlik yok.", "Wrong calendar" => "Yanlış takvim", +"The file contained either no events or all events are already saved in your calendar." => "Dosya ya hiçbir etkinlik içermiyor veya bütün etkinlikler takviminizde zaten saklı.", +"events has been saved in the new calendar" => "Etkinlikler yeni takvimde saklandı", +"Import failed" => "İçeri aktarma baÅŸarısız oldu.", +"events has been saved in your calendar" => "Etkinlikler takviminizde saklandı", "New Timezone:" => "Yeni Zamandilimi:", "Timezone changed" => "Zaman dilimi deÄŸiÅŸtirildi", "Invalid request" => "Geçersiz istek", "Calendar" => "Takvim", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "AAA g[ yyyy]{ '—'[ AAA] g yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", "Birthday" => "DoÄŸum günü", "Business" => "Ä°ÅŸ", "Call" => "Arama", @@ -22,7 +33,9 @@ "Projects" => "Projeler", "Questions" => "Sorular", "Work" => "Ä°ÅŸ", +"by" => "hazırlayan", "unnamed" => "isimsiz", +"New Calendar" => "Yeni Takvim", "Does not repeat" => "Tekrar etmiyor", "Daily" => "Günlük", "Weekly" => "Haftalı", @@ -67,8 +80,26 @@ "by day and month" => "gün ve aya göre", "Date" => "Tarih", "Cal." => "Takv.", +"Sun." => "Paz.", +"Mon." => "Pzt.", +"Tue." => "Sal.", +"Wed." => "Çar.", +"Thu." => "Per.", +"Fri." => "Cum.", +"Sat." => "Cmt.", +"Jan." => "Oca.", +"Feb." => "Åžbt.", +"Mar." => "Mar.", +"Apr." => "Nis", +"May." => "May.", +"Jun." => "Haz.", +"Jul." => "Tem.", +"Aug." => "Agu.", +"Sep." => "Eyl.", +"Oct." => "Eki.", +"Nov." => "Kas.", +"Dec." => "Ara.", "All day" => "Tüm gün", -"New Calendar" => "Yeni Takvim", "Missing fields" => "Eksik alanlar", "Title" => "BaÅŸlık", "From Date" => "Bu Tarihten", @@ -81,9 +112,6 @@ "Month" => "Ay", "List" => "Liste", "Today" => "Bugün", -"Calendars" => "Takvimler", -"There was a fail, while parsing the file." => "Dosya okunurken baÅŸarısızlık oldu.", -"Choose active calendars" => "Aktif takvimleri seçin", "Your calendars" => "Takvimleriniz", "CalDav Link" => "CalDav BaÄŸlantısı", "Shared calendars" => "Paylaşılan", @@ -132,27 +160,29 @@ "Interval" => "Aralık", "End" => "Son", "occurrences" => "olaylar", -"Import a calendar file" => "Takvim dosyasını içeri aktar", -"Please choose the calendar" => "Lütfen takvimi seçin", "create a new calendar" => "Yeni bir takvim oluÅŸtur", +"Import a calendar file" => "Takvim dosyasını içeri aktar", +"Please choose a calendar" => "Lütfen takvim seçiniz", "Name of new calendar" => "Yeni takvimin adı", +"Take an available name!" => "Müsait ismi al !", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Bu isimde bir takvim zaten mevcut. Yine de devam ederseniz bu takvimler birleÅŸtirilecektir.", "Import" => "İçe Al", -"Importing calendar" => "Takvim içe aktarılıyor", -"Calendar imported successfully" => "Takvim baÅŸarıyla içe aktarıldı", "Close Dialog" => "Diyalogu kapat", "Create a new event" => "Yeni olay oluÅŸtur", "View an event" => "Bir olay görüntüle", "No categories selected" => "Kategori seçilmedi", -"Select category" => "Kategori seçin", "of" => "nın", "at" => "üzerinde", "Timezone" => "Zaman dilimi", -"Check always for changes of the timezone" => "Sürekli zaman dilimi deÄŸiÅŸikliklerini kontrol et", -"Timeformat" => "Saat biçimi", "24h" => "24s", "12h" => "12s", -"First day of the week" => "Haftanın ilk günü", -"Calendar CalDAV syncing address:" => "CalDAV Takvim eÅŸzamanlama adresi:", +"Cache" => "Önbellek", +"Clear cache for repeating events" => "Tekrar eden etkinlikler için ön belleÄŸi temizle.", +"Calendar CalDAV syncing addresses" => "CalDAV takvimi adresleri senkronize ediyor.", +"more info" => "daha fazla bilgi", +"Primary address (Kontact et al)" => "Öncelikli adres", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "Sadece okunabilir iCalendar link(ler)i", "Users" => "Kullanıcılar", "select users" => "kullanıcıları seç", "Editable" => "Düzenlenebilir", diff --git a/apps/calendar/l10n/uk.php b/apps/calendar/l10n/uk.php index 892896742da..2911307e588 100644 --- a/apps/calendar/l10n/uk.php +++ b/apps/calendar/l10n/uk.php @@ -16,6 +16,7 @@ "Projects" => "Проекти", "Questions" => "ЗапитаннÑ", "Work" => "Робота", +"New Calendar" => "новий Календар", "Does not repeat" => "Ðе повторювати", "Daily" => "Щоденно", "Weekly" => "ЩотижнÑ", @@ -52,21 +53,29 @@ "Date" => "Дата", "Cal." => "Кал.", "All day" => "УвеÑÑŒ день", -"New Calendar" => "новий Календар", +"Missing fields" => "Пропущені полÑ", "Title" => "Ðазва", +"From Date" => "Від Дати", +"From Time" => "З ЧаÑу", +"To Date" => "До ЧаÑу", +"To Time" => "По Дату", +"The event ends before it starts" => "ÐŸÐ¾Ð´Ñ–Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐ°Ñ”Ñ‚ÑŒÑÑ Ð´Ð¾ Ñ—Ñ— початку", +"There was a database fail" => "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° бази даних", "Week" => "Тиждень", "Month" => "МіÑÑць", "List" => "СпиÑок", "Today" => "Сьогодні", -"Calendars" => "Календарі", -"There was a fail, while parsing the file." => "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при обробці файлу", -"Choose active calendars" => "Вибрати активні календарі", +"Your calendars" => "Ваші календарі", "Download" => "Завантажити", "Edit" => "Редагувати", +"Delete" => "Видалити", "New calendar" => "Ðовий календар", "Edit calendar" => "Редагувати календар", "Active" => "Ðктивний", "Calendar color" => "Колір календарÑ", +"Save" => "Зберегти", +"Cancel" => "Відмінити", +"Export" => "ЕкÑпорт", "Title of the Event" => "Ðазва події", "Category" => "КатегоріÑ", "From" => "З", @@ -76,14 +85,14 @@ "Description" => "ОпиÑ", "Description of the Event" => "ÐžÐ¿Ð¸Ñ Ð¿Ð¾Ð´Ñ–Ñ—", "Repeat" => "Повторювати", -"Import a calendar file" => "Імпортувати файл календарÑ", "create a new calendar" => "Ñтворити новий календар", +"Import a calendar file" => "Імпортувати файл календарÑ", "Name of new calendar" => "Ðазва нового календарÑ", -"Calendar imported successfully" => "Календар уÑпішно імпортовано", +"Import" => "Імпорт", "Create a new event" => "Створити нову подію", "Timezone" => "ЧаÑовий поÑÑ", -"Timeformat" => "Формат чаÑу", "24h" => "24г", "12h" => "12г", -"Calendar CalDAV syncing address:" => "ÐдреÑа Ñинхронізації ÐºÐ°Ð»ÐµÐ½Ð´Ð°Ñ€Ñ CalDAV:" +"Users" => "КориÑтувачі", +"Groups" => "Групи" ); diff --git a/apps/calendar/l10n/vi.php b/apps/calendar/l10n/vi.php new file mode 100644 index 00000000000..3594a095eba --- /dev/null +++ b/apps/calendar/l10n/vi.php @@ -0,0 +1,131 @@ +<?php $TRANSLATIONS = array( +"No calendars found." => "Không tìm thấy lịch.", +"No events found." => "Không tìm thấy sá»± kiện nà o", +"Wrong calendar" => "Sai lịch", +"New Timezone:" => "Múi giá» má»›i :", +"Timezone changed" => "Thay đổi múi giá»", +"Invalid request" => "Yêu cầu không hợp lệ", +"Calendar" => "Lịch", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", +"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", +"Birthday" => "Ngà y sinh nháºt", +"Business" => "Công việc", +"Call" => "Số Ä‘iện thoại", +"Clients" => "Máy trạm", +"Holidays" => "Ngà y lá»…", +"Ideas" => "à tưởng", +"Jubilee" => "Lá»… ká»· niệm", +"Meeting" => "Há»™i nghị", +"Other" => "Khác", +"Personal" => "Cá nhân", +"Projects" => "Dá»± án", +"Questions" => "Câu há»i", +"Work" => "Công việc", +"New Calendar" => "Lịch má»›i", +"Does not repeat" => "Không lặp lại", +"Daily" => "Hà ng ngà y", +"Weekly" => "Hà ng tuần", +"Every Weekday" => "Má»—i ngà y trong tuần", +"Bi-Weekly" => "Hai tuần má»™t lần", +"Monthly" => "Hà ng tháng", +"Yearly" => "Hà ng năm", +"never" => "không thay đổi", +"by occurrences" => "bởi xuất hiện", +"by date" => "bởi ngà y", +"by monthday" => "bởi ngà y trong tháng", +"by weekday" => "bởi ngà y trong tuần", +"Monday" => "Thứ 2", +"Tuesday" => "Thứ 3", +"Wednesday" => "Thứ 4", +"Thursday" => "Thứ 5", +"Friday" => "Thứ ", +"Saturday" => "Thứ 7", +"Sunday" => "Chủ nháºt", +"events week of month" => "sá»± kiện trong tuần của tháng", +"first" => "đầu tiên", +"second" => "Thứ hai", +"third" => "Thứ ba", +"fourth" => "Thứ tÆ°", +"fifth" => "Thứ năm", +"January" => "Tháng 1", +"February" => "Tháng 2", +"March" => "Tháng 3", +"April" => "Tháng 4", +"May" => "Tháng 5", +"June" => "Tháng 6", +"July" => "Tháng 7", +"August" => "Tháng 8", +"September" => "Tháng 9", +"October" => "Tháng 10", +"November" => "Tháng 11", +"December" => "Tháng 12", +"by events date" => "Theo ngà y tháng sá»± kiện", +"by weeknumber(s)" => "số tuần", +"by day and month" => "ngà y, tháng", +"Date" => "Ngà y", +"Cal." => "Cal.", +"All day" => "Tất cả các ngà y", +"Title" => "Tiêu Ä‘á»", +"From Date" => "Từ ngà y", +"From Time" => "Từ thá»i gian", +"To Date" => "Tá»›i ngà y", +"To Time" => "Tá»›i thá»i gian", +"The event ends before it starts" => "Sá»± kiện nà y kết thúc trÆ°á»›c khi nó bắt đầu", +"Week" => "Tuần", +"Month" => "Tháng", +"List" => "Danh sách", +"Today" => "Hôm nay", +"Your calendars" => "Lịch của bạn", +"CalDav Link" => "Liên kết CalDav ", +"Shared calendars" => "Chia sẻ lịch", +"No shared calendars" => "Không chia sẻ lcihj", +"Share Calendar" => "Chia sẻ lịch", +"Download" => "Tải vá»", +"Edit" => "Chỉnh sá»a", +"Delete" => "Xóa", +"shared with you by" => "Chia sẻ bởi", +"New calendar" => "Lịch má»›i", +"Edit calendar" => "sá»a Lịch", +"Displayname" => "Hiển thị tên", +"Active" => "KÃch hoạt", +"Calendar color" => "Mà u lịch", +"Save" => "LÆ°u", +"Submit" => "Submit", +"Cancel" => "Hủy", +"Edit an event" => "Sá»a sá»± kiện", +"Share" => "Chia sẻ", +"Title of the Event" => "Tên sá»± kiện", +"Category" => "Danh mục", +"All Day Event" => "Sá»± kiện trong ngà y", +"From" => "Từ", +"To" => "Tá»›i", +"Advanced options" => "Tùy chá»n nâng cao", +"Location" => "NÆ¡i", +"Location of the Event" => "NÆ¡i tổ chức sá»± kiện", +"Description" => "Mô tả", +"Description of the Event" => "Mô tả sá»± kiện", +"Repeat" => "Lặp lại", +"Advanced" => "Nâng cao", +"Select weekdays" => "Chá»n ngà y trong tuần", +"Select days" => "Chá»n ngà y", +"and the events day of year." => "và sá»± kiện của ngà y trong năm", +"and the events day of month." => "và sá»± kiện của má»™t ngà y trong năm", +"Select months" => "Chá»n tháng", +"Select weeks" => "Chá»n tuần", +"and the events week of year." => "và sá»± kiện của tuần trong năm.", +"create a new calendar" => "Tạo lịch má»›i", +"Name of new calendar" => "Tên lịch má»›i", +"Close Dialog" => "Äóng há»™p thoại", +"Create a new event" => "Tạo má»™t sá»± kiện má»›i", +"View an event" => "Xem má»™t sá»± kiện", +"No categories selected" => "Không danh sách nà o được chá»n", +"of" => "của", +"at" => "tại", +"Timezone" => "Múi giá»", +"24h" => "24h", +"12h" => "12h" +); diff --git a/apps/calendar/l10n/zh_CN.GB2312.php b/apps/calendar/l10n/zh_CN.GB2312.php new file mode 100644 index 00000000000..38f039e6611 --- /dev/null +++ b/apps/calendar/l10n/zh_CN.GB2312.php @@ -0,0 +1,121 @@ +<?php $TRANSLATIONS = array( +"Wrong calendar" => "错误的日历", +"New Timezone:" => "新时区", +"Timezone changed" => "时区改å˜äº†", +"Invalid request" => "éžæ³•è¯·æ±‚", +"Calendar" => "日历", +"Birthday" => "生日", +"Business" => "商务", +"Call" => "呼å«", +"Clients" => "客户端", +"Deliverer" => "交付者", +"Holidays" => "å‡æœŸ", +"Ideas" => "çµæ„Ÿ", +"Journey" => "æ—…è¡Œ", +"Jubilee" => "五å年纪念", +"Meeting" => "会é¢", +"Other" => "其它", +"Personal" => "个人的", +"Projects" => "项目", +"Questions" => "问题", +"Work" => "工作", +"New Calendar" => "新的日历", +"Does not repeat" => "ä¸è¦é‡å¤", +"Daily" => "æ¯å¤©", +"Weekly" => "æ¯æ˜ŸæœŸ", +"Every Weekday" => "æ¯ä¸ªå‘¨æœ«", +"Bi-Weekly" => "æ¯ä¸¤å‘¨", +"Monthly" => "æ¯ä¸ªæœˆ", +"Yearly" => "æ¯å¹´", +"never" => "从ä¸", +"by occurrences" => "æ ¹æ®å‘生时", +"by date" => "æ ¹æ®æ—¥æœŸ", +"by monthday" => "æ ¹æ®æœˆå¤©", +"by weekday" => "æ ¹æ®æ˜ŸæœŸ", +"Monday" => "星期一", +"Tuesday" => "星期二", +"Wednesday" => "星期三", +"Thursday" => "星期四", +"Friday" => "星期五", +"Saturday" => "星期å…", +"Sunday" => "星期天", +"events week of month" => "时间æ¯æœˆå‘生的周数", +"first" => "首先", +"second" => "其次", +"third" => "第三", +"fourth" => "第四", +"fifth" => "第五", +"last" => "最åŽ", +"January" => "一月", +"February" => "二月", +"March" => "三月", +"April" => "四月", +"May" => "五月", +"June" => "å…月", +"July" => "七月", +"August" => "八月", +"September" => "ä¹æœˆ", +"October" => "å月", +"November" => "å一月", +"December" => "å二月", +"by events date" => "æ ¹æ®æ—¶é—´æ—¥æœŸ", +"by yearday(s)" => "æ ¹æ®å¹´æ•°", +"by weeknumber(s)" => "æ ¹æ®å‘¨æ•°", +"by day and month" => "æ ¹æ®å¤©å’Œæœˆ", +"Date" => "日期", +"Cal." => "Cal.", +"All day" => "整天", +"Missing fields" => "丢失的输入框", +"Title" => "æ ‡é¢˜", +"From Date" => "从日期", +"From Time" => "从时间", +"To Date" => "到日期", +"To Time" => "到时间", +"The event ends before it starts" => "在它开始å‰éœ€è¦ç»“æŸçš„事件", +"There was a database fail" => "å‘生了一个数æ®åº“失败", +"Week" => "星期", +"Month" => "月", +"List" => "列表", +"Today" => "今天", +"CalDav Link" => "CalDav 链接", +"Download" => "下载", +"Edit" => "编辑", +"Delete" => "åˆ é™¤", +"New calendar" => "新的日历", +"Edit calendar" => "编辑日历", +"Displayname" => "显示å称", +"Active" => "活动", +"Calendar color" => "日历颜色", +"Save" => "ä¿å˜", +"Submit" => "æ交", +"Cancel" => " å–消", +"Edit an event" => "编辑一个事件", +"Export" => "导出", +"Title of the Event" => "äº‹ä»¶çš„æ ‡é¢˜", +"Category" => "分类", +"All Day Event" => "æ¯å¤©çš„事件", +"From" => "从", +"To" => "到", +"Advanced options" => "进阶选项", +"Location" => "地点", +"Location of the Event" => "事件的地点", +"Description" => "解释", +"Description of the Event" => "事件æè¿°", +"Repeat" => "é‡å¤", +"Advanced" => "进阶", +"Select weekdays" => "选择星期", +"Select days" => "选择日", +"and the events day of year." => "选择æ¯å¹´æ—¶é—´å‘生天数", +"and the events day of month." => "选择æ¯ä¸ªæœˆäº‹ä»¶å‘生的天", +"Select months" => "选择月份", +"Select weeks" => "选择星期", +"and the events week of year." => "æ¯å¹´æ—¶é—´å‘生的星期", +"Interval" => "é—´éš”", +"End" => "结æŸ", +"occurrences" => "å‘生", +"Import" => "导入", +"Create a new event" => "新建一个时间", +"Timezone" => "时区", +"24h" => "24å°æ—¶", +"12h" => "12å°æ—¶" +); diff --git a/apps/calendar/l10n/zh_CN.php b/apps/calendar/l10n/zh_CN.php index bb7e0a28724..add84588d35 100644 --- a/apps/calendar/l10n/zh_CN.php +++ b/apps/calendar/l10n/zh_CN.php @@ -6,6 +6,7 @@ "Timezone changed" => "时区已修改", "Invalid request" => "éžæ³•è¯·æ±‚", "Calendar" => "日历", +"ddd" => "ddd", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", "Birthday" => "生日", "Business" => "商务", @@ -23,6 +24,7 @@ "Questions" => "问题", "Work" => "工作", "unnamed" => "未命å", +"New Calendar" => "新日历", "Does not repeat" => "ä¸é‡å¤", "Daily" => "æ¯å¤©", "Weekly" => "æ¯å‘¨", @@ -68,7 +70,6 @@ "Date" => "日期", "Cal." => "日历", "All day" => "全天", -"New Calendar" => "新日历", "Missing fields" => "缺少å—段", "Title" => "æ ‡é¢˜", "From Date" => "从", @@ -132,6 +133,7 @@ "Interval" => "é—´éš”", "End" => "结æŸ", "occurrences" => "次", +"create a new calendar" => "创建新日历", "Import a calendar file" => "导入日历文件", "Please choose the calendar" => "请选择日历", "create a new calendar" => "创建新日历", @@ -143,7 +145,6 @@ "Create a new event" => "创建新事件", "View an event" => "查看事件", "No categories selected" => "æ— é€‰ä¸åˆ†ç±»", -"Select category" => "选择分类", "of" => "在", "at" => "在", "Timezone" => "时区", diff --git a/apps/calendar/l10n/zh_TW.php b/apps/calendar/l10n/zh_TW.php index 746594462c4..48897b8ca06 100644 --- a/apps/calendar/l10n/zh_TW.php +++ b/apps/calendar/l10n/zh_TW.php @@ -23,6 +23,7 @@ "Questions" => "å•é¡Œ", "Work" => "工作", "unnamed" => "ç„¡å稱的", +"New Calendar" => "新日曆", "Does not repeat" => "ä¸é‡è¦†", "Daily" => "æ¯æ—¥", "Weekly" => "æ¯é€±", @@ -68,7 +69,6 @@ "Date" => "日期", "Cal." => "行事曆", "All day" => "整天", -"New Calendar" => "新日曆", "Missing fields" => "éºå¤±æ¬„ä½", "Title" => "標題", "From Date" => "自日期", @@ -81,9 +81,6 @@ "Month" => "月", "List" => "清單", "Today" => "今日", -"Calendars" => "日曆", -"There was a fail, while parsing the file." => "解æžæª”案時失敗。", -"Choose active calendars" => "é¸æ“‡ä¸€å€‹ä½œç”¨ä¸çš„日曆", "Your calendars" => "ä½ çš„è¡Œäº‹æ›†", "CalDav Link" => "CalDav è¯çµ", "Shared calendars" => "分享的行事曆", @@ -132,6 +129,7 @@ "Interval" => "é–“éš”", "End" => "çµæŸ", "occurrences" => "事件", +"create a new calendar" => "建立新日曆", "Import a calendar file" => "匯入日曆檔案", "Please choose the calendar" => "è«‹é¸æ“‡æ—¥æ›†", "create a new calendar" => "建立新日曆", @@ -143,7 +141,6 @@ "Create a new event" => "建立一個新事件", "View an event" => "觀看一個活動", "No categories selected" => "沒有é¸æ“‡åˆ†é¡ž", -"Select category" => "é¸æ“‡åˆ†é¡ž", "of" => "æ–¼", "at" => "æ–¼", "Timezone" => "時å€", @@ -151,7 +148,6 @@ "Timeformat" => "æ—¥æœŸæ ¼å¼", "24h" => "24å°æ™‚制", "12h" => "12å°æ™‚制", -"First day of the week" => "æ¯é€±çš„第一天", "Calendar CalDAV syncing address:" => "CalDAV 的日曆åŒæ¥åœ°å€ï¼š", "Users" => "使用者", "select users" => "é¸æ“‡ä½¿ç”¨è€…", diff --git a/apps/calendar/lib/app.php b/apps/calendar/lib/app.php index 6c55bd19884..8bdb54f4867 100644 --- a/apps/calendar/lib/app.php +++ b/apps/calendar/lib/app.php @@ -9,26 +9,26 @@ * This class manages our app actions */ OC_Calendar_App::$l10n = new OC_L10N('calendar'); -OC_Calendar_App::$tz = OCP\Config::getUserValue(OCP\USER::getUser(), 'calendar', 'timezone', date_default_timezone_get()); +OC_Calendar_App::$tz = OC_Calendar_App::getTimezone(); class OC_Calendar_App{ const CALENDAR = 'calendar'; const EVENT = 'event'; - /* + /** * @brief language object for calendar app */ public static $l10n; - /* + /** * @brief categories of the user */ protected static $categories = null; - /* + /** * @brief timezone of the user */ public static $tz; - /* + /** * @brief returns informations about a calendar * @param int $id - id of the calendar * @param bool $security - check access rights or not @@ -50,13 +50,10 @@ class OC_Calendar_App{ return false; } } - if($calendar === false){ - return false; - } - return OC_Calendar_Calendar::find($id); + return $calendar; } - /* + /** * @brief returns informations about an event * @param int $id - id of the event * @param bool $security - check access rights or not @@ -82,7 +79,7 @@ class OC_Calendar_App{ return $event; } - /* + /** * @brief returns the parsed calendar data * @param int $id - id of the event * @param bool $security - check access rights or not @@ -100,7 +97,7 @@ class OC_Calendar_App{ return $vobject; } - /* + /** * @brief checks if an event was edited and dies if it was * @param (object) $vevent - vevent object of the event * @param (int) $lastmodified - time of last modification as unix timestamp @@ -115,12 +112,11 @@ class OC_Calendar_App{ return true; } - /* + /** * @brief returns the default categories of ownCloud * @return (array) $categories */ - protected static function getDefaultCategories() - { + protected static function getDefaultCategories(){ return array( self::$l10n->t('Birthday'), self::$l10n->t('Business'), @@ -140,7 +136,7 @@ class OC_Calendar_App{ ); } - /* + /** * @brief returns the vcategories object of the user * @return (object) $vcategories */ @@ -151,12 +147,11 @@ class OC_Calendar_App{ return self::$categories; } - /* + /** * @brief returns the categories of the vcategories object * @return (array) $categories */ - public static function getCategoryOptions() - { + public static function getCategoryOptions(){ $categories = self::getVCategories()->categories(); return $categories; } @@ -199,17 +194,24 @@ class OC_Calendar_App{ } else if (isset($calendar->VTODO)) { $object = $calendar->VTODO; + } else + if (isset($calendar->VJOURNAL)) { + $object = $calendar->VJOURNAL; } if ($object) { self::getVCategories()->loadFromVObject($object, true); } } - + + /** + * @brief returns the options for the repeat rule of an repeating event + * @return array - valid inputs for the repeat rule of an repeating event + */ public static function getRepeatOptions(){ return OC_Calendar_Object::getRepeatOptions(self::$l10n); } - /* + /** * @brief returns the options for the end of an repeating event * @return array - valid inputs for the end of an repeating events */ @@ -217,7 +219,7 @@ class OC_Calendar_App{ return OC_Calendar_Object::getEndOptions(self::$l10n); } - /* + /** * @brief returns the options for an monthly repeating event * @return array - valid inputs for monthly repeating events */ @@ -225,7 +227,7 @@ class OC_Calendar_App{ return OC_Calendar_Object::getMonthOptions(self::$l10n); } - /* + /** * @brief returns the options for an weekly repeating event * @return array - valid inputs for weekly repeating events */ @@ -233,7 +235,7 @@ class OC_Calendar_App{ return OC_Calendar_Object::getWeeklyOptions(self::$l10n); } - /* + /** * @brief returns the options for an yearly repeating event * @return array - valid inputs for yearly repeating events */ @@ -241,7 +243,7 @@ class OC_Calendar_App{ return OC_Calendar_Object::getYearOptions(self::$l10n); } - /* + /** * @brief returns the options for an yearly repeating event which occurs on specific days of the year * @return array - valid inputs for yearly repeating events */ @@ -249,7 +251,7 @@ class OC_Calendar_App{ return OC_Calendar_Object::getByYearDayOptions(); } - /* + /** * @brief returns the options for an yearly repeating event which occurs on specific month of the year * @return array - valid inputs for yearly repeating events */ @@ -257,7 +259,7 @@ class OC_Calendar_App{ return OC_Calendar_Object::getByMonthOptions(self::$l10n); } - /* + /** * @brief returns the options for an yearly repeating event which occurs on specific week numbers of the year * @return array - valid inputs for yearly repeating events */ @@ -265,7 +267,7 @@ class OC_Calendar_App{ return OC_Calendar_Object::getByWeekNoOptions(); } - /* + /** * @brief returns the options for an yearly or monthly repeating event which occurs on specific days of the month * @return array - valid inputs for yearly or monthly repeating events */ @@ -273,15 +275,25 @@ class OC_Calendar_App{ return OC_Calendar_Object::getByMonthDayOptions(); } - /* + /** * @brief returns the options for an monthly repeating event which occurs on specific weeks of the month * @return array - valid inputs for monthly repeating events */ public static function getWeekofMonth(){ return OC_Calendar_Object::getWeekofMonth(self::$l10n); } - - /* + + /** + * @return (string) $timezone as set by user or the default timezone + */ + public static function getTimezone() { + return OCP\Config::getUserValue(OCP\User::getUser(), + 'calendar', + 'timezone', + date_default_timezone_get()); + } + + /** * @brief checks the access for a calendar / an event * @param (int) $id - id of the calendar / event * @param (string) $type - type of the id (calendar/event) @@ -322,7 +334,7 @@ class OC_Calendar_App{ } } - /* + /** * @brief analyses the parameter for calendar parameter and returns the objects * @param (string) $calendarid - calendarid * @param (int) $start - unixtimestamp of start @@ -362,97 +374,62 @@ class OC_Calendar_App{ return $events; } - /* + /** * @brief generates the output for an event which will be readable for our js * @param (mixed) $event - event object / array - * @param (int) $start - unixtimestamp of start - * @param (int) $end - unixtimestamp of end + * @param (int) $start - DateTime object of start + * @param (int) $end - DateTime object of end * @return (array) $output - readable output */ public static function generateEventOutput($event, $start, $end){ - $output = array(); - - if(isset($event['calendardata'])){ - $object = OC_VObject::parse($event['calendardata']); - $vevent = $object->VEVENT; - }else{ - $vevent = $event['vevent']; + if(!isset($event['calendardata']) && !isset($event['vevent'])){ + return false; } - + if(!isset($event['calendardata']) && isset($event['vevent'])){ + $event['calendardata'] = "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:ownCloud's Internal iCal System\n" . $event['vevent']->serialize() . "END:VCALENDAR"; + } + $object = OC_VObject::parse($event['calendardata']); + $vevent = $object->VEVENT; + $return = array(); + $id = $event['id']; + $allday = ($vevent->DTSTART->getDateType() == Sabre_VObject_Element_DateTime::DATE)?true:false; $last_modified = @$vevent->__get('LAST-MODIFIED'); $lastmodified = ($last_modified)?$last_modified->getDateTime()->format('U'):0; - - $output = array('id'=>(int)$event['id'], + $staticoutput = array('id'=>(int)$event['id'], 'title' => ($event['summary']!=NULL || $event['summary'] != '')?$event['summary']: self::$l10n->t('unnamed'), 'description' => isset($vevent->DESCRIPTION)?$vevent->DESCRIPTION->value:'', - 'lastmodified'=>$lastmodified); - - $dtstart = $vevent->DTSTART; - $start_dt = $dtstart->getDateTime(); - $dtend = OC_Calendar_Object::getDTEndFromVEvent($vevent); - $end_dt = $dtend->getDateTime(); - - if ($dtstart->getDateType() == Sabre_VObject_Element_DateTime::DATE){ - $output['allDay'] = true; - }else{ - $output['allDay'] = false; - $start_dt->setTimezone(new DateTimeZone(self::$tz)); - $end_dt->setTimezone(new DateTimeZone(self::$tz)); - } - - // Handle exceptions to recurring events - $exceptionDateObjects = $vevent->select('EXDATE'); - $exceptionDateMap = Array(); - foreach ($exceptionDateObjects as $exceptionObject) { - foreach($exceptionObject->getDateTimes() as $datetime) { - $ts = $datetime->getTimestamp(); - $exceptionDateMap[idate('Y',$ts)][idate('m', $ts)][idate('d', $ts)] = true; - } - } - - $return = array(); - if($event['repeating'] == 1){ - $duration = (double) $end_dt->format('U') - (double) $start_dt->format('U'); - $r = new When(); - $r->recur($start_dt)->rrule((string) $vevent->RRULE); - /*$r = new iCal_Repeat_Generator(array('RECUR' => $start_dt, - * 'RRULE' => (string)$vevent->RRULE - * 'RDATE' => (string)$vevent->RDATE - * 'EXRULE' => (string)$vevent->EXRULE - * 'EXDATE' => (string)$vevent->EXDATE));*/ - while($result = $r->next()){ - if($result < $start){ - continue; - } - if($result > $end){ - break; - } - // Check for exceptions to recurring events - $ts = $result->getTimestamp(); - if (isset($exceptionDateMap[idate('Y',$ts)][idate('m', $ts)][idate('d', $ts)])) { - continue; - } - unset($ts); - - if($output['allDay'] == true){ - $output['start'] = $result->format('Y-m-d'); - $output['end'] = date('Y-m-d', $result->format('U') + --$duration); + 'lastmodified'=>$lastmodified, + 'allDay'=>$allday); + if(OC_Calendar_Object::isrepeating($id) && OC_Calendar_Repeat::is_cached_inperiod($event['id'], $start, $end)){ + $cachedinperiod = OC_Calendar_Repeat::get_inperiod($id, $start, $end); + foreach($cachedinperiod as $cachedevent){ + $dynamicoutput = array(); + if($allday){ + $start_dt = new DateTime($cachedevent['startdate'], new DateTimeZone('UTC')); + $end_dt = new DateTime($cachedevent['enddate'], new DateTimeZone('UTC')); + $dynamicoutput['start'] = $start_dt->format('Y-m-d'); + $dynamicoutput['end'] = $end_dt->format('Y-m-d'); }else{ - $output['start'] = $result->format('Y-m-d H:i:s'); - $output['end'] = date('Y-m-d H:i:s', $result->format('U') + $result->format('Z') + $duration); + $start_dt = new DateTime($cachedevent['startdate'], new DateTimeZone('UTC')); + $end_dt = new DateTime($cachedevent['enddate'], new DateTimeZone('UTC')); + $start_dt->setTimezone(new DateTimeZone(self::$tz)); + $end_dt->setTimezone(new DateTimeZone(self::$tz)); + $dynamicoutput['start'] = $start_dt->format('Y-m-d H:i:s'); + $dynamicoutput['end'] = $end_dt->format('Y-m-d H:i:s'); } - $return[] = $output; + $return[] = array_merge($staticoutput, $dynamicoutput); } }else{ - if($output['allDay'] == true){ - $output['start'] = $start_dt->format('Y-m-d'); - $end_dt->modify('-1 sec'); - $output['end'] = $end_dt->format('Y-m-d'); - }else{ - $output['start'] = $start_dt->format('Y-m-d H:i:s'); - $output['end'] = $end_dt->format('Y-m-d H:i:s'); + if(OC_Calendar_Object::isrepeating($id) || $event['repeating'] == 1){ + $object->expand($start, $end); + } + foreach($object->getComponents() as $singleevent){ + if(!($singleevent instanceof Sabre_VObject_Component_VEvent)){ + continue; + } + $dynamicoutput = OC_Calendar_Object::generateStartEndDate($singleevent->DTSTART, OC_Calendar_Object::getDTEndFromVEvent($singleevent), $allday, self::$tz); + $return[] = array_merge($staticoutput, $dynamicoutput); } - $return[] = $output; } return $return; } diff --git a/apps/calendar/lib/calendar.php b/apps/calendar/lib/calendar.php index 09cbee204dc..f8f5aab3636 100644 --- a/apps/calendar/lib/calendar.php +++ b/apps/calendar/lib/calendar.php @@ -5,24 +5,11 @@ * later. * See the COPYING-README file. */ -/* +/** * * The following SQL statement is just a help for developers and will not be * executed! * - * CREATE TABLE calendar_objects ( - * id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, - * calendarid INTEGER UNSIGNED NOT NULL, - * objecttype VARCHAR(40) NOT NULL, - * startdate DATETIME, - * enddate DATETIME, - * repeating INT(1), - * summary VARCHAR(255), - * calendardata TEXT, - * uri VARCHAR(100), - * lastmodified INT(11) - * ); - * * CREATE TABLE calendar_calendars ( * id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, * userid VARCHAR(255), @@ -35,6 +22,7 @@ * timezone TEXT, * components VARCHAR(20) * ); + * */ /** @@ -44,10 +32,10 @@ class OC_Calendar_Calendar{ /** * @brief Returns the list of calendars for a specific user. * @param string $uid User ID - * @param boolean $active Only return calendars with this $active state, default(=null) is don't care + * @param boolean $active Only return calendars with this $active state, default(=false) is don't care * @return array */ - public static function allCalendars($uid, $active=null){ + public static function allCalendars($uid, $active=false){ $values = array($uid); $active_where = ''; if (!is_null($active) && $active){ @@ -109,7 +97,10 @@ class OC_Calendar_Calendar{ $stmt = OCP\DB::prepare( 'INSERT INTO `*PREFIX*calendar_calendars` (`userid`,`displayname`,`uri`,`ctag`,`calendarorder`,`calendarcolor`,`timezone`,`components`) VALUES(?,?,?,?,?,?,?,?)' ); $result = $stmt->execute(array($userid,$name,$uri,1,$order,$color,$timezone,$components)); - return OCP\DB::insertid('*PREFIX*calendar_calendars'); + $insertid = OCP\DB::insertid('*PREFIX*calendar_calendars'); + OCP\Util::emitHook('OC_Calendar', 'addCalendar', $insertid); + + return $insertid; } /** @@ -129,7 +120,10 @@ class OC_Calendar_Calendar{ $stmt = OCP\DB::prepare( 'INSERT INTO `*PREFIX*calendar_calendars` (`userid`,`displayname`,`uri`,`ctag`,`calendarorder`,`calendarcolor`,`timezone`,`components`) VALUES(?,?,?,?,?,?,?,?)' ); $result = $stmt->execute(array($userid,$name,$uri,1,$order,$color,$timezone,$components)); - return OCP\DB::insertid('*PREFIX*calendar_calendars'); + $insertid = OCP\DB::insertid('*PREFIX*calendar_calendars'); + OCP\Util::emitHook('OC_Calendar', 'addCalendar', $insertid); + + return $insertid; } /** @@ -158,6 +152,7 @@ class OC_Calendar_Calendar{ $stmt = OCP\DB::prepare( 'UPDATE `*PREFIX*calendar_calendars` SET `displayname`=?,`calendarorder`=?,`calendarcolor`=?,`timezone`=?,`components`=?,`ctag`=`ctag`+1 WHERE `id`=?' ); $result = $stmt->execute(array($name,$order,$color,$timezone,$components,$id)); + OCP\Util::emitHook('OC_Calendar', 'editCalendar', $id); return true; } @@ -198,9 +193,27 @@ class OC_Calendar_Calendar{ $stmt = OCP\DB::prepare( 'DELETE FROM `*PREFIX*calendar_objects` WHERE `calendarid` = ?' ); $stmt->execute(array($id)); + OCP\Util::emitHook('OC_Calendar', 'deleteCalendar', $id); + if(count(self::allCalendars(OCP\USER::getUser())) == 0) { + self::addCalendar(OCP\USER::getUser(),'Default calendar'); + } + return true; } - + + /** + * @brief merges two calendars + * @param integer $id1 + * @param integer $id2 + * @return boolean + */ + public static function mergeCalendar($id1, $id2){ + $stmt = OCP\DB::prepare('UPDATE `*PREFIX*calendar_objects` SET `calendarid` = ? WHERE `calendarid` = ?'); + $stmt->execute(array($id1, $id2)); + self::touchCalendar($id1); + self::deleteCalendar($id2); + } + /** * @brief Creates a URI for Calendar * @param string $name name of the calendar @@ -226,6 +239,11 @@ class OC_Calendar_Calendar{ list($prefix,$userid) = Sabre_DAV_URLUtil::splitPath($principaluri); return $userid; } + + /** + * @brief returns the possible color for calendars + * @return array + */ public static function getCalendarColorOptions(){ return array( '#ff0000', // "Red" @@ -239,13 +257,52 @@ class OC_Calendar_Calendar{ ); } + /** + * @brief generates the Event Source Info for our JS + * @param array $calendar calendar data + * @return array + */ public static function getEventSourceInfo($calendar){ return array( 'url' => OCP\Util::linkTo('calendar', 'ajax/events.php').'?calendar_id='.$calendar['id'], 'backgroundColor' => $calendar['calendarcolor'], 'borderColor' => '#888', - 'textColor' => 'black', + 'textColor' => self::generateTextColor($calendar['calendarcolor']), 'cache' => true, ); } + + /* + * @brief checks if a calendar name is available for a user + * @param string $calendarname + * @param string $userid + * @return boolean + */ + public static function isCalendarNameavailable($calendarname, $userid){ + $calendars = self::allCalendars($userid); + foreach($calendars as $calendar){ + if($calendar['displayname'] == $calendarname){ + return false; + } + } + return true; + } + + /* + * @brief generates the text color for the calendar + * @param string $calendarcolor rgb calendar color code in hex format (with or without the leading #) + * (this function doesn't pay attention on the alpha value of rgba color codes) + * @return boolean + */ + public static function generateTextColor($calendarcolor){ + if(substr_count($calendarcolor, '#') == 1){ + $calendarcolor = substr($calendarcolor,1); + } + $red = hexdec(substr($calendarcolor,0,2)); + $green = hexdec(substr($calendarcolor,2,2)); + $blue = hexdec(substr($calendarcolor,2,2)); + //recommendation by W3C + $computation = ((($red * 299) + ($green * 587) + ($blue * 114)) / 1000); + return ($computation > 130)?'#000000':'#FAFAFA'; + } } diff --git a/apps/calendar/lib/export.php b/apps/calendar/lib/export.php new file mode 100644 index 00000000000..8f26891f366 --- /dev/null +++ b/apps/calendar/lib/export.php @@ -0,0 +1,94 @@ +<?php +/** + * Copyright (c) 2012 Georg Ehrke <ownclouddev@georgswebsite.de> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +/** + * This class does export and converts all times to UTC + */ +class OC_Calendar_Export{ + /** + * @brief Use one of these constants as second parameter if you call OC_Calendar_Export::export() + */ + const CALENDAR = 'calendar'; + const EVENT = 'event'; + + /** + * @brief export a calendar or an event + * @param integer $id id of calendar / event + * @param string $type use OC_Calendar_Export constants + * @return string + */ + public static function export($id, $type){ + if($type == self::EVENT){ + $return = self::event($id); + }else{ + $return = self::calendar($id); + } + return self::fixLineBreaks($return); + } + + /** + * @brief exports a calendar and convert all times to UTC + * @param integer $id id of the calendar + * @return string + */ + private static function calendar($id){ + $events = OC_Calendar_Object::all($id); + $calendar = OC_Calendar_Calendar::find($id); + $return = "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:ownCloud Calendar " . OCP\App::getAppVersion('calendar') . "\nX-WR-CALNAME:" . $calendar['displayname'] . "\n"; + foreach($events as $event){ + $return .= self::generateEvent($event); + } + $return .= "END:VCALENDAR"; + return $return; + } + + /** + * @brief exports an event and convert all times to UTC + * @param integer $id id of the event + * @return string + */ + private static function event($id){ + $event = OC_Calendar_Object::find($id); + $return = "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:ownCloud Calendar " . OCP\App::getAppVersion('calendar') . "\nX-WR-CALNAME:" . $event['summary'] . "\n"; + $return .= self::generateEvent($event); + $return .= "END:VCALENDAR"; + return $return; + } + + /** + * @brief generates the VEVENT with UTC dates + * @param array $event + * @return string + */ + private static function generateEvent($event){ + $object = OC_VObject::parse($event['calendardata']); + $dtstart = $object->VEVENT->DTSTART; + $start_dt = $dtstart->getDateTime(); + $dtend = OC_Calendar_Object::getDTEndFromVEvent($object->VEVENT); + $end_dt = $dtend->getDateTime(); + if($dtstart->getDateType() !== Sabre_VObject_Element_DateTime::DATE){ + $start_dt->setTimezone(new DateTimeZone('UTC')); + $end_dt->setTimezone(new DateTimeZone('UTC')); + $object->VEVENT->setDateTime('DTSTART', $start_dt, Sabre_VObject_Property_DateTime::UTC); + $object->VEVENT->setDateTime('DTEND', $end_dt, Sabre_VObject_Property_DateTime::UTC); + } + return $object->VEVENT->serialize(); + } + + /** + * @brief fixes new line breaks + * (fixes problems with Apple iCal) + * @param string $string to fix + * @return string + */ + private static function fixLineBreaks($string){ + $string = str_replace("\r\n", "\n", $string); + $string = str_replace("\r", "\n", $string); + $string = str_replace("\n", "\r\n", $string); + return $string; + } +} diff --git a/apps/calendar/lib/import.php b/apps/calendar/lib/import.php new file mode 100644 index 00000000000..368f8406e71 --- /dev/null +++ b/apps/calendar/lib/import.php @@ -0,0 +1,336 @@ +<?php +/** + * Copyright (c) 2012 Georg Ehrke <ownclouddev@georgswebsite.de> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +/* + * This class does import and converts all times to the users current timezone + */ +class OC_Calendar_Import{ + /* + * @brief counts the absolute number of parsed elements + */ + private $abscount; + + /* + * @brief var saves if the percentage should be saved with OC_Cache + */ + private $cacheprogress; + + /* + * @brief Sabre_VObject_Component_VCalendar object - for documentation see http://code.google.com/p/sabredav/wiki/Sabre_VObject_Component_VCalendar + */ + private $calobject; + + /* + * @brief var counts the number of imported elements + */ + private $count; + + /* + * @brief var to check if errors happend while initialization + */ + private $error; + + /* + * @brief var saves the ical string that was submitted with the __construct function + */ + private $ical; + + /* + * @brief calendar id for import + */ + private $id; + + /* + * @brief var saves the percentage of the import's progress + */ + private $progress; + + /* + * @brief var saves the key for the percentage of the import's progress + */ + private $progresskey; + + /* + * @brief var saves the timezone the events shell converted to + */ + private $tz; + + /* + * @brief var saves the userid + */ + private $userid; + + /* + * public methods + */ + + /* + * @brief does general initialization for import object + * @param string $calendar content of ical file + * @param string $tz timezone of the user + * @return boolean + */ + public function __construct($ical){ + $this->error = null; + $this->ical = $ical; + $this->abscount = 0; + $this->count = 0; + try{ + $this->calobject = OC_VObject::parse($this->ical); + }catch(Exception $e){ + //MISSING: write some log + $this->error = true; + return false; + } + return true; + } + + /* + * @brief imports a calendar + * @return boolean + */ + public function import(){ + if(!$this->isValid()){ + return false; + } + $numofcomponents = count($this->calobject->getComponents()); + foreach($this->calobject->getComponents() as $object){ + if(!($object instanceof Sabre_VObject_Component_VEvent) && !($object instanceof Sabre_VObject_Component_VJournal) && !($object instanceof Sabre_VObject_Component_VTodo)){ + continue; + } + $dtend = OC_Calendar_Object::getDTEndFromVEvent($object); + $object->DTSTART->getDateTime()->setTimezone(new DateTimeZone($this->tz)); + $object->DTEND->setDateTime($dtend->getDateTime(), $object->DTSTART->getDateType()); + $object->DTEND->getDateTime()->setTimezone(new DateTimeZone($this->tz)); + $vcalendar = $this->createVCalendar($object->serialize()); + $insertid = OC_Calendar_Object::add($this->id, $vcalendar); + $this->abscount++; + if($this->isDuplicate($insertid)){ + OC_Calendar_Object::delete($insertid); + }else{ + $this->count++; + } + $this->updateProgress(intval(($this->abscount / $numofcomponents)*100)); + } + OC_Cache::remove($this->progresskey); + return true; + } + + /* + * @brief sets the timezone + * @return boolean + */ + public function setTimeZone($tz){ + $this->tz = $tz; + return true; + } + + /* + * @brief sets the progresskey + * @return boolean + */ + public function setProgresskey($progresskey){ + $this->progresskey = $progresskey; + return true; + } + + /* + * @brief checks if something went wrong while initialization + * @return boolean + */ + public function isValid(){ + if(is_null($this->error)){ + return true; + } + return false; + } + + /* + * @brief returns the percentage of progress + * @return integer + */ + public function getProgress(){ + return $this->progress; + } + + /* + * @brief enables the cache for the percentage of progress + * @return boolean + */ + public function enableProgressCache(){ + $this->cacheprogress = true; + return true; + } + + /* + * @brief disables the cache for the percentage of progress + * @return boolean + */ + public function disableProgressCache(){ + $this->cacheprogress = false; + return false; + } + + /* + * @brief generates a new calendar name + * @return string + */ + public function createCalendarName(){ + $calendars = OC_Calendar_Calendar::allCalendars($this->userid); + $calendarname = $guessedcalendarname = !is_null($this->guessCalendarName())?($this->guessCalendarName()):(OC_Calendar_App::$l10n->t('New Calendar')); + $i = 1; + while(!OC_Calendar_Calendar::isCalendarNameavailable($calendarname, $this->userid)){ + $calendarname = $guessedcalendarname . ' (' . $i . ')'; + $i++; + } + return $calendarname; + } + + /* + * @brief generates a new calendar color + * @return string + */ + public function createCalendarColor(){ + if(is_null($this->guessCalendarColor())){ + return '#9fc6e7'; + } + return $this->guessCalendarColor(); + } + + /* + * @brief sets the id for the calendar + * @param integer $id of the calendar + * @return boolean + */ + public function setCalendarID($id){ + $this->id = $id; + return true; + } + + /* + * @brief sets the userid to import the calendar + * @param string $id of the user + * @return boolean + */ + public function setUserID($userid){ + $this->userid = $userid; + return true; + } + + /* + * @brief returns the private + * @param string $id of the user + * @return boolean + */ + public function getCount(){ + return $this->count; + } + + /* + * private methods + */ + + /* + * @brief generates an unique ID + * @return string + */ + //private function createUID(){ + // return substr(md5(rand().time()),0,10); + //} + + /* + * @brief checks is the UID is already in use for another event + * @param string $uid uid to check + * @return boolean + */ + //private function isUIDAvailable($uid){ + // + //} + + /* + * @brief generates a proper VCalendar string + * @param string $vobject + * @return string + */ + private function createVCalendar($vobject){ + if(is_object($vobject)){ + $vobject = @$vobject->serialize(); + } + $vcalendar = "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:ownCloud Calendar " . OCP\App::getAppVersion('calendar') . "\n"; + $vcalendar .= $vobject; + $vcalendar .= "END:VCALENDAR"; + return $vcalendar; + } + + /* + * @brief checks if an event already exists in the user's calendars + * @param integer $insertid id of the new object + * @return boolean + */ + private function isDuplicate($insertid){ + $newobject = OC_Calendar_Object::find($insertid); + $stmt = OCP\DB::prepare('SELECT COUNT(*) as count FROM *PREFIX*calendar_objects + INNER JOIN *PREFIX*calendar_calendars ON calendarid=*PREFIX*calendar_calendars.id + WHERE objecttype=? AND startdate=? AND enddate=? AND repeating=? AND summary=? AND calendardata=? AND userid = ?'); + $result = $stmt->execute(array($newobject['objecttype'],$newobject['startdate'],$newobject['enddate'],$newobject['repeating'],$newobject['summary'],$newobject['calendardata'], $this->userid)); + $result = $result->fetchRow(); + if($result['count'] >= 2){ + return true; + } + return false; + } + + /* + * @brief updates the progress var + * @param integer $percentage + * @return boolean + */ + private function updateProgress($percentage){ + $this->progress = $percentage; + if($this->cacheprogress){ + OC_Cache::set($this->progresskey, $this->progress, 300); + } + return true; + } + + /* + * public methods for (pre)rendering of X-... Attributes + */ + + /* + * @brief guesses the calendar color + * @return mixed - string or boolean + */ + public function guessCalendarColor(){ + if(!is_null($this->calobject->__get('X-APPLE-CALENDAR-COLOR'))){ + return $this->calobject->__get('X-APPLE-CALENDAR-COLOR'); + } + return null; + } + + /* + * @brief guesses the calendar description + * @return mixed - string or boolean + */ + public function guessCalendarDescription(){ + if(!is_null($this->calobject->__get('X-WR-CALDESC'))){ + return $this->calobject->__get('X-WR-CALDESC'); + } + return null; + } + + /* + * @brief guesses the calendar name + * @return mixed - string or boolean + */ + public function guessCalendarName(){ + if(!is_null($this->calobject->__get('X-WR-CALNAME'))){ + return $this->calobject->__get('X-WR-CALNAME'); + } + return null; + } +} diff --git a/apps/calendar/lib/object.php b/apps/calendar/lib/object.php index df866bd3c5f..8020d7c2e53 100644 --- a/apps/calendar/lib/object.php +++ b/apps/calendar/lib/object.php @@ -1,10 +1,31 @@ <?php /** * Copyright (c) 2011 Jakob Sack <mail@jakobsack.de> + * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * Copyright (c) 2012 Georg Ehrke <ownclouddev at georgswebsite dot de> * This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. */ + /** + * + * The following SQL statement is just a help for developers and will not be + * executed! + * + * CREATE TABLE calendar_objects ( + * id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + * calendarid INTEGER UNSIGNED NOT NULL, + * objecttype VARCHAR(40) NOT NULL, + * startdate DATETIME, + * enddate DATETIME, + * repeating INT(1), + * summary VARCHAR(255), + * calendardata TEXT, + * uri VARCHAR(100), + * lastmodified INT(11) + * ); + * + */ /** * This class manages our calendar objects @@ -108,7 +129,7 @@ class OC_Calendar_Object{ $object_id = OCP\DB::insertid('*PREFIX*calendar_objects'); OC_Calendar_Calendar::touchCalendar($id); - + OCP\Util::emitHook('OC_Calendar', 'addEvent', $object_id); return $object_id; } @@ -128,7 +149,7 @@ class OC_Calendar_Object{ $object_id = OCP\DB::insertid('*PREFIX*calendar_objects'); OC_Calendar_Calendar::touchCalendar($id); - + OCP\Util::emitHook('OC_Calendar', 'addEvent', $object_id); return $object_id; } @@ -149,6 +170,7 @@ class OC_Calendar_Object{ $stmt->execute(array($type,$startdate,$enddate,$repeating,$summary,$data,time(),$id)); OC_Calendar_Calendar::touchCalendar($oldobject['calendarid']); + OCP\Util::emitHook('OC_Calendar', 'editEvent', $id); return true; } @@ -170,6 +192,7 @@ class OC_Calendar_Object{ $stmt->execute(array($type,$startdate,$enddate,$repeating,$summary,$data,time(),$oldobject['id'])); OC_Calendar_Calendar::touchCalendar($oldobject['calendarid']); + OCP\Util::emitHook('OC_Calendar', 'editEvent', $oldobject['id']); return true; } @@ -184,6 +207,7 @@ class OC_Calendar_Object{ $stmt = OCP\DB::prepare( 'DELETE FROM `*PREFIX*calendar_objects` WHERE `id` = ?' ); $stmt->execute(array($id)); OC_Calendar_Calendar::touchCalendar($oldobject['calendarid']); + OCP\Util::emitHook('OC_Calendar', 'deleteEvent', $id); return true; } @@ -195,9 +219,11 @@ class OC_Calendar_Object{ * @return boolean */ public static function deleteFromDAVData($cid,$uri){ + $oldobject = self::findWhereDAVDataIs($cid, $uri); $stmt = OCP\DB::prepare( 'DELETE FROM `*PREFIX*calendar_objects` WHERE `calendarid`= ? AND `uri`=?' ); $stmt->execute(array($cid,$uri)); OC_Calendar_Calendar::touchCalendar($cid); + OCP\Util::emitHook('OC_Calendar', 'deleteEvent', $oldobject['id']); return true; } @@ -207,6 +233,7 @@ class OC_Calendar_Object{ $stmt->execute(array($calendarid,$id)); OC_Calendar_Calendar::touchCalendar($id); + OCP\Util::emitHook('OC_Calendar', 'moveEvent', $id); return true; } @@ -302,12 +329,16 @@ class OC_Calendar_Object{ * This function creates a date string that can be used by MDB2. * Furthermore it converts the time to UTC. */ - protected static function getUTCforMDB($datetime){ + public static function getUTCforMDB($datetime){ return date('Y-m-d H:i', $datetime->format('U') - $datetime->getOffset()); } - - public static function getDTEndFromVEvent($vevent) - { + + /** + * @brief returns the DTEND of an $vevent object + * @param object $vevent vevent object + * @return object + */ + public static function getDTEndFromVEvent($vevent){ if ($vevent->DTEND) { $dtend = $vevent->DTEND; }else{ @@ -331,9 +362,12 @@ class OC_Calendar_Object{ } return $dtend; } - - public static function getRepeatOptions($l10n) - { + + /** + * @brief returns the options for the repeat rule of an repeating event + * @return array - valid inputs for the repeat rule of an repeating event + */ + public static function getRepeatOptions($l10n){ return array( 'doesnotrepeat' => $l10n->t('Does not repeat'), 'daily' => $l10n->t('Daily'), @@ -344,26 +378,35 @@ class OC_Calendar_Object{ 'yearly' => $l10n->t('Yearly') ); } - - public static function getEndOptions($l10n) - { + + /** + * @brief returns the options for the end of an repeating event + * @return array - valid inputs for the end of an repeating events + */ + public static function getEndOptions($l10n){ return array( 'never' => $l10n->t('never'), 'count' => $l10n->t('by occurrences'), 'date' => $l10n->t('by date') ); } - - public static function getMonthOptions($l10n) - { + + /** + * @brief returns the options for an monthly repeating event + * @return array - valid inputs for monthly repeating events + */ + public static function getMonthOptions($l10n){ return array( 'monthday' => $l10n->t('by monthday'), 'weekday' => $l10n->t('by weekday') ); } - - public static function getWeeklyOptions($l10n) - { + + /** + * @brief returns the options for an weekly repeating event + * @return array - valid inputs for weekly repeating events + */ + public static function getWeeklyOptions($l10n){ return array( 'MO' => $l10n->t('Monday'), 'TU' => $l10n->t('Tuesday'), @@ -374,9 +417,12 @@ class OC_Calendar_Object{ 'SU' => $l10n->t('Sunday') ); } - - public static function getWeekofMonth($l10n) - { + + /** + * @brief returns the options for an monthly repeating event which occurs on specific weeks of the month + * @return array - valid inputs for monthly repeating events + */ + public static function getWeekofMonth($l10n){ return array( 'auto' => $l10n->t('events week of month'), '1' => $l10n->t('first'), @@ -387,7 +433,11 @@ class OC_Calendar_Object{ '-1' => $l10n->t('last') ); } - + + /** + * @brief returns the options for an yearly repeating event which occurs on specific days of the year + * @return array - valid inputs for yearly repeating events + */ public static function getByYearDayOptions(){ $return = array(); foreach(range(1,366) as $num){ @@ -395,7 +445,11 @@ class OC_Calendar_Object{ } return $return; } - + + /** + * @brief returns the options for an yearly or monthly repeating event which occurs on specific days of the month + * @return array - valid inputs for yearly or monthly repeating events + */ public static function getByMonthDayOptions(){ $return = array(); foreach(range(1,31) as $num){ @@ -403,7 +457,11 @@ class OC_Calendar_Object{ } return $return; } - + + /** + * @brief returns the options for an yearly repeating event which occurs on specific month of the year + * @return array - valid inputs for yearly repeating events + */ public static function getByMonthOptions($l10n){ return array( '1' => $l10n->t('January'), @@ -420,7 +478,11 @@ class OC_Calendar_Object{ '12' => $l10n->t('December') ); } - + + /** + * @brief returns the options for an yearly repeating event + * @return array - valid inputs for yearly repeating events + */ public static function getYearOptions($l10n){ return array( 'bydate' => $l10n->t('by events date'), @@ -429,13 +491,21 @@ class OC_Calendar_Object{ 'bydaymonth' => $l10n->t('by day and month') ); } - + + /** + * @brief returns the options for an yearly repeating event which occurs on specific week numbers of the year + * @return array - valid inputs for yearly repeating events + */ public static function getByWeekNoOptions(){ return range(1, 52); } - - public static function validateRequest($request) - { + + /** + * @brief validates a request + * @param array $request + * @return mixed (array / boolean) + */ + public static function validateRequest($request){ $errnum = 0; $errarr = array('title'=>'false', 'cal'=>'false', 'from'=>'false', 'fromtime'=>'false', 'to'=>'false', 'totime'=>'false', 'endbeforestart'=>'false'); if($request['title'] == ''){ @@ -582,17 +652,24 @@ class OC_Calendar_Object{ } return false; } - - protected static function checkTime($time) - { + + /** + * @brief validates time + * @param string $time + * @return boolean + */ + protected static function checkTime($time){ list($hours, $minutes) = explode(':', $time); return empty($time) || $hours < 0 || $hours > 24 || $minutes < 0 || $minutes > 60; } - - public static function createVCalendarFromRequest($request) - { + + /** + * @brief creates an VCalendar Object from the request data + * @param array $request + * @return object created $vcalendar + */ public static function createVCalendarFromRequest($request){ $vcalendar = new OC_VObject('VCALENDAR'); $vcalendar->add('PRODID', 'ownCloud Calendar'); $vcalendar->add('VERSION', '2.0'); @@ -605,9 +682,14 @@ class OC_Calendar_Object{ $vevent->setUID(); return self::updateVCalendarFromRequest($request, $vcalendar); } - - public static function updateVCalendarFromRequest($request, $vcalendar) - { + + /** + * @brief updates an VCalendar Object from the request data + * @param array $request + * @param object $vcalendar + * @return object updated $vcalendar + */ + public static function updateVCalendarFromRequest($request, $vcalendar){ $title = $request["title"]; $location = $request["location"]; $categories = $request["categories"]; @@ -774,7 +856,7 @@ class OC_Calendar_Object{ $vevent->setDateTime('DTSTART', $start, Sabre_VObject_Property_DateTime::DATE); $vevent->setDateTime('DTEND', $end, Sabre_VObject_Property_DateTime::DATE); }else{ - $timezone = OCP\Config::getUserValue(OCP\USER::getUser(), 'calendar', 'timezone', date_default_timezone_get()); + $timezone = OC_Calendar_App::getTimezone(); $timezone = new DateTimeZone($timezone); $start = new DateTime($from.' '.$fromtime, $timezone); $end = new DateTime($to.' '.$totime, $timezone); @@ -793,15 +875,63 @@ class OC_Calendar_Object{ return $vcalendar; } - + + /** + * @brief returns the owner of an object + * @param integer $id + * @return string + */ public static function getowner($id){ $event = self::find($id); $cal = OC_Calendar_Calendar::find($event['calendarid']); return $cal['userid']; } + /** + * @brief returns the calendarid of an object + * @param integer $id + * @return integer + */ public static function getCalendarid($id){ $event = self::find($id); return $event['calendarid']; } + + /** + * @brief checks if an object is repeating + * @param integer $id + * @return boolean + */ + public static function isrepeating($id){ + $event = self::find($id); + return ($event['repeating'] == 1)?true:false; + } + + /** + * @brief converts the start_dt and end_dt to a new timezone + * @param object $dtstart + * @param object $dtend + * @param boolean $allday + * @param string $tz + * @return array + */ + public static function generateStartEndDate($dtstart, $dtend, $allday, $tz){ + $start_dt = $dtstart->getDateTime(); + $end_dt = $dtend->getDateTime(); + $return = array(); + if($allday){ + $return['start'] = $start_dt->format('Y-m-d'); + $end_dt->modify('-1 minute'); + while($start_dt >= $end_dt){ + $end_dt->modify('+1 day'); + } + $return['end'] = $end_dt->format('Y-m-d'); + }else{ + $start_dt->setTimezone(new DateTimeZone($tz)); + $end_dt->setTimezone(new DateTimeZone($tz)); + $return['start'] = $start_dt->format('Y-m-d H:i:s'); + $return['end'] = $end_dt->format('Y-m-d H:i:s'); + } + return $return; + } } diff --git a/apps/calendar/lib/repeat.php b/apps/calendar/lib/repeat.php new file mode 100644 index 00000000000..b9fbee8fe0a --- /dev/null +++ b/apps/calendar/lib/repeat.php @@ -0,0 +1,204 @@ +<?php +/** + * Copyright (c) 2012 Georg Ehrke <ownclouddev@georgswebsite.de> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +/** + * This class manages the caching of repeating events + * Events will be cached for the current year ± 5 years + */ +class OC_Calendar_Repeat{ + /** + * @brief returns the cache of an event + * @param (int) $id - id of the event + * @return (array) + */ + public static function get($id){ + $stmt = OCP\DB::prepare('SELECT * FROM *PREFIX*calendar_repeat WHERE eventid = ?'); + $result = $stmt->execute(array($id)); + $return = array(); + while($row = $result->fetchRow()){ + $return[] = $row; + } + return $return; + } + /** + * @brief returns the cache of an event in a specific peroid + * @param (int) $id - id of the event + * @param (DateTime) $from - start for period in UTC + * @param (DateTime) $until - end for period in UTC + * @return (array) + */ + public static function get_inperiod($id, $from, $until){ + $stmt = OCP\DB::prepare( 'SELECT * FROM *PREFIX*calendar_repeat WHERE eventid = ?' + .' AND ((startdate >= ? AND startdate <= ?)' + .' OR (enddate >= ? AND enddate <= ?))'); + $result = $stmt->execute(array($id, + OC_Calendar_Object::getUTCforMDB($from), OC_Calendar_Object::getUTCforMDB($until), + OC_Calendar_Object::getUTCforMDB($from), OC_Calendar_Object::getUTCforMDB($until))); + $return = array(); + while($row = $result->fetchRow()){ + $return[] = $row; + } + return $return; + } + /** + * @brief returns the cache of all repeating events of a calendar + * @param (int) $id - id of the calendar + * @return (array) + */ + public static function getCalendar($id){ + $stmt = OCP\DB::prepare('SELECT * FROM *PREFIX*calendar_repeat WHERE calid = ?'); + $result = $stmt->execute(array($id)); + $return = array(); + while($row = $result->fetchRow()){ + $return[] = $row; + } + return $return; + } + /** + * @brief returns the cache of all repeating events of a calendar in a specific period + * @param (int) $id - id of the event + * @param (string) $from - start for period in UTC + * @param (string) $until - end for period in UTC + * @return (array) + */ + public static function getCalendar_inperiod($id, $from, $until){ + $stmt = OCP\DB::prepare( 'SELECT * FROM *PREFIX*calendar_repeat WHERE calid = ?' + .' AND ((startdate >= ? AND startdate <= ?)' + .' OR (enddate >= ? AND enddate <= ?))'); + $result = $stmt->execute(array($id, + $from, $until, + $from, $until)); + $return = array(); + while($row = $result->fetchRow()){ + $return[] = $row; + } + return $return; + } + /** + * @brief generates the cache the first time + * @param (int) id - id of the event + * @return (bool) + */ + public static function generate($id){ + $event = OC_Calendar_Object::find($id); + if($event['repeating'] == 0){ + return false; + } + $object = OC_VObject::parse($event['calendardata']); + $start = new DateTime('01-01-' . date('Y') . ' 00:00:00', new DateTimeZone('UTC')); + $start->modify('-5 years'); + $end = new DateTime('31-12-' . date('Y') . ' 23:59:59', new DateTimeZone('UTC')); + $end->modify('+5 years'); + $object->expand($start, $end); + foreach($object->getComponents() as $vevent){ + if(!($vevent instanceof Sabre_VObject_Component_VEvent)){ + continue; + } + $startenddate = OC_Calendar_Object::generateStartEndDate($vevent->DTSTART, OC_Calendar_Object::getDTEndFromVEvent($vevent), ($vevent->DTSTART->getDateType() == Sabre_VObject_Element_DateTime::DATE)?true:false, 'UTC'); + $stmt = OCP\DB::prepare('INSERT INTO *PREFIX*calendar_repeat (eventid,calid,startdate,enddate) VALUES(?,?,?,?)'); + $stmt->execute(array($id,OC_Calendar_Object::getCalendarid($id),$startenddate['start'],$startenddate['end'])); + } + return true; + } + /** + * @brief generates the cache the first time for all repeating event of an calendar + * @param (int) id - id of the calendar + * @return (bool) + */ + public static function generateCalendar($id){ + $allobjects = OC_Calendar_Object::all($id); + foreach($allobjects as $event){ + self::generate($event['id']); + } + return true; + } + /** + * @brief updates an event that is already cached + * @param (int) id - id of the event + * @return (bool) + */ + public static function update($id){ + self::clean($id); + self::generate($id); + return true; + } + /** + * @brief updates all repating events of a calendar that are already cached + * @param (int) id - id of the calendar + * @return (bool) + */ + public static function updateCalendar($id){ + self::cleanCalendar($id); + self::generateCalendar($id); + return true; + } + /** + * @brief checks if an event is already cached + * @param (int) id - id of the event + * @return (bool) + */ + public static function is_cached($id){ + if(count(self::get($id)) != 0){ + return true; + }else{ + return false; + } + } + /** + * @brief checks if an event is already cached in a specific period + * @param (int) id - id of the event + * @param (DateTime) $from - start for period in UTC + * @param (DateTime) $until - end for period in UTC + * @return (bool) + */ + public static function is_cached_inperiod($id, $start, $end){ + if(count(self::get_inperiod($id, $start, $end)) != 0){ + return true; + }else{ + return false; + } + + } + /** + * @brief checks if a whole calendar is already cached + * @param (int) id - id of the calendar + * @return (bool) + */ + public static function is_calendar_cached($id){ + $cachedevents = count(self::getCalendar($id)); + $repeatingevents = 0; + $allevents = OC_Calendar_Object::all($id); + foreach($allevents as $event){ + if($event['repeating'] === 1){ + $repeatingevents++; + } + } + if($cachedevents < $repeatingevents){ + return false; + }else{ + return true; + } + } + /** + * @brief removes the cache of an event + * @param (int) id - id of the event + * @return (bool) + */ + public static function clean($id){ + $stmt = OCP\DB::prepare('DELETE FROM *PREFIX*calendar_repeat WHERE eventid = ?'); + $stmt->execute(array($id)); + } + /** + * @brief removes the cache of all events of a calendar + * @param (int) id - id of the calendar + * @return (bool) + */ + public static function cleanCalendar($id){ + $stmt = OCP\DB::prepare('DELETE FROM *PREFIX*calendar_repeat WHERE calid = ?'); + $stmt->execute(array($id)); + } +}
\ No newline at end of file diff --git a/apps/calendar/lib/connector_sabre.php b/apps/calendar/lib/sabre/backend.php index 8eea06da7e2..ac3b26ceb33 100644 --- a/apps/calendar/lib/connector_sabre.php +++ b/apps/calendar/lib/sabre/backend.php @@ -212,6 +212,10 @@ class OC_Connector_Sabre_CalDAV extends Sabre_CalDAV_Backend_Abstract { * @return void */ public function deleteCalendar($calendarId) { + if(preg_match( '=iCal/[1-4]?.*Mac OS X/10.[1-6](.[0-9])?=', $_SERVER['HTTP_USER_AGENT'] )){ + throw new Sabre_DAV_Exception_Forbidden("Action is not possible with OSX 10.6.x", 403); + } + OC_Calendar_Calendar::deleteCalendar($calendarId); } diff --git a/apps/calendar/lib/sabre/calendar.php b/apps/calendar/lib/sabre/calendar.php new file mode 100644 index 00000000000..179be1b2813 --- /dev/null +++ b/apps/calendar/lib/sabre/calendar.php @@ -0,0 +1,127 @@ +<?php +/** + * ownCloud - OC_Connector_Sabre_Sabre_CalDAV_Calendar + * + * @author Thomas Tanghus + * @copyright 2012 Thomas Tanghus (thomas@tanghus.net) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +/** + * This class overrides Sabre_CalDAV_Calendar::getACL() to return read/write + * permissions based on user and shared state and it overrides + * Sabre_CalDAV_Calendar::getChild() and Sabre_CalDAV_Calendar::getChildren() + * to instantiate OC_Connector_Sabre_CalDAV_CalendarObjects. +*/ +class OC_Connector_Sabre_CalDAV_Calendar extends Sabre_CalDAV_Calendar { + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + public function getACL() { + + $readprincipal = $this->getOwner(); + $writeprincipal = $this->getOwner(); + $uid = OC_Calendar_Calendar::extractUserID($this->getOwner()); + + if($uid != OCP\USER::getUser()) { + $sharedCalendar = OCP\Share::getItemSharedWithBySource('calendar', $this->$calendarInfo['id']); + if ($sharedCalendar && ($sharedCalendar['permissions'] & OCP\Share::PERMISSION_READ)) { + $readprincipal = 'principals/' . OCP\USER::getUser(); + } + if ($sharedCalendar && ($sharedCalendar['permissions'] & OCP\Share::PERMISSION_UPDATE)) { + $writeprincipal = 'principals/' . OCP\USER::getUser(); + } + } + + return array( + array( + 'privilege' => '{DAV:}read', + 'principal' => $readprincipal, + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $writeprincipal, + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $readprincipal . '/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $writeprincipal . '/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $readprincipal . '/calendar-proxy-read', + 'protected' => true, + ), + array( + 'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}read-free-busy', + 'principal' => '{DAV:}authenticated', + 'protected' => true, + ), + + ); + + } + + /** + * Returns a calendar object + * + * The contained calendar objects are for example Events or Todo's. + * + * @param string $name + * @return Sabre_DAV_ICalendarObject + */ + public function getChild($name) { + + $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name); + if (!$obj) throw new Sabre_DAV_Exception_NotFound('Calendar object not found'); + return new OC_Connector_Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj); + + } + + /** + * Returns the full list of calendar objects + * + * @return array + */ + public function getChildren() { + + $objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']); + $children = array(); + foreach($objs as $obj) { + $children[] = new OC_Connector_Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj); + } + return $children; + + } + +}
\ No newline at end of file diff --git a/apps/calendar/lib/sabre/calendarroot.php b/apps/calendar/lib/sabre/calendarroot.php new file mode 100644 index 00000000000..e09731c95b7 --- /dev/null +++ b/apps/calendar/lib/sabre/calendarroot.php @@ -0,0 +1,45 @@ +<?php +/** + * ownCloud - OC_Connector_Sabre_CalDAV_CalendarRoot + * + * @author Thomas Tanghus + * @copyright 2012 Thomas Tanghus (thomas@tanghus.net) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +/** + * This class overrides Sabre_CalDAV_CalendarRootNode::getChildForPrincipal() + * to instantiate OC_Connector_Sabre_CalDAV_UserCalendars. +*/ +class OC_Connector_Sabre_CalDAV_CalendarRoot extends Sabre_CalDAV_CalendarRootNode { + + /** + * This method returns a node for a principal. + * + * The passed array contains principal information, and is guaranteed to + * at least contain a uri item. Other properties may or may not be + * supplied by the authentication backend. + * + * @param array $principal + * @return Sabre_DAV_INode + */ + public function getChildForPrincipal(array $principal) { + + return new OC_Connector_Sabre_CalDAV_UserCalendars($this->principalBackend, $this->caldavBackend, $principal['uri']); + + } + +}
\ No newline at end of file diff --git a/apps/calendar/lib/sabre/object.php b/apps/calendar/lib/sabre/object.php new file mode 100644 index 00000000000..25954e6ee55 --- /dev/null +++ b/apps/calendar/lib/sabre/object.php @@ -0,0 +1,87 @@ +<?php +/** + * ownCloud - OC_Connector_Sabre_CalDAV_CalendarObject + * + * @author Thomas Tanghus + * @copyright 2012 Thomas Tanghus (thomas@tanghus.net) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +/** + * This class overrides Sabre_CalDAV_CalendarObject::getACL() + * to return read/write permissions based on user and shared state. +*/ +class OC_Connector_Sabre_CalDAV_CalendarObject extends Sabre_CalDAV_CalendarObject { + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + public function getACL() { + + $readprincipal = $this->getOwner(); + $writeprincipal = $this->getOwner(); + $uid = OC_Calendar_Calendar::extractUserID($this->getOwner()); + + if($uid != OCP\USER::getUser()) { + $sharedAddressbook = OCP\Share::getItemSharedWithBySource('calendar', $this->$calendarInfo['id']); + if ($sharedAddressbook && ($sharedAddressbook['permissions'] & OCP\Share::PERMISSION_READ)) { + $readprincipal = 'principals/' . OCP\USER::getUser(); + } + if ($sharedAddressbook && ($sharedAddressbook['permissions'] & OCP\Share::PERMISSION_UPDATE)) { + $writeprincipal = 'principals/' . OCP\USER::getUser(); + } + } + + return array( + array( + 'privilege' => '{DAV:}read', + 'principal' => $readprincipal, + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $writeprincipal, + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $readprincipal . '/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $writeprincipal . '/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $readprincipal . '/calendar-proxy-read', + 'protected' => true, + ), + ); + + } + +}
\ No newline at end of file diff --git a/apps/calendar/lib/sabre/usercalendars.php b/apps/calendar/lib/sabre/usercalendars.php new file mode 100644 index 00000000000..919f6b27e18 --- /dev/null +++ b/apps/calendar/lib/sabre/usercalendars.php @@ -0,0 +1,46 @@ +<?php +/** + * ownCloud - OC_Connector_Sabre_CalDAV_UserCalendars + * + * @author Thomas Tanghus + * @copyright 2012 Thomas Tanghus (thomas@tanghus.net) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +/** + * This class overrides Sabre_CalDAV_UserCalendars::getChildren() + * to instantiate OC_Connector_Sabre_CalDAV_Calendars. +*/ +class OC_Connector_Sabre_CalDAV_UserCalendars extends Sabre_CalDAV_UserCalendars { + + /** + * Returns a list of calendars + * + * @return array + */ + public function getChildren() { + + $calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']); + $objs = array(); + foreach($calendars as $calendar) { + $objs[] = new OC_Connector_Sabre_CalDAV_Calendar($this->principalBackend, $this->caldavBackend, $calendar); + } + $objs[] = new Sabre_CalDAV_Schedule_Outbox($this->principalInfo['uri']); + return $objs; + + } + +}
\ No newline at end of file diff --git a/apps/calendar/lib/search.php b/apps/calendar/lib/search.php index 8b8b0e0c7ee..551489672b9 100644 --- a/apps/calendar/lib/search.php +++ b/apps/calendar/lib/search.php @@ -1,7 +1,7 @@ <?php class OC_Search_Provider_Calendar extends OC_Search_Provider{ function search($query){ - $calendars = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser(), 1); + $calendars = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser(), true); if(count($calendars)==0 || !OCP\App::isEnabled('calendar')){ //return false; } @@ -12,7 +12,7 @@ class OC_Search_Provider_Calendar extends OC_Search_Provider{ }else{ $searchquery[] = $query; } - $user_timezone = OCP\Config::getUserValue(OCP\USER::getUser(), 'calendar', 'timezone', date_default_timezone_get()); + $user_timezone = OC_Calendar_App::getTimezone(); $l = new OC_l10n('calendar'); foreach($calendars as $calendar){ $objects = OC_Calendar_Object::all($calendar['id']); diff --git a/apps/calendar/lib/share.php b/apps/calendar/lib/share.php index aba27221653..23892157fef 100644 --- a/apps/calendar/lib/share.php +++ b/apps/calendar/lib/share.php @@ -5,39 +5,36 @@ * later. * See the COPYING-README file. */ -/* +/** * This class manages shared calendars */ class OC_Calendar_Share{ const CALENDAR = 'calendar'; const EVENT = 'event'; - /* + /** * @brief: returns informations about all calendar or events which users are sharing with the user - userid - * @param: (string) $userid - id of the user - * @param: (string) $type - use const self::CALENDAR or self::EVENT - * @return: (array) $return - information about calendars + * @param: string $userid - id of the user + * @param: string $type - use const self::CALENDAR or self::EVENT + * @return: array $return - information about calendars */ public static function allSharedwithuser($userid, $type, $active=null, $permission=null){ - $group_where = self::group_sql(OC_Group::getUserGroups($userid)); - $permission_where = self::permission_sql($permission); - if($type == self::CALENDAR){ - $active_where = self::active_sql($active); - }else{ - $active_where = ''; - } - $stmt = OCP\DB::prepare("SELECT * FROM `*PREFIX*calendar_share_" . $type . "` WHERE ((`share` = ? AND `sharetype` = 'user') " . $group_where . ") AND `owner` <> ? " . $permission_where . " " . $active_where); - $result = $stmt->execute(array($userid, $userid)); - $return = array(); - while( $row = $result->fetchRow()){ - $return[] = $row; + $format = OC_Share_Backend_Calendar::FORMAT_CALENDAR; + if ($type == self::EVENT) { + $format = OC_Share_Backend_Event::FORMAT_EVENT; } + $return = OCP\Share::getItemsSharedWith($type, + $format, + array( + 'active' => $active, + 'permissions' => $permission, + )); return $return; } - /* + /** * @brief: returns all users a calendar / event is shared with - * @param: (int) id - id of the calendar / event - * @param: (string) $type - use const self::CALENDAR or self::EVENT - * @return: (array) $users - information about users a calendar / event is shared with + * @param: integer id - id of the calendar / event + * @param: string $type - use const self::CALENDAR or self::EVENT + * @return: array $users - information about users a calendar / event is shared with */ public static function allUsersSharedwith($id, $type){ $stmt = OCP\DB::prepare('SELECT * FROM `*PREFIX*calendar_share_' . $type . '` WHERE `' . $type . 'id` = ? ORDER BY `share`'); @@ -48,13 +45,13 @@ class OC_Calendar_Share{ } return $users; } - /* + /** * @brief: shares a calendar / event - * @param: (string) $owner - userid of the owner - * @param: (string) $share - userid (if $sharetype == user) / groupid (if $sharetype == group) / token (if $sharetype == public) - * @param: (string) $sharetype - type of sharing (can be: user/group/public) - * @param: (string) $id - id of the calendar / event - * @param: (string) $type - use const self::CALENDAR or self::EVENT + * @param: string $owner - userid of the owner + * @param: string $share - userid (if $sharetype == user) / groupid (if $sharetype == group) / token (if $sharetype == public) + * @param: string $sharetype - type of sharing (can be: user/group/public) + * @param: string $id - id of the calendar / event + * @param: string $type - use const self::CALENDAR or self::EVENT * @return (mixed) - token (if $sharetype == public) / bool (if $sharetype != public) */ public static function share($owner, $share, $sharetype, $id, $type){ @@ -80,14 +77,14 @@ class OC_Calendar_Share{ return true; } } - /* + /** * @brief: stops sharing a calendar / event - * @param: (string) $owner - userid of the owner - * @param: (string) $share - userid (if $sharetype == user) / groupid (if $sharetype == group) / token (if $sharetype == public) - * @param: (string) $sharetype - type of sharing (can be: user/group/public) - * @param: (string) $id - id of the calendar / event - * @param: (string) $type - use const self::CALENDAR or self::EVENT - * @return (bool) + * @param: string $owner - userid of the owner + * @param: string $share - userid (if $sharetype == user) / groupid (if $sharetype == group) / token (if $sharetype == public) + * @param: string $sharetype - type of sharing (can be: user/group/public) + * @param: string $id - id of the calendar / event + * @param: string $type - use const self::CALENDAR or self::EVENT + * @return boolean */ public static function unshare($owner, $share, $sharetype, $id, $type){ $stmt = OCP\DB::prepare('DELETE FROM `*PREFIX*calendar_share_' . $type . '` WHERE `owner` = ? ' . (($sharetype != 'public')?'AND `share` = ?':'') . ' AND `sharetype` = ? AND `' . $type . 'id` = ?'); @@ -98,14 +95,14 @@ class OC_Calendar_Share{ } return true; } - /* + /** * @brief: changes the permission for a calendar / event - * @param: (string) $share - userid (if $sharetype == user) / groupid (if $sharetype == group) / token (if $sharetype == public) - * @param: (string) $sharetype - type of sharing (can be: user/group/public) - * @param: (string) $id - id of the calendar / event - * @param: (int) $permission - permission of user the calendar / event is shared with (if $sharetype == public then $permission = 0) - * @param: (string) $type - use const self::CALENDAR or self::EVENT - * @return (bool) + * @param: string $share - userid (if $sharetype == user) / groupid (if $sharetype == group) / token (if $sharetype == public) + * @param: string $sharetype - type of sharing (can be: user/group/public) + * @param: string $id - id of the calendar / event + * @param: integer $permission - permission of user the calendar / event is shared with (if $sharetype == public then $permission = 0) + * @param: string $type - use const self::CALENDAR or self::EVENT + * @return boolean */ public static function changepermission($share, $sharetype, $id, $permission, $type){ if($sharetype == 'public' && $permission == 1){ @@ -115,9 +112,9 @@ class OC_Calendar_Share{ $stmt->execute(array($permission, $share, $sharetype, $id)); return true; } - /* + /** * @brief: generates a token for public calendars / events - * @return: (string) $token + * @return: string $token */ private static function generate_token($id, $type){ $uniqid = uniqid(); @@ -138,14 +135,14 @@ class OC_Calendar_Share{ $token = md5($string); return substr($token, rand(0,16), 15); } - /* + /** * @brief: checks if it is already shared - * @param: (string) $owner - userid of the owner - * @param: (string) $share - userid (if $sharetype == user) / groupid (if $sharetype == group) / token (if $sharetype == public) - * @param: (string) $sharetype - type of sharing (can be: user/group/public) - * @param: (string) $id - id of the calendar / event - * @param: (string) $type - use const self::CALENDAR or self::EVENT - * @return (bool) + * @param: string $owner - userid of the owner + * @param: string $share - userid (if $sharetype == user) / groupid (if $sharetype == group) / token (if $sharetype == public) + * @param: string $sharetype - type of sharing (can be: user/group/public) + * @param: string $id - id of the calendar / event + * @param: string $type - use const self::CALENDAR or self::EVENT + * @return boolean */ public static function is_already_shared($owner, $share, $sharetype, $id, $type){ $stmt = OCP\DB::prepare('SELECT * FROM `*PREFIX*calendar_share_' . $type . '` WHERE `owner` = ? AND `share` = ? AND `sharetype` = ? AND `' . $type . 'id` = ?'); @@ -181,12 +178,12 @@ class OC_Calendar_Share{ } return $active_where; } - /* + /** * @brief: checks the permission for editing an event - * @param: (string) $share - userid (if $sharetype == user) / groupid (if $sharetype == group) / token (if $sharetype == public) - * @param: (string) $id - id of the calendar / event - * @param: (string) $type - use const self::CALENDAR or self::EVENT - * @return (bool) + * @param: string $share - userid (if $sharetype == user) / groupid (if $sharetype == group) / token (if $sharetype == public) + * @param: string $id - id of the calendar / event + * @param: string $type - use const self::CALENDAR or self::EVENT + * @return boolean */ public static function is_editing_allowed($share, $id, $type){ $group_where = self::group_sql(OC_Group::getUserGroups($share)); @@ -202,16 +199,16 @@ class OC_Calendar_Share{ } return false; } - /* + /** * @brief: checks the access of - * @param: (string) $share - userid (if $sharetype == user) / groupid (if $sharetype == group) / token (if $sharetype == public) - * @param: (string) $id - id of the calendar / event - * @param: (string) $type - use const self::CALENDAR or self::EVENT - * @return (bool) + * @param: string $share - userid (if $sharetype == user) / groupid (if $sharetype == group) / token (if $sharetype == public) + * @param: string $id - id of the calendar / event + * @param: string $type - use const self::CALENDAR or self::EVENT + * @return boolean */ public static function check_access($share, $id, $type){ $group_where = self::group_sql(OC_Group::getUserGroups($share)); - $stmt = OCP\DB::prepare('SELECT * FROM `*PREFIX*calendar_share`_' . $type . '` WHERE (`' . $type . 'id` = ? AND (`share` = ? AND `sharetype` = \'user\') ' . $group_where . ')'); + $stmt = OCP\DB::prepare('SELECT * FROM `*PREFIX*calendar_share_' . $type . '` WHERE (`' . $type . 'id` = ? AND (`share` = ? AND `sharetype` = \'user\') ' . $group_where . ')'); $result = $stmt->execute(array($id,$share)); $rows = $result->numRows(); if($rows > 0){ @@ -223,9 +220,9 @@ class OC_Calendar_Share{ return false; } } - /* + /** * @brief: returns the calendardata of an event or a calendar - * @param: (string) $token - token which should be searched + * @param: string $token - token which should be searched * @return: mixed - bool if false, array with type and id if true */ public static function getElementByToken($token){ @@ -248,19 +245,19 @@ class OC_Calendar_Share{ return $return; } - /* + /** * @brief sets the active status of the calendar - * @param (string) $ + * @param string */ public static function set_active($share, $id, $active){ $stmt = OCP\DB::prepare("UPDATE `*PREFIX*calendar_share_calendar` SET `active` = ? WHERE `share` = ? AND `sharetype` = 'user' AND `calendarid` = ?"); $stmt->execute(array($active, $share, $id)); } - /* - * @brief delete all shared calendars / events after a user was deleted - * @param (string) $userid - * @return (bool) + /** + * @brief deletes all shared calendars / events after a user was deleted + * @param string $userid + * @return boolean */ public static function post_userdelete($userid){ $stmt = OCP\DB::prepare('DELETE FROM `*PREFIX*calendar_share_calendar` WHERE `owner` = ?'); @@ -273,4 +270,26 @@ class OC_Calendar_Share{ $stmt->execute(array($userid)); return true; } + + /** + * @brief deletes all shared events of a calendar + * @param integer $calid + * @return boolean + */ + public static function post_caldelete($calid){ + $stmt = OCP\DB::prepare('DELETE FROM `*PREFIX*calendar_share_calendar` WHERE `calendarid` = ?'); + $stmt->execute(array($calid)); + return true; + } + + /** + * @brief deletes all shares of an event + * @param integer $eventid + * @return boolean + */ + public static function post_eventdelete($eventid){ + $stmt = OCP\DB::prepare('DELETE FROM `*PREFIX*calendar_share_event` WHERE `eventid` = ?'); + $stmt->execute(array($eventid)); + return true; + } }
\ No newline at end of file diff --git a/apps/calendar/lib/share/calendar.php b/apps/calendar/lib/share/calendar.php new file mode 100644 index 00000000000..7f498292419 --- /dev/null +++ b/apps/calendar/lib/share/calendar.php @@ -0,0 +1,111 @@ +<?php +/** +* ownCloud +* +* @author Michael Gapczynski +* @copyright 2012 Michael Gapczynski mtgap@owncloud.com +* Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +class OC_Share_Backend_Calendar implements OCP\Share_Backend_Collection { + const FORMAT_CALENDAR = 1; + + /** + * @brief Get the source of the item to be stored in the database + * @param string Item + * @param string Owner of the item + * @return mixed|array|false Source + * + * Return an array if the item is file dependent, the array needs two keys: 'item' and 'file' + * Return false if the item does not exist for the user + * + * The formatItems() function will translate the source returned back into the item + */ + public function isValidSource($itemSource, $uidOwner) { + $calendar = OC_Calendar_App::getCalendar( $itemSource ); + if ($calendar || $calendar['userid'] != $uidOwner) { + return false; + } + return true; + } + + /** + * @brief Get a unique name of the item for the specified user + * @param string Item + * @param string|false User the item is being shared with + * @param array|null List of similar item names already existing as shared items + * @return string Target name + * + * This function needs to verify that the user does not already have an item with this name. + * If it does generate a new name e.g. name_# + */ + public function generateTarget($itemSource, $shareWith, $exclude = null) { + $calendar = OC_Calendar_App::getCalendar( $itemSource ); + $user_calendars = array(); + foreach(OC_Contacts_Addressbook::all($uid) as $user_calendar) { + $user_calendars[] = $user_calendar['displayname']; + } + $name = $calendar['userid']."'s ".$calendar['displayname']; + $suffix = ''; + while (in_array($name.$suffix, $user_calendars)) { + $suffix++; + } + + return $name.$suffix; + } + + /** + * @brief Converts the shared item sources back into the item in the specified format + * @param array Shared items + * @param int Format + * @return ? + * + * The items array is a 3-dimensional array with the item_source as the first key and the share id as the second key to an array with the share info. + * The key/value pairs included in the share info depend on the function originally called: + * If called by getItem(s)Shared: id, item_type, item, item_source, share_type, share_with, permissions, stime, file_source + * If called by getItem(s)SharedWith: id, item_type, item, item_source, item_target, share_type, share_with, permissions, stime, file_source, file_target + * This function allows the backend to control the output of shared items with custom formats. + * It is only called through calls to the public getItem(s)Shared(With) functions. + */ + public function formatItems($items, $format, $parameters = null) { + $calendars = array(); + if ($format == self::FORMAT_CALENDAR) { + foreach ($items as $item) { + $calendar = OC_Calendar_App::getCalendar($item['item_source'], false); + // TODO: really check $parameters['permissions'] == 'rw'/'r' + if ($parameters['permissions'] == 'rw') { + continue; // TODO + } + $calendar['displaynamename'] = $item['item_target']; + $calendar['calendarid'] = $calendar['id']; + $calendar['owner'] = $calendar['userid']; + $calendars[] = $calendar; + } + } + return $calendars; + } + + public function getChildren($itemSource) { + $query = OCP\DB::prepare('SELECT id FROM *PREFIX*calendar_objects WHERE calendarid = ?'); + $result = $query->execute(array($itemSource)); + $sources = array(); + while ($object = $result->fetchRow()) { + $sources[] = $object['id']; + } + return $sources; + } + +}
\ No newline at end of file diff --git a/apps/calendar/lib/share/event.php b/apps/calendar/lib/share/event.php new file mode 100644 index 00000000000..5bb72ee6c98 --- /dev/null +++ b/apps/calendar/lib/share/event.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_Share_Backend_Event implements OCP\Share_Backend { + + const FORMAT_EVENT = 0; + + private static $event; + + public function isValidSource($itemSource, $uidOwner) { + self::$event = OC_Calendar_Object::find($itemSource); + if (self::$event) { + return true; + } + return false; + } + + public function generateTarget($itemSource, $shareWith, $exclude = null) { + // TODO Get default calendar and check for conflicts + return self::$event['summary']; + } + + public function formatItems($items, $format, $parameters = null) { + $events = array(); + if ($format == self::FORMAT_EVENT) { + foreach ($items as $item) { + $event = OC_Calendar_Object::find($item['item_source']); + $event['summary'] = $item['item_target']; + $events[] = $event; + } + } + return $events; + } + +} diff --git a/apps/calendar/lib/share_backend.php b/apps/calendar/lib/share_backend.php new file mode 100644 index 00000000000..f937c0d1c34 --- /dev/null +++ b/apps/calendar/lib/share_backend.php @@ -0,0 +1,44 @@ +<?php +/** +* ownCloud +* +* @author Michael Gapczynski +* @copyright 2012 Michael Gapczynski mtgap@owncloud.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +class OC_Share_Backend_Calendar extends OCP\Share_Backend { + + public function getSource($item, $uid) { + $query = OCP\DB::prepare('SELECT id FROM *PREFIX*calendar_calendars WHERE userid = ? AND displayname = ? LIMIT 1'); + return $query->execute(array($uid, $item))->fetchAll(); + } + + public function generateTarget($item, $uid) { + + } + + public function getItems($sources) { + + } + +} + +class OC_Share_Backend_Event extends OCP\Share_Backend { + +} + + +?>
\ No newline at end of file diff --git a/apps/calendar/settings.php b/apps/calendar/settings.php index a18b1ca9f42..f563518046d 100644 --- a/apps/calendar/settings.php +++ b/apps/calendar/settings.php @@ -10,7 +10,8 @@ $tmpl = new OCP\Template( 'calendar', 'settings'); $timezone=OCP\Config::getUserValue(OCP\USER::getUser(),'calendar','timezone',''); $tmpl->assign('timezone',$timezone); $tmpl->assign('timezones',DateTimeZone::listIdentifiers()); +$tmpl->assign('calendars', OC_Calendar_Calendar::allCalendars(OCP\USER::getUser()), false); OCP\Util::addscript('calendar','settings'); -return $tmpl->fetchPage(); +$tmpl->printPage();
\ No newline at end of file diff --git a/apps/calendar/share.php b/apps/calendar/share.php index 68c7d0ffae2..bffcf0b4709 100644 --- a/apps/calendar/share.php +++ b/apps/calendar/share.php @@ -1,22 +1,31 @@ <?php +/** + * Copyright (c) 2012 Georg Ehrke <ownclouddev at georgswebsite dot de> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ $token = strip_tags($_GET['t']); $shared = OC_Calendar_Share::getElementByToken($token); -$nl = "\n\r"; if($shared['type'] == OC_Calendar_Share::CALENDAR){ $calendar = OC_Calendar_App::getCalendar($shared['id'], false); - $calobjects = OC_Calendar_Object::all($shared['id']); - header('Content-Type: text/Calendar'); - header('Content-Disposition: inline; filename=' . $calendar['displayname'] . '.ics'); - foreach($calobjects as $calobject){ - echo $calobject['calendardata'] . $nl; + if(!$calendar){ + header('HTTP/1.0 404 Not Found'); + exit; } + header('Content-Type: text/Calendar'); + header('Content-Disposition: inline; filename=' . str_replace(' ', '-', $calendar['displayname']) . '.ics'); + echo OC_Calendar_Export::export($shared['id'], OC_Calendar_Export::CALENDAR); }elseif($shared['type'] == OC_Calendar_Share::EVENT){ $data = OC_Calendar_App::getEventObject($shared['id'], false); - $calendarid = $data['calendarid']; - $calendar = OC_Calendar_App::getCalendar($calendarid); + if(!$data){ + header('HTTP/1.0 404 Not Found'); + exit; + } header('Content-Type: text/Calendar'); - header('Content-Disposition: inline; filename=' . $data['summary'] . '.ics'); - echo $data['calendardata']; + header('Content-Disposition: inline; filename=' . str_replace(' ', '-', $data['summary']) . '.ics'); + echo OC_Calendar_Export::export($shared['id'], OC_Calendar_Export::EVENT); }else{ - header('Error 404: Not Found'); -}
\ No newline at end of file + header('HTTP/1.0 404 Not Found'); + exit; +} diff --git a/apps/calendar/templates/calendar.php b/apps/calendar/templates/calendar.php index 832194f0fe1..15891aafd9e 100644 --- a/apps/calendar/templates/calendar.php +++ b/apps/calendar/templates/calendar.php @@ -2,10 +2,10 @@ var defaultView = '<?php echo OCP\Config::getUserValue(OCP\USER::getUser(), 'calendar', 'currentview', 'month') ?>'; var eventSources = <?php echo json_encode($_['eventSources']) ?>; var categories = <?php echo json_encode($_['categories']); ?>; - var dayNames = <?php echo json_encode($l->tA(array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'))) ?>; - var dayNamesShort = <?php echo json_encode($l->tA(array('Sun.', 'Mon.', 'Tue.', 'Wed.', 'Thu.', 'Fri.', 'Sat.'))) ?>; - var monthNames = <?php echo json_encode($l->tA(array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'))) ?>; - var monthNamesShort = <?php echo json_encode($l->tA(array('Jan.', 'Feb.', 'Mar.', 'Apr.', 'May.', 'Jun.', 'Jul.', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.'))) ?>; + var dayNames = new Array("<?php echo $l -> t("Sunday");?>", "<?php echo $l -> t("Monday");?>", "<?php echo $l -> t("Tuesday");?>", "<?php echo $l -> t("Wednesday");?>", "<?php echo $l -> t("Thursday");?>", "<?php echo $l -> t("Friday");?>", "<?php echo $l -> t("Saturday");?>"); + var dayNamesShort = new Array("<?php echo $l -> t("Sun.");?>", "<?php echo $l -> t("Mon.");?>", "<?php echo $l -> t("Tue.");?>", "<?php echo $l -> t("Wed.");?>", "<?php echo $l -> t("Thu.");?>", "<?php echo $l -> t("Fri.");?>", "<?php echo $l -> t("Sat.");?>"); + var monthNames = new Array("<?php echo $l -> t("January");?>", "<?php echo $l -> t("February");?>", "<?php echo $l -> t("March");?>", "<?php echo $l -> t("April");?>", "<?php echo $l -> t("May");?>", "<?php echo $l -> t("June");?>", "<?php echo $l -> t("July");?>", "<?php echo $l -> t("August");?>", "<?php echo $l -> t("September");?>", "<?php echo $l -> t("October");?>", "<?php echo $l -> t("November");?>", "<?php echo $l -> t("December");?>"); + var monthNamesShort = new Array("<?php echo $l -> t("Jan.");?>", "<?php echo $l -> t("Feb.");?>", "<?php echo $l -> t("Mar.");?>", "<?php echo $l -> t("Apr.");?>", "<?php echo $l -> t("May.");?>", "<?php echo $l -> t("Jun.");?>", "<?php echo $l -> t("Jul.");?>", "<?php echo $l -> t("Aug.");?>", "<?php echo $l -> t("Sep.");?>", "<?php echo $l -> t("Oct.");?>", "<?php echo $l -> t("Nov.");?>", "<?php echo $l -> t("Dec.");?>"); var agendatime = '<?php echo ((int) OCP\Config::getUserValue(OCP\USER::getUser(), 'calendar', 'timeformat', '24') == 24 ? 'HH:mm' : 'hh:mm tt'); ?>{ - <?php echo ((int) OCP\Config::getUserValue(OCP\USER::getUser(), 'calendar', 'timeformat', '24') == 24 ? 'HH:mm' : 'hh:mm tt'); ?>}'; var defaulttime = '<?php echo ((int) OCP\Config::getUserValue(OCP\USER::getUser(), 'calendar', 'timeformat', '24') == 24 ? 'HH:mm' : 'hh:mm tt'); ?>'; var allDayText = '<?php echo addslashes($l->t('All day')) ?>'; @@ -33,37 +33,25 @@ ?> }); </script> - <div id="controls"> - <div> - <form> - <div id="view"> - <input type="button" value="<?php echo $l->t('Week');?>" id="oneweekview_radio"/> - <input type="button" value="<?php echo $l->t('Month');?>" id="onemonthview_radio"/> - <input type="button" value="<?php echo $l->t('List');?>" id="listview_radio"/> - <img id="loading" src="<?php echo OCP\Util::imagePath('core', 'loading.gif'); ?>" /> - </div> - </form> - <form> - <div id="choosecalendar"> - <input type="button" id="today_input" value="<?php echo $l->t("Today");?>"/> - <input type="button" id="choosecalendar_input" value="<?php echo $l->t("Calendars");?>" onclick="Calendar.UI.Calendar.overview();" /> - </div> - </form> - <form> - <div id="datecontrol"> - <input type="button" value=" < " id="datecontrol_left"/> - <span class="button" id="datecontrol_date"></span> - <input type="button" value=" > " id="datecontrol_right"/> - </div> - </form> - </div> - </div> <div id="notification" style="display:none;"></div> - <div id="calendar_holder"> + <div id="controls"> + <form id="view"> + <input type="button" value="<?php echo $l->t('Week');?>" id="oneweekview_radio"/> + <input type="button" value="<?php echo $l->t('Month');?>" id="onemonthview_radio"/> + <input type="button" value="<?php echo $l->t('List');?>" id="listview_radio"/> + <img id="loading" src="<?php echo OCP\Util::imagePath('core', 'loading.gif'); ?>" /> + </form> + <form id="choosecalendar"> + <!--<input type="button" id="today_input" value="<?php echo $l->t("Today");?>"/>--> + <a class="settings calendarsettings" title="<?php echo $l->t('Settings'); ?>"><img class="svg" src="<?php echo OCP\Util::imagePath('calendar', 'icon.svg'); ?>" alt="<?php echo $l->t('Settings'); ?>" /></a> + <a class="settings generalsettings" title="<?php echo $l->t('Settings'); ?>"><img class="svg" src="<?php echo OCP\Util::imagePath('core', 'actions/settings.svg'); ?>" alt="<?php echo $l->t('Settings'); ?>" /></a> + </form> + <form id="datecontrol"> + <input type="button" value=" < " id="datecontrol_left"/> + <input type="button" value="" id="datecontrol_date"/> + <input type="button" value=" > " id="datecontrol_right"/> + </form> </div> - <!-- Dialogs --> + <div id="fullcalendar"></div> <div id="dialog_holder"></div> - <div id="parsingfail_dialog" title="Parsing Fail"> - <?php echo $l->t("There was a fail, while parsing the file."); ?> - </div> - <!-- End of Dialogs --> + <div id="appsettings" class="popup topright hidden"></div>
\ No newline at end of file diff --git a/apps/calendar/templates/part.choosecalendar.php b/apps/calendar/templates/part.choosecalendar.php index 8d621cc3630..ad2f9e753f2 100644 --- a/apps/calendar/templates/part.choosecalendar.php +++ b/apps/calendar/templates/part.choosecalendar.php @@ -1,51 +1,52 @@ -<div id="choosecalendar_dialog" title="<?php echo $l->t("Choose active calendars"); ?>"> -<p><b><?php echo $l->t('Your calendars'); ?>:</b></p> -<table width="100%" style="border: 0;"> -<?php -$option_calendars = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser()); -for($i = 0; $i < count($option_calendars); $i++){ - echo "<tr>"; - $tmpl = new OCP\Template('calendar', 'part.choosecalendar.rowfields'); - $tmpl->assign('calendar', $option_calendars[$i]); - if(OC_Calendar_Share::allUsersSharedwith($option_calendars[$i]['id'], OC_Calendar_Share::CALENDAR) == array()){ - $shared = false; - }else{ - $shared = true; +<form id="calendar"> + <p><b><?php echo $l->t('Your calendars'); ?>:</b></p> + <table width="100%" style="border: 0;"> + <?php + $option_calendars = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser()); + for($i = 0; $i < count($option_calendars); $i++){ + echo "<tr>"; + $tmpl = new OCP\Template('calendar', 'part.choosecalendar.rowfields'); + $tmpl->assign('calendar', $option_calendars[$i]); + if(OC_Calendar_Share::allUsersSharedwith($option_calendars[$i]['id'], OC_Calendar_Share::CALENDAR) == array()){ + $shared = false; + }else{ + $shared = true; + } + $tmpl->assign('shared', $shared); + $tmpl->printpage(); + echo "</tr>"; } - $tmpl->assign('shared', $shared); - $tmpl->printpage(); - echo "</tr>"; -} -?> -<tr> - <td colspan="6"> - <a href="#" onclick="Calendar.UI.Calendar.newCalendar(this);"><input type="button" value="<?php echo $l->t('New Calendar') ?>"></a> - </td> -</tr> -<tr> - <td colspan="6"> - <p style="margin: 0 auto;width: 90%;"><input style="display:none;width: 90%;float: left;" type="text" id="caldav_url" onmouseover="$('#caldav_url').select();" title="<?php echo $l->t("CalDav Link"); ?>"><img id="caldav_url_close" style="height: 20px;vertical-align: middle;display: none;" src="<?php echo OCP\Util::imagePath('core', 'actions/delete.svg') ?>" alt="close" onclick="$('#caldav_url').hide();$('#caldav_url_close').hide();"/></p> - </td> -</tr> -</table><br> -<p><b><?php echo $l->t('Shared calendars'); ?>: </b></p> -<table width="100%" style="border: 0;"> -<?php -$share = OC_Calendar_Share::allSharedwithuser(OCP\USER::getUser(), OC_Calendar_Share::CALENDAR); -$count = count($share); -for($i = 0; $i < $count; $i++){ - $share[$i]['calendar'] = OC_Calendar_App::getCalendar($share[$i]['calendarid'], false, false); - echo '<tr>'; - $tmpl = new OCP\Template('calendar', 'part.choosecalendar.rowfields.shared'); - $tmpl->assign('share', $share[$i]); - $tmpl->printpage(); - echo '</tr>'; -} -?> -</table> -<?php -if($count == 0){ - echo '<p style="text-align:center;"><b>' . $l->t('No shared calendars') . '</b></p>'; -} -?> -</div>
\ No newline at end of file + ?> + <tr> + <td colspan="6"> + <a href="#" onclick="Calendar.UI.Calendar.newCalendar(this);"><input type="button" value="<?php echo $l->t('New Calendar') ?>"></a> + </td> + </tr> + <tr> + <td colspan="6"> + <p style="margin: 0 auto;width: 90%;"><input style="display:none;width: 90%;float: left;" type="text" id="caldav_url" onmouseover="$('#caldav_url').select();" title="<?php echo $l->t("CalDav Link"); ?>"><img id="caldav_url_close" style="height: 20px;vertical-align: middle;display: none;" src="<?php echo OCP\Util::imagePath('core', 'actions/delete.svg') ?>" alt="close" onclick="$('#caldav_url').hide();$('#caldav_url_close').hide();"/></p> + </td> + </tr> + </table><br> + <p><b><?php echo $l->t('Shared calendars'); ?>: </b></p> + <table width="100%" style="border: 0;"> + <?php + $share = OC_Calendar_Share::allSharedwithuser(OCP\USER::getUser(), OC_Calendar_Share::CALENDAR); + $count = count($share); + for($i = 0; $i < $count; $i++){ + $share[$i]['calendar'] = OC_Calendar_App::getCalendar($share[$i]['calendarid'], false, false); + echo '<tr>'; + $tmpl = new OCP\Template('calendar', 'part.choosecalendar.rowfields.shared'); + $tmpl->assign('share', $share[$i]); + $tmpl->printpage(); + echo '</tr>'; + } + ?> + </table> + <?php + if($count == 0){ + echo '<p style="text-align:center;"><b>' . $l->t('No shared calendars') . '</b></p>'; + } + ?> + </fieldset> +</form>
\ No newline at end of file diff --git a/apps/calendar/templates/part.choosecalendar.rowfields.php b/apps/calendar/templates/part.choosecalendar.rowfields.php index 268c3356011..64aaa797197 100644 --- a/apps/calendar/templates/part.choosecalendar.rowfields.php +++ b/apps/calendar/templates/part.choosecalendar.rowfields.php @@ -1,8 +1,21 @@ -<?php -echo '<td width="20px"><input id="active_' . $_['calendar']['id'] . '" type="checkbox" onClick="Calendar.UI.Calendar.activation(this,' . $_['calendar']['id'] . ')"' . ($_['calendar']['active'] ? ' checked="checked"' : '') . '></td>'; -echo '<td id="' . OCP\USER::getUser() . '_' . $_['calendar']['id'] . '"><label for="active_' . $_['calendar']['id'] . '">' . htmlspecialchars($_['calendar']['displayname']) . '</label></td>'; -echo '<td width="20px"><a href="#" onclick="Calendar.UI.Share.dropdown(\'' . OCP\USER::getUser() . '\', \'' . $_['calendar']['id'] . '\');" title="' . $l->t("Share Calendar") . '" class="action"><img class="svg action" src="' . ((!$_['shared']) ? OCP\Util::imagePath('core', 'actions/share.svg') : OCP\Util::imagePath('core', 'actions/shared.svg')) . '"></a></td>'; -echo '<td width="20px"><a href="#" onclick="Calendar.UI.showCalDAVUrl(\'' . OCP\USER::getUser() . '\', \'' . htmlentities($_['calendar']['uri']) . '\');" title="' . $l->t("CalDav Link") . '" class="action"><img class="svg action" src="'.OCP\Util::imagePath('core', 'actions/public.svg').'"></a></td>'; -echo '<td width="20px"><a href="?app=calendar&getfile=export.php?calid=' . $_['calendar']['id'] . '" title="' . $l->t('Download') . '" class="action"><img class="svg action" src="'.OCP\Util::imagePath('core', 'actions/download.svg').'"></a></td>'; -echo '<td width="20px"><a href="#" title="' . $l->t('Edit') . '" class="action" onclick="Calendar.UI.Calendar.edit(this, ' . $_['calendar']['id'] . ');"><img class="svg action" src="'.OCP\Util::imagePath('core', 'actions/rename.svg').'"></a></td>'; -echo '<td width="20px"><a href="#" onclick="Calendar.UI.Calendar.deleteCalendar(\'' . $_['calendar']['id'] . '\');" title="' . $l->t('Delete') . '" class="action"><img class="svg action" src="'.OCP\Util::imagePath('core', 'actions/delete.svg').'"></a></td>'; +<td width="20px"> + <input type="checkbox" id="active_<?php echo $_['calendar']['id'] ?>" onclick="Calendar.UI.Calendar.activation(this,<?php echo $_['calendar']['id'] ?>)"<?php echo $_['calendar']['active'] ? ' checked="checked"' : '' ?>> +</td> +<td id="<?php echo OCP\USER::getUser() ?>_<?php echo $_['calendar']['id'] ?>"> + <label for="active_<?php echo $_['calendar']['id'] ?>"><?php echo $_['calendar']['displayname'] ?></label> +</td> +<td width="20px"> + <a href="#" class="share" data-item-type="calendar" data-item="<?php echo $_['calendar']['id']; ?>" title="<?php echo $l->t('Share Calendar') ?>" class="action"><img class="svg action" src="<?php echo (!$_['shared']) ? OCP\Util::imagePath('core', 'actions/share.svg') : OCP\Util::imagePath('core', 'actions/shared.svg') ?>"></a> +</td> +<td width="20px"> + <a href="#" onclick="Calendar.UI.showCalDAVUrl('<?php echo OCP\USER::getUser() ?>', '<?php echo rawurlencode(html_entity_decode($_['calendar']['uri'], ENT_QUOTES, 'UTF-8')) ?>');" title="<?php echo $l->t('CalDav Link') ?>" class="action"><img class="svg action" src="<?php echo OCP\Util::imagePath('core', 'actions/public.svg') ?>"></a> +</td> +<td width="20px"> + <a href="<?php echo OCP\Util::linkTo('calendar', 'export.php') . '?calid=' . $_['calendar']['id'] ?>" title="<?php echo $l->t('Download') ?>" class="action"><img class="svg action" src="<?php echo OCP\Util::imagePath('core', 'actions/download.svg') ?>"></a> +</td> +<td width="20px"> + <a href="#" onclick="Calendar.UI.Calendar.edit(this, <?php echo $_['calendar']['id'] ?>);" title="<?php echo $l->t('Edit') ?>" class="action"><img class="svg action" src="<?php echo OCP\Util::imagePath('core', 'actions/rename.svg') ?>"></a> +</td> +<td width="20px"> + <a href="#" onclick="Calendar.UI.Calendar.deleteCalendar(<?php echo $_['calendar']['id'] ?>);" title="<?php echo $l->t('Delete') ?>" class="action"><img class="svg action" src="<?php echo OCP\Util::imagePath('core', 'actions/delete.svg') ?>"></a> +</td> diff --git a/apps/calendar/templates/part.editevent.php b/apps/calendar/templates/part.editevent.php index 102366f8f08..ea91192cc66 100644 --- a/apps/calendar/templates/part.editevent.php +++ b/apps/calendar/templates/part.editevent.php @@ -5,9 +5,9 @@ <?php echo $this->inc("part.eventform"); ?> <div style="width: 100%;text-align: center;color: #FF1D1D;" id="errorbox"></div> <span id="actions"> - <input type="button" class="submit" style="float: left;" value="<?php echo $l->t("Submit");?>" onclick="Calendar.UI.validateEventForm('?app=calendar&getfile=ajax/event/edit.php');"> - <input type="button" class="submit" style="float: left;" name="delete" value="<?php echo $l->t("Delete");?>" onclick="Calendar.UI.submitDeleteEventForm('?app=calendar&getfile=ajax/event/delete.php');"> - <input type="button" class="submit" style="float: right;" name="export" value="<?php echo $l->t("Export");?>" onclick="window.location='?app=calendar&getfile=export.php?eventid=<?php echo $_['eventid'] ?>';"> + <input type="button" class="submit" style="float: left;" value="<?php echo $l->t("Submit");?>" onclick="Calendar.UI.validateEventForm('<?php echo OCP\Util::linkTo('calendar', 'ajax/event/edit.php') ?>');"> + <input type="button" class="submit" style="float: left;" name="delete" value="<?php echo $l->t("Delete");?>" onclick="Calendar.UI.submitDeleteEventForm('<?php echo OCP\Util::linkTo('calendar', 'ajax/event/delete.php') ?>');"> + <input type="button" class="submit" style="float: right;" name="export" value="<?php echo $l->t("Export");?>" onclick="window.location='<?php echo OCP\Util::linkTo('calendar', 'export.php') ?>?eventid=<?php echo $_['eventid'] ?>';"> </span> </form> </div> diff --git a/apps/calendar/templates/part.eventform.php b/apps/calendar/templates/part.eventform.php index 2d86ce4d318..95eecf26223 100644 --- a/apps/calendar/templates/part.eventform.php +++ b/apps/calendar/templates/part.eventform.php @@ -18,7 +18,7 @@ echo 'Calendar.UI.Share.idtype = "event";' . "\n" . 'Calendar.UI.Share.currentid <tr> <th width="75px"><?php echo $l->t("Title");?>:</th> <td> - <input type="text" style="width:350px;" size="100" placeholder="<?php echo $l->t("Title of the Event");?>" value="<?php echo isset($_['title']) ? htmlspecialchars($_['title']) : '' ?>" maxlength="100" name="title"/> + <input type="text" style="width:350px;" size="100" placeholder="<?php echo $l->t("Title of the Event");?>" value="<?php echo isset($_['title']) ? $_['title'] : '' ?>" maxlength="100" name="title"/> </td> </tr> </table> @@ -26,7 +26,7 @@ echo 'Calendar.UI.Share.idtype = "event";' . "\n" . 'Calendar.UI.Share.currentid <tr> <th width="75px"><?php echo $l->t("Category");?>:</th> <td> - <input id="category" name="categories" type="text" placeholder="<?php echo $l->t('Separate categories with commas'); ?>" value="<?php echo isset($_['categories']) ? htmlspecialchars($_['categories']) : '' ?>"> + <input id="category" name="categories" type="text" placeholder="<?php echo $l->t('Separate categories with commas'); ?>" value="<?php echo isset($_['categories']) ? $_['categories'] : '' ?>"> <a class="action edit" onclick="$(this).tipsy('hide');OCCategories.edit();" title="<?php echo $l->t('Edit categories'); ?>"><img alt="<?php echo $l->t('Edit categories'); ?>" src="<?php echo OCP\image_path('core','actions/rename.svg')?>" class="svg action" style="width: 16px; height: 16px;"></a> </td> <?php if(count($_['calendar_options']) > 1) { ?> @@ -80,7 +80,7 @@ echo 'Calendar.UI.Share.idtype = "event";' . "\n" . 'Calendar.UI.Share.currentid <tr> <th width="85px"><?php echo $l->t("Location");?>:</th> <td> - <input type="text" style="width:350px;" size="100" placeholder="<?php echo $l->t("Location of the Event");?>" value="<?php echo isset($_['location']) ? htmlspecialchars($_['location']) : '' ?>" maxlength="100" name="location" /> + <input type="text" style="width:350px;" size="100" placeholder="<?php echo $l->t("Location of the Event");?>" value="<?php echo isset($_['location']) ? $_['location'] : '' ?>" maxlength="100" name="location" /> </td> </tr> </table> @@ -88,7 +88,7 @@ echo 'Calendar.UI.Share.idtype = "event";' . "\n" . 'Calendar.UI.Share.currentid <tr> <th width="85px" style="vertical-align: top;"><?php echo $l->t("Description");?>:</th> <td> - <textarea style="width:350px;height: 150px;" placeholder="<?php echo $l->t("Description of the Event");?>" name="description"><?php echo isset($_['description']) ? htmlspecialchars($_['description']) : '' ?></textarea> + <textarea style="width:350px;height: 150px;" placeholder="<?php echo $l->t("Description of the Event");?>" name="description"><?php echo isset($_['description']) ? $_['description'] : '' ?></textarea> </td> </tr> </table> diff --git a/apps/calendar/templates/part.import.php b/apps/calendar/templates/part.import.php index 3850ddde565..2ce3cc34239 100644 --- a/apps/calendar/templates/part.import.php +++ b/apps/calendar/templates/part.import.php @@ -1,30 +1,58 @@ -<div id="calendar_import_dialog" title="<?php echo $l->t("Import a calendar file"); ?>"> -<div id="form_container"> -<input type="hidden" id="filename" value="<?php echo $_['filename'];?>"> -<input type="hidden" id="path" value="<?php echo $_['path'];?>"> -<input type="hidden" id="progressfile" value="<?php echo md5(session_id()) . '.txt';?>"> -<p style="text-align:center;"><b><?php echo $l->t('Please choose the calendar'); ?></b></p> -<select style="width:100%;" id="calendar" name="calendar"> <?php +//Prerendering for iCalendar file +$file = OC_Filesystem::file_get_contents($_['path'] . '/' . $_['filename']); +if(!$file){ + OCP\JSON::error(array('error'=>'404')); +} +$import = new OC_Calendar_Import($file); +$import->setUserID(OCP\User::getUser()); +$newcalendarname = strip_tags($import->createCalendarName()); +$guessedcalendarname = strip_tags($import->guessCalendarName()); +$calendarcolor = strip_tags($import->createCalendarColor()); +//loading calendars for select box $calendar_options = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser()); $calendar_options[] = array('id'=>'newcal', 'displayname'=>$l->t('create a new calendar')); -for($i = 0;$i<count($calendar_options);$i++){ - $calendar_options[$i]['displayname'] = htmlspecialchars($calendar_options[$i]['displayname']); -} -echo OCP\html_select_options($calendar_options, $calendar_options[0]['id'], array('value'=>'id', 'label'=>'displayname')); +$defaultcolors = OC_Calendar_Calendar::getCalendarColorOptions(); ?> -</select> -<div id="newcalform" style="display: none;"> - <input type="text" style="width: 97%;" placeholder="<?php echo $l->t('Name of new calendar'); ?>" id="newcalendar" name="newcalendar"> -</div> -<input type="button" value="<?php echo $l->t("Import");?>!" id="startimport"> -</div> -<div id="progressbar_container" style="display: none"> -<p style="text-align:center;"><b><?php echo $l->t('Importing calendar'); ?></b></p> -<div id="progressbar"></div> -<div id="import_done" style="display: none;"> -<p style="text-align:center;"><b><?php echo $l->t('Calendar imported successfully'); ?></b></p> -<input type="button" value="<?php echo $l->t('Close Dialog'); ?>" id="import_done_button"> +<div id="calendar_import_dialog" title="<?php echo $l->t("Import a calendar file");?>"> +<div id="calendar_import_form"> + <form> + <input type="hidden" id="calendar_import_filename" value="<?php echo $_['filename'];?>"> + <input type="hidden" id="calendar_import_path" value="<?php echo $_['path'];?>"> + <input type="hidden" id="calendar_import_progresskey" value="<?php echo rand() ?>"> + <input type="hidden" id="calendar_import_availablename" value="<?php echo $newcalendarname ?>"> + <div id="calendar_import_form_message"><?php echo $l->t('Please choose a calendar'); ?></div> + <select style="width:100%;" id="calendar_import_calendar" name="calendar_import_calendar"> + <?php + for($i = 0;$i<count($calendar_options);$i++){ + $calendar_options[$i]['displayname'] = $calendar_options[$i]['displayname']; + } + echo OCP\html_select_options($calendar_options, $calendar_options[0]['id'], array('value'=>'id', 'label'=>'displayname')); + ?> + </select> + <br><br> + <div id="calendar_import_newcalform"> + <input id="calendar_import_newcalendar_color" class="color-picker" type="hidden" size="6" value="<?php echo substr($calendarcolor,1); ?>"> + <input id="calendar_import_newcalendar" class="" type="text" placeholder="<?php echo $l->t('Name of new calendar'); ?>" value="<?php echo $guessedcalendarname ?>"><br> + <div id="calendar_import_defaultcolors"> + <?php + foreach($defaultcolors as $color){ + echo '<span class="calendar-colorpicker-color" rel="' . $color . '" style="background-color: ' . $color . ';"></span>'; + } + ?> + </div> + <!--<input id="calendar_import_generatename" type="button" class="button" value="<?php echo $l->t('Take an available name!'); ?>"><br>--> + <div id="calendar_import_mergewarning" class="hint"><?php echo $l->t('A Calendar with this name already exists. If you continue anyhow, these calendars will be merged.'); ?></div> + </div> + <input id="calendar_import_submit" type="button" class="button" value="» <?php echo $l->t('Import'); ?> »" id="startimport"> + <form> </div> +<div id="calendar_import_process"> + <div id="calendar_import_process_message"></div> + <div id="calendar_import_progressbar"></div> + <br> + <div id="calendar_import_status" class="hint"></div> + <br> + <input id="calendar_import_done" type="button" value="<?php echo $l->t('Close Dialog'); ?>"> </div> </div>
\ No newline at end of file diff --git a/apps/calendar/templates/part.newevent.php b/apps/calendar/templates/part.newevent.php index f4bb867b180..a4f48aecbc6 100644 --- a/apps/calendar/templates/part.newevent.php +++ b/apps/calendar/templates/part.newevent.php @@ -3,7 +3,7 @@ <?php echo $this->inc("part.eventform"); ?> <div style="width: 100%;text-align: center;color: #FF1D1D;" id="errorbox"></div> <span id="actions"> - <input type="button" class="submit" style="float: left;" value="<?php echo $l->t("Submit");?>" onclick="Calendar.UI.validateEventForm('?app=calendar&getfile=ajax/event/new.php');"> + <input type="button" class="submit" style="float: left;" value="<?php echo $l->t("Submit");?>" onclick="Calendar.UI.validateEventForm('<?php echo OCP\Util::linkTo('calendar', 'ajax/event/new.php') ?>');"> </span> </form> </div> diff --git a/apps/calendar/templates/part.showevent.php b/apps/calendar/templates/part.showevent.php index a4c862ad417..59684d831e5 100644 --- a/apps/calendar/templates/part.showevent.php +++ b/apps/calendar/templates/part.showevent.php @@ -10,7 +10,7 @@ <tr> <th width="75px"><?php echo $l->t("Title");?>:</th> <td> - <?php echo isset($_['title']) ? htmlspecialchars($_['title']) : '' ?> + <?php echo isset($_['title']) ? $_['title'] : '' ?> </td> </tr> </table> @@ -76,7 +76,7 @@ <tr> <th width="85px"><?php echo $l->t("Location");?>:</th> <td> - <?php echo isset($_['location']) ? htmlspecialchars($_['location']) : '' ?> + <?php echo isset($_['location']) ? $_['location'] : '' ?> </td> </tr> </table> @@ -84,7 +84,7 @@ <tr> <th width="85px" style="vertical-align: top;"><?php echo $l->t("Description");?>:</th> <td> - <?php echo isset($_['description']) ? htmlspecialchars($_['description']) : '' ?></textarea> + <?php echo isset($_['description']) ? $_['description'] : '' ?></textarea> </tr> </table> </div> diff --git a/apps/calendar/templates/settings.php b/apps/calendar/templates/settings.php index feb06655120..56a6a42ee0e 100644 --- a/apps/calendar/templates/settings.php +++ b/apps/calendar/templates/settings.php @@ -1,17 +1,22 @@ <?php /** * Copyright (c) 2011 Bart Visscher <bartv@thisnet.nl> - * Copyright (c) 2011 Georg Ehrke <ownclouddev at georgswebsite dot de> + * Copyright (c) 2012 Georg Ehrke <ownclouddev at georgswebsite dot de> * This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. */ ?> -<form id="calendar"> - <fieldset class="personalblock"> - <legend><?php echo $l->t('Calendar'); ?></legend> - <table class="nostyle"> - <tr><td><label for="timezone" class="bold"><?php echo $l->t('Timezone');?></label></td><td><select style="display: none;" id="timezone" name="timezone"> +<h2 id="title_general"><?php echo $l->t('General'); ?></h2> +<div id="general"> + <table class="nostyle"> + <tr> + <td> + <label for="timezone" class="bold"><?php echo $l->t('Timezone');?></label> + + </td> + <td> + <select style="display: none;" id="timezone" name="timezone"> <?php $continent = ''; foreach($_['timezones'] as $timezone): @@ -27,31 +32,70 @@ $city=strtr($ex[1], '_', ' '); $continent=$ex[0]; echo '<option value="'.$timezone.'"'.($_['timezone'] == $timezone?' selected="selected"':'').'>'.$city.'</option>'; + var_dump($_['timezone']); endforeach;?> - </select><input type="checkbox" name="timezonedetection" id="timezonedetection"><label for="timezonedetection"><?php echo $l->t('Check always for changes of the timezone'); ?></label></td></tr> - - <tr><td><label for="timeformat" class="bold"><?php echo $l->t('Timeformat');?></label></td><td> + </select> + </td> + </tr> + <tr> + <td> + + </td> + <td> + <input type="checkbox" name="timezonedetection" id="timezonedetection"> + + <label for="timezonedetection"><?php echo $l->t('Update timezone automatically'); ?></label> + </td> + </tr> + <tr> + <td> + <label for="timeformat" class="bold"><?php echo $l->t('Time format');?></label> + + </td> + <td> <select style="display: none; width: 60px;" id="timeformat" title="<?php echo "timeformat"; ?>" name="timeformat"> <option value="24" id="24h"><?php echo $l->t("24h"); ?></option> <option value="ampm" id="ampm"><?php echo $l->t("12h"); ?></option> </select> - </td></tr> - - <tr><td><label for="firstday" class="bold"><?php echo $l->t('First day of the week');?></label></td><td> + </td> + </tr> + <tr> + <td> + <label for="firstday" class="bold"><?php echo $l->t('Start week on');?></label> + + </td> + <td> <select style="display: none;" id="firstday" title="<?php echo "First day"; ?>" name="firstday"> <option value="mo" id="mo"><?php echo $l->t("Monday"); ?></option> <option value="su" id="su"><?php echo $l->t("Sunday"); ?></option> </select> - </td></tr> - - </table> - + </td> + </tr> + <tr class="advancedsettings"> + <td> + <label for="" class="bold"><?php echo $l->t('Cache');?></label> + + </td> + <td> + <input id="cleancalendarcache" type="button" class="button" value="<?php echo $l->t('Clear cache for repeating events');?>"> + </td> + </tr> + </table> +</div> +<h2 id="title_urls"><?php echo $l->t('URLs'); ?></h2> +<div id="urls"> <?php echo $l->t('Calendar CalDAV syncing addresses'); ?> (<a href="http://owncloud.org/synchronisation/" target="_blank"><?php echo $l->t('more info'); ?></a>) <dl> <dt><?php echo $l->t('Primary address (Kontact et al)'); ?></dt> <dd><code><?php echo OCP\Util::linkToRemote('caldav'); ?></code></dd> <dt><?php echo $l->t('iOS/OS X'); ?></dt> <dd><code><?php echo OCP\Util::linkToRemote('caldav'); ?>principals/<?php echo OCP\USER::getUser(); ?></code>/</dd> + <dt><?php echo $l->t('Read only iCalendar link(s)'); ?></dt> + <dd> + <?php foreach($_['calendars'] as $calendar) { ?> + <a href="<?php echo OCP\Util::linkToRemote('caldav').'calendars/'.OCP\USER::getUser().'/'.rawurlencode($calendar['uri']) ?>?export"><?php echo $calendar['displayname'] ?></a><br /> + <?php } ?> + </dd> </dl> - </fieldset> -</form> + </div> +</div>
\ No newline at end of file diff --git a/apps/calendar/templates/share.dropdown.php b/apps/calendar/templates/share.dropdown.php index 07b4c4bced5..391ae83765b 100644 --- a/apps/calendar/templates/share.dropdown.php +++ b/apps/calendar/templates/share.dropdown.php @@ -33,7 +33,7 @@ echo OCP\html_select_options($allusers, array()); </select><br> <ul id="sharewithuser_list"> <?php foreach($users as $user): ?> - <li id="sharewithuser_<?php echo $user['share']; ?>"><input type="checkbox" width="12px" <?php echo ($user['permissions']?'checked="checked"':'')?> style="visibility:hidden;" title="<?php echo $l->t('Editable'); ?>"><?php echo $user['share']; ?><img src="<?php echo OC::$WEBROOT; ?>/core/img/actions/delete.svg" class="svg action" style="display:none;float:right;"></li> + <li id="sharewithuser_<?php echo $user['share']; ?>"><input type="checkbox" width="12px" <?php echo ($user['permissions']?'checked="checked"':'')?> style="visibility:hidden;" title="<?php echo $l->t('Editable'); ?>"><?php echo $user['share']; ?><img src="<?php echo OCP\Util::imagePath('core', 'actions/delete.svg'); ?>" class="svg action" style="display:none;float:right;"></li> <script> $('#sharewithuser_<?php echo $user['share']; ?> > img').click(function(){ $('#share_user option[value="<?php echo $user['share']; ?>"]').removeAttr('disabled'); @@ -59,7 +59,7 @@ echo OCP\html_select_options($allgroups, array()); </select><br> <ul id="sharewithgroup_list"> <?php foreach($groups as $group): ?> - <li id="sharewithgroup_<?php echo $group['share']; ?>"><input type="checkbox" width="12px" <?php echo ($group['permissions']?'checked="checked"':'')?> style="visibility:hidden;" title="<?php echo $l->t('Editable'); ?>"><?php echo $group['share']; ?><img src="<?php echo OC::$WEBROOT; ?>/core/img/actions/delete.svg" class="svg action" style="display:none;float:right;"></li> + <li id="sharewithgroup_<?php echo $group['share']; ?>"><input type="checkbox" width="12px" <?php echo ($group['permissions']?'checked="checked"':'')?> style="visibility:hidden;" title="<?php echo $l->t('Editable'); ?>"><?php echo $group['share']; ?><img src="<?php echo OCP\Util::imagePath('core', 'actions/delete.svg'); ?>" class="svg action" style="display:none;float:right;"></li> <script> $('#sharewithgroup_<?php echo $group['share']; ?> > img').click(function(){ $('#share_group option[value="<?php echo $group['share']; ?>"]').removeAttr('disabled'); diff --git a/apps/contacts/ajax/activation.php b/apps/contacts/ajax/activation.php deleted file mode 100644 index 74cb738ab8f..00000000000 --- a/apps/contacts/ajax/activation.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php -/** - * Copyright (c) 2011 Thomas Tanghus <thomas@tanghus.net> - * Copyright (c) 2011 Bart Visscher <bartv@thisnet.nl> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - - -OCP\JSON::checkLoggedIn(); -OCP\JSON::checkAppEnabled('contacts'); -OCP\JSON::callCheck(); - -$bookid = $_POST['bookid']; -$book = OC_Contacts_App::getAddressbook($bookid);// is owner access check - -if(!OC_Contacts_Addressbook::setActive($bookid, $_POST['active'])) { - OCP\Util::writeLog('contacts','ajax/activation.php: Error activating addressbook: '.$bookid, OCP\Util::ERROR); - OCP\JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('Error (de)activating addressbook.')))); - exit(); -} - -OCP\JSON::success(array( - 'active' => OC_Contacts_Addressbook::isActive($bookid), - 'bookid' => $bookid, - 'book' => $book, -)); diff --git a/apps/contacts/ajax/addbook.php b/apps/contacts/ajax/addbook.php deleted file mode 100644 index 70f47cc8123..00000000000 --- a/apps/contacts/ajax/addbook.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php -/** - * Copyright (c) 2011 Bart Visscher <bartv@thisnet.nl> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - - -OCP\JSON::checkLoggedIn(); -OCP\JSON::checkAppEnabled('contacts'); -$book = array( - 'id' => 'new', - 'displayname' => '', -); -$tmpl = new OCP\Template('contacts', 'part.editaddressbook'); -$tmpl->assign('new', true); -$tmpl->assign('addressbook', $book); -$tmpl->printPage(); -?> diff --git a/apps/contacts/ajax/addressbook/activate.php b/apps/contacts/ajax/addressbook/activate.php new file mode 100644 index 00000000000..a8dec21dac7 --- /dev/null +++ b/apps/contacts/ajax/addressbook/activate.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright (c) 2011 Thomas Tanghus <thomas@tanghus.net> + * Copyright (c) 2011 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + + +OCP\JSON::checkLoggedIn(); +OCP\JSON::checkAppEnabled('contacts'); +OCP\JSON::callCheck(); + +$id = $_POST['id']; +$book = OC_Contacts_App::getAddressbook($id);// is owner access check + +if(!OC_Contacts_Addressbook::setActive($id, $_POST['active'])) { + OCP\Util::writeLog('contacts', + 'ajax/activation.php: Error activating addressbook: '. $id, + OCP\Util::ERROR); + OCP\JSON::error(array( + 'data' => array( + 'message' => OC_Contacts_App::$l10n->t('Error (de)activating addressbook.')))); + exit(); +} + +OCP\JSON::success(array( + 'active' => OC_Contacts_Addressbook::isActive($id), + 'id' => $id, + 'addressbook' => $book, +)); diff --git a/apps/contacts/ajax/addressbook/add.php b/apps/contacts/ajax/addressbook/add.php new file mode 100644 index 00000000000..65077743ed5 --- /dev/null +++ b/apps/contacts/ajax/addressbook/add.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright (c) 2011-2012 Thomas Tanghus <thomas@tanghus.net> + * Copyright (c) 2011 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + + +// Check if we are a user +OCP\JSON::checkLoggedIn(); +OCP\JSON::checkAppEnabled('contacts'); +OCP\JSON::callCheck(); +require_once __DIR__.'/../loghandler.php'; + +debug('name: '.$_POST['name']); + +$userid = OCP\USER::getUser(); +$name = isset($_POST['name'])?trim(strip_tags($_POST['name'])):null; +$description = isset($_POST['description']) + ? trim(strip_tags($_POST['description'])) + : null; + +if(is_null($name)) { + bailOut('Cannot add addressbook with an empty name.'); +} +$bookid = OC_Contacts_Addressbook::add($userid, $name, $description); +if(!$bookid) { + bailOut('Error adding addressbook: '.$name); +} + +if(!OC_Contacts_Addressbook::setActive($bookid, 1)) { + bailOut('Error activating addressbook.'); +} +$addressbook = OC_Contacts_App::getAddressbook($bookid); +OCP\JSON::success(array('data' => array('addressbook' => $addressbook))); diff --git a/apps/contacts/ajax/deletebook.php b/apps/contacts/ajax/addressbook/delete.php index 4520374a23b..f59c605f4e4 100644 --- a/apps/contacts/ajax/deletebook.php +++ b/apps/contacts/ajax/addressbook/delete.php @@ -20,16 +20,16 @@ * */ -// Init owncloud - - // Check if we are a user OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('contacts'); OCP\JSON::callCheck(); +require_once __DIR__.'/../loghandler.php'; -//$id = $_GET['id']; $id = $_POST['id']; +if(!$id) { + bailOut(OC_Contacts_App::$l10n->t('id is not set.')); +} OC_Contacts_App::getAddressbook( $id ); // is owner access check OC_Contacts_Addressbook::delete($id); diff --git a/apps/contacts/ajax/addressbook/update.php b/apps/contacts/ajax/addressbook/update.php new file mode 100644 index 00000000000..0fc66c3a3bf --- /dev/null +++ b/apps/contacts/ajax/addressbook/update.php @@ -0,0 +1,39 @@ +<?php +/** + * Copyright (c) 2011-2012 Thomas Tanghus <thomas@tanghus.net> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + + + +// Check if we are a user +OCP\JSON::checkLoggedIn(); +OCP\JSON::checkAppEnabled('contacts'); +require_once __DIR__.'/../loghandler.php'; + +$id = $_POST['id']; +$name = trim(strip_tags($_POST['name'])); +$description = trim(strip_tags($_POST['description'])); +if(!$id) { + bailOut(OC_Contacts_App::$l10n->t('id is not set.')); +} + +if(!$name) { + bailOut(OC_Contacts_App::$l10n->t('Cannot update addressbook with an empty name.')); +} + +if(!OC_Contacts_Addressbook::edit($id, $name, $description)) { + bailOut(OC_Contacts_App::$l10n->t('Error updating addressbook.')); +} + +if(!OC_Contacts_Addressbook::setActive($id, $_POST['active'])) { + bailOut(OC_Contacts_App::$l10n->t('Error (de)activating addressbook.')); +} + +OC_Contacts_App::getAddressbook($id); // is owner access check +$addressbook = OC_Contacts_App::getAddressbook($id); +OCP\JSON::success(array( + 'addressbook' => $addressbook, +)); diff --git a/apps/contacts/ajax/categories/categoriesfor.php b/apps/contacts/ajax/categories/categoriesfor.php index 846af300de8..8391b14b545 100644 --- a/apps/contacts/ajax/categories/categoriesfor.php +++ b/apps/contacts/ajax/categories/categoriesfor.php @@ -12,17 +12,23 @@ OCP\JSON::checkAppEnabled('contacts'); $id = isset($_GET['id'])?$_GET['id']:null; if(is_null($id)) { - OCP\JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('No ID provided')))); + OCP\JSON::error(array( + 'data' => array( + 'message' => OC_Contacts_App::$l10n->t('No ID provided')))); exit(); } $vcard = OC_Contacts_App::getContactVCard( $id ); foreach($vcard->children as $property){ - //OCP\Util::writeLog('contacts','ajax/categories/checksumfor.php: '.$property->name, OCP\Util::DEBUG); if($property->name == 'CATEGORIES') { $checksum = md5($property->serialize()); - OCP\JSON::success(array('data' => array('value'=>$property->value, 'checksum'=>$checksum))); + OCP\JSON::success(array( + 'data' => array( + 'value' => $property->value, + 'checksum' => $checksum, + ))); exit(); } } -OCP\JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('Error setting checksum.')))); -?> +OCP\JSON::error(array( + 'data' => array( + 'message' => OC_Contacts_App::$l10n->t('Error setting checksum.')))); diff --git a/apps/contacts/ajax/categories/delete.php b/apps/contacts/ajax/categories/delete.php index bee2dbe3f6b..bc9f3e14e87 100644 --- a/apps/contacts/ajax/categories/delete.php +++ b/apps/contacts/ajax/categories/delete.php @@ -9,19 +9,9 @@ OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('contacts'); +OCP\JSON::callCheck(); -foreach ($_POST as $key=>$element) { - debug('_POST: '.$key.'=>'.print_r($element, true)); -} - -function bailOut($msg) { - OCP\JSON::error(array('data' => array('message' => $msg))); - OCP\Util::writeLog('contacts','ajax/categories/delete.php: '.$msg, OCP\Util::DEBUG); - exit(); -} -function debug($msg) { - OCP\Util::writeLog('contacts','ajax/categories/delete.php: '.$msg, OCP\Util::DEBUG); -} +require_once __DIR__.'/../loghandler.php'; $categories = isset($_POST['categories'])?$_POST['categories']:null; @@ -56,5 +46,3 @@ $catman->delete($categories, $cards); debug('After delete: '.print_r($catman->categories(), true)); OC_Contacts_VCard::updateDataByID($cards); OCP\JSON::success(array('data' => array('categories'=>$catman->categories()))); - -?> diff --git a/apps/contacts/ajax/categories/list.php b/apps/contacts/ajax/categories/list.php index 3ae7635390c..f234116ba8c 100644 --- a/apps/contacts/ajax/categories/list.php +++ b/apps/contacts/ajax/categories/list.php @@ -13,5 +13,3 @@ OCP\JSON::checkAppEnabled('contacts'); $categories = OC_Contacts_App::getCategories(); OCP\JSON::success(array('data' => array('categories'=>$categories))); - -?> diff --git a/apps/contacts/ajax/categories/rescan.php b/apps/contacts/ajax/categories/rescan.php index 84a67dec0b1..a06e7803955 100644 --- a/apps/contacts/ajax/categories/rescan.php +++ b/apps/contacts/ajax/categories/rescan.php @@ -9,36 +9,9 @@ OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('contacts'); +OCP\JSON::callCheck(); -foreach ($_POST as $key=>$element) { - debug('_POST: '.$key.'=>'.print_r($element, true)); -} - -function bailOut($msg) { - OCP\JSON::error(array('data' => array('message' => $msg))); - OCP\Util::writeLog('contacts','ajax/categories/rescan.php: '.$msg, OCP\Util::DEBUG); - exit(); -} -function debug($msg) { - OCP\Util::writeLog('contacts','ajax/categories/rescan.php: '.$msg, OCP\Util::DEBUG); -} - -$addressbooks = OC_Contacts_Addressbook::all(OCP\USER::getUser()); -if(count($addressbooks) == 0) { - bailOut(OC_Contacts_App::$l10n->t('No address books found.')); -} -$addressbookids = array(); -foreach($addressbooks as $addressbook) { - $addressbookids[] = $addressbook['id']; -} -$contacts = OC_Contacts_VCard::all($addressbookids); -if(count($contacts) == 0) { - bailOut(OC_Contacts_App::$l10n->t('No contacts found.')); -} - -OC_Contacts_App::scanCategories($contacts); +OC_Contacts_App::scanCategories(); $categories = OC_Contacts_App::getCategories(); OCP\JSON::success(array('data' => array('categories'=>$categories))); - -?> diff --git a/apps/contacts/ajax/chooseaddressbook.php b/apps/contacts/ajax/chooseaddressbook.php deleted file mode 100644 index 9088a4e9d7d..00000000000 --- a/apps/contacts/ajax/chooseaddressbook.php +++ /dev/null @@ -1,14 +0,0 @@ -<?php -/** - * Copyright (c) 2011 Thomas Tanghus <thomas@tanghus.net> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - - -OCP\JSON::checkLoggedIn(); -OCP\JSON::checkAppEnabled('contacts'); - -$output = new OCP\Template("contacts", "part.chooseaddressbook"); -$output -> printpage(); diff --git a/apps/contacts/ajax/addcontact.php b/apps/contacts/ajax/contact/add.php index d15ad8c6216..c7cec7d9461 100644 --- a/apps/contacts/ajax/addcontact.php +++ b/apps/contacts/ajax/contact/add.php @@ -20,14 +20,6 @@ * */ -// Init owncloud - -function bailOut($msg) { - OCP\JSON::error(array('data' => array('message' => $msg))); - OCP\Util::writeLog('contacts','ajax/addcontact.php: '.$msg, OCP\Util::DEBUG); - exit(); -} - // Check if we are a user OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('contacts'); @@ -45,14 +37,26 @@ $n = trim($_POST['n']); $vcard = new OC_VObject('VCARD'); $vcard->setUID(); -$vcard->setString('FN',$fn); -$vcard->setString('N',$n); +$vcard->setString('FN', $fn); +$vcard->setString('N', $n); -$id = OC_Contacts_VCard::add($aid,$vcard, null, $isnew); +$id = OC_Contacts_VCard::add($aid, $vcard, null, $isnew); if(!$id) { - OCP\JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('There was an error adding the contact.')))); - OCP\Util::writeLog('contacts','ajax/addcontact.php: Recieved non-positive ID on adding card: '.$id, OCP\Util::ERROR); + OCP\JSON::error(array( + 'data' => array( + 'message' => OC_Contacts_App::$l10n->t('There was an error adding the contact.')))); + OCP\Util::writeLog('contacts', 'ajax/addcontact.php: Recieved non-positive ID on adding card: '.$id, OCP\Util::ERROR); exit(); } -OCP\JSON::success(array('data' => array( 'id' => $id ))); +$lastmodified = OC_Contacts_App::lastModified($vcard); +if(!$lastmodified) { + $lastmodified = new DateTime(); +} +OCP\JSON::success(array( + 'data' => array( + 'id' => $id, + 'aid' => $aid, + 'lastmodified' => $lastmodified->format('U') + ) +)); diff --git a/apps/contacts/ajax/addproperty.php b/apps/contacts/ajax/contact/addproperty.php index be9e849be72..2b80ebd58bf 100644 --- a/apps/contacts/ajax/addproperty.php +++ b/apps/contacts/ajax/contact/addproperty.php @@ -20,19 +20,12 @@ * */ -// Init owncloud - - // Check if we are a user OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('contacts'); OCP\JSON::callCheck(); -function bailOut($msg) { - OCP\JSON::error(array('data' => array('message' => $msg))); - OCP\Util::writeLog('contacts','ajax/addproperty.php: '.$msg, OCP\Util::DEBUG); - exit(); -} +require_once __DIR__.'/../loghandler.php'; $id = isset($_POST['id'])?$_POST['id']:null; $name = isset($_POST['name'])?$_POST['name']:null; @@ -40,22 +33,27 @@ $value = isset($_POST['value'])?$_POST['value']:null; $parameters = isset($_POST['parameters'])?$_POST['parameters']:array(); $vcard = OC_Contacts_App::getContactVCard($id); +$l10n = OC_Contacts_App::$l10n; if(!$name) { - bailOut(OC_Contacts_App::$l10n->t('element name is not set.')); + bailOut($l10n->t('element name is not set.')); } if(!$id) { - bailOut(OC_Contacts_App::$l10n->t('id is not set.')); + bailOut($l10n->t('id is not set.')); } if(!$vcard) { - bailOut(OC_Contacts_App::$l10n->t('Could not parse contact: ').$id); + bailOut($l10n->t('Could not parse contact: ').$id); } -if(!is_array($value)){ +if(!is_array($value)) { $value = trim($value); - if(!$value && in_array($name, array('TEL', 'EMAIL', 'ORG', 'BDAY', 'URL', 'NICKNAME', 'NOTE'))) { - bailOut(OC_Contacts_App::$l10n->t('Cannot add empty property.')); + if(!$value + && in_array( + $name, + array('TEL', 'EMAIL', 'ORG', 'BDAY', 'URL', 'NICKNAME', 'NOTE')) + ) { + bailOut($l10n->t('Cannot add empty property.')); } } elseif($name === 'ADR') { // only add if non-empty elements. $empty = true; @@ -66,7 +64,7 @@ if(!is_array($value)){ } } if($empty) { - bailOut(OC_Contacts_App::$l10n->t('At least one of the address fields has to be filled out.')); + bailOut($l10n->t('At least one of the address fields has to be filled out.')); } } @@ -75,12 +73,14 @@ $current = $vcard->select($name); foreach($current as $item) { $tmpvalue = (is_array($value)?implode(';', $value):$value); if($tmpvalue == $item->value) { - bailOut(OC_Contacts_App::$l10n->t('Trying to add duplicate property: '.$name.': '.$tmpvalue)); + bailOut($l10n->t('Trying to add duplicate property: '.$name.': '.$tmpvalue)); } } if(is_array($value)) { - ksort($value); // NOTE: Important, otherwise the compound value will be set in the order the fields appear in the form! + // NOTE: Important, otherwise the compound value will + // be set in the order the fields appear in the form! + ksort($value); $value = array_map('strip_tags', $value); } else { $value = strip_tags($value); @@ -108,7 +108,17 @@ switch($name) { $value = strtolower($value); break; case 'TEL': - case 'ADR': // should I delete the property if empty or throw an error? + case 'ADR': + break; + case 'IMPP': + if(is_null($parameters) || !isset($parameters['X-SERVICE-TYPE'])) { + bailOut(OC_Contacts_App::$l10n->t('Missing IM parameter.')); + } + $impp = OC_Contacts_App::getIMOptions($parameters['X-SERVICE-TYPE']); + if(is_null($impp)) { + bailOut(OC_Contacts_App::$l10n->t('Unknown IM: '.$parameters['X-SERVICE-TYPE'])); + } + $value = $impp['protocol'] . ':' . $value; break; } @@ -123,24 +133,36 @@ switch($name) { $line = count($vcard->children) - 1; -// Apparently Sabre_VObject_Parameter doesn't do well with multiple values or I don't know how to do it. Tanghus. +// Apparently Sabre_VObject_Parameter doesn't do well with +// multiple values or I don't know how to do it. Tanghus. foreach ($parameters as $key=>$element) { - if(is_array($element) && strtoupper($key) == 'TYPE') { + if(is_array($element) /*&& strtoupper($key) == 'TYPE'*/) { // NOTE: Maybe this doesn't only apply for TYPE? // And it probably shouldn't be done here anyways :-/ - foreach($element as $e){ - if($e != '' && !is_null($e)){ - $vcard->children[$line]->parameters[] = new Sabre_VObject_Parameter($key,$e); + foreach($element as $e) { + if($e != '' && !is_null($e)) { + if(trim($e)) { + $vcard->children[$line]->parameters[] = new Sabre_VObject_Parameter($key, $e); + } } } } else { - $vcard->children[$line]->parameters[] = new Sabre_VObject_Parameter($key,$element); + if(trim($element)) { + $vcard->children[$line]->parameters[] = new Sabre_VObject_Parameter($key, $element); + } } } $checksum = md5($vcard->children[$line]->serialize()); -if(!OC_Contacts_VCard::edit($id,$vcard)) { - bailOut(OC_Contacts_App::$l10n->t('Error adding contact property: '.$name)); +try { + OC_Contacts_VCard::edit($id, $vcard); +} catch(Exception $e) { + bailOut($e->getMessage()); } -OCP\JSON::success(array('data' => array( 'checksum' => $checksum ))); +OCP\JSON::success(array( + 'data' => array( + 'checksum' => $checksum, + 'lastmodified' => OC_Contacts_App::lastModified($vcard)->format('U')) + ) +); diff --git a/apps/contacts/ajax/deletecard.php b/apps/contacts/ajax/contact/delete.php index f998185be41..e73f34f898d 100644 --- a/apps/contacts/ajax/deletecard.php +++ b/apps/contacts/ajax/contact/delete.php @@ -4,6 +4,7 @@ * * @author Jakob Sack * @copyright 2011 Jakob Sack mail@jakobsack.de + * @copyright 2012 Thomas Tanghus (thomas@tanghus.net) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -19,25 +20,25 @@ * License along with this library. If not, see <http://www.gnu.org/licenses/>. * */ -function bailOut($msg) { - OCP\JSON::error(array('data' => array('message' => $msg))); - OCP\Util::writeLog('contacts','ajax/saveproperty.php: '.$msg, OCP\Util::DEBUG); - exit(); -} - -// Init owncloud - - // Check if we are a user OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('contacts'); OCP\JSON::callCheck(); +require_once __DIR__.'/../loghandler.php'; + $id = isset($_POST['id'])?$_POST['id']:null; if(!$id) { bailOut(OC_Contacts_App::$l10n->t('id is not set.')); } -$card = OC_Contacts_App::getContactObject( $id ); -OC_Contacts_VCard::delete($id); +try { + OC_Contacts_VCard::delete($id); +} catch(Exception $e) { + $msg = $e->getMessage(); + OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$msg, + OCP\Util::DEBUG); + OCP\Util::writeLog('contacts', __METHOD__.', id'.$id, OCP\Util::DEBUG); + bailOut($msg); +} OCP\JSON::success(array('data' => array( 'id' => $id ))); diff --git a/apps/contacts/ajax/deleteproperty.php b/apps/contacts/ajax/contact/deleteproperty.php index 95fd43e0d95..b76b6e55ede 100644 --- a/apps/contacts/ajax/deleteproperty.php +++ b/apps/contacts/ajax/contact/deleteproperty.php @@ -20,30 +20,35 @@ * */ -// Init owncloud - - // Check if we are a user OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('contacts'); OCP\JSON::callCheck(); +require_once __DIR__.'/../loghandler.php'; + $id = $_POST['id']; $checksum = $_POST['checksum']; +$l10n = OC_Contacts_App::$l10n; $vcard = OC_Contacts_App::getContactVCard( $id ); $line = OC_Contacts_App::getPropertyLineByChecksum($vcard, $checksum); -if(is_null($line)){ - OCP\JSON::error(array('data' => array( 'message' => OC_Contacts_App::$l10n->t('Information about vCard is incorrect. Please reload the page.')))); +if(is_null($line)) { + bailOut($l10n->t('Information about vCard is incorrect. Please reload the page.')); exit(); } unset($vcard->children[$line]); -if(!OC_Contacts_VCard::edit($id,$vcard)) { - OCP\JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('Error deleting contact property.')))); - OCP\Util::writeLog('contacts','ajax/deleteproperty.php: Error deleting contact property', OCP\Util::ERROR); - exit(); +try { + OC_Contacts_VCard::edit($id, $vcard); +} catch(Exception $e) { + bailOut($e->getMessage()); } -OCP\JSON::success(array('data' => array( 'id' => $id ))); +OCP\JSON::success(array( + 'data' => array( + 'id' => $id, + 'lastmodified' => OC_Contacts_App::lastModified($vcard)->format('U'), + ) +)); diff --git a/apps/contacts/ajax/contactdetails.php b/apps/contacts/ajax/contact/details.php index 657dc5d586c..5bf337e645e 100644 --- a/apps/contacts/ajax/contactdetails.php +++ b/apps/contacts/ajax/contact/details.php @@ -20,13 +20,7 @@ * */ -// Init owncloud - -function bailOut($msg) { - OCP\JSON::error(array('data' => array('message' => $msg))); - OCP\Util::writeLog('contacts','ajax/contactdetails.php: '.$msg, OCP\Util::DEBUG); - exit(); -} +require_once __DIR__.'/../loghandler.php'; // Check if we are a user OCP\JSON::checkLoggedIn(); @@ -36,24 +30,13 @@ $id = isset($_GET['id'])?$_GET['id']:null; if(is_null($id)) { bailOut(OC_Contacts_App::$l10n->t('Missing ID')); } -$vcard = OC_Contacts_App::getContactVCard( $id ); +$card = OC_Contacts_VCard::find($id); +$vcard = OC_VObject::parse($card['carddata']); if(is_null($vcard)) { bailOut(OC_Contacts_App::$l10n->t('Error parsing VCard for ID: "'.$id.'"')); } $details = OC_Contacts_VCard::structureContact($vcard); -// Some Google exported files have no FN field. -/*if(!isset($details['FN'])) { - $fn = ''; - if(isset($details['N'])) { - $details['FN'] = array(implode(' ', $details['N'][0]['value'])); - } elseif(isset($details['EMAIL'])) { - $details['FN'] = array('value' => $details['EMAIL'][0]['value']); - } else { - $details['FN'] = array('value' => OC_Contacts_App::$l10n->t('Unknown')); - } -}*/ - // Make up for not supporting the 'N' field in earlier version. if(!isset($details['N'])) { $details['N'] = array(); @@ -67,6 +50,13 @@ if(isset($details['PHOTO'])) { } else { $details['PHOTO'] = false; } +$lastmodified = OC_Contacts_App::lastModified($vcard); +if(!$lastmodified) { + $lastmodified = new DateTime(); +} $details['id'] = $id; +$details['displayname'] = $card['fullname']; +$details['addressbookid'] = $card['addressbookid']; +$details['lastmodified'] = $lastmodified->format('U'); OC_Contacts_App::setLastModifiedHeader($vcard); OCP\JSON::success(array('data' => $details)); diff --git a/apps/contacts/ajax/contact/list.php b/apps/contacts/ajax/contact/list.php new file mode 100644 index 00000000000..4e2509d8d5a --- /dev/null +++ b/apps/contacts/ajax/contact/list.php @@ -0,0 +1,97 @@ +<?php +/** + * Copyright (c) 2011 Thomas Tanghus <thomas@tanghus.net> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +function cmp($a, $b) +{ + if ($a['displayname'] == $b['displayname']) { + return 0; + } + return ($a['displayname'] < $b['displayname']) ? -1 : 1; +} + +OCP\JSON::checkLoggedIn(); +OCP\JSON::checkAppEnabled('contacts'); + +$start = isset($_GET['startat'])?$_GET['startat']:0; +$aid = isset($_GET['aid'])?$_GET['aid']:null; + +if(is_null($aid)) { + // Called initially to get the active addressbooks. + $active_addressbooks = OC_Contacts_Addressbook::active(OCP\USER::getUser()); +} else { + // called each time more contacts has to be shown. + $active_addressbooks = array(OC_Contacts_Addressbook::find($aid)); +} + + +session_write_close(); + +// create the addressbook associate array +$contacts_addressbook = array(); +$ids = array(); +foreach($active_addressbooks as $addressbook) { + $ids[] = $addressbook['id']; + if(!isset($contacts_addressbook[$addressbook['id']])) { + $contacts_addressbook[$addressbook['id']] + = array('contacts' => array('type' => 'book',)); + $contacts_addressbook[$addressbook['id']]['displayname'] + = $addressbook['displayname']; + $contacts_addressbook[$addressbook['id']]['permissions'] + = isset($addressbook['permissions']) + ? $addressbook['permissions'] + : '0'; + } +} + +$contacts_alphabet = array(); + +// get next 50 for each addressbook. +foreach($ids as $id) { + if($id) { + $contacts_alphabet = array_merge( + $contacts_alphabet, + OC_Contacts_VCard::all($id, $start, 50) + ); + } +} +// Our new array for the contacts sorted by addressbook +if($contacts_alphabet) { + foreach($contacts_alphabet as $contact) { + // This should never execute. + if(!isset($contacts_addressbook[$contact['addressbookid']])) { + $contacts_addressbook[$contact['addressbookid']] = array( + 'contacts' => array('type' => 'book',) + ); + } + $display = trim($contact['fullname']); + if(!$display) { + $vcard = OC_Contacts_App::getContactVCard($contact['id']); + if(!is_null($vcard)) { + $struct = OC_Contacts_VCard::structureContact($vcard); + $display = isset($struct['EMAIL'][0]) + ? $struct['EMAIL'][0]['value'] + : '[UNKNOWN]'; + } + } + $contacts_addressbook[$contact['addressbookid']]['contacts'][] = array( + 'type' => 'contact', + 'id' => $contact['id'], + 'addressbookid' => $contact['addressbookid'], + 'displayname' => htmlspecialchars($display), + 'permissions' => + isset($contacts_addressbook[$contact['addressbookid']]['permissions']) + ? $contacts_addressbook[$contact['addressbookid']]['permissions'] + : '0', + ); + } +} +unset($contacts_alphabet); +uasort($contacts_addressbook, 'cmp'); + +OCP\JSON::success(array('data' => array('entries' => $contacts_addressbook))); + diff --git a/apps/contacts/ajax/contact/move.php b/apps/contacts/ajax/contact/move.php new file mode 100644 index 00000000000..053343c47ed --- /dev/null +++ b/apps/contacts/ajax/contact/move.php @@ -0,0 +1,29 @@ +<?php +/** +* @author Victor Dubiniuk +* Copyright (c) 2012 Victor Dubiniuk <victor.dubiniuk@gmail.com> +* Copyright (c) 2012 Thomas Tanghus <thomas@tanghus.net> +* This file is licensed under the Affero General Public License version 3 or +* later. +* See the COPYING-README file. +*/ + +OCP\JSON::checkLoggedIn(); +OCP\JSON::checkAppEnabled('contacts'); +OCP\JSON::callCheck(); + +$id = intval($_POST['id']); +$aid = intval($_POST['aid']); +$isaddressbook = isset($_POST['isaddressbook']) ? true: false; + +// Ownership checking +OC_Contacts_App::getAddressbook($aid); +try { + OC_Contacts_VCard::moveToAddressBook($aid, $id, $isaddressbook); +} catch (Exception $e) { + $msg = $e->getMessage(); + OCP\Util::writeLog('contacts', 'Error moving contacts "'.implode(',', $id).'" to addressbook "'.$aid.'"'.$msg, OCP\Util::ERROR); + OC_JSON::error(array('data' => array('message' => $msg,))); +} + +OC_JSON::success(array('data' => array('ids' => $id,)));
\ No newline at end of file diff --git a/apps/contacts/ajax/saveproperty.php b/apps/contacts/ajax/contact/saveproperty.php index a27b5489ce6..7ae183538b6 100644 --- a/apps/contacts/ajax/saveproperty.php +++ b/apps/contacts/ajax/contact/saveproperty.php @@ -20,37 +20,17 @@ * */ -// Init owncloud - +require_once __DIR__.'/../loghandler.php'; // Check if we are a user OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('contacts'); OCP\JSON::callCheck(); - -function bailOut($msg) { - OCP\JSON::error(array('data' => array('message' => $msg))); - OCP\Util::writeLog('contacts','ajax/saveproperty.php: '.$msg, OCP\Util::DEBUG); - exit(); -} -function debug($msg) { - OCP\Util::writeLog('contacts','ajax/saveproperty.php: '.$msg, OCP\Util::DEBUG); -} -// foreach ($_POST as $key=>$element) { -// debug('_POST: '.$key.'=>'.print_r($element, true)); -// } - $id = isset($_POST['id'])?$_POST['id']:null; $name = isset($_POST['name'])?$_POST['name']:null; $value = isset($_POST['value'])?$_POST['value']:null; $parameters = isset($_POST['parameters'])?$_POST['parameters']:null; $checksum = isset($_POST['checksum'])?$_POST['checksum']:null; -// if(!is_null($parameters)) { -// debug('parameters: '.count($parameters)); -// foreach($parameters as $key=>$val ) { -// debug('parameter: '.$key.'=>'.implode('/',$val)); -// } -// } if(!$name) { bailOut(OC_Contacts_App::$l10n->t('element name is not set.')); @@ -61,9 +41,11 @@ if(!$id) { if(!$checksum) { bailOut(OC_Contacts_App::$l10n->t('checksum is not set.')); } -if(is_array($value)){ +if(is_array($value)) { $value = array_map('strip_tags', $value); - ksort($value); // NOTE: Important, otherwise the compound value will be set in the order the fields appear in the form! + // NOTE: Important, otherwise the compound value will be + // set in the order the fields appear in the form! + ksort($value); //if($name == 'CATEGORIES') { // $value = OC_Contacts_VCard::escapeDelimiters($value, ','); //} else { @@ -76,19 +58,22 @@ if(is_array($value)){ $vcard = OC_Contacts_App::getContactVCard( $id ); $line = OC_Contacts_App::getPropertyLineByChecksum($vcard, $checksum); if(is_null($line)) { - bailOut(OC_Contacts_App::$l10n->t('Information about vCard is incorrect. Please reload the page: ').$checksum); + bailOut(OC_Contacts_App::$l10n->t( + 'Information about vCard is incorrect. Please reload the page: ').$checksum + ); } $element = $vcard->children[$line]->name; if($element != $name) { - bailOut(OC_Contacts_App::$l10n->t('Something went FUBAR. ').$name.' != '.$element); + bailOut(OC_Contacts_App::$l10n->t( + 'Something went FUBAR. ').$name.' != '.$element + ); } /* preprocessing value */ switch($element) { case 'BDAY': $date = New DateTime($value); - //$vcard->setDateTime('BDAY', $date, Sabre_VObject_Element_DateTime::DATE); $value = $date->format('Y-m-d'); break; case 'FN': @@ -103,6 +88,16 @@ switch($element) { case 'EMAIL': $value = strtolower($value); break; + case 'IMPP': + if(is_null($parameters) || !isset($parameters['X-SERVICE-TYPE'])) { + bailOut(OC_Contacts_App::$l10n->t('Missing IM parameter.')); + } + $impp = OC_Contacts_App::getIMOptions($parameters['X-SERVICE-TYPE']); + if(is_null($impp)) { + bailOut(OC_Contacts_App::$l10n->t('Unknown IM: '.$parameters['X-SERVICE-TYPE'])); + } + $value = $impp['protocol'] . ':' . $value; + break; } if(!$value) { @@ -112,28 +107,23 @@ if(!$value) { /* setting value */ switch($element) { case 'BDAY': - // I don't use setDateTime() because that formats it as YYYYMMDD instead of YYYY-MM-DD - // which is what the RFC recommends. + // I don't use setDateTime() because that formats it as YYYYMMDD instead + // of YYYY-MM-DD which is what the RFC recommends. $vcard->children[$line]->setValue($value); $vcard->children[$line]->parameters = array(); - $vcard->children[$line]->add(new Sabre_VObject_Parameter('VALUE', 'DATE')); + $vcard->children[$line]->add( + new Sabre_VObject_Parameter('VALUE', 'DATE') + ); debug('Setting value:'.$name.' '.$vcard->children[$line]); break; - case 'FN': - case 'N': - case 'ORG': - case 'NOTE': - case 'NICKNAME': - debug('Setting string:'.$name.' '.$value); - $vcard->setString($name, $value); - break; case 'CATEGORIES': debug('Setting string:'.$name.' '.$value); $vcard->children[$line]->setValue($value); break; case 'EMAIL': case 'TEL': - case 'ADR': // should I delete the property if empty or throw an error? + case 'ADR': + case 'IMPP': debug('Setting element: (EMAIL/TEL/ADR)'.$element); $vcard->children[$line]->setValue($value); $vcard->children[$line]->parameters = array(); @@ -141,22 +131,46 @@ if(!$value) { debug('Setting parameters: '.$parameters); foreach($parameters as $key => $parameter) { debug('Adding parameter: '.$key); - foreach($parameter as $val) { - debug('Adding parameter: '.$key.'=>'.$val); - $vcard->children[$line]->add(new Sabre_VObject_Parameter($key, strtoupper(strip_tags($val)))); + if(is_array($parameter)) { + foreach($parameter as $val) { + if(trim($val)) { + debug('Adding parameter: '.$key.'=>'.$val); + $vcard->children[$line]->add(new Sabre_VObject_Parameter( + $key, + strtoupper(strip_tags($val))) + ); + } + } + } else { + if(trim($parameter)) { + $vcard->children[$line]->add(new Sabre_VObject_Parameter( + $key, + strtoupper(strip_tags($parameter))) + ); + } } } } break; + default: + debug('Setting string:'.$name.' '.$value); + $vcard->setString($name, $value); + break; } // Do checksum and be happy $checksum = md5($vcard->children[$line]->serialize()); } //debug('New checksum: '.$checksum); -if(!OC_Contacts_VCard::edit($id,$vcard)) { - bailOut(OC_Contacts_App::$l10n->t('Error updating contact property.')); - exit(); +try { + OC_Contacts_VCard::edit($id, $vcard); +} catch(Exception $e) { + bailOut($e->getMessage()); } -OCP\JSON::success(array('data' => array( 'line' => $line, 'checksum' => $checksum, 'oldchecksum' => $_POST['checksum'] ))); +OCP\JSON::success(array('data' => array( + 'line' => $line, + 'checksum' => $checksum, + 'oldchecksum' => $_POST['checksum'], + 'lastmodified' => OC_Contacts_App::lastModified($vcard)->format('U'), +))); diff --git a/apps/contacts/ajax/contacts.php b/apps/contacts/ajax/contacts.php deleted file mode 100644 index 45c54f90bce..00000000000 --- a/apps/contacts/ajax/contacts.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php -/** - * Copyright (c) 2011 Thomas Tanghus <thomas@tanghus.net> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - - -OCP\JSON::checkLoggedIn(); -OCP\JSON::checkAppEnabled('contacts'); - -$ids = OC_Contacts_Addressbook::activeIds(OCP\USER::getUser()); -$allcontacts = OC_Contacts_VCard::all($ids); -$contacts = array(); -foreach($allcontacts as $contact) { // try to conserve some memory - $contacts[] = array('id' => $contact['id'], 'addressbookid' => $contact['addressbookid'], 'fullname' => $contact['fullname']); -} -unset($allcontacts); -$addressbooks = OC_Contacts_Addressbook::active(OCP\USER::getUser()); - -$tmpl = new OCP\Template("contacts", "part.contacts"); -$tmpl->assign('contacts', $contacts); -$page = $tmpl->fetchPage(); - -OCP\JSON::success(array('data' => array( 'page' => $page ))); -?> diff --git a/apps/contacts/ajax/createaddressbook.php b/apps/contacts/ajax/createaddressbook.php deleted file mode 100644 index 616766bb1a0..00000000000 --- a/apps/contacts/ajax/createaddressbook.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php -/** - * Copyright (c) 2011-2012 Thomas Tanghus <thomas@tanghus.net> - * Copyright (c) 2011 Bart Visscher <bartv@thisnet.nl> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - - -// Check if we are a user -OCP\JSON::checkLoggedIn(); -OCP\JSON::checkAppEnabled('contacts'); -OCP\JSON::callCheck(); - -$userid = OCP\USER::getUser(); -$name = trim(strip_tags($_POST['name'])); -if(!$name) { - OCP\JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('Cannot add addressbook with an empty name.')))); - OCP\Util::writeLog('contacts','ajax/createaddressbook.php: Cannot add addressbook with an empty name: '.strip_tags($_POST['name']), OCP\Util::ERROR); - exit(); -} -$bookid = OC_Contacts_Addressbook::add($userid, $name, null); -if(!$bookid) { - OCP\JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('Error adding addressbook.')))); - OCP\Util::writeLog('contacts','ajax/createaddressbook.php: Error adding addressbook: '.$_POST['name'], OCP\Util::ERROR); - exit(); -} - -if(!OC_Contacts_Addressbook::setActive($bookid, 1)) { - OCP\JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('Error activating addressbook.')))); - OCP\Util::writeLog('contacts','ajax/createaddressbook.php: Error activating addressbook: '.$bookid, OCP\Util::ERROR); - //exit(); -} -$addressbook = OC_Contacts_App::getAddressbook($bookid); -$tmpl = new OCP\Template('contacts', 'part.chooseaddressbook.rowfields'); -$tmpl->assign('addressbook', $addressbook); -OCP\JSON::success(array( - 'page' => $tmpl->fetchPage(), - 'addressbook' => $addressbook, -)); diff --git a/apps/contacts/ajax/cropphoto.php b/apps/contacts/ajax/cropphoto.php index 7006c6fc1ff..eb9f1fcdb5d 100644 --- a/apps/contacts/ajax/cropphoto.php +++ b/apps/contacts/ajax/cropphoto.php @@ -20,19 +20,15 @@ * */ -// Init owncloud - - // Check if we are a user OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('contacts'); -$tmp_path = strip_tags($_GET['tmp_path']); -$requesttoken = strip_tags($_GET['requesttoken']); +$tmpkey = $_GET['tmpkey']; +$requesttoken = $_GET['requesttoken']; $id = $_GET['id']; -OCP\Util::writeLog('contacts','ajax/cropphoto.php: tmp_path: '.$tmp_path.', exists: '.file_exists($tmp_path), OCP\Util::DEBUG); $tmpl = new OCP\Template("contacts", "part.cropphoto"); -$tmpl->assign('tmp_path', $tmp_path); +$tmpl->assign('tmpkey', $tmpkey); $tmpl->assign('id', $id); $tmpl->assign('requesttoken', $requesttoken); $page = $tmpl->fetchPage(); diff --git a/apps/contacts/ajax/currentphoto.php b/apps/contacts/ajax/currentphoto.php index d0654b17d64..96080e661ef 100644 --- a/apps/contacts/ajax/currentphoto.php +++ b/apps/contacts/ajax/currentphoto.php @@ -19,43 +19,30 @@ * License along with this library. If not, see <http://www.gnu.org/licenses/>. * */ -// Init owncloud -//require_once('../../../lib/base.php'); -// Check if we are a user // Firefox and Konqueror tries to download application/json for me. --Arthur OCP\JSON::setContentTypeHeader('text/plain'); OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('contacts'); -function bailOut($msg) { - OCP\JSON::error(array('data' => array('message' => $msg))); - OCP\Util::writeLog('contacts','ajax/currentphoto.php: '.$msg, OCP\Util::ERROR); - exit(); -} -function debug($msg) { - OCP\Util::writeLog('contacts','ajax/currentphoto.php: '.$msg, OCP\Util::DEBUG); -} +require_once 'loghandler.php'; if (!isset($_GET['id'])) { bailOut(OC_Contacts_App::$l10n->t('No contact ID was submitted.')); } -$tmpfname = tempnam(get_temp_dir(), "occOrig"); $contact = OC_Contacts_App::getContactVCard($_GET['id']); -$image = new OC_Image(); -if(!$image) { - bailOut(OC_Contacts_App::$l10n->t('Error loading image.')); -} // invalid vcard if( is_null($contact)) { bailOut(OC_Contacts_App::$l10n->t('Error reading contact photo.')); } else { + $image = new OC_Image(); if(!$image->loadFromBase64($contact->getAsString('PHOTO'))) { $image->loadFromBase64($contact->getAsString('LOGO')); } if($image->valid()) { - if($image->save($tmpfname)) { - OCP\JSON::success(array('data' => array('id'=>$_GET['id'], 'tmp'=>$tmpfname))); + $tmpkey = 'contact-photo-'.$contact->getAsString('UID'); + if(OC_Cache::set($tmpkey, $image->data(), 600)) { + OCP\JSON::success(array('data' => array('id'=>$_GET['id'], 'tmp'=>$tmpkey))); exit(); } else { bailOut(OC_Contacts_App::$l10n->t('Error saving temporary file.')); @@ -64,5 +51,3 @@ if( is_null($contact)) { bailOut(OC_Contacts_App::$l10n->t('The loading photo is not valid.')); } } - -?> diff --git a/apps/contacts/ajax/editaddress.php b/apps/contacts/ajax/editaddress.php index 9fb35a0b5f7..b5e4b72ed57 100644 --- a/apps/contacts/ajax/editaddress.php +++ b/apps/contacts/ajax/editaddress.php @@ -20,12 +20,22 @@ if($checksum) { $line = OC_Contacts_App::getPropertyLineByChecksum($vcard, $checksum); $element = $vcard->children[$line]; $adr = OC_Contacts_VCard::structureProperty($element); - $tmpl->assign('adr',$adr); + $types = array(); + if(isset($adr['parameters']['TYPE'])) { + if(is_array($adr['parameters']['TYPE'])) { + $types = array_map('htmlspecialchars', $adr['parameters']['TYPE']); + $types = array_map('strtoupper', $types); + } else { + $types = array(strtoupper(htmlspecialchars($adr['parameters']['TYPE']))); + } + } + $tmpl->assign('types', $types, false); + $adr = array_map('htmlspecialchars', $adr['value']); + $tmpl->assign('adr', $adr, false); } -$tmpl->assign('id',$id); -$tmpl->assign('adr_types',$adr_types); +$tmpl->assign('id', $id); +$tmpl->assign('adr_types', $adr_types); -$tmpl->printpage(); - -?> +$page = $tmpl->fetchPage(); +OCP\JSON::success(array('data' => array('page'=>$page, 'checksum'=>$checksum))); diff --git a/apps/contacts/ajax/editaddressbook.php b/apps/contacts/ajax/editaddressbook.php deleted file mode 100644 index 7a9b757ae0d..00000000000 --- a/apps/contacts/ajax/editaddressbook.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php -/** - * Copyright (c) 2011 Bart Visscher <bartv@thisnet.nl> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - - -OCP\JSON::checkLoggedIn(); -OCP\JSON::checkAppEnabled('contacts'); -$addressbook = OC_Contacts_App::getAddressbook($_GET['bookid']); -$tmpl = new OCP\Template("contacts", "part.editaddressbook"); -$tmpl->assign('new', false); -$tmpl->assign('addressbook', $addressbook); -$tmpl->printPage(); -?> diff --git a/apps/contacts/ajax/editname.php b/apps/contacts/ajax/editname.php index dc64eeb5101..eb55634011d 100644 --- a/apps/contacts/ajax/editname.php +++ b/apps/contacts/ajax/editname.php @@ -9,19 +9,12 @@ OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('contacts'); -function bailOut($msg) { - OCP\JSON::error(array('data' => array('message' => $msg))); - OCP\Util::writeLog('contacts','ajax/editname.php: '.$msg, OCP\Util::DEBUG); - exit(); -} -function debug($msg) { - OCP\Util::writeLog('contacts','ajax/editname.php: '.$msg, OCP\Util::DEBUG); -} +require_once 'loghandler.php'; $tmpl = new OCP\Template("contacts", "part.edit_name_dialog"); $id = isset($_GET['id'])?$_GET['id']:''; -debug('id: '.$id); + if($id) { $vcard = OC_Contacts_App::getContactVCard($id); $name = array('', '', '', '', ''); @@ -31,13 +24,11 @@ if($id) { $name = OC_Contacts_VCard::structureProperty($property); } } - $tmpl->assign('name',$name); - $tmpl->assign('id',$id); + $name = array_map('htmlspecialchars', $name['value']); + $tmpl->assign('name', $name, false); + $tmpl->assign('id', $id, false); } else { bailOut(OC_Contacts_App::$l10n->t('Contact ID is missing.')); - //$addressbooks = OC_Contacts_Addressbook::active(OCP\USER::getUser()); - //$tmpl->assign('addressbooks', $addressbooks); } -$tmpl->printpage(); - -?> +$page = $tmpl->fetchPage(); +OCP\JSON::success(array('data' => array('page'=>$page))); diff --git a/apps/contacts/ajax/importaddressbook.php b/apps/contacts/ajax/importaddressbook.php deleted file mode 100644 index 3c01e24a185..00000000000 --- a/apps/contacts/ajax/importaddressbook.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php -/** - * Copyright (c) 2012 Georg Ehrke <ownclouddev at georgswebsite dot de> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -OCP\JSON::checkLoggedIn(); -OCP\App::checkAppEnabled('contacts'); -$upload_max_filesize = OCP\Util::computerFileSize(ini_get('upload_max_filesize')); -$post_max_size = OCP\Util::computerFileSize(ini_get('post_max_size')); -$maxUploadFilesize = min($upload_max_filesize, $post_max_size); - -$freeSpace=OC_Filesystem::free_space('/'); -$freeSpace=max($freeSpace,0); -$maxUploadFilesize = min($maxUploadFilesize ,$freeSpace); - -$tmpl = new OCP\Template('contacts', 'part.importaddressbook'); -$tmpl->assign('uploadMaxFilesize', $maxUploadFilesize); -$tmpl->assign('requesttoken', $_SERVER['HTTP_REQUESTTOKEN']); -$tmpl->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize)); -$tmpl->printpage(); -?> diff --git a/apps/contacts/ajax/importdialog.php b/apps/contacts/ajax/importdialog.php index 5f8805a6106..691522538fb 100644 --- a/apps/contacts/ajax/importdialog.php +++ b/apps/contacts/ajax/importdialog.php @@ -13,4 +13,3 @@ $tmpl = new OCP\Template('contacts', 'part.import'); $tmpl->assign('path', $_POST['path']); $tmpl->assign('filename', $_POST['filename']); $tmpl->printpage(); -?> diff --git a/apps/contacts/ajax/loadcard.php b/apps/contacts/ajax/loadcard.php index 09b6bbe0cf9..82501ffd2ff 100644 --- a/apps/contacts/ajax/loadcard.php +++ b/apps/contacts/ajax/loadcard.php @@ -20,20 +20,6 @@ * */ -// Init owncloud - -function bailOut($msg) { - OCP\JSON::error(array('data' => array('message' => $msg))); - OCP\Util::writeLog('contacts','ajax/loadcard.php: '.$msg, OCP\Util::DEBUG); - exit(); -} -function debug($msg) { - OCP\Util::writeLog('contacts','ajax/loadcard.php: '.$msg, OCP\Util::DEBUG); -} -// foreach ($_POST as $key=>$element) { -// debug('_POST: '.$key.'=>'.$element); -// } - // Check if we are a user OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('contacts'); @@ -41,22 +27,32 @@ OCP\JSON::checkAppEnabled('contacts'); $upload_max_filesize = OCP\Util::computerFileSize(ini_get('upload_max_filesize')); $post_max_size = OCP\Util::computerFileSize(ini_get('post_max_size')); $maxUploadFilesize = min($upload_max_filesize, $post_max_size); +$requesttoken = $_GET['requesttoken']; $freeSpace=OC_Filesystem::free_space('/'); -$freeSpace=max($freeSpace,0); -$maxUploadFilesize = min($maxUploadFilesize ,$freeSpace); +$freeSpace=max($freeSpace, 0); +$maxUploadFilesize = min($maxUploadFilesize, $freeSpace); $adr_types = OC_Contacts_App::getTypesOfProperty('ADR'); $phone_types = OC_Contacts_App::getTypesOfProperty('TEL'); $email_types = OC_Contacts_App::getTypesOfProperty('EMAIL'); +$impp_types = OC_Contacts_App::getTypesOfProperty('IMPP'); +$ims = OC_Contacts_App::getIMOptions(); +$im_protocols = array(); +foreach($ims as $name => $values) { + $im_protocols[$name] = $values['displayname']; +} -$tmpl = new OCP\Template('contacts','part.contact'); +$tmpl = new OCP\Template('contacts', 'part.contact'); $tmpl->assign('uploadMaxFilesize', $maxUploadFilesize); $tmpl->assign('requesttoken', $_SERVER['HTTP_REQUESTTOKEN']); $tmpl->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize)); -$tmpl->assign('adr_types',$adr_types); -$tmpl->assign('phone_types',$phone_types); -$tmpl->assign('email_types',$email_types); -$tmpl->assign('id',''); +$tmpl->assign('adr_types', $adr_types); +$tmpl->assign('phone_types', $phone_types); +$tmpl->assign('email_types', $email_types); +$tmpl->assign('impp_types', $impp_types, false); +$tmpl->assign('im_protocols', $im_protocols, false); +$tmpl->assign('requesttoken', $requesttoken); +$tmpl->assign('id', ''); $page = $tmpl->fetchPage(); OCP\JSON::success(array('data' => array( 'page' => $page ))); diff --git a/apps/contacts/ajax/loadintro.php b/apps/contacts/ajax/loadintro.php index 6e8fcc4b049..1da08950ca0 100644 --- a/apps/contacts/ajax/loadintro.php +++ b/apps/contacts/ajax/loadintro.php @@ -25,7 +25,7 @@ OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('contacts'); -$tmpl = new OCP\Template('contacts','part.no_contacts'); +$tmpl = new OCP\Template('contacts', 'part.no_contacts'); $page = $tmpl->fetchPage(); OCP\JSON::success(array('data' => array( 'page' => $page ))); diff --git a/apps/contacts/ajax/loadphoto.php b/apps/contacts/ajax/loadphoto.php deleted file mode 100644 index ef429e82891..00000000000 --- a/apps/contacts/ajax/loadphoto.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php -/** - * ownCloud - Addressbook - * - * @author Thomas Tanghus - * @copyright 2012 Thomas Tanghus <thomas@tanghus.net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - */ -// Init owncloud - -// Check if we are a user -OCP\JSON::checkLoggedIn(); -OCP\JSON::checkAppEnabled('contacts'); - -// foreach ($_POST as $key=>$element) { -// OCP\Util::writeLog('contacts','ajax/savecrop.php: '.$key.'=>'.$element, OCP\Util::DEBUG); -// } - -function bailOut($msg) { - OCP\JSON::error(array('data' => array('message' => $msg))); - OCP\Util::writeLog('contacts','ajax/loadphoto.php: '.$msg, OCP\Util::DEBUG); - exit(); -} - -$image = null; - -$id = isset($_GET['id']) ? $_GET['id'] : ''; -$refresh = isset($_GET['refresh']) ? true : false; - -if($id == '') { - bailOut(OC_Contacts_App::$l10n->t('Missing contact id.')); -} - -$checksum = ''; -$vcard = OC_Contacts_App::getContactVCard( $id ); -foreach($vcard->children as $property){ - if($property->name == 'PHOTO') { - $checksum = md5($property->serialize()); - break; - } -} - -$tmpl = new OCP\Template("contacts", "part.contactphoto"); -$tmpl->assign('id', $id); -if($refresh) { - $tmpl->assign('refresh', 1); -} -$page = $tmpl->fetchPage(); -OCP\JSON::success(array('data' => array('page'=>$page, 'checksum'=>$checksum))); -?> diff --git a/apps/contacts/ajax/loghandler.php b/apps/contacts/ajax/loghandler.php new file mode 100644 index 00000000000..be4b98c1112 --- /dev/null +++ b/apps/contacts/ajax/loghandler.php @@ -0,0 +1,44 @@ +<?php +/** + * ownCloud - Addressbook + * + * @author Thomas Tanghus + * @copyright 2012 Thomas Tanghus <thomas@tanghus.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +function bailOut($msg, $tracelevel=1, $debuglevel=OCP\Util::ERROR) +{ + OCP\JSON::error(array('data' => array('message' => $msg))); + debug($msg, $tracelevel, $debuglevel); + exit(); +} + +function debug($msg, $tracelevel=0, $debuglevel=OCP\Util::DEBUG) +{ + if(PHP_VERSION >= "5.4") { + $call = debug_backtrace(false, $tracelevel+1); + } else { + $call = debug_backtrace(false); + } + error_log('trace: '.print_r($call, true)); + $call = $call[$tracelevel]; + if($debuglevel !== false) { + OCP\Util::writeLog('contacts', + $call['file'].'. Line: '.$call['line'].': '.$msg, + $debuglevel); + } +} diff --git a/apps/contacts/ajax/oc_photo.php b/apps/contacts/ajax/oc_photo.php index 903d2aedfbe..fe37b530f82 100644 --- a/apps/contacts/ajax/oc_photo.php +++ b/apps/contacts/ajax/oc_photo.php @@ -20,19 +20,9 @@ * */ // Check if we are a user -// Firefox and Konqueror tries to download application/json for me. --Arthur -OCP\JSON::setContentTypeHeader('text/plain'); OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('contacts'); -OCP\JSON::callCheck(); -function bailOut($msg) { - OCP\JSON::error(array('data' => array('message' => $msg))); - OCP\Util::writeLog('contacts','ajax/oc_photo.php: '.$msg, OCP\Util::ERROR); - exit(); -} -function debug($msg) { - OCP\Util::writeLog('contacts','ajax/oc_photo.php: '.$msg, OCP\Util::DEBUG); -} +require_once 'loghandler.php'; if(!isset($_GET['id'])) { bailOut(OC_Contacts_App::$l10n->t('No contact ID was submitted.')); @@ -43,31 +33,30 @@ if(!isset($_GET['path'])) { } $localpath = OC_Filesystem::getLocalFile($_GET['path']); -$tmpfname = tempnam(get_temp_dir(), "occOrig"); +$tmpkey = 'contact-photo-'.$_GET['id']; if(!file_exists($localpath)) { bailOut(OC_Contacts_App::$l10n->t('File doesn\'t exist:').$localpath); } -file_put_contents($tmpfname, file_get_contents($localpath)); $image = new OC_Image(); if(!$image) { bailOut(OC_Contacts_App::$l10n->t('Error loading image.')); } -if(!$image->loadFromFile($tmpfname)) { +if(!$image->loadFromFile($localpath)) { bailOut(OC_Contacts_App::$l10n->t('Error loading image.')); } if($image->width() > 400 || $image->height() > 400) { $image->resize(400); // Prettier resizing than with browser and saves bandwidth. } if(!$image->fixOrientation()) { // No fatal error so we don't bail out. - debug('Couldn\'t save correct image orientation: '.$tmpfname); + OCP\Util::writeLog('contacts', + 'ajax/oc_photo.php: Couldn\'t save correct image orientation: '.$localpath, + OCP\Util::DEBUG); } -if($image->save($tmpfname)) { - OCP\JSON::success(array('data' => array('id'=>$_GET['id'], 'tmp'=>$tmpfname))); +if(OC_Cache::set($tmpkey, $image->data(), 600)) { + OCP\JSON::success(array('data' => array('id'=>$_GET['id'], 'tmp'=>$tmpkey))); exit(); } else { - bailOut('Couldn\'t save temporary image: '.$tmpfname); + bailOut('Couldn\'t save temporary image: '.$tmpkey); } - -?> diff --git a/apps/contacts/ajax/savecrop.php b/apps/contacts/ajax/savecrop.php index a5517c5d843..2483d0f7128 100644 --- a/apps/contacts/ajax/savecrop.php +++ b/apps/contacts/ajax/savecrop.php @@ -18,23 +18,16 @@ * You should have received a copy of the GNU Affero General Public * License along with this library. If not, see <http://www.gnu.org/licenses/>. * - * TODO: Translatable strings. - * Remember to delete tmp file at some point. */ - -function bailOut($msg) { - OCP\JSON::error(array('data' => array('message' => $msg))); - OCP\Util::writeLog('contacts','ajax/savecrop.php: '.$msg, OCP\Util::DEBUG); - exit(); -} - // Check if we are a user OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('contacts'); OCP\JSON::callCheck(); // Firefox and Konqueror tries to download application/json for me. --Arthur -OCP\JSON::setContentTypeHeader('text/plain'); +OCP\JSON::setContentTypeHeader('text/plain; charset=utf-8'); + +require_once 'loghandler.php'; $image = null; @@ -44,88 +37,85 @@ $y1 = (isset($_POST['y1']) && $_POST['y1']) ? $_POST['y1'] : 0; //$y2 = isset($_POST['y2']) ? $_POST['y2'] : -1; $w = (isset($_POST['w']) && $_POST['w']) ? $_POST['w'] : -1; $h = (isset($_POST['h']) && $_POST['h']) ? $_POST['h'] : -1; -$tmp_path = isset($_POST['tmp_path']) ? $_POST['tmp_path'] : ''; +$tmpkey = isset($_POST['tmpkey']) ? $_POST['tmpkey'] : ''; $id = isset($_POST['id']) ? $_POST['id'] : ''; -if($tmp_path == '') { - bailOut('Missing path to temporary file.'); +if($tmpkey == '') { + bailOut('Missing key to temporary file.'); } if($id == '') { bailOut('Missing contact id.'); } -OCP\Util::writeLog('contacts','savecrop.php: files: '.$tmp_path.' exists: '.file_exists($tmp_path), OCP\Util::DEBUG); +OCP\Util::writeLog('contacts', 'savecrop.php: key: '.$tmpkey, OCP\Util::DEBUG); -if(file_exists($tmp_path)) { +$data = OC_Cache::get($tmpkey); +if($data) { $image = new OC_Image(); - if($image->loadFromFile($tmp_path)) { + if($image->loadFromdata($data)) { $w = ($w != -1 ? $w : $image->width()); $h = ($h != -1 ? $h : $image->height()); - OCP\Util::writeLog('contacts','savecrop.php, x: '.$x1.' y: '.$y1.' w: '.$w.' h: '.$h, OCP\Util::DEBUG); + OCP\Util::writeLog('contacts', + 'savecrop.php, x: '.$x1.' y: '.$y1.' w: '.$w.' h: '.$h, + OCP\Util::DEBUG); if($image->crop($x1, $y1, $w, $h)) { - if(($image->width() <= 200 && $image->height() <= 200) || $image->resize(200)) { - $tmpfname = tempnam(get_temp_dir(), "occCropped"); // create a new file because of caching issues. - if($image->save($tmpfname)) { - unlink($tmp_path); - $card = OC_Contacts_App::getContactVCard($id); - if(!$card) { - unlink($tmpfname); - bailOut('Error getting contact object.'); - } - if($card->__isset('PHOTO')) { - OCP\Util::writeLog('contacts','savecrop.php: PHOTO property exists.', OCP\Util::DEBUG); - $property = $card->__get('PHOTO'); - if(!$property) { - unlink($tmpfname); - bailOut('Error getting PHOTO property.'); - } - $property->setValue($image->__toString()); - $property->parameters[] = new Sabre_VObject_Parameter('ENCODING', 'b'); - $property->parameters[] = new Sabre_VObject_Parameter('TYPE', $image->mimeType()); - $card->__set('PHOTO', $property); - } else { - OCP\Util::writeLog('contacts','savecrop.php: files: Adding PHOTO property.', OCP\Util::DEBUG); - $card->addProperty('PHOTO', $image->__toString(), array('ENCODING' => 'b', 'TYPE' => $image->mimeType())); - } - $now = new DateTime; - $card->setString('REV', $now->format(DateTime::W3C)); - if(!OC_Contacts_VCard::edit($id,$card)) { - bailOut('Error saving contact.'); + if(($image->width() <= 200 && $image->height() <= 200) + || $image->resize(200)) { + $vcard = OC_Contacts_App::getContactVCard($id); + if(!$vcard) { + OC_Cache::remove($tmpkey); + bailOut(OC_Contacts_App::$l10n + ->t('Error getting contact object.')); + } + if($vcard->__isset('PHOTO')) { + OCP\Util::writeLog('contacts', + 'savecrop.php: PHOTO property exists.', + OCP\Util::DEBUG); + $property = $vcard->__get('PHOTO'); + if(!$property) { + OC_Cache::remove($tmpkey); + bailOut(OC_Contacts_App::$l10n + ->t('Error getting PHOTO property.')); } - unlink($tmpfname); - //$result=array( "status" => "success", 'mime'=>$image->mimeType(), 'tmp'=>$tmp_path); - $tmpl = new OCP\Template("contacts", "part.contactphoto"); - $tmpl->assign('tmp_path', $tmpfname); - $tmpl->assign('mime', $image->mimeType()); - $tmpl->assign('id', $id); - $tmpl->assign('refresh', true); - $tmpl->assign('width', $image->width()); - $tmpl->assign('height', $image->height()); - $page = $tmpl->fetchPage(); - OCP\JSON::success(array('data' => array('page'=>$page, 'tmp'=>$tmpfname))); - exit(); + $property->setValue($image->__toString()); + $property->parameters[] + = new Sabre_VObject_Parameter('ENCODING', 'b'); + $property->parameters[] + = new Sabre_VObject_Parameter('TYPE', $image->mimeType()); + $vcard->__set('PHOTO', $property); } else { - if(file_exists($tmpfname)) { - unlink($tmpfname); - } - bailOut('Error saving temporary image'); + OCP\Util::writeLog('contacts', + 'savecrop.php: files: Adding PHOTO property.', + OCP\Util::DEBUG); + $vcard->addProperty('PHOTO', + $image->__toString(), array('ENCODING' => 'b', + 'TYPE' => $image->mimeType())); + } + $now = new DateTime; + $vcard->setString('REV', $now->format(DateTime::W3C)); + if(!OC_Contacts_VCard::edit($id, $vcard)) { + bailOut(OC_Contacts_App::$l10n->t('Error saving contact.')); } + OCP\JSON::success(array( + 'data' => array( + 'id' => $id, + 'width' => $image->width(), + 'height' => $image->height(), + 'lastmodified' => OC_Contacts_App::lastModified($vcard)->format('U') + ) + )); } else { - bailOut('Error resizing image'); + bailOut(OC_Contacts_App::$l10n->t('Error resizing image')); } } else { - bailOut('Error cropping image'); + bailOut(OC_Contacts_App::$l10n->t('Error cropping image')); } } else { - bailOut('Error creating temporary image'); + bailOut(OC_Contacts_App::$l10n->t('Error creating temporary image')); } } else { - bailOut('Error finding image: '.$tmp_path); -} - -if($tmp_path != '' && file_exists($tmp_path)) { - unlink($tmp_path); + bailOut(OC_Contacts_App::$l10n->t('Error finding image: ').$tmpkey); } -?> +OC_Cache::remove($tmpkey); diff --git a/apps/contacts/ajax/updateaddressbook.php b/apps/contacts/ajax/updateaddressbook.php deleted file mode 100644 index 68fe8f81211..00000000000 --- a/apps/contacts/ajax/updateaddressbook.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php -/** - * Copyright (c) 2011-2012 Thomas Tanghus <thomas@tanghus.net> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - - - -// Check if we are a user -OCP\JSON::checkLoggedIn(); -OCP\JSON::checkAppEnabled('contacts'); -OCP\JSON::callCheck(); - -$bookid = $_POST['id']; -OC_Contacts_App::getAddressbook($bookid); // is owner access check - -$name = trim(strip_tags($_POST['name'])); -if(!$name) { - OCP\JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('Cannot update addressbook with an empty name.')))); - OCP\Util::writeLog('contacts','ajax/updateaddressbook.php: Cannot update addressbook with an empty name: '.strip_tags($_POST['name']), OCP\Util::ERROR); - exit(); -} - -if(!OC_Contacts_Addressbook::edit($bookid, $name, null)) { - OCP\JSON::error(array('data' => array('message' => $l->t('Error updating addressbook.')))); - OCP\Util::writeLog('contacts','ajax/updateaddressbook.php: Error adding addressbook: ', OCP\Util::ERROR); - //exit(); -} - -if(!OC_Contacts_Addressbook::setActive($bookid, $_POST['active'])) { - OCP\JSON::error(array('data' => array('message' => $l->t('Error (de)activating addressbook.')))); - OCP\Util::writeLog('contacts','ajax/updateaddressbook.php: Error (de)activating addressbook: '.$bookid, OCP\Util::ERROR); - //exit(); -} - -$addressbook = OC_Contacts_App::getAddressbook($bookid); -$tmpl = new OCP\Template('contacts', 'part.chooseaddressbook.rowfields'); -$tmpl->assign('addressbook', $addressbook); -OCP\JSON::success(array( - 'page' => $tmpl->fetchPage(), - 'addressbook' => $addressbook, -)); diff --git a/apps/contacts/ajax/uploadimport.php b/apps/contacts/ajax/uploadimport.php index 4c3f5eadf08..87032b731a5 100644 --- a/apps/contacts/ajax/uploadimport.php +++ b/apps/contacts/ajax/uploadimport.php @@ -24,56 +24,57 @@ OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('contacts'); OCP\JSON::callCheck(); -function bailOut($msg) { - OCP\JSON::error(array('data' => array('message' => $msg))); - OCP\Util::writeLog('contacts','ajax/uploadimport.php: '.$msg, OCP\Util::ERROR); - exit(); -} +require_once 'loghandler.php'; + +$l10n = OC_Contacts_App::$l10n; $view = OCP\Files::getStorage('contacts'); +if(!$view->file_exists('imports')) { + $view->mkdir('imports'); +} $tmpfile = md5(rand()); // If it is a Drag'n'Drop transfer it's handled here. $fn = (isset($_SERVER['HTTP_X_FILE_NAME']) ? $_SERVER['HTTP_X_FILE_NAME'] : false); if($fn) { - if($view->file_put_contents('/'.$tmpfile, file_get_contents('php://input'))) { - OCP\JSON::success(array('data' => array('path'=>'', 'file'=>$tmpfile))); + if($view->file_put_contents('/imports/'.$fn, file_get_contents('php://input'))) { + OCP\JSON::success(array('data' => array('file'=>$tmpfile, 'name'=>$fn))); exit(); } else { - bailOut(OC_Contacts_App::$l10n->t('Error uploading contacts to storage.')); + bailOut($l10n->t('Error uploading contacts to storage.')); } } // File input transfers are handled here if (!isset($_FILES['importfile'])) { - OCP\Util::writeLog('contacts','ajax/uploadphoto.php: No file was uploaded. Unknown error.', OCP\Util::DEBUG); - OCP\JSON::error(array('data' => array( 'message' => 'No file was uploaded. Unknown error' ))); + OCP\Util::writeLog('contacts', + 'ajax/uploadphoto.php: No file was uploaded. Unknown error.', + OCP\Util::DEBUG); + OCP\JSON::error(array(' + data' => array( + 'message' => 'No file was uploaded. Unknown error' ))); exit(); } $error = $_FILES['importfile']['error']; if($error !== UPLOAD_ERR_OK) { $errors = array( - 0=>OC_Contacts_App::$l10n->t("There is no error, the file uploaded with success"), - 1=>OC_Contacts_App::$l10n->t("The uploaded file exceeds the upload_max_filesize directive in php.ini").ini_get('upload_max_filesize'), - 2=>OC_Contacts_App::$l10n->t("The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form"), - 3=>OC_Contacts_App::$l10n->t("The uploaded file was only partially uploaded"), - 4=>OC_Contacts_App::$l10n->t("No file was uploaded"), - 6=>OC_Contacts_App::$l10n->t("Missing a temporary folder") + 0=>$l10n->t("There is no error, the file uploaded with success"), + 1=>$l10n->t("The uploaded file exceeds the upload_max_filesize directive in php.ini").ini_get('upload_max_filesize'), + 2=>$l10n->t("The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form"), + 3=>$l10n->t("The uploaded file was only partially uploaded"), + 4=>$l10n->t("No file was uploaded"), + 6=>$l10n->t("Missing a temporary folder") ); bailOut($errors[$error]); } $file=$_FILES['importfile']; -$tmpfname = tempnam(get_temp_dir(), "occOrig"); if(file_exists($file['tmp_name'])) { - if($view->file_put_contents('/'.$tmpfile, file_get_contents($file['tmp_name']))) { - OCP\JSON::success(array('data' => array('path'=>'', 'file'=>$tmpfile))); + if($view->file_put_contents('/imports/'.$file['name'], file_get_contents($file['tmp_name']))) { + OCP\JSON::success(array('data' => array('file'=>$file['name'], 'name'=>$file['name']))); } else { - bailOut(OC_Contacts_App::$l10n->t('Error uploading contacts to storage.')); + bailOut($l10n->t('Error uploading contacts to storage.')); } } else { bailOut('Temporary file: \''.$file['tmp_name'].'\' has gone AWOL?'); } - - -?> diff --git a/apps/contacts/ajax/uploadphoto.php b/apps/contacts/ajax/uploadphoto.php index 52e25e4a335..34830b291d2 100644 --- a/apps/contacts/ajax/uploadphoto.php +++ b/apps/contacts/ajax/uploadphoto.php @@ -19,96 +19,98 @@ * License along with this library. If not, see <http://www.gnu.org/licenses/>. * */ -function bailOut($msg) { - OCP\JSON::error(array('data' => array('message' => $msg))); - OCP\Util::writeLog('contacts','ajax/uploadphoto.php: '.$msg, OCP\Util::DEBUG); - exit(); -} -function debug($msg) { - OCP\Util::writeLog('contacts','ajax/uploadphoto.php: '.$msg, OCP\Util::DEBUG); -} -OCP\JSON::setContentTypeHeader('text/plain'); + +// Check if we are a user OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('contacts'); OCP\JSON::callCheck(); +// Firefox and Konqueror tries to download application/json for me. --Arthur +OCP\JSON::setContentTypeHeader('text/plain; charset=utf-8'); +require_once 'loghandler.php'; +$l10n = OC_Contacts_App::$l10n; // If it is a Drag'n'Drop transfer it's handled here. $fn = (isset($_SERVER['HTTP_X_FILE_NAME']) ? $_SERVER['HTTP_X_FILE_NAME'] : false); if ($fn) { - // AJAX call if (!isset($_GET['id'])) { - OCP\Util::writeLog('contacts','ajax/uploadphoto.php: No contact ID was submitted.', OCP\Util::DEBUG); - OCP\JSON::error(array('data' => array( 'message' => 'No contact ID was submitted.' ))); - exit(); + bailOut($l10n->t('No contact ID was submitted.')); } $id = $_GET['id']; - $tmpfname = tempnam(get_temp_dir(), 'occOrig'); - file_put_contents($tmpfname, file_get_contents('php://input')); + $tmpkey = 'contact-photo-'.md5($fn); + $data = file_get_contents('php://input'); $image = new OC_Image(); - if($image->loadFromFile($tmpfname)) { + sleep(1); // Apparently it needs time to load the data. + if($image->loadFromData($data)) { if($image->width() > 400 || $image->height() > 400) { $image->resize(400); // Prettier resizing than with browser and saves bandwidth. } if(!$image->fixOrientation()) { // No fatal error so we don't bail out. - debug('Couldn\'t save correct image orientation: '.$tmpfname); + debug('Couldn\'t save correct image orientation: '.$tmpkey); } - if($image->save($tmpfname)) { - OCP\JSON::success(array('data' => array('mime'=>$_SERVER['CONTENT_TYPE'], 'name'=>$fn, 'id'=>$id, 'tmp'=>$tmpfname))); + if(OC_Cache::set($tmpkey, $image->data(), 600)) { + OCP\JSON::success(array( + 'data' => array( + 'mime'=>$_SERVER['CONTENT_TYPE'], + 'name'=>$fn, + 'id'=>$id, + 'tmp'=>$tmpkey))); exit(); } else { - bailOut('Couldn\'t save temporary image: '.$tmpfname); + bailOut($l10n->t('Couldn\'t save temporary image: ').$tmpkey); } } else { - bailOut('Couldn\'t load temporary image: '.$file['tmp_name']); + bailOut($l10n->t('Couldn\'t load temporary image: ').$tmpkey); } } - +// Uploads from file dialog are handled here. if (!isset($_POST['id'])) { - OCP\Util::writeLog('contacts','ajax/uploadphoto.php: No contact ID was submitted.', OCP\Util::DEBUG); - OCP\JSON::error(array('data' => array( 'message' => 'No contact ID was submitted.' ))); - exit(); + bailOut($l10n->t('No contact ID was submitted.')); } if (!isset($_FILES['imagefile'])) { - OCP\Util::writeLog('contacts','ajax/uploadphoto.php: No file was uploaded. Unknown error.', OCP\Util::DEBUG); - OCP\JSON::error(array('data' => array( 'message' => 'No file was uploaded. Unknown error' ))); - exit(); + bailOut($l10n->t('No file was uploaded. Unknown error')); } + $error = $_FILES['imagefile']['error']; if($error !== UPLOAD_ERR_OK) { $errors = array( - 0=>OC_Contacts_App::$l10n->t("There is no error, the file uploaded with success"), - 1=>OC_Contacts_App::$l10n->t("The uploaded file exceeds the upload_max_filesize directive in php.ini").ini_get('upload_max_filesize'), - 2=>OC_Contacts_App::$l10n->t("The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form"), - 3=>OC_Contacts_App::$l10n->t("The uploaded file was only partially uploaded"), - 4=>OC_Contacts_App::$l10n->t("No file was uploaded"), - 6=>OC_Contacts_App::$l10n->t("Missing a temporary folder") + 0=>$l10n->t("There is no error, the file uploaded with success"), + 1=>$l10n->t("The uploaded file exceeds the upload_max_filesize directive in php.ini").ini_get('upload_max_filesize'), + 2=>$l10n->t("The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form"), + 3=>$l10n->t("The uploaded file was only partially uploaded"), + 4=>$l10n->t("No file was uploaded"), + 6=>$l10n->t("Missing a temporary folder") ); bailOut($errors[$error]); } $file=$_FILES['imagefile']; -$tmpfname = tempnam(get_temp_dir(), "occOrig"); if(file_exists($file['tmp_name'])) { + $tmpkey = 'contact-photo-'.md5(basename($file['tmp_name'])); $image = new OC_Image(); if($image->loadFromFile($file['tmp_name'])) { if($image->width() > 400 || $image->height() > 400) { $image->resize(400); // Prettier resizing than with browser and saves bandwidth. } if(!$image->fixOrientation()) { // No fatal error so we don't bail out. - debug('Couldn\'t save correct image orientation: '.$tmpfname); + debug('Couldn\'t save correct image orientation: '.$tmpkey); } - if($image->save($tmpfname)) { - OCP\JSON::success(array('data' => array('mime'=>$file['type'],'size'=>$file['size'],'name'=>$file['name'], 'id'=>$_POST['id'], 'tmp'=>$tmpfname))); + if(OC_Cache::set($tmpkey, $image->data(), 600)) { + OCP\JSON::success(array( + 'data' => array( + 'mime'=>$file['type'], + 'size'=>$file['size'], + 'name'=>$file['name'], + 'id'=>$_POST['id'], + 'tmp'=>$tmpkey, + ))); exit(); } else { - bailOut('Couldn\'t save temporary image: '.$tmpfname); + bailOut($l10n->t('Couldn\'t save temporary image: ').$tmpkey); } } else { - bailOut('Couldn\'t load temporary image: '.$file['tmp_name']); + bailOut($l10n->t('Couldn\'t load temporary image: ').$file['tmp_name']); } } else { bailOut('Temporary file: \''.$file['tmp_name'].'\' has gone AWOL?'); } - -?> diff --git a/apps/contacts/appinfo/app.php b/apps/contacts/appinfo/app.php index a298857d605..102c04705a4 100644 --- a/apps/contacts/appinfo/app.php +++ b/apps/contacts/appinfo/app.php @@ -3,18 +3,20 @@ OC::$CLASSPATH['OC_Contacts_App'] = 'apps/contacts/lib/app.php'; OC::$CLASSPATH['OC_Contacts_Addressbook'] = 'apps/contacts/lib/addressbook.php'; OC::$CLASSPATH['OC_Contacts_VCard'] = 'apps/contacts/lib/vcard.php'; OC::$CLASSPATH['OC_Contacts_Hooks'] = 'apps/contacts/lib/hooks.php'; -OC::$CLASSPATH['OC_Connector_Sabre_CardDAV'] = 'apps/contacts/lib/connector_sabre.php'; +OC::$CLASSPATH['OC_Share_Backend_Contact'] = 'apps/contacts/lib/share/contact.php'; +OC::$CLASSPATH['OC_Share_Backend_Addressbook'] = 'apps/contacts/lib/share/addressbook.php'; +OC::$CLASSPATH['OC_Connector_Sabre_CardDAV'] = 'apps/contacts/lib/sabre/backend.php'; +OC::$CLASSPATH['OC_Connector_Sabre_CardDAV_AddressBookRoot'] = 'apps/contacts/lib/sabre/addressbookroot.php'; +OC::$CLASSPATH['OC_Connector_Sabre_CardDAV_UserAddressBooks'] = 'apps/contacts/lib/sabre/useraddressbooks.php'; +OC::$CLASSPATH['OC_Connector_Sabre_CardDAV_AddressBook'] = 'apps/contacts/lib/sabre/addressbook.php'; +OC::$CLASSPATH['OC_Connector_Sabre_CardDAV_Card'] = 'apps/contacts/lib/sabre/card.php'; +OC::$CLASSPATH['OC_Connector_Sabre_CardDAV_VCFExportPlugin'] = 'apps/contacts/lib/sabre/vcfexportplugin.php'; OC::$CLASSPATH['OC_Search_Provider_Contacts'] = 'apps/contacts/lib/search.php'; OCP\Util::connectHook('OC_User', 'post_createUser', 'OC_Contacts_Hooks', 'createUser'); OCP\Util::connectHook('OC_User', 'post_deleteUser', 'OC_Contacts_Hooks', 'deleteUser'); OCP\Util::connectHook('OC_Calendar', 'getEvents', 'OC_Contacts_Hooks', 'getBirthdayEvents'); OCP\Util::connectHook('OC_Calendar', 'getSources', 'OC_Contacts_Hooks', 'getCalenderSources'); -OCP\App::register( array( - 'order' => 10, - 'id' => 'contacts', - 'name' => 'Contacts' )); - OCP\App::addNavigationEntry( array( 'id' => 'contacts_index', 'order' => 10, @@ -22,7 +24,8 @@ OCP\App::addNavigationEntry( array( 'icon' => OCP\Util::imagePath( 'settings', 'users.svg' ), 'name' => OC_L10N::get('contacts')->t('Contacts') )); - -OCP\App::registerPersonal('contacts','settings'); OCP\Util::addscript('contacts', 'loader'); OC_Search::registerProvider('OC_Search_Provider_Contacts'); +OCP\Share::registerBackend('contact', 'OC_Share_Backend_Contact'); +OCP\Share::registerBackend('addressbook', 'OC_Share_Backend_Addressbook', 'contact'); + diff --git a/apps/contacts/appinfo/database.xml b/apps/contacts/appinfo/database.xml index f83a04b8f47..1e2e097e49b 100644 --- a/apps/contacts/appinfo/database.xml +++ b/apps/contacts/appinfo/database.xml @@ -44,7 +44,7 @@ <type>text</type> <default></default> <notnull>false</notnull> - <length>100</length> + <length>200</length> </field> <field> @@ -62,6 +62,14 @@ <length>4</length> </field> + <field> + <name>active</name> + <type>integer</type> + <default>1</default> + <notnull>true</notnull> + <length>4</length> + </field> + </declaration> </table> @@ -110,7 +118,7 @@ <type>text</type> <default></default> <notnull>false</notnull> - <length>100</length> + <length>200</length> </field> <field> diff --git a/apps/contacts/appinfo/migrate.php b/apps/contacts/appinfo/migrate.php index f43126e5c25..2559b4ea456 100644 --- a/apps/contacts/appinfo/migrate.php +++ b/apps/contacts/appinfo/migrate.php @@ -2,7 +2,7 @@ class OC_Migration_Provider_Contacts extends OC_Migration_Provider{ // Create the xml for the user supplied - function export( ){ + function export( ) { $options = array( 'table'=>'contacts_addressbooks', 'matchcol'=>'userid', @@ -21,38 +21,40 @@ class OC_Migration_Provider_Contacts extends OC_Migration_Provider{ $ids2 = $this->content->copyRows( $options ); // If both returned some ids then they worked - if( is_array( $ids ) && is_array( $ids2 ) ) - { - return true; + if(is_array($ids) && is_array($ids2)) { + return true; } else { return false; } } - // Import function for bookmarks - function import( ){ - switch( $this->appinfo->version ){ + // Import function for contacts + function import( ) { + switch( $this->appinfo->version ) { default: // All versions of the app have had the same db structure, so all can use the same import function $query = $this->content->prepare( "SELECT * FROM contacts_addressbooks WHERE userid LIKE ?" ); $results = $query->execute( array( $this->olduid ) ); $idmap = array(); - while( $row = $results->fetchRow() ){ - // Import each bookmark, saving its id into the map - $query = OCP\DB::prepare( "INSERT INTO `*PREFIX*contacts_addressbooks` (`userid`, `displayname`, `uri`, `description`, `ctag`) VALUES (?, ?, ?, ?, ?)" ); - $query->execute( array( $this->uid, $row['displayname'], $row['uri'], $row['description'], $row['ctag'] ) ); + while( $row = $results->fetchRow() ) { + // Import each addressbook + $addressbookquery = OCP\DB::prepare( "INSERT INTO *PREFIX*contacts_addressbooks (`userid`, `displayname`, `uri`, `description`, `ctag`) VALUES (?, ?, ?, ?, ?)" ); + $addressbookquery->execute( array( $this->uid, $row['displayname'], $row['uri'], $row['description'], $row['ctag'] ) ); // Map the id - $idmap[$row['id']] = OCP\DB::insertid(); + $idmap[$row['id']] = OCP\DB::insertid('*PREFIX*contacts_addressbooks'); + // Make the addressbook active + OC_Contacts_Addressbook::setActive($idmap[$row['id']], true); } // Now tags - foreach($idmap as $oldid => $newid){ + foreach($idmap as $oldid => $newid) { + $query = $this->content->prepare( "SELECT * FROM contacts_cards WHERE addressbookid LIKE ?" ); $results = $query->execute( array( $oldid ) ); while( $row = $results->fetchRow() ){ - // Import the tags for this bookmark, using the new bookmark id - $query = OCP\DB::prepare( "INSERT INTO `*PREFIX*contacts_cards` (`addressbookid`, `fullname`, `carddata`, `uri`, `lastmodified`) VALUES (?, ?, ?, ?, ?)" ); - $query->execute( array( $newid, $row['fullname'], $row['carddata'], $row['uri'], $row['lastmodified'] ) ); + // Import the contacts + $contactquery = OCP\DB::prepare( "INSERT INTO *PREFIX*contacts_cards (`addressbookid`, `fullname`, `carddata`, `uri`, `lastmodified`) VALUES (?, ?, ?, ?, ?)" ); + $contactquery->execute( array( $newid, $row['fullname'], $row['carddata'], $row['uri'], $row['lastmodified'] ) ); } } // All done! diff --git a/apps/contacts/appinfo/remote.php b/apps/contacts/appinfo/remote.php index 2810358e7f2..d8677d7f731 100644 --- a/apps/contacts/appinfo/remote.php +++ b/apps/contacts/appinfo/remote.php @@ -22,8 +22,8 @@ OCP\App::checkAppEnabled('contacts'); -if(substr($_SERVER["REQUEST_URI"],0,strlen(OC::$APPSWEBROOT . '/apps/contacts/carddav.php')) == OC::$APPSWEBROOT . '/apps/contacts/carddav.php'){ - $baseuri = OC::$APPSWEBROOT . '/apps/contacts/carddav.php'; +if(substr($_SERVER["REQUEST_URI"], 0, strlen(OC_App::getAppWebPath('contacts').'/carddav.php')) == OC_App::getAppWebPath('contacts').'/carddav.php') { + $baseuri = OC_App::getAppWebPath('contacts').'/carddav.php'; } // only need authentication apps @@ -36,25 +36,26 @@ $principalBackend = new OC_Connector_Sabre_Principal(); $carddavBackend = new OC_Connector_Sabre_CardDAV(); // Root nodes -$Sabre_CalDAV_Principal_Collection = new Sabre_CalDAV_Principal_Collection($principalBackend); -$Sabre_CalDAV_Principal_Collection->disableListing = true; // Disable listening +$principalCollection = new Sabre_CalDAV_Principal_Collection($principalBackend); +$principalCollection->disableListing = true; // Disable listening -$Sabre_CardDAV_AddressBookRoot = new Sabre_CardDAV_AddressBookRoot($principalBackend, $carddavBackend); -$Sabre_CardDAV_AddressBookRoot->disableListing = true; // Disable listening +$addressBookRoot = new OC_Connector_Sabre_CardDAV_AddressBookRoot($principalBackend, $carddavBackend); +$addressBookRoot->disableListing = true; // Disable listening -$nodes = array( - $Sabre_CalDAV_Principal_Collection, - $Sabre_CardDAV_AddressBookRoot, +$nodes = array( + $principalCollection, + $addressBookRoot, ); // Fire up server $server = new Sabre_DAV_Server($nodes); $server->setBaseUri($baseuri); // Add plugins -$server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend,'ownCloud')); +$server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend, 'ownCloud')); $server->addPlugin(new Sabre_CardDAV_Plugin()); $server->addPlugin(new Sabre_DAVACL_Plugin()); $server->addPlugin(new Sabre_DAV_Browser_Plugin(false)); // Show something in the Browser, but no upload +$server->addPlugin(new OC_Connector_Sabre_CardDAV_VCFExportPlugin()); // And off we go! $server->exec(); diff --git a/apps/contacts/appinfo/update.php b/apps/contacts/appinfo/update.php new file mode 100644 index 00000000000..21e736bb446 --- /dev/null +++ b/apps/contacts/appinfo/update.php @@ -0,0 +1,25 @@ +<?php + +$installedVersion=OCP\Config::getAppValue('contacts', 'installed_version'); +if (version_compare($installedVersion, '0.2.3', '<')) { + // First set all address books in-active. + $stmt = OCP\DB::prepare( 'UPDATE *PREFIX*contacts_addressbooks SET active=0' ); + $result = $stmt->execute(array()); + + // Then get all the active address books. + $stmt = OCP\DB::prepare( 'SELECT userid,configvalue FROM *PREFIX*preferences WHERE appid=\'contacts\' AND configkey=\'openaddressbooks\'' ); + $result = $stmt->execute(array()); + + // Prepare statement for updating the new 'active' field. + $stmt = OCP\DB::prepare( 'UPDATE *PREFIX*contacts_addressbooks SET active=? WHERE id=? AND userid=?' ); + while( $row = $result->fetchRow()) { + $ids = explode(';', $row['configvalue']); + foreach($ids as $id) { + $r = $stmt->execute(array(1, $id, $row['userid'])); + } + } + + // Remove the old preferences. + $stmt = OCP\DB::prepare( 'DELETE FROM *PREFIX*preferences WHERE appid=\'contacts\' AND configkey=\'openaddressbooks\'' ); + $result = $stmt->execute(array()); +} diff --git a/apps/contacts/appinfo/version b/apps/contacts/appinfo/version index 2f4536184bc..72f9fa82020 100644 --- a/apps/contacts/appinfo/version +++ b/apps/contacts/appinfo/version @@ -1 +1 @@ -0.2
\ No newline at end of file +0.2.4
\ No newline at end of file diff --git a/apps/contacts/carddav.php b/apps/contacts/carddav.php index e0579e625d7..264eb30836b 100644 --- a/apps/contacts/carddav.php +++ b/apps/contacts/carddav.php @@ -1,6 +1,6 @@ <?php -if(!file_exists('../../lib/base.php')){ +if(!file_exists('../../lib/base.php')) { die('Please update the path to /lib/base.php in carddav.php or make use of /remote.php/carddav/'); } -require_once('../../lib/base.php'); -require_once('appinfo/remote.php');
\ No newline at end of file +require_once '../../lib/base.php'; +require_once 'appinfo/remote.php';
\ No newline at end of file diff --git a/apps/contacts/css/contacts.css b/apps/contacts/css/contacts.css index 6163c60630f..bc1a57756ae 100644 --- a/apps/contacts/css/contacts.css +++ b/apps/contacts/css/contacts.css @@ -2,36 +2,46 @@ font-weight: bold; }*/ #leftcontent { top: 3.5em !important; padding: 0; margin: 0; } +#leftcontent a { padding: 0 0 0 25px; } #rightcontent { top: 3.5em !important; padding-top: 5px; } -#contacts { background: #fff; width: 20em; left: 12.5em; top: 3.7em; bottom:3em; position: fixed; overflow: auto; padding: 0; margin: 0; } -#contacts a { height: 23px; display: block; margin: 0 0 0 0; padding: 0 0 0 25px; } +#leftcontent h3 { cursor: pointer; -moz-transition: background 300ms ease 0s; background: none no-repeat scroll 1em center #eee; border-bottom: 1px solid #ddd; border-top: 1px solid #fff; display: block; max-width: 100%; padding: 0.5em 0.8em; color: #666; text-shadow: 0 1px 0 #f8f8f8; font-size: 1.2em; } +#leftcontent h3:hover,#leftcontent h3:active,#leftcontent h3.active { background-color: #DBDBDB; border-bottom: 1px solid #CCCCCC; border-top: 1px solid #D4D4D4; color: #333333; font-weight: bold; } +#contacts { position: fixed; background: #fff; max-width: 100%; width: 20em; left: 12.5em; top: 3.7em; bottom: 3em; overflow: auto; padding: 0; margin: 0; } +.contacts a { height: 23px; display: block; left: 12.5em; margin: 0 0 0 0; padding: 0 0 0 25px; } +.contacts li.ui-draggable { height: 23px; } +.ui-draggable-dragging { width: 17em; cursor: move; } +.ui-state-hover { border: 1px solid dashed; } #bottomcontrols { padding: 0; bottom:0px; height:2.8em; width: 20em; margin:0; background:#eee; border-top:1px solid #ccc; position:fixed; -moz-box-shadow: 0 -3px 3px -3px #000; -webkit-box-shadow: 0 -3px 3px -3px #000; box-shadow: 0 -3px 3px -3px #000;} -#contacts_newcontact { float: left; margin: 0.2em 0 0 1em; } -#chooseaddressbook { float: right; margin: 0.2em 1em 0 0; } -#actionbar { position: relative; clear: both; height: 30px;} +#bottomcontrols img { margin-top: 0.35em; } +#uploadprogressbar { display: none; padding: 0; bottom: 3em; height:2em; width: 20em; margin:0; background:#eee; border:1px solid #ccc; position:fixed; } +#contacts_newcontact, #bottomcontrols .settings { float: left; margin: 0.2em 0 0 1em; border: 0 none; border-radius: 0; -moz-box-shadow: none; box-shadow: none; outline: 0 none; } +#bottomcontrols .settings { float: right; margin: 0.2em 1em 0 0; } +#actionbar { clear: both; height: 30px;} #contacts_deletecard {position:relative; float:left; background:url('%webroot%/core/img/actions/delete.svg') no-repeat center; } #contacts_downloadcard {position:relative; float:left; background:url('%webroot%/core/img/actions/download.svg') no-repeat center; } #contacts_propertymenu { clear: left; float:left; max-width: 15em; margin: 2em; } #contacts_propertymenu_button { position:relative;top:0;left:0; margin: 0; } -#contacts_propertymenu_dropdown { background-color: #fff; position:relative; right:0; overflow:hidden; text-overflow:ellipsis; /*border: thin solid #1d2d44;*/ -moz-box-shadow:0 0 10px #000; -webkit-box-shadow:0 0 10px #000; box-shadow:0 0 10px #000; -moz-border-radius:0.5em; -webkit-border-radius:0.5em; border-radius:0.5em; -moz-border-radius:0.5em; -webkit-border-radius:0.5em; border-radius:0.5em; } +#contacts_propertymenu_dropdown { background-color: #fff; position:relative; right:0; overflow:hidden; text-overflow:ellipsis; border: thin solid #1d2d44; box-shadow: 0 3px 5px #bbb; /* -moz-box-shadow:0 0 10px #000; -webkit-box-shadow:0 0 10px #000; box-shadow:0 0 10px #000; -moz-border-radius:0.5em; -webkit-border-radius:0.5em; border-radius:0.5em; -moz-border-radius:0.5em; -webkit-border-radius:0.5em;*/ border-radius: 3px; } #contacts_propertymenu li { display: block; font-weight: bold; height: 20px; } #contacts_propertymenu li a { padding: 3px; display: block } #contacts_propertymenu li:hover { background-color: #1d2d44; } #contacts_propertymenu li a:hover { color: #fff } -#card { width: auto;/*max-width: 70em; border: thin solid lightgray; display: block;*/ } +#card { width: auto; font-size: 10px; /*max-width: 70em; border: thin solid lightgray; display: block;*/ } #firstrun { width: 100%; position: absolute; top: 5em; left: 0; text-align: center; font-weight:bold; font-size:1.5em; color:#777; } #firstrun #selections { font-size:0.8em; margin: 2em auto auto auto; clear: both; } -#card input[type="text"].contacts_property,input[type="email"].contacts_property { width: 14em; float: left; font-weight: bold; } +#card input[type="text"].contacts_property,input[type="email"].contacts_property,input[type="url"].contacts_property { width: 14em; float: left; font-weight: bold; } .categories { float: left; width: 16em; } -#card input[type="text"],input[type="email"],input[type="tel"],input[type="date"], select, textarea { background-color: #fefefe; border: 0 !important; -webkit-appearance:none !important; -moz-appearance:none !important; -webkit-box-sizing:none !important; -moz-box-sizing:none !important; box-sizing:none !important; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px; float: left; } -#card input[type="text"]:hover, input[type="text"]:focus, input[type="text"]:active,input[type="email"]:hover,input[type="tel"]:hover,input[type="date"]:hover,input[type="date"],input[type="date"]:hover,input[type="date"]:active,input[type="date"]:active,input[type="date"]:active,input[type="email"]:active,input[type="tel"]:active, select:hover, select:focus, select:active, textarea:focus, textarea:hover { border: 0 !important; -webkit-appearance:textfield; -moz-appearance:textfield; -webkit-box-sizing:content-box; -moz-box-sizing:content-box; box-sizing:content-box; background:#fff; color:#333; border:1px solid #ddd; -moz-box-shadow:0 1px 1px #ddd, 0 2px 0 #bbb inset; -webkit-box-shadow:0 1px 1px #ddd, 0 1px 0 #bbb inset; box-shadow:0 1px 1px #ddd, 0 1px 0 #bbb inset; -moz-border-radius:.5em; -webkit-border-radius:.5em; border-radius:.5em; outline:none; float: left; } -textarea { width: 80%; min-height: 5em; min-width: 30em; margin: 0 !important; padding: 0 !important; outline: 0 !important;} -dl.form { width: 100%; float: left; clear: right; margin: 0; padding: 0; } +#card input[type="checkbox"].contacts_property, #card input[type="text"], #card input[type="email"], #card input[type="url"], #card input[type="tel"], #card input[type="date"], #card select, #card textarea { background-color: #fefefe; border: 0 !important; -moz-appearance:none !important; -webkit-box-sizing:none !important; -moz-box-sizing:none !important; box-sizing:none !important; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px; float: left; } +#card input[type="text"]:hover, #card input[type="text"]:focus, #card input[type="text"]:active, input[type="email"]:hover, #card input[type="url"]:hover, #card input[type="tel"]:hover, #card input[type="date"]:hover, #card input[type="date"], #card input[type="date"]:hover, #card input[type="date"]:active, #card input[type="date"]:active, #card input[type="date"]:active, #card input[type="email"]:active, #card input[type="url"]:active, #card input[type="tel"]:active, #card textarea:focus, #card textarea:hover { border: 0 !important; -webkit-appearance:textfield; -moz-appearance:textfield; -webkit-box-sizing:content-box; -moz-box-sizing:content-box; box-sizing:content-box; background:#fff; color:#333; border:1px solid #ddd; -moz-box-shadow:0 1px 1px #ddd, 0 2px 0 #bbb inset; -webkit-box-shadow:0 1px 1px #ddd, 0 1px 0 #bbb inset; box-shadow:0 1px 1px #ddd, 0 1px 0 #bbb inset; -moz-border-radius:.5em; -webkit-border-radius:.5em; border-radius:.5em; outline:none; float: left; } +#card textarea { width: 80%; min-height: 5em; min-width: 30em; margin: 0 !important; padding: 0 !important; outline: 0 !important;} +dl.form { width: 100%; float: left; clear: right; margin: 0; padding: 0; cursor: normal; } .form dt { display: table-cell; clear: left; float: left; width: 7em; margin: 0; padding: 0.8em 0.5em 0 0; text-align:right; text-overflow:ellipsis; o-text-overflow: ellipsis; vertical-align: text-bottom; color: #bbb;/* white-space: pre-wrap; white-space: -moz-pre-wrap !important; white-space: -pre-wrap; white-space: -o-pre-wrap;*/ } .form dd { display: table-cell; clear: right; float: left; margin: 0; padding: 0px; white-space: nowrap; vertical-align: text-bottom; } -#address.form dt { min-width: 5em; } -#address.form dl { min-width: 10em; } +label:hover, dt:hover { color: #333; } +/*::-webkit-input-placeholder { color: #bbb; } +:-moz-placeholder { color: #bbb; } +:-ms-input-placeholder { color: #bbb; }*/ .droptarget { margin: 0.5em; padding: 0.5em; border: thin solid #ccc; -moz-border-radius:.3em; -webkit-border-radius:.3em; border-radius:.3em; } .droppable { margin: 0.5em; padding: 0.5em; border: thin dashed #333; -moz-border-radius:.3em; -webkit-border-radius:.3em; border-radius:.3em; } .loading { background: url('%webroot%/core/img/loading.gif') no-repeat center !important; /*cursor: progress; */ cursor: wait; } @@ -40,11 +50,12 @@ dl.form { width: 100%; float: left; clear: right; margin: 0; padding: 0; } .float { float: left; } .svg { border: inherit; background: inherit; } .listactions { height: 1em; width:60px; float: left; clear: right; } -.add,.edit,.delete,.mail, .globe, .upload, .download, .cloud { cursor: pointer; width: 20px; height: 20px; margin: 0; float: left; position:relative; opacity: 0.1; } +.add,.edit,.delete,.mail, .globe, .upload, .download, .cloud, .share { cursor: pointer; width: 20px; height: 20px; margin: 0; float: left; position:relative; opacity: 0.1; } .add:hover,.edit:hover,.delete:hover,.mail:hover, .globe:hover, .upload:hover, .download:hover .cloud:hover { opacity: 1.0 } .add { background:url('%webroot%/core/img/actions/add.svg') no-repeat center; clear: both; } .delete { background:url('%webroot%/core/img/actions/delete.svg') no-repeat center; } .edit { background:url('%webroot%/core/img/actions/rename.svg') no-repeat center; } +.share { background:url('%webroot%/core/img/actions/share.svg') no-repeat center; } .mail { background:url('%webroot%/core/img/actions/mail.svg') no-repeat center; } .upload { background:url('%webroot%/core/img/actions/upload.svg') no-repeat center; } .download { background:url('%webroot%/core/img/actions/download.svg') no-repeat center; } @@ -60,13 +71,15 @@ dl.form { width: 100%; float: left; clear: right; margin: 0; padding: 0; } #fn { float: left !important; width: 18em !important; } #name { /*position: absolute; top: 0px; left: 0px;*/ min-width: 25em; height: 2em; clear: right; display: block; } #identityprops { /*position: absolute; top: 2.5em; left: 0px;*/ } -/*#contact_photo { max-width: 250px; }*/ -#contact_identity { min-width: 30em; } -.contactsection { position: relative; float: left; /*max-width: 40em;*/ padding: 0.5em; height: auto: border: thin solid lightgray;/* -webkit-border-radius: 0.5em; -moz-border-radius: 0.5em; border-radius: 0.5em; background-color: #f8f8f8;*/ } +#contact_photo { float: left; margin: 1em; } +#contact_identity { min-width: 30em; padding: 0.5em;} +.contactsection { position: relative; float: left; width: 35em; padding: 0.5em; height: auto; } #cropbox { margin: auto; } -#contacts_details_photo { border-radius: 0.5em; border: thin solid #bbb; margin: 0.3em; background: url('%webroot%/core/img/loading.gif') no-repeat center center; -moz-box-shadow: 0 1px 3px #777; -webkit-box-shadow: 0 1px 3px #777; box-shadow: 0 1px 3px #777; } -#contacts_details_photo:hover { background: #fff; cursor: default; } +#contacts_details_photo_wrapper { width: 150px; } +#contacts_details_photo_wrapper.wait { opacity: 0.6; filter:alpha(opacity=0.6); z-index:1000; background: url('%webroot%/core/img/loading.gif') no-repeat center center; cursor: wait; } +.contacts_details_photo { border-radius: 0.5em; border: thin solid #bbb; margin: 0.3em; background: url('%webroot%/core/img/loading.gif') no-repeat center center; -moz-box-shadow: 0 1px 3px #777; -webkit-box-shadow: 0 1px 3px #777; box-shadow: 0 1px 3px #777; opacity: 1; } +.contacts_details_photo:hover { background: #fff; cursor: default; } #phototools { position:absolute; margin: 5px 0 0 10px; width:auto; height:22px; padding:0px; background-color:#fff; list-style-type:none; border-radius: 0.5em; -moz-box-shadow: 0 1px 3px #777; -webkit-box-shadow: 0 1px 3px #777; box-shadow: 0 1px 3px #777; } #phototools li { display: inline; } #phototools li a { float:left; cursor:pointer; width:22px; height:22px; opacity: 0.6; } @@ -76,10 +89,17 @@ dl.form { width: 100%; float: left; clear: right; margin: 0; padding: 0; } #addressdisplay { padding: 0.5em; } dl.addresscard { background-color: #fff; float: left; width: auto; margin: 0 0.3em 0.3em 0.3em; padding: 0; border: 0; } dl.addresscard dd {} -dl.addresscard dt { padding: 0.3em; /*border-bottom: thin solid lightgray;*/ font-weight: bold; clear: both; color: #bbb;} +dl.addresscard dt { padding: 0.3em; font-weight: bold; clear: both; color: #aaa; } dl.addresscard dt:hover { color:#777; } dl.addresscard dd > ul { margin: 0.3em; padding: 0.3em; } dl.addresscard .action { float: right; } +#address dt { width: 30%; white-space:nowrap; } +#address dd { width: 66%; } +#address input { width: 12em; padding: 0.6em 0.5em 0.4em; } +#address input:-moz-placeholder { color: #aaa; } +#address input::-webkit-input-placeholder { color: #aaa; } +#address input:-ms-input-placeholder { color: #aaa; } +#address input:placeholder { color: #aaa; } #adr_type {} /* Select */ #adr_pobox {} #adr_extended {} @@ -92,17 +112,44 @@ dl.addresscard .action { float: right; } #file_upload_form { width: 0; height: 0; } #file_upload_target, #import_upload_target, #crop_target { display:none; } #file_upload_start, #import_upload_start { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter:alpha(opacity=0); opacity:0; z-index:1001; width:0; height:0;} -#import_upload_start { width: 16px; height: 16px; margin: 0 0 0 0; } +#import_upload_start { width: 20px; height: 20px; margin: 0 0 -24px 0; padding: 0;} input[type="checkbox"] { width: 20px; height: 20px; vertical-align: bottom; } .big { font-weight:bold; font-size:1.2em; } .huge { font-weight:bold; font-size:1.5em; } .propertycontainer dd { float: left; width: 25em; } -.propertylist { clear: none; max-width: 28em; } -.propertylist li.propertycontainer { white-space: nowrap; min-width: 35em; /*max-width: 30em;*/ display: block; clear: right; } -.propertycontainer[data-element="EMAIL"] > input[type="email"] { min-width: 19em !important; float: left; } -.propertycontainer[data-element="TEL"] > input[type="text"] { width: 10em !important; float: left; } -.propertylist li > input[type="checkbox"],input[type="radio"] { float: left; clear: left; width: 20px; height: 20px; vertical-align: middle; } +/*.propertylist { clear: none; max-width: 33em; }*/ +.propertylist li.propertycontainer { white-space: nowrap; min-width: 35em; display: block; clear: both; } +.propertycontainer[data-element="EMAIL"] > input[type="email"],.propertycontainer[data-element="TEL"] > input[type="text"] { min-width: 12em !important; float: left; } +.propertylist li > input[type="checkbox"],input[type="radio"] { float: left; clear: left; width: 16px; height: 16px; vertical-align: middle; padding: 0; } .propertylist li > select { float: left; max-width: 8em; } -.typelist[type="button"] { float: left; max-width: 10em; border: 0; background-color: #fff; color: #bbb} /* for multiselect */ +.propertylist li > .select_wrapper { float: left; overflow: hidden; color: #bbb; font-size: 0.8em; } +.propertylist li > .select_wrapper select { float: left; overflow: hidden; color: #bbb; } +.propertylist li > .select_wrapper select option { color: #777; } +.propertylist li > .select_wrapper select:hover,.propertylist li > select:focus,.propertylist li > select:active { color: #777; } +.propertylist li > .select_wrapper select.impp { margin-left: -23px; direction: rtl; } +.propertylist li > .select_wrapper select.types { margin-right: -23px; } +.propertylist li > input[type="checkbox"].impp { clear: none; } +.propertylist li > label.xab { display: block; color: #bbb; float:left; clear: both; padding: 0.5em 0 0 2.5em; } +.propertylist li > label.xab:hover { color: #777; } +.typelist[type="button"] { float: left; max-width: 8em; border: 0; background-color: #fff; color: #bbb; box-shadow: none; } /* for multiselect */ .typelist[type="button"]:hover { color: #777; } /* for multiselect */ .addresslist { clear: both; font-weight: bold; } +#ninjahelp { position: absolute; bottom: 0; left: 0; right: 0; padding: 1em; margin: 1em; opacity: 0.9; } +#ninjahelp .close { position: absolute; top: 5px; right: 5px; height: 20px; width: 20px; } +#ninjahelp h2, .help-section h3 { width: 100%; font-weight: bold; text-align: center; } +#ninjahelp h2 { font-size: 1.4em; } +.help-section { width: 45%; min-width: 35em; float: left; } +.help-section h3 { font-size: 1.2em; } +.help-section dl { width: 100%; float: left; clear: right; margin: 0; padding: 0; cursor: normal; } +.help-section dt { display: table-cell; clear: left; float: left; width: 35%; margin: 0; padding: 0.2em; text-align: right; text-overflow: ellipsis; vertical-align: text-bottom; font-weight: bold: } +.help-section dd { display: table-cell; clear: right; float: left; margin: 0; padding: 0.2em; white-space: nowrap; vertical-align: text-bottom; } +.contacts-settings dl { width: 100%; } +.addressbooks-settings table { width: 100%; } +.addressbooks-settings .actions { width: 100%; white-space: nowrap; } +.addressbooks-settings .actions * { float: left; } +.addressbooks-settings .actions input.name { width: 5em; } +.addressbooks-settings .actions input.name { width: 7em; } +.addressbooks-settings a.action { opacity: 0.2; } +.addressbooks-settings a.action:hover { opacity: 1; } +.addressbooks-settings td.active, .addressbooks-settings td.action { width: 20px; } + diff --git a/apps/contacts/css/jquery.Jcrop.css b/apps/contacts/css/jquery.Jcrop.css index c9b24a5ebe9..a9ba4746e07 100644 --- a/apps/contacts/css/jquery.Jcrop.css +++ b/apps/contacts/css/jquery.Jcrop.css @@ -14,7 +14,7 @@ } .jcrop-vline, .jcrop-hline { - background: white url('%webroot%/apps/contacts/img/Jcrop.gif') top left repeat; + background: white url('%appswebroot%/contacts/img/Jcrop.gif') top left repeat; font-size: 0px; position: absolute; } diff --git a/apps/contacts/export.php b/apps/contacts/export.php index 4e4ade2f2ba..161ca8047ac 100644 --- a/apps/contacts/export.php +++ b/apps/contacts/export.php @@ -9,22 +9,30 @@ OCP\User::checkLoggedIn(); OCP\App::checkAppEnabled('contacts'); -$bookid = isset($_GET['bookid']) ? $_GET['bookid'] : NULL; -$contactid = isset($_GET['contactid']) ? $_GET['contactid'] : NULL; +$bookid = isset($_GET['bookid']) ? $_GET['bookid'] : null; +$contactid = isset($_GET['contactid']) ? $_GET['contactid'] : null; $nl = "\n"; -if(isset($bookid)){ +if(isset($bookid)) { $addressbook = OC_Contacts_App::getAddressbook($bookid); - $cardobjects = OC_Contacts_VCard::all($bookid); + //$cardobjects = OC_Contacts_VCard::all($bookid); header('Content-Type: text/directory'); - header('Content-Disposition: inline; filename=' . str_replace(' ', '_', $addressbook['displayname']) . '.vcf'); + header('Content-Disposition: inline; filename=' + . str_replace(' ', '_', $addressbook['displayname']) . '.vcf'); - foreach($cardobjects as $card) { - echo $card['carddata'] . $nl; + $start = 0; + $batchsize = OCP\Config::getUserValue(OCP\User::getUser(), + 'contacts', + 'export_batch_size', 20); + while($cardobjects = OC_Contacts_VCard::all($bookid, $start, $batchsize)){ + foreach($cardobjects as $card) { + echo $card['carddata'] . $nl; + } + $start += $batchsize; } -}elseif(isset($contactid)){ +}elseif(isset($contactid)) { $data = OC_Contacts_App::getContactObject($contactid); - header('Content-Type: text/directory'); - header('Content-Disposition: inline; filename=' . str_replace(' ', '_', $data['fullname']) . '.vcf'); + header('Content-Type: text/vcard'); + header('Content-Disposition: inline; filename=' + . str_replace(' ', '_', $data['fullname']) . '.vcf'); echo $data['carddata']; } -?> diff --git a/apps/contacts/img/contact-new.png b/apps/contacts/img/contact-new.png Binary files differindex 087ad9ab2d3..a91f2e620fa 100644 --- a/apps/contacts/img/contact-new.png +++ b/apps/contacts/img/contact-new.png diff --git a/apps/contacts/img/person.png b/apps/contacts/img/person.png Binary files differindex 17e79196540..e88bb5653af 100644 --- a/apps/contacts/img/person.png +++ b/apps/contacts/img/person.png diff --git a/apps/contacts/img/person_large.png b/apps/contacts/img/person_large.png Binary files differindex 4edba0c5489..8293df61829 100644 --- a/apps/contacts/img/person_large.png +++ b/apps/contacts/img/person_large.png diff --git a/apps/contacts/import.php b/apps/contacts/import.php index 85d4cebeb02..1986d79f6d6 100644 --- a/apps/contacts/import.php +++ b/apps/contacts/import.php @@ -12,131 +12,139 @@ OCP\JSON::checkLoggedIn(); OCP\App::checkAppEnabled('contacts'); session_write_close(); -$cr = "\r"; $nl = "\n"; -$progressfile = 'import_tmp/' . md5(session_id()) . '.txt'; + +global $progresskey; +$progresskey = 'contacts.import-' . (isset($_GET['progresskey'])?$_GET['progresskey']:''); + +if (isset($_GET['progress']) && $_GET['progress']) { + echo OC_Cache::get($progresskey); + die; +} function writeProgress($pct) { - if(is_writable('import_tmp/')){ - $progressfopen = fopen($progressfile, 'w'); - fwrite($progressfopen, $pct); - fclose($progressfopen); - } + global $progresskey; + OC_Cache::set($progresskey, $pct, 300); } writeProgress('10'); $view = $file = null; if(isset($_POST['fstype']) && $_POST['fstype'] == 'OC_FilesystemView') { $view = OCP\Files::getStorage('contacts'); - $file = $view->file_get_contents('/' . $_POST['file']); + $file = $view->file_get_contents('/imports/' . $_POST['file']); } else { $file = OC_Filesystem::file_get_contents($_POST['path'] . '/' . $_POST['file']); } if(!$file) { - OCP\JSON::error(array('message' => 'Import file was empty.')); + OCP\JSON::error(array('data' => array('message' => 'Import file was empty.'))); exit(); } -if(isset($_POST['method']) && $_POST['method'] == 'new'){ - $id = OC_Contacts_Addressbook::add(OCP\USER::getUser(), $_POST['addressbookname']); +if(isset($_POST['method']) && $_POST['method'] == 'new') { + $id = OC_Contacts_Addressbook::add(OCP\USER::getUser(), + $_POST['addressbookname']); if(!$id) { - OCP\JSON::error(array('message' => 'Error creating address book.')); + OCP\JSON::error( + array( + 'data' => array('message' => 'Error creating address book.') + ) + ); exit(); } OC_Contacts_Addressbook::setActive($id, 1); }else{ $id = $_POST['id']; if(!$id) { - OCP\JSON::error(array('message' => 'Error getting the ID of the address book.')); + OCP\JSON::error( + array( + 'data' => array( + 'message' => 'Error getting the ID of the address book.', + 'file'=>$_POST['file'] + ) + ) + ); exit(); } OC_Contacts_App::getAddressbook($id); // is owner access check } //analyse the contacts file -writeProgress('20'); -$searchfor = array('VCARD'); -$parts = $searchfor; -$filearr = explode($nl, $file); -if(count($filearr) == 1) { // Mac eol - $filearr = explode($cr, $file); -} +writeProgress('40'); +$file = str_replace(array("\r","\n\n"), array("\n","\n"), $file); +$lines = explode($nl, $file); $inelement = false; $parts = array(); -$i = 0; -foreach($filearr as $line){ - foreach($searchfor as $search){ - if(substr_count($line, $search) == 1){ - list($attr, $val) = explode(':', $line); - if($attr == 'BEGIN'){ - $parts[]['begin'] = $i; - $inelement = true; - } - if($attr == 'END'){ - $parts[count($parts) - 1]['end'] = $i; - $inelement = false; - } - } - } - $i++; -} -//import the contacts -writeProgress('40'); -$start = ''; -for ($i = 0; $i < $parts[0]['begin']; $i++) { - if($i == 0){ - $start = $filearr[0]; - }else{ - $start .= $nl . $filearr[$i]; - } -} -$end = ''; -for($i = $parts[count($parts) - 1]['end'] + 1;$i <= count($filearr) - 1; $i++){ - if($i == $parts[count($parts) - 1]['end'] + 1){ - $end = $filearr[$parts[count($parts) - 1]['end'] + 1]; - }else{ - $end .= $nl . $filearr[$i]; +$card = array(); +foreach($lines as $line){ + if(strtoupper(trim($line)) == 'BEGIN:VCARD') { + $inelement = true; + } elseif (strtoupper(trim($line)) == 'END:VCARD') { + $card[] = $line; + $parts[] = implode($nl, $card); + $card = array(); + $inelement = false; } -} -writeProgress('50'); -$importready = array(); -foreach($parts as $part){ - for($i = $part['begin']; $i <= $part['end'];$i++){ - if($i == $part['begin']){ - $content = $filearr[$i]; - }else{ - $content .= $nl . $filearr[$i]; - } + if ($inelement === true && trim($line) != '') { + $card[] = $line; } - $importready[] = $start . $nl . $content . $nl . $end; } +//import the contacts writeProgress('70'); -if(count($parts) == 1){ - $importready = array($file); -} $imported = 0; $failed = 0; -if(!count($importready) > 0) { - OCP\JSON::error(array('data' => (array('message' => 'No contacts to import in .'.$_POST['file'].' Please check if the file is corrupted.')))); +if(!count($parts) > 0) { + OCP\JSON::error( + array( + 'data' => array( + 'message' => 'No contacts to import in ' + . $_POST['file'].'. Please check if the file is corrupted.', + 'file'=>$_POST['file'] + ) + ) + ); + if(isset($_POST['fstype']) && $_POST['fstype'] == 'OC_FilesystemView') { + if(!$view->unlink('/imports/' . $_POST['file'])) { + OCP\Util::writeLog('contacts', + 'Import: Error unlinking OC_FilesystemView ' . '/' . $_POST['file'], + OCP\Util::ERROR); + } + } exit(); } -foreach($importready as $import){ - $card = OC_VObject::parse($import); +foreach($parts as $part){ + $card = OC_VObject::parse($part); if (!$card) { $failed += 1; - OCP\Util::writeLog('contacts','Import: skipping card. Error parsing VCard: '.$import, OCP\Util::ERROR); + OCP\Util::writeLog('contacts', + 'Import: skipping card. Error parsing VCard: ' . $part, + OCP\Util::ERROR); continue; // Ditch cards that can't be parsed by Sabre. } - $imported += 1; - OC_Contacts_VCard::add($id, $card); + try { + OC_Contacts_VCard::add($id, $card); + $imported += 1; + } catch (Exception $e) { + OCP\Util::writeLog('contacts', + 'Error importing vcard: ' . $e->getMessage() . $nl . $card, + OCP\Util::ERROR); + $failed += 1; + } } //done the import writeProgress('100'); sleep(3); -if(is_writable('import_tmp/')){ - unlink($progressfile); -} +OC_Cache::remove($progresskey); if(isset($_POST['fstype']) && $_POST['fstype'] == 'OC_FilesystemView') { - if(!$view->unlink('/' . $_POST['file'])) { - OCP\Util::writeLog('contacts','Import: Error unlinking OC_FilesystemView ' . '/' . $_POST['file'], OCP\Util::ERROR); + if(!$view->unlink('/imports/' . $_POST['file'])) { + OCP\Util::writeLog('contacts', + 'Import: Error unlinking OC_FilesystemView ' . '/' . $_POST['file'], + OCP\Util::ERROR); } } -OCP\JSON::success(array('data' => array('imported'=>$imported, 'failed'=>$failed))); +OCP\JSON::success( + array( + 'data' => array( + 'imported'=>$imported, + 'failed'=>$failed, + 'file'=>$_POST['file'], + ) + ) +); diff --git a/apps/contacts/import_tmp/Info b/apps/contacts/import_tmp/Info deleted file mode 100644 index abafbce435c..00000000000 --- a/apps/contacts/import_tmp/Info +++ /dev/null @@ -1,2 +0,0 @@ -This folder contains static files with the percentage of the import. -Requires write permission diff --git a/apps/contacts/index.php b/apps/contacts/index.php index 994539e9fce..f16d6f5641f 100644 --- a/apps/contacts/index.php +++ b/apps/contacts/index.php @@ -14,31 +14,28 @@ OCP\App::checkAppEnabled('contacts'); // Get active address books. This creates a default one if none exists. $ids = OC_Contacts_Addressbook::activeIds(OCP\USER::getUser()); -$allcontacts = OC_Contacts_VCard::all($ids); -$contacts = array(); -foreach($allcontacts as $contact) { // try to conserve some memory - $contacts[] = array('id' => $contact['id'], 'addressbookid' => $contact['addressbookid'], 'fullname' => $contact['fullname']); +$has_contacts = (count(OC_Contacts_VCard::all($ids, 0, 1)) > 0 + ? true + : false); // just to check if there are any contacts. +if($has_contacts === false) { + OCP\Util::writeLog('contacts', + 'index.html: No contacts found.', + OCP\Util::DEBUG); } -unset($allcontacts); -$addressbooks = OC_Contacts_Addressbook::active(OCP\USER::getUser()); // Load the files we need -OCP\App::setActiveNavigationEntry( 'contacts_index' ); +OCP\App::setActiveNavigationEntry('contacts_index'); // Load a specific user? $id = isset( $_GET['id'] ) ? $_GET['id'] : null; -$details = array(); - -if(is_null($id) && count($contacts) > 0) { - $id = $contacts[0]['id']; -} -if(!is_null($id)) { - $vcard = OC_Contacts_App::getContactVCard($id); - $details = OC_Contacts_VCard::structureContact($vcard); -} -$property_types = OC_Contacts_App::getAddPropertyOptions(); +$impp_types = OC_Contacts_App::getTypesOfProperty('IMPP'); $phone_types = OC_Contacts_App::getTypesOfProperty('TEL'); $email_types = OC_Contacts_App::getTypesOfProperty('EMAIL'); +$ims = OC_Contacts_App::getIMOptions(); +$im_protocols = array(); +foreach($ims as $name => $values) { + $im_protocols[$name] = $values['displayname']; +} $categories = OC_Contacts_App::getCategories(); $upload_max_filesize = OCP\Util::computerFileSize(ini_get('upload_max_filesize')); @@ -46,33 +43,32 @@ $post_max_size = OCP\Util::computerFileSize(ini_get('post_max_size')); $maxUploadFilesize = min($upload_max_filesize, $post_max_size); $freeSpace=OC_Filesystem::free_space('/'); -$freeSpace=max($freeSpace,0); -$maxUploadFilesize = min($maxUploadFilesize ,$freeSpace); +$freeSpace=max($freeSpace, 0); +$maxUploadFilesize = min($maxUploadFilesize, $freeSpace); -OCP\Util::addscript('','jquery.multiselect'); -OCP\Util::addscript('','oc-vcategories'); -OCP\Util::addscript('contacts','contacts'); -OCP\Util::addscript('contacts','expanding'); -OCP\Util::addscript('contacts','jquery.combobox'); -OCP\Util::addscript('contacts','jquery.inview'); -OCP\Util::addscript('contacts','jquery.Jcrop'); -OCP\Util::addscript('contacts','jquery.multi-autocomplete'); -OCP\Util::addStyle('','jquery.multiselect'); -OCP\Util::addStyle('contacts','jquery.combobox'); -OCP\Util::addStyle('contacts','jquery.Jcrop'); -OCP\Util::addStyle('contacts','contacts'); +OCP\Util::addscript('', 'jquery.multiselect'); +OCP\Util::addscript('', 'oc-vcategories'); +OCP\Util::addscript('contacts', 'contacts'); +OCP\Util::addscript('contacts', 'expanding'); +OCP\Util::addscript('contacts', 'jquery.combobox'); +OCP\Util::addscript('files', 'jquery.fileupload'); +OCP\Util::addscript('contacts', 'jquery.inview'); +OCP\Util::addscript('contacts', 'jquery.Jcrop'); +OCP\Util::addscript('contacts', 'jquery.multi-autocomplete'); +OCP\Util::addStyle('', 'jquery.multiselect'); +OCP\Util::addStyle('contacts', 'jquery.combobox'); +OCP\Util::addStyle('contacts', 'jquery.Jcrop'); +OCP\Util::addStyle('contacts', 'contacts'); $tmpl = new OCP\Template( "contacts", "index", "user" ); -$tmpl->assign('uploadMaxFilesize', $maxUploadFilesize); -$tmpl->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize)); -$tmpl->assign('property_types', $property_types); -$tmpl->assign('phone_types', $phone_types); -$tmpl->assign('email_types', $email_types); -$tmpl->assign('categories', $categories); -$tmpl->assign('addressbooks', $addressbooks); -$tmpl->assign('contacts', $contacts); -$tmpl->assign('details', $details ); -$tmpl->assign('id',$id); +$tmpl->assign('uploadMaxFilesize', $maxUploadFilesize, false); +$tmpl->assign('uploadMaxHumanFilesize', + OCP\Util::humanFileSize($maxUploadFilesize), false); +$tmpl->assign('phone_types', $phone_types, false); +$tmpl->assign('email_types', $email_types, false); +$tmpl->assign('impp_types', $impp_types, false); +$tmpl->assign('categories', $categories, false); +$tmpl->assign('im_protocols', $im_protocols, false); +$tmpl->assign('has_contacts', $has_contacts, false); +$tmpl->assign('id', $id, false); $tmpl->printPage(); - -?> diff --git a/apps/contacts/js/contacts.js b/apps/contacts/js/contacts.js index 7f260ea54d9..09cf26bf7fe 100644 --- a/apps/contacts/js/contacts.js +++ b/apps/contacts/js/contacts.js @@ -10,1588 +10,1998 @@ String.prototype.strip_tags = function(){ return stripped; }; -Contacts={ - UI:{ - notification:function(msg, ndata) { - $('#notification').text(msg); - if(data) { - $('#notification').data(ndata[0],ndata[1]); - } - $('#notification').fadeIn(); - setTimeout($('#notification').fadeOut(), 10000); - }, - notImplemented:function() { - OC.dialogs.alert(t('contacts', 'Sorry, this functionality has not been implemented yet'), t('contacts', 'Not implemented')); - }, - searchOSM:function(obj) { - var adr = Contacts.UI.propertyContainerFor(obj).find('.adr').val(); - if(adr == undefined) { - OC.dialogs.alert(t('contacts', 'Couldn\'t get a valid address.'), t('contacts', 'Error')); - return; - } - // FIXME: I suck at regexp. /Tanghus - var adrarr = adr.split(';'); - var adrstr = ''; - if(adrarr[2].trim() != '') { - adrstr = adrstr + adrarr[2].trim() + ','; - } - if(adrarr[3].trim() != '') { - adrstr = adrstr + adrarr[3].trim() + ','; +OC.Contacts={ + /** + * Arguments: + * message: The text message to show. + * timeout: The timeout in seconds before the notification disappears. Default 10. + * timeouthandler: A function to run on timeout. + * clickhandler: A function to run on click. If a timeouthandler is given it will be cancelled. + * data: An object that will be passed as argument to the timeouthandler and clickhandler functions. + * cancel: If set cancel all ongoing timer events and hide the notification. + */ + notify:function(params) { + var self = this; + if(!self.notifier) { + self.notifier = $('#notification'); + } + if(params.cancel) { + self.notifier.off('click'); + for(var id in self.notifier.data()) { + if($.isNumeric(id)) { + clearTimeout(parseInt(id)); + } } - if(adrarr[4].trim() != '') { - adrstr = adrstr + adrarr[4].trim() + ','; + self.notifier.text('').fadeOut().removeData(); + return; + } + self.notifier.text(params.message); + self.notifier.fadeIn(); + self.notifier.on('click', function() { $(this).fadeOut();}); + var timer = setTimeout(function() { + if(!self || !self.notifier) { + var self = OC.Contacts; + self.notifier = $('#notification'); } - if(adrarr[5].trim() != '') { - adrstr = adrstr + adrarr[5].trim() + ','; + self.notifier.fadeOut(); + if(params.timeouthandler && $.isFunction(params.timeouthandler)) { + params.timeouthandler(self.notifier.data(dataid)); + self.notifier.off('click'); + self.notifier.removeData(dataid); } - if(adrarr[6].trim() != '') { - adrstr = adrstr + adrarr[6].trim(); + }, params.timeout && $.isNumeric(params.timeout) ? parseInt(params.timeout)*1000 : 10000); + var dataid = timer.toString(); + if(params.data) { + self.notifier.data(dataid, params.data); + } + if(params.clickhandler && $.isFunction(params.clickhandler)) { + self.notifier.on('click', function() { + if(!self || !self.notifier) { + var self = OC.Contacts; + self.notifier = $(this); + } + clearTimeout(timer); + self.notifier.off('click'); + params.clickhandler(self.notifier.data(dataid)); + self.notifier.removeData(dataid); + }); + } + }, + notImplemented:function() { + OC.dialogs.alert(t('contacts', 'Sorry, this functionality has not been implemented yet'), t('contacts', 'Not implemented')); + }, + searchOSM:function(obj) { + var adr = OC.Contacts.propertyContainerFor(obj).find('.adr').val(); + if(adr == undefined) { + OC.dialogs.alert(t('contacts', 'Couldn\'t get a valid address.'), t('contacts', 'Error')); + return; + } + // FIXME: I suck at regexp. /Tanghus + var adrarr = adr.split(';'); + var adrstr = ''; + if(adrarr[2].trim() != '') { + adrstr = adrstr + adrarr[2].trim() + ','; + } + if(adrarr[3].trim() != '') { + adrstr = adrstr + adrarr[3].trim() + ','; + } + if(adrarr[4].trim() != '') { + adrstr = adrstr + adrarr[4].trim() + ','; + } + if(adrarr[5].trim() != '') { + adrstr = adrstr + adrarr[5].trim() + ','; + } + if(adrarr[6].trim() != '') { + adrstr = adrstr + adrarr[6].trim(); + } + adrstr = encodeURIComponent(adrstr); + var uri = 'http://open.mapquestapi.com/nominatim/v1/search.php?q=' + adrstr + '&limit=10&addressdetails=1&polygon=1&zoom='; + var newWindow = window.open(uri,'_blank'); + newWindow.focus(); + }, + mailTo:function(obj) { + var adr = OC.Contacts.propertyContainerFor($(obj)).find('input[type="email"]').val().trim(); + if(adr == '') { + OC.dialogs.alert(t('contacts', 'Please enter an email address.'), t('contacts', 'Error')); + return; + } + window.location.href='mailto:' + adr; + }, + propertyContainerFor:function(obj) { + return $(obj).parents('.propertycontainer').first(); + }, + checksumFor:function(obj) { + return $(obj).parents('.propertycontainer').first().data('checksum'); + }, + propertyTypeFor:function(obj) { + return $(obj).parents('.propertycontainer').first().data('element'); + }, + loading:function(obj, state) { + if(state) { + $(obj).addClass('loading'); + } else { + $(obj).removeClass('loading'); + } + }, + showCardDAVUrl:function(username, bookname){ + $('#carddav_url').val(totalurl + '/' + username + '/' + decodeURIComponent(bookname)); + $('#carddav_url').show(); + $('#carddav_url_close').show(); + }, + loadListHandlers:function() { + $('.propertylist li a.delete').unbind('click'); + $('.propertylist li a.delete').unbind('keydown'); + var deleteItem = function(obj) { + obj.tipsy('hide'); + OC.Contacts.Card.deleteProperty(obj, 'list'); + } + $('.propertylist li a.delete, .addresscard .delete').click(function() { deleteItem($(this)) }); + $('.propertylist li a.delete, .addresscard .delete').keydown(function() { deleteItem($(this)) }); + $('.propertylist li a.mail').click(function() { OC.Contacts.mailTo(this) }); + $('.propertylist li a.mail').keydown(function() { OC.Contacts.mailTo(this) }); + $('.addresscard .globe').click(function() { $(this).tipsy('hide');OC.Contacts.searchOSM(this); }); + $('.addresscard .globe').keydown(function() { $(this).tipsy('hide');OC.Contacts.searchOSM(this); }); + $('.addresscard .edit').click(function() { $(this).tipsy('hide');OC.Contacts.Card.editAddress(this, false); }); + $('.addresscard .edit').keydown(function() { $(this).tipsy('hide');OC.Contacts.Card.editAddress(this, false); }); + $('.addresscard,.propertylist li,.propertycontainer').hover( + function () { + $(this).find('.globe,.mail,.delete,.edit').animate({ opacity: 1.0 }, 200, function() {}); + }, + function () { + $(this).find('.globe,.mail,.delete,.edit').animate({ opacity: 0.1 }, 200, function() {}); } - adrstr = encodeURIComponent(adrstr); - var uri = 'http://open.mapquestapi.com/nominatim/v1/search.php?q=' + adrstr + '&limit=10&addressdetails=1&polygon=1&zoom='; - var newWindow = window.open(uri,'_blank'); + ); + }, + loadHandlers:function() { + var deleteItem = function(obj) { + obj.tipsy('hide'); + OC.Contacts.Card.deleteProperty(obj, 'single'); + } + var goToUrl = function(obj) { + var url = OC.Contacts.propertyContainerFor(obj).find('#url').val(); + if(url != '') { + var newWindow = window.open(url,'_blank'); newWindow.focus(); - }, - mailTo:function(obj) { - var adr = Contacts.UI.propertyContainerFor($(obj)).find('input[type="email"]').val().trim(); - if(adr == '') { - OC.dialogs.alert(t('contacts', 'Please enter an email address.'), t('contacts', 'Error')); - return; } - window.location.href='mailto:' + adr; - }, - propertyContainerFor:function(obj) { - return $(obj).parents('.propertycontainer').first(); - }, - checksumFor:function(obj) { - return $(obj).parents('.propertycontainer').first().data('checksum'); - }, - propertyTypeFor:function(obj) { - return $(obj).parents('.propertycontainer').first().data('element'); - }, - loading:function(obj, state) { - if(state) { - $(obj).addClass('loading'); - } else { - $(obj).removeClass('loading'); + } + + $('#identityprops a.delete').click( function() { deleteItem($(this)) }); + $('#identityprops a.delete').keydown( function() { deleteItem($(this)) }); + $('#categories_value a.edit').click( function() { $(this).tipsy('hide');OCCategories.edit(); } ); + $('#categories_value a.edit').keydown( function() { $(this).tipsy('hide');OCCategories.edit(); } ); + $('#url_value a.globe').click( function() { $(this).tipsy('hide');goToUrl($(this)); } ); + $('#url_value a.globe').keydown( function() { $(this).tipsy('hide');goToUrl($(this)); } ); + $('#fn_select').combobox({ + 'id': 'fn', + 'name': 'value', + 'classes': ['contacts_property', 'nonempty', 'huge', 'tip', 'float'], + 'attributes': {'placeholder': t('contacts', 'Enter name')}, + 'title': t('contacts', 'Format custom, Short name, Full name, Reverse or Reverse with comma')}); + $('#bday').datepicker({ + dateFormat : 'dd-mm-yy' + }); + // Style phone types + $('#phonelist').find('select.contacts_property').multiselect({ + noneSelectedText: t('contacts', 'Select type'), + header: false, + selectedList: 4, + classes: 'typelist' + }); + $('#edit_name').click(function(){OC.Contacts.Card.editName()}); + $('#edit_name').keydown(function(){OC.Contacts.Card.editName()}); + + $('#phototools li a').click(function() { + $(this).tipsy('hide'); + }); + $('#contacts_details_photo_wrapper').hover( + function () { + $('#phototools').slideDown(200); + }, + function () { + $('#phototools').slideUp(200); } - }, - showCardDAVUrl:function(username, bookname){ - $('#carddav_url').val(totalurl + '/' + username + '/' + bookname); - $('#carddav_url').show(); - $('#carddav_url_close').show(); - }, - loadListHandlers:function() { - $('.propertylist li a.delete').unbind('click'); - $('.propertylist li a.delete').unbind('keydown'); - var deleteItem = function(obj) { - obj.tipsy('hide'); - Contacts.UI.Card.deleteProperty(obj, 'list'); - } - $('.propertylist li a.delete, .addresscard .delete').click(function() { deleteItem($(this)) }); - $('.propertylist li a.delete, .addresscard .delete').keydown(function() { deleteItem($(this)) }); - $('.propertylist li a.mail').click(function() { Contacts.UI.mailTo(this) }); - $('.propertylist li a.mail').keydown(function() { Contacts.UI.mailTo(this) }); - $('.addresscard .globe').click(function() { $(this).tipsy('hide');Contacts.UI.searchOSM(this); }); - $('.addresscard .globe').keydown(function() { $(this).tipsy('hide');Contacts.UI.searchOSM(this); }); - $('.addresscard .edit').click(function() { $(this).tipsy('hide');Contacts.UI.Card.editAddress(this, false); }); - $('.addresscard .edit').keydown(function() { $(this).tipsy('hide');Contacts.UI.Card.editAddress(this, false); }); - $('.addresscard,.propertylist li,.propertycontainer').hover( - function () { - $(this).find('.globe,.mail,.delete,.edit').animate({ opacity: 1.0 }, 200, function() {}); - }, - function () { - $(this).find('.globe,.mail,.delete,.edit').animate({ opacity: 0.1 }, 200, function() {}); - } - ); - }, - loadHandlers:function() { - var deleteItem = function(obj) { - obj.tipsy('hide'); - Contacts.UI.Card.deleteProperty(obj, 'single'); - } - $('#identityprops a.delete').click( function() { deleteItem($(this)) }); - $('#identityprops a.delete').keydown( function() { deleteItem($(this)) }); - $('#categories_value a.edit').click( function() { $(this).tipsy('hide');OCCategories.edit(); } ); - $('#categories_value a.edit').keydown( function() { $(this).tipsy('hide');OCCategories.edit(); } ); - $('#fn_select').combobox({ - 'id': 'fn', - 'name': 'value', - 'classes': ['contacts_property', 'nonempty', 'huge', 'tip', 'float'], - 'attributes': {'placeholder': t('contacts', 'Enter name')}, - 'title': t('contacts', 'Format custom, Short name, Full name, Reverse or Reverse with comma')}); - $('#bday').datepicker({ - dateFormat : 'dd-mm-yy' - }); - // Style phone types - $('#phonelist').find('select.contacts_property').multiselect({ - noneSelectedText: t('contacts', 'Select type'), - header: false, - selectedList: 4, - classes: 'typelist' - }); - $('#edit_name').click(function(){Contacts.UI.Card.editName()}); - $('#edit_name').keydown(function(){Contacts.UI.Card.editName()}); - - /* Initialize the photo edit dialog */ - $('#edit_photo_dialog').dialog({ autoOpen: false, modal: true, height: 'auto', width: 'auto' }); - $('#edit_photo_dialog' ).dialog( 'option', 'buttons', [ - { - text: "Ok", - click: function() { - Contacts.UI.Card.savePhoto(this); - $(this).dialog('close'); - } - }, - { - text: "Cancel", - click: function() { $(this).dialog('close'); } - } - ] ); - - /*$('#fn').blur(function(){ - if($('#fn').val() == '') { - OC.dialogs.alert(t('contacts','The name field cannot be empty. Please enter a name for this contact.'), t('contacts','Name is empty'), function() { $('#fn').focus(); }); - $('#fn').focus(); - return false; - } - });*/ - - // Name has changed. Update it and reorder. - $('#fn').change(function(){ - var name = $('#fn').val().strip_tags(); - var item = $('#contacts [data-id="'+Contacts.UI.Card.id+'"]'); - $(item).find('a').html(name); - Contacts.UI.Card.fn = name; - var added = false; - $('#contacts li').each(function(){ - if ($(this).text().toLowerCase() > name.toLowerCase()) { - $(this).before(item).fadeIn('fast'); - added = true; - return false; - } - }); - if(!added) { - $('#leftcontent ul').append(item); + ); + $('#phototools').hover( + function () { + $(this).removeClass('transparent'); + }, + function () { + $(this).addClass('transparent'); + } + ); + $('#phototools .upload').click(function() { + $('#file_upload_start').trigger('click'); + }); + $('#phototools .cloud').click(function() { + OC.dialogs.filepicker(t('contacts', 'Select photo'), OC.Contacts.Card.cloudPhotoSelected, false, 'image', true); + }); + /* Initialize the photo edit dialog */ + $('#edit_photo_dialog').dialog({ + autoOpen: false, modal: true, height: 'auto', width: 'auto' + }); + $('#edit_photo_dialog' ).dialog( 'option', 'buttons', [ + { + text: "Ok", + click: function() { + OC.Contacts.Card.savePhoto(this); + $(this).dialog('close'); } - Contacts.UI.Contacts.scrollTo(Contacts.UI.Card.id); - }); + }, + { + text: "Cancel", + click: function() { $(this).dialog('close'); } + } + ] ); - $('#contacts_deletecard').click( function() { Contacts.UI.Card.doDelete();return false;} ); - $('#contacts_deletecard').keydown( function(event) { - if(event.which == 13) { - Contacts.UI.Card.doDelete(); - } - return false; - }); + // Name has changed. Update it and reorder. + $('#fn').change(function(){ + var name = $('#fn').val().strip_tags(); + var item = $('.contacts li[data-id="'+OC.Contacts.Card.id+'"]').detach(); + $(item).find('a').html(name); + OC.Contacts.Card.fn = name; + OC.Contacts.Contacts.insertContact({contact:item}); + OC.Contacts.Contacts.scrollTo(OC.Contacts.Card.id); + }); - $('#contacts_downloadcard').click( function() { Contacts.UI.Card.doExport();return false;} ); - $('#contacts_downloadcard').keydown( function(event) { - if(event.which == 13) { - Contacts.UI.Card.doExport(); - } - return false; - }); + $('#contacts_deletecard').click( function() { OC.Contacts.Card.delayedDelete();return false;} ); + $('#contacts_deletecard').keydown( function(event) { + if(event.which == 13 || event.which == 32) { + OC.Contacts.Card.delayedDelete(); + } + return false; + }); - // Profile picture upload handling - // New profile picture selected - $('#file_upload_start').change(function(){ - Contacts.UI.Card.uploadPhoto(this.files); - }); - $('#contacts_details_photo_wrapper').bind('dragover',function(event){ - $(event.target).addClass('droppable'); - event.stopPropagation(); - event.preventDefault(); - }); - $('#contacts_details_photo_wrapper').bind('dragleave',function(event){ - $(event.target).removeClass('droppable'); - //event.stopPropagation(); - //event.preventDefault(); - }); - $('#contacts_details_photo_wrapper').bind('drop',function(event){ - event.stopPropagation(); - event.preventDefault(); - $(event.target).removeClass('droppable'); - $.fileUpload(event.originalEvent.dataTransfer.files); - }); + $('#contacts_downloadcard').click( function() { OC.Contacts.Card.doExport();return false;} ); + $('#contacts_downloadcard').keydown( function(event) { + if(event.which == 13 || event.which == 32) { + OC.Contacts.Card.doExport(); + } + return false; + }); - $('#categories').multiple_autocomplete({source: categories}); - $('#contacts_deletecard').tipsy({gravity: 'ne'}); - $('#contacts_downloadcard').tipsy({gravity: 'ne'}); - $('#contacts_propertymenu_button').tipsy(); - $('#contacts_newcontact, #chooseaddressbook').tipsy({gravity: 'sw'}); - }, - Card:{ - id:'', - fn:'', - fullname:'', - shortname:'', - famname:'', - givname:'', - addname:'', - honpre:'', - honsuf:'', - data:undefined, - update:function(id) { - var newid; - if(!id) { - newid = $('#contacts li:first-child').data('id'); - } else { - newid = id; - } - var localLoadContact = function(id) { - if($('#contacts li').length > 0) { - $('#leftcontent li[data-id="'+newid+'"]').addClass('active'); - $.getJSON(OC.filePath('contacts', 'ajax', 'contactdetails.php'),{'id':newid},function(jsondata){ - if(jsondata.status == 'success'){ - Contacts.UI.Card.loadContact(jsondata.data); - } else { - OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); - } - }); + // Profile picture upload handling + // New profile picture selected + $('#file_upload_start').change(function(){ + OC.Contacts.Card.uploadPhoto(this.files); + }); + $('#contacts_details_photo_wrapper').bind('dragover',function(event){ + $(event.target).addClass('droppable'); + event.stopPropagation(); + event.preventDefault(); + }); + $('#contacts_details_photo_wrapper').bind('dragleave',function(event){ + $(event.target).removeClass('droppable'); + }); + $('#contacts_details_photo_wrapper').bind('drop',function(event){ + event.stopPropagation(); + event.preventDefault(); + $(event.target).removeClass('droppable'); + $.fileUpload(event.originalEvent.dataTransfer.files); + }); + + $('#categories').multiple_autocomplete({source: categories}); + $('#contacts_deletecard').tipsy({gravity: 'ne'}); + $('#contacts_downloadcard').tipsy({gravity: 'ne'}); + $('#contacts_propertymenu_button').tipsy(); + $('#contacts_newcontact, #contacts_import, #bottomcontrols .settings').tipsy({gravity: 'sw'}); + + $('body').click(function(e){ + if(!$(e.target).is('#contacts_propertymenu_button')) { + $('#contacts_propertymenu_dropdown').hide(); + } + }); + function propertyMenu(){ + var menu = $('#contacts_propertymenu_dropdown'); + if(menu.is(':hidden')) { + menu.show(); + menu.find('li').first().focus(); + } else { + menu.hide(); + } + } + $('#contacts_propertymenu_button').click(propertyMenu); + $('#contacts_propertymenu_button').keydown(propertyMenu); + function propertyMenuItem(){ + var type = $(this).data('type'); + OC.Contacts.Card.addProperty(type); + $('#contacts_propertymenu_dropdown').hide(); + } + $('#contacts_propertymenu_dropdown a').click(propertyMenuItem); + $('#contacts_propertymenu_dropdown a').keydown(propertyMenuItem); + }, + Card:{ + update:function(params) { // params {cid:int, aid:int} + if(!params) { params = {}; } + $('#contacts li,#contacts h3').removeClass('active'); + console.log('Card, cid: ' + params.cid + ' aid: ' + params.aid); + var newid, bookid, firstitem; + if(!parseInt(params.cid) && !parseInt(params.aid)) { + firstitem = $('#contacts ul').find('li:first-child'); + if(firstitem.length > 0) { + if(firstitem.length > 1) { + firstitem = firstitem.first(); } + newid = parseInt(firstitem.data('id')); + bookid = parseInt(firstitem.data('bookid')); + } + } else if(!parseInt(params.cid) && parseInt(params.aid)) { + bookid = parseInt(params.aid); + newid = parseInt($('#contacts').find('li[data-bookid="'+bookid+'"]').first().data('id')); + } else if(parseInt(params.cid) && !parseInt(params.aid)) { + newid = parseInt(params.cid); + var listitem = OC.Contacts.Contacts.getContact(newid); //$('#contacts li[data-id="'+newid+'"]'); + console.log('Is contact in list? ' + listitem.length); + if(listitem.length) { + //bookid = parseInt($('#contacts li[data-id="'+newid+'"]').data('bookid')); + bookid = parseInt(OC.Contacts.Contacts.getContact(newid).data('bookid')); + } else { // contact isn't in list yet. + bookid = 'unknown'; } - - // Make sure proper DOM is loaded. - if(!$('#card')[0]) { - $.getJSON(OC.filePath('contacts', 'ajax', 'loadcard.php'),{},function(jsondata){ - if(jsondata.status == 'success'){ - $('#rightcontent').html(jsondata.data.page).ready(function() { - Contacts.UI.loadHandlers(); - localLoadContact(newid); - }); - } else { - OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); - } - }); - } - else if($('#contacts li').length == 0) { - // load intro page - $.getJSON(OC.filePath('contacts', 'ajax', 'loadintro.php'),{},function(jsondata){ + } else { + newid = parseInt(params.cid); + bookid = parseInt(params.aid); + } + if(!bookid || !newid) { + bookid = parseInt($('#contacts h3').first().data('id')); + newid = parseInt($('#contacts').find('li[data-bookid="'+bookid+'"]').first().data('id')); + } + console.log('newid: ' + newid + ' bookid: ' +bookid); + var localLoadContact = function(newid, bookid) { + if($('.contacts li').length > 0) { + $.getJSON(OC.filePath('contacts', 'ajax', 'contact/details.php'),{'id':newid},function(jsondata){ if(jsondata.status == 'success'){ - id = ''; - $('#rightcontent').data('id',''); - $('#rightcontent').html(jsondata.data.page); + if(bookid == 'unknown') { + bookid = jsondata.data.addressbookid; + var contact = OC.Contacts.Contacts.insertContact({ + contactlist:$('#contacts ul[data-id="'+bookid+'"]'), + data:jsondata.data + }); + } + $('#contacts li[data-id="'+newid+'"],#contacts h3[data-id="'+bookid+'"]').addClass('active'); + $('#contacts ul[data-id="'+bookid+'"]').slideDown(300); + OC.Contacts.Card.loadContact(jsondata.data, bookid); + OC.Contacts.Contacts.scrollTo(newid); } else { OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); } }); } - else { - localLoadContact(); - } - }, - doExport:function() { - document.location.href = OC.linkTo('contacts', 'export.php') + '?contactid=' + this.id; - }, - doImport:function(){ - Contacts.UI.notImplemented(); - }, - editNew:function(){ // add a new contact - this.id = ''; this.bookid = '', this.fn = ''; this.fullname = ''; this.givname = ''; this.famname = ''; this.addname = ''; this.honpre = ''; this.honsuf = ''; - self = this; - $.getJSON(OC.filePath('contacts', 'ajax', 'selectaddressbook.php'),{},function(jsondata) { - if(jsondata.status == 'success') { - if(jsondata.data.type == 'dialog') { - // Load dialog to select addressbook. - if($('#selectaddressbook_dialog').dialog('isOpen') == true) { - $('#selectaddressbook_dialog').dialog('moveToTop'); - } else { - $('#dialog_holder').html(jsondata.data.page).ready(function($) { - $('#selectaddressbook_dialog').dialog({ - modal: true, height: 'auto', width: 'auto', - buttons: { - 'Ok':function() { - Contacts.UI.Card.add(';;;;;', '',$('#selectaddressbook_dialog').find('input:checked').val(), true); - $(this).dialog('close'); - }, - 'Cancel':function() { $(this).dialog('close'); } - }, - close: function(event, ui) { - $(this).dialog('destroy').remove(); - } - }); - }); - } - } else { - Contacts.UI.Card.bookid = jsondata.data.id; - Contacts.UI.Card.add(';;;;;', '',jsondata.data.id, true); - } + } + + // Make sure proper DOM is loaded. + if(!$('#card').length && newid) { + console.log('Loading card DOM'); + $.getJSON(OC.filePath('contacts', 'ajax', 'loadcard.php'),{requesttoken:requesttoken},function(jsondata){ + if(jsondata.status == 'success'){ + $('#rightcontent').html(jsondata.data.page).ready(function() { + OC.Contacts.loadHandlers(); + localLoadContact(newid, bookid); + }); } else { OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); } }); - return false; - }, - add:function(n, fn, aid, isnew){ // add a new contact - var localAddcontact = function(n, fn, aid, isnew) { - $.post(OC.filePath('contacts', 'ajax', 'addcontact.php'), { n: n, fn: fn, aid: aid, isnew: isnew }, - function(jsondata) { - if (jsondata.status == 'success'){ - $('#rightcontent').data('id',jsondata.data.id); - var id = jsondata.data.id; - $.getJSON(OC.filePath('contacts', 'ajax', 'contactdetails.php'),{'id':id},function(jsondata){ - if(jsondata.status == 'success'){ - Contacts.UI.Card.loadContact(jsondata.data); - $('#leftcontent .active').removeClass('active'); - var item = $('<li role="button" data-id="'+jsondata.data.id+'" data-book-id="'+aid+'" class="active"><a href="index.php?id='+jsondata.data.id+'" style="background: url('+OC.filePath('contacts', '', 'thumbnail.php')+'?id='+jsondata.data.id+') no-repeat scroll 0% 0% transparent;">'+Contacts.UI.Card.fn+'</a></li>'); - var added = false; - $('#leftcontent ul li').each(function(){ - if ($(this).text().toLowerCase() > Contacts.UI.Card.fn.toLowerCase()) { - $(this).before(item).fadeIn('fast'); - added = true; - return false; - } - }); - if(!added) { - $('#leftcontent ul').append(item); - } - if(isnew) { // add some default properties - Contacts.UI.Card.addProperty('EMAIL'); - Contacts.UI.Card.addProperty('TEL'); - $('#fn').focus(); - } - } - else{ - OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); - } - }); - $('#contact_identity').show(); - $('#actionbar').show(); - } - else{ - OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); - } - }); - } - - var card = $('#card')[0]; - if(!card) { - $.getJSON(OC.filePath('contacts', 'ajax', 'loadcard.php'),{},function(jsondata){ - if(jsondata.status == 'success'){ - $('#rightcontent').html(jsondata.data.page).ready(function() { - Contacts.UI.loadHandlers(); - localAddcontact(n, fn, aid, isnew); - }); - } else{ - OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); - } - }); - } else { - localAddcontact(n, fn, aid, isnew); - } - }, - doDelete:function() { - $('#contacts_deletecard').tipsy('hide'); - OC.dialogs.confirm(t('contacts', 'Are you sure you want to delete this contact?'), t('contacts', 'Warning'), function(answer) { - if(answer == true) { - $.post(OC.filePath('contacts', 'ajax', 'deletecard.php'),{'id':Contacts.UI.Card.id},function(jsondata){ + } else if(!newid) { + console.log('Loading intro'); + // load intro page + $.getJSON(OC.filePath('contacts', 'ajax', 'loadintro.php'),{},function(jsondata){ + if(jsondata.status == 'success'){ + id = ''; + $('#rightcontent').data('id',''); + $('#rightcontent').html(jsondata.data.page); + } else { + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); + } + }); + } + else { + localLoadContact(newid, bookid); + } + }, + setEnabled:function(enabled) { + console.log('setEnabled', enabled); + $('.contacts_property,.action').each(function () { + $(this).prop('disabled', !enabled); + OC.Contacts.Card.enabled = enabled; + }); + }, + doExport:function() { + document.location.href = OC.linkTo('contacts', 'export.php') + '?contactid=' + this.id; + }, + editNew:function(){ // add a new contact + var book = $('#contacts h3.active'); + var permissions = parseInt(book.data('permissions')); + if(permissions == 0 + || permissions & OC.Share.PERMISSION_UPDATE + || permissions & OC.Share.PERMISSION_DELETE) { + with(this) { + delete id; delete fn; delete fullname; delete givname; delete famname; + delete addname; delete honpre; delete honsuf; + } + this.bookid = book.data('id'); + OC.Contacts.Card.add(';;;;;', '', '', true); + } else { + OC.dialogs.alert(t('contacts', 'You do not have permission to add contacts to ') + + book.text() + '. ' + t('contacts', 'Please select one of your own address books.'), t('contacts', 'Permission error')); + } + return false; + }, + add:function(n, fn, aid, isnew){ // add a new contact + console.log('Adding ' + fn); + aid = aid?aid:$('#contacts h3.active').first().data('id'); + var localAddcontact = function(n, fn, aid, isnew) { + $.post(OC.filePath('contacts', 'ajax', 'contact/add.php'), { n: n, fn: fn, aid: aid, isnew: isnew }, + function(jsondata) { + if (jsondata.status == 'success'){ + $('#rightcontent').data('id',jsondata.data.id); + var id = jsondata.data.id; + var aid = jsondata.data.aid; + $.getJSON(OC.filePath('contacts', 'ajax', 'contact/details.php'),{'id':id},function(jsondata){ if(jsondata.status == 'success'){ - var newid = ''; - var curlistitem = $('#leftcontent [data-id="'+jsondata.data.id+'"]'); - var newlistitem = curlistitem.prev(); - if(newlistitem == undefined) { - newlistitem = curlistitem.next(); - } - curlistitem.remove(); - if(newlistitem != undefined) { - newid = newlistitem.data('id'); - } - $('#rightcontent').data('id',newid); - this.id = this.fn = this.fullname = this.shortname = this.famname = this.givname = this.addname = this.honpre = this.honsuf = ''; - this.data = undefined; - - if($('#contacts li').length > 0) { // Load first in list. - Contacts.UI.Card.update(newid); - } else { - // load intro page - $.getJSON(OC.filePath('contacts', 'ajax', 'loadintro.php'),{},function(jsondata){ - if(jsondata.status == 'success'){ - id = ''; - $('#rightcontent').data('id',''); - $('#rightcontent').html(jsondata.data.page); - } - else{ - OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); - } - }); + OC.Contacts.Card.loadContact(jsondata.data, aid); + var item = OC.Contacts.Contacts.insertContact({data:jsondata.data}); + if(isnew) { // add some default properties + OC.Contacts.Card.addProperty('EMAIL'); + OC.Contacts.Card.addProperty('TEL'); + $('#fn').focus(); } } else{ OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); } }); + $('#contact_identity').show(); + $('#actionbar').show(); + } + else{ + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); } }); - return false; - }, - loadContact:function(jsondata){ - this.data = jsondata; - this.id = this.data.id; - $('#rightcontent').data('id',this.id); - this.populateNameFields(); - this.loadPhoto(); - this.loadMails(); - this.loadPhones(); - this.loadAddresses(); - this.loadSingleProperties(); - Contacts.UI.loadListHandlers(); - if(this.data.NOTE) { - $('#note').data('checksum', this.data.NOTE[0]['checksum']); - var note = $('#note').find('textarea'); - var txt = this.data.NOTE[0]['value']; - var nheight = txt.split('\n').length > 4 ? txt.split('\n').length+2 : 5; - note.css('min-height', nheight+'em'); - note.attr('rows', nheight); - note.val(txt); - $('#note').show(); - note.expandingTextarea(); - $('#contacts_propertymenu_dropdown a[data-type="NOTE"]').parent().hide(); - } else { - $('#note').data('checksum', ''); - $('#note').find('textarea').val(''); - $('#note').hide(); - $('#contacts_propertymenu_dropdown a[data-type="NOTE"]').parent().show(); - } - }, - loadSingleProperties:function() { - var props = ['BDAY', 'NICKNAME', 'ORG', 'CATEGORIES']; - // Clear all elements - $('#ident .propertycontainer').each(function(){ - if(props.indexOf($(this).data('element')) > -1) { - $(this).data('checksum', ''); - $(this).find('input').val(''); - $(this).hide(); - $(this).prev().hide(); + } + + if(!$('#card').length) { + console.log('Loading card DOM'); + $.getJSON(OC.filePath('contacts', 'ajax', 'loadcard.php'),{'requesttoken': requesttoken},function(jsondata){ + if(jsondata.status == 'success'){ + $('#rightcontent').html(jsondata.data.page).ready(function() { + OC.Contacts.loadHandlers(); + localAddcontact(n, fn, aid, isnew); + }); + } else{ + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); } }); - for(var prop in props) { - if(this.data[props[prop]] != undefined) { - $('#contacts_propertymenu_dropdown a[data-type="'+props[prop]+'"]').parent().hide(); - var property = this.data[props[prop]][0]; - var value = property['value'], checksum = property['checksum']; - switch(props[prop]) { - case 'BDAY': - var val = $.datepicker.parseDate('yy-mm-dd', value.substring(0, 10)); - value = $.datepicker.formatDate('dd-mm-yy', val); - $('#contact_identity').find('#bday').val(value); - $('#contact_identity').find('#bday_value').data('checksum', checksum); - $('#contact_identity').find('#bday_label').show(); - $('#contact_identity').find('#bday_value').show(); - break; - case 'NICKNAME': - $('#contact_identity').find('#nickname').val(value); - $('#contact_identity').find('#nickname_value').data('checksum', checksum); - $('#contact_identity').find('#nickname_label').show(); - $('#contact_identity').find('#nickname_value').show(); - break; - case 'ORG': - $('#contact_identity').find('#org').val(value); - $('#contact_identity').find('#org_value').data('checksum', checksum); - $('#contact_identity').find('#org_label').show(); - $('#contact_identity').find('#org_value').show(); - break; - case 'CATEGORIES': - $('#contact_identity').find('#categories').val(value); - $('#contact_identity').find('#categories_value').data('checksum', checksum); - $('#contact_identity').find('#categories_label').show(); - $('#contact_identity').find('#categories_value').show(); - break; - } + } else { + localAddcontact(n, fn, aid, isnew); + } + }, + delayedDelete:function() { + $('#contacts_deletecard').tipsy('hide'); + var newid = '', bookid; + var curlistitem = OC.Contacts.Contacts.getContact(this.id); + curlistitem.removeClass('active'); + var newlistitem = curlistitem.prev('li'); + if(!newlistitem) { + newlistitem = curlistitem.next('li'); + } + curlistitem.detach(); + if($(newlistitem).is('li')) { + newid = newlistitem.data('id'); + bookid = newlistitem.data('bookid'); + } + $('#rightcontent').data('id', newid); + + OC.Contacts.Contacts.deletionQueue.push(parseInt(this.id)); + if(!window.onbeforeunload) { + window.onbeforeunload = OC.Contacts.Contacts.warnNotDeleted; + } + + with(this) { + delete id; delete fn; delete fullname; delete shortname; delete famname; + delete givname; delete addname; delete honpre; delete honsuf; delete data; + } + + if($('.contacts li').length > 0) { + OC.Contacts.Card.update({cid:newid, aid:bookid}); + } else { + // load intro page + $.getJSON(OC.filePath('contacts', 'ajax', 'loadintro.php'),{},function(jsondata){ + if(jsondata.status == 'success'){ + id = ''; + $('#rightcontent').html(jsondata.data.page).removeData('id'); } else { - $('#contacts_propertymenu_dropdown a[data-type="'+props[prop]+'"]').parent().show(); - } - } - }, - populateNameFields:function() { - var props = ['FN', 'N']; - // Clear all elements - $('#ident .propertycontainer').each(function(){ - if(props.indexOf($(this).data('element')) > -1) { - $(this).data('checksum', ''); - $(this).find('input').val(''); + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); } }); - this.fn = ''; this.fullname = ''; this.givname = ''; this.famname = ''; this.addname = ''; this.honpre = ''; this.honsuf = ''; - var narray = undefined; - if(this.data.FN) { - this.fn = this.data.FN[0]['value']; - } - else { - this.fn = ''; + } + OC.Contacts.notify({ + data:curlistitem, + message:t('contacts','Click to undo deletion of "') + curlistitem.find('a').text() + '"', + //timeout:5, + timeouthandler:function(contact) { + console.log('timeout'); + OC.Contacts.Card.doDelete(contact.data('id'), true, function(res) { + if(!res) { + OC.Contacts.Contacts.insertContact({contact:contact}); + } else { + delete contact; + } + }); + }, + clickhandler:function(contact) { + OC.Contacts.Contacts.insertContact({contact:contact}); + OC.Contacts.notify({message:t('contacts', 'Cancelled deletion of: "') + curlistitem.find('a').text() + '"'}); + window.onbeforeunload = null; } - if(this.data.N == undefined) { - narray = [this.fn,'','','','']; // Checking for non-existing 'N' property :-P - } else { - narray = this.data.N[0]['value']; + }); + }, + doDelete:function(id, removeFromQueue, cb) { + var updateQueue = function(id, remove) { + if(removeFromQueue) { + OC.Contacts.Contacts.deletionQueue.splice(OC.Contacts.Contacts.deletionQueue.indexOf(parseInt(id)), 1); } - this.famname = narray[0] || ''; - this.givname = narray[1] || ''; - this.addname = narray[2] || ''; - this.honpre = narray[3] || ''; - this.honsuf = narray[4] || ''; - if(this.honpre.length > 0) { - this.fullname += this.honpre + ' '; + if(OC.Contacts.Contacts.deletionQueue.length == 0) { + window.onbeforeunload = null; } - if(this.givname.length > 0) { - this.fullname += ' ' + this.givname; + } + + if(OC.Contacts.Contacts.deletionQueue.indexOf(parseInt(id)) == -1 && removeFromQueue) { + console.log('returning'); + updateQueue(id, removeFromQueue); + if(typeof cb == 'function') { + cb(true); } - if(this.addname.length > 0) { - this.fullname += ' ' + this.addname; + return; + } + $.post(OC.filePath('contacts', 'ajax', 'contact/delete.php'), {'id':id},function(jsondata) { + if(jsondata.status == 'error'){ + OC.Contacts.notify({message:jsondata.data.message}); + if(typeof cb == 'function') { + cb(false); + } } - if(this.famname.length > 0) { - this.fullname += ' ' + this.famname; + updateQueue(id, removeFromQueue); + }); + if(typeof cb == 'function') { + cb(true); + } + }, + loadContact:function(jsondata, bookid){ + this.data = jsondata; + this.id = this.data.id; + this.bookid = bookid; + $('#rightcontent').data('id',this.id); + this.populateNameFields(); + this.loadPhoto(); + this.loadMails(); + this.loadPhones(); + this.loadIMs(); + this.loadAddresses(); + this.loadSingleProperties(); + OC.Contacts.loadListHandlers(); + var note = $('#note'); + if(this.data.NOTE) { + note.data('checksum', this.data.NOTE[0]['checksum']); + var textarea = note.find('textarea'); + var txt = this.data.NOTE[0]['value']; + var nheight = txt.split('\n').length > 4 ? txt.split('\n').length+2 : 5; + textarea.css('min-height', nheight+'em'); + textarea.attr('rows', nheight); + textarea.val(txt); + $('#contact_note').show(); + textarea.expandingTextarea(); + $('#contacts_propertymenu_dropdown a[data-type="NOTE"]').parent().hide(); + } else { + note.removeData('checksum'); + note.find('textarea').val(''); + $('#contact_note').hide(); + $('#contacts_propertymenu_dropdown a[data-type="NOTE"]').parent().show(); + } + var permissions = OC.Contacts.Card.permissions = parseInt($('#contacts ul[data-id="' + bookid + '"]').data('permissions')); + console.log('permissions', permissions); + this.setEnabled(permissions == 0 + || permissions & OC.Share.PERMISSION_UPDATE + || permissions & OC.Share.PERMISSION_DELETE); + }, + loadSingleProperties:function() { + var props = ['BDAY', 'NICKNAME', 'ORG', 'URL', 'CATEGORIES']; + // Clear all elements + $('#ident .propertycontainer').each(function(){ + if(props.indexOf($(this).data('element')) > -1) { + $(this).data('checksum', ''); + $(this).find('input').val(''); + $(this).hide(); + $(this).prev().hide(); } - if(this.honsuf.length > 0) { - this.fullname += ', ' + this.honsuf; + }); + for(var prop in props) { + var propname = props[prop]; + if(this.data[propname] != undefined) { + $('#contacts_propertymenu_dropdown a[data-type="'+propname+'"]').parent().hide(); + var property = this.data[propname][0]; + var value = property['value'], checksum = property['checksum']; + + if(propname == 'BDAY') { + var val = $.datepicker.parseDate('yy-mm-dd', value.substring(0, 10)); + value = $.datepicker.formatDate('dd-mm-yy', val); + } + var identcontainer = $('#contact_identity'); + identcontainer.find('#'+propname.toLowerCase()).val(value); + identcontainer.find('#'+propname.toLowerCase()+'_value').data('checksum', checksum); + identcontainer.find('#'+propname.toLowerCase()+'_label').show(); + identcontainer.find('#'+propname.toLowerCase()+'_value').show(); + } else { + $('#contacts_propertymenu_dropdown a[data-type="'+propname+'"]').parent().show(); } - $('#n').val(narray.join(';')); - $('#fn_select option').remove(); - var names = [this.fn, this.fullname, this.givname + ' ' + this.famname, this.famname + ' ' + this.givname, this.famname + ', ' + this.givname]; - if(this.data.ORG) { - names[names.length]=this.data.ORG[0].value; + } + }, + populateNameFields:function() { + var props = ['FN', 'N']; + // Clear all elements + $('#ident .propertycontainer').each(function(){ + if(props.indexOf($(this).data('element')) > -1) { + $(this).data('checksum', ''); + $(this).find('input').val(''); } - $.each(names, function(key, value) { - $('#fn_select') - .append($('<option></option>') - .text(value)); - }); + }); + + with(this) { + delete fn; delete fullname; delete givname; delete famname; + delete addname; delete honpre; delete honsuf; + } + + if(this.data.FN) { + this.fn = this.data.FN[0]['value']; + } + else { + this.fn = ''; + } + if(this.data.N == undefined) { + narray = [this.fn,'','','','']; // Checking for non-existing 'N' property :-P + } else { + narray = this.data.N[0]['value']; + } + this.famname = narray[0] || ''; + this.givname = narray[1] || ''; + this.addname = narray[2] || ''; + this.honpre = narray[3] || ''; + this.honsuf = narray[4] || ''; + if(this.honpre.length > 0) { + this.fullname += this.honpre + ' '; + } + if(this.givname.length > 0) { + this.fullname += ' ' + this.givname; + } + if(this.addname.length > 0) { + this.fullname += ' ' + this.addname; + } + if(this.famname.length > 0) { + this.fullname += ' ' + this.famname; + } + if(this.honsuf.length > 0) { + this.fullname += ', ' + this.honsuf; + } + $('#n').val(narray.join(';')); + $('#fn_select option').remove(); + var names = [this.fn, this.fullname, this.givname + ' ' + this.famname, this.famname + ' ' + this.givname, this.famname + ', ' + this.givname]; + if(this.data.ORG) { + names[names.length]=this.data.ORG[0].value; + } + $.each(names, function(key, value) { + $('#fn_select') + .append($('<option></option>') + .text(value)); + }); $('#fn_select').combobox('value', this.fn); $('#contact_identity').find('*[data-element="N"]').data('checksum', this.data.N[0]['checksum']); if(this.data.FN) { $('#contact_identity').find('*[data-element="FN"]').data('checksum', this.data.FN[0]['checksum']); } $('#contact_identity').show(); - }, - hasCategory:function(category) { - if(this.data.CATEGORIES) { - var categories = this.data.CATEGORIES[0]['value'].split(/,\s*/); - for(var c in categories) { - var cat = this.data.CATEGORIES[0]['value'][c]; - if(typeof cat === 'string' && (cat.toUpperCase() === category.toUpperCase())) { - return true; - } + }, + hasCategory:function(category) { + if(this.data.CATEGORIES) { + var categories = this.data.CATEGORIES[0]['value'].split(/,\s*/); + for(var c in categories) { + var cat = this.data.CATEGORIES[0]['value'][c]; + if(typeof cat === 'string' && (cat.toUpperCase() === category.toUpperCase())) { + return true; } } - return false; - }, - categoriesChanged:function(newcategories) { // Categories added/deleted. - categories = $.map(newcategories, function(v) {return v;}); - $('#categories').multiple_autocomplete('option', 'source', categories); - var categorylist = $('#categories_value').find('input'); - $.getJSON(OC.filePath('contacts', 'ajax', 'categories/categoriesfor.php'),{'id':Contacts.UI.Card.id},function(jsondata){ - if(jsondata.status == 'success'){ - $('#categories_value').data('checksum', jsondata.data.checksum); - categorylist.val(jsondata.data.value); + } + return false; + }, + categoriesChanged:function(newcategories) { // Categories added/deleted. + categories = $.map(newcategories, function(v) {return v;}); + $('#categories').multiple_autocomplete('option', 'source', categories); + var categorylist = $('#categories_value').find('input'); + $.getJSON(OC.filePath('contacts', 'ajax', 'categories/categoriesfor.php'),{'id':OC.Contacts.Card.id},function(jsondata){ + if(jsondata.status == 'success'){ + $('#categories_value').data('checksum', jsondata.data.checksum); + categorylist.val(jsondata.data.value); + } else { + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); + } + }); + }, + savePropertyInternal:function(name, fields, oldchecksum, checksum) { + // TODO: Add functionality for new fields. + //console.log('savePropertyInternal: ' + name + ', fields: ' + fields + 'checksum: ' + checksum); + //console.log('savePropertyInternal: ' + this.data[name]); + var multivalue = ['CATEGORIES']; + var params = {}; + var value = multivalue.indexOf(name) != -1 ? new Array() : undefined; + jQuery.each(fields, function(i, field){ + //.substring(11,'parameters[TYPE][]'.indexOf(']')) + if(field.name.substring(0, 5) === 'value') { + if(multivalue.indexOf(name) != -1) { + value.push(field.value); } else { - OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); - } - }); - }, - savePropertyInternal:function(name, fields, oldchecksum, checksum){ - // TODO: Add functionality for new fields. - //console.log('savePropertyInternal: ' + name + ', fields: ' + fields + 'checksum: ' + checksum); - //console.log('savePropertyInternal: ' + this.data[name]); - var multivalue = ['CATEGORIES']; - var params = {}; - var value = multivalue.indexOf(name) != -1 ? new Array() : undefined; - jQuery.each(fields, function(i, field){ - //.substring(11,'parameters[TYPE][]'.indexOf(']')) - if(field.name.substring(0, 5) === 'value') { - if(multivalue.indexOf(name) != -1) { - value.push(field.value); - } else { - value = field.value; - } - } else if(field.name.substring(0, 10) === 'parameters') { - var p = field.name.substring(11,'parameters[TYPE][]'.indexOf(']')); - if(!(p in params)) { - params[p] = []; - } - params[p].push(field.value); + value = field.value; } - }); - for(var i in this.data[name]) { - if(this.data[name][i]['checksum'] == oldchecksum) { - this.data[name][i]['checksum'] = checksum; - this.data[name][i]['value'] = value; - this.data[name][i]['parameters'] = params; + } else if(field.name.substring(0, 10) === 'parameters') { + var p = field.name.substring(11,'parameters[TYPE][]'.indexOf(']')); + if(!(p in params)) { + params[p] = []; } + params[p].push(field.value); } - }, - saveProperty:function(obj){ - if(!$(obj).hasClass('contacts_property')) { - return false; - } - if($(obj).hasClass('nonempty') && $(obj).val().trim() == '') { - OC.dialogs.alert(t('contacts', 'This property has to be non-empty.'), t('contacts', 'Error')); - return false; - } - container = $(obj).parents('.propertycontainer').first(); // get the parent holding the metadata. - Contacts.UI.loading(obj, true); - var checksum = container.data('checksum'); - var name = container.data('element'); - var fields = container.find('input.contacts_property,select.contacts_property').serializeArray(); - switch(name) { - case 'FN': - var nempty = true; - for(var i in Contacts.UI.Card.data.N[0]['value']) { - if(Contacts.UI.Card.data.N[0]['value'][i] != '') { - nempty = false; - break; - } + }); + for(var i in this.data[name]) { + if(this.data[name][i]['checksum'] == oldchecksum) { + this.data[name][i]['checksum'] = checksum; + this.data[name][i]['value'] = value; + this.data[name][i]['parameters'] = params; } - if(nempty) { - $('#n').val(fields[0].value + ';;;;'); - Contacts.UI.Card.data.N[0]['value'] = Array(fields[0].value, '', '', '', ''); - setTimeout(function() {Contacts.UI.Card.saveProperty($('#n'))}, 500); + } + }, + saveProperty:function(obj) { + if(!$(obj).hasClass('contacts_property')) { + return false; + } + if($(obj).hasClass('nonempty') && $(obj).val().trim() == '') { + OC.dialogs.alert(t('contacts', 'This property has to be non-empty.'), t('contacts', 'Error')); + return false; + } + container = $(obj).parents('.propertycontainer').first(); // get the parent holding the metadata. + OC.Contacts.loading(obj, true); + var checksum = container.data('checksum'); + var name = container.data('element'); + var fields = container.find('input.contacts_property,select.contacts_property').serializeArray(); + switch(name) { + case 'FN': + var nempty = true; + for(var i in OC.Contacts.Card.data.N[0]['value']) { + if(OC.Contacts.Card.data.N[0]['value'][i] != '') { + nempty = false; + break; } - break; - } - var q = container.find('input.contacts_property,select.contacts_property,textarea.contacts_property').serialize(); - if(q == '' || q == undefined) { - OC.dialogs.alert(t('contacts', 'Couldn\'t serialize elements.'), t('contacts', 'Error')); - Contacts.UI.loading(obj, false); - return false; - } - q = q + '&id=' + this.id + '&name=' + name; - if(checksum != undefined && checksum != '') { // save + } + if(nempty) { + $('#n').val(fields[0].value + ';;;;'); + OC.Contacts.Card.data.N[0]['value'] = Array(fields[0].value, '', '', '', ''); + setTimeout(function() {OC.Contacts.Card.saveProperty($('#n'))}, 500); + } + break; + } + var q = container.find('input.contacts_property,select.contacts_property,textarea.contacts_property').serialize(); + if(q == '' || q == undefined) { + OC.dialogs.alert(t('contacts', 'Couldn\'t serialize elements.'), t('contacts', 'Error')); + OC.Contacts.loading(obj, false); + return false; + } + q = q + '&id=' + this.id + '&name=' + name; + if(checksum != undefined && checksum != '') { // save q = q + '&checksum=' + checksum; - //console.log('Saving: ' + q); + console.log('Saving: ' + q); $(obj).attr('disabled', 'disabled'); - $.post(OC.filePath('contacts', 'ajax', 'saveproperty.php'),q,function(jsondata){ + $.post(OC.filePath('contacts', 'ajax', 'contact/saveproperty.php'),q,function(jsondata){ + if(!jsondata) { + OC.dialogs.alert(t('contacts', 'Unknown error. Please check logs.'), t('contacts', 'Error')); + OC.Contacts.loading(obj, false); + $(obj).removeAttr('disabled'); + OC.Contacts.Card.update({cid:OC.Contacts.Card.id}); + return false; + } if(jsondata.status == 'success'){ container.data('checksum', jsondata.data.checksum); - Contacts.UI.Card.savePropertyInternal(name, fields, checksum, jsondata.data.checksum); - Contacts.UI.loading(obj, false); + OC.Contacts.Card.savePropertyInternal(name, fields, checksum, jsondata.data.checksum); + OC.Contacts.loading(obj, false); $(obj).removeAttr('disabled'); return true; } else{ OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); - Contacts.UI.loading(obj, false); + OC.Contacts.loading(obj, false); $(obj).removeAttr('disabled'); + OC.Contacts.Card.update({cid:OC.Contacts.Card.id}); return false; } },'json'); - } else { // add - //console.log('Adding: ' + q); + } else { // add + console.log('Adding: ' + q); $(obj).attr('disabled', 'disabled'); - $.post(OC.filePath('contacts', 'ajax', 'addproperty.php'),q,function(jsondata){ + $.post(OC.filePath('contacts', 'ajax', 'contact/addproperty.php'),q,function(jsondata){ if(jsondata.status == 'success'){ container.data('checksum', jsondata.data.checksum); // TODO: savePropertyInternal doesn't know about new fields - //Contacts.UI.Card.savePropertyInternal(name, fields, checksum, jsondata.data.checksum); - Contacts.UI.loading(obj, false); + //OC.Contacts.Card.savePropertyInternal(name, fields, checksum, jsondata.data.checksum); + OC.Contacts.loading(obj, false); $(obj).removeAttr('disabled'); return true; } else{ OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); - Contacts.UI.loading(obj, false); + OC.Contacts.loading(obj, false); $(obj).removeAttr('disabled'); + OC.Contacts.Card.update({cid:OC.Contacts.Card.id}); return false; } },'json'); - } - }, - addProperty:function(type){ - switch (type) { - case 'PHOTO': - this.loadPhoto(true); - $('#file_upload_form').show(); - $('#contacts_propertymenu_dropdown a[data-type="'+type+'"]').parent().hide(); - $('#file_upload_start').trigger('click'); - break; - case 'NOTE': - $('#note').show(); - $('#contacts_propertymenu_dropdown a[data-type="'+type+'"]').parent().hide(); - $('#note').find('textarea').expandingTextarea(); - $('#note').find('textarea').focus(); - break; - case 'EMAIL': - if($('#emaillist>li').length == 1) { - $('#emails').show(); - } - Contacts.UI.Card.addMail(); - break; - case 'TEL': - if($('#phonelist>li').length == 1) { - $('#phones').show(); - } - Contacts.UI.Card.addPhone(); - break; - case 'ADR': - if($('#addressdisplay>dl').length == 1) { - $('#addresses').show(); - } - Contacts.UI.Card.editAddress('new', true); - break; - case 'NICKNAME': - case 'ORG': - case 'BDAY': - case 'CATEGORIES': - $('dl dt[data-element="'+type+'"],dd[data-element="'+type+'"]').show(); - $('dd[data-element="'+type+'"]').find('input').focus(); - $('#contacts_propertymenu_dropdown a[data-type="'+type+'"]').parent().hide(); - break; - } - }, - deleteProperty:function(obj, type){ - Contacts.UI.loading(obj, true); - var checksum = Contacts.UI.checksumFor(obj); - if(checksum) { - $.post(OC.filePath('contacts', 'ajax', 'deleteproperty.php'),{'id': this.id, 'checksum': checksum },function(jsondata){ - if(jsondata.status == 'success'){ - if(type == 'list') { - Contacts.UI.propertyContainerFor(obj).remove(); - } else if(type == 'single') { - var proptype = Contacts.UI.propertyTypeFor(obj); - Contacts.UI.Card.data[proptype] = null; - var othertypes = ['NOTE', 'PHOTO']; - if(othertypes.indexOf(proptype) != -1) { - Contacts.UI.propertyContainerFor(obj).data('checksum', ''); - if(proptype == 'PHOTO') { - Contacts.UI.Contacts.refreshThumbnail(Contacts.UI.Card.id); - Contacts.UI.Card.loadPhoto(true); - } else if(proptype == 'NOTE') { - $('#note').find('textarea').val(''); - Contacts.UI.propertyContainerFor(obj).hide(); - } - } else { - $('dl dt[data-element="'+proptype+'"],dd[data-element="'+proptype+'"]').hide(); - $('dl dd[data-element="'+proptype+'"]').data('checksum', ''); - $('dl dd[data-element="'+proptype+'"]').find('input').val(''); + } + }, + addProperty:function(type) { + if(!this.enabled) { + return; + } + switch (type) { + case 'NOTE': + $('#contacts_propertymenu_dropdown a[data-type="'+type+'"]').parent().hide(); + $('#note').find('textarea').expandingTextarea().show().focus(); + $('#contact_note').show(); + break; + case 'EMAIL': + if($('#emaillist>li').length == 1) { + $('#emails').show(); + } + OC.Contacts.Card.addMail(); + break; + case 'TEL': + if($('#phonelist>li').length == 1) { + $('#phones').show(); + } + OC.Contacts.Card.addPhone(); + break; + case 'IMPP': + if($('#imlist>li').length == 1) { + $('#ims').show(); + } + OC.Contacts.Card.addIM(); + break; + case 'ADR': + if($('addresses>dl').length == 1) { + $('#addresses').show(); + } + OC.Contacts.Card.editAddress('new', true); + break; + case 'NICKNAME': + case 'URL': + case 'ORG': + case 'BDAY': + case 'CATEGORIES': + $('dl dt[data-element="'+type+'"],dd[data-element="'+type+'"]').show(); + $('dd[data-element="'+type+'"]').find('input').focus(); + $('#contacts_propertymenu_dropdown a[data-type="'+type+'"]').parent().hide(); + break; + } + }, + deleteProperty:function(obj, type) { + console.log('deleteProperty'); + if(!this.enabled) { + return; + } + OC.Contacts.loading(obj, true); + var checksum = OC.Contacts.checksumFor(obj); + if(checksum) { + $.post(OC.filePath('contacts', 'ajax', 'contact/deleteproperty.php'),{'id': this.id, 'checksum': checksum },function(jsondata){ + if(jsondata.status == 'success'){ + if(type == 'list') { + OC.Contacts.propertyContainerFor(obj).remove(); + } else if(type == 'single') { + var proptype = OC.Contacts.propertyTypeFor(obj); + OC.Contacts.Card.data[proptype] = null; + var othertypes = ['NOTE', 'PHOTO']; + if(othertypes.indexOf(proptype) != -1) { + OC.Contacts.propertyContainerFor(obj).data('checksum', ''); + if(proptype == 'PHOTO') { + OC.Contacts.Contacts.refreshThumbnail(OC.Contacts.Card.id); + OC.Contacts.Card.loadPhoto(); + } else if(proptype == 'NOTE') { + $('#note').find('textarea').val(''); + $('#contact_note').hide(); + OC.Contacts.propertyContainerFor(obj).hide(); } - $('#contacts_propertymenu_dropdown a[data-type="'+proptype+'"]').parent().show(); - Contacts.UI.loading(obj, false); } else { - OC.dialogs.alert(t('contacts', '\'deleteProperty\' called without type argument. Please report at bugs.owncloud.org'), t('contacts', 'Error')); - Contacts.UI.loading(obj, false); + $('dl dt[data-element="'+proptype+'"],dd[data-element="'+proptype+'"]').hide(); + $('dl dd[data-element="'+proptype+'"]').data('checksum', '').find('input').val(''); } + $('#contacts_propertymenu_dropdown a[data-type="'+proptype+'"]').parent().show(); + OC.Contacts.loading(obj, false); + } else { + OC.dialogs.alert(t('contacts', '\'deleteProperty\' called without type argument. Please report at bugs.owncloud.org'), t('contacts', 'Error')); + OC.Contacts.loading(obj, false); } - else{ - Contacts.UI.loading(obj, false); - OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); - } - }); - } else { // Property hasn't been saved so there's nothing to delete. - if(type == 'list') { - Contacts.UI.propertyContainerFor(obj).remove(); - } else if(type == 'single') { - var proptype = Contacts.UI.propertyTypeFor(obj); - $('dl dt[data-element="'+proptype+'"],dd[data-element="'+proptype+'"]').hide(); - $('#contacts_propertymenu_dropdown a[data-type="'+proptype+'"]').parent().show(); - Contacts.UI.loading(obj, false); - } else { - OC.dialogs.alert(t('contacts', '\'deleteProperty\' called without type argument. Please report at bugs.owncloud.org'), t('contacts', 'Error')); } + else{ + OC.Contacts.loading(obj, false); + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); + } + }); + } else { // Property hasn't been saved so there's nothing to delete. + if(type == 'list') { + OC.Contacts.propertyContainerFor(obj).remove(); + } else if(type == 'single') { + var proptype = OC.Contacts.propertyTypeFor(obj); + $('dl dt[data-element="'+proptype+'"],dd[data-element="'+proptype+'"]').hide(); + $('#contacts_propertymenu_dropdown a[data-type="'+proptype+'"]').parent().show(); + OC.Contacts.loading(obj, false); + } else { + OC.dialogs.alert(t('contacts', '\'deleteProperty\' called without type argument. Please report at bugs.owncloud.org'), t('contacts', 'Error')); } - }, - editName:function(){ - var isnew = (this.id == ''); - /* Initialize the name edit dialog */ - if($('#edit_name_dialog').dialog('isOpen') == true){ - $('#edit_name_dialog').dialog('moveToTop'); - }else{ - $('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'editname.php')+'?id='+this.id, function(jsondata){ - if(jsondata.status != 'error'){ - $('#edit_name_dialog' ).dialog({ - modal: true, - closeOnEscape: (isnew == '' && false || true), - title: (isnew && t('contacts', 'Add contact') || t('contacts', 'Edit name')), - height: 'auto', width: 'auto', - buttons: { - 'Ok':function() { - Contacts.UI.Card.saveName(this); - $(this).dialog('destroy').remove(); - }, - 'Cancel':function() { $(this).dialog('destroy').remove(); } - }, - close: function(event, ui) { - $(this).dialog('destroy').remove(); - //return event; + } + }, + editName:function() { + if(!this.enabled) { + return; + } + var params = {id: this.id}; + /* Initialize the name edit dialog */ + if($('#edit_name_dialog').dialog('isOpen') == true) { + $('#edit_name_dialog').dialog('moveToTop'); + } else { + $.getJSON(OC.filePath('contacts', 'ajax', 'editname.php'),{id: this.id},function(jsondata) { + if(jsondata.status == 'success') { + $('body').append('<div id="name_dialog"></div>'); + $('#name_dialog').html(jsondata.data.page).find('#edit_name_dialog' ).dialog({ + modal: true, + closeOnEscape: true, + title: t('contacts', 'Edit name'), + height: 'auto', width: 'auto', + buttons: { + 'Ok':function() { + OC.Contacts.Card.saveName(this); + $(this).dialog('close'); }, - open: function(event, ui) { - // load 'N' property - maybe :-P - } - }); - } else { - OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); - } - }); + 'Cancel':function() { $(this).dialog('close'); } + }, + close: function(event, ui) { + $(this).dialog('destroy').remove(); + $('#name_dialog').remove(); + }, + open: function(event, ui) { + // load 'N' property - maybe :-P + } + }); + } else { + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); + } + }); + } + }, + saveName:function(dlg) { + if(!this.enabled) { + return; + } + //console.log('saveName, id: ' + this.id); + var n = new Array($(dlg).find('#fam').val().strip_tags(),$(dlg).find('#giv').val().strip_tags(),$(dlg).find('#add').val().strip_tags(),$(dlg).find('#pre').val().strip_tags(),$(dlg).find('#suf').val().strip_tags()); + this.famname = n[0]; + this.givname = n[1]; + this.addname = n[2]; + this.honpre = n[3]; + this.honsuf = n[4]; + this.fullname = ''; + + $('#n').val(n.join(';')); + if(n[3].length > 0) { + this.fullname = n[3] + ' '; + } + this.fullname += n[1] + ' ' + n[2] + ' ' + n[0]; + if(n[4].length > 0) { + this.fullname += ', ' + n[4]; + } + + $('#fn_select option').remove(); + //$('#fn_select').combobox('value', this.fn); + var tmp = [this.fullname, this.givname + ' ' + this.famname, this.famname + ' ' + this.givname, this.famname + ', ' + this.givname]; + var names = new Array(); + for(var name in tmp) { + if(names.indexOf(tmp[name]) == -1) { + names.push(tmp[name]); } - }, - saveName:function(dlg){ - //console.log('saveName, id: ' + this.id); - var n = new Array($(dlg).find('#fam').val().strip_tags(),$(dlg).find('#giv').val().strip_tags(),$(dlg).find('#add').val().strip_tags(),$(dlg).find('#pre').val().strip_tags(),$(dlg).find('#suf').val().strip_tags()); - this.famname = n[0]; - this.givname = n[1]; - this.addname = n[2]; - this.honpre = n[3]; - this.honsuf = n[4]; - this.fullname = ''; + } + $.each(names, function(key, value) { + $('#fn_select') + .append($('<option></option>') + .text(value)); + }); - $('#n').val(n.join(';')); - if(n[3].length > 0) { - this.fullname = n[3] + ' '; + if(this.id == '') { + var aid = $(dlg).find('#aid').val(); + OC.Contacts.Card.add(n.join(';'), $('#short').text(), aid); + } else { + OC.Contacts.Card.saveProperty($('#n')); + } + }, + loadAddresses:function() { + $('#addresses').hide(); + $('#addresses dl.propertycontainer').remove(); + var addresscontainer = $('#addresses'); + for(var adr in this.data.ADR) { + addresscontainer.find('dl').first().clone().insertAfter($('#addresses dl').last()).show(); + addresscontainer.find('dl').last().removeClass('template').addClass('propertycontainer'); + addresscontainer.find('dl').last().data('checksum', this.data.ADR[adr]['checksum']); + var adrarray = this.data.ADR[adr]['value']; + var adrtxt = ''; + if(adrarray[0] && adrarray[0].length > 0) { + adrtxt = adrtxt + '<li>' + adrarray[0].strip_tags() + '</li>'; } - this.fullname += n[1] + ' ' + n[2] + ' ' + n[0]; - if(n[4].length > 0) { - this.fullname += ', ' + n[4]; + if(adrarray[1] && adrarray[1].length > 0) { + adrtxt = adrtxt + '<li>' + adrarray[1].strip_tags() + '</li>'; } - - $('#fn_select option').remove(); - //$('#fn_select').combobox('value', this.fn); - var tmp = [this.fullname, this.givname + ' ' + this.famname, this.famname + ' ' + this.givname, this.famname + ', ' + this.givname]; - var names = new Array(); - for(var name in tmp) { - if(names.indexOf(tmp[name]) == -1) { - names.push(tmp[name]); - } + if(adrarray[2] && adrarray[2].length > 0) { + adrtxt = adrtxt + '<li>' + adrarray[2].strip_tags() + '</li>'; } - $.each(names, function(key, value) { - $('#fn_select') - .append($('<option></option>') - .text(value)); - }); - - if(this.id == '') { - var aid = $(dlg).find('#aid').val(); - Contacts.UI.Card.add(n.join(';'), $('#short').text(), aid); - } else { - Contacts.UI.Card.saveProperty($('#n')); + if((3 in adrarray && 5 in adrarray) && adrarray[3].length > 0 || adrarray[5].length > 0) { + adrtxt = adrtxt + '<li>' + adrarray[5].strip_tags() + ' ' + adrarray[3].strip_tags() + '</li>'; } - }, - loadAddresses:function(){ - $('#addresses').hide(); - $('#addressdisplay dl.propertycontainer').remove(); - for(var adr in this.data.ADR) { - $('#addressdisplay dl').first().clone().insertAfter($('#addressdisplay dl').last()).show(); - $('#addressdisplay dl').last().removeClass('template').addClass('propertycontainer'); - $('#addressdisplay dl').last().data('checksum', this.data.ADR[adr]['checksum']); - var adrarray = this.data.ADR[adr]['value']; - var adrtxt = ''; - if(adrarray[0] && adrarray[0].length > 0) { - adrtxt = adrtxt + '<li>' + adrarray[0].strip_tags() + '</li>'; - } - if(adrarray[1] && adrarray[1].length > 0) { - adrtxt = adrtxt + '<li>' + adrarray[1].strip_tags() + '</li>'; - } - if(adrarray[2] && adrarray[2].length > 0) { - adrtxt = adrtxt + '<li>' + adrarray[2].strip_tags() + '</li>'; - } - if((adrarray[3] && adrarray[5]) && adrarray[3].length > 0 || adrarray[5].length > 0) { - adrtxt = adrtxt + '<li>' + adrarray[5].strip_tags() + ' ' + adrarray[3].strip_tags() + '</li>'; - } - if(adrarray[4] && adrarray[4].length > 0) { - adrtxt = adrtxt + '<li>' + adrarray[4].strip_tags() + '</li>'; - } - if(adrarray[6] && adrarray[6].length > 0) { - adrtxt = adrtxt + '<li>' + adrarray[6].strip_tags() + '</li>'; - } - $('#addressdisplay dl').last().find('.addresslist').html(adrtxt); - var types = new Array(); - var ttypes = new Array(); - for(var param in this.data.ADR[adr]['parameters']) { - if(param.toUpperCase() == 'TYPE') { - types.push(t('contacts', ucwords(this.data.ADR[adr]['parameters'][param].toLowerCase()))); - ttypes.push(this.data.ADR[adr]['parameters'][param]); - } - } - $('#addressdisplay dl').last().find('.adr_type_label').text(types.join('/')); - $('#addressdisplay dl').last().find('.adr_type').val(ttypes.join(',')); - $('#addressdisplay dl').last().find('.adr').val(adrarray.join(';')); - $('#addressdisplay dl').last().data('checksum', this.data.ADR[adr]['checksum']); + if(adrarray[4] && adrarray[4].length > 0) { + adrtxt = adrtxt + '<li>' + adrarray[4].strip_tags() + '</li>'; } - if($('#addressdisplay dl').length > 1) { - $('#addresses').show(); - $('#contact_communication').show(); + if(adrarray[6] && adrarray[6].length > 0) { + adrtxt = adrtxt + '<li>' + adrarray[6].strip_tags() + '</li>'; } - return false; - }, - editAddress:function(obj, isnew){ - var container = undefined; - var q = q = '?id=' + this.id; - if(obj === 'new') { - isnew = true; - $('#addressdisplay dl').first().clone(true).insertAfter($('#addressdisplay dl').last()).show(); - container = $('#addressdisplay dl').last(); - container.removeClass('template').addClass('propertycontainer'); - } else { - q = q + '&checksum='+Contacts.UI.checksumFor(obj); + addresscontainer.find('dl').last().find('.addresslist').html(adrtxt); + var types = new Array(); + var ttypes = new Array(); + for(var param in this.data.ADR[adr]['parameters']) { + if(param.toUpperCase() == 'TYPE') { + types.push(t('contacts', ucwords(this.data.ADR[adr]['parameters'][param].toLowerCase()))); + ttypes.push(this.data.ADR[adr]['parameters'][param]); + } } - /* Initialize the address edit dialog */ - if($('#edit_address_dialog').dialog('isOpen') == true){ - $('#edit_address_dialog').dialog('moveToTop'); - }else{ - $('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'editaddress.php')+q, function(jsondata){ - if(jsondata.status != 'error'){ - $('#edit_address_dialog' ).dialog({ - /*modal: true,*/ - height: 'auto', width: 'auto', - buttons: { - 'Ok':function() { - if(isnew) { - Contacts.UI.Card.saveAddress(this, $('#addressdisplay dl:last-child').find('input').first(), isnew); - } else { - Contacts.UI.Card.saveAddress(this, obj, isnew); - } - $(this).dialog('destroy').remove(); - }, - 'Cancel':function() { - $(this).dialog('destroy').remove(); - if(isnew) { - container.remove(); - } + addresscontainer.find('dl').last().find('.adr_type_label').text(types.join('/')); + addresscontainer.find('dl').last().find('.adr_type').val(ttypes.join(',')); + addresscontainer.find('dl').last().find('.adr').val(adrarray.join(';')); + addresscontainer.find('dl').last().data('checksum', this.data.ADR[adr]['checksum']); + } + if(addresscontainer.find('dl').length > 1) { + $('#addresses').show(); + } + return false; + }, + editAddress:function(obj, isnew){ + if(!this.enabled) { + return; + } + var container = undefined; + var params = {id: this.id}; + if(obj === 'new') { + isnew = true; + $('#addresses dl').first().clone(true).insertAfter($('#addresses dl').last()).show(); + container = $('#addresses dl').last(); + container.removeClass('template').addClass('propertycontainer'); + } else { + params['checksum'] = OC.Contacts.checksumFor(obj); + } + /* Initialize the address edit dialog */ + if($('#edit_address_dialog').dialog('isOpen') == true){ + $('#edit_address_dialog').dialog('moveToTop'); + }else{ + $.getJSON(OC.filePath('contacts', 'ajax', 'editaddress.php'),params,function(jsondata){ + if(jsondata.status == 'success'){ + $('body').append('<div id="address_dialog"></div>'); + $('#address_dialog').html(jsondata.data.page).find('#edit_address_dialog' ).dialog({ + height: 'auto', width: 'auto', + buttons: { + 'Ok':function() { + if(isnew) { + OC.Contacts.Card.saveAddress(this, $('#addresses dl:last-child').find('input').first(), isnew); + } else { + OC.Contacts.Card.saveAddress(this, obj, isnew); } + $(this).dialog('close'); }, - close : function(event, ui) { - $(this).dialog('destroy').remove(); + 'Cancel':function() { + $(this).dialog('close'); if(isnew) { container.remove(); } - }, - open : function(event, ui) { - $( "#adr_city" ).autocomplete({ - source: function( request, response ) { - $.ajax({ - url: "http://ws.geonames.org/searchJSON", - dataType: "jsonp", - data: { - featureClass: "P", - style: "full", - maxRows: 12, - lang: lang, - name_startsWith: request.term - }, - success: function( data ) { - response( $.map( data.geonames, function( item ) { - return { - label: item.name + (item.adminName1 ? ", " + item.adminName1 : "") + ", " + item.countryName, - value: item.name, - country: item.countryName - } - })); - } - }); - }, - minLength: 2, - select: function( event, ui ) { - if(ui.item && $('#adr_country').val().trim().length == 0) { - $('#adr_country').val(ui.item.country); + } + }, + close : function(event, ui) { + $(this).dialog('destroy').remove(); + $('#address_dialog').remove(); + }, + open : function(event, ui) { + $( "#adr_city" ).autocomplete({ + source: function( request, response ) { + $.ajax({ + url: "http://ws.geonames.org/searchJSON", + dataType: "jsonp", + data: { + featureClass: "P", + style: "full", + maxRows: 12, + lang: lang, + name_startsWith: request.term + }, + success: function( data ) { + response( $.map( data.geonames, function( item ) { + return { + label: item.name + (item.adminName1 ? ", " + item.adminName1 : "") + ", " + item.countryName, + value: item.name, + country: item.countryName + } + })); } - }, - open: function() { - $( this ).removeClass( "ui-corner-all" ).addClass( "ui-corner-top" ); - }, - close: function() { - $( this ).removeClass( "ui-corner-top" ).addClass( "ui-corner-all" ); + }); + }, + minLength: 2, + select: function( event, ui ) { + if(ui.item && $('#adr_country').val().trim().length == 0) { + $('#adr_country').val(ui.item.country); } - }); - $( "#adr_country" ).autocomplete({ - source: function( request, response ) { - $.ajax({ - url: "http://ws.geonames.org/searchJSON", - dataType: "jsonp", - data: { - /*featureClass: "A",*/ - featureCode: "PCLI", - /*countryBias: "true",*/ - /*style: "full",*/ - lang: lang, - maxRows: 12, - name_startsWith: request.term - }, - success: function( data ) { - response( $.map( data.geonames, function( item ) { - return { - label: item.name, - value: item.name - } - })); - } - }); - }, - minLength: 2, - select: function( event, ui ) { - /*if(ui.item) { - $('#adr_country').val(ui.item.country); + }, + open: function() { + $( this ).removeClass( "ui-corner-all" ).addClass( "ui-corner-top" ); + }, + close: function() { + $( this ).removeClass( "ui-corner-top" ).addClass( "ui-corner-all" ); + } + }); + $('#adr_country').autocomplete({ + source: function( request, response ) { + $.ajax({ + url: "http://ws.geonames.org/searchJSON", + dataType: "jsonp", + data: { + /*featureClass: "A",*/ + featureCode: "PCLI", + /*countryBias: "true",*/ + /*style: "full",*/ + lang: lang, + maxRows: 12, + name_startsWith: request.term + }, + success: function( data ) { + response( $.map( data.geonames, function( item ) { + return { + label: item.name, + value: item.name + } + })); } - log( ui.item ? - "Selected: " + ui.item.label : - "Nothing selected, input was " + this.value);*/ - }, - open: function() { - $( this ).removeClass( "ui-corner-all" ).addClass( "ui-corner-top" ); - }, - close: function() { - $( this ).removeClass( "ui-corner-top" ).addClass( "ui-corner-all" ); + }); + }, + minLength: 2, + select: function( event, ui ) { + /*if(ui.item) { + $('#adr_country').val(ui.item.country); } - }); - } - }); - } else { - alert(jsondata.data.message); - } - }); - } - }, - saveAddress:function(dlg, obj, isnew){ - if(isnew) { - container = $('#addressdisplay dl').last(); - obj = $('#addressdisplay dl:last-child').find('input').first(); - } else { - checksum = Contacts.UI.checksumFor(obj); - container = Contacts.UI.propertyContainerFor(obj); + log( ui.item ? + "Selected: " + ui.item.label : + "Nothing selected, input was " + this.value);*/ + }, + open: function() { + $( this ).removeClass( "ui-corner-all" ).addClass( "ui-corner-top" ); + }, + close: function() { + $( this ).removeClass( "ui-corner-top" ).addClass( "ui-corner-all" ); + } + }); + } + }); + } else { + alert(jsondata.data.message); + } + }); + } + }, + saveAddress:function(dlg, obj, isnew){ + if(!this.enabled) { + return; + } + if(isnew) { + container = $('#addresses dl').last(); + obj = container.find('input').first(); + } else { + checksum = OC.Contacts.checksumFor(obj); + container = OC.Contacts.propertyContainerFor(obj); + } + var adr = new Array( + $(dlg).find('#adr_pobox').val().strip_tags(), + $(dlg).find('#adr_extended').val().strip_tags(), + $(dlg).find('#adr_street').val().strip_tags(), + $(dlg).find('#adr_city').val().strip_tags(), + $(dlg).find('#adr_region').val().strip_tags(), + $(dlg).find('#adr_zipcode').val().strip_tags(), + $(dlg).find('#adr_country').val().strip_tags() + ); + container.find('.adr').val(adr.join(';')); + container.find('.adr_type').val($(dlg).find('#adr_type').val()); + container.find('.adr_type_label').html(t('contacts',ucwords($(dlg).find('#adr_type').val().toLowerCase()))); + OC.Contacts.Card.saveProperty($(container).find('input').first()); + var adrtxt = ''; + if(adr[0].length > 0) { + adrtxt = adrtxt + '<li>' + adr[0] + '</li>'; + } + if(adr[1].length > 0) { + adrtxt = adrtxt + '<li>' + adr[1] + '</li>'; + } + if(adr[2].length > 0) { + adrtxt = adrtxt + '<li>' + adr[2] + '</li>'; + } + if(adr[3].length > 0 || adr[5].length > 0) { + adrtxt = adrtxt + '<li>' + adr[5] + ' ' + adr[3] + '</li>'; + } + if(adr[4].length > 0) { + adrtxt = adrtxt + '<li>' + adr[4] + '</li>'; + } + if(adr[6].length > 0) { + adrtxt = adrtxt + '<li>' + adr[6] + '</li>'; + } + container.find('.addresslist').html(adrtxt); + }, + uploadPhoto:function(filelist) { + if(!this.enabled) { + return; + } + if(!filelist) { + OC.dialogs.alert(t('contacts','No files selected for upload.'), t('contacts', 'Error')); + return; + } + var file = filelist[0]; + var target = $('#file_upload_target'); + var form = $('#file_upload_form'); + var totalSize=0; + if(file.size > $('#max_upload').val()){ + OC.dialogs.alert(t('contacts','The file you are trying to upload exceed the maximum size for file uploads on this server.'), t('contacts', 'Error')); + return; + } else { + target.load(function(){ + var response=jQuery.parseJSON(target.contents().text()); + if(response != undefined && response.status == 'success'){ + OC.Contacts.Card.editPhoto(response.data.id, response.data.tmp); + //alert('File: ' + file.tmp + ' ' + file.name + ' ' + file.mime); + }else{ + OC.dialogs.alert(response.data.message, t('contacts', 'Error')); + } + }); + form.submit(); + } + }, + loadPhotoHandlers:function() { + var phototools = $('#phototools'); + phototools.find('li a').tipsy('hide'); + phototools.find('li a').tipsy(); + if(this.data.PHOTO) { + phototools.find('.delete').click(function() { + $(this).tipsy('hide'); + OC.Contacts.Card.deleteProperty($('#contacts_details_photo'), 'single'); + $(this).hide(); + }); + phototools.find('.edit').click(function() { + $(this).tipsy('hide'); + OC.Contacts.Card.editCurrentPhoto(); + }); + phototools.find('.delete').show(); + phototools.find('.edit').show(); + } else { + phototools.find('.delete').hide(); + phototools.find('.edit').hide(); + } + }, + cloudPhotoSelected:function(path){ + $.getJSON(OC.filePath('contacts', 'ajax', 'oc_photo.php'),{'path':path,'id':OC.Contacts.Card.id},function(jsondata){ + if(jsondata.status == 'success'){ + //alert(jsondata.data.page); + OC.Contacts.Card.editPhoto(jsondata.data.id, jsondata.data.tmp) + $('#edit_photo_dialog_img').html(jsondata.data.page); } - var adr = new Array($(dlg).find('#adr_pobox').val().strip_tags(),$(dlg).find('#adr_extended').val().strip_tags(),$(dlg).find('#adr_street').val().strip_tags(),$(dlg).find('#adr_city').val().strip_tags(),$(dlg).find('#adr_region').val().strip_tags(),$(dlg).find('#adr_zipcode').val().strip_tags(),$(dlg).find('#adr_country').val().strip_tags()); - $(container).find('.adr').val(adr.join(';')); - $(container).find('.adr_type').val($(dlg).find('#adr_type').val()); - $(container).find('.adr_type_label').html(t('contacts',ucwords($(dlg).find('#adr_type').val().toLowerCase()))); - Contacts.UI.Card.saveProperty($(container).find('input').first()); - var adrtxt = ''; - if(adr[0].length > 0) { - adrtxt = adrtxt + '<li>' + adr[0] + '</li>'; + else{ + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); } - if(adr[1].length > 0) { - adrtxt = adrtxt + '<li>' + adr[1] + '</li>'; + }); + }, + loadPhoto:function(){ + var self = this; + var refreshstr = '&refresh='+Math.random(); + $('#phototools li a').tipsy('hide'); + var wrapper = $('#contacts_details_photo_wrapper'); + wrapper.addClass('loading').addClass('wait'); + delete this.photo; + this.photo = new Image(); + $(this.photo).load(function () { + $('img.contacts_details_photo').remove() + $(this).addClass('contacts_details_photo'); + wrapper.css('width', $(this).get(0).width + 10); + wrapper.removeClass('loading').removeClass('wait'); + $(this).insertAfter($('#phototools')).fadeIn(); + }).error(function () { + // notify the user that the image could not be loaded + OC.Contacts.notify({message:t('contacts','Error loading profile picture.')}); + }).attr('src', OC.linkTo('contacts', 'photo.php')+'?id='+self.id+refreshstr); + this.loadPhotoHandlers() + }, + editCurrentPhoto:function(){ + if(!this.enabled) { + return; + } + $.getJSON(OC.filePath('contacts', 'ajax', 'currentphoto.php'),{'id':this.id},function(jsondata){ + if(jsondata.status == 'success'){ + //alert(jsondata.data.page); + OC.Contacts.Card.editPhoto(jsondata.data.id, jsondata.data.tmp) + $('#edit_photo_dialog_img').html(jsondata.data.page); } - if(adr[2].length > 0) { - adrtxt = adrtxt + '<li>' + adr[2] + '</li>'; + else{ + wrapper.removeClass('wait'); + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); } - if(adr[3].length > 0 || adr[5].length > 0) { - adrtxt = adrtxt + '<li>' + adr[5] + ' ' + adr[3] + '</li>'; + }); + }, + editPhoto:function(id, tmpkey){ + if(!this.enabled) { + return; + } + //alert('editPhoto: ' + tmpkey); + $.getJSON(OC.filePath('contacts', 'ajax', 'cropphoto.php'),{'tmpkey':tmpkey,'id':this.id, 'requesttoken':requesttoken},function(jsondata){ + if(jsondata.status == 'success'){ + //alert(jsondata.data.page); + $('#edit_photo_dialog_img').html(jsondata.data.page); } - if(adr[4].length > 0) { - adrtxt = adrtxt + '<li>' + adr[4] + '</li>'; + else{ + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); } - if(adr[6].length > 0) { - adrtxt = adrtxt + '<li>' + adr[6] + '</li>'; + }); + if($('#edit_photo_dialog').dialog('isOpen') == true){ + $('#edit_photo_dialog').dialog('moveToTop'); + } else { + $('#edit_photo_dialog').dialog('open'); + } + }, + savePhoto:function() { + if(!this.enabled) { + return; + } + var target = $('#crop_target'); + var form = $('#cropform'); + var wrapper = $('#contacts_details_photo_wrapper'); + var self = this; + wrapper.addClass('wait'); + form.submit(); + target.load(function(){ + var response=jQuery.parseJSON(target.contents().text()); + if(response != undefined && response.status == 'success'){ + // load cropped photo. + self.loadPhoto(); + OC.Contacts.Card.data.PHOTO = true; + }else{ + OC.dialogs.alert(response.data.message, t('contacts', 'Error')); + wrapper.removeClass('wait'); } - $(container).find('.addresslist').html(adrtxt); - }, - uploadPhoto:function(filelist) { - if(!filelist) { - OC.dialogs.alert(t('contacts','No files selected for upload.'), t('contacts', 'Error')); - return; + }); + OC.Contacts.Contacts.refreshThumbnail(this.id); + }, + addIM:function() { + //alert('addMail'); + var imlist = $('#imlist'); + imlist.find('li.template:first-child').clone(true).appendTo(imlist).show().find('a .tip').tipsy(); + imlist.find('li.template:last-child').find('select').addClass('contacts_property'); + imlist.find('li.template:last-child').removeClass('template').addClass('propertycontainer'); + imlist.find('li:last-child').find('input[type="text"]').focus(); + return false; + }, + loadIMs:function() { + //console.log('loadIMs'); + $('#ims').hide(); + $('#imlist li.propertycontainer').remove(); + var imlist = $('#imlist'); + for(var im in this.data.IMPP) { + this.addIM(); + var curim = imlist.find('li.propertycontainer:last-child'); + if(typeof this.data.IMPP[im].label != 'undefined') { + curim.prepend('<label class="xab">'+this.data.IMPP[im].label+'</label>'); + } + curim.data('checksum', this.data.IMPP[im]['checksum']) + curim.find('input[type="text"]').val(this.data.IMPP[im]['value'].split(':').pop()); + for(var param in this.data.IMPP[im]['parameters']) { + if(param.toUpperCase() == 'PREF') { + curim.find('input[type="checkbox"]').attr('checked', 'checked') + } + else if(param.toUpperCase() == 'TYPE') { + if(typeof this.data.IMPP[im]['parameters'][param] == 'string') { + var found = false; + var imt = this.data.IMPP[im]['parameters'][param]; + curim.find('select.types option').each(function(){ + if($(this).val().toUpperCase() == imt.toUpperCase()) { + $(this).attr('selected', 'selected'); + found = true; + } + }); + if(!found) { + curim.find('select.type option:last-child').after('<option value="'+imt+'" selected="selected">'+imt+'</option>'); + } + } else if(typeof this.data.IMPP[im]['parameters'][param] == 'object') { + for(imtype in this.data.IMPP[im]['parameters'][param]) { + var found = false; + var imt = this.data.IMPP[im]['parameters'][param][imtype]; + curim.find('select.types option').each(function(){ + if($(this).val().toUpperCase() == imt.toUpperCase().split(',')) { + $(this).attr('selected', 'selected'); + found = true; + } + }); + if(!found) { + curim.find('select.type option:last-child').after('<option value="'+imt+'" selected="selected">'+imt+'</option>'); + } + } + } + } + else if(param.toUpperCase() == 'X-SERVICE-TYPE') { + curim.find('select.impp').val(this.data.IMPP[im]['parameters'][param].toLowerCase()); + } } - var file = filelist[0]; - var target = $('#file_upload_target'); - var form = $('#file_upload_form'); - var totalSize=0; - if(file.size > $('#max_upload').val()){ - OC.dialogs.alert(t('contacts','The file you are trying to upload exceed the maximum size for file uploads on this server.'), t('contacts', 'Error')); - return; - } else { - target.load(function(){ - var response=jQuery.parseJSON(target.contents().text()); - if(response != undefined && response.status == 'success'){ - Contacts.UI.Card.editPhoto(response.data.id, response.data.tmp); - //alert('File: ' + file.tmp + ' ' + file.name + ' ' + file.mime); - }else{ - OC.dialogs.alert(response.data.message, t('contacts', 'Error')); + } + if($('#imlist li').length > 1) { + $('#ims').show(); + } + return false; + }, + addMail:function() { + var emaillist = $('#emaillist'); + emaillist.find('li.template:first-child').clone(true).appendTo(emaillist).show().find('a .tip').tipsy(); + emaillist.find('li.template:last-child').find('select').addClass('contacts_property'); + emaillist.find('li.template:last-child').removeClass('template').addClass('propertycontainer'); + emaillist.find('li:last-child').find('input[type="email"]').focus(); + emaillist.find('li:last-child').find('select').multiselect({ + noneSelectedText: t('contacts', 'Select type'), + header: false, + selectedList: 4, + classes: 'typelist' + }); + return false; + }, + loadMails:function() { + $('#emails').hide(); + $('#emaillist li.propertycontainer').remove(); + var emaillist = $('#emaillist'); + for(var mail in this.data.EMAIL) { + this.addMail(); + emaillist.find('li:last-child').find('select').multiselect('destroy'); + var curemail = emaillist.find('li.propertycontainer:last-child'); + if(typeof this.data.EMAIL[mail].label != 'undefined') { + curemail.prepend('<label class="xab">'+this.data.EMAIL[mail].label+'</label>'); + } + curemail.data('checksum', this.data.EMAIL[mail]['checksum']) + curemail.find('input[type="email"]').val(this.data.EMAIL[mail]['value']); + for(var param in this.data.EMAIL[mail]['parameters']) { + if(param.toUpperCase() == 'PREF') { + curemail.find('input[type="checkbox"]').attr('checked', 'checked') + } + else if(param.toUpperCase() == 'TYPE') { + for(etype in this.data.EMAIL[mail]['parameters'][param]) { + var found = false; + var et = this.data.EMAIL[mail]['parameters'][param][etype]; + curemail.find('select option').each(function(){ + if($.inArray($(this).val().toUpperCase(), et.toUpperCase().split(',')) > -1) { + $(this).attr('selected', 'selected'); + found = true; + } + }); + if(!found) { + curemail.find('select option:last-child').after('<option value="'+et+'" selected="selected">'+et+'</option>'); + } } - }); - form.submit(); + } } - }, - loadPhotoHandlers:function(){ - $('#phototools li a').tipsy('hide'); - $('#phototools li a').tipsy(); - $('#phototools li a').click(function() { - $(this).tipsy('hide'); - }); - $('#contacts_details_photo_wrapper').hover( - function () { - $('#phototools').slideDown(200); - }, - function () { - $('#phototools').slideUp(200); + curemail.find('select').multiselect({ + noneSelectedText: t('contacts', 'Select type'), + header: false, + selectedList: 4, + classes: 'typelist' + }); + } + if($('#emaillist li').length > 1) { + $('#emails').show(); + } + $('#emaillist li:last-child').find('input[type="text"]').focus(); + return false; + }, + addPhone:function() { + var phonelist = $('#phonelist'); + phonelist.find('li.template:first-child').clone(true).appendTo(phonelist); //.show(); + phonelist.find('li.template:last-child').find('select').addClass('contacts_property'); + phonelist.find('li.template:last-child').removeClass('template').addClass('propertycontainer'); + phonelist.find('li:last-child').find('input[type="text"]').focus(); + phonelist.find('li:last-child').find('select').multiselect({ + noneSelectedText: t('contacts', 'Select type'), + header: false, + selectedList: 4, + classes: 'typelist' + }); + phonelist.find('li:last-child').show(); + return false; + }, + loadPhones:function() { + $('#phones').hide(); + $('#phonelist li.propertycontainer').remove(); + var phonelist = $('#phonelist'); + for(var phone in this.data.TEL) { + this.addPhone(); + var curphone = phonelist.find('li.propertycontainer:last-child'); + if(typeof this.data.TEL[phone].label != 'undefined') { + curphone.prepend('<label class="xab">'+this.data.TEL[phone].label+'</label>'); + } + curphone.find('select').multiselect('destroy'); + curphone.data('checksum', this.data.TEL[phone]['checksum']) + curphone.find('input[type="text"]').val(this.data.TEL[phone]['value']); + for(var param in this.data.TEL[phone]['parameters']) { + if(param.toUpperCase() == 'PREF') { + curphone.find('input[type="checkbox"]').attr('checked', 'checked'); } - ); - $('#phototools').hover( - function () { - $(this).removeClass('transparent'); - }, - function () { - $(this).addClass('transparent'); + else if(param.toUpperCase() == 'TYPE') { + for(ptype in this.data.TEL[phone]['parameters'][param]) { + var found = false; + var pt = this.data.TEL[phone]['parameters'][param][ptype]; + curphone.find('select option').each(function() { + //if ($(this).val().toUpperCase() == pt.toUpperCase()) { + if ($.inArray($(this).val().toUpperCase(), pt.toUpperCase().split(',')) > -1) { + $(this).attr('selected', 'selected'); + found = true; + } + }); + if(!found) { + curphone.find('select option:last-child').after('<option class="custom" value="'+pt+'" selected="selected">'+pt+'</option>'); + } + } } - ); - if(this.data.PHOTO) { - $('#phototools .delete').click(function() { - $(this).tipsy('hide'); - Contacts.UI.Card.deleteProperty($('#contacts_details_photo'), 'single'); - $(this).hide(); - }); - $('#phototools .edit').click(function() { - $(this).tipsy('hide'); - Contacts.UI.Card.editCurrentPhoto(); - }); - } else { - $('#phototools .delete').hide(); - $('#phototools .edit').hide(); } - $('#phototools .upload').click(function() { - $('#file_upload_start').trigger('click'); - }); - $('#phototools .cloud').click(function() { - OC.dialogs.filepicker(t('contacts', 'Select photo'), Contacts.UI.Card.cloudPhotoSelected, false, 'image', true); - }); - }, - cloudPhotoSelected:function(path){ - $.getJSON(OC.filePath('contacts', 'ajax', 'oc_photo.php'),{'path':path,'id':Contacts.UI.Card.id},function(jsondata){ - if(jsondata.status == 'success'){ - //alert(jsondata.data.page); - Contacts.UI.Card.editPhoto(jsondata.data.id, jsondata.data.tmp) - $('#edit_photo_dialog_img').html(jsondata.data.page); + curphone.find('select').multiselect({ + noneSelectedText: t('contacts', 'Select type'), + header: false, + selectedList: 4, + classes: 'typelist' + }); + } + if(phonelist.find('li').length > 1) { + $('#phones').show(); + } + return false; + }, + }, + Contacts:{ + contacts:{}, + deletionQueue:[], + batchnum:50, + warnNotDeleted:function(e) { + e = e || window.event; + var warn = t('contacts', 'Some contacts are marked for deletion, but not deleted yet. Please wait for them to be deleted.'); + if (e) { + e.returnValue = String(warn); + } + if(OC.Contacts.Contacts.deletionQueue.length > 0) { + setTimeout(OC.Contacts.Contacts.deleteFilesInQueue, 1); + } + return warn; + }, + deleteFilesInQueue:function() { + var queue = OC.Contacts.Contacts.deletionQueue; + if(queue.length > 0) { + OC.Contacts.notify({cancel:true}); + while(queue.length > 0) { + var id = queue.pop(); + if(id) { + OC.Contacts.Card.doDelete(id, false); } - else{ - OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); + } + } + }, + getContact:function(id) { + if(!this.contacts[id]) { + this.contacts[id] = $('#contacts li[data-id="'+id+'"]'); + if(!this.contacts[id]) { + self = this; + $.getJSON(OC.filePath('contacts', 'ajax', 'contactdetails.php'),{'id':id},function(jsondata){ + if(jsondata.status == 'success'){ + self.contacts[id] = self.insertContact({data:jsondata.data}); + } + else{ + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); + } + }); } - }); - }, - loadPhoto:function(refresh){ - $('#phototools li a').tipsy('hide'); - $.getJSON(OC.filePath('contacts', 'ajax', 'loadphoto.php'),{'id':this.id, 'refresh': refresh},function(jsondata){ + } + return this.contacts[id]; + }, + drop:function(event, ui) { + var dragitem = ui.draggable, droptarget = $(this); + if(dragitem.is('li')) { + OC.Contacts.Contacts.dropContact(event, dragitem, droptarget); + } else { + OC.Contacts.Contacts.dropAddressbook(event, dragitem, droptarget); + } + }, + dropContact:function(event, dragitem, droptarget) { + if(dragitem.data('bookid') == droptarget.data('id')) { + return false; + } + var droplist = (droptarget.is('ul')) ? droptarget : droptarget.next(); + $.post(OC.filePath('contacts', 'ajax', 'contact/move.php'), + { + id: dragitem.data('id'), + aid: droptarget.data('id') + }, + function(jsondata){ if(jsondata.status == 'success'){ - $('#contacts_details_photo_wrapper').data('checksum', jsondata.data.checksum); - $('#contacts_details_photo_wrapper').html(jsondata.data.page); - Contacts.UI.Card.loadPhotoHandlers(); - } - else{ + dragitem.attr('data-bookid', droptarget.data('id')) + dragitem.data('bookid', droptarget.data('id')); + OC.Contacts.Contacts.insertContact({ + contactlist:droplist, + contact:dragitem.detach() + }); + OC.Contacts.Contacts.scrollTo(dragitem.data('id')); + } else { OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); } - }); - $('#file_upload_form').show(); - $('#contacts_propertymenu_dropdown a[data-type="PHOTO"]').parent().hide(); - }, - editCurrentPhoto:function(){ - $.getJSON(OC.filePath('contacts', 'ajax', 'currentphoto.php'),{'id':this.id},function(jsondata){ + }); + }, + dropAddressbook:function(event, dragitem, droptarget) { + if(confirm(t('contacts', 'Do you want to merge these address books?'))) { + if(dragitem.data('bookid') == droptarget.data('id')) { + return false; + } + var droplist = (droptarget.is('ul')) ? droptarget : droptarget.next(); + $.post(OC.filePath('contacts', 'ajax', 'contact/move.php'), + { + id: dragitem.data('id'), + aid: droptarget.data('id'), + isaddressbook: 1 + }, + function(jsondata){ if(jsondata.status == 'success'){ - //alert(jsondata.data.page); - Contacts.UI.Card.editPhoto(jsondata.data.id, jsondata.data.tmp) - $('#edit_photo_dialog_img').html(jsondata.data.page); - } - else{ + OC.Contacts.Contacts.update(); // Easier to refresh the whole bunch. + } else { OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); } }); - }, - editPhoto:function(id, tmp_path){ - //alert('editPhoto: ' + tmp_path); - $.getJSON(OC.filePath('contacts', 'ajax', 'cropphoto.php'),{'tmp_path':tmp_path,'id':this.id,'requesttoken':requesttoken},function(jsondata){ - if(jsondata.status == 'success'){ - //alert(jsondata.data.page); - $('#edit_photo_dialog_img').html(jsondata.data.page); - } - else{ - OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); + } else { + return false; + } + }, + /** + * @params params An object with the properties 'contactlist':a jquery object of the ul to insert into, + * 'contacts':a jquery object of all items in the list and either 'data': an object with the properties + * id, addressbookid and displayname or 'contact': a listitem to be inserted directly. + * If 'contactlist' or 'contacts' aren't defined they will be search for based in the properties in 'data'. + */ + insertContact:function(params) { + var id, bookid; + if(!params.contactlist) { + // FIXME: Check if contact really exists. + bookid = params.data ? params.data.addressbookid : params.contact.data('bookid'); + id = params.data ? params.data.id : params.contact.data('id'); + params.contactlist = $('#contacts ul[data-id="'+bookid+'"]'); + } + if(!params.contacts) { + bookid = params.data ? params.data.addressbookid : params.contact.data('bookid'); + id = params.data ? params.data.id : params.contact.data('id'); + params.contacts = $('#contacts ul[data-id="'+bookid+'"] li'); + } + var contact = params.data + ? $('<li data-id="'+params.data.id+'" data-bookid="'+params.data.addressbookid + + '" role="button"><a href="'+OC.linkTo('contacts', 'index.php')+'&id=' + + params.data.id+'" style="background: url('+OC.filePath('contacts', '', 'thumbnail.php') + + '?id='+params.data.id+') no-repeat scroll 0% 0% transparent;">' + + params.data.displayname+'</a></li>') + : params.contact; + var added = false; + var name = params.data ? params.data.displayname.toLowerCase() : contact.find('a').text().toLowerCase(); + if(params.contacts) { + params.contacts.each(function() { + if ($(this).text().toLowerCase() > name) { + $(this).before(contact); + added = true; + return false; } }); - if($('#edit_photo_dialog').dialog('isOpen') == true){ - $('#edit_photo_dialog').dialog('moveToTop'); + } + if(!added || !params.contacts) { + params.contactlist.append(contact); + } + //this.contacts[id] = contact; + return contact; + }, + addAddressbook:function(name, description, cb) { + $.post(OC.filePath('contacts', 'ajax/addressbook', 'add.php'), { name: name, description: description, active: true }, + function(jsondata) { + if(jsondata.status == 'success'){ + if(cb && typeof cb == 'function') { + cb(jsondata.data.addressbook); + } } else { - $('#edit_photo_dialog').dialog('open'); + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); + return false; } - }, - savePhoto:function(){ - var target = $('#crop_target'); - var form = $('#cropform'); - form.submit(); - target.load(function(){ - var response=jQuery.parseJSON(target.contents().text()); - if(response != undefined && response.status == 'success'){ - // load cropped photo. - $('#contacts_details_photo_wrapper').html(response.data.page); - Contacts.UI.Card.data.PHOTO = true; - Contacts.UI.Card.loadPhotoHandlers(); - }else{ - OC.dialogs.alert(response.data.message, t('contacts', 'Error')); + }); + }, + doImport:function(file, aid){ + $.post(OC.filePath('contacts', '', 'import.php'), { id: aid, file: file, fstype: 'OC_FilesystemView' }, + function(jsondata){ + if(jsondata.status != 'success'){ + OC.Contacts.notify({message:jsondata.data.message}); } + }); + return false; + }, + next:function(reverse) { + var curlistitem = this.getContact(OC.Contacts.Card.id); + var newlistitem = reverse ? curlistitem.prev('li') : curlistitem.next('li'); + if(newlistitem) { + curlistitem.removeClass('active'); + OC.Contacts.Card.update({ + cid:newlistitem.data('id'), + aid:newlistitem.data('bookid') }); - Contacts.UI.Contacts.refreshThumbnail(this.id); - }, - addMail:function() { - //alert('addMail'); - $('#emaillist li.template:first-child').clone(true).appendTo($('#emaillist')).show().find('a .tip').tipsy(); - $('#emaillist li.template:last-child').find('select').addClass('contacts_property'); - $('#emaillist li.template:last-child').removeClass('template').addClass('propertycontainer'); - $('#emaillist li:last-child').find('input[type="email"]').focus(); - return false; - }, - loadMails:function() { - $('#emails').hide(); - $('#emaillist li.propertycontainer').remove(); - for(var mail in this.data.EMAIL) { - this.addMail(); - //$('#emaillist li:first-child').clone().appendTo($('#emaillist')).show(); - $('#emaillist li:last-child').data('checksum', this.data.EMAIL[mail]['checksum']) - $('#emaillist li:last-child').find('input[type="email"]').val(this.data.EMAIL[mail]['value']); - for(var param in this.data.EMAIL[mail]['parameters']) { - if(param.toUpperCase() == 'PREF') { - $('#emaillist li:last-child').find('input[type="checkbox"]').attr('checked', 'checked') - } - else if(param.toUpperCase() == 'TYPE') { - for(etype in this.data.EMAIL[mail]['parameters'][param]) { - var et = this.data.EMAIL[mail]['parameters'][param][etype]; - $('#emaillist li:last-child').find('select option').each(function(){ - if($.inArray($(this).val().toUpperCase(), et.toUpperCase().split(',')) > -1) { - $(this).attr('selected', 'selected'); - } - }); - } - } - } + } + }, + previous:function() { + this.next(true); + }, + nextAddressbook:function(reverse) { + console.log('nextAddressbook', reverse); + var curlistitem = this.getContact(OC.Contacts.Card.id); + var parent = curlistitem.parent('ul'); + var newparent = reverse + ? parent.prevAll('ul').first() + : parent.nextAll('ul').first(); + if(newparent) { + newlistitem = newparent.find('li:first-child'); + if(newlistitem) { + parent.slideUp().prev('h3').removeClass('active'); + newparent.slideDown().prev('h3').addClass('active'); + curlistitem.removeClass('active'); + OC.Contacts.Card.update({ + cid:newlistitem.data('id'), + aid:newlistitem.data('bookid') + }); } - if($('#emaillist li').length > 1) { - $('#emails').show(); - $('#contact_communication').show(); + } + }, + previousAddressbook:function() { + console.log('previousAddressbook'); + this.nextAddressbook(true); + }, + // Reload the contacts list. + update:function(params){ + if(!params) { params = {}; } + if(!params.start) { + if(params.aid) { + $('#contacts h3[data-id="'+params.aid+'"],#contacts ul[data-id="'+params.aid+'"]').remove(); + } else { + $('#contacts').empty(); } - - $('#emaillist li:last-child').find('input[type="text"]').focus(); - return false; - }, - addPhone:function() { - $('#phonelist li.template:first-child').clone(true).appendTo($('#phonelist')); //.show(); - $('#phonelist li.template:last-child').find('select').addClass('contacts_property'); - $('#phonelist li.template:last-child').removeClass('template').addClass('propertycontainer'); - $('#phonelist li:last-child').find('input[type="text"]').focus(); - $('#phonelist li:last-child').find('select').multiselect({ - noneSelectedText: t('contacts', 'Select type'), - header: false, - selectedList: 4, - classes: 'typelist' - }); - $('#phonelist li:last-child').show(); - return false; - }, - loadPhones:function() { - $('#phones').hide(); - $('#phonelist li.propertycontainer').remove(); - for(var phone in this.data.TEL) { - this.addPhone(); - $('#phonelist li:last-child').find('select').multiselect('destroy'); - $('#phonelist li:last-child').data('checksum', this.data.TEL[phone]['checksum']) - $('#phonelist li:last-child').find('input[type="text"]').val(this.data.TEL[phone]['value']); - for(var param in this.data.TEL[phone]['parameters']) { - if(param.toUpperCase() == 'PREF') { - $('#phonelist li:last-child').find('input[type="checkbox"]').attr('checked', 'checked'); - } - else if(param.toUpperCase() == 'TYPE') { - for(ptype in this.data.TEL[phone]['parameters'][param]) { - var pt = this.data.TEL[phone]['parameters'][param][ptype]; - $('#phonelist li:last-child').find('select option').each(function(){ - //if ($(this).val().toUpperCase() == pt.toUpperCase()) { - if ($.inArray($(this).val().toUpperCase(), pt.toUpperCase().split(',')) > -1) { - $(this).attr('selected', 'selected'); + } + self = this; + console.log('update: ' + params.cid + ' ' + params.aid + ' ' + params.start); + var firstrun = false; + var opts = {}; + opts['startat'] = (params.start?params.start:0); + if(params.aid) { + opts['aid'] = params.aid; + } + $.getJSON(OC.filePath('contacts', 'ajax', 'contact/list.php'),opts,function(jsondata){ + if(jsondata.status == 'success'){ + var books = jsondata.data.entries; + $.each(books, function(b, book) { + if($('#contacts h3[data-id="'+b+'"]').length == 0) { + firstrun = true; + if($('#contacts h3').length == 0) { + $('#contacts').html('<h3 class="addressbook" contextmenu="addressbookmenu" data-id="' + + b + '" data-permissions="' + book.permissions + '">' + book.displayname + + '</h3><ul class="contacts hidden" data-id="'+b+'" data-permissions="' + + book.permissions + '"></ul>'); + } else { + if(!$('#contacts h3[data-id="' + b + '"]').length) { + var item = $('<h3 class="addressbook" contextmenu="addressbookmenu" data-id="' + + b + '" data-permissions="' + book.permissions + '">' + + book.displayname+'</h3><ul class="contacts hidden" data-id="' + b + + '" data-permissions="' + book.permissions + '"></ul>'); + var added = false; + $('#contacts h3').each(function(){ + if ($(this).text().toLowerCase() > book.displayname.toLowerCase()) { + $(this).before(item).fadeIn('fast'); + added = true; + return false; + } + }); + if(!added) { + $('#contacts').append(item); } - }); + } } + $('#contacts h3[data-id="'+b+'"]').on('click', function(event) { + $('#contacts h3').removeClass('active'); + $(this).addClass('active'); + $('#contacts ul[data-id="'+b+'"]').slideToggle(300); + return false; + }); + var accept = 'li:not([data-bookid="'+b+'"]),h3:not([data-id="'+b+'"])'; + $('#contacts h3[data-id="'+b+'"],#contacts ul[data-id="'+b+'"]').droppable({ + drop: OC.Contacts.Contacts.drop, + activeClass: 'ui-state-hover', + accept: accept + }); } - } - $('#phonelist li:last-child').find('select').multiselect({ - noneSelectedText: t('contacts', 'Select type'), - header: false, - selectedList: 4, - classes: 'typelist' - }); - } - if($('#phonelist li').length > 1) { - $('#phones').show(); - $('#contact_communication').show(); - } - return false; - }, - }, - Addressbooks:{ - droptarget:undefined, - droptext:t('contacts', 'Drop a VCF file to import contacts.'), - overview:function(){ - if($('#chooseaddressbook_dialog').dialog('isOpen') == true){ - $('#chooseaddressbook_dialog').dialog('moveToTop'); - }else{ - $('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'chooseaddressbook.php'), function(jsondata){ - if(jsondata.status != 'error'){ - $('#chooseaddressbook_dialog').dialog({ - minWidth: 600, - close: function(event, ui) { - $(this).dialog('destroy').remove(); + var contactlist = $('#contacts ul[data-id="'+b+'"]'); + var contacts = $('#contacts ul[data-id="'+b+'"] li'); + for(var c in book.contacts) { + if(book.contacts[c].id == undefined) { continue; } + if(!$('#contacts li[data-id="'+book.contacts[c]['id']+'"]').length) { + var contact = OC.Contacts.Contacts.insertContact({contactlist:contactlist, contacts:contacts, data:book.contacts[c]}); + if(c == self.batchnum-10) { + contact.bind('inview', function(event, isInView, visiblePartX, visiblePartY) { + $(this).unbind(event); + var bookid = $(this).data('bookid'); + var numsiblings = $('.contacts li[data-bookid="'+bookid+'"]').length; + if (isInView && numsiblings >= self.batchnum) { + console.log('This would be a good time to load more contacts.'); + OC.Contacts.Contacts.update({cid:params.cid, aid:bookid, start:$('#contacts li[data-bookid="'+bookid+'"]').length}); + } + }); } - }).css('overflow','visible'); - } else { - alert(jsondata.data.message); + } } }); - } - return false; - }, - activation:function(checkbox, bookid) - { - $.post(OC.filePath('contacts', 'ajax', 'activation.php'), { bookid: bookid, active: checkbox.checked?1:0 }, - function(data) { - if (data.status == 'success'){ - checkbox.checked = data.active == 1; - Contacts.UI.Contacts.update(); - } - }); - }, - newAddressbook:function(object){ - var tr = $(document.createElement('tr')) - .load(OC.filePath('contacts', 'ajax', 'addbook.php')); - $(object).closest('tr').after(tr).hide(); - }, - editAddressbook:function(object, bookid){ - var tr = $(document.createElement('tr')) - .load(OC.filePath('contacts', 'ajax', 'editaddressbook.php') + "?bookid="+bookid); - $(object).closest('tr').after(tr).hide(); - }, - deleteAddressbook:function(obj, bookid){ - var check = confirm("Do you really want to delete this address book?"); - if(check == false){ - return false; - }else{ - $.post(OC.filePath('contacts', 'ajax', 'deletebook.php'), { id: bookid}, - function(jsondata) { - if (jsondata.status == 'success'){ - $(obj).closest('tr').remove(); - Contacts.UI.Contacts.update(); - } else { - OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); - } - }); - } - }, - loadImportHandlers:function() { - $('#import_upload_start').change(function(){ - Contacts.UI.Addressbooks.uploadImport(this.files); - }); - $('#importaddressbook_dialog').find('.upload').click(function() { - Contacts.UI.Addressbooks.droptarget.html(t('contacts', 'Uploading...')); - Contacts.UI.loading(Contacts.UI.Addressbooks.droptarget, true); - //$('#import_upload_start').trigger('click'); - //return false; - }); - $('#importaddressbook_dialog').find('.upload').tipsy(); - this.droptarget = $('#import_drop_target'); - $(this.droptarget).bind('dragover',function(event){ - $(event.target).addClass('droppable'); - event.stopPropagation(); - event.preventDefault(); - }); - $(this.droptarget).bind('dragleave',function(event){ - $(event.target).removeClass('droppable'); - }); - $(this.droptarget).bind('drop',function(event){ - event.stopPropagation(); - event.preventDefault(); - $(event.target).removeClass('droppable'); - $(event.target).html(t('contacts', 'Uploading...')); - Contacts.UI.loading(event.target, true); - $.importUpload(event.originalEvent.dataTransfer.files); - }); - - $.importUpload = function(files){ - var file = files[0]; - if(file.size > $('#max_upload').val()){ - OC.dialogs.alert(t('contacts','The file you are trying to upload exceed the maximum size for file uploads on this server.'), t('contacts','Upload too large')); - $(Contacts.UI.Addressbooks.droptarget).html(Contacts.UI.Addressbooks.droptext); - Contacts.UI.loading(Contacts.UI.Addressbooks.droptarget, false); - return; + if($('#contacts h3').length > 1) { + $('#contacts li,#contacts h3').draggable({ + distance: 10, + revert: 'invalid', + axis: 'y', containment: '#contacts', + scroll: true, scrollSensitivity: 40, + opacity: 0.7, helper: 'clone' + }); + } else { + $('#contacts h3').first().addClass('active'); } - if(file.type.indexOf('text') != 0) { - OC.dialogs.alert(t('contacts','You have dropped a file type that cannot be imported: ') + file.type, t('contacts','Wrong file type')); - $(Contacts.UI.Addressbooks.droptarget).html(Contacts.UI.Addressbooks.droptext); - Contacts.UI.loading(Contacts.UI.Addressbooks.droptarget, false); - return; + if(opts['startat'] == 0) { // only update card on first load. + OC.Contacts.Card.update(params); } - var xhr = new XMLHttpRequest(); - - if (!xhr.upload) { - OC.dialogs.alert(t('contacts', 'Your browser doesn\'t support AJAX upload. Please upload the contacts file to ownCloud and import that way.'), t('contacts', 'Error')) - } - importUpload = xhr.upload, - xhr.onreadystatechange = function() { - if (xhr.readyState == 4){ - response = $.parseJSON(xhr.responseText); - if(response.status == 'success') { - if(xhr.status == 200) { - Contacts.UI.Addressbooks.doImport(response.data.path, response.data.file); - } else { - $(Contacts.UI.Addressbooks.droptarget).html(Contacts.UI.Addressbooks.droptext); - Contacts.UI.loading(Contacts.UI.Addressbooks.droptarget, false); - OC.dialogs.alert(xhr.status + ': ' + xhr.responseText, t('contacts', 'Error')); - } - } else { - OC.dialogs.alert(response.data.message, t('contacts', 'Error')); - } - } - }; - xhr.open('POST', OC.filePath('contacts', 'ajax', 'uploadimport.php') + '?file='+encodeURIComponent(file.name)+'&requesttoken='+requesttoken, true); - xhr.setRequestHeader('Cache-Control', 'no-cache'); - xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); - xhr.setRequestHeader('X_FILE_NAME', encodeURIComponent(file.name)); - xhr.setRequestHeader('X-File-Size', file.size); - xhr.setRequestHeader('Content-Type', file.type); - xhr.send(file); - } - }, - uploadImport:function(filelist) { - if(!filelist) { - OC.dialogs.alert(t('contacts','No files selected for upload.'), t('contacts', 'Error')); - return; - } - //var file = filelist.item(0); - var file = filelist[0]; - var target = $('#import_upload_target'); - var form = $('#import_upload_form'); - var totalSize=0; - if(file.size > $('#max_upload').val()){ - OC.dialogs.alert(t('contacts','The file you are trying to upload exceed the maximum size for file uploads on this server.'), t('contacts', 'Error')); - return; } else { - target.load(function(){ - var response=jQuery.parseJSON(target.contents().text()); - if(response != undefined && response.status == 'success'){ - Contacts.UI.Addressbooks.doImport(response.data.path, response.data.file); - }else{ - OC.dialogs.alert(response.data.message, t('contacts', 'Error')); - } - }); - form.submit(); + OC.Contacts.notify({message:t('contacts', 'Error')+': '+jsondata.data.message}); } - }, - importAddressbook:function(object){ - var tr = $(document.createElement('tr')) - .load(OC.filePath('contacts', 'ajax', 'importaddressbook.php')); - $(object).closest('tr').after(tr).hide(); - }, - doImport:function(path, file){ - $(Contacts.UI.Addressbooks.droptarget).html(t('contacts', 'Importing...')); - Contacts.UI.loading(Contacts.UI.Addressbooks.droptarget, true); - var id = $('#importaddressbook_dialog').find('#book').val(); - $.post(OC.filePath('contacts', '', 'import.php'), { id: id, path: path, file: file, fstype: 'OC_FilesystemView' }, - function(jsondata){ - if(jsondata.status == 'success'){ - Contacts.UI.Addressbooks.droptarget.html(t('contacts', 'Import done. Success/Failure: ')+jsondata.data.imported+'/'+jsondata.data.failed); - $('#chooseaddressbook_dialog').find('#close_button').val(t('contacts', 'OK')); - Contacts.UI.Contacts.update(); - setTimeout( - function() { - $(Contacts.UI.Addressbooks.droptarget).html(Contacts.UI.Addressbooks.droptext); - }, 5000); - } else { - OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); - } - }); - Contacts.UI.loading(Contacts.UI.Addressbooks.droptarget, false); - }, - submit:function(button, bookid){ - var displayname = $("#displayname_"+bookid).val().trim(); - var active = $("#edit_active_"+bookid+":checked").length; - var description = $("#description_"+bookid).val(); - - if(displayname.length == 0) { - OC.dialogs.alert(t('contacts', 'Displayname cannot be empty.'), t('contacts', 'Error')); - return false; - } - var url; - if (bookid == 'new'){ - url = OC.filePath('contacts', 'ajax', 'createaddressbook.php'); - }else{ - url = OC.filePath('contacts', 'ajax', 'updateaddressbook.php'); - } - $.post(url, { id: bookid, name: displayname, active: active, description: description }, - function(jsondata){ - if(jsondata.status == 'success'){ - $(button).closest('tr').prev().html(jsondata.page).show().next().remove(); - Contacts.UI.Contacts.update(); - } else { - OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); - } - }); - }, - cancel:function(button, bookid){ - $(button).closest('tr').prev().show().next().remove(); - } + }); }, - Contacts:{ - // Reload the contacts list. - update:function(){ - $.getJSON(OC.filePath('contacts', 'ajax', 'contacts.php'),{},function(jsondata){ - if(jsondata.status == 'success'){ - $('#contacts').html(jsondata.data.page); - Contacts.UI.Card.update(); - } - else{ - OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); - } - }); - setTimeout(function() { - $('#contacts li').unbind('inview'); - $('#contacts li').bind('inview', function(event, isInView, visiblePartX, visiblePartY) { - if (isInView) { - if (!$(this).find('a').attr('style')) { - $(this).find('a').css('background','url('+OC.filePath('contacts', '', 'thumbnail.php')+'?id='+$(this).data('id')+') no-repeat'); - } - } - })}, 500); - setTimeout(Contacts.UI.Contacts.lazyupdate, 500); - }, - // Add thumbnails to the contact list as they become visible in the viewport. - lazyupdate:function(){ - $('#contacts li').live('inview', function(){ - if (!$(this).find('a').attr('style')) { - $(this).find('a').css('background','url('+OC.filePath('contacts', '', 'thumbnail.php')+'?id='+$(this).data('id')+') no-repeat'); - } - }); - }, - refreshThumbnail:function(id){ - var item = $('#contacts [data-id="'+id+'"]').find('a'); - item.html(Contacts.UI.Card.fn); - item.css('background','url('+OC.filePath('contacts', '', 'thumbnail.php')+'?id='+id+'&refresh=1'+Math.random()+') no-repeat'); - }, - scrollTo:function(id){ + refreshThumbnail:function(id){ + var item = $('.contacts li[data-id="'+id+'"]').find('a'); + item.html(OC.Contacts.Card.fn); + item.css('background','url('+OC.filePath('contacts', '', 'thumbnail.php')+'?id='+id+'&refresh=1'+Math.random()+') no-repeat'); + }, + scrollTo:function(id){ + var item = $('#contacts li[data-id="'+id+'"]'); + if(item && $.isNumeric(item.offset().top)) { + console.log('scrollTo ' + parseInt(item.offset().top)); $('#contacts').animate({ - scrollTop: $('#leftcontent li[data-id="'+id+'"]').offset().top-20}, 'slow','swing'); + scrollTop: parseInt(item.offset()).top-40}, 'slow','swing'); } } } } $(document).ready(function(){ - OCCategories.changed = Contacts.UI.Card.categoriesChanged; + OCCategories.changed = OC.Contacts.Card.categoriesChanged; OCCategories.app = 'contacts'; - $('#notification').click(function(){ - $('#notification').fadeOut(); + var ninjahelp = $('#ninjahelp'); + + $('#bottomcontrols .settings').on('click keydown', function() { + try { + ninjahelp.hide(); + OC.appSettings({appid:'contacts', loadJS:true, cache:false}); + } catch(e) { + console.log('error:', e.message); + } + }); + $('#bottomcontrols .import').click(function() { + $('#import_upload_start').trigger('click'); }); - - $('#chooseaddressbook').click(Contacts.UI.Addressbooks.overview); - $('#chooseaddressbook').keydown(Contacts.UI.Addressbooks.overview); + $('#contacts_newcontact').on('click keydown', OC.Contacts.Card.editNew); + + ninjahelp.find('.close').on('click keydown',function() { + ninjahelp.hide(); + }); + + $(document).on('keyup', function(event) { + if(event.target.nodeName.toUpperCase() != 'BODY' + || $('#contacts li').length == 0 + || !OC.Contacts.Card.id) { + return; + } + //console.log(event.which + ' ' + event.target.nodeName); + /** + * To add: + * Shift-a: add addressbook + * u (85): hide/show leftcontent + * f (70): add field + */ + switch(event.which) { + case 27: // Esc + ninjahelp.hide(); + break; + case 46: // Delete + if(event.shiftKey) { + OC.Contacts.Card.delayedDelete(); + } + break; + case 40: // down + case 74: // j + OC.Contacts.Contacts.next(); + break; + case 65: // a + if(event.shiftKey) { + // add addressbook + OC.Contacts.notImplemented(); + break; + } + OC.Contacts.Card.editNew(); + break; + case 38: // up + case 75: // k + OC.Contacts.Contacts.previous(); + break; + case 34: // PageDown + case 78: // n + // next addressbook + OC.Contacts.Contacts.nextAddressbook(); + break; + case 79: // o + var aid = $('#contacts h3.active').first().data('id'); + if(aid) { + $('#contacts ul[data-id="'+aid+'"]').slideToggle(300); + } + break; + case 33: // PageUp + case 80: // p + // prev addressbook + OC.Contacts.Contacts.previousAddressbook(); + break; + case 82: // r + OC.Contacts.Contacts.update({cid:OC.Contacts.Card.id}); + break; + case 191: // ? + ninjahelp.toggle('fast'); + break; + } + + }); + + //$(window).on('beforeunload', OC.Contacts.Contacts.deleteFilesInQueue); - $('#contacts_newcontact').click(Contacts.UI.Card.editNew); - $('#contacts_newcontact').keydown(Contacts.UI.Card.editNew); - // Load a contact. - $('#contacts').keydown(function(event) { - if(event.which == 13) { - $('#contacts').click(); + $('.contacts').keydown(function(event) { + if(event.which == 13 || event.which == 32) { + $('.contacts').click(); } }); - $('#contacts').click(function(event){ + $(document).on('click', '#contacts', function(event){ var $tgt = $(event.target); if ($tgt.is('li') || $tgt.is('a')) { var item = $tgt.is('li')?$($tgt):($tgt).parent(); var id = item.data('id'); + var bookid = item.data('bookid'); item.addClass('active'); var oldid = $('#rightcontent').data('id'); if(oldid != 0){ - $('#contacts li[data-id="'+oldid+'"]').removeClass('active'); + var olditem = $('.contacts li[data-id="'+oldid+'"]'); + var oldbookid = olditem.data('bookid'); + olditem.removeClass('active'); + if(oldbookid != bookid) { + $('#contacts h3[data-id="'+oldbookid+'"]').removeClass('active'); + $('#contacts h3[data-id="'+bookid+'"]').addClass('active'); + } } - $.getJSON(OC.filePath('contacts', 'ajax', 'contactdetails.php'),{'id':id},function(jsondata){ + $.getJSON(OC.filePath('contacts', 'ajax', 'contact/details.php'),{'id':id},function(jsondata){ if(jsondata.status == 'success'){ - Contacts.UI.Card.loadContact(jsondata.data); + OC.Contacts.Card.loadContact(jsondata.data, bookid); } else{ OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); @@ -1601,102 +2011,267 @@ $(document).ready(function(){ return false; }); - $('#contacts li').bind('inview', function(event, isInView, visiblePartX, visiblePartY) { - if (isInView) { //NOTE: I've kept all conditions for future reference ;-) - // element is now visible in the viewport - if (visiblePartY == 'top') { - // top part of element is visible - } else if (visiblePartY == 'bottom') { - // bottom part of element is visible - } else { - // whole part of element is visible - if (!$(this).find('a').attr('style')) { - //alert($(this).data('id') + ' has background: ' + $(this).attr('style')); - $(this).find('a').css('background','url('+OC.filePath('contacts', '', 'thumbnail.php')+'?id='+$(this).data('id')+') no-repeat'); - }/* else { - alert($(this).data('id') + ' has style ' + $(this).attr('style').match('url')); - }*/ + $('.contacts_property').live('change', function(){ + OC.Contacts.Card.saveProperty(this); + }); + + $(function() { + // Upload function for dropped contact photos files. Should go in the Contacts class/object. + $.fileUpload = function(files){ + var file = files[0]; + if(file.size > $('#max_upload').val()){ + OC.dialogs.alert(t('contacts','The file you are trying to upload exceed the maximum size for file uploads on this server.'), t('contacts','Upload too large')); + return; } - } else { - // element has gone out of viewport + if (file.type.indexOf("image") != 0) { + OC.dialogs.alert(t('contacts','Only image files can be used as profile picture.'), t('contacts','Wrong file type')); + return; + } + var xhr = new XMLHttpRequest(); + + if (!xhr.upload) { + OC.dialogs.alert(t('contacts', 'Your browser doesn\'t support AJAX upload. Please click on the profile picture to select a photo to upload.'), t('contacts', 'Error')) + } + fileUpload = xhr.upload, + xhr.onreadystatechange = function() { + if (xhr.readyState == 4){ + response = $.parseJSON(xhr.responseText); + if(response.status == 'success') { + if(xhr.status == 200) { + OC.Contacts.Card.editPhoto(response.data.id, response.data.tmp); + } else { + OC.dialogs.alert(xhr.status + ': ' + xhr.responseText, t('contacts', 'Error')); + } + } else { + OC.dialogs.alert(response.data.message, t('contacts', 'Error')); + } + } + }; + + fileUpload.onprogress = function(e){ + if (e.lengthComputable){ + var _progress = Math.round((e.loaded * 100) / e.total); + //if (_progress != 100){ + //} + } + }; + xhr.open('POST', OC.filePath('contacts', 'ajax', 'uploadphoto.php')+'?id='+OC.Contacts.Card.id+'&requesttoken='+requesttoken+'&imagefile='+encodeURIComponent(file.name), true); + xhr.setRequestHeader('Cache-Control', 'no-cache'); + xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); + xhr.setRequestHeader('X_FILE_NAME', encodeURIComponent(file.name)); + xhr.setRequestHeader('X-File-Size', file.size); + xhr.setRequestHeader('Content-Type', file.type); + xhr.send(file); } }); - - $('.contacts_property').live('change', function(){ - Contacts.UI.Card.saveProperty(this); + + $(document).bind('drop dragover', function (e) { + e.preventDefault(); // prevent browser from doing anything, if file isn't dropped in dropZone }); - /** - * Upload function for dropped files. Should go in the Contacts class/object. - */ - $.fileUpload = function(files){ - var file = files[0]; - if(file.size > $('#max_upload').val()){ - OC.dialogs.alert(t('contacts','The file you are trying to upload exceed the maximum size for file uploads on this server.'), t('contacts','Upload too large')); - return; - } - if (file.type.indexOf("image") != 0) { - OC.dialogs.alert(t('contacts','Only image files can be used as profile picture.'), t('contacts','Wrong file type')); - return; - } - var xhr = new XMLHttpRequest(); + //add multiply file upload attribute to all browsers except konqueror (which crashes when it's used) + if(navigator.userAgent.search(/konqueror/i)==-1){ + $('#import_upload_start').attr('multiple','multiple') + } + // Import using jquery.fileupload + $(function() { + var uploadingFiles = {}, numfiles = 0, uploadedfiles = 0, retries = 0; + var aid; - if (!xhr.upload) { - OC.dialogs.alert(t('contacts', 'Your browser doesn\'t support AJAX upload. Please click on the profile picture to select a photo to upload.'), t('contacts', 'Error')) - } - fileUpload = xhr.upload, - xhr.onreadystatechange = function() { - if (xhr.readyState == 4){ - response = $.parseJSON(xhr.responseText); - if(response.status == 'success') { - if(xhr.status == 200) { - Contacts.UI.Card.editPhoto(response.data.id, response.data.tmp); + $('#import_upload_start').fileupload({ + dropZone: $('#contacts'), // restrict dropZone to contacts list. + acceptFileTypes: /^text\/(directory|vcard|x-vcard)$/i, + add: function(e, data) { + var files = data.files; + var totalSize=0; + if(files) { + numfiles += files.length; uploadedfiles = 0; + for(var i=0;i<files.length;i++) { + if(files[i].size ==0 && files[i].type== '') { + OC.dialogs.alert(t('files', 'Unable to upload your file as it is a directory or has 0 bytes'), t('files', 'Upload Error')); + return; + } + totalSize+=files[i].size; + } + } + if(totalSize>$('#max_upload').val()){ + OC.dialogs.alert(t('contacts','The file you are trying to upload exceed the maximum size for file uploads on this server.'), t('contacts','Upload too large')); + numfiles = uploadedfiles = retries = aid = 0; + uploadingFiles = {}; + return; + }else{ + if($.support.xhrFileUpload) { + for(var i=0;i<files.length;i++){ + var fileName = files[i].name; + var dropTarget; + if($(e.originalEvent.target).is('h3')) { + dropTarget = $(e.originalEvent.target).next('ul'); + } else { + dropTarget = $(e.originalEvent.target).closest('ul'); + } + if(dropTarget && dropTarget.hasClass('contacts')) { // TODO: More thorough check for where we are. + aid = dropTarget.attr('data-id'); + } else { + aid = undefined; + } + var jqXHR = $('#import_upload_start').fileupload('send', {files: files[i], + formData: function(form) { + var formArray = form.serializeArray(); + formArray['aid'] = aid; + return formArray; + }}) + .success(function(result, textStatus, jqXHR) { + if(result.status == 'success') { + // import the file + uploadedfiles += 1; + } else { + OC.Contacts.notify({message:jsondata.data.message}); + } + return false; + }) + .error(function(jqXHR, textStatus, errorThrown) { + console.log(textStatus); + OC.Contacts.notify({message:errorThrown + ': ' + textStatus,}); + }); + uploadingFiles[fileName] = jqXHR; + } } else { - OC.dialogs.alert(xhr.status + ': ' + xhr.responseText, t('contacts', 'Error')); + data.submit().success(function(data, status) { + response = jQuery.parseJSON(data[0].body.innerText); + if(response[0] != undefined && response[0].status == 'success') { + var file=response[0]; + delete uploadingFiles[file.name]; + $('tr').filterAttr('data-file',file.name).data('mime',file.mime); + var size = $('tr').filterAttr('data-file',file.name).find('td.filesize').text(); + if(size==t('files','Pending')){ + $('tr').filterAttr('data-file',file.name).find('td.filesize').text(file.size); + } + FileList.loadingDone(file.name); + } else { + OC.Contacts.notify({message:response.data.message}); + } + }); } + } + }, + fail: function(e, data) { + console.log('fail'); + OC.Contacts.notify({message:data.errorThrown + ': ' + data.textStatus}); + // TODO: Remove file from upload queue. + }, + progressall: function(e, data) { + var progress = (data.loaded/data.total)*50; + $('#uploadprogressbar').progressbar('value',progress); + }, + start: function(e, data) { + $('#uploadprogressbar').progressbar({value:0}); + $('#uploadprogressbar').fadeIn(); + if(data.dataType != 'iframe ') { + $('#upload input.stop').show(); + } + }, + stop: function(e, data) { + // stop only gets fired once so we collect uploaded items here. + var importFiles = function(aid, fileList) { + // Create a closure that can be called from different places. + if(numfiles != uploadedfiles) { + OC.Contacts.notify({message:t('contacts', 'Not all files uploaded. Retrying...')}); + retries += 1; + if(retries > 3) { + numfiles = uploadedfiles = retries = aid = 0; + uploadingFiles = {}; + $('#uploadprogressbar').fadeOut(); + OC.dialogs.alert(t('contacts', 'Something went wrong with the upload, please retry.'), t('contacts', 'Error')); + return; + } + setTimeout(function() { // Just to let any uploads finish + importFiles(aid, uploadingFiles); + }, 1000); + } + $('#uploadprogressbar').progressbar('value',50); + var todo = uploadedfiles; + $.each(fileList, function(fileName, data) { + OC.Contacts.Contacts.doImport(fileName, aid); + delete fileList[fileName]; + numfiles -= 1; uploadedfiles -= 1; + $('#uploadprogressbar').progressbar('value',50+(50/(todo-uploadedfiles))); + }) + $('#uploadprogressbar').progressbar('value',100); + $('#uploadprogressbar').fadeOut(); + setTimeout(function() { + OC.Contacts.Contacts.update({aid:aid}); + numfiles = uploadedfiles = retries = aid = 0; + }, 1000); + } + if(!aid) { + // Either selected with filepicker or dropped outside of an address book. + $.getJSON(OC.filePath('contacts', 'ajax', 'selectaddressbook.php'),{},function(jsondata) { + if(jsondata.status == 'success') { + if($('#selectaddressbook_dialog').dialog('isOpen') == true) { + $('#selectaddressbook_dialog').dialog('moveToTop'); + } else { + $('#dialog_holder').html(jsondata.data.page).ready(function($) { + var select_dlg = $('#selectaddressbook_dialog'); + select_dlg.dialog({ + modal: true, height: 'auto', width: 'auto', + buttons: { + 'Ok':function() { + aid = select_dlg.find('input:checked').val(); + if(aid == 'new') { + var displayname = select_dlg.find('input.name').val(); + var description = select_dlg.find('input.desc').val(); + if(!displayname.trim()) { + OC.dialogs.alert(t('contacts', 'The address book name cannot be empty.'), t('contacts', 'Error')); + return false; + } + $(this).dialog('close'); + OC.Contacts.Contacts.addAddressbook(displayname, description, function(addressbook) { + aid = addressbook.id; + setTimeout(function() { + importFiles(aid, uploadingFiles); + }, 500); + console.log('aid ' + aid); + }); + } else { + setTimeout(function() { + importFiles(aid, uploadingFiles); + }, 500); + console.log('aid ' + aid); + $(this).dialog('close'); + } + }, + 'Cancel':function() { + $(this).dialog('close'); + numfiles = uploadedfiles = retries = aid = 0; + uploadingFiles = {}; + $('#uploadprogressbar').fadeOut(); + } + }, + close: function(event, ui) { + // TODO: If numfiles != 0 delete tmp files after a timeout. + $(this).dialog('destroy').remove(); + } + }); + }); + } + } else { + $('#uploadprogressbar').fadeOut(); + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); + } + }); } else { - OC.dialogs.alert(response.data.message, t('contacts', 'Error')); + // Dropped on an address book or it's list. + setTimeout(function() { // Just to let any uploads finish + importFiles(aid, uploadingFiles); + }, 1000); + } + if(data.dataType != 'iframe ') { + $('#upload input.stop').hide(); } } - }; - - fileUpload.onprogress = function(e){ - if (e.lengthComputable){ - var _progress = Math.round((e.loaded * 100) / e.total); - //if (_progress != 100){ - //} - } - }; - xhr.open('POST', OC.filePath('contacts', 'ajax', 'uploadphoto.php')+'?id='+Contacts.UI.Card.id+'&requesttoken='+requesttoken+'&imagefile='+encodeURIComponent(file.name), true); - xhr.setRequestHeader('Cache-Control', 'no-cache'); - xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); - xhr.setRequestHeader('X_FILE_NAME', encodeURIComponent(file.name)); - xhr.setRequestHeader('X-File-Size', file.size); - xhr.setRequestHeader('Content-Type', file.type); - xhr.send(file); - } - - $('body').click(function(e){ - if(!$(e.target).is('#contacts_propertymenu_button')) { - $('#contacts_propertymenu_dropdown').hide(); - } + }) }); - function propertyMenu(){ - var menu = $('#contacts_propertymenu_dropdown'); - if(menu.is(':hidden')) { - menu.show(); - menu.find('li').first().focus(); - } else { - menu.hide(); - } - } - $('#contacts_propertymenu_button').click(propertyMenu); - $('#contacts_propertymenu_button').keydown(propertyMenu); - function propertyMenuItem(){ - var type = $(this).data('type'); - Contacts.UI.Card.addProperty(type); - $('#contacts_propertymenu_dropdown').hide(); - } - $('#contacts_propertymenu_dropdown a').click(propertyMenuItem); - $('#contacts_propertymenu_dropdown a').keydown(propertyMenuItem); + + OC.Contacts.loadHandlers(); + OC.Contacts.Contacts.update({cid:id}); }); diff --git a/apps/contacts/js/jquery.combobox.js b/apps/contacts/js/jquery.combobox.js index f12d1d7dd20..d9959eb6cde 100644 --- a/apps/contacts/js/jquery.combobox.js +++ b/apps/contacts/js/jquery.combobox.js @@ -23,16 +23,16 @@ minLength: 0, source: function( request, response ) { var matcher = new RegExp( $.ui.autocomplete.escapeRegex(request.term), "i" ); - response( select.children( "option" ).map(function() { + response( select.children('option').map(function() { var text = $( this ).text(); if ( this.value && ( !request.term || matcher.test(text) ) ) return { label: text.replace( new RegExp( - "(?![^&;]+;)(?!<[^<>]*)(" + + '(?![^&;]+;)(?!<[^<>]*)(' + $.ui.autocomplete.escapeRegex(request.term) + - ")(?![^<>]*>)(?![^&;]+;)", "gi" - ), "<strong>$1</strong>" ), + ')(?![^<>]*>)(?![^&;]+;)', 'gi' + ), '<strong>$1</strong>'), value: text, option: this }; @@ -42,17 +42,17 @@ self.input.val($(ui.item.option).text()); self.input.trigger('change'); ui.item.option.selected = true; - self._trigger( "selected", event, { + self._trigger('selected', event, { item: ui.item.option }); }, change: function( event, ui ) { if ( !ui.item ) { - var matcher = new RegExp( "^" + $.ui.autocomplete.escapeRegex( $(this).val() ) + "$", "i" ), + var matcher = new RegExp( '^' + $.ui.autocomplete.escapeRegex( $(this).val() ) + '$', 'i' ), valid = false; self.input.val($(this).val()); //self.input.trigger('change'); - select.children( "option" ).each(function() { + select.children('option').each(function() { if ( $( this ).text().match( matcher ) ) { this.selected = valid = true; return false; @@ -62,36 +62,41 @@ // remove invalid value, as it didn't match anything $( this ).val( "" ); select.val( "" ); - input.data( "autocomplete" ).term = ""; + input.data('autocomplete').term = ''; return false; } } } }) - .addClass( "ui-widget ui-widget-content ui-corner-left" ); + .addClass('ui-widget ui-widget-content ui-corner-left'); - input.data( "autocomplete" )._renderItem = function( ul, item ) { - return $( "<li></li>" ) - .data( "item.autocomplete", item ) - .append( "<a>" + item.label + "</a>" ) + input.data('autocomplete')._renderItem = function( ul, item ) { + return $('<li></li>') + .data('item.autocomplete', item ) + .append('<a>' + item.label + '</a>') .appendTo( ul ); }; $.each(this.options, function(key, value) { self._setOption(key, value); }); + input.dblclick(function() { + // pass empty string as value to search for, displaying all results + input.autocomplete('search', ''); + }); + if(this.options['showButton']) { - this.button = $( "<button type='button'> </button>" ) - .attr( "tabIndex", -1 ) - .attr( "title", "Show All Items" ) + this.button = $('<button type="button"> </button>') + .attr('tabIndex', -1 ) + .attr('title', 'Show All Items') .insertAfter( input ) .addClass('svg') .addClass('action') .addClass('combo-button') .click(function() { // close if already visible - if ( input.autocomplete( "widget" ).is( ":visible" ) ) { - input.autocomplete( "close" ); + if ( input.autocomplete('widget').is(':visible') ) { + input.autocomplete('close'); return; } @@ -99,7 +104,7 @@ $( this ).blur(); // pass empty string as value to search for, displaying all results - input.autocomplete( "search", "" ); + input.autocomplete('search', ''); input.focus(); }); } diff --git a/apps/contacts/js/jquery.inview.js b/apps/contacts/js/jquery.inview.js index 01900b0b4b4..9687cd83368 100644 --- a/apps/contacts/js/jquery.inview.js +++ b/apps/contacts/js/jquery.inview.js @@ -62,8 +62,8 @@ function getViewportOffset() { return { - top: w.pageYOffset || documentElement.scrollTop || d.body.scrollTop, - left: w.pageXOffset || documentElement.scrollLeft || d.body.scrollLeft + top: w.pageYOffset || documentElement.scrollTop || (d.body?d.body.scrollTop:0), + left: w.pageXOffset || documentElement.scrollLeft || (d.body?d.body.scrollLeft:0) }; } diff --git a/apps/contacts/js/loader.js b/apps/contacts/js/loader.js index 0e6da10e024..3b1f4070485 100644 --- a/apps/contacts/js/loader.js +++ b/apps/contacts/js/loader.js @@ -42,8 +42,8 @@ Contacts_Import={ }
$('#newaddressbook').attr('readonly', 'readonly');
$('#contacts').attr('disabled', 'disabled');
- var progressfile = $('#progressfile').val();
- $.post(OC.filePath('contacts', '', 'import.php'), {method: String (method), addressbookname: String (addressbookname), path: String (path), file: String (filename), id: String (addressbookid)}, function(jsondata){
+ var progresskey = $('#progresskey').val();
+ $.post(OC.filePath('contacts', '', 'import.php') + '?progresskey='+progresskey, {method: String (method), addressbookname: String (addressbookname), path: String (path), file: String (filename), id: String (addressbookid)}, function(jsondata){
if(jsondata.status == 'success'){
$('#progressbar').progressbar('option', 'value', 100);
$('#import_done').find('p').html(t('contacts', 'Result: ') + jsondata.data.imported + t('contacts', ' imported, ') + jsondata.data.failed + t('contacts', ' failed.'));
@@ -55,7 +55,7 @@ Contacts_Import={ });
$('#form_container').css('display', 'none');
$('#progressbar_container').css('display', 'block');
- window.setTimeout('Contacts_Import.getimportstatus(\'' + progressfile + '\')', 500);
+ window.setTimeout('Contacts_Import.getimportstatus(\'' + progresskey + '\')', 500);
});
$('#contacts').change(function(){
if($('#contacts option:selected').val() == 'newaddressbook'){
@@ -65,11 +65,11 @@ Contacts_Import={ }
});
},
- getimportstatus: function(progressfile){
- $.get(OC.filePath('contacts', 'import_tmp', progressfile), function(percent){
+ getimportstatus: function(progresskey){
+ $.get(OC.filePath('contacts', '', 'import.php') + '?progress=1&progresskey=' + progresskey, function(percent){
$('#progressbar').progressbar('option', 'value', parseInt(percent));
if(percent < 100){
- window.setTimeout('Contacts_Import.getimportstatus(\'' + progressfile + '\')', 500);
+ window.setTimeout('Contacts_Import.getimportstatus(\'' + progresskey + '\')', 500);
}else{
$('#import_done').css('display', 'block');
}
@@ -78,9 +78,9 @@ Contacts_Import={ }
$(document).ready(function(){
if(typeof FileActions !== 'undefined'){
- FileActions.register('text/vcard','importaddressbook', '', Contacts_Import.importdialog);
+ FileActions.register('text/vcard','importaddressbook', FileActions.PERMISSION_READ, '', Contacts_Import.importdialog);
FileActions.setDefault('text/vcard','importaddressbook');
- FileActions.register('text/x-vcard','importaddressbook', '', Contacts_Import.importdialog);
+ FileActions.register('text/x-vcard','importaddressbook', FileActions.PERMISSION_READ, '', Contacts_Import.importdialog);
FileActions.setDefault('text/x-vcard','importaddressbook');
};
});
\ No newline at end of file diff --git a/apps/contacts/js/settings.js b/apps/contacts/js/settings.js new file mode 100644 index 00000000000..69cf473e06a --- /dev/null +++ b/apps/contacts/js/settings.js @@ -0,0 +1,196 @@ +OC.Contacts = OC.Contacts || {}; +OC.Contacts.Settings = OC.Contacts.Settings || { + init:function() { + this.Addressbook.adrsettings = $('.addressbooks-settings').first(); + this.Addressbook.adractions = $('#contacts-settings').find('div.actions'); + console.log('actions: ' + this.Addressbook.adractions.length); + OC.Share.loadIcons('addressbook'); + }, + Addressbook:{ + showActions:function(act) { + this.adractions.children().hide(); + this.adractions.children('.'+act.join(',.')).show(); + }, + doActivate:function(id, tgt) { + var active = tgt.is(':checked'); + console.log('doActivate: ', id, active); + $.post(OC.filePath('contacts', 'ajax', 'addressbook/activate.php'), {id: id, active: Number(active)}, function(jsondata) { + if (jsondata.status == 'success'){ + if(!active) { + $('#contacts h3[data-id="'+id+'"],#contacts ul[data-id="'+id+'"]').remove(); + } else { + OC.Contacts.Contacts.update(); + } + } else { + console.log('Error:', jsondata.data.message); + OC.Contacts.notify(t('contacts', 'Error') + ': ' + jsondata.data.message); + tgt.checked = !active; + } + }); + }, + doDelete:function(id) { + console.log('doDelete: ', id); + var check = confirm('Do you really want to delete this address book?'); + if(check == false){ + return false; + } else { + $.post(OC.filePath('contacts', 'ajax', 'addressbook/delete.php'), { id: id}, function(jsondata) { + if (jsondata.status == 'success'){ + $('#contacts h3[data-id="'+id+'"],#contacts ul[data-id="'+id+'"]').remove(); + $('.addressbooks-settings tr[data-id="'+id+'"]').remove() + OC.Contacts.Contacts.update(); + } else { + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); + } + }); + } + }, + doEdit:function(id) { + console.log('doEdit: ', id); + this.showActions(['active', 'name', 'description', 'save', 'cancel']); + var name = this.adrsettings.find('[data-id="'+id+'"]').find('.name').text(); + var description = this.adrsettings.find('[data-id="'+id+'"]').find('.description').text(); + var active = this.adrsettings.find('[data-id="'+id+'"]').find(':checkbox').is(':checked'); + console.log('name, desc', name, description); + this.adractions.find('.active').prop('checked', active); + this.adractions.find('.name').val(name); + this.adractions.find('.description').val(description); + this.adractions.data('id', id); + }, + doSave:function() { + var name = this.adractions.find('.name').val(); + var description = this.adractions.find('.description').val(); + var active = this.adractions.find('.active').is(':checked'); + var id = this.adractions.data('id'); + console.log('doSave:', id, name, description, active); + + if(name.length == 0) { + OC.dialogs.alert(t('contacts', 'Displayname cannot be empty.'), t('contacts', 'Error')); + return false; + } + var url; + if (id == 'new'){ + url = OC.filePath('contacts', 'ajax', 'addressbook/add.php'); + }else{ + url = OC.filePath('contacts', 'ajax', 'addressbook/update.php'); + } + self = this; + $.post(url, { id: id, name: name, active: Number(active), description: description }, + function(jsondata){ + if(jsondata.status == 'success'){ + self.showActions(['new',]); + self.adractions.removeData('id'); + active = Boolean(Number(jsondata.data.addressbook.active)); + if(id == 'new') { + self.adrsettings.find('table') + .append('<tr class="addressbook" data-id="'+jsondata.data.addressbook.id+'" data-uri="'+jsondata.data.addressbook.uri+'">' + + '<td class="active"><input type="checkbox" '+(active ? 'checked="checked"' : '')+' /></td>' + + '<td class="name">'+jsondata.data.addressbook.displayname+'</td>' + + '<td class="description">'+jsondata.data.addressbook.description+'</td>' + + '<td class="action"><a class="svg action globe" title="'+t('contacts', 'Show CardDav link')+'"></a></td>' + + '<td class="action"><a class="svg action cloud" title="'+t('contacts', 'Show read-only VCF link')+'"></a></td>' + + '<td class="action"><a class="svg action download" title="'+t('contacts', 'Download')+'" ' + + 'href="'+OC.linkTo('contacts', 'export.php')+'?bookid='+jsondata.data.addressbook.id+'"></a></td>' + + '<td class="action"><a class="svg action edit" title="'+t('contacts', 'Edit')+'"></a></td>' + + '<td class="action"><a class="svg action delete" title="'+t('contacts', 'Delete')+'"></a></td>' + + '</tr>'); + } else { + var row = self.adrsettings.find('tr[data-id="'+id+'"]'); + row.find('td.active').find('input:checkbox').prop('checked', active); + row.find('td.name').text(jsondata.data.addressbook.displayname); + row.find('td.description').text(jsondata.data.addressbook.description); + } + OC.Contacts.Contacts.update(); + } else { + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); + } + }); + }, + showLink:function(id, row, link) { + console.log('row:', row.length); + row.next('tr.link').remove(); + var linkrow = $('<tr class="link"><td colspan="5"><input style="width: 95%;" type="text" value="'+link+'" /></td>' + + '<td colspan="3"><button>'+t('contacts', 'Cancel')+'</button></td></tr>').insertAfter(row); + linkrow.find('input').focus().select(); + linkrow.find('button').click(function() { + $(this).parents('tr').first().remove(); + }); + }, + showCardDAV:function(id) { + console.log('showCardDAV: ', id); + var row = this.adrsettings.find('tr[data-id="'+id+'"]'); + this.showLink(id, row, totalurl+'/'+encodeURIComponent(oc_current_user)); + }, + showVCF:function(id) { + console.log('showVCF: ', id); + var row = this.adrsettings.find('tr[data-id="'+id+'"]'); + var link = totalurl+'/'+encodeURIComponent(oc_current_user)+'/'+encodeURIComponent(row.data('uri'))+'?export'; + console.log(link); + this.showLink(id, row, link); + } + } +}; + + +$(document).ready(function() { + OC.Contacts.Settings.init(); + + var moreless = $('#contacts-settings').find('.moreless').first(); + moreless.keydown(function(event) { + if(event.which == 13 || event.which == 32) { + moreless.click(); + } + }); + moreless.on('click', function(event) { + event.preventDefault(); + if(OC.Contacts.Settings.Addressbook.adrsettings.is(':visible')) { + OC.Contacts.Settings.Addressbook.adrsettings.slideUp(); + OC.Contacts.Settings.Addressbook.adrsettings.prev('dt').hide(); + moreless.text(t('contacts', 'More...')); + } else { + OC.Contacts.Settings.Addressbook.adrsettings.slideDown(); + OC.Contacts.Settings.Addressbook.adrsettings.prev('dt').show(); + moreless.text(t('contacts', 'Less...')); + } + }); + + OC.Contacts.Settings.Addressbook.adrsettings.keydown(function(event) { + if(event.which == 13 || event.which == 32) { + OC.Contacts.Settings.Addressbook.adrsettings.click(); + } + }); + + + OC.Contacts.Settings.Addressbook.adrsettings.on('click', function(event){ + $('.tipsy').remove(); + var tgt = $(event.target); + if(tgt.is('a') || tgt.is(':checkbox')) { + var id = tgt.parents('tr').first().data('id'); + if(!id) { + return; + } + if(tgt.is(':checkbox')) { + OC.Contacts.Settings.Addressbook.doActivate(id, tgt); + } else if(tgt.is('a')) { + if(tgt.hasClass('edit')) { + OC.Contacts.Settings.Addressbook.doEdit(id); + } else if(tgt.hasClass('delete')) { + OC.Contacts.Settings.Addressbook.doDelete(id); + } else if(tgt.hasClass('globe')) { + OC.Contacts.Settings.Addressbook.showCardDAV(id); + } else if(tgt.hasClass('cloud')) { + OC.Contacts.Settings.Addressbook.showVCF(id); + } + } + } else if(tgt.is('button')) { + event.preventDefault(); + if(tgt.hasClass('save')) { + OC.Contacts.Settings.Addressbook.doSave(); + } else if(tgt.hasClass('cancel')) { + OC.Contacts.Settings.Addressbook.showActions(['new']); + } else if(tgt.hasClass('new')) { + OC.Contacts.Settings.Addressbook.doEdit('new'); + } + } + }); +}); diff --git a/apps/contacts/l10n/ar.php b/apps/contacts/l10n/ar.php index 6bebda3b6ad..2b28d6ca6d3 100644 --- a/apps/contacts/l10n/ar.php +++ b/apps/contacts/l10n/ar.php @@ -1,22 +1,13 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "خطء خلال توقي٠كتاب العناوين.", +"Error updating addressbook." => "خطء خلال تعديل كتاب العناوين", "There was an error adding the contact." => "خطء خلال اضاÙØ© معرÙÙ‡ جديده.", "Cannot add empty property." => "لا يمكنك اضاÙÙ‡ صÙÙ‡ خاليه.", "At least one of the address fields has to be filled out." => "يجب ملء على الاقل خانه واØده من العنوان.", -"Error adding contact property." => "خطء خلال اضاÙØ© صÙØ© المعرÙÙ‡.", -"Error adding addressbook." => "خطء خلال اضاÙØ© كتاب عناوين.", -"Error activating addressbook." => "خطء خلال تÙعيل كتاب العناوين.", "Information about vCard is incorrect. Please reload the page." => "المعلومات الموجودة ÙÙŠ ال vCard غير صØÙŠØØ©. الرجاء إعادة تØديث الصÙØØ©.", -"Error deleting contact property." => "خطء خلال Ù…ØÙŠ الصÙÙ‡.", -"Error updating contact property." => "خطء خلال تعديل الصÙÙ‡.", -"Error updating addressbook." => "خطء خلال تعديل كتاب العناوين", "Contacts" => "المعارÙ", "This is not your addressbook." => "هذا ليس دÙتر عناوينك.", "Contact could not be found." => "لم يتم العثور على الشخص.", -"Address" => "عنوان", -"Telephone" => "الهاتÙ", -"Email" => "البريد الالكتروني", -"Organization" => "المؤسسة", "Work" => "الوظيÙØ©", "Home" => "البيت", "Mobile" => "الهات٠المØمول", @@ -25,34 +16,29 @@ "Fax" => "الÙاكس", "Video" => "الÙيديو", "Pager" => "الرنان", +"Birthday" => "تاريخ الميلاد", "Contact" => "معرÙÙ‡", "Add Contact" => "أض٠شخص ", "Addressbooks" => "كتب العناوين", -"New Address Book" => "كتاب عناوين جديد", -"CardDav Link" => "وصلة CardDav", -"Download" => "انزال", -"Edit" => "تعديل", +"Organization" => "المؤسسة", "Delete" => "ØØ°Ù", -"Download contact" => "انزال المعرÙÙ‡", -"Delete contact" => "امØÙŠ المعرÙÙ‡", -"Birthday" => "تاريخ الميلاد", "Preferred" => "Ù…Ùضل", "Phone" => "الهاتÙ", +"Email" => "البريد الالكتروني", +"Address" => "عنوان", +"Download contact" => "انزال المعرÙÙ‡", +"Delete contact" => "امØÙŠ المعرÙÙ‡", "Type" => "نوع", "PO Box" => "العنوان البريدي", "Extended" => "إضاÙØ©", -"Street" => "شارع", "City" => "المدينة", "Region" => "المنطقة", "Zipcode" => "رقم المنطقة", "Country" => "البلد", -"Add" => "أدخل", "Addressbook" => "كتاب العناوين", -"New Addressbook" => "كتاب عناوين جديد", -"Edit Addressbook" => "عدل كتاب العناوين", -"Displayname" => "الاسم المعروض", -"Active" => "Ùعال", +"Download" => "انزال", +"Edit" => "تعديل", +"New Address Book" => "كتاب عناوين جديد", "Save" => "ØÙظ", -"Submit" => "ارسال", "Cancel" => "الغاء" ); diff --git a/apps/contacts/l10n/ca.php b/apps/contacts/l10n/ca.php index 0c95b83d5eb..b930d7c685c 100644 --- a/apps/contacts/l10n/ca.php +++ b/apps/contacts/l10n/ca.php @@ -1,39 +1,42 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "Error en (des)activar la llibreta d'adreces.", -"There was an error adding the contact." => "S'ha produït un error en afegir el contacte.", -"Cannot add empty property." => "No es pot afegir una propietat buida.", -"At least one of the address fields has to be filled out." => "Almenys heu d'omplir un dels camps d'adreça.", -"Trying to add duplicate property: " => "Esteu intentant afegir una propietat duplicada:", -"Error adding contact property." => "Error en afegir la propietat del contacte.", +"id is not set." => "no s'ha establert la id.", +"Cannot update addressbook with an empty name." => "No es pot actualitzar la llibreta d'adreces amb un nom buit", +"Error updating addressbook." => "Error en actualitzar la llibreta d'adreces.", "No ID provided" => "No heu facilitat cap ID", "Error setting checksum." => "Error en establir la suma de verificació.", "No categories selected for deletion." => "No heu seleccionat les categories a eliminar.", "No address books found." => "No s'han trobat llibretes d'adreces.", "No contacts found." => "No s'han trobat contactes.", +"There was an error adding the contact." => "S'ha produït un error en afegir el contacte.", +"element name is not set." => "no s'ha establert el nom de l'element.", +"Could not parse contact: " => "No s'ha pogut processar el contacte:", +"Cannot add empty property." => "No es pot afegir una propietat buida.", +"At least one of the address fields has to be filled out." => "Almenys heu d'omplir un dels camps d'adreça.", +"Trying to add duplicate property: " => "Esteu intentant afegir una propietat duplicada:", +"Missing IM parameter." => "Falta el parà metre IM.", +"Unknown IM: " => "IM desconegut:", +"Information about vCard is incorrect. Please reload the page." => "La informació de la vCard és incorrecta. Carregueu la pà gina de nou.", "Missing ID" => "Falta la ID", "Error parsing VCard for ID: \"" => "Error en analitzar la ID de la VCard: \"", -"Cannot add addressbook with an empty name." => "No es pot afegir una llibreta d'adreces amb un nom buit.", -"Error adding addressbook." => "Error en afegir la llibreta d'adreces.", -"Error activating addressbook." => "Error en activar la llibreta d'adreces.", +"checksum is not set." => "no s'ha establert la suma de verificació.", +"Information about vCard is incorrect. Please reload the page: " => "La informació de la vCard és incorrecta. Carregueu de nou la pà gina:", +"Something went FUBAR. " => "Alguna cosa ha anat FUBAR.", "No contact ID was submitted." => "No s'ha tramès cap ID de contacte.", "Error reading contact photo." => "Error en llegir la foto del contacte.", "Error saving temporary file." => "Error en desar el fitxer temporal.", "The loading photo is not valid." => "La foto carregada no és và lida.", -"id is not set." => "no s'ha establert la id.", -"Information about vCard is incorrect. Please reload the page." => "La informació de la vCard és incorrecta. Carregueu la pà gina de nou.", -"Error deleting contact property." => "Error en eliminar la propietat del contacte.", "Contact ID is missing." => "falta la ID del contacte.", -"Missing contact id." => "Falta la id del contacte.", "No photo path was submitted." => "No heu tramès el camà de la foto.", "File doesn't exist:" => "El fitxer no existeix:", "Error loading image." => "Error en carregar la imatge.", -"element name is not set." => "no s'ha establert el nom de l'element.", -"checksum is not set." => "no s'ha establert la suma de verificació.", -"Information about vCard is incorrect. Please reload the page: " => "La informació de la vCard és incorrecta. Carregueu de nou la pà gina:", -"Something went FUBAR. " => "Alguna cosa ha anat FUBAR.", -"Error updating contact property." => "Error en actualitzar la propietat del contacte.", -"Cannot update addressbook with an empty name." => "No es pot actualitzar la llibreta d'adreces amb un nom buit", -"Error updating addressbook." => "Error en actualitzar la llibreta d'adreces.", +"Error getting contact object." => "Error en obtenir l'objecte contacte.", +"Error getting PHOTO property." => "Error en obtenir la propietat PHOTO.", +"Error saving contact." => "Error en desar el contacte.", +"Error resizing image" => "Error en modificar la mida de la imatge", +"Error cropping image" => "Error en retallar la imatge", +"Error creating temporary image" => "Error en crear la imatge temporal", +"Error finding image: " => "Error en trobar la imatge:", "Error uploading contacts to storage." => "Error en carregar contactes a l'emmagatzemament.", "There is no error, the file uploaded with success" => "No hi ha errors, el fitxer s'ha carregat correctament", "The uploaded file exceeds the upload_max_filesize directive in php.ini" => "El fitxer carregat supera la directiva upload_max_filesize de php.ini", @@ -41,17 +44,46 @@ "The uploaded file was only partially uploaded" => "El fitxer només s'ha carregat parcialment", "No file was uploaded" => "No s'ha carregat cap fitxer", "Missing a temporary folder" => "Falta un fitxer temporal", +"Couldn't save temporary image: " => "No s'ha pogut desar la imatge temporal: ", +"Couldn't load temporary image: " => "No s'ha pogut carregar la imatge temporal: ", +"No file was uploaded. Unknown error" => "No s'ha carregat cap fitxer. Error desconegut", "Contacts" => "Contactes", -"Drop a VCF file to import contacts." => "Elimina un fitxer VCF per importar contactes.", -"Addressbook not found." => "No s'ha trobat la llibreta d'adreces.", +"Sorry, this functionality has not been implemented yet" => "Aquesta funcionalitat encara no està implementada", +"Not implemented" => "No implementada", +"Couldn't get a valid address." => "No s'ha pogut obtenir una adreça và lida.", +"Error" => "Error", +"This property has to be non-empty." => "Aquesta propietat no pot ser buida.", +"Couldn't serialize elements." => "No s'han pogut serialitzar els elements.", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "'deleteProperty' s'ha cridat sense argument de tipus. Informeu-ne a bugs.owncloud.org", +"Edit name" => "Edita el nom", +"No files selected for upload." => "No s'han seleccionat fitxers per a la pujada.", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "El fitxer que intenteu pujar excedeix la mida mà xima de pujada en aquest servidor.", +"Error loading profile picture." => "Error en carregar la imatge de perfil.", +"Select type" => "Seleccioneu un tipus", +"Some contacts are marked for deletion, but not deleted yet. Please wait for them to be deleted." => "Heu marcat eliminar alguns contactes, però encara no s'han eliminat. Espereu mentre s'esborren.", +"Do you want to merge these address books?" => "Voleu fusionar aquestes llibretes d'adreces?", +"Result: " => "Resultat: ", +" imported, " => " importat, ", +" failed." => " fallada.", +"Displayname cannot be empty." => "El nom a mostrar no pot ser buit", +"Addressbook not found: " => "No s'ha trobat la llibreta d'adreces: ", "This is not your addressbook." => "Aquesta no és la vostra llibreta d'adreces", "Contact could not be found." => "No s'ha trobat el contacte.", -"Address" => "Adreça", -"Telephone" => "Telèfon", -"Email" => "Correu electrònic", -"Organization" => "Organització", +"Jabber" => "Jabber", +"AIM" => "AIM", +"MSN" => "MSN", +"Twitter" => "Twitter", +"GoogleTalk" => "GoogleTalk", +"Facebook" => "Facebook", +"XMPP" => "XMPP", +"ICQ" => "ICQ", +"Yahoo" => "Yahoo", +"Skype" => "Skype", +"QQ" => "QQ", +"GaduGadu" => "GaduGadu", "Work" => "Feina", "Home" => "Casa", +"Other" => "Altres", "Mobile" => "Mòbil", "Text" => "Text", "Voice" => "Veu", @@ -60,25 +92,52 @@ "Video" => "VÃdeo", "Pager" => "Paginador", "Internet" => "Internet", +"Birthday" => "Aniversari", +"Business" => "Negocis", +"Call" => "Trucada", +"Clients" => "Clients", +"Deliverer" => "Emissari", +"Holidays" => "Vacances", +"Ideas" => "Idees", +"Journey" => "Viatge", +"Jubilee" => "Aniversari", +"Meeting" => "Reunió", +"Personal" => "Personal", +"Projects" => "Projectes", +"Questions" => "Preguntes", "{name}'s Birthday" => "Aniversari de {name}", "Contact" => "Contacte", "Add Contact" => "Afegeix un contacte", +"Import" => "Importa", +"Settings" => "Configuració", "Addressbooks" => "Llibretes d'adreces", -"Configure Address Books" => "Configura les llibretes d'adreces", -"New Address Book" => "Nova llibreta d'adreces", -"Import from VCF" => "Importa de VFC", -"CardDav Link" => "Enllaç CardDav", -"Download" => "Baixa", -"Edit" => "Edita", -"Delete" => "Suprimeix", -"Download contact" => "Baixa el contacte", -"Delete contact" => "Suprimeix el contacte", +"Close" => "Tanca", +"Keyboard shortcuts" => "Dreceres de teclat", +"Navigation" => "Navegació", +"Next contact in list" => "Següent contacte de la llista", +"Previous contact in list" => "Contacte anterior de la llista", +"Expand/collapse current addressbook" => "Expandeix/col·lapsa la llibreta d'adreces", +"Next addressbook" => "Llibreta d'adreces següent", +"Previous addressbook" => "Llibreta d'adreces anterior", +"Actions" => "Accions", +"Refresh contacts list" => "Carrega de nou la llista de contactes", +"Add new contact" => "Afegeix un contacte nou", +"Add new addressbook" => "Afegeix una llibreta d'adreces nova", +"Delete current contact" => "Esborra el contacte", "Drop photo to upload" => "Elimina la foto a carregar", +"Delete current photo" => "Elimina la foto actual", +"Edit current photo" => "Edita la foto actual", +"Upload new photo" => "Carrega una foto nova", +"Select photo from ownCloud" => "Selecciona una foto de ownCloud", "Format custom, Short name, Full name, Reverse or Reverse with comma" => "Format personalitzat, Nom curt, Nom sencer, Invertit o Invertit amb coma", "Edit name details" => "Edita detalls del nom", +"Organization" => "Organització", +"Delete" => "Suprimeix", "Nickname" => "Sobrenom", "Enter nickname" => "Escriviu el sobrenom", -"Birthday" => "Aniversari", +"Web site" => "Adreça web", +"http://www.somesite.com" => "http://www.somesite.com", +"Go to web site" => "Vés a la web", "dd-mm-yyyy" => "dd-mm-yyyy", "Groups" => "Grups", "Separate groups with commas" => "Separeu els grups amb comes", @@ -90,28 +149,33 @@ "Delete email address" => "Elimina l'adreça de correu electrònic", "Enter phone number" => "Escriviu el número de telèfon", "Delete phone number" => "Elimina el número de telèfon", +"Instant Messenger" => "Instant Messenger", +"Delete IM" => "Elimina IM", "View on map" => "Visualitza al mapa", "Edit address details" => "Edita els detalls de l'adreça", "Add notes here." => "Afegiu notes aquÃ.", "Add field" => "Afegeix un camp", -"Profile picture" => "Foto de perfil", "Phone" => "Telèfon", +"Email" => "Correu electrònic", +"Instant Messaging" => "Missatgeria instantà nia", +"Address" => "Adreça", "Note" => "Nota", -"Delete current photo" => "Elimina la foto actual", -"Edit current photo" => "Edita la foto actual", -"Upload new photo" => "Carrega una foto nova", -"Select photo from ownCloud" => "Selecciona una foto de ownCloud", +"Download contact" => "Baixa el contacte", +"Delete contact" => "Suprimeix el contacte", +"The temporary image has been removed from cache." => "La imatge temporal ha estat eliminada de la memòria de cau.", "Edit address" => "Edita l'adreça", "Type" => "Tipus", "PO Box" => "Adreça postal", +"Street address" => "Adreça", +"Street and number" => "Carrer i número", "Extended" => "Addicional", -"Street" => "Carrer", +"Apartment number etc." => "Número d'apartament, etc.", "City" => "Ciutat", "Region" => "Comarca", +"E.g. state or province" => "p. ex. Estat o provÃncia ", "Zipcode" => "Codi postal", +"Postal code" => "Codi postal", "Country" => "PaÃs", -"Edit categories" => "Edita categories", -"Add" => "Afegeix", "Addressbook" => "Llibreta d'adreces", "Hon. prefixes" => "Prefix honorÃfic:", "Miss" => "Srta", @@ -132,26 +196,35 @@ "Esq." => "Esq.", "Jr." => "Jr.", "Sn." => "Sn.", -"New Addressbook" => "Nova llibreta d'adreces", -"Edit Addressbook" => "Edita la llibreta d'adreces", -"Displayname" => "Nom a mostrar", -"Active" => "Actiu", -"Save" => "Desa", -"Submit" => "Envia", -"Cancel" => "Cancel·la", "Import a contacts file" => "Importa un fitxer de contactes", "Please choose the addressbook" => "Escolliu la llibreta d'adreces", "create a new addressbook" => "crea una llibreta d'adreces nova", "Name of new addressbook" => "Nom de la nova llibreta d'adreces", -"Import" => "Importa", "Importing contacts" => "S'estan important contactes", +"Contacts imported successfully" => "Els contactes s'han importat correctament", +"Close Dialog" => "Tanca el dià leg", +"Import Addressbook" => "Importa la llibreta d'adreces", "Select address book to import to:" => "Seleccioneu la llibreta d'adreces a la que voleu importar:", +"Drop a VCF file to import contacts." => "Elimina un fitxer VCF per importar contactes.", "Select from HD" => "Selecciona de HD", "You have no contacts in your addressbook." => "No teniu contactes a la llibreta d'adreces.", "Add contact" => "Afegeix un contacte", -"Configure addressbooks" => "Configura les llibretes d'adreces", +"Select Address Books" => "Selecccioneu llibretes d'adreces", +"Enter name" => "Escriviu un nom", +"Enter description" => "Escriviu una descripció", "CardDAV syncing addresses" => "Adreces de sincronització CardDAV", "more info" => "més informació", "Primary address (Kontact et al)" => "Adreça primà ria (Kontact i al)", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Show CardDav link" => "Mostra l'enllaç CardDav", +"Show read-only VCF link" => "Mostra l'enllaç VCF només de lectura", +"Share" => "Comparteix", +"Download" => "Baixa", +"Edit" => "Edita", +"New Address Book" => "Nova llibreta d'adreces", +"Name" => "Nom", +"Description" => "Descripció", +"Save" => "Desa", +"Cancel" => "Cancel·la", +"More..." => "Més..." ); diff --git a/apps/contacts/l10n/cs_CZ.php b/apps/contacts/l10n/cs_CZ.php index 391a62010b1..830077aa278 100644 --- a/apps/contacts/l10n/cs_CZ.php +++ b/apps/contacts/l10n/cs_CZ.php @@ -1,39 +1,40 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "Chyba pÅ™i (de)aktivaci adresáře.", -"There was an error adding the contact." => "BÄ›hem pÅ™idávánà kontaktu nastala chyba.", -"Cannot add empty property." => "Nelze pÅ™idat prazdný údaj.", -"At least one of the address fields has to be filled out." => "Musà být uveden nejménÄ› jeden z adresnÃch údajů", -"Trying to add duplicate property: " => "PokouÅ¡Ãte se pÅ™idat duplicitnà atribut: ", -"Error adding contact property." => "Chyba bÄ›hem pÅ™dávánà údaje kontaktu.", +"id is not set." => "id neni nastaveno.", +"Cannot update addressbook with an empty name." => "Nelze aktualizovat adresář s prázdným jménem.", +"Error updating addressbook." => "Chyba pÅ™i aktualizaci adresáře.", "No ID provided" => "ID nezadáno", "Error setting checksum." => "Chyba pÅ™i nastavovánà kontrolnÃho souÄtu.", "No categories selected for deletion." => "Žádné kategorie nebyly vybrány k smazánÃ.", "No address books found." => "Žádný adresář nenalezen.", "No contacts found." => "Žádné kontakty nenalezeny.", +"There was an error adding the contact." => "BÄ›hem pÅ™idávánà kontaktu nastala chyba.", +"element name is not set." => "jméno elementu nenà nastaveno.", +"Could not parse contact: " => "Nelze analyzovat kontakty", +"Cannot add empty property." => "Nelze pÅ™idat prazdný údaj.", +"At least one of the address fields has to be filled out." => "Musà být uveden nejménÄ› jeden z adresnÃch údajů", +"Trying to add duplicate property: " => "PokouÅ¡Ãte se pÅ™idat duplicitnà atribut: ", +"Information about vCard is incorrect. Please reload the page." => "Informace o vCard je nesprávná. Obnovte stránku, prosÃm.", "Missing ID" => "Chybà ID", "Error parsing VCard for ID: \"" => "Chyba pÅ™i parsovánà VCard pro ID: \"", -"Cannot add addressbook with an empty name." => "Nelze pÅ™idat adresář s prázdným jménem.", -"Error adding addressbook." => "Chyba pÅ™i pÅ™idávánà adresáře.", -"Error activating addressbook." => "Chyba pÅ™i aktivaci adresáře.", +"checksum is not set." => "kontrolnà souÄet nenà nastaven.", +"Information about vCard is incorrect. Please reload the page: " => "Informace o vCard je nesprávná. Obnovte stránku, prosÃm.", +"Something went FUBAR. " => "NÄ›co se pokazilo. ", "No contact ID was submitted." => "Nebylo nastaveno ID kontaktu.", "Error reading contact photo." => "Chyba pÅ™i naÄÃtánà fotky kontaktu.", "Error saving temporary file." => "Chyba pÅ™i ukládánà doÄasného souboru.", "The loading photo is not valid." => "NaÄÃtaná fotka je vadná.", -"id is not set." => "id neni nastaveno.", -"Information about vCard is incorrect. Please reload the page." => "Informace o vCard je nesprávná. Obnovte stránku, prosÃm.", -"Error deleting contact property." => "Chyba pÅ™i odstraňovánà údaje kontaktu.", "Contact ID is missing." => "Chybà ID kontaktu.", -"Missing contact id." => "Chybà id kontaktu.", "No photo path was submitted." => "Žádná fotka nebyla nahrána.", "File doesn't exist:" => "Soubor neexistuje:", "Error loading image." => "Chyba pÅ™i naÄÃtánà obrázku.", -"element name is not set." => "jméno elementu nenà nastaveno.", -"checksum is not set." => "kontrolnà souÄet nenà nastaven.", -"Information about vCard is incorrect. Please reload the page: " => "Informace o vCard je nesprávná. Obnovte stránku, prosÃm.", -"Something went FUBAR. " => "NÄ›co se pokazilo. ", -"Error updating contact property." => "Chyba pÅ™i aktualizaci údaje kontaktu.", -"Cannot update addressbook with an empty name." => "Nelze aktualizovat adresář s prázdným jménem.", -"Error updating addressbook." => "Chyba pÅ™i aktualizaci adresáře.", +"Error getting contact object." => "Chyba pÅ™i pÅ™evzetà objektu kontakt.", +"Error getting PHOTO property." => "Chyba pÅ™i zÃskávánà fotky.", +"Error saving contact." => "Chyba pÅ™i ukládánà kontaktu.", +"Error resizing image" => "Chyba pÅ™i zmÄ›nÄ› velikosti obrázku.", +"Error cropping image" => "Chyba pÅ™i osekávánà obrázku.", +"Error creating temporary image" => "Chyba pÅ™i vytvářenà doÄasného obrázku.", +"Error finding image: " => "Chyba pÅ™i hledánà obrázku:", "Error uploading contacts to storage." => "Chyba pÅ™i nahrávánà kontaktů do úložiÅ¡tÄ›.", "There is no error, the file uploaded with success" => "Nevyskytla se žádná chyba, soubor byl úspěšnÄ› nahrán", "The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Nahrávaný soubor pÅ™ekraÄuje nastavenà upload_max_filesize directive v php.ini", @@ -41,17 +42,33 @@ "The uploaded file was only partially uploaded" => "Nahrávaný soubor se nahrál pouze z Äásti", "No file was uploaded" => "Žádný soubor nebyl nahrán", "Missing a temporary folder" => "Chybà doÄasný adresář", +"Couldn't save temporary image: " => "Nemohu uložit doÄasný obrázek: ", +"Couldn't load temporary image: " => "Nemohu naÄÃst doÄasný obrázek: ", +"No file was uploaded. Unknown error" => "Soubor nebyl odeslán. Neznámá chyba", "Contacts" => "Kontakty", -"Drop a VCF file to import contacts." => "Pro import kontaktů sem pÅ™etáhnÄ›te soubor VCF", -"Addressbook not found." => "Adresář nenalezen.", +"Sorry, this functionality has not been implemented yet" => "Bohužel, tato funkce nebyla jeÅ¡tÄ› implementována.", +"Not implemented" => "Neimplementováno", +"Couldn't get a valid address." => "Nelze zÃskat platnou adresu.", +"Error" => "Chyba", +"This property has to be non-empty." => "Tento parametr nemuže zůstat nevyplnÄ›n.", +"Couldn't serialize elements." => "Prvky nelze pÅ™evést..", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "'deleteProperty' voláno bez argumentu. ProsÃm oznamte chybu na bugs.owncloud.org", +"Edit name" => "Upravit jméno", +"No files selected for upload." => "Žádné soubory nebyly vybrány k nahránÃ.", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "Soubor, který se pokouÅ¡Ãte odeslat, pÅ™esahuje maximálnà povolenou velikost.", +"Error loading profile picture." => "Chyba pÅ™i otevÃránà obrázku profilu", +"Select type" => "Vybrat typ", +"Some contacts are marked for deletion, but not deleted yet. Please wait for them to be deleted." => "NÄ›které kontakty jsou oznaÄeny ke smazánÃ. PoÄkete prosÃm na dokonÄenà operace.", +"Result: " => "Výsledek: ", +" imported, " => "importováno v pořádku,", +" failed." => "neimportováno.", +"Displayname cannot be empty." => "Zobrazované jméno nemůže zůstat prázdné.", +"Addressbook not found: " => "Adresář nenalezen:", "This is not your addressbook." => "Toto nenà Váš adresář.", "Contact could not be found." => "Kontakt nebyl nalezen.", -"Address" => "Adresa", -"Telephone" => "Telefon", -"Email" => "Email", -"Organization" => "Organizace", "Work" => "PracovnÃ", "Home" => "DomácÃ", +"Other" => "OstatnÃ", "Mobile" => "Mobil", "Text" => "Text", "Voice" => "Hlas", @@ -60,25 +77,52 @@ "Video" => "Video", "Pager" => "Pager", "Internet" => "Internet", +"Birthday" => "Narozeniny", +"Business" => "PracovnÃ", +"Call" => "Volat", +"Clients" => "Klienti", +"Deliverer" => "Dodavatel", +"Holidays" => "Prázdniny", +"Ideas" => "Nápady", +"Journey" => "CestovánÃ", +"Jubilee" => "Jubileum", +"Meeting" => "Schůzka", +"Personal" => "OsobnÃ", +"Projects" => "Projekty", +"Questions" => "Dotazy", "{name}'s Birthday" => "Narozeniny {name}", "Contact" => "Kontakt", "Add Contact" => "PÅ™idat kontakt", +"Import" => "Import", +"Settings" => "NastavenÃ", "Addressbooks" => "Adresáře", -"Configure Address Books" => "Nastavit adresáře", -"New Address Book" => "Nový adresář", -"Import from VCF" => "Importovat z VCF", -"CardDav Link" => "CardDav odkaz", -"Download" => "StaženÃ", -"Edit" => "Editovat", -"Delete" => "Odstranit", -"Download contact" => "Stáhnout kontakt", -"Delete contact" => "Odstranit kontakt", +"Close" => "ZavÅ™Ãt", +"Keyboard shortcuts" => "Klávesovà zkratky", +"Navigation" => "Navigace", +"Next contact in list" => "NásledujÃcà kontakt v seznamu", +"Previous contact in list" => "PÅ™edchozà kontakt v seznamu", +"Expand/collapse current addressbook" => "Rozbalit/sbalit aktuálnà Adresář", +"Next addressbook" => "NásledujÃcà Adresář", +"Previous addressbook" => "PÅ™edchozà Adresář", +"Actions" => "Akce", +"Refresh contacts list" => "ObÄerstvit seznam kontaktů", +"Add new contact" => "PÅ™idat kontakt", +"Add new addressbook" => "PÅ™edat nový Adresář", +"Delete current contact" => "Odstranit aktuálnà kontakt", "Drop photo to upload" => "PÅ™etáhnÄ›te sem fotku pro jejà nahránÃ", +"Delete current photo" => "Smazat souÄasnou fotku", +"Edit current photo" => "Upravit souÄasnou fotku", +"Upload new photo" => "Nahrát novou fotku", +"Select photo from ownCloud" => "Vybrat fotku z ownCloudu", "Format custom, Short name, Full name, Reverse or Reverse with comma" => "Formát vlastnÃ, kÅ™estnÃ, celé jméno, obrácenÄ› nebo obrácenÄ› oddelené Äárkami", "Edit name details" => "Upravit podrobnosti jména", +"Organization" => "Organizace", +"Delete" => "Odstranit", "Nickname" => "PÅ™ezdÃvka", "Enter nickname" => "Zadejte pÅ™ezdÃvku", -"Birthday" => "Narozeniny", +"Web site" => "Web", +"http://www.somesite.com" => "http://www.somesite.com", +"Go to web site" => "PÅ™ejÃt na web", "dd-mm-yyyy" => "dd. mm. yyyy", "Groups" => "Skupiny", "Separate groups with commas" => "OddÄ›lte skupiny Äárkami", @@ -94,24 +138,26 @@ "Edit address details" => "Upravit podrobnosti adresy", "Add notes here." => "Zde můžete pÅ™ipsat poznámky.", "Add field" => "PÅ™idat polÃÄko", -"Profile picture" => "Profilová fotka", "Phone" => "Telefon", +"Email" => "Email", +"Address" => "Adresa", "Note" => "Poznámka", -"Delete current photo" => "Smazat souÄasnou fotku", -"Edit current photo" => "Upravit souÄasnou fotku", -"Upload new photo" => "Nahrát novou fotku", -"Select photo from ownCloud" => "Vybrat fotku z ownCloudu", +"Download contact" => "Stáhnout kontakt", +"Delete contact" => "Odstranit kontakt", +"The temporary image has been removed from cache." => "Obrázek byl odstranÄ›n z doÄasné pamÄ›ti.", "Edit address" => "Upravit adresu", "Type" => "Typ", "PO Box" => "PO box", +"Street address" => "Ulice", +"Street and number" => "Ulice a ÄÃslo", "Extended" => "RozÅ¡ÃÅ™ené", -"Street" => "Ulice", +"Apartment number etc." => "Byt ÄÃslo atd.", "City" => "MÄ›sto", "Region" => "Kraj", +"E.g. state or province" => "NapÅ™. stát nebo okres", "Zipcode" => "PSÄŒ", +"Postal code" => "PSÄŒ", "Country" => "ZemÄ›", -"Edit categories" => "Upravit kategorie", -"Add" => "PÅ™idat", "Addressbook" => "Adresář", "Hon. prefixes" => "Tituly pÅ™ed", "Miss" => "SleÄna", @@ -132,26 +178,34 @@ "Esq." => "Esq.", "Jr." => "ml.", "Sn." => "st.", -"New Addressbook" => "Nový adresář", -"Edit Addressbook" => "Editace adresáře", -"Displayname" => "Zobrazené jméno", -"Active" => "AktivnÃ", -"Save" => "Uložit", -"Submit" => "Potvrdit", -"Cancel" => "Storno", "Import a contacts file" => "Importovat soubor kontaktů", "Please choose the addressbook" => "ProsÃm zvolte adresář", "create a new addressbook" => "vytvoÅ™it nový adresář", "Name of new addressbook" => "Jméno nového adresáře", -"Import" => "Import", "Importing contacts" => "Importovánà kontaktů", +"Contacts imported successfully" => "Kontakty úspěšnÄ› importovány", +"Close Dialog" => "ZavÃracà dialog", +"Import Addressbook" => "Importovat adresář", "Select address book to import to:" => "Vyberte adresář do kterého chcete importovat:", +"Drop a VCF file to import contacts." => "Pro import kontaktů sem pÅ™etáhnÄ›te soubor VCF", "Select from HD" => "Vybrat z disku", "You have no contacts in your addressbook." => "Nemáte žádné kontakty v adresáři.", "Add contact" => "PÅ™idat kontakt", -"Configure addressbooks" => "Nastavit adresář", +"Select Address Books" => "Vybrat Adresář", +"Enter name" => "Vložte jméno", +"Enter description" => "Vložte popis", "CardDAV syncing addresses" => "Adresa pro synchronizaci pomocà CardDAV:", "more info" => "vÃc informacÃ", "Primary address (Kontact et al)" => "Hlavnà adresa (Kontakt etc)", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Show CardDav link" => "Zobrazit odklaz CardDAV:", +"Show read-only VCF link" => "Zobrazit odkaz VCF pouze pro ÄtenÃ", +"Download" => "StaženÃ", +"Edit" => "Editovat", +"New Address Book" => "Nový adresář", +"Name" => "Název", +"Description" => "Popis", +"Save" => "Uložit", +"Cancel" => "Storno", +"More..." => "VÃce..." ); diff --git a/apps/contacts/l10n/da.php b/apps/contacts/l10n/da.php index 28753edda4e..97c5f1307cd 100644 --- a/apps/contacts/l10n/da.php +++ b/apps/contacts/l10n/da.php @@ -1,39 +1,39 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "Fejl ved (de)aktivering af adressebogen", -"There was an error adding the contact." => "Der opstod en fejl ved tilføjelse af kontaktpersonen.", -"Cannot add empty property." => "Kan ikke tilføje en egenskab uden indhold.", -"At least one of the address fields has to be filled out." => "Der skal udfyldes mindst et adressefelt.", -"Trying to add duplicate property: " => "Kan ikke tilføje overlappende element.", -"Error adding contact property." => "Fejl ved tilføjelse af egenskab.", +"id is not set." => "Intet ID medsendt.", +"Cannot update addressbook with an empty name." => "Kan ikke opdatére adressebogen med et tomt navn.", +"Error updating addressbook." => "Fejl ved opdatering af adressebog", "No ID provided" => "Intet ID medsendt", "Error setting checksum." => "Kunne ikke sætte checksum.", "No categories selected for deletion." => "Der ikke valgt nogle grupper at slette.", "No address books found." => "Der blev ikke fundet nogen adressebøger.", "No contacts found." => "Der blev ikke fundet nogen kontaktpersoner.", +"There was an error adding the contact." => "Der opstod en fejl ved tilføjelse af kontaktpersonen.", +"element name is not set." => "Elementnavnet er ikke medsendt.", +"Cannot add empty property." => "Kan ikke tilføje en egenskab uden indhold.", +"At least one of the address fields has to be filled out." => "Der skal udfyldes mindst et adressefelt.", +"Trying to add duplicate property: " => "Kan ikke tilføje overlappende element.", +"Information about vCard is incorrect. Please reload the page." => "Informationen om vCard er forkert. Genindlæs siden.", "Missing ID" => "Manglende ID", -"Error parsing VCard for ID: \"" => "Kunne ikke indlæse VCard med ID'en: \"", -"Cannot add addressbook with an empty name." => "Kan ikke tilføje adressebog uden et navn.", -"Error adding addressbook." => "Fejl ved tilføjelse af adressebog.", -"Error activating addressbook." => "Fejl ved aktivering af adressebog.", +"Error parsing VCard for ID: \"" => "Kunne ikke indlæse VCard med ID'et: \"", +"checksum is not set." => "Checksum er ikke medsendt.", +"Information about vCard is incorrect. Please reload the page: " => "Informationen om dette VCard stemmer ikke. Genindlæs venligst siden: ", +"Something went FUBAR. " => "Noget gik grueligt galt. ", "No contact ID was submitted." => "Ingen ID for kontakperson medsendt.", "Error reading contact photo." => "Kunne ikke indlæse foto for kontakperson.", "Error saving temporary file." => "Kunne ikke gemme midlertidig fil.", "The loading photo is not valid." => "Billedet under indlæsning er ikke gyldigt.", -"id is not set." => "Intet ID medsendt.", -"Information about vCard is incorrect. Please reload the page." => "Informationen om vCard er forkert. Genindlæs siden.", -"Error deleting contact property." => "Fejl ved sletning af egenskab for kontaktperson.", "Contact ID is missing." => "Kontaktperson ID mangler.", -"Missing contact id." => "Kontaktperson ID mangler.", "No photo path was submitted." => "Der blev ikke medsendt en sti til fotoet.", "File doesn't exist:" => "Filen eksisterer ikke:", "Error loading image." => "Kunne ikke indlæse billede.", -"element name is not set." => "Element navnet er ikke medsendt.", -"checksum is not set." => "Checksum er ikke medsendt.", -"Information about vCard is incorrect. Please reload the page: " => "Informationen om dette VCard stemmer ikke. Genindlæs venligst siden: ", -"Something went FUBAR. " => "Noget gik grueligt galt. ", -"Error updating contact property." => "Fejl ved opdatering af egenskab for kontaktperson.", -"Cannot update addressbook with an empty name." => "Kan ikke opdatére adressebogen med et tomt navn.", -"Error updating addressbook." => "Fejl ved opdatering af adressebog", +"Error getting contact object." => "Fejl ved indlæsning af kontaktpersonobjektet.", +"Error getting PHOTO property." => "Fejl ved indlæsning af PHOTO feltet.", +"Error saving contact." => "Kunne ikke gemme kontaktpersonen.", +"Error resizing image" => "Kunne ikke ændre billedets størrelse", +"Error cropping image" => "Kunne ikke beskære billedet", +"Error creating temporary image" => "Kunne ikke oprette midlertidigt billede", +"Error finding image: " => "Kunne ikke finde billedet: ", "Error uploading contacts to storage." => "Kunne ikke uploade kontaktepersoner til midlertidig opbevaring.", "There is no error, the file uploaded with success" => "Der skete ingen fejl, filen blev succesfuldt uploadet", "The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Den uploadede fil er større end upload_max_filesize indstillingen i php.ini", @@ -41,15 +41,26 @@ "The uploaded file was only partially uploaded" => "Filen blev kun delvist uploadet.", "No file was uploaded" => "Ingen fil uploadet", "Missing a temporary folder" => "Manglende midlertidig mappe.", +"Couldn't save temporary image: " => "Kunne ikke gemme midlertidigt billede: ", +"Couldn't load temporary image: " => "Kunne ikke indlæse midlertidigt billede", +"No file was uploaded. Unknown error" => "Ingen fil blev uploadet. Ukendt fejl.", "Contacts" => "Kontaktpersoner", -"Drop a VCF file to import contacts." => "Drop en VCF fil for at importere kontaktpersoner.", -"Addressbook not found." => "Adressebogen blev ikke fundet.", +"Sorry, this functionality has not been implemented yet" => "Denne funktion er desværre ikke implementeret endnu", +"Not implemented" => "Ikke implementeret", +"Couldn't get a valid address." => "Kunne ikke finde en gyldig adresse.", +"Error" => "Fejl", +"This property has to be non-empty." => "Dette felt mÃ¥ ikke være tomt.", +"Couldn't serialize elements." => "Kunne ikke serialisere elementerne.", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "'deleteProperty' kaldet uden typeargument. Indrapporter fejl pÃ¥ bugs.owncloud.org", +"Edit name" => "Rediger navn", +"No files selected for upload." => "Der er ikke valgt nogen filer at uploade.", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "Dr.", +"Select type" => "Vælg type", +"Result: " => "Resultat:", +" imported, " => " importeret ", +" failed." => " fejl.", "This is not your addressbook." => "Dette er ikke din adressebog.", "Contact could not be found." => "Kontaktperson kunne ikke findes.", -"Address" => "Adresse", -"Telephone" => "Telefon", -"Email" => "Email", -"Organization" => "Organisation", "Work" => "Arbejde", "Home" => "Hjemme", "Mobile" => "Mobil", @@ -60,25 +71,24 @@ "Video" => "Video", "Pager" => "Personsøger", "Internet" => "Internet", -"{name}'s Birthday" => "{name}'s fødselsdag", +"Birthday" => "Fødselsdag", +"{name}'s Birthday" => "{name}s fødselsdag", "Contact" => "Kontaktperson", "Add Contact" => "Tilføj kontaktperson", +"Import" => "Importer", "Addressbooks" => "Adressebøger", -"Configure Address Books" => "Konfigurer adressebøger", -"New Address Book" => "Ny adressebog", -"Import from VCF" => "Importer fra VCF", -"CardDav Link" => "CardDav-link", -"Download" => "Download", -"Edit" => "Rediger", -"Delete" => "Slet", -"Download contact" => "Download kontaktperson", -"Delete contact" => "Slet kontaktperson", +"Close" => "Luk", "Drop photo to upload" => "Drop foto for at uploade", +"Delete current photo" => "Slet nuværende foto", +"Edit current photo" => "Rediger nuværende foto", +"Upload new photo" => "Upload nyt foto", +"Select photo from ownCloud" => "Vælg foto fra ownCloud", "Format custom, Short name, Full name, Reverse or Reverse with comma" => "Formatter som valgfrit, fuldt navn, efternavn først eller efternavn først med komma", "Edit name details" => "Rediger navnedetaljer.", -"Nickname" => "Øgenavn", -"Enter nickname" => "Indtast øgenavn", -"Birthday" => "Fødselsdag", +"Organization" => "Organisation", +"Delete" => "Slet", +"Nickname" => "Kaldenavn", +"Enter nickname" => "Indtast kaldenavn", "dd-mm-yyyy" => "dd-mm-åååå", "Groups" => "Grupper", "Separate groups with commas" => "Opdel gruppenavne med kommaer", @@ -93,25 +103,22 @@ "View on map" => "Vis pÃ¥ kort", "Edit address details" => "Rediger adresse detaljer", "Add notes here." => "Tilføj noter her.", -"Add field" => "Tilfæj element", -"Profile picture" => "Profilbillede", +"Add field" => "Tilføj element", "Phone" => "Telefon", +"Email" => "Email", +"Address" => "Adresse", "Note" => "Note", -"Delete current photo" => "Slet nuværende foto", -"Edit current photo" => "Rediger nuværende foto", -"Upload new photo" => "Upload nyt foto", -"Select photo from ownCloud" => "Vælg foto fra ownCloud", +"Download contact" => "Download kontaktperson", +"Delete contact" => "Slet kontaktperson", +"The temporary image has been removed from cache." => "Det midlertidige billede er ikke længere tilgængeligt.", "Edit address" => "Rediger adresse", "Type" => "Type", "PO Box" => "Postboks", "Extended" => "Udvidet", -"Street" => "Vej", "City" => "By", "Region" => "Region", "Zipcode" => "Postnummer", "Country" => "Land", -"Edit categories" => "Rediger grupper", -"Add" => "Tilføj", "Addressbook" => "Adressebog", "Hon. prefixes" => "Foranstillede titler", "Miss" => "Frøken", @@ -119,7 +126,7 @@ "Mr" => "Hr.", "Sir" => "Sir", "Mrs" => "Fru", -"Dr" => "Dr", +"Dr" => "Dr.", "Given name" => "Fornavn", "Additional names" => "Mellemnavne", "Family name" => "Efternavn", @@ -132,26 +139,20 @@ "Esq." => "Esq.", "Jr." => "Jr.", "Sn." => "Sn.", -"New Addressbook" => "Ny adressebog", -"Edit Addressbook" => "Rediger adressebog", -"Displayname" => "Vist navn", -"Active" => "Aktiv", -"Save" => "Gem", -"Submit" => "Gem", -"Cancel" => "Fortryd", "Import a contacts file" => "Importer fil med kontaktpersoner", "Please choose the addressbook" => "Vælg venligst adressebog", "create a new addressbook" => "Opret ny adressebog", "Name of new addressbook" => "Navn pÃ¥ ny adressebog", -"Import" => "Importer", "Importing contacts" => "Importerer kontaktpersoner", -"Select address book to import to:" => "Vælg hvilken adressebog, der skal importeres til:", -"Select from HD" => "Vælg fra harddisk.", "You have no contacts in your addressbook." => "Du har ingen kontaktpersoner i din adressebog.", "Add contact" => "Tilføj kontaktpeson.", -"Configure addressbooks" => "Konfigurer adressebøger", "CardDAV syncing addresses" => "CardDAV synkroniserings adresse", "more info" => "mere info", "Primary address (Kontact et al)" => "Primær adresse (Kontak m. fl.)", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Download" => "Download", +"Edit" => "Rediger", +"New Address Book" => "Ny adressebog", +"Save" => "Gem", +"Cancel" => "Fortryd" ); diff --git a/apps/contacts/l10n/de.php b/apps/contacts/l10n/de.php index 3a1013eb4fc..ca65d26cec1 100644 --- a/apps/contacts/l10n/de.php +++ b/apps/contacts/l10n/de.php @@ -1,57 +1,89 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "(De-)Aktivierung des Adressbuches fehlgeschlagen", -"There was an error adding the contact." => "Erstellen des Kontakts fehlgeschlagen", -"Cannot add empty property." => "Feld darf nicht leer sein.", -"At least one of the address fields has to be filled out." => "Mindestens eines der Adressfelder muss ausgefüllt werden.", -"Trying to add duplicate property: " => "Versuche, doppelte Eigenschaft hinzuzufügen: ", -"Error adding contact property." => "Kontakt ändern fehlgeschlagen", +"id is not set." => "ID ist nicht angegeben.", +"Cannot update addressbook with an empty name." => "Adressbuch kann nicht mir leeren Namen aktualisiert werden.", +"Error updating addressbook." => "Adressbuch aktualisieren fehlgeschlagen.", "No ID provided" => "Keine ID angegeben", "Error setting checksum." => "Fehler beim Setzen der Prüfsumme.", "No categories selected for deletion." => "Keine Kategorien zum Löschen ausgewählt.", "No address books found." => "Keine Adressbücher gefunden.", "No contacts found." => "Keine Kontakte gefunden.", +"There was an error adding the contact." => "Erstellen des Kontakts fehlgeschlagen.", +"element name is not set." => "Kein Name für das Element angegeben.", +"Could not parse contact: " => "Konnte folgenden Kontakt nicht verarbeiten:", +"Cannot add empty property." => "Feld darf nicht leer sein.", +"At least one of the address fields has to be filled out." => "Mindestens eines der Adressfelder muss ausgefüllt werden.", +"Trying to add duplicate property: " => "Versuche doppelte Eigenschaft hinzuzufügen: ", +"Missing IM parameter." => "IM-Parameter fehlt.", +"Unknown IM: " => "IM unbekannt:", +"Information about vCard is incorrect. Please reload the page." => "Die Information der vCard ist fehlerhaft. Bitte aktualisieren Sie die Seite.", "Missing ID" => "Fehlende ID", "Error parsing VCard for ID: \"" => "Fehler beim Einlesen der VCard für die ID: \"", -"Cannot add addressbook with an empty name." => "Bitte einen Namen für das Adressbuch angeben.", -"Error adding addressbook." => "Adressbuch hinzufügen fehlgeschlagen", -"Error activating addressbook." => "Adressbuchaktivierung fehlgeschlagen", +"checksum is not set." => "Keine Prüfsumme angegeben.", +"Information about vCard is incorrect. Please reload the page: " => "Die Informationen zur vCard sind fehlerhaft. Bitte Seite neu laden: ", +"Something went FUBAR. " => "Irgendwas ist hier so richtig schief gelaufen. ", "No contact ID was submitted." => "Es wurde keine Kontakt-ID übermittelt.", -"Error reading contact photo." => "Fehler beim auslesen des Kontaktfotos.", +"Error reading contact photo." => "Fehler beim Auslesen des Kontaktfotos.", "Error saving temporary file." => "Fehler beim Speichern der temporären Datei.", "The loading photo is not valid." => "Das Kontaktfoto ist fehlerhaft.", -"id is not set." => "ID ist nicht angegeben.", -"Information about vCard is incorrect. Please reload the page." => "Die Information der vCard ist fehlerhaft. Bitte aktualisiere die Seite.", -"Error deleting contact property." => "Kontakteigenschaft löschen fehlgeschlagen", "Contact ID is missing." => "Keine Kontakt-ID angegeben.", -"Missing contact id." => "Fehlende Kontakt-ID.", "No photo path was submitted." => "Kein Foto-Pfad übermittelt.", "File doesn't exist:" => "Datei existiert nicht: ", "Error loading image." => "Fehler beim Laden des Bildes.", -"element name is not set." => "Kein Name für das Element angegeben.", -"checksum is not set." => "Keine Prüfsumme angegeben.", -"Information about vCard is incorrect. Please reload the page: " => "Die Informationen zur vCard sind fehlerhaft. Bitte Seite neu laden: ", -"Something went FUBAR. " => "Irgendwas ist hier so richtig schief gelaufen. ", -"Error updating contact property." => "Kontakteigenschaft aktualisieren fehlgeschlagen", -"Cannot update addressbook with an empty name." => "Adressbuch kann nicht mir leeren Namen aktualisiert werden.", -"Error updating addressbook." => "Adressbuch aktualisieren fehlgeschlagen", -"Error uploading contacts to storage." => "Ãœbertragen der Kontakte fehlgeschlagen", +"Error getting contact object." => "Fehler beim Abruf des Kontakt-Objektes.", +"Error getting PHOTO property." => "Fehler beim Abrufen der PHOTO-Eigenschaft.", +"Error saving contact." => "Fehler beim Speichern des Kontaktes.", +"Error resizing image" => "Fehler bei der Größenänderung des Bildes", +"Error cropping image" => "Fehler beim Zuschneiden des Bildes", +"Error creating temporary image" => "Fehler beim Erstellen des temporären Bildes", +"Error finding image: " => "Fehler beim Suchen des Bildes: ", +"Error uploading contacts to storage." => "Ãœbertragen der Kontakte fehlgeschlagen.", "There is no error, the file uploaded with success" => "Alles bestens, Datei erfolgreich übertragen.", -"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Datei größer als durch die upload_max_filesize Direktive in php.ini erlaubt", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Datei größer als die MAX_FILE_SIZE Direktive erlaubt, die im HTML Formular spezifiziert ist", +"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Datei größer, als durch die upload_max_filesize Direktive in php.ini erlaubt", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Datei größer, als die MAX_FILE_SIZE Direktive erlaubt, die im HTML Formular spezifiziert ist", "The uploaded file was only partially uploaded" => "Datei konnte nur teilweise übertragen werden", "No file was uploaded" => "Keine Datei konnte übertragen werden.", "Missing a temporary folder" => "Kein temporärer Ordner vorhanden", +"Couldn't save temporary image: " => "Konnte das temporäre Bild nicht speichern:", +"Couldn't load temporary image: " => "Konnte das temporäre Bild nicht laden:", +"No file was uploaded. Unknown error" => "Keine Datei hochgeladen. Unbekannter Fehler", "Contacts" => "Kontakte", -"Drop a VCF file to import contacts." => "Zieh' eine VCF Datei hierher zum Kontaktimport", -"Addressbook not found." => "Adressbuch nicht gefunden.", -"This is not your addressbook." => "Dies ist nicht dein Adressbuch.", +"Sorry, this functionality has not been implemented yet" => "Diese Funktion steht leider noch nicht zur Verfügung", +"Not implemented" => "Nicht verfügbar", +"Couldn't get a valid address." => "Konnte keine gültige Adresse abrufen.", +"Error" => "Fehler", +"This property has to be non-empty." => "Dieses Feld darf nicht leer sein.", +"Couldn't serialize elements." => "Konnte Elemente nicht serialisieren", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "'deleteProperty' wurde ohne Argumente aufgerufen. Bitte melden Sie dies auf bugs.owncloud.org", +"Edit name" => "Name ändern", +"No files selected for upload." => "Keine Datei(en) zum Hochladen ausgewählt.", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "Die Datei, die Sie hochladen möchten, überschreitet die maximale Größe für Datei-Uploads auf diesem Server.", +"Error loading profile picture." => "Fehler beim Laden des Profilbildes.", +"Select type" => "Wähle Typ", +"Some contacts are marked for deletion, but not deleted yet. Please wait for them to be deleted." => "Einige zum Löschen markiert Kontakte wurden noch nicht gelöscht. Bitte warten.", +"Do you want to merge these address books?" => "Möchten Sie diese Adressbücher zusammenführen?", +"Result: " => "Ergebnis: ", +" imported, " => " importiert, ", +" failed." => " fehlgeschlagen.", +"Displayname cannot be empty." => "Der Anzeigename darf nicht leer sein.", +"Addressbook not found: " => "Adressbuch nicht gefunden:", +"This is not your addressbook." => "Dies ist nicht Ihr Adressbuch.", "Contact could not be found." => "Kontakt konnte nicht gefunden werden.", -"Address" => "Adresse", -"Telephone" => "Telefon", -"Email" => "Email", -"Organization" => "Organisation", +"Jabber" => "Jabber", +"AIM" => "AIM", +"MSN" => "MSN", +"Twitter" => "Twitter", +"GoogleTalk" => "GoogleTalk", +"Facebook" => "Facebook", +"XMPP" => "XMPP", +"ICQ" => "ICQ", +"Yahoo" => "Yahoo", +"Skype" => "Skype", +"QQ" => "QQ", +"GaduGadu" => "GaduGadu", "Work" => "Arbeit", "Home" => "Zuhause", +"Other" => "Andere", "Mobile" => "Mobil", "Text" => "Text", "Voice" => "Anruf", @@ -60,58 +92,90 @@ "Video" => "Video", "Pager" => "Pager", "Internet" => "Internet", +"Birthday" => "Geburtstag", +"Business" => "Geschäftlich", +"Call" => "Anruf", +"Clients" => "Kunden", +"Deliverer" => "Lieferant", +"Holidays" => "Feiertage", +"Ideas" => "Ideen", +"Journey" => "Reise", +"Jubilee" => "Jubiläum", +"Meeting" => "Besprechung", +"Personal" => "Persönlich", +"Projects" => "Projekte", +"Questions" => "Fragen", "{name}'s Birthday" => "Geburtstag von {name}", "Contact" => "Kontakt", "Add Contact" => "Kontakt hinzufügen", +"Import" => "Importieren", +"Settings" => "Einstellungen", "Addressbooks" => "Adressbücher", -"Configure Address Books" => "Adressbücher konfigurieren", -"New Address Book" => "Neues Adressbuch", -"Import from VCF" => "Import von VCF Datei", -"CardDav Link" => "CardDav Link", -"Download" => "Herunterladen", -"Edit" => "Bearbeiten", +"Close" => "Schließen", +"Keyboard shortcuts" => "Tastaturbefehle", +"Navigation" => "Navigation", +"Next contact in list" => "Nächster Kontakt aus der Liste", +"Previous contact in list" => "Vorheriger Kontakt aus der Liste", +"Expand/collapse current addressbook" => "Ausklappen/Einklappen des Adressbuches", +"Next addressbook" => "Nächstes Adressbuch", +"Previous addressbook" => "Vorheriges Adressbuch", +"Actions" => "Aktionen", +"Refresh contacts list" => "Kontaktliste neu laden", +"Add new contact" => "Neuen Kontakt hinzufügen", +"Add new addressbook" => "Neues Adressbuch hinzufügen", +"Delete current contact" => "Aktuellen Kontakt löschen", +"Drop photo to upload" => "Ziehen Sie ein Foto zum Hochladen hierher", +"Delete current photo" => "Derzeitiges Foto löschen", +"Edit current photo" => "Foto ändern", +"Upload new photo" => "Neues Foto hochladen", +"Select photo from ownCloud" => "Foto aus der ownCloud auswählen", +"Format custom, Short name, Full name, Reverse or Reverse with comma" => "Format benutzerdefiniert, Kurzname, Vollname, Rückwärts oder Rückwärts mit Komma", +"Edit name details" => "Name ändern", +"Organization" => "Organisation", "Delete" => "Löschen", -"Download contact" => "Kontakt herunterladen", -"Delete contact" => "Kontakt löschen", -"Drop photo to upload" => "Zieh' ein Foto hierher zum hochladen", -"Format custom, Short name, Full name, Reverse or Reverse with comma" => "Format benutzerdefiniert, Kurzname, Vollname, Rückwärts order Rückwärts mit Komma", -"Edit name details" => "Namen ändern", "Nickname" => "Spitzname", -"Enter nickname" => "Spitznamen angeben", -"Birthday" => "Geburtstag", -"dd-mm-yyyy" => "TT-MM-JJJJ", +"Enter nickname" => "Spitzname angeben", +"Web site" => "Webseite", +"http://www.somesite.com" => "http://www.somesite.com", +"Go to web site" => "Webseite aufrufen", +"dd-mm-yyyy" => "dd.mm.yyyy", "Groups" => "Gruppen", -"Separate groups with commas" => "Gruppen mit Komma trennt", +"Separate groups with commas" => "Gruppen mit Komma getrennt", "Edit groups" => "Gruppen editieren", "Preferred" => "Bevorzugt", "Please specify a valid email address." => "Bitte eine gültige E-Mail-Adresse angeben.", -"Enter email address" => "E-Mail-Adresse angeben.", +"Enter email address" => "E-Mail-Adresse angeben", "Mail to address" => "E-Mail an diese Adresse schreiben", "Delete email address" => "E-Mail-Adresse löschen", "Enter phone number" => "Telefonnummer angeben", "Delete phone number" => "Telefonnummer löschen", +"Instant Messenger" => "Instant Messenger", +"Delete IM" => "IM löschen", "View on map" => "Auf Karte anzeigen", "Edit address details" => "Adressinformationen ändern", "Add notes here." => "Füge hier Notizen ein.", "Add field" => "Feld hinzufügen", -"Profile picture" => "Profil Bild", "Phone" => "Telefon", +"Email" => "E-Mail", +"Instant Messaging" => "Instant Messaging", +"Address" => "Adresse", "Note" => "Notiz", -"Delete current photo" => "Derzeitiges Foto löschen", -"Edit current photo" => "Foto ändern", -"Upload new photo" => "Neues Foto hochladen", -"Select photo from ownCloud" => "Foto aus ownCloud auswählen", +"Download contact" => "Kontakt herunterladen", +"Delete contact" => "Kontakt löschen", +"The temporary image has been removed from cache." => "Das temporäre Bild wurde aus dem Cache gelöscht.", "Edit address" => "Adresse ändern", "Type" => "Typ", "PO Box" => "Postfach", +"Street address" => "Straßenanschrift", +"Street and number" => "Straße und Nummer", "Extended" => "Erweitert", -"Street" => "Straße", +"Apartment number etc." => "Wohnungsnummer usw.", "City" => "Stadt", "Region" => "Region", +"E.g. state or province" => "Z.B. Staat oder Bezirk", "Zipcode" => "Postleitzahl", +"Postal code" => "PLZ", "Country" => "Land", -"Edit categories" => "Kategorie ändern", -"Add" => "Hinzufügen", "Addressbook" => "Adressbuch", "Hon. prefixes" => "Höflichkeitspräfixe", "Miss" => "Frau", @@ -119,12 +183,12 @@ "Mr" => "Herr", "Sir" => "Herr", "Mrs" => "Frau", -"Dr" => "Dr", +"Dr" => "Dr.", "Given name" => "Vorname", "Additional names" => "Zusätzliche Namen", "Family name" => "Familienname", "Hon. suffixes" => "Höflichkeitssuffixe", -"J.D." => "Dr. Jur", +"J.D." => "Dr. Jur.", "M.D." => "Dr. med.", "D.O." => "DGOM", "D.C." => "MChiro", @@ -132,26 +196,29 @@ "Esq." => "Hochwohlgeborenen", "Jr." => "Jr.", "Sn." => "Senior", -"New Addressbook" => "Neues Adressbuch", -"Edit Addressbook" => "Adressbuch editieren", -"Displayname" => "Anzeigename", -"Active" => "Aktiv", -"Save" => "Speichern", -"Submit" => "Eintragen", -"Cancel" => "Abbrechen", "Import a contacts file" => "Kontaktdatei importieren", "Please choose the addressbook" => "Bitte Adressbuch auswählen", "create a new addressbook" => "Neues Adressbuch erstellen", "Name of new addressbook" => "Name des neuen Adressbuchs", -"Import" => "Importieren", "Importing contacts" => "Kontakte werden importiert", -"Select address book to import to:" => "Adressbuch, in das importiert werden soll", -"Select from HD" => "Von der Festplatte auswählen", -"You have no contacts in your addressbook." => "Du hast keine Kontakte im Adressbuch.", +"You have no contacts in your addressbook." => "Sie haben keine Kontakte im Adressbuch.", "Add contact" => "Kontakt hinzufügen", -"Configure addressbooks" => "Adressbücher konfigurieren", +"Select Address Books" => "Wähle Adressbuch", +"Enter name" => "Name eingeben", +"Enter description" => "Beschreibung eingeben", "CardDAV syncing addresses" => "CardDAV Sync-Adressen", -"more info" => "mehr Info", -"Primary address (Kontact et al)" => "primäre Adresse (für Kontact o.ä. Programme)", -"iOS/OS X" => "iOS/OS X" +"more info" => "mehr Informationen", +"Primary address (Kontact et al)" => "Primäre Adresse (für Kontakt o.ä.)", +"iOS/OS X" => "iOS/OS X", +"Show CardDav link" => "CardDav-Link anzeigen", +"Show read-only VCF link" => "Schreibgeschützten VCF-Link anzeigen", +"Share" => "Teilen", +"Download" => "Herunterladen", +"Edit" => "Bearbeiten", +"New Address Book" => "Neues Adressbuch", +"Name" => "Name", +"Description" => "Beschreibung", +"Save" => "Speichern", +"Cancel" => "Abbrechen", +"More..." => "Mehr..." ); diff --git a/apps/contacts/l10n/el.php b/apps/contacts/l10n/el.php index f015f0ca363..3552ffba616 100644 --- a/apps/contacts/l10n/el.php +++ b/apps/contacts/l10n/el.php @@ -1,39 +1,42 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "Σφάλμα (απ)ενεÏγοποίησης βιβλίου διευθÏνσεων", -"There was an error adding the contact." => "Σφάλμα κατά την Ï€Ïοσθήκη επαφής.", -"Cannot add empty property." => "ΑδÏνατη Ï€Ïοσθήκη κενής ιδιότητας.", -"At least one of the address fields has to be filled out." => "Î ÏÎπει να συμπληÏωθεί τουλάχιστον Îνα από τα παιδία διεÏθυνσης.", -"Trying to add duplicate property: " => "Î Ïοσπάθεια Ï€Ïοσθήκης διπλότυπης ιδιότητας:", -"Error adding contact property." => "Σφάλμα Ï€Ïοσθήκης ιδιότητας επαφής.", -"No ID provided" => "Δε δώθηκε ID", +"id is not set." => "δεν οÏίστηκε id", +"Cannot update addressbook with an empty name." => "Δε μποÏεί να γίνει αλλαγή βιβλίου διευθÏνσεων χωÏίς όνομα", +"Error updating addressbook." => "Σφάλμα ενημÎÏωσης βιβλίου διευθÏνσεων.", +"No ID provided" => "Δε δόθηκε ID", "Error setting checksum." => "Λάθος κατά τον οÏισμό checksum ", "No categories selected for deletion." => "Δε επελÎγησαν κατηγοÏίες για διαγÏαφή", "No address books found." => "Δε βÏÎθηκε βιβλίο διευθÏνσεων", "No contacts found." => "Δεν βÏÎθηκαν επαφÎÏ‚", +"There was an error adding the contact." => "Σφάλμα κατά την Ï€Ïοσθήκη επαφής.", +"element name is not set." => "δεν οÏίστηκε όνομα στοιχείου", +"Could not parse contact: " => "Δε αναγνώστηκε η επαφή", +"Cannot add empty property." => "ΑδÏνατη Ï€Ïοσθήκη κενής ιδιότητας.", +"At least one of the address fields has to be filled out." => "Î ÏÎπει να συμπληÏωθεί τουλάχιστον Îνα από τα παιδία διεÏθυνσης.", +"Trying to add duplicate property: " => "Î Ïοσπάθεια Ï€Ïοσθήκης διπλότυπης ιδιότητας:", +"Missing IM parameter." => "Λείπει IM παÏάμετÏος.", +"Unknown IM: " => "Άγνωστο IM:", +"Information about vCard is incorrect. Please reload the page." => "Οι πληÏοφοÏίες σχετικά με vCard είναι εσφαλμÎνες. ΠαÏακαλώ επαναφοÏτώστε τη σελίδα.", "Missing ID" => "Λείπει ID", "Error parsing VCard for ID: \"" => "Σφάλμα κατά την ανάγνωση του VCard για το ID:\"", -"Cannot add addressbook with an empty name." => "Δε μποÏεί να Ï€Ïοστεθεί βιβλίο διευθÏνσεων με κενό όνομα", -"Error adding addressbook." => "Σφάλμα Ï€Ïοσθήκης βιβλίου διευθÏνσεων.", -"Error activating addressbook." => "Σφάλμα ενεÏγοποίησης βιβλίου διευθÏνσεων", +"checksum is not set." => "δε οÏίστηκε checksum ", +"Information about vCard is incorrect. Please reload the page: " => "Οι πληÏοφοÏίες για τη vCard είναι λανθασμÎνες.ΠαÏακαλώ ξαναφοÏτώστε τη σελίδα: ", +"Something went FUBAR. " => "Κάτι χάθηκε στο άγνωστο. ", "No contact ID was submitted." => "Δε υπεβλήθει ID επαφής", "Error reading contact photo." => "Σφάλμα ανάγνωσης εικόνας επαφής", "Error saving temporary file." => "Σφάλμα αποθήκευσης Ï€ÏοσωÏÎ¹Î½Î¿Ï Î±Ïχείου", "The loading photo is not valid." => "Η φοÏτωμÎνη φωτογÏαφία δεν είναι ÎγκυÏη", -"id is not set." => "δεν οÏίστηκε id", -"Information about vCard is incorrect. Please reload the page." => "Οι πληÏοφοÏίες σχετικά με vCard είναι εσφαλμÎνες. ΠαÏακαλώ επαναφοÏτώστε τη σελίδα.", -"Error deleting contact property." => "Σφάλμα διαγÏαφής ιδιότητας επαφής.", "Contact ID is missing." => "Λείπει ID επαφής", -"Missing contact id." => "Απουσιαζει ID επαφής", "No photo path was submitted." => "Δε δόθηκε διαδÏομή εικόνας", "File doesn't exist:" => "Το αÏχείο δεν υπάÏχει:", "Error loading image." => "Σφάλμα φόÏτωσης εικόνας", -"element name is not set." => "δεν οÏίστηκε όνομα στοιχείου", -"checksum is not set." => "δε οÏίστηκε checksum ", -"Information about vCard is incorrect. Please reload the page: " => "Οι πληÏοφοÏίες για τη vCard είναι λανθασμÎνες.ΠαÏακαλώ ξαναφοÏτώστε τη σελίδα:", -"Something went FUBAR. " => "Κάτι χάθηκε στο άγνωστο", -"Error updating contact property." => "Σφάλμα ενημÎÏωσης ιδιότητας επαφής.", -"Cannot update addressbook with an empty name." => "Δε μποÏεί να γίνει αλλαγή βιβλίου διευθÏνσεων χωÏίς όνομα", -"Error updating addressbook." => "Σφάλμα ενημÎÏωσης βιβλίου διευθÏνσεων.", +"Error getting contact object." => "Σφάλμα κατά τη λήψη αντικειμÎνου επαφής", +"Error getting PHOTO property." => "Σφάλμα κατά τη λήψη ιδιοτήτων ΦΩΤΟΓΡΑΦΙΑΣ.", +"Error saving contact." => "Σφάλμα κατά την αποθήκευση επαφής.", +"Error resizing image" => "Σφάλμα κατά την αλλαγή μεγÎθους εικόνας", +"Error cropping image" => "Σφάλμα κατά την πεÏικοπή εικόνας", +"Error creating temporary image" => "Σφάλμα κατά την δημιουÏγία Ï€ÏοσωÏινής εικόνας", +"Error finding image: " => "Σφάλμα κατά την εÏÏεση της εικόνας: ", "Error uploading contacts to storage." => "Σφάλμα κατά την αποθήκευση επαφών", "There is no error, the file uploaded with success" => "Δεν υπάÏχει σφάλμα, το αÏχείο ανÎβηκε με επιτυχία ", "The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Το μÎγεθος του αÏχείου ξεπεÏνάει το upload_max_filesize του php.ini", @@ -41,44 +44,100 @@ "The uploaded file was only partially uploaded" => "Το αÏχείο ανÎβηκε μεÏικώς", "No file was uploaded" => "Δεν ανÎβηκε κάποιο αÏχείο", "Missing a temporary folder" => "Λείπει ο Ï€ÏοσωÏινός φάκελος", +"Couldn't save temporary image: " => "Δεν ήταν δυνατή η αποθήκευση της Ï€ÏοσωÏινής εικόνας: ", +"Couldn't load temporary image: " => "Δεν ήταν δυνατή η φόÏτωση της Ï€ÏοσωÏινής εικόνας: ", +"No file was uploaded. Unknown error" => "Δεν ανÎβηκε κάποιο αÏχείο. Άγνωστο σφάλμα", "Contacts" => "ΕπαφÎÏ‚", -"Drop a VCF file to import contacts." => "Εισάγεται Îνα VCF αÏχείο για εισαγωγή επαφών", -"Addressbook not found." => "Δε βÏÎθηκε βιβλίο διευθÏνσεων", +"Sorry, this functionality has not been implemented yet" => "ΛυποÏμαστε, αυτή η λειτουÏγία δεν Îχει υλοποιηθεί ακόμα", +"Not implemented" => "Δεν Îχει υλοποιηθεί", +"Couldn't get a valid address." => "Αδυναμία λήψης ÎγκυÏης διεÏθυνσης", +"Error" => "Σφάλμα", +"This property has to be non-empty." => "Το πεδίο δεν Ï€ÏÎπει να είναι άδειο.", +"Couldn't serialize elements." => "ΑδÏνατο να μπουν σε σειÏά τα στοιχεία", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "το 'deleteProperty' καλÎστηκε χωÏίς without type argument. ΠαÏακαλώ αναφÎÏατε στο bugs.owncloud.org", +"Edit name" => "Αλλαγή ονόματος", +"No files selected for upload." => "Δεν επιλÎχτηκαν αÏχεία για μεταφόÏτωση", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "Το αÏχείο που Ï€Ïοσπαθείτε να ανεβάσετε υπεÏβαίνει το μÎγιστο μÎγεθος για τις Ï€Ïοσθήκες αÏχείων σε αυτόν τον server.", +"Error loading profile picture." => "Σφάλμα στην φόÏτωση εικόνας Ï€Ïοφίλ.", +"Select type" => "Επιλογή Ï„Ïπου", +"Some contacts are marked for deletion, but not deleted yet. Please wait for them to be deleted." => "Κάποιες επαφÎÏ‚ σημειώθηκαν Ï€Ïος διαγÏαφή,δεν Îχουν διαγÏαφεί ακόμα. ΠαÏακαλώ πεÏιμÎνετε μÎχÏι να διαγÏαφοÏν.", +"Do you want to merge these address books?" => "Επιθυμείτε να συγχωνεÏσετε αυτά τα δÏο βιβλία διευθÏνσεων?", +"Result: " => "ΑποτÎλεσμα: ", +" imported, " => " εισάγεται,", +" failed." => " απÎτυχε.", +"Displayname cannot be empty." => "Το όνομα Ï€Ïοβολής δεν μποÏεί να είναι κενό. ", +"Addressbook not found: " => "Το βιβλίο διευθÏνσεων δεν βÏÎθηκε:", "This is not your addressbook." => "Αυτό δεν είναι το βιβλίο διευθÏνσεων σας.", "Contact could not be found." => "Η επαφή δεν μπόÏεσε να βÏεθεί.", -"Address" => "ΔιεÏθυνση", -"Telephone" => "ΤηλÎφωνο", -"Email" => "Email", -"Organization" => "ΟÏγανισμός", +"Jabber" => "Jabber", +"AIM" => "AIM", +"MSN" => "MSN", +"Twitter" => "Twitter", +"GoogleTalk" => "GoogleTalk", +"Facebook" => "Facebook", +"XMPP" => "XMPP", +"ICQ" => "ICQ", +"Yahoo" => "Yahoo", +"Skype" => "Skype", +"QQ" => "QQ", +"GaduGadu" => "GaduGadu", "Work" => "ΕÏγασία", "Home" => "Σπίτι", +"Other" => "Άλλο", "Mobile" => "Κινητό", "Text" => "Κείμενο", "Voice" => "Ομιλία", -"Message" => "Μήνυμα ", +"Message" => "Μήνυμα", "Fax" => "Φαξ", "Video" => "Βίντεο", "Pager" => "Βομβητής", "Internet" => "Διαδίκτυο", +"Birthday" => "ΓενÎθλια", +"Business" => "ΕπιχείÏηση", +"Call" => "Κάλεσε", +"Clients" => "Πελάτες", +"Deliverer" => "Î Ïομηθευτής", +"Holidays" => "ΔιακοπÎÏ‚", +"Ideas" => "ΙδÎες", +"Journey" => "Ταξίδι", +"Jubilee" => "Ιωβηλαίο", +"Meeting" => "Συνάντηση", +"Personal" => "Î Ïοσωπικό", +"Projects" => "ΈÏγα", +"Questions" => "ΕÏωτήσεις", "{name}'s Birthday" => "{name} Îχει ΓενÎθλια", "Contact" => "Επαφή", "Add Contact" => "Î Ïοσθήκη επαφής", +"Import" => "Εισαγωγή", +"Settings" => "Ρυθμίσεις", "Addressbooks" => "Βιβλία διευθÏνσεων", -"Configure Address Books" => "Ρυθμίστε το βιβλίο διευθÏνσεων ", -"New Address Book" => "ÎÎο βιβλίο διευθÏνσεων", -"Import from VCF" => "Εισαγωγή από VCF αÏχείο", -"CardDav Link" => "ΣÏνδεσμος CardDav", -"Download" => "Λήψη", -"Edit" => "ΕπεξεÏγασία", -"Delete" => "ΔιαγÏαφή", -"Download contact" => "Λήψη επαφής", -"Delete contact" => "ΔιαγÏαφή επαφής", +"Close" => "Κλείσιμο ", +"Keyboard shortcuts" => "ΣυντομεÏσεις πλητÏολογίου", +"Navigation" => "Πλοήγηση", +"Next contact in list" => "Επόμενη επαφή στη λίστα", +"Previous contact in list" => "Î ÏοηγοÏμενη επαφή στη λίστα", +"Expand/collapse current addressbook" => "Ανάπτυξη/σÏμπτυξη Ï„ÏÎχοντος βιβλίου διευθÏνσεων", +"Next addressbook" => "Επόμενο βιβλίο διευθÏνσεων", +"Previous addressbook" => "Î ÏοηγοÏμενο βιβλίο διευθÏνσεων", +"Actions" => "ΕνÎÏγειες", +"Refresh contacts list" => "ΑνανÎωσε τη λίστα επαφών", +"Add new contact" => "Î Ïοσθήκη νÎας επαφής", +"Add new addressbook" => "Î Ïοσθήκη νÎου βιβλίου επαφών", +"Delete current contact" => "ΔιαγÏαφή Ï„ÏÎχουσας επαφής", "Drop photo to upload" => "Ρίξε μια φωτογÏαφία για ανÎβασμα", +"Delete current photo" => "ΔιαγÏαφή Ï„ÏÎχουσας φωτογÏαφίας", +"Edit current photo" => "ΕπεξεÏγασία Ï„ÏÎχουσας φωτογÏαφίας", +"Upload new photo" => "ΑνÎβασε νÎα φωτογÏαφία", +"Select photo from ownCloud" => "ΕπÎλεξε φωτογÏαφία από το ownCloud", "Format custom, Short name, Full name, Reverse or Reverse with comma" => "Format custom, Όνομα, Επώνυμο, ΑντίστÏοφο ή ΑντίστÏοφο με κόμμα", "Edit name details" => "Αλλάξτε τις λεπτομÎÏειες ονόματος", +"Organization" => "ΟÏγανισμός", +"Delete" => "ΔιαγÏαφή", "Nickname" => "ΠαÏατσοÏκλι", -"Enter nickname" => "Εισάγεται παÏατσοÏκλι", -"Birthday" => "ΓενÎθλια", +"Enter nickname" => "Εισάγετε παÏατσοÏκλι", +"Web site" => "Ιστότοπος", +"http://www.somesite.com" => "http://www.somesite.com", +"Go to web site" => "Πήγαινε στον ιστότοπο", "dd-mm-yyyy" => "ΗΗ-ΜΜ-ΕΕΕΕ", "Groups" => "Ομάδες", "Separate groups with commas" => "ΔιαχώÏισε τις ομάδες με κόμμα ", @@ -90,28 +149,33 @@ "Delete email address" => "ΔιαγÏαφή διεÏθυνση email", "Enter phone number" => "Εισήγαγε αÏιθμό τηλεφώνου", "Delete phone number" => "ΔιÎγÏαψε αÏιθμό τηλεφώνου", +"Instant Messenger" => "Instant Messenger", +"Delete IM" => "ΔιαγÏαφή IM", "View on map" => "Î Ïοβολή στο χάÏτη", "Edit address details" => "ΕπεξεÏγασία λεπτομεÏειών διεÏθυνσης", "Add notes here." => "Î Ïόσθεσε τις σημειώσεις εδώ", "Add field" => "Î Ïοσθήκη πεδίου", -"Profile picture" => "ΦωτογÏαφία Ï€Ïοφίλ", "Phone" => "ΤηλÎφωνο", +"Email" => "Email", +"Instant Messaging" => "Άμεσα μυνήματα", +"Address" => "ΔιεÏθυνση", "Note" => "Σημείωση", -"Delete current photo" => "ΔιαγÏαφή Ï„ÏÎχουσας φωτογÏαφίας", -"Edit current photo" => "ΕπεξεÏγασία Ï„ÏÎχουσας φωτογÏαφίας", -"Upload new photo" => "ΑνÎβασε νÎα φωτογÏαφία", -"Select photo from ownCloud" => "ΕπÎλεξε φωτογÏαφία από το ownCloud", +"Download contact" => "Λήψη επαφής", +"Delete contact" => "ΔιαγÏαφή επαφής", +"The temporary image has been removed from cache." => "Η Ï€ÏοσωÏινή εικόνα αφαιÏÎθηκε από την κÏυφή μνήμη.", "Edit address" => "ΕπεξεÏγασία διεÏθυνσης", "Type" => "ΤÏπος", "PO Box" => "Ταχ. ΘυÏίδα", +"Street address" => "ΔιεÏθυνση οδοÏ", +"Street and number" => "Οδός και αÏιθμός", "Extended" => "ΕκτεταμÎνη", -"Street" => "Οδός", +"Apartment number etc." => "ΑÏιθμός διαμεÏίσματος", "City" => "Πόλη", "Region" => "ΠεÏιοχή", +"E.g. state or province" => "Î .χ. Πολιτεία ή επαÏχεία", "Zipcode" => "Τ.Κ.", +"Postal code" => "ΤαχυδÏομικός Κωδικός", "Country" => "ΧώÏα", -"Edit categories" => "ΕπεξεÏγασία κατηγοÏίας", -"Add" => "Î Ïοσθήκη", "Addressbook" => "Βιβλίο διευθÏνσεων", "Hon. prefixes" => "Ï€ÏοθÎματα", "Miss" => "Δις", @@ -132,26 +196,35 @@ "Esq." => "Esq.", "Jr." => "Jr.", "Sn." => "Sn.", -"New Addressbook" => "ÎÎο βιβλίο διευθÏνσεων", -"Edit Addressbook" => "ΕπεξεÏγασία βιβλίου διευθÏνσεων", -"Displayname" => "Î Ïοβαλόμενο όνομα", -"Active" => "ΕνεÏγό", -"Save" => "Αποθήκευση", -"Submit" => "ΚαταχώÏηση", -"Cancel" => "ΑκÏÏωση", "Import a contacts file" => "Εισαγωγή αÏχείου επαφών", "Please choose the addressbook" => "ΠαÏακαλώ επÎλεξε βιβλίο διευθÏνσεων", "create a new addressbook" => "ΔημιουÏγία νÎου βιβλίου διευθÏνσεων", "Name of new addressbook" => "Όνομα νÎου βιβλίου διευθÏνσεων", -"Import" => "Εισαγωγή", "Importing contacts" => "Εισαγωγή επαφών", +"Contacts imported successfully" => "Οι επαφÎÏ‚ εισήχθησαν επιτυχώς", +"Close Dialog" => "Κλείσιμο διαλόγου", +"Import Addressbook" => "Εισαγωγή βιβλίου διευθÏνσεων", "Select address book to import to:" => "ΕπÎλεξε σε ποιο βιβλίο διευθÏνσεων για εισαγωγή:", +"Drop a VCF file to import contacts." => "Εισάγεται Îνα VCF αÏχείο για εισαγωγή επαφών", "Select from HD" => "Επιλογή από HD", "You have no contacts in your addressbook." => "Δεν Îχεις επαφÎÏ‚ στο βιβλίο διευθÏνσεων", "Add contact" => "Î Ïοσθήκη επαφής", -"Configure addressbooks" => "ΡÏθμισε το βιβλίο διευθÏνσεων", +"Select Address Books" => "ΕπÎλεξε βιβλίο διευθÏνσεων", +"Enter name" => "Εισαγωγή ονόματος", +"Enter description" => "Εισαγωγή πεÏιγÏαφής", "CardDAV syncing addresses" => "συγχÏονισμός διευθÏνσεων μÎσω CardDAV ", "more info" => "πεÏισσότεÏες πληÏοφοÏίες", "Primary address (Kontact et al)" => "ΚÏÏια διεÏθυνση", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Show CardDav link" => "Εμφάνιση συνδÎσμου CardDav", +"Show read-only VCF link" => "Εμφάνιση συνδÎσμου VCF μόνο για ανάγνωση", +"Share" => "ΜοιÏάσου", +"Download" => "Λήψη", +"Edit" => "ΕπεξεÏγασία", +"New Address Book" => "ÎÎο βιβλίο διευθÏνσεων", +"Name" => "Όνομα", +"Description" => "ΠεÏιγÏαφή", +"Save" => "Αποθήκευση", +"Cancel" => "ΑκÏÏωση", +"More..." => "ΠεÏισσότεÏα..." ); diff --git a/apps/contacts/l10n/eo.php b/apps/contacts/l10n/eo.php index ffc885a41a5..e4eb06db2aa 100644 --- a/apps/contacts/l10n/eo.php +++ b/apps/contacts/l10n/eo.php @@ -1,54 +1,70 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "Eraro dum (mal)aktivigo de adresaro.", -"There was an error adding the contact." => "Eraro okazis dum aldono de kontakto.", -"Cannot add empty property." => "Ne eblas aldoni malplenan propraĵon.", -"At least one of the address fields has to be filled out." => "AlmenaÅ unu el la adreskampoj necesas pleniÄi.", -"Trying to add duplicate property: " => "Provante aldoni duobligitan propraĵon:", -"Error adding contact property." => "Eraro dum aldono de kontaktopropraĵo.", +"id is not set." => "identigilo ne agordiÄis.", +"Cannot update addressbook with an empty name." => "Ne eblas Äisdatigi adresaron kun malplena nomo.", +"Error updating addressbook." => "Eraro dum Äisdatigo de adresaro.", "No ID provided" => "Neniu identigilo proviziÄis.", "Error setting checksum." => "Eraro dum agordado de kontrolsumo.", "No categories selected for deletion." => "Neniu kategorio elektiÄis por forigi.", "No address books found." => "Neniu adresaro troviÄis.", "No contacts found." => "Neniu kontakto troviÄis.", +"There was an error adding the contact." => "Eraro okazis dum aldono de kontakto.", +"element name is not set." => "eronomo ne agordiÄis.", +"Could not parse contact: " => "Ne eblis analizi kontakton:", +"Cannot add empty property." => "Ne eblas aldoni malplenan propraĵon.", +"At least one of the address fields has to be filled out." => "AlmenaÅ unu el la adreskampoj necesas pleniÄi.", +"Trying to add duplicate property: " => "Provante aldoni duobligitan propraĵon:", +"Information about vCard is incorrect. Please reload the page." => "Informo pri vCard estas malÄusta. Bonvolu reÅargi la paÄon.", "Missing ID" => "Mankas identigilo", "Error parsing VCard for ID: \"" => "Eraro dum analizo de VCard por identigilo:", -"Cannot add addressbook with an empty name." => "Ne eblas aldoni adresaron kun malplena nomo.", -"Error adding addressbook." => "Eraro dum aldono de adresaro.", -"Error activating addressbook." => "Eraro dum aktivigo de adresaro.", +"checksum is not set." => "kontrolsumo ne agordiÄis.", +"Information about vCard is incorrect. Please reload the page: " => "Informo pri vCard malÄustas. Bonvolu reÅargi la paÄon:", +"Something went FUBAR. " => "Io FUBAR-is.", "No contact ID was submitted." => "Neniu kontaktidentigilo sendiÄis.", "Error reading contact photo." => "Eraro dum lego de kontakta foto.", "Error saving temporary file." => "Eraro dum konservado de provizora dosiero.", "The loading photo is not valid." => "La alÅutata foto ne validas.", -"id is not set." => "identigilo ne agordiÄis.", -"Information about vCard is incorrect. Please reload the page." => "Informo pri vCard estas malÄusta. Bonvolu reÅargi la paÄon.", -"Error deleting contact property." => "Eraro dum forigo de kontaktopropraĵo.", "Contact ID is missing." => "Kontaktidentigilo mankas.", -"Missing contact id." => "Mankas kontaktidentigilo.", "No photo path was submitted." => "Neniu vojo al foto sendiÄis.", "File doesn't exist:" => "Dosiero ne ekzistas:", "Error loading image." => "Eraro dum Åargado de bildo.", -"element name is not set." => "eronomo ne agordiÄis.", -"checksum is not set." => "kontrolsumo ne agordiÄis.", -"Information about vCard is incorrect. Please reload the page: " => "Informo pri vCard malÄustas. Bonvolu reÅargi la paÄon:", -"Something went FUBAR. " => "Io FUBAR-is.", -"Error updating contact property." => "Eraro dum Äisdatigo de kontaktopropraĵo.", -"Cannot update addressbook with an empty name." => "Ne eblas Äisdatigi adresaron kun malplena nomo.", -"Error updating addressbook." => "Eraro dum Äisdatigo de adresaro.", +"Error getting contact object." => "Eraro dum ekhaviÄis kontakta objekto.", +"Error getting PHOTO property." => "Eraro dum ekhaviÄis la propraĵon PHOTO.", +"Error saving contact." => "Eraro dum konserviÄis kontakto.", +"Error resizing image" => "Eraro dum aligrandiÄis bildo", +"Error cropping image" => "Eraro dum stuciÄis bildo.", +"Error creating temporary image" => "Eraro dum kreiÄis provizora bildo.", +"Error finding image: " => "Eraro dum serĉo de bildo: ", +"Error uploading contacts to storage." => "Eraro dum alÅutiÄis kontaktoj al konservejo.", "There is no error, the file uploaded with success" => "Ne estas eraro, la dosiero alÅutiÄis sukcese.", +"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "La alÅutita dosiero transpasas la preskribon upload_max_filesize en php.ini", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "La alÅutita dosiero transpasas la preskribon MAX_FILE_SIZE kiu specifiÄis en la HTML-formularo", "The uploaded file was only partially uploaded" => "la alÅutita dosiero nur parte alÅutiÄis", "No file was uploaded" => "Neniu dosiero alÅutiÄis.", "Missing a temporary folder" => "Mankas provizora dosierujo.", +"Couldn't save temporary image: " => "Ne eblis konservi provizoran bildon: ", +"Couldn't load temporary image: " => "Ne eblis Åargi provizoran bildon: ", +"No file was uploaded. Unknown error" => "Neniu dosiero alÅutiÄis. Nekonata eraro.", "Contacts" => "Kontaktoj", -"Drop a VCF file to import contacts." => "Demetu VCF-dosieron por enporti kontaktojn.", -"Addressbook not found." => "Adresaro ne troviÄis.", +"Sorry, this functionality has not been implemented yet" => "Pardonu, ĉi tiu funkcio ankoraÅ ne estas realigita.", +"Not implemented" => "Ne disponebla", +"Couldn't get a valid address." => "Ne eblis ekhavi validan adreson.", +"Error" => "Eraro", +"This property has to be non-empty." => "Ĉi tiu propraĵo devas ne esti malplena.", +"Couldn't serialize elements." => "Ne eblis seriigi erojn.", +"Edit name" => "Redakti nomon", +"No files selected for upload." => "Neniu dosiero elektita por alÅuto.", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "La dosiero, kiun vi provas alÅuti, transpasas la maksimuman grandon por dosieraj alÅutoj en ĉi tiu servilo.", +"Select type" => "Elektu tipon", +"Result: " => "Rezulto: ", +" imported, " => " enportoj, ", +" failed." => "malsukcesoj.", +"Addressbook not found: " => "Adresaro ne troviÄis:", "This is not your addressbook." => "Ĉi tiu ne estas via adresaro.", "Contact could not be found." => "Ne eblis trovi la kontakton.", -"Address" => "Adreso", -"Telephone" => "Telefono", -"Email" => "RetpoÅtadreso", -"Organization" => "Organizaĵo", "Work" => "Laboro", "Home" => "Hejmo", +"Other" => "Alia", "Mobile" => "PoÅtelefono", "Text" => "Teksto", "Voice" => "Voĉo", @@ -57,25 +73,51 @@ "Video" => "Videaĵo", "Pager" => "Televokilo", "Internet" => "Interreto", +"Birthday" => "NaskiÄotago", +"Business" => "Negoco", +"Call" => "Voko", +"Clients" => "Klientoj", +"Deliverer" => "Liveranto", +"Holidays" => "Ferioj", +"Ideas" => "Ideoj", +"Journey" => "VojaÄo", +"Jubilee" => "Jubileo", +"Meeting" => "Kunveno", +"Personal" => "Persona", +"Projects" => "Projektoj", +"Questions" => "Demandoj", "{name}'s Birthday" => "NaskiÄtago de {name}", "Contact" => "Kontakto", "Add Contact" => "Aldoni kontakton", +"Import" => "Enporti", +"Settings" => "Agordo", "Addressbooks" => "Adresaroj", -"Configure Address Books" => "Agordi adresarojn", -"New Address Book" => "Nova adresaro", -"Import from VCF" => "Enporti el VCF", -"CardDav Link" => "CardDav-ligilo", -"Download" => "ElÅuti", -"Edit" => "Redakti", -"Delete" => "Forigi", -"Download contact" => "ElÅuti kontakton", -"Delete contact" => "Forigi kontakton", +"Close" => "Fermi", +"Keyboard shortcuts" => "Fulmoklavoj", +"Navigation" => "Navigado", +"Next contact in list" => "Jena kontakto en la listo", +"Previous contact in list" => "Maljena kontakto en la listo", +"Next addressbook" => "Jena adresaro", +"Previous addressbook" => "Maljena adresaro", +"Actions" => "Agoj", +"Refresh contacts list" => "RefreÅigi la kontaktoliston", +"Add new contact" => "Aldoni novan kontakton", +"Add new addressbook" => "Aldoni novan adresaron", +"Delete current contact" => "Forigi la nunan kontakton", "Drop photo to upload" => "Demeti foton por alÅuti", +"Delete current photo" => "Forigi nunan foton", +"Edit current photo" => "Redakti nunan foton", +"Upload new photo" => "AlÅuti novan foton", +"Select photo from ownCloud" => "Elekti foton el ownCloud", "Format custom, Short name, Full name, Reverse or Reverse with comma" => "Propra formo, Mallonga nomo, Longa nomo, Inversa aÅ Inversa kun komo", "Edit name details" => "Redakti detalojn de nomo", +"Organization" => "Organizaĵo", +"Delete" => "Forigi", "Nickname" => "Kromnomo", "Enter nickname" => "Enigu kromnomon", -"Birthday" => "NaskiÄotago", +"Web site" => "TTT-ejo", +"http://www.somesite.com" => "http://www.iuejo.com", +"Go to web site" => "Iri al TTT-ejon", "dd-mm-yyyy" => "yyyy-mm-dd", "Groups" => "Grupoj", "Separate groups with commas" => "Disigi grupojn per komoj", @@ -91,24 +133,24 @@ "Edit address details" => "Redakti detalojn de adreso", "Add notes here." => "Aldoni notojn ĉi tie.", "Add field" => "Aldoni kampon", -"Profile picture" => "Profila bildo", "Phone" => "Telefono", +"Email" => "RetpoÅtadreso", +"Address" => "Adreso", "Note" => "Noto", -"Delete current photo" => "Forigi nunan foton", -"Edit current photo" => "Redakti nunan foton", -"Upload new photo" => "AlÅuti novan foton", -"Select photo from ownCloud" => "Elekti foton el ownCloud", +"Download contact" => "ElÅuti kontakton", +"Delete contact" => "Forigi kontakton", +"The temporary image has been removed from cache." => "La provizora bildo estas forigita de la kaÅmemoro.", "Edit address" => "Redakti adreson", "Type" => "Tipo", "PO Box" => "Abonkesto", +"Street address" => "Stratadreso", +"Street and number" => "Strato kaj numero", "Extended" => "Etendita", -"Street" => "Strato", "City" => "Urbo", "Region" => "Regiono", "Zipcode" => "PoÅtokodo", +"Postal code" => "PoÅtkodo", "Country" => "Lando", -"Edit categories" => "Redakti kategoriojn", -"Add" => "Aldoni", "Addressbook" => "Adresaro", "Hon. prefixes" => "Honoraj antaÅmetaĵoj", "Miss" => "f-ino", @@ -121,26 +163,28 @@ "Additional names" => "Pliaj nomoj", "Family name" => "Familia nomo", "Hon. suffixes" => "Honoraj postmetaĵoj", -"New Addressbook" => "Nova adresaro", -"Edit Addressbook" => "Redakti adresaron", -"Displayname" => "Montronomo", -"Active" => "Aktiva", -"Save" => "Konservi", -"Submit" => "Sendi", -"Cancel" => "Nuligi", "Import a contacts file" => "Enporti kontaktodosieron", "Please choose the addressbook" => "Bonvolu elekti adresaron", "create a new addressbook" => "krei novan adresaron", "Name of new addressbook" => "Nomo de nova adresaro", -"Import" => "Enporti", "Importing contacts" => "Enportante kontaktojn", -"Select address book to import to:" => "Elektu adresaron kien enporti:", -"Select from HD" => "Elekti el malmoldisko", "You have no contacts in your addressbook." => "Vi ne havas kontaktojn en via adresaro", "Add contact" => "Aldoni kontakton", -"Configure addressbooks" => "Agordi adresarojn", +"Select Address Books" => "Elektu adresarojn", +"Enter name" => "Enigu nomon", +"Enter description" => "Enigu priskribon", "CardDAV syncing addresses" => "adresoj por CardDAV-sinkronigo", "more info" => "pli da informo", "Primary address (Kontact et al)" => "Ĉefa adreso (por Kontakt kaj aliaj)", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Show CardDav link" => "Montri CardDav-ligilon", +"Show read-only VCF link" => "Montri nur legeblan VCF-ligilon", +"Download" => "ElÅuti", +"Edit" => "Redakti", +"New Address Book" => "Nova adresaro", +"Name" => "Nomo", +"Description" => "Priskribo", +"Save" => "Konservi", +"Cancel" => "Nuligi", +"More..." => "Pli..." ); diff --git a/apps/contacts/l10n/es.php b/apps/contacts/l10n/es.php index 2bdb300aa8f..c80c2987e10 100644 --- a/apps/contacts/l10n/es.php +++ b/apps/contacts/l10n/es.php @@ -1,39 +1,41 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "Error al (des)activar libreta de direcciones.", -"There was an error adding the contact." => "Se ha producido un error al añadir el contacto.", -"Cannot add empty property." => "No se puede añadir una propiedad vacÃa.", -"At least one of the address fields has to be filled out." => "Al menos uno de los campos de direcciones se tiene que rellenar.", -"Trying to add duplicate property: " => "Intentando añadir una propiedad duplicada: ", -"Error adding contact property." => "Error al añadir una propiedad del contacto.", +"id is not set." => "no se ha puesto ninguna ID.", +"Cannot update addressbook with an empty name." => "No se puede actualizar una libreta de direcciones sin nombre.", +"Error updating addressbook." => "Error al actualizar la libreta de direcciones.", "No ID provided" => "No se ha proporcionado una ID", "Error setting checksum." => "Error al establecer la suma de verificación.", "No categories selected for deletion." => "No se seleccionaron categorÃas para borrar.", "No address books found." => "No se encontraron libretas de direcciones.", "No contacts found." => "No se encontraron contactos.", +"There was an error adding the contact." => "Se ha producido un error al añadir el contacto.", +"element name is not set." => "no se ha puesto ningún nombre de elemento.", +"Cannot add empty property." => "No se puede añadir una propiedad vacÃa.", +"At least one of the address fields has to be filled out." => "Al menos uno de los campos de direcciones se tiene que rellenar.", +"Trying to add duplicate property: " => "Intentando añadir una propiedad duplicada: ", +"Missing IM parameter." => "Falta un parámetro del MI.", +"Unknown IM: " => "MI desconocido:", +"Information about vCard is incorrect. Please reload the page." => "La información sobre el vCard es incorrecta. Por favor vuelve a cargar la página.", "Missing ID" => "Falta la ID", "Error parsing VCard for ID: \"" => "Error al analizar el VCard para la ID: \"", -"Cannot add addressbook with an empty name." => "No se puede añadir una libreta de direcciones sin nombre", -"Error adding addressbook." => "Error al añadir la libreta de direcciones.", -"Error activating addressbook." => "Error al activar la libreta de direcciones.", +"checksum is not set." => "no se ha puesto ninguna suma de comprobación.", +"Information about vCard is incorrect. Please reload the page: " => "La información sobre la vCard es incorrecta. Por favor, recarga la página:", +"Something went FUBAR. " => "Plof. Algo ha fallado.", "No contact ID was submitted." => "No se ha mandado ninguna ID de contacto.", "Error reading contact photo." => "Error leyendo fotografÃa del contacto.", "Error saving temporary file." => "Error al guardar archivo temporal.", "The loading photo is not valid." => "La foto que se estaba cargando no es válida.", -"id is not set." => "no se ha puesto ninguna ID.", -"Information about vCard is incorrect. Please reload the page." => "La información sobre el vCard es incorrecta. Por favor vuelve a cargar la página.", -"Error deleting contact property." => "Error al borrar una propiedad del contacto.", "Contact ID is missing." => "Falta la ID del contacto.", -"Missing contact id." => "Falta la id del contacto.", "No photo path was submitted." => "No se ha introducido la ruta de la foto.", "File doesn't exist:" => "Archivo inexistente:", "Error loading image." => "Error cargando imagen.", -"element name is not set." => "no se ha puesto ningún nombre de elemento.", -"checksum is not set." => "no se ha puesto ninguna suma de comprobación.", -"Information about vCard is incorrect. Please reload the page: " => "La información sobre la vCard es incorrecta. Por favor, recarga la página:", -"Something went FUBAR. " => "Plof. Algo ha fallado.", -"Error updating contact property." => "Error al actualizar una propiedad del contacto.", -"Cannot update addressbook with an empty name." => "No se puede actualizar una libreta de direcciones sin nombre.", -"Error updating addressbook." => "Error al actualizar la libreta de direcciones.", +"Error getting contact object." => "Fallo al coger el contacto.", +"Error getting PHOTO property." => "Fallo al coger las propiedades de la foto .", +"Error saving contact." => "Fallo al salvar un contacto", +"Error resizing image" => "Fallo al cambiar de tamaño una foto", +"Error cropping image" => "Fallo al cortar el tamaño de la foto", +"Error creating temporary image" => "Fallo al crear la foto temporal", +"Error finding image: " => "Fallo al encontrar la imagen", "Error uploading contacts to storage." => "Error al subir contactos al almacenamiento.", "There is no error, the file uploaded with success" => "No hay ningún error, el archivo se ha subido con éxito", "The uploaded file exceeds the upload_max_filesize directive in php.ini" => "El archivo subido sobrepasa la directiva upload_max_filesize de php.ini", @@ -41,17 +43,41 @@ "The uploaded file was only partially uploaded" => "El archivo se ha subido parcialmente", "No file was uploaded" => "No se ha subido ningún archivo", "Missing a temporary folder" => "Falta la carpeta temporal", +"Couldn't save temporary image: " => "Fallo no pudo salvar a una imagen temporal", +"Couldn't load temporary image: " => "Fallo no pudo cargara de una imagen temporal", +"No file was uploaded. Unknown error" => "Fallo no se subió el fichero", "Contacts" => "Contactos", -"Drop a VCF file to import contacts." => "Suelta un archivo VCF para importar contactos.", -"Addressbook not found." => "Libreta de direcciones no encontrada.", +"Sorry, this functionality has not been implemented yet" => "Perdón esta función no esta aún implementada", +"Not implemented" => "No esta implementada", +"Couldn't get a valid address." => "Fallo : no hay dirección valida", +"Error" => "Fallo", +"This property has to be non-empty." => "Este campo no puede estar vacÃo.", +"Couldn't serialize elements." => "Fallo no podido ordenar los elementos", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "La propiedad de \"borrar\" se llamado sin argumentos envia fallos a\nbugs.owncloud.org", +"Edit name" => "Edita el Nombre", +"No files selected for upload." => "No hay ficheros seleccionados para subir", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "El fichero que quieres subir excede el tamaño máximo permitido en este servidor.", +"Select type" => "Selecciona el tipo", +"Result: " => "Resultado :", +" imported, " => "Importado.", +" failed." => "Fallo.", "This is not your addressbook." => "Esta no es tu agenda de contactos.", "Contact could not be found." => "No se ha podido encontrar el contacto.", -"Address" => "Dirección", -"Telephone" => "Teléfono", -"Email" => "Correo electrónico", -"Organization" => "Organización", +"Jabber" => "Jabber", +"AIM" => "AIM", +"MSN" => "MSN", +"Twitter" => "Twitter", +"GoogleTalk" => "Google Talk", +"Facebook" => "Facebook", +"XMPP" => "XMPP", +"ICQ" => "ICQ", +"Yahoo" => "Yahoo", +"Skype" => "Skype", +"QQ" => "QQ", +"GaduGadu" => "GaduGadu", "Work" => "Trabajo", "Home" => "Particular", +"Other" => "Otro", "Mobile" => "Móvil", "Text" => "Texto", "Voice" => "Voz", @@ -60,25 +86,47 @@ "Video" => "VÃdeo", "Pager" => "Localizador", "Internet" => "Internet", +"Birthday" => "Cumpleaños", +"Business" => "Negocio", +"Call" => "Llamada", +"Clients" => "Clientes", +"Holidays" => "Vacaciones", +"Ideas" => "Ideas", +"Journey" => "Jornada", +"Meeting" => "Reunión", +"Personal" => "Personal", +"Projects" => "Proyectos", +"Questions" => "Preguntas", "{name}'s Birthday" => "Cumpleaños de {name}", "Contact" => "Contacto", "Add Contact" => "Añadir contacto", +"Import" => "Importar", +"Settings" => "Configuración", "Addressbooks" => "Libretas de direcciones", -"Configure Address Books" => "Configurar libretas de direcciones", -"New Address Book" => "Nueva libreta de direcciones", -"Import from VCF" => "Importar desde VCF", -"CardDav Link" => "Enlace CardDav", -"Download" => "Descargar", -"Edit" => "Editar", -"Delete" => "Borrar", -"Download contact" => "Descargar contacto", -"Delete contact" => "Eliminar contacto", +"Close" => "Cierra.", +"Keyboard shortcuts" => "Atajos de teclado", +"Navigation" => "Navegación", +"Next contact in list" => "Siguiente contacto en la lista", +"Previous contact in list" => "Anterior contacto en la lista", +"Actions" => "Acciones", +"Refresh contacts list" => "Refrescar la lista de contactos", +"Add new contact" => "Añadir un nuevo contacto", +"Add new addressbook" => "Añadir nueva libreta de direcciones", +"Delete current contact" => "Eliminar contacto actual", "Drop photo to upload" => "Suelta una foto para subirla", +"Delete current photo" => "Eliminar fotografÃa actual", +"Edit current photo" => "Editar fotografÃa actual", +"Upload new photo" => "Subir nueva fotografÃa", +"Select photo from ownCloud" => "Seleccionar fotografÃa desde ownCloud", "Format custom, Short name, Full name, Reverse or Reverse with comma" => "Formato personalizado, nombre abreviado, nombre completo, al revés o al revés con coma", "Edit name details" => "Editar los detalles del nombre", +"Organization" => "Organización", +"Delete" => "Borrar", "Nickname" => "Alias", "Enter nickname" => "Introduce un alias", -"Birthday" => "Cumpleaños", +"Web site" => "Sitio Web", +"http://www.somesite.com" => "http://www.unsitio.com", +"Go to web site" => "Ir al sitio Web", "dd-mm-yyyy" => "dd-mm-yyyy", "Groups" => "Grupos", "Separate groups with commas" => "Separa los grupos con comas", @@ -90,28 +138,31 @@ "Delete email address" => "Eliminar dirección de correo electrónico", "Enter phone number" => "Introduce un número de teléfono", "Delete phone number" => "Eliminar número de teléfono", +"Instant Messenger" => "Mensajero instantáneo", "View on map" => "Ver en el mapa", "Edit address details" => "Editar detalles de la dirección", "Add notes here." => "Añade notas aquÃ.", "Add field" => "Añadir campo", -"Profile picture" => "Foto del perfil", "Phone" => "Teléfono", +"Email" => "Correo electrónico", +"Instant Messaging" => "MensajerÃa instantánea", +"Address" => "Dirección", "Note" => "Nota", -"Delete current photo" => "Eliminar fotografÃa actual", -"Edit current photo" => "Editar fotografÃa actual", -"Upload new photo" => "Subir nueva fotografÃa", -"Select photo from ownCloud" => "Seleccionar fotografÃa desde ownCloud", +"Download contact" => "Descargar contacto", +"Delete contact" => "Eliminar contacto", +"The temporary image has been removed from cache." => "La foto temporal se ha borrado del cache.", "Edit address" => "Editar dirección", "Type" => "Tipo", "PO Box" => "Código postal", +"Street and number" => "Calle y número", "Extended" => "Extendido", -"Street" => "Calle", +"Apartment number etc." => "Número del apartamento, etc.", "City" => "Ciudad", "Region" => "Región", +"E.g. state or province" => "Ej: región o provincia", "Zipcode" => "Código postal", +"Postal code" => "Código postal", "Country" => "PaÃs", -"Edit categories" => "Editar categorÃas", -"Add" => "Añadir", "Addressbook" => "Libreta de direcciones", "Hon. prefixes" => "Prefijos honorÃficos", "Miss" => "Srta", @@ -132,26 +183,32 @@ "Esq." => "Don", "Jr." => "Jr.", "Sn." => "Sn.", -"New Addressbook" => "Nueva libreta de direcciones", -"Edit Addressbook" => "Editar libreta de direcciones", -"Displayname" => "Nombre a mostrar", -"Active" => "Activo", -"Save" => "Guardar", -"Submit" => "Aceptar", -"Cancel" => "Cancelar", "Import a contacts file" => "Importar archivo de contactos", "Please choose the addressbook" => "Por favor escoge la agenda", "create a new addressbook" => "crear una nueva agenda", "Name of new addressbook" => "Nombre de la nueva agenda", -"Import" => "Importar", "Importing contacts" => "Importando contactos", +"Contacts imported successfully" => "Contactos importados correctamente", +"Close Dialog" => "Cerrar Diálogo", +"Import Addressbook" => "Importar agenda", "Select address book to import to:" => "Selecciona una agenda para importar a:", +"Drop a VCF file to import contacts." => "Suelta un archivo VCF para importar contactos.", "Select from HD" => "Seleccionar del disco duro", "You have no contacts in your addressbook." => "No hay contactos en tu agenda.", "Add contact" => "Añadir contacto", -"Configure addressbooks" => "Configurar agenda", +"Enter name" => "Introducir nombre", +"Enter description" => "Introducir descripción", "CardDAV syncing addresses" => "Sincronizando direcciones", "more info" => "más información", "Primary address (Kontact et al)" => "Dirección primaria (Kontact et al)", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Share" => "Compartir", +"Download" => "Descargar", +"Edit" => "Editar", +"New Address Book" => "Nueva libreta de direcciones", +"Name" => "Nombre", +"Description" => "Descripción", +"Save" => "Guardar", +"Cancel" => "Cancelar", +"More..." => "Más..." ); diff --git a/apps/contacts/l10n/et_EE.php b/apps/contacts/l10n/et_EE.php index 39a287c9619..e15ea0c10bd 100644 --- a/apps/contacts/l10n/et_EE.php +++ b/apps/contacts/l10n/et_EE.php @@ -1,39 +1,39 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "Viga aadressiraamatu (de)aktiveerimisel.", -"There was an error adding the contact." => "Konktakti lisamisel tekkis viga.", -"Cannot add empty property." => "Tühja omadust ei saa lisada.", -"At least one of the address fields has to be filled out." => "Vähemalt üks aadressiväljadest peab olema täidetud.", -"Trying to add duplicate property: " => "Proovitakse lisada topeltomadust: ", -"Error adding contact property." => "Viga konktakti korralikul lisamisel.", +"id is not set." => "ID on määramata.", +"Cannot update addressbook with an empty name." => "Tühja nimega aadressiraamatut ei saa uuendada.", +"Error updating addressbook." => "Viga aadressiraamatu uuendamisel.", "No ID provided" => "ID-d pole sisestatud", "Error setting checksum." => "Viga kontrollsumma määramisel.", "No categories selected for deletion." => "Kustutamiseks pole valitud ühtegi kategooriat.", "No address books found." => "Ei leitud ühtegi aadressiraamatut.", "No contacts found." => "Ãœhtegi kontakti ei leitud.", +"There was an error adding the contact." => "Konktakti lisamisel tekkis viga.", +"element name is not set." => "elemendi nime pole määratud.", +"Cannot add empty property." => "Tühja omadust ei saa lisada.", +"At least one of the address fields has to be filled out." => "Vähemalt üks aadressiväljadest peab olema täidetud.", +"Trying to add duplicate property: " => "Proovitakse lisada topeltomadust: ", +"Information about vCard is incorrect. Please reload the page." => "Visiitkaardi info pole korrektne. Palun lae leht uuesti.", "Missing ID" => "Puudub ID", "Error parsing VCard for ID: \"" => "Viga VCard-ist ID parsimisel: \"", -"Cannot add addressbook with an empty name." => "Tühja nimega aadressiraamatut ei saa lisada.", -"Error adding addressbook." => "Viga aadressiraamatu lisamisel.", -"Error activating addressbook." => "Viga aadressiraamatu aktiveerimisel.", +"checksum is not set." => "kontrollsummat pole määratud.", +"Information about vCard is incorrect. Please reload the page: " => "vCard info pole korrektne. Palun lae lehekülg uuesti: ", +"Something went FUBAR. " => "Midagi läks tõsiselt metsa.", "No contact ID was submitted." => "Kontakti ID-d pole sisestatud.", "Error reading contact photo." => "Viga kontakti foto lugemisel.", "Error saving temporary file." => "Viga ajutise faili salvestamisel.", "The loading photo is not valid." => "Laetav pilt pole korrektne pildifail.", -"id is not set." => "ID on määramata.", -"Information about vCard is incorrect. Please reload the page." => "Visiitkaardi info pole korrektne. Palun lae leht uuesti.", -"Error deleting contact property." => "Viga konktaki korralikul kustutamisel.", "Contact ID is missing." => "Kontakti ID puudub.", -"Missing contact id." => "Puuduv kontakti ID.", "No photo path was submitted." => "Foto asukohta pole määratud.", "File doesn't exist:" => "Faili pole olemas:", "Error loading image." => "Viga pildi laadimisel.", -"element name is not set." => "elemendi nime pole määratud.", -"checksum is not set." => "kontrollsummat pole määratud.", -"Information about vCard is incorrect. Please reload the page: " => "vCard info pole korrektne. Palun lae lehekülg uuesti: ", -"Something went FUBAR. " => "Midagi läks tõsiselt metsa.", -"Error updating contact property." => "Viga konktaki korralikul uuendamisel.", -"Cannot update addressbook with an empty name." => "Tühja nimega aadressiraamatut ei saa uuendada.", -"Error updating addressbook." => "Viga aadressiraamatu uuendamisel.", +"Error getting contact object." => "Viga kontakti objekti hankimisel.", +"Error getting PHOTO property." => "Viga PHOTO omaduse hankimisel.", +"Error saving contact." => "Viga kontakti salvestamisel.", +"Error resizing image" => "Viga pildi suuruse muutmisel", +"Error cropping image" => "Viga pildi lõikamisel", +"Error creating temporary image" => "Viga ajutise pildi loomisel", +"Error finding image: " => "Viga pildi leidmisel: ", "Error uploading contacts to storage." => "Viga kontaktide üleslaadimisel kettale.", "There is no error, the file uploaded with success" => "Ãœhtegi tõrget polnud, fail on üles laetud", "The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Ãœleslaetud fail ületab php.ini failis määratud upload_max_filesize suuruse", @@ -41,15 +41,23 @@ "The uploaded file was only partially uploaded" => "Fail laeti üles ainult osaliselt", "No file was uploaded" => "Ãœhtegi faili ei laetud üles", "Missing a temporary folder" => "Ajutiste failide kaust puudub", +"Couldn't save temporary image: " => "Ajutise pildi salvestamine ebaõnnestus: ", +"Couldn't load temporary image: " => "Ajutise pildi laadimine ebaõnnestus: ", +"No file was uploaded. Unknown error" => "Ãœhtegi faili ei laetud üles. Tundmatu viga", "Contacts" => "Kontaktid", -"Drop a VCF file to import contacts." => "Lohista siia VCF-fail, millest kontakte importida.", -"Addressbook not found." => "Aadressiraamatut ei leitud", +"Sorry, this functionality has not been implemented yet" => "Vabandust, aga see funktsioon pole veel valmis", +"Not implemented" => "Pole implementeeritud", +"Couldn't get a valid address." => "Kehtiva aadressi hankimine ebaõnnestus", +"Error" => "Viga", +"This property has to be non-empty." => "See omadus ei tohi olla tühi.", +"Edit name" => "Muuda nime", +"No files selected for upload." => "Ãœleslaadimiseks pole faile valitud.", +"Select type" => "Vali tüüp", +"Result: " => "Tulemus: ", +" imported, " => " imporditud, ", +" failed." => " ebaõnnestus.", "This is not your addressbook." => "See pole sinu aadressiraamat.", "Contact could not be found." => "Kontakti ei leitud.", -"Address" => "Aadress", -"Telephone" => "Telefon", -"Email" => "E-post", -"Organization" => "Organisatsioon", "Work" => "Töö", "Home" => "Kodu", "Mobile" => "Mobiil", @@ -60,25 +68,24 @@ "Video" => "Video", "Pager" => "Piipar", "Internet" => "Internet", +"Birthday" => "Sünnipäev", "{name}'s Birthday" => "{name} sünnipäev", "Contact" => "Kontakt", "Add Contact" => "Lisa kontakt", +"Import" => "Impordi", "Addressbooks" => "Aadressiraamatud", -"Configure Address Books" => "Seadista aadressiraamatut", -"New Address Book" => "Uus aadressiraamat", -"Import from VCF" => "Impordi VCF-ist", -"CardDav Link" => "CardDav link", -"Download" => "Lae alla", -"Edit" => "Muuda", -"Delete" => "Kustuta", -"Download contact" => "Lae kontakt alla", -"Delete contact" => "Kustuta kontakt", +"Close" => "Sule", "Drop photo to upload" => "Lohista üleslaetav foto siia", +"Delete current photo" => "Kustuta praegune foto", +"Edit current photo" => "Muuda praegust pilti", +"Upload new photo" => "Lae üles uus foto", +"Select photo from ownCloud" => "Vali foto ownCloudist", "Format custom, Short name, Full name, Reverse or Reverse with comma" => "Kohandatud vorming, Lühike nimi, Täielik nimi, vastupidine või vastupidine komadega", "Edit name details" => "Muuda nime üksikasju", +"Organization" => "Organisatsioon", +"Delete" => "Kustuta", "Nickname" => "Hüüdnimi", "Enter nickname" => "Sisesta hüüdnimi", -"Birthday" => "Sünnipäev", "dd-mm-yyyy" => "dd.mm.yyyy", "Groups" => "Grupid", "Separate groups with commas" => "Eralda grupid komadega", @@ -94,24 +101,21 @@ "Edit address details" => "Muuda aaressi infot", "Add notes here." => "Lisa märkmed siia.", "Add field" => "Lisa väli", -"Profile picture" => "Profiili pilt", "Phone" => "Telefon", +"Email" => "E-post", +"Address" => "Aadress", "Note" => "Märkus", -"Delete current photo" => "Kustuta praegune foto", -"Edit current photo" => "Muuda praegust pilti", -"Upload new photo" => "Lae üles uus foto", -"Select photo from ownCloud" => "Vali foto ownCloudist", +"Download contact" => "Lae kontakt alla", +"Delete contact" => "Kustuta kontakt", +"The temporary image has been removed from cache." => "Ajutine pilt on puhvrist eemaldatud.", "Edit address" => "Muuda aadressi", "Type" => "Tüüp", "PO Box" => "Postkontori postkast", "Extended" => "Laiendatud", -"Street" => "Tänav", "City" => "Linn", "Region" => "Piirkond", "Zipcode" => "Postiindeks", "Country" => "Riik", -"Edit categories" => "Muuda kategooriat", -"Add" => "Lisa", "Addressbook" => "Aadressiraamat", "Hon. prefixes" => "Eesliited", "Miss" => "Preili", @@ -132,26 +136,26 @@ "Esq." => "Esq.", "Jr." => "Jr.", "Sn." => "Senior.", -"New Addressbook" => "Uus aadressiraamat", -"Edit Addressbook" => "Muuda aadressiraamatut", -"Displayname" => "Näidatav nimi", -"Active" => "Aktiivne", -"Save" => "Salvesta", -"Submit" => "Saada", -"Cancel" => "Loobu", "Import a contacts file" => "Impordi kontaktifail", "Please choose the addressbook" => "Palun vali aadressiraamat", "create a new addressbook" => "loo uus aadressiraamat", "Name of new addressbook" => "Uue aadressiraamatu nimi", -"Import" => "Impordi", "Importing contacts" => "Kontaktide importimine", +"Contacts imported successfully" => "Kontaktid on imporditud", +"Close Dialog" => "Sulge dialoog", +"Import Addressbook" => "Impordi aadressiraamat", "Select address book to import to:" => "Vali aadressiraamat, millesse importida:", +"Drop a VCF file to import contacts." => "Lohista siia VCF-fail, millest kontakte importida.", "Select from HD" => "Vali kõvakettalt", "You have no contacts in your addressbook." => "Sinu aadressiraamatus pole ühtegi kontakti.", "Add contact" => "Lisa kontakt", -"Configure addressbooks" => "Seadista aadressiraamatuid", "CardDAV syncing addresses" => "CardDAV sünkroniseerimise aadressid", "more info" => "lisainfo", "Primary address (Kontact et al)" => "Peamine aadress", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Download" => "Lae alla", +"Edit" => "Muuda", +"New Address Book" => "Uus aadressiraamat", +"Save" => "Salvesta", +"Cancel" => "Loobu" ); diff --git a/apps/contacts/l10n/eu.php b/apps/contacts/l10n/eu.php index 6dfbf0a7b23..b676b45c0fd 100644 --- a/apps/contacts/l10n/eu.php +++ b/apps/contacts/l10n/eu.php @@ -1,44 +1,69 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "Errore bat egon da helbide-liburua (des)gaitzen", -"There was an error adding the contact." => "Errore bat egon da kontaktua gehitzerakoan", -"Cannot add empty property." => "Ezin da propieta hutsa gehitu.", -"At least one of the address fields has to be filled out." => "Behintzat helbide eremuetako bat bete behar da.", -"Error adding contact property." => "Errorea kontaktu propietatea gehitzean.", +"id is not set." => "IDa ez da ezarri.", +"Cannot update addressbook with an empty name." => "Ezin da helbide liburua eguneratu izen huts batekin.", +"Error updating addressbook." => "Errore bat egon da helbide liburua eguneratzen.", "No ID provided" => "Ez da IDrik eman", +"Error setting checksum." => "Errorea kontrol-batura ezartzean.", "No categories selected for deletion." => "Ez dira ezabatzeko kategoriak hautatu.", "No address books found." => "Ez da helbide libururik aurkitu.", "No contacts found." => "Ez da kontakturik aurkitu.", +"There was an error adding the contact." => "Errore bat egon da kontaktua gehitzerakoan", +"element name is not set." => "elementuaren izena ez da ezarri.", +"Could not parse contact: " => "Ezin izan da kontaktua analizatu:", +"Cannot add empty property." => "Ezin da propieta hutsa gehitu.", +"At least one of the address fields has to be filled out." => "Behintzat helbide eremuetako bat bete behar da.", +"Trying to add duplicate property: " => "Propietate bikoiztuta gehitzen saiatzen ari zara:", +"Information about vCard is incorrect. Please reload the page." => "vCard-aren inguruko informazioa okerra da. Mesedez birkargatu orrialdea.", "Missing ID" => "ID falta da", -"Error adding addressbook." => "Errore bat egon da helbide liburua gehitzean.", -"Error activating addressbook." => "Errore bat egon da helbide-liburua aktibatzen.", +"Error parsing VCard for ID: \"" => "Errorea VCard analizatzean hurrengo IDrako: \"", +"checksum is not set." => "Kontrol-batura ezarri gabe dago.", +"Information about vCard is incorrect. Please reload the page: " => "vCard honen informazioa ez da zuzena.Mezedez birkargatu orria:", +"No contact ID was submitted." => "Ez da kontaktuaren IDrik eman.", "Error reading contact photo." => "Errore bat izan da kontaktuaren argazkia igotzerakoan.", +"Error saving temporary file." => "Errore bat izan da aldi bateko fitxategia gordetzerakoan.", "The loading photo is not valid." => "Kargatzen ari den argazkia ez da egokia.", -"id is not set." => "IDa ez da ezarri.", -"Information about vCard is incorrect. Please reload the page." => "vCard-aren inguruko informazioa okerra da. Mesedez birkargatu orrialdea.", -"Error deleting contact property." => "Errorea kontaktu propietatea ezabatzean.", "Contact ID is missing." => "Kontaktuaren IDa falta da.", -"Missing contact id." => "Kontaktuaren IDa falta da.", +"No photo path was submitted." => "Ez da argazkiaren bide-izenik eman.", "File doesn't exist:" => "Fitxategia ez da existitzen:", "Error loading image." => "Errore bat izan da irudia kargatzearkoan.", -"element name is not set." => "elementuaren izena ez da ezarri.", -"Error updating contact property." => "Errorea kontaktu propietatea eguneratzean.", -"Cannot update addressbook with an empty name." => "Ezin da helbide liburua eguneratu izen huts batekin.", -"Error updating addressbook." => "Errore bat egon da helbide liburua eguneratzen.", +"Error getting contact object." => "Errore bat izan da kontaktu objetua lortzean.", +"Error getting PHOTO property." => "Errore bat izan da PHOTO propietatea lortzean.", +"Error saving contact." => "Errore bat izan da kontaktua gordetzean.", +"Error resizing image" => "Errore bat izan da irudiaren tamaina aldatzean", +"Error cropping image" => "Errore bat izan da irudia mozten", +"Error creating temporary image" => "Errore bat izan da aldi bateko irudia sortzen", +"Error finding image: " => "Ezin izan da irudia aurkitu:", "Error uploading contacts to storage." => "Errore bat egon da kontaktuak biltegira igotzerakoan.", "There is no error, the file uploaded with success" => "Ez da errorerik egon, fitxategia ongi igo da", +"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Igotako fitxategia php.ini fitxategiko upload_max_filesize direktiba baino handiagoa da", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Igotako fitxategia HTML formularioan zehaztutako MAX_FILE_SIZE direktiba baino handidagoa da.", "The uploaded file was only partially uploaded" => "Igotako fitxategiaren zati bat bakarrik igo da", "No file was uploaded" => "Ez da fitxategirik igo", +"Missing a temporary folder" => "Aldi bateko karpeta falta da", +"Couldn't save temporary image: " => "Ezin izan da aldi bateko irudia gorde:", +"Couldn't load temporary image: " => "Ezin izan da aldi bateko irudia kargatu:", +"No file was uploaded. Unknown error" => "Ez da fitxategirik igo. Errore ezezaguna", "Contacts" => "Kontaktuak", -"Drop a VCF file to import contacts." => "Askatu VCF fitxategia kontaktuak inportatzeko.", -"Addressbook not found." => "Helbide liburua ez da aurkitu", +"Sorry, this functionality has not been implemented yet" => "Barkatu, aukera hau ez da oriandik inplementatu", +"Not implemented" => "Inplementatu gabe", +"Couldn't get a valid address." => "Ezin izan da eposta baliagarri bat hartu.", +"Error" => "Errorea", +"This property has to be non-empty." => "Propietate hau ezin da hutsik egon.", +"Couldn't serialize elements." => "Ezin izan dira elementuak serializatu.", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "'deleteProperty' argumenturik gabe deitu da. Mezedez abisatu bugs.owncloud.org-en", +"Edit name" => "Editatu izena", +"No files selected for upload." => "Ez duzu igotzeko fitxategirik hautatu.", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "Igo nahi duzun fitxategia zerbitzariak onartzen duen tamaina baino handiagoa da.", +"Select type" => "Hautatu mota", +"Result: " => "Emaitza:", +" imported, " => " inportatua, ", +" failed." => "huts egin du.", "This is not your addressbook." => "Hau ez da zure helbide liburua.", "Contact could not be found." => "Ezin izan da kontaktua aurkitu.", -"Address" => "Helbidea", -"Telephone" => "Telefonoa", -"Email" => "Eposta", -"Organization" => "Erakundea", "Work" => "Lana", "Home" => "Etxea", +"Other" => "Bestelakoa", "Mobile" => "Mugikorra", "Text" => "Testua", "Voice" => "Ahotsa", @@ -47,24 +72,46 @@ "Video" => "Bideoa", "Pager" => "Bilagailua", "Internet" => "Internet", +"Birthday" => "Jaioteguna", +"Call" => "Deia", +"Clients" => "Bezeroak", +"Holidays" => "Oporrak", +"Ideas" => "Ideiak", +"Journey" => "Bidaia", +"Meeting" => "Bilera", +"Personal" => "Pertsonala", +"Projects" => "Proiektuak", +"Questions" => "Galderak", "{name}'s Birthday" => "{name}ren jaioteguna", "Contact" => "Kontaktua", "Add Contact" => "Gehitu kontaktua", +"Import" => "Inportatu", +"Settings" => "Ezarpenak", "Addressbooks" => "Helbide Liburuak", -"Configure Address Books" => "Konfiguratu Helbide Liburuak", -"New Address Book" => "Helbide-liburu berria", -"Import from VCF" => "VCFtik inportatu", -"CardDav Link" => "CardDav lotura", -"Download" => "Deskargatu", -"Edit" => "Editatu", -"Delete" => "Ezabatu", -"Download contact" => "Deskargatu kontaktua", -"Delete contact" => "Ezabatu kontaktua", +"Close" => "Itxi", +"Keyboard shortcuts" => "Teklatuaren lasterbideak", +"Navigation" => "Nabigazioa", +"Next contact in list" => "Hurrengoa kontaktua zerrendan", +"Previous contact in list" => "Aurreko kontaktua zerrendan", +"Expand/collapse current addressbook" => "Zabaldu/tolestu uneko helbide-liburua", +"Actions" => "Ekintzak", +"Refresh contacts list" => "Gaurkotu kontaktuen zerrenda", +"Add new contact" => "Gehitu kontaktu berria", +"Add new addressbook" => "Gehitu helbide-liburu berria", +"Delete current contact" => "Ezabatu uneko kontaktuak", "Drop photo to upload" => "Askatu argazkia igotzeko", +"Delete current photo" => "Ezabatu oraingo argazkia", +"Edit current photo" => "Editatu oraingo argazkia", +"Upload new photo" => "Igo argazki berria", +"Select photo from ownCloud" => "Hautatu argazki bat ownCloudetik", "Edit name details" => "Editatu izenaren zehaztasunak", +"Organization" => "Erakundea", +"Delete" => "Ezabatu", "Nickname" => "Ezizena", "Enter nickname" => "Sartu ezizena", -"Birthday" => "Jaioteguna", +"Web site" => "Web orria", +"http://www.somesite.com" => "http://www.webgunea.com", +"Go to web site" => "Web orrira joan", "dd-mm-yyyy" => "yyyy-mm-dd", "Groups" => "Taldeak", "Separate groups with commas" => "Banatu taldeak komekin", @@ -80,45 +127,49 @@ "Edit address details" => "Editatu helbidearen zehaztasunak", "Add notes here." => "Gehitu oharrak hemen.", "Add field" => "Gehitu eremua", -"Profile picture" => "Profilaren irudia", "Phone" => "Telefonoa", +"Email" => "Eposta", +"Address" => "Helbidea", "Note" => "Oharra", -"Delete current photo" => "Ezabatu oraingo argazkia", -"Edit current photo" => "Editatu oraingo argazkia", -"Upload new photo" => "Igo argazki berria", -"Select photo from ownCloud" => "Hautatu argazki bat ownCloudetik", +"Download contact" => "Deskargatu kontaktua", +"Delete contact" => "Ezabatu kontaktua", +"The temporary image has been removed from cache." => "Aldi bateko irudia cachetik ezabatu da.", "Edit address" => "Editatu helbidea", "Type" => "Mota", "PO Box" => "Posta kutxa", +"Street address" => "Kalearen helbidea", +"Street and number" => "Kalea eta zenbakia", "Extended" => "Hedatua", -"Street" => "Kalea", +"Apartment number etc." => "Etxe zenbakia eab.", "City" => "Hiria", "Region" => "Eskualdea", "Zipcode" => "Posta kodea", +"Postal code" => "Posta kodea", "Country" => "Herrialdea", -"Edit categories" => "Editatu kategoriak", -"Add" => "Gehitu", "Addressbook" => "Helbide-liburua", -"New Addressbook" => "Helbide-liburu berria", -"Edit Addressbook" => "Editatu helbide-liburua", -"Displayname" => "Bistaratzeko izena", -"Active" => "Aktibo", -"Save" => "Gorde", -"Submit" => "Bidali", -"Cancel" => "Ezeztatu", "Import a contacts file" => "Inporatu kontaktuen fitxategia", "Please choose the addressbook" => "Mesedez, aukeratu helbide liburua", "create a new addressbook" => "sortu helbide liburu berria", "Name of new addressbook" => "Helbide liburuaren izena", -"Import" => "Inportatu", "Importing contacts" => "Kontaktuak inportatzen", +"Contacts imported successfully" => "Kontaktuak ongi inportatu dira", +"Close Dialog" => "Dialogoa itxi", +"Import Addressbook" => "Inporatu helbide liburua", "Select address book to import to:" => "Hautau helburuko helbide liburua:", +"Drop a VCF file to import contacts." => "Askatu VCF fitxategia kontaktuak inportatzeko.", "Select from HD" => "Hautatu disko gogorretik", "You have no contacts in your addressbook." => "Ez duzu kontakturik zure helbide liburuan.", "Add contact" => "Gehitu kontaktua", -"Configure addressbooks" => "Konfiguratu helbide liburuak", +"Select Address Books" => "Hautatu helbide-liburuak", +"Enter name" => "Sartu izena", +"Enter description" => "Sartu deskribapena", "CardDAV syncing addresses" => "CardDAV sinkronizazio helbideak", "more info" => "informazio gehiago", "Primary address (Kontact et al)" => "Helbide nagusia", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Download" => "Deskargatu", +"Edit" => "Editatu", +"New Address Book" => "Helbide-liburu berria", +"Save" => "Gorde", +"Cancel" => "Ezeztatu" ); diff --git a/apps/contacts/l10n/fa.php b/apps/contacts/l10n/fa.php index 9278975c407..9ee6ee54661 100644 --- a/apps/contacts/l10n/fa.php +++ b/apps/contacts/l10n/fa.php @@ -1,39 +1,39 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "خطا در (غیر) Ùعال سازی کتابچه نشانه ها", -"There was an error adding the contact." => "یک خطا در اÙزودن اطلاعات شخص مورد نظر", -"Cannot add empty property." => "نمیتوان یک خاصیت خالی ایجاد کرد", -"At least one of the address fields has to be filled out." => "At least one of the address fields has to be filled out. ", -"Trying to add duplicate property: " => "امتØان کردن برای وارد کردن مشخصات تکراری", -"Error adding contact property." => "خطا درهنگام اÙزودن ویژگی", +"id is not set." => "شناسه تعیین نشده", +"Cannot update addressbook with an empty name." => "نمی توانید کتابچه نشانی ها را با یک نام خالی بروزرسانی کنید", +"Error updating addressbook." => "خطا در هنگام بروزرسانی کتابچه نشانی ها", "No ID provided" => "هیچ شناسه ای ارائه نشده", "Error setting checksum." => "خطا در تنظیم checksum", "No categories selected for deletion." => "هیچ گروهی برای Øذ٠شدن در نظر گرÙته نشده", "No address books found." => "هیچ کتابچه نشانی پیدا نشد", "No contacts found." => "هیچ شخصی پیدا نشد", +"There was an error adding the contact." => "یک خطا در اÙزودن اطلاعات شخص مورد نظر", +"element name is not set." => "نام اصلی تنظیم نشده است", +"Cannot add empty property." => "نمیتوان یک خاصیت خالی ایجاد کرد", +"At least one of the address fields has to be filled out." => "At least one of the address fields has to be filled out. ", +"Trying to add duplicate property: " => "امتØان کردن برای وارد کردن مشخصات تکراری", +"Information about vCard is incorrect. Please reload the page." => "اطلاعات درمورد vCard شما اشتباه است لطÙا صÙØÙ‡ را دوباره بار گذاری کنید", "Missing ID" => "نشانی Ú¯Ù… شده", "Error parsing VCard for ID: \"" => "خطا در تجزیه کارت ویزا برای شناسه:", -"Cannot add addressbook with an empty name." => "نمیتوانید یک نام خالی را به کتابچه نشانی ها اÙزود", -"Error adding addressbook." => "خطا درهنگام اÙزودن کتابچه نشانی ها", -"Error activating addressbook." => "خطا درهنگام Ùعال سازیکتابچه نشانی ها", +"checksum is not set." => "checksum تنظیم شده نیست", +"Information about vCard is incorrect. Please reload the page: " => "اطلاعات کارت ویزا شما غلط است لطÙا صÙØÙ‡ را دوباره بارگزاری کنید", +"Something went FUBAR. " => "چند چیز به FUBAR رÙتند", "No contact ID was submitted." => "هیچ اطلاعاتی راجع به شناسه ارسال نشده", "Error reading contact photo." => "خطا در خواندن اطلاعات تصویر", "Error saving temporary file." => "خطا در ذخیره پرونده موقت", "The loading photo is not valid." => "بارگزاری تصویر امکان پذیر نیست", -"id is not set." => "شناسه تعیین نشده", -"Information about vCard is incorrect. Please reload the page." => "اطلاعات درمورد vCard شما اشتباه است لطÙا صÙØÙ‡ را دوباره بار گذاری کنید", -"Error deleting contact property." => "خطا در هنگام پاک کرد ویژگی", "Contact ID is missing." => "اطلاعات شناسه Ú¯Ù… شده", -"Missing contact id." => "شما اطلاعات شناسه را Ùراموش کرده اید", "No photo path was submitted." => "هیچ نشانی از تصویرارسال نشده", "File doesn't exist:" => "پرونده وجود ندارد", "Error loading image." => "خطا در بارگزاری تصویر", -"element name is not set." => "نام اصلی تنظیم نشده است", -"checksum is not set." => "checksum تنظیم شده نیست", -"Information about vCard is incorrect. Please reload the page: " => "اطلاعات کارت ویزا شما غلط است لطÙا صÙØÙ‡ را دوباره بارگزاری کنید", -"Something went FUBAR. " => "چند چیز به FUBAR رÙتند", -"Error updating contact property." => "خطا در هنگام بروزرسانی اطلاعات شخص مورد نظر", -"Cannot update addressbook with an empty name." => "نمی توانید کتابچه نشانی ها را با یک نام خالی بروزرسانی کنید", -"Error updating addressbook." => "خطا در هنگام بروزرسانی کتابچه نشانی ها", +"Error getting contact object." => "خطا در گرÙتن اطلاعات شخص", +"Error getting PHOTO property." => "خطا در درباÙت تصویر ویژگی شخصی", +"Error saving contact." => "خطا در ذخیره سازی اطلاعات", +"Error resizing image" => "خطا در تغییر دادن اندازه تصویر", +"Error cropping image" => "خطا در برداشت تصویر", +"Error creating temporary image" => "خطا در ساخت تصویر temporary", +"Error finding image: " => "خطا در پیدا کردن تصویر:", "Error uploading contacts to storage." => "خطا در هنگام بارگذاری Ùˆ ذخیره سازی", "There is no error, the file uploaded with success" => "هیچ خطایی نیست بارگذاری پرونده موÙقیت آمیز بود", "The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Øجم آپلود از طریق Php.ini تعیین Ù…ÛŒ شود", @@ -41,15 +41,26 @@ "The uploaded file was only partially uploaded" => "پرونده بارگذاری شده Ùقط تاØدودی بارگذاری شده", "No file was uploaded" => "هیچ پروندهای بارگذاری نشده", "Missing a temporary folder" => "یک پوشه موقت Ú¯Ù… شده", +"Couldn't save temporary image: " => "قابلیت ذخیره تصویر موقت وجود ندارد:", +"Couldn't load temporary image: " => "قابلیت بارگذاری تصویر موقت وجود ندارد:", +"No file was uploaded. Unknown error" => "هیچ Ùایلی آپلود نشد.خطای ناشناس", "Contacts" => "اشخاص", -"Drop a VCF file to import contacts." => "یک پرونده VCF را به اینجا بکشید تا اشخاص اÙزوده شوند", -"Addressbook not found." => "کتابچه نشانی ها یاÙت نشد", +"Sorry, this functionality has not been implemented yet" => "با عرض پوزش،این قابلیت هنوز اجرا نشده است", +"Not implemented" => "انجام نشد", +"Couldn't get a valid address." => "Couldn't get a valid address.", +"Error" => "خطا", +"This property has to be non-empty." => "این ویژگی باید به صورت غیر تهی عمل کند", +"Couldn't serialize elements." => "قابلیت مرتب سازی عناصر وجود ندارد", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "پاک کردن ویژگی بدون استدلال انجام شده.لطÙا این مورد را گزارش دهید:bugs.owncloud.org", +"Edit name" => "نام تغییر", +"No files selected for upload." => "هیچ Ùایلی برای آپلود انتخاب نشده است", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "Øجم Ùایل بسیار بیشتر از Øجم تنظیم شده در تنظیمات سرور است", +"Select type" => "نوع را انتخاب کنید", +"Result: " => "نتیجه:", +" imported, " => "وارد شد،", +" failed." => "ناموÙÙ‚", "This is not your addressbook." => "این کتابچه ÛŒ نشانه های شما نیست", "Contact could not be found." => "اتصال ویا تماسی یاÙت نشد", -"Address" => "نشانی", -"Telephone" => "تلÙÙ†", -"Email" => "نشانی پست الکترنیک", -"Organization" => "نهاد(ارگان)", "Work" => "کار", "Home" => "خانه", "Mobile" => "موبایل", @@ -60,25 +71,24 @@ "Video" => "رسانه تصویری", "Pager" => "صÙØÙ‡", "Internet" => "اینترنت", +"Birthday" => "روزتولد", "{name}'s Birthday" => "روز تولد {name} است", "Contact" => "اشخاص", "Add Contact" => "اÙزودن اطلاعات شخص مورد نظر", +"Import" => "وارد کردن", "Addressbooks" => "کتابچه ÛŒ نشانی ها", -"Configure Address Books" => "پیکر بندی کتابچه نشانی ها", -"New Address Book" => "کتابچه نشانه های جدید", -"Import from VCF" => "وارد شده از VCF", -"CardDav Link" => "CardDav Link", -"Download" => "بارگیری", -"Edit" => "ویرایش", -"Delete" => "پاک کردن", -"Download contact" => "دانلود مشخصات اشخاص", -"Delete contact" => "پاک کردن اطلاعات شخص مورد نظر", +"Close" => "بستن", "Drop photo to upload" => "تصویر را به اینجا بکشید تا بار گذازی شود", +"Delete current photo" => "پاک کردن تصویر کنونی", +"Edit current photo" => "ویرایش تصویر کنونی", +"Upload new photo" => "بار گذاری یک تصویر جدید", +"Select photo from ownCloud" => "انتخاب یک تصویر از ابر های شما", "Format custom, Short name, Full name, Reverse or Reverse with comma" => "Format custom, Short name, Full name, Reverse or Reverse with comma", "Edit name details" => "ویرایش نام جزئیات", +"Organization" => "نهاد(ارگان)", +"Delete" => "پاک کردن", "Nickname" => "نام مستعار", "Enter nickname" => "یک نام مستعار وارد کنید", -"Birthday" => "روزتولد", "dd-mm-yyyy" => "dd-mm-yyyy", "Groups" => "گروه ها", "Separate groups with commas" => "جدا کردن گروه ها به وسیله درنگ نما", @@ -94,24 +104,21 @@ "Edit address details" => "ویرایش جزئیات نشانی ها", "Add notes here." => "اینجا یادداشت ها را بیاÙزایید", "Add field" => "اضاÙÙ‡ کردن Ùیلد", -"Profile picture" => "تصویر پروÙایل", "Phone" => "شماره تلÙÙ†", +"Email" => "نشانی پست الکترنیک", +"Address" => "نشانی", "Note" => "یادداشت", -"Delete current photo" => "پاک کردن تصویر کنونی", -"Edit current photo" => "ویرایش تصویر کنونی", -"Upload new photo" => "بار گذاری یک تصویر جدید", -"Select photo from ownCloud" => "انتخاب یک تصویر از ابر های شما", +"Download contact" => "دانلود مشخصات اشخاص", +"Delete contact" => "پاک کردن اطلاعات شخص مورد نظر", +"The temporary image has been removed from cache." => "تصویر موقت از Ú©Ø´ پاک شد.", "Edit address" => "ویرایش نشانی", "Type" => "نوع", "PO Box" => "صندوق پستی", "Extended" => "تمدید شده", -"Street" => "خیابان", "City" => "شهر", "Region" => "ناØیه", "Zipcode" => "کد پستی", "Country" => "کشور", -"Edit categories" => "ویرایش گروه", -"Add" => "اÙزودن", "Addressbook" => "کتابچه ÛŒ نشانی ها", "Hon. prefixes" => "پیشوند های Ù…Øترمانه", "Miss" => "خانم", @@ -132,26 +139,26 @@ "Esq." => "Esq.", "Jr." => "Jr.", "Sn." => "Sn.", -"New Addressbook" => "کتابچه نشانی جدید", -"Edit Addressbook" => "ویرایش کتابچه نشانی", -"Displayname" => "نام برای نمایش", -"Active" => "Ùعال", -"Save" => "ذخیره سازی", -"Submit" => "ارسال", -"Cancel" => "انصراÙ", "Import a contacts file" => "وارد کردن پرونده Øاوی اطلاعات", "Please choose the addressbook" => "لطÙا یک کتابچه نشانی انتخاب کنید", "create a new addressbook" => "یک کتابچه نشانی بسازید", "Name of new addressbook" => "نام کتابچه نشانی جدید", -"Import" => "وارد کردن", "Importing contacts" => "وارد کردن اشخاص", +"Contacts imported successfully" => "اشخاص با موÙقیت اÙزوده شدند", +"Close Dialog" => "بستن دیالوگ", +"Import Addressbook" => "وارد کردن کتابچه نشانی", "Select address book to import to:" => "یک کتابچه نشانی انتخاب کنید تا وارد شود", +"Drop a VCF file to import contacts." => "یک پرونده VCF را به اینجا بکشید تا اشخاص اÙزوده شوند", "Select from HD" => "انتخاب از دیسک سخت", "You have no contacts in your addressbook." => "شماهیچ شخصی در کتابچه نشانی خود ندارید", "Add contact" => "اÙزودن اطلاعات شخص مورد نظر", -"Configure addressbooks" => "پیکربندی کتابچه ÛŒ نشانی ها", "CardDAV syncing addresses" => "CardDAV syncing addresses ", "more info" => "اطلاعات بیشتر", "Primary address (Kontact et al)" => "نشانی اولیه", -"iOS/OS X" => "iOS/OS X " +"iOS/OS X" => "iOS/OS X ", +"Download" => "بارگیری", +"Edit" => "ویرایش", +"New Address Book" => "کتابچه نشانه های جدید", +"Save" => "ذخیره سازی", +"Cancel" => "انصراÙ" ); diff --git a/apps/contacts/l10n/fi_FI.php b/apps/contacts/l10n/fi_FI.php index 2e3e91611bb..23cafe44dcf 100644 --- a/apps/contacts/l10n/fi_FI.php +++ b/apps/contacts/l10n/fi_FI.php @@ -1,36 +1,60 @@ <?php $TRANSLATIONS = array( -"There was an error adding the contact." => "Virhe yhteystietoa lisättäessä.", -"Cannot add empty property." => "Tyhjää ominaisuutta ei voi lisätä.", -"At least one of the address fields has to be filled out." => "Vähintään yksi osoitekenttä tulee täyttää.", -"Error adding contact property." => "Virhe lisättäessä ominaisuutta yhteystietoon.", +"Error updating addressbook." => "Virhe päivitettäessä osoitekirjaa.", +"Error setting checksum." => "Virhe asettaessa tarkistussummaa.", "No categories selected for deletion." => "Luokkia ei ole valittu poistettavaksi.", "No address books found." => "Osoitekirjoja ei löytynyt.", "No contacts found." => "Yhteystietoja ei löytynyt.", +"There was an error adding the contact." => "Virhe yhteystietoa lisättäessä.", +"Cannot add empty property." => "Tyhjää ominaisuutta ei voi lisätä.", +"At least one of the address fields has to be filled out." => "Vähintään yksi osoitekenttä tulee täyttää.", +"Information about vCard is incorrect. Please reload the page." => "vCardin tiedot eivät kelpaa. Lataa sivu uudelleen.", "Error parsing VCard for ID: \"" => "Virhe jäsennettäessä vCardia tunnisteelle: \"", -"Cannot add addressbook with an empty name." => "Ilman nimeä olevaa osoitekirjaa ei voi lisätä.", -"Error adding addressbook." => "Virhe lisättäessä osoitekirjaa.", -"Error activating addressbook." => "Virhe aktivoitaessa osoitekirjaa.", "Error saving temporary file." => "Virhe tallennettaessa tilapäistiedostoa.", -"Error deleting contact property." => "Virhe poistettaessa yhteystiedon ominaisuutta.", +"No photo path was submitted." => "Kuvan polkua ei annettu.", "File doesn't exist:" => "Tiedostoa ei ole olemassa:", "Error loading image." => "Virhe kuvaa ladatessa.", -"Error updating contact property." => "Virhe päivitettäessä yhteystiedon ominaisuutta.", -"Error updating addressbook." => "Virhe päivitettäessä osoitekirjaa.", +"Error saving contact." => "Virhe yhteystietoa tallennettaessa.", +"Error resizing image" => "Virhe asettaessa kuvaa uuteen kokoon", +"Error cropping image" => "Virhe rajatessa kuvaa", +"Error creating temporary image" => "Virhe luotaessa väliaikaista kuvaa", "There is no error, the file uploaded with success" => "Ei virhettä, tiedosto lähetettiin onnistuneesti", "The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Lähetetyn tiedoston koko ylittää upload_max_filesize-asetuksen arvon php.ini-tiedostossa", "The uploaded file was only partially uploaded" => "Lähetetty tiedosto lähetettiin vain osittain", "No file was uploaded" => "Tiedostoa ei lähetetty", "Missing a temporary folder" => "Tilapäiskansio puuttuu", +"Couldn't save temporary image: " => "Väliaikaiskuvan tallennus epäonnistui:", +"Couldn't load temporary image: " => "Väliaikaiskuvan lataus epäonnistui:", +"No file was uploaded. Unknown error" => "Tiedostoa ei lähetetty. Tuntematon virhe", "Contacts" => "Yhteystiedot", -"Addressbook not found." => "Osoitekirjaa ei löytynyt.", +"Error" => "Virhe", +"Edit name" => "Muokkaa nimeä", +"No files selected for upload." => "Tiedostoja ei ole valittu lähetettäväksi.", +"Error loading profile picture." => "Virhe profiilikuvaa ladatessa.", +"Select type" => "Valitse tyyppi", +"Some contacts are marked for deletion, but not deleted yet. Please wait for them to be deleted." => "Jotkin yhteystiedot on merkitty poistettaviksi, mutta niitä ei ole vielä poistettu. Odota hetki, että kyseiset yhteystiedot poistetaan.", +"Do you want to merge these address books?" => "Haluatko yhdistää nämä osoitekirjat?", +"Result: " => "Tulos: ", +" imported, " => " tuotu, ", +" failed." => " epäonnistui.", +"Displayname cannot be empty." => "Näyttönimi ei voi olla tyhjä.", +"Addressbook not found: " => "Osoitekirjaa ei löytynyt:", "This is not your addressbook." => "Tämä ei ole osoitekirjasi.", "Contact could not be found." => "Yhteystietoa ei löytynyt.", -"Address" => "Osoite", -"Telephone" => "Puhelin", -"Email" => "Sähköposti", -"Organization" => "Organisaatio", +"Jabber" => "Jabber", +"AIM" => "AIM", +"MSN" => "MSN", +"Twitter" => "Twitter", +"GoogleTalk" => "Google Talk", +"Facebook" => "Facebook", +"XMPP" => "XMPP", +"ICQ" => "ICQ", +"Yahoo" => "Yahoo", +"Skype" => "Skype", +"QQ" => "QQ", +"GaduGadu" => "GaduGadu", "Work" => "Työ", "Home" => "Koti", +"Other" => "Muu", "Mobile" => "Mobiili", "Text" => "Teksti", "Voice" => "Ääni", @@ -39,71 +63,97 @@ "Video" => "Video", "Pager" => "Hakulaite", "Internet" => "Internet", +"Birthday" => "Syntymäpäivä", +"Business" => "Työ", +"Questions" => "Kysymykset", "{name}'s Birthday" => "Henkilön {name} syntymäpäivä", "Contact" => "Yhteystieto", "Add Contact" => "Lisää yhteystieto", +"Import" => "Tuo", +"Settings" => "Asetukset", "Addressbooks" => "Osoitekirjat", -"Configure Address Books" => "Muokkaa osoitekirjoja", -"New Address Book" => "Uusi osoitekirja", -"Import from VCF" => "Tuo VCF-tiedostosta", -"CardDav Link" => "CardDav-linkki", -"Download" => "Lataa", -"Edit" => "Muokkaa", +"Close" => "Sulje", +"Keyboard shortcuts" => "Pikanäppäimet", +"Next contact in list" => "Seuraava yhteystieto luettelossa", +"Previous contact in list" => "Edellinen yhteystieto luettelossa", +"Next addressbook" => "Seuraava osoitekirja", +"Previous addressbook" => "Edellinen osoitekirja", +"Actions" => "Toiminnot", +"Refresh contacts list" => "Päivitä yhteystietoluettelo", +"Add new contact" => "Lisää uusi yhteystieto", +"Add new addressbook" => "Lisää uusi osoitekirja", +"Delete current contact" => "Poista nykyinen yhteystieto", +"Delete current photo" => "Poista nykyinen valokuva", +"Edit current photo" => "Muokkaa nykyistä valokuvaa", +"Upload new photo" => "Lähetä uusi valokuva", +"Select photo from ownCloud" => "Valitse valokuva ownCloudista", +"Edit name details" => "Muokkaa nimitietoja", +"Organization" => "Organisaatio", "Delete" => "Poista", -"Download contact" => "Lataa yhteystieto", -"Delete contact" => "Poista yhteystieto", "Nickname" => "Kutsumanimi", "Enter nickname" => "Anna kutsumanimi", -"Birthday" => "Syntymäpäivä", +"Web site" => "Verkkosivu", +"http://www.somesite.com" => "http://www.somesite.com", +"Go to web site" => "Siirry verkkosivulle", "Groups" => "Ryhmät", "Separate groups with commas" => "Erota ryhmät pilkuilla", "Edit groups" => "Muokkaa ryhmiä", +"Preferred" => "Ensisijainen", "Please specify a valid email address." => "Anna kelvollinen sähköpostiosoite.", "Enter email address" => "Anna sähköpostiosoite", +"Mail to address" => "Lähetä sähköpostia", "Delete email address" => "Poista sähköpostiosoite", "Enter phone number" => "Anna puhelinnumero", "Delete phone number" => "Poista puhelinnumero", +"Instant Messenger" => "Pikaviestin", "View on map" => "Näytä kartalla", +"Edit address details" => "Muokkaa osoitetietoja", "Add notes here." => "Lisää huomiot tähän.", "Add field" => "Lisää kenttä", -"Profile picture" => "Profiilikuva", "Phone" => "Puhelin", +"Email" => "Sähköposti", +"Address" => "Osoite", "Note" => "Huomio", -"Delete current photo" => "Poista nykyinen valokuva", -"Edit current photo" => "Muokkaa nykyistä valokuvaa", -"Upload new photo" => "Lähetä uusi valokuva", -"Select photo from ownCloud" => "Valitse valokuva ownCloudista", +"Download contact" => "Lataa yhteystieto", +"Delete contact" => "Poista yhteystieto", +"The temporary image has been removed from cache." => "Väliaikainen kuva on poistettu välimuistista.", "Edit address" => "Muokkaa osoitetta", "Type" => "Tyyppi", "PO Box" => "Postilokero", +"Street address" => "Katuosoite", +"Street and number" => "Katu ja numero", "Extended" => "Laajennettu", -"Street" => "Katuosoite", +"Apartment number etc." => "Asunnon numero jne.", "City" => "Paikkakunta", "Region" => "Alue", "Zipcode" => "Postinumero", +"Postal code" => "Postinumero", "Country" => "Maa", -"Edit categories" => "Muokkaa luokkia", -"Add" => "Lisää", "Addressbook" => "Osoitekirja", "Given name" => "Etunimi", "Additional names" => "Lisänimet", "Family name" => "Sukunimi", -"New Addressbook" => "Uusi osoitekirja", -"Edit Addressbook" => "Muokkaa osoitekirjaa", -"Active" => "Aktiivinen", -"Save" => "Tallenna", -"Submit" => "Lähetä", -"Cancel" => "Peru", "Import a contacts file" => "Tuo yhteystiedon sisältävä tiedosto", "Please choose the addressbook" => "Valitse osoitekirja", "create a new addressbook" => "luo uusi osoitekirja", "Name of new addressbook" => "Uuden osoitekirjan nimi", -"Import" => "Tuo", "Importing contacts" => "Tuodaan yhteystietoja", -"Select address book to import to:" => "Valitse osoitekirja, johon yhteystiedot tuodaan:", "You have no contacts in your addressbook." => "Osoitekirjassasi ei ole yhteystietoja.", "Add contact" => "Lisää yhteystieto", -"Configure addressbooks" => "Muokkaa osoitekirjoja", +"Select Address Books" => "Valitse osoitekirjat", +"Enter name" => "Anna nimi", +"Enter description" => "Anna kuvaus", "CardDAV syncing addresses" => "CardDAV-synkronointiosoitteet", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Show CardDav link" => "Näytä CardDav-linkki", +"Show read-only VCF link" => "Näytä vain luku -muodossa oleva VCF-linkki", +"Share" => "Jaa", +"Download" => "Lataa", +"Edit" => "Muokkaa", +"New Address Book" => "Uusi osoitekirja", +"Name" => "Nimi", +"Description" => "Kuvaus", +"Save" => "Tallenna", +"Cancel" => "Peru", +"More..." => "Lisää..." ); diff --git a/apps/contacts/l10n/fr.php b/apps/contacts/l10n/fr.php index 0a2a4e58b9f..87942444356 100644 --- a/apps/contacts/l10n/fr.php +++ b/apps/contacts/l10n/fr.php @@ -1,39 +1,42 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "Des erreurs se sont produites lors de l'activation/désactivation du carnet d'adresses.", -"There was an error adding the contact." => "Une erreur s'est produite lors de l'ajout du contact.", -"Cannot add empty property." => "Impossible d'ajouter un champ vide.", -"At least one of the address fields has to be filled out." => "Au moins un des champs d'adresses doit être complété.", -"Trying to add duplicate property: " => "Ajout d'une propriété en double:", -"Error adding contact property." => "Erreur lors de l'ajout du champ.", +"id is not set." => "L'ID n'est pas défini.", +"Cannot update addressbook with an empty name." => "Impossible de mettre à jour le carnet d'adresses avec un nom vide.", +"Error updating addressbook." => "Erreur lors de la mise à jour du carnet d'adresses.", "No ID provided" => "Aucun ID fourni", "Error setting checksum." => "Erreur lors du paramétrage du hachage.", "No categories selected for deletion." => "Pas de catégories sélectionnées pour la suppression.", "No address books found." => "Pas de carnet d'adresses trouvé.", "No contacts found." => "Aucun contact trouvé.", +"There was an error adding the contact." => "Une erreur s'est produite lors de l'ajout du contact.", +"element name is not set." => "Le champ Nom n'est pas défini.", +"Could not parse contact: " => "Impossible de lire le contact :", +"Cannot add empty property." => "Impossible d'ajouter un champ vide.", +"At least one of the address fields has to be filled out." => "Au moins un des champs d'adresses doit être complété.", +"Trying to add duplicate property: " => "Ajout d'une propriété en double:", +"Missing IM parameter." => "Paramètre de Messagerie Instantanée manquants.", +"Unknown IM: " => "Messagerie Instantanée inconnue", +"Information about vCard is incorrect. Please reload the page." => "Les informations relatives à cette vCard sont incorrectes. Veuillez recharger la page.", "Missing ID" => "ID manquant", "Error parsing VCard for ID: \"" => "Erreur lors de l'analyse du VCard pour l'ID: \"", -"Cannot add addressbook with an empty name." => "Ne peut être ajouté avec un nom vide.", -"Error adding addressbook." => "Erreur lors de l'ajout du carnet d'adresses.", -"Error activating addressbook." => "Erreur lors de l'activation du carnet d'adresses.", +"checksum is not set." => "L'hachage n'est pas défini.", +"Information about vCard is incorrect. Please reload the page: " => "L'informatiion à propos de la vCard est incorrect. Merci de rafraichir la page:", +"Something went FUBAR. " => "Quelque chose est FUBAR.", "No contact ID was submitted." => "Aucun ID de contact envoyé", "Error reading contact photo." => "Erreur de lecture de la photo du contact.", "Error saving temporary file." => "Erreur de sauvegarde du fichier temporaire.", "The loading photo is not valid." => "La photo chargée est invalide.", -"id is not set." => "L'ID n'est pas défini.", -"Information about vCard is incorrect. Please reload the page." => "Les informations relatives à cette vCard sont incorrectes. Veuillez recharger la page.", -"Error deleting contact property." => "Erreur lors de la suppression du champ.", "Contact ID is missing." => "L'ID du contact est manquant.", -"Missing contact id." => "ID contact manquant.", "No photo path was submitted." => "Le chemin de la photo n'a pas été envoyé.", "File doesn't exist:" => "Fichier inexistant:", "Error loading image." => "Erreur lors du chargement de l'image.", -"element name is not set." => "Le champ Nom n'est pas défini.", -"checksum is not set." => "L'hachage n'est pas défini.", -"Information about vCard is incorrect. Please reload the page: " => "L'informatiion à propos de la vCard est incorrect. Merci de rafraichir la page:", -"Something went FUBAR. " => "Quelque chose est FUBAR.", -"Error updating contact property." => "Erreur lors de la mise à jour du champ.", -"Cannot update addressbook with an empty name." => "Impossible de mettre à jour le carnet d'adresses avec un nom vide.", -"Error updating addressbook." => "Erreur lors de la mise à jour du carnet d'adresses.", +"Error getting contact object." => "Erreur lors de l'obtention de l'objet contact", +"Error getting PHOTO property." => "Erreur lors de l'obtention des propriétés de la photo", +"Error saving contact." => "Erreur de sauvegarde du contact", +"Error resizing image" => "Erreur de redimensionnement de l'image", +"Error cropping image" => "Erreur lors du rognage de l'image", +"Error creating temporary image" => "Erreur de création de l'image temporaire", +"Error finding image: " => "Erreur pour trouver l'image :", "Error uploading contacts to storage." => "Erreur lors de l'envoi des contacts vers le stockage.", "There is no error, the file uploaded with success" => "Il n'y a pas d'erreur, le fichier a été envoyé avec succes.", "The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Le fichier envoyé dépasse la directive upload_max_filesize dans php.ini", @@ -41,17 +44,46 @@ "The uploaded file was only partially uploaded" => "Le fichier envoyé n'a été que partiellement envoyé.", "No file was uploaded" => "Pas de fichier envoyé.", "Missing a temporary folder" => "Absence de dossier temporaire.", +"Couldn't save temporary image: " => "Impossible de sauvegarder l'image temporaire :", +"Couldn't load temporary image: " => "Impossible de charger l'image temporaire :", +"No file was uploaded. Unknown error" => "Aucun fichier n'a été chargé. Erreur inconnue", "Contacts" => "Contacts", -"Drop a VCF file to import contacts." => "Glisser un fichier VCF pour importer des contacts.", -"Addressbook not found." => "Carnet d'adresses introuvable.", +"Sorry, this functionality has not been implemented yet" => "Désolé cette fonctionnalité n'a pas encore été implémentée", +"Not implemented" => "Pas encore implémenté", +"Couldn't get a valid address." => "Impossible de trouver une adresse valide.", +"Error" => "Erreur", +"This property has to be non-empty." => "Cette valeur ne doit pas être vide", +"Couldn't serialize elements." => "Impossible de sérialiser les éléments.", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "'deleteProperty' a été appelé sans type d'arguments. Merci de rapporter un bug à bugs.owncloud.org", +"Edit name" => "Éditer le nom", +"No files selected for upload." => "Aucun fichiers choisis pour être chargés", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "Le fichier que vous tentez de charger dépasse la taille maximum de fichier autorisée sur ce serveur.", +"Error loading profile picture." => "Erreur pendant le chargement de la photo de profil.", +"Select type" => "Sélectionner un type", +"Some contacts are marked for deletion, but not deleted yet. Please wait for them to be deleted." => "Certains contacts sont marqués pour être supprimés, mais ne le sont pas encore. Veuillez attendre que l'opération se termine.", +"Do you want to merge these address books?" => "Voulez-vous fusionner ces carnets d'adresses ?", +"Result: " => "Résultat :", +" imported, " => "importé,", +" failed." => "échoué.", +"Displayname cannot be empty." => "Le nom d'affichage ne peut pas être vide.", +"Addressbook not found: " => "Carnet d'adresse introuvable : ", "This is not your addressbook." => "Ce n'est pas votre carnet d'adresses.", "Contact could not be found." => "Ce contact n'a pu être trouvé.", -"Address" => "Adresse", -"Telephone" => "Téléphone", -"Email" => "E-mail", -"Organization" => "Société", +"Jabber" => "Jabber", +"AIM" => "Messagerie Instantanée", +"MSN" => "MSN", +"Twitter" => "Twitter", +"GoogleTalk" => "GoogleTalk", +"Facebook" => "Facebook", +"XMPP" => "XMPP", +"ICQ" => "ICQ", +"Yahoo" => "Yahoo", +"Skype" => "Skype", +"QQ" => "QQ", +"GaduGadu" => "GaduGadu", "Work" => "Travail", "Home" => "Maison", +"Other" => "Autre", "Mobile" => "Mobile", "Text" => "Texte", "Voice" => "Voix", @@ -60,25 +92,52 @@ "Video" => "Vidéo", "Pager" => "Bipeur", "Internet" => "Internet", +"Birthday" => "Anniversaire", +"Business" => "Business", +"Call" => "Appel", +"Clients" => "Clients", +"Deliverer" => "Livreur", +"Holidays" => "Vacances", +"Ideas" => "Idées", +"Journey" => "Trajet", +"Jubilee" => "Jubilé", +"Meeting" => "Rendez-vous", +"Personal" => "Personnel", +"Projects" => "Projets", +"Questions" => "Questions", "{name}'s Birthday" => "Anniversaire de {name}", "Contact" => "Contact", "Add Contact" => "Ajouter un Contact", +"Import" => "Importer", +"Settings" => "Paramètres", "Addressbooks" => "Carnets d'adresses", -"Configure Address Books" => "Paramétrer carnet d'adresses", -"New Address Book" => "Nouveau Carnet d'adresses", -"Import from VCF" => "Importer depuis VCF", -"CardDav Link" => "Lien CardDav", -"Download" => "Télécharger", -"Edit" => "Modifier", -"Delete" => "Supprimer", -"Download contact" => "Télécharger le contact", -"Delete contact" => "Supprimer le contact", +"Close" => "Fermer", +"Keyboard shortcuts" => "Raccourcis clavier", +"Navigation" => "Navigation", +"Next contact in list" => "Contact suivant dans la liste", +"Previous contact in list" => "Contact précédent dans la liste", +"Expand/collapse current addressbook" => "Dé/Replier le carnet d'adresses courant", +"Next addressbook" => "Carnet d'adresses suivant", +"Previous addressbook" => "Carnet d'adresses précédent", +"Actions" => "Actions", +"Refresh contacts list" => "Actualiser la liste des contacts", +"Add new contact" => "Ajouter un nouveau contact", +"Add new addressbook" => "Ajouter un nouveau carnet d'adresses", +"Delete current contact" => "Effacer le contact sélectionné", "Drop photo to upload" => "Glisser une photo pour l'envoi", +"Delete current photo" => "Supprimer la photo actuelle", +"Edit current photo" => "Editer la photo actuelle", +"Upload new photo" => "Envoyer une nouvelle photo", +"Select photo from ownCloud" => "Sélectionner une photo depuis ownCloud", "Format custom, Short name, Full name, Reverse or Reverse with comma" => "Formatage personnalisé, Nom court, Nom complet, Inversé, Inversé avec virgule", "Edit name details" => "Editer les noms", +"Organization" => "Société", +"Delete" => "Supprimer", "Nickname" => "Surnom", "Enter nickname" => "Entrer un surnom", -"Birthday" => "Anniversaire", +"Web site" => "Page web", +"http://www.somesite.com" => "http://www.somesite.com", +"Go to web site" => "Allez à la page web", "dd-mm-yyyy" => "jj-mm-aaaa", "Groups" => "Groupes", "Separate groups with commas" => "Séparer les groupes avec des virgules", @@ -86,31 +145,37 @@ "Preferred" => "Préféré", "Please specify a valid email address." => "Merci d'entrer une adresse e-mail valide.", "Enter email address" => "Entrer une adresse e-mail", +"Mail to address" => "Envoyer à l'adresse", "Delete email address" => "Supprimer l'adresse e-mail", "Enter phone number" => "Entrer un numéro de téléphone", "Delete phone number" => "Supprimer le numéro de téléphone", +"Instant Messenger" => "Instant Messenger", +"Delete IM" => "Supprimer la Messagerie Instantanée", "View on map" => "Voir sur une carte", "Edit address details" => "Editer les adresses", "Add notes here." => "Ajouter des notes ici.", "Add field" => "Ajouter un champ.", -"Profile picture" => "Photo de profil", "Phone" => "Téléphone", +"Email" => "E-mail", +"Instant Messaging" => "Messagerie instantanée", +"Address" => "Adresse", "Note" => "Note", -"Delete current photo" => "Supprimer la photo actuelle", -"Edit current photo" => "Editer la photo actuelle", -"Upload new photo" => "Envoyer une nouvelle photo", -"Select photo from ownCloud" => "Sélectionner une photo depuis ownCloud", +"Download contact" => "Télécharger le contact", +"Delete contact" => "Supprimer le contact", +"The temporary image has been removed from cache." => "L'image temporaire a été supprimée du cache.", "Edit address" => "Editer l'adresse", "Type" => "Type", "PO Box" => "Boîte postale", +"Street address" => "Adresse postale", +"Street and number" => "Rue et numéro", "Extended" => "Étendu", -"Street" => "Rue", +"Apartment number etc." => "Numéro d'appartement, etc.", "City" => "Ville", "Region" => "Région", +"E.g. state or province" => "Ex: état ou province", "Zipcode" => "Code postal", +"Postal code" => "Code postal", "Country" => "Pays", -"Edit categories" => "Editer les catégories", -"Add" => "Ajouter", "Addressbook" => "Carnet d'adresses", "Hon. prefixes" => "Préfixe hon.", "Miss" => "Mlle", @@ -123,27 +188,43 @@ "Additional names" => "Nom supplémentaires", "Family name" => "Nom de famille", "Hon. suffixes" => "Suffixes hon.", +"J.D." => "J.D.", +"M.D." => "Dr.", +"D.O." => "D.O.", +"D.C." => "D.C.", "Ph.D." => "Dr", -"New Addressbook" => "Nouveau carnet d'adresses", -"Edit Addressbook" => "Éditer le carnet d'adresses", -"Displayname" => "Nom", -"Active" => "Carnet actif", -"Save" => "Sauvegarder", -"Submit" => "Envoyer", -"Cancel" => "Annuler", +"Esq." => "Esq.", +"Jr." => "Jr.", +"Sn." => "Sn.", "Import a contacts file" => "Importer un fichier de contacts", "Please choose the addressbook" => "Choisissez le carnet d'adresses SVP", "create a new addressbook" => "Créer un nouveau carnet d'adresses", "Name of new addressbook" => "Nom du nouveau carnet d'adresses", -"Import" => "Importer", "Importing contacts" => "Importation des contacts", +"Contacts imported successfully" => "Contacts importés avec succes", +"Close Dialog" => "Fermer la boite de dialogue", +"Import Addressbook" => "Importer un carnet d'adresses.", "Select address book to import to:" => "Selectionner le carnet d'adresses à importer vers:", +"Drop a VCF file to import contacts." => "Glisser un fichier VCF pour importer des contacts.", "Select from HD" => "Selectionner depuis le disque dur", "You have no contacts in your addressbook." => "Il n'y a pas de contact dans votre carnet d'adresses.", "Add contact" => "Ajouter un contact", -"Configure addressbooks" => "Paramétrer carnet d'adresses", +"Select Address Books" => "Choix du carnet d'adresses", +"Enter name" => "Saisissez le nom", +"Enter description" => "Saisissez une description", "CardDAV syncing addresses" => "Synchronisation des contacts CardDAV", "more info" => "Plus d'infos", "Primary address (Kontact et al)" => "Adresse principale", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Show CardDav link" => "Afficher le lien CardDav", +"Show read-only VCF link" => "Afficher les liens VCF en lecture seule", +"Share" => "Partager", +"Download" => "Télécharger", +"Edit" => "Modifier", +"New Address Book" => "Nouveau Carnet d'adresses", +"Name" => "Nom", +"Description" => "Description", +"Save" => "Sauvegarder", +"Cancel" => "Annuler", +"More..." => "Plus…" ); diff --git a/apps/contacts/l10n/gl.php b/apps/contacts/l10n/gl.php index bc3ec7449c9..f25219b22ce 100644 --- a/apps/contacts/l10n/gl.php +++ b/apps/contacts/l10n/gl.php @@ -1,58 +1,157 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "Produciuse un erro (des)activando a axenda.", +"id is not set." => "non se estableceu o id.", +"Cannot update addressbook with an empty name." => "Non se pode actualizar a libreta de enderezos sen completar o nome.", +"Error updating addressbook." => "Produciuse un erro actualizando a axenda.", +"No ID provided" => "Non se proveeu ID", +"Error setting checksum." => "Erro establecendo a suma de verificación", +"No categories selected for deletion." => "Non se seleccionaron categorÃas para borrado.", +"No address books found." => "Non se atoparon libretas de enderezos.", +"No contacts found." => "Non se atoparon contactos.", "There was an error adding the contact." => "Produciuse un erro engadindo o contacto.", +"element name is not set." => "non se nomeou o elemento.", "Cannot add empty property." => "Non se pode engadir unha propiedade baleira.", "At least one of the address fields has to be filled out." => "Polo menos un dos campos do enderezo ten que ser cuberto.", -"Error adding contact property." => "Produciuse un erro engadindo unha propiedade do contacto.", -"Error adding addressbook." => "Produciuse un erro engadindo a axenda.", -"Error activating addressbook." => "Produciuse un erro activando a axenda.", +"Trying to add duplicate property: " => "Tentando engadir propiedade duplicada: ", "Information about vCard is incorrect. Please reload the page." => "A información sobre a vCard é incorrecta. Por favor volva cargar a páxina.", -"Error deleting contact property." => "Produciuse un erro borrando a propiedade do contacto.", -"Error updating contact property." => "Produciuse un erro actualizando a propiedade do contacto.", -"Error updating addressbook." => "Produciuse un erro actualizando a axenda.", +"Missing ID" => "ID perdido", +"Error parsing VCard for ID: \"" => "Erro procesando a VCard para o ID: \"", +"checksum is not set." => "non se estableceu a suma de verificación.", +"Information about vCard is incorrect. Please reload the page: " => "A información sobre a vCard é incorrecta. Por favor, recargue a páxina: ", +"No contact ID was submitted." => "Non se enviou ningún ID de contacto.", +"Error reading contact photo." => "Erro lendo a fotografÃa do contacto.", +"Error saving temporary file." => "Erro gardando o ficheiro temporal.", +"The loading photo is not valid." => "A fotografÃa cargada non é válida.", +"Contact ID is missing." => "Falta o ID do contacto.", +"No photo path was submitted." => "Non se enviou a ruta a unha foto.", +"File doesn't exist:" => "O ficheiro non existe:", +"Error loading image." => "Erro cargando imaxe.", +"Error getting contact object." => "Erro obtendo o obxeto contacto.", +"Error getting PHOTO property." => "Erro obtendo a propiedade PHOTO.", +"Error saving contact." => "Erro gardando o contacto.", +"Error resizing image" => "Erro cambiando o tamaño da imaxe", +"Error cropping image" => "Erro recortando a imaxe", +"Error creating temporary image" => "Erro creando a imaxe temporal", +"Error finding image: " => "Erro buscando a imaxe: ", +"Error uploading contacts to storage." => "Erro subindo os contactos ao almacén.", +"There is no error, the file uploaded with success" => "Non houbo erros, o ficheiro subeuse con éxito", +"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "O ficheiro subido supera a directiva upload_max_filesize no php.ini", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "O ficheiro subido supera a directiva MAX_FILE_SIZE especificada no formulario HTML", +"The uploaded file was only partially uploaded" => "O ficheiro so foi parcialmente subido", +"No file was uploaded" => "Non se subeu ningún ficheiro", +"Missing a temporary folder" => "Falta o cartafol temporal", +"Couldn't save temporary image: " => "Non se puido gardar a imaxe temporal: ", +"Couldn't load temporary image: " => "Non se puido cargar a imaxe temporal: ", +"No file was uploaded. Unknown error" => "Non se subeu ningún ficheiro. Erro descoñecido.", "Contacts" => "Contactos", +"Sorry, this functionality has not been implemented yet" => "SentÃmolo, esta función aÃnda non foi implementada.", +"Not implemented" => "Non implementada.", +"Couldn't get a valid address." => "Non se puido obter un enderezo de correo válido.", +"Error" => "Erro", +"This property has to be non-empty." => "Esta propiedade non pode quedar baldeira.", +"Couldn't serialize elements." => "Non se puido serializar os elementos.", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "'deleteProperty' chamado sen argumento. Por favor, informe en bugs.owncloud.org", +"Edit name" => "Editar nome", +"No files selected for upload." => "Sen ficheiros escollidos para subir.", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "O ficheiro que tenta subir supera o tamaño máximo permitido neste servidor.", +"Select type" => "Seleccione tipo", +"Result: " => "Resultado: ", +" imported, " => " importado, ", +" failed." => " fallou.", "This is not your addressbook." => "Esta non é a súa axenda.", "Contact could not be found." => "Non se atopou o contacto.", -"Address" => "Enderezo", -"Telephone" => "Teléfono", -"Email" => "Correo electrónico", -"Organization" => "Organización", "Work" => "Traballo", "Home" => "Casa", "Mobile" => "Móbil", "Text" => "Texto", "Voice" => "Voz", +"Message" => "Mensaxe", "Fax" => "Fax", "Video" => "VÃdeo", "Pager" => "Paxinador", +"Internet" => "Internet", +"Birthday" => "Aniversario", +"{name}'s Birthday" => "Cumpleanos de {name}", "Contact" => "Contacto", "Add Contact" => "Engadir contacto", +"Import" => "Importar", "Addressbooks" => "Axendas", -"New Address Book" => "Nova axenda", -"CardDav Link" => "Ligazón CardDav", -"Download" => "Descargar", -"Edit" => "Editar", +"Close" => "Pechar", +"Drop photo to upload" => "Solte a foto a subir", +"Delete current photo" => "Borrar foto actual", +"Edit current photo" => "Editar a foto actual", +"Upload new photo" => "Subir unha nova foto", +"Select photo from ownCloud" => "Escoller foto desde ownCloud", +"Format custom, Short name, Full name, Reverse or Reverse with comma" => "Formato personalizado, Nome corto, Nome completo, Inverso ou Inverso con coma", +"Edit name details" => "Editar detalles do nome", +"Organization" => "Organización", "Delete" => "Eliminar", -"Download contact" => "Descargar contacto", -"Delete contact" => "Borrar contacto", -"Birthday" => "Aniversario", +"Nickname" => "Apodo", +"Enter nickname" => "Introuza apodo", +"dd-mm-yyyy" => "dd-mm-yyyy", +"Groups" => "Grupos", +"Separate groups with commas" => "Separe grupos con comas", +"Edit groups" => "Editar grupos", "Preferred" => "Preferido", +"Please specify a valid email address." => "Por favor indique un enderezo de correo electrónico válido.", +"Enter email address" => "Introduza enderezo de correo electrónico", +"Mail to address" => "Correo ao enderezo", +"Delete email address" => "Borrar enderezo de correo electrónico", +"Enter phone number" => "Introducir número de teléfono", +"Delete phone number" => "Borrar número de teléfono", +"View on map" => "Ver no mapa", +"Edit address details" => "Editar detalles do enderezo", +"Add notes here." => "Engadir aquà as notas.", +"Add field" => "Engadir campo", "Phone" => "Teléfono", +"Email" => "Correo electrónico", +"Address" => "Enderezo", +"Note" => "Nota", +"Download contact" => "Descargar contacto", +"Delete contact" => "Borrar contacto", +"The temporary image has been removed from cache." => "A imaxe temporal foi eliminada da caché.", +"Edit address" => "Editar enderezo", "Type" => "Escribir", "PO Box" => "Apartado de correos", "Extended" => "Ampliado", -"Street" => "Rúa", "City" => "Cidade", "Region" => "AutonomÃa", "Zipcode" => "Código postal", "Country" => "PaÃs", -"Add" => "Engadir", "Addressbook" => "Axenda", -"New Addressbook" => "Nova axenda", -"Edit Addressbook" => "Editar axenda", -"Displayname" => "Nome a mostrar", -"Active" => "Activo", +"Hon. prefixes" => "Prefixos honorÃficos", +"Miss" => "Srta", +"Ms" => "Sra/Srta", +"Mr" => "Sr", +"Sir" => "Sir", +"Mrs" => "Sra", +"Dr" => "Dr", +"Given name" => "Apodo", +"Additional names" => "Nomes adicionais", +"Family name" => "Nome familiar", +"Hon. suffixes" => "Sufixos honorarios", +"J.D." => "J.D.", +"M.D." => "M.D.", +"D.O." => "D.O.", +"D.C." => "D.C.", +"Ph.D." => "Ph.D.", +"Esq." => "Esq.", +"Jr." => "Jr.", +"Sn." => "Sn.", +"Import a contacts file" => "Importar un ficheiro de contactos", +"Please choose the addressbook" => "Por favor escolla unha libreta de enderezos", +"create a new addressbook" => "crear unha nova libreta de enderezos", +"Name of new addressbook" => "Nome da nova libreta de enderezos", +"Importing contacts" => "Importando contactos", +"You have no contacts in your addressbook." => "Non ten contactos na súa libreta de enderezos.", +"Add contact" => "Engadir contacto", +"CardDAV syncing addresses" => "Enderezos CardDAV a sincronizar", +"more info" => "máis información", +"Primary address (Kontact et al)" => "Enderezo primario (Kontact et al)", +"iOS/OS X" => "iOS/OS X", +"Download" => "Descargar", +"Edit" => "Editar", +"New Address Book" => "Nova axenda", "Save" => "Gardar", -"Submit" => "Enviar", "Cancel" => "Cancelar" ); diff --git a/apps/contacts/l10n/he.php b/apps/contacts/l10n/he.php index 6e40ad71e1d..b224403ad22 100644 --- a/apps/contacts/l10n/he.php +++ b/apps/contacts/l10n/he.php @@ -1,65 +1,135 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "שגי××” בהפעלה ×ו ×‘× ×˜×¨×•×œ ×¤× ×§×¡ הכתובות.", -"There was an error adding the contact." => "×ירעה שגי××” בעת הוספת ×יש הקשר.", -"Cannot add empty property." => "×œ× × ×™×ª×Ÿ להוסיף מ×פיין ריק.", -"At least one of the address fields has to be filled out." => "יש ×œ×ž×œ× ×œ×¤×—×•×ª ×חד משדות הכתובת.", -"Trying to add duplicate property: " => "× ×™×¡×™×•×Ÿ להוספת מ×פיין כפול: ", -"Error adding contact property." => "שגי××” בהוספת מ×פיין ל×יש הקשר.", +"id is not set." => "מספר מזהה ×œ× × ×§×‘×¢.", +"Cannot update addressbook with an empty name." => "××™ ×פשר לעדכן ספר כתובות ×œ×œ× ×©×", +"Error updating addressbook." => "שגי××” בעדכון ×¤× ×§×¡ הכתובות.", "No ID provided" => "×œ× ×¦×•×™×Ÿ מזהה", "Error setting checksum." => "שגי××” בהגדרת × ×ª×•× ×™ הביקורת.", "No categories selected for deletion." => "×œ× × ×‘×—×•×¨ קטגוריות למחיקה.", "No address books found." => "×œ× × ×ž×¦×ו ×¤× ×§×¡×™ כתובות.", "No contacts found." => "×œ× × ×ž×¦×ו ×× ×©×™ קשר.", -"Missing ID" => "מזהה חסר", -"Error adding addressbook." => "שגי××” בהוספת ×¤× ×§×¡ הכתובות.", -"Error activating addressbook." => "שגי××” בהפעלת ×¤× ×§×¡ הכתובות.", +"There was an error adding the contact." => "×ירעה שגי××” בעת הוספת ×יש הקשר.", +"element name is not set." => "×©× ×”××œ×ž× ×˜ ×œ× × ×§×‘×¢.", +"Cannot add empty property." => "×œ× × ×™×ª×Ÿ להוסיף מ×פיין ריק.", +"At least one of the address fields has to be filled out." => "יש ×œ×ž×œ× ×œ×¤×—×•×ª ×חד משדות הכתובת.", +"Trying to add duplicate property: " => "× ×™×¡×™×•×Ÿ להוספת מ×פיין כפול: ", "Information about vCard is incorrect. Please reload the page." => "המידע ×ודות vCard ××™× ×• × ×›×•×Ÿ. × × ×œ×˜×¢×•×Ÿ מחדש ×ת הדף.", -"Error deleting contact property." => "שגי××” במחיקת מ×פיין של ×יש הקשר.", -"Error updating contact property." => "שגי××” בעדכון המ×פיין של ×יש הקשר.", -"Error updating addressbook." => "שגי××” בעדכון ×¤× ×§×¡ הכתובות.", +"Missing ID" => "מזהה חסר", +"Error parsing VCard for ID: \"" => "שגי××” ×‘×¤×¢× ×•×— ×” VCard עבור מספר המזהה: \"", +"checksum is not set." => "×¡×™×›×•× ×‘×™×§×•×¨×ª ×œ× × ×§×‘×¢.", +"Information about vCard is incorrect. Please reload the page: " => "המידע עבור ×” vCard ××™× ×• × ×›×•×Ÿ. ×× × ×˜×¢×Ÿ ×ת העמוד: ", +"Something went FUBAR. " => "משהו ×œ× ×”×ª× ×”×œ כצפוי.", +"No contact ID was submitted." => "מספר מזהה של ×ישר הקשר ×œ× × ×©×œ×—.", +"Error reading contact photo." => "שגי××” בקרי×ת ×ª×ž×•× ×ª ×יש הקשר.", +"Error saving temporary file." => "שגי××” בשמירת קובץ ×–×ž× ×™.", +"The loading photo is not valid." => "×”×ª×ž×•× ×” ×”× ×˜×¢× ×ª ××™× ×” ×ª×§× ×™×ª.", +"Contact ID is missing." => "מספר מזהה של ×ישר הקשר חסר.", +"No photo path was submitted." => "כתובת ×”×ª×ž×•× ×” ×œ× × ×©×œ×—×”", +"File doesn't exist:" => "קובץ ×œ× ×§×™×™×:", +"Error loading image." => "שגי××” ×‘×˜×¢×™× ×ª ×”×ª×ž×•× ×”.", +"Error getting contact object." => "שגי××” בקבלת ×ובי×קט ×יש הקשר", +"Error getting PHOTO property." => "שגי××” בקבלת מידע של ×ª×ž×•× ×”", +"Error saving contact." => "שגי××” בשמירת ×יש הקשר", +"Error resizing image" => "שגי××” ×‘×©×™× ×•×™ גודל ×”×ª×ž×•× ×”", +"Error uploading contacts to storage." => "התרשה שגי××” בהעל×ת ×× ×©×™ הקשר ל×כסון.", +"There is no error, the file uploaded with success" => "×œ× ×”×ª×¨×—×©×” שגי××”, הקובץ הועלה בהצלחה", +"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "גודל הקובץ שהועלה גדול מהערך upload_max_filesize שמוגדר בקובץ php.ini", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "הקובץ שהועלה גדוך מהערך MAX_FILE_SIZE שהוגדר בתופס HTML", +"The uploaded file was only partially uploaded" => "הקובץ הועלה ב×ופן חלקי בלבד", +"No file was uploaded" => "×©×•× ×§×•×‘×¥ ×œ× ×”×•×¢×œ×”", +"Missing a temporary folder" => "תקיה ×–×ž× ×™×ª חסרה", "Contacts" => "×× ×©×™ קשר", "This is not your addressbook." => "זהו ××™× ×• ספר הכתובות שלך", "Contact could not be found." => "×œ× × ×™×ª×Ÿ ל×תר ×יש קשר", -"Address" => "כתובת", -"Telephone" => "טלפון", -"Email" => "דו×ר ××œ×§×˜×¨×•× ×™", -"Organization" => "×רגון", "Work" => "עבודה", "Home" => "בית", "Mobile" => "× ×™×™×“", "Text" => "טקסט", "Voice" => "קולי", +"Message" => "הודעה", "Fax" => "פקס", "Video" => "ויד×ו", "Pager" => "×–×™×ž×•× ×™×ª", +"Internet" => "××™× ×˜×¨× ×˜", +"Birthday" => "×™×•× ×”×•×œ×“×ª", +"{name}'s Birthday" => "×™×•× ×”×”×•×œ×“×ª של {name}", "Contact" => "×יש קשר", "Add Contact" => "הוספת ×יש קשר", +"Import" => "יב×", "Addressbooks" => "×¤× ×§×¡×™ כתובות", -"New Address Book" => "×¤× ×§×¡ כתובות חדש", -"CardDav Link" => "קישור ", -"Download" => "הורדה", -"Edit" => "עריכה", +"Drop photo to upload" => "גרור ושחרר ×ª×ž×•× ×” בשביל להעלות", +"Delete current photo" => "מחק ×ª×ž×•× ×” × ×•×›×—×™×ª", +"Edit current photo" => "ערוך ×ª×ž×•× ×” × ×•×›×—×™×ª", +"Upload new photo" => "העלה ×ª×ž×•× ×” חדשה", +"Select photo from ownCloud" => "בחר ×ª×ž×•× ×” מ ownCloud", +"Edit name details" => "ערוך פרטי ש×", +"Organization" => "×רגון", "Delete" => "מחיקה", -"Download contact" => "הורדת ×יש קשר", -"Delete contact" => "מחיקת ×יש קשר", -"Birthday" => "×™×•× ×”×•×œ×“×ª", +"Nickname" => "×›×™× ×•×™", +"Enter nickname" => "×”×›× ×¡ ×›×™× ×•×™", +"dd-mm-yyyy" => "dd-mm-yyyy", +"Groups" => "קבוצות", +"Separate groups with commas" => "הפרד קבוצות ×¢× ×¤×¡×™×§×™×", +"Edit groups" => "ערוך קבוצות", "Preferred" => "מועדף", +"Please specify a valid email address." => "×× × ×”×–×Ÿ כתובת דו×\"ל חוקית", +"Enter email address" => "הזן כתובת דו×\"ל", +"Mail to address" => "כתובת", +"Delete email address" => "מחק כתובת דו×\"ל", +"Enter phone number" => "×”×›× ×¡ מספר טלפון", +"Delete phone number" => "מחק מספר טלפון", +"View on map" => "ר××” במפה", +"Edit address details" => "ערוך פרטי כתובת", +"Add notes here." => "הוסף הערות ×›×ן.", +"Add field" => "הוסף שדה", "Phone" => "טלפון", +"Email" => "דו×ר ××œ×§×˜×¨×•× ×™", +"Address" => "כתובת", +"Note" => "הערה", +"Download contact" => "הורדת ×יש קשר", +"Delete contact" => "מחיקת ×יש קשר", +"Edit address" => "ערוך כתובת", "Type" => "סוג", "PO Box" => "×ª× ×“×•×ר", "Extended" => "מורחב", -"Street" => "רחוב", "City" => "עיר", "Region" => "×זור", "Zipcode" => "מיקוד", "Country" => "×ž×“×™× ×”", -"Add" => "הוספה", "Addressbook" => "×¤× ×§×¡ כתובות", -"New Addressbook" => "×¤× ×§×¡ כתובות חדש", -"Edit Addressbook" => "עריכת ×¤× ×§×¡ הכתובות", -"Displayname" => "×©× ×”×ª×¦×•×’×”", -"Active" => "פעיל", +"Hon. prefixes" => "קידומות ש×", +"Miss" => "גב'", +"Ms" => "גב'", +"Mr" => "מר'", +"Sir" => "×דון", +"Mrs" => "גב'", +"Dr" => "ד\"ר", +"Given name" => "ש×", +"Additional names" => "שמות × ×•×¡×¤×™×", +"Family name" => "×©× ×ž×©×¤×—×”", +"Hon. suffixes" => "סיומות ש×", +"J.D." => "J.D.", +"M.D." => "M.D.", +"D.O." => "D.O.", +"D.C." => "D.C.", +"Ph.D." => "Ph.D.", +"Esq." => "Esq.", +"Jr." => "Jr.", +"Sn." => "Sn.", +"Import a contacts file" => "×™×‘× ×§×•×‘×¥ ×× ×©×™ קשר", +"Please choose the addressbook" => "×× × ×‘×—×¨ ספר כתובות", +"create a new addressbook" => "צור ספר כתובות חדש", +"Name of new addressbook" => "×©× ×¡×¤×¨ כתובות החדש", +"Importing contacts" => "×ž×™×‘× ×× ×©×™ קשר", +"You have no contacts in your addressbook." => "×יך לך ×× ×©×™ קשר בספר הכתובות", +"Add contact" => "הוסף ×יש קשר", +"CardDAV syncing addresses" => "CardDAV ×ž×¡× ×›×¨×Ÿ כתובות", +"more info" => "מידע × ×•×¡×£", +"Primary address (Kontact et al)" => "כתובת ר×שית", +"iOS/OS X" => "iOS/OS X", +"Download" => "הורדה", +"Edit" => "עריכה", +"New Address Book" => "×¤× ×§×¡ כתובות חדש", "Save" => "שמירה", -"Submit" => "ביצוע", "Cancel" => "ביטול" ); diff --git a/apps/contacts/l10n/hr.php b/apps/contacts/l10n/hr.php index d381bf4fa90..769a6ced0fd 100644 --- a/apps/contacts/l10n/hr.php +++ b/apps/contacts/l10n/hr.php @@ -1,38 +1,69 @@ <?php $TRANSLATIONS = array( +"Error (de)activating addressbook." => "PogreÅ¡ka pri (de)aktivaciji adresara.", +"id is not set." => "id nije postavljen.", +"Cannot update addressbook with an empty name." => "Ne mogu ažurirati adresar sa praznim nazivom.", +"Error updating addressbook." => "PogreÅ¡ka pri ažuriranju adresara.", +"No ID provided" => "Nema dodijeljenog ID identifikatora", +"Error setting checksum." => "PogreÅ¡ka pri postavljanju checksuma.", +"No categories selected for deletion." => "Niti jedna kategorija nije odabrana za brisanje.", +"No address books found." => "Nema adresara.", +"No contacts found." => "Nema kontakata.", +"There was an error adding the contact." => "Dogodila se pogreÅ¡ka prilikom dodavanja kontakta.", +"element name is not set." => "naziv elementa nije postavljen.", +"Cannot add empty property." => "Prazno svojstvo se ne može dodati.", +"At least one of the address fields has to be filled out." => "Morate ispuniti barem jedno od adresnih polja.", +"Trying to add duplicate property: " => "PokuÅ¡ali ste dodati duplo svojstvo:", "Information about vCard is incorrect. Please reload the page." => "Informacija o vCard je neispravna. Osvježite stranicu.", +"Missing ID" => "Nedostupan ID identifikator", +"Error parsing VCard for ID: \"" => "PogreÅ¡ka pri raÅ¡Älanjivanju VCard za ID:", +"checksum is not set." => "checksum nije postavljen.", +"Information about vCard is incorrect. Please reload the page: " => "Informacije o VCard su pogreÅ¡ne. Molimo, uÄitajte ponovno stranicu:", +"Something went FUBAR. " => "NeÅ¡to je otiÅ¡lo... krivo...", +"No contact ID was submitted." => "ID kontakta nije podneÅ¡en.", +"Error reading contact photo." => "PogreÅ¡ka pri Äitanju kontakt fotografije.", +"Error saving temporary file." => "PogreÅ¡ka pri spremanju privremene datoteke.", +"The loading photo is not valid." => "Fotografija nije valjana.", +"Contact ID is missing." => "ID kontakta nije dostupan.", +"No photo path was submitted." => "Putanja do fotografije nije podneÅ¡ena.", "File doesn't exist:" => "Datoteka ne postoji:", +"Error loading image." => "PogreÅ¡ka pri uÄitavanju slike.", +"Error uploading contacts to storage." => "PogreÅ¡ka pri slanju kontakata.", +"There is no error, the file uploaded with success" => "Nema pogreÅ¡ke, datoteka je poslana uspjeÅ¡no.", +"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "VeliÄina poslane datoteke prelazi veliÄinu prikazanu u upload_max_filesize direktivi u konfiguracijskoj datoteci php.ini", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Poslana datoteka prelazi veliÄinu prikazanu u MAX_FILE_SIZE direktivi u HTML formi", +"The uploaded file was only partially uploaded" => "Poslana datoteka je parcijalno poslana", +"No file was uploaded" => "Datoteka nije poslana", +"Missing a temporary folder" => "Nedostaje privremeni direktorij", "Contacts" => "Kontakti", "This is not your addressbook." => "Ovo nije vaÅ¡ adresar.", "Contact could not be found." => "Kontakt ne postoji.", -"Address" => "Adresa", -"Telephone" => "Telefon", -"Email" => "E-mail", -"Organization" => "Organizacija", "Work" => "Posao", "Home" => "Kuća", "Mobile" => "Mobitel", "Text" => "Tekst", "Voice" => "Glasovno", +"Message" => "Poruka", "Fax" => "Fax", "Video" => "Video", "Pager" => "Pager", +"Internet" => "Internet", +"Birthday" => "RoÄ‘endan", +"{name}'s Birthday" => "{name} RoÄ‘endan", "Contact" => "Kontakt", "Add Contact" => "Dodaj kontakt", +"Import" => "Uvezi", "Addressbooks" => "Adresari", -"New Address Book" => "Novi adresar", -"CardDav Link" => "CardDav poveznica", -"Download" => "Preuzimanje", -"Edit" => "Uredi", -"Delete" => "ObriÅ¡i", -"Download contact" => "Preuzmi kontakt", -"Delete contact" => "IzbriÅ¡i kontakt", +"Drop photo to upload" => "Dovucite fotografiju za slanje", +"Edit current photo" => "Uredi trenutnu sliku", "Edit name details" => "Uredi detalje imena", +"Organization" => "Organizacija", +"Delete" => "ObriÅ¡i", "Nickname" => "Nadimak", "Enter nickname" => "Unesi nadimank", -"Birthday" => "RoÄ‘endan", "dd-mm-yyyy" => "dd-mm-yyyy", "Groups" => "Grupe", "Edit groups" => "Uredi grupe", +"Preferred" => "Preferirano", "Enter email address" => "Unesi email adresu", "Enter phone number" => "Unesi broj telefona", "View on map" => "Prikaži na karti", @@ -40,24 +71,23 @@ "Add notes here." => "Dodaj biljeÅ¡ke ovdje.", "Add field" => "Dodaj polje", "Phone" => "Telefon", +"Email" => "E-mail", +"Address" => "Adresa", "Note" => "BiljeÅ¡ka", -"Edit current photo" => "Uredi trenutnu sliku", +"Download contact" => "Preuzmi kontakt", +"Delete contact" => "IzbriÅ¡i kontakt", "Edit address" => "Uredi adresu", "Type" => "Tip", "PO Box" => "PoÅ¡tanski Pretinac", "Extended" => "ProÅ¡ireno", -"Street" => "Ulica", "City" => "Grad", "Region" => "Regija", "Zipcode" => "PoÅ¡tanski broj", "Country" => "Država", -"Edit categories" => "Uredi kategorije", -"Add" => "Dodaj", "Addressbook" => "Adresar", -"New Addressbook" => "Novi adresar", -"Edit Addressbook" => "Uredi adresar", +"Download" => "Preuzimanje", +"Edit" => "Uredi", +"New Address Book" => "Novi adresar", "Save" => "Spremi", -"Submit" => "PoÅ¡alji", -"Cancel" => "Prekini", -"Import" => "Uvezi" +"Cancel" => "Prekini" ); diff --git a/apps/contacts/l10n/hu_HU.php b/apps/contacts/l10n/hu_HU.php index 68f7b2607eb..4febe9c29f2 100644 --- a/apps/contacts/l10n/hu_HU.php +++ b/apps/contacts/l10n/hu_HU.php @@ -1,39 +1,39 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "CÃmlista (de)aktiválása sikertelen", -"There was an error adding the contact." => "Hiba a kapcsolat hozzáadásakor", -"Cannot add empty property." => "Nem adható hozzá üres tulajdonság", -"At least one of the address fields has to be filled out." => "Legalább egy cÃmmezÅ‘ kitöltendÅ‘", -"Trying to add duplicate property: " => "KÃsérlet dupla tulajdonság hozzáadására: ", -"Error adding contact property." => "Hiba a kapcsolat-tulajdonság hozzáadásakor", +"id is not set." => "ID nincs beállÃtva", +"Cannot update addressbook with an empty name." => "Ãœres névvel nem frissÃthetÅ‘ a cÃmlista", +"Error updating addressbook." => "Hiba a cÃmlista frissÃtésekor", "No ID provided" => "Nincs ID megadva", "Error setting checksum." => "Hiba az ellenÅ‘rzőösszeg beállÃtásakor", "No categories selected for deletion." => "Nincs kiválasztva törlendÅ‘ kategória", "No address books found." => "Nem található cÃmlista", "No contacts found." => "Nem található kontakt", +"There was an error adding the contact." => "Hiba a kapcsolat hozzáadásakor", +"element name is not set." => "az elem neve nincs beállÃtva", +"Cannot add empty property." => "Nem adható hozzá üres tulajdonság", +"At least one of the address fields has to be filled out." => "Legalább egy cÃmmezÅ‘ kitöltendÅ‘", +"Trying to add duplicate property: " => "KÃsérlet dupla tulajdonság hozzáadására: ", +"Information about vCard is incorrect. Please reload the page." => "A vCardról szóló információ helytelen. Töltsd újra az oldalt.", "Missing ID" => "Hiányzó ID", "Error parsing VCard for ID: \"" => "VCard elemzése sikertelen a következÅ‘ ID-hoz: \"", -"Cannot add addressbook with an empty name." => "Nem adható hozzá névtelen cÃmlista", -"Error adding addressbook." => "Hiba a cÃmlista hozzáadásakor", -"Error activating addressbook." => "CÃmlista aktiválása sikertelen", +"checksum is not set." => "az ellenÅ‘rzőösszeg nincs beállÃtva", +"Information about vCard is incorrect. Please reload the page: " => "Helytelen információ a vCardról. Töltse újra az oldalt: ", +"Something went FUBAR. " => "Valami balul sült el.", "No contact ID was submitted." => "Nincs ID megadva a kontakthoz", "Error reading contact photo." => "A kontakt képének beolvasása sikertelen", "Error saving temporary file." => "Ideiglenes fájl mentése sikertelen", "The loading photo is not valid." => "A kép érvénytelen", -"id is not set." => "ID nincs beállÃtva", -"Information about vCard is incorrect. Please reload the page." => "A vCardról szóló információ helytelen. Töltsd újra az oldalt.", -"Error deleting contact property." => "Hiba a kapcsolat-tulajdonság törlésekor", "Contact ID is missing." => "Hiányzik a kapcsolat ID", -"Missing contact id." => "Hiányzik a kontakt ID", "No photo path was submitted." => "Nincs fénykép-útvonal megadva", "File doesn't exist:" => "A fájl nem létezik:", "Error loading image." => "Kép betöltése sikertelen", -"element name is not set." => "az elem neve nincs beállÃtva", -"checksum is not set." => "az ellenÅ‘rzőösszeg nincs beállÃtva", -"Information about vCard is incorrect. Please reload the page: " => "Helytelen információ a vCardról. Töltse újra az oldalt: ", -"Something went FUBAR. " => "Valami balul sült el.", -"Error updating contact property." => "Hiba a kapcsolat-tulajdonság frissÃtésekor", -"Cannot update addressbook with an empty name." => "Ãœres névvel nem frissÃthetÅ‘ a cÃmlista", -"Error updating addressbook." => "Hiba a cÃmlista frissÃtésekor", +"Error getting contact object." => "A kontakt-objektum feldolgozása sikertelen", +"Error getting PHOTO property." => "A PHOTO-tulajdonság feldolgozása sikertelen", +"Error saving contact." => "A kontakt mentése sikertelen", +"Error resizing image" => "Képméretezés sikertelen", +"Error cropping image" => "Képvágás sikertelen", +"Error creating temporary image" => "Ideiglenes kép létrehozása sikertelen", +"Error finding image: " => "A kép nem található", "Error uploading contacts to storage." => "Hiba a kapcsolatok feltöltésekor", "There is no error, the file uploaded with success" => "Nincs hiba, a fájl sikeresen feltöltÅ‘dött", "The uploaded file exceeds the upload_max_filesize directive in php.ini" => "A feltöltött fájl mérete meghaladja az upload_max_filesize értéket a php.ini-ben", @@ -41,15 +41,26 @@ "The uploaded file was only partially uploaded" => "A fájl csak részlegesen lett feltöltve", "No file was uploaded" => "Nincs feltöltött fájl", "Missing a temporary folder" => "Hiányzik az ideiglenes könyvtár", +"Couldn't save temporary image: " => "Ideiglenes kép létrehozása sikertelen", +"Couldn't load temporary image: " => "Ideiglenes kép betöltése sikertelen", +"No file was uploaded. Unknown error" => "Nem történt feltöltés. Ismeretlen hiba", "Contacts" => "Kapcsolatok", -"Drop a VCF file to import contacts." => "Húzza ide a VCF fájlt a kapcsolatok importálásához", -"Addressbook not found." => "CÃmlista nem található", +"Sorry, this functionality has not been implemented yet" => "Sajnáljuk, ez a funkció még nem támogatott", +"Not implemented" => "Nem támogatott", +"Couldn't get a valid address." => "Érvényes cÃm lekérése sikertelen", +"Error" => "Hiba", +"This property has to be non-empty." => "Ezt a tulajdonságot muszáj kitölteni", +"Couldn't serialize elements." => "Sorbarakás sikertelen", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "A 'deleteProperty' argumentum nélkül lett meghÃvva. Kérjük, jelezze a hibát.", +"Edit name" => "Név szerkesztése", +"No files selected for upload." => "Nincs kiválasztva feltöltendÅ‘ fájl", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "A feltöltendÅ‘ fájl mérete meghaladja a megengedett mértéket", +"Select type" => "TÃpus kiválasztása", +"Result: " => "Eredmény: ", +" imported, " => " beimportálva, ", +" failed." => " sikertelen", "This is not your addressbook." => "Ez nem a te cÃmjegyzéked.", "Contact could not be found." => "Kapcsolat nem található.", -"Address" => "CÃm", -"Telephone" => "Telefonszám", -"Email" => "E-mail", -"Organization" => "Szervezet", "Work" => "Munkahelyi", "Home" => "Otthoni", "Mobile" => "Mobiltelefonszám", @@ -60,25 +71,24 @@ "Video" => "Video", "Pager" => "SzemélyhÃvó", "Internet" => "Internet", +"Birthday" => "Születésnap", "{name}'s Birthday" => "{name} születésnapja", "Contact" => "Kapcsolat", "Add Contact" => "Kapcsolat hozzáadása", +"Import" => "Import", "Addressbooks" => "CÃmlisták", -"Configure Address Books" => "CÃmlisták beállÃtása", -"New Address Book" => "Új cÃmlista", -"Import from VCF" => "Importálás VCF-bÅ‘l", -"CardDav Link" => "CardDav hivatkozás", -"Download" => "Letöltés", -"Edit" => "Szerkesztés", -"Delete" => "Törlés", -"Download contact" => "Kapcsolat letöltése", -"Delete contact" => "Kapcsolat törlése", +"Close" => "Bezár", "Drop photo to upload" => "Húzza ide a feltöltendÅ‘ képet", +"Delete current photo" => "Aktuális kép törlése", +"Edit current photo" => "Aktuális kép szerkesztése", +"Upload new photo" => "Új kép feltöltése", +"Select photo from ownCloud" => "Kép kiválasztása ownCloud-ból", "Format custom, Short name, Full name, Reverse or Reverse with comma" => "Formátum egyedi, Rövid név, Teljes név, Visszafelé vagy Visszafelé vesszÅ‘vel", "Edit name details" => "Név részleteinek szerkesztése", +"Organization" => "Szervezet", +"Delete" => "Törlés", "Nickname" => "Becenév", "Enter nickname" => "Becenév megadása", -"Birthday" => "Születésnap", "dd-mm-yyyy" => "yyyy-mm-dd", "Groups" => "Csoportok", "Separate groups with commas" => "VesszÅ‘vel válassza el a csoportokat", @@ -94,24 +104,21 @@ "Edit address details" => "CÃm részleteinek szerkesztése", "Add notes here." => "Megjegyzések", "Add field" => "MezÅ‘ hozzáadása", -"Profile picture" => "Profilkép", "Phone" => "Telefonszám", +"Email" => "E-mail", +"Address" => "CÃm", "Note" => "Jegyzet", -"Delete current photo" => "Aktuális kép törlése", -"Edit current photo" => "Aktuális kép szerkesztése", -"Upload new photo" => "Új kép feltöltése", -"Select photo from ownCloud" => "Kép kiválasztása ownCloud-ból", +"Download contact" => "Kapcsolat letöltése", +"Delete contact" => "Kapcsolat törlése", +"The temporary image has been removed from cache." => "Az ideiglenes kép el lett távolÃtva a gyorsÃtótárból", "Edit address" => "CÃm szerkesztése", "Type" => "TÃpus", "PO Box" => "Postafiók", "Extended" => "Kiterjesztett", -"Street" => "Utca", "City" => "Város", "Region" => "Megye", "Zipcode" => "IrányÃtószám", "Country" => "Ország", -"Edit categories" => "Kategóriák szerkesztése", -"Add" => "Hozzáad", "Addressbook" => "CÃmlista", "Hon. prefixes" => "ElÅ‘tag", "Miss" => "Miss", @@ -132,26 +139,20 @@ "Esq." => "Esq.", "Jr." => "Ifj.", "Sn." => "Id.", -"New Addressbook" => "Új CÃmlista", -"Edit Addressbook" => "CÃmlista szerkesztése", -"Displayname" => "MegjelenÃtett név", -"Active" => "AktÃv", -"Save" => "Mentés", -"Submit" => "Elküld", -"Cancel" => "Mégsem", "Import a contacts file" => "Kapcsolat-fájl importálása", "Please choose the addressbook" => "Válassza ki a cÃmlistát", "create a new addressbook" => "CÃmlista létrehozása", "Name of new addressbook" => "Új cÃmlista neve", -"Import" => "Import", "Importing contacts" => "Kapcsolatok importálása", -"Select address book to import to:" => "Melyik cÃmlistába történjen az importálás:", -"Select from HD" => "Kiválasztás merevlemezrÅ‘l", "You have no contacts in your addressbook." => "Nincsenek kapcsolatok a cÃmlistában", "Add contact" => "Kapcsolat hozzáadása", -"Configure addressbooks" => "CÃmlisták beállÃtása", "CardDAV syncing addresses" => "CardDAV szinkronizációs cÃmek", "more info" => "további információ", "Primary address (Kontact et al)" => "ElsÅ‘dleges cÃm", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Download" => "Letöltés", +"Edit" => "Szerkesztés", +"New Address Book" => "Új cÃmlista", +"Save" => "Mentés", +"Cancel" => "Mégsem" ); diff --git a/apps/contacts/l10n/ia.php b/apps/contacts/l10n/ia.php index f420b48d842..4d455f7c976 100644 --- a/apps/contacts/l10n/ia.php +++ b/apps/contacts/l10n/ia.php @@ -1,21 +1,14 @@ <?php $TRANSLATIONS = array( -"Cannot add empty property." => "Non pote adder proprietate vacue.", "No address books found." => "Nulle adressario trovate", "No contacts found." => "Nulle contactos trovate.", -"Error adding addressbook." => "Error durante que il addeva le adressario.", -"Error activating addressbook." => "Error in activar adressario", +"Cannot add empty property." => "Non pote adder proprietate vacue.", "Error saving temporary file." => "Error durante le scriptura in le file temporari", "Error loading image." => "Il habeva un error durante le cargamento del imagine.", "No file was uploaded" => "Nulle file esseva incargate.", "Missing a temporary folder" => "Manca un dossier temporari", "Contacts" => "Contactos", -"Addressbook not found." => "Adressario non trovate.", "This is not your addressbook." => "Iste non es tu libro de adresses", "Contact could not be found." => "Contacto non poterea esser legite", -"Address" => "Adresse", -"Telephone" => "Telephono", -"Email" => "E-posta", -"Organization" => "Organisation", "Work" => "Travalio", "Home" => "Domo", "Mobile" => "Mobile", @@ -26,19 +19,19 @@ "Video" => "Video", "Pager" => "Pager", "Internet" => "Internet", +"Birthday" => "Anniversario", "Contact" => "Contacto", "Add Contact" => "Adder contacto", +"Import" => "Importar", "Addressbooks" => "Adressarios", -"New Address Book" => "Nove adressario", -"CardDav Link" => "Ligamine CardDav", -"Download" => "Discargar", -"Edit" => "Modificar", +"Delete current photo" => "Deler photo currente", +"Edit current photo" => "Modificar photo currente", +"Upload new photo" => "Incargar nove photo", +"Select photo from ownCloud" => "Seliger photo ex ownCloud", +"Organization" => "Organisation", "Delete" => "Deler", -"Download contact" => "Discargar contacto", -"Delete contact" => "Deler contacto", "Nickname" => "Pseudonymo", "Enter nickname" => "Inserer pseudonymo", -"Birthday" => "Anniversario", "Groups" => "Gruppos", "Edit groups" => "Modificar gruppos", "Preferred" => "Preferite", @@ -49,24 +42,20 @@ "View on map" => "Vider in un carta", "Add notes here." => "Adder notas hic", "Add field" => "Adder campo", -"Profile picture" => "Imagine de profilo", "Phone" => "Phono", +"Email" => "E-posta", +"Address" => "Adresse", "Note" => "Nota", -"Delete current photo" => "Deler photo currente", -"Edit current photo" => "Modificar photo currente", -"Upload new photo" => "Incargar nove photo", -"Select photo from ownCloud" => "Seliger photo ex ownCloud", +"Download contact" => "Discargar contacto", +"Delete contact" => "Deler contacto", "Edit address" => "Modificar adresses", "Type" => "Typo", "PO Box" => "Cassa postal", "Extended" => "Extendite", -"Street" => "Strata", "City" => "Citate", "Region" => "Region", "Zipcode" => "Codice postal", "Country" => "Pais", -"Edit categories" => "Modificar categorias", -"Add" => "Adder", "Addressbook" => "Adressario", "Hon. prefixes" => "Prefixos honorific", "Miss" => "Senioretta", @@ -77,18 +66,20 @@ "Additional names" => "Nomines additional", "Family name" => "Nomine de familia", "Hon. suffixes" => "Suffixos honorific", -"New Addressbook" => "Nove adressario", -"Edit Addressbook" => "Modificar adressario", -"Active" => "Active", -"Save" => "Salveguardar", -"Submit" => "Submitter", -"Cancel" => "Cancellar", "Import a contacts file" => "Importar un file de contactos", "Please choose the addressbook" => "Per favor selige le adressario", "create a new addressbook" => "Crear un nove adressario", "Name of new addressbook" => "Nomine del nove gruppo:", "Import" => "Importar", +"Contacts imported successfully" => "Contactos importate con successo.", +"Close Dialog" => "Clauder dialogo", +"Import Addressbook" => "Importar adressario.", "Add contact" => "Adder adressario", "more info" => "plus info", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Download" => "Discargar", +"Edit" => "Modificar", +"New Address Book" => "Nove adressario", +"Save" => "Salveguardar", +"Cancel" => "Cancellar" ); diff --git a/apps/contacts/l10n/it.php b/apps/contacts/l10n/it.php index 2a5478e6c4b..8302bfffdca 100644 --- a/apps/contacts/l10n/it.php +++ b/apps/contacts/l10n/it.php @@ -1,39 +1,42 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "Errore nel (dis)attivare la rubrica.", -"There was an error adding the contact." => "Si è verificato un errore nell'aggiunta del contatto.", -"Cannot add empty property." => "Impossibile aggiungere una proprietà vuota.", -"At least one of the address fields has to be filled out." => "Deve essere riempito almeno un indirizzo.", -"Trying to add duplicate property: " => "P", -"Error adding contact property." => "Errore durante l'aggiunta della proprietà del contatto.", +"id is not set." => "ID non impostato.", +"Cannot update addressbook with an empty name." => "Impossibile aggiornare una rubrica senza nome.", +"Error updating addressbook." => "Errore durante l'aggiornamento della rubrica.", "No ID provided" => "Nessun ID fornito", "Error setting checksum." => "Errore di impostazione del codice di controllo.", "No categories selected for deletion." => "Nessuna categoria selezionata per l'eliminazione.", "No address books found." => "Nessuna rubrica trovata.", "No contacts found." => "Nessun contatto trovato.", +"There was an error adding the contact." => "Si è verificato un errore nell'aggiunta del contatto.", +"element name is not set." => "il nome dell'elemento non è impostato.", +"Could not parse contact: " => "Impossibile elaborare il contatto: ", +"Cannot add empty property." => "Impossibile aggiungere una proprietà vuota.", +"At least one of the address fields has to be filled out." => "Deve essere inserito almeno un indirizzo.", +"Trying to add duplicate property: " => "P", +"Missing IM parameter." => "Parametro IM mancante.", +"Unknown IM: " => "IM sconosciuto:", +"Information about vCard is incorrect. Please reload the page." => "Informazioni sulla vCard non corrette. Ricarica la pagina.", "Missing ID" => "ID mancante", "Error parsing VCard for ID: \"" => "Errore in fase di elaborazione del file VCard per l'ID: \"", -"Cannot add addressbook with an empty name." => "Impossibile aggiungere una rubrica senza nome.", -"Error adding addressbook." => "Errore durante l'aggiunta della rubrica.", -"Error activating addressbook." => "Errore durante l'attivazione della rubrica.", +"checksum is not set." => "il codice di controllo non è impostato.", +"Information about vCard is incorrect. Please reload the page: " => "Le informazioni della vCard non sono corrette. Ricarica la pagina: ", +"Something went FUBAR. " => "Qualcosa è andato storto. ", "No contact ID was submitted." => "Nessun ID di contatto inviato.", "Error reading contact photo." => "Errore di lettura della foto del contatto.", "Error saving temporary file." => "Errore di salvataggio del file temporaneo.", "The loading photo is not valid." => "La foto caricata non è valida.", -"id is not set." => "ID non impostato.", -"Information about vCard is incorrect. Please reload the page." => "Informazioni sulla vCard non corrette. Ricarica la pagina.", -"Error deleting contact property." => "Errore durante l'eliminazione della proprietà del contatto.", "Contact ID is missing." => "Manca l'ID del contatto.", -"Missing contact id." => "ID di contatto mancante.", "No photo path was submitted." => "Non è stato inviato alcun percorso a una foto.", "File doesn't exist:" => "Il file non esiste:", "Error loading image." => "Errore di caricamento immagine.", -"element name is not set." => "il nome dell'elemento non è impostato.", -"checksum is not set." => "il codice di controllo non è impostato.", -"Information about vCard is incorrect. Please reload the page: " => "Le informazioni della vCard non sono corrette. Ricarica la pagina: ", -"Something went FUBAR. " => "Qualcosa è andato storto. ", -"Error updating contact property." => "Errore durante l'aggiornamento della proprietà del contatto.", -"Cannot update addressbook with an empty name." => "Impossibile aggiornare una rubrica senza nome.", -"Error updating addressbook." => "Errore durante l'aggiornamento della rubrica.", +"Error getting contact object." => "Errore di recupero dell'oggetto contatto.", +"Error getting PHOTO property." => "Errore di recupero della proprietà FOTO.", +"Error saving contact." => "Errore di salvataggio del contatto.", +"Error resizing image" => "Errore di ridimensionamento dell'immagine", +"Error cropping image" => "Errore di ritaglio dell'immagine", +"Error creating temporary image" => "Errore durante la creazione dell'immagine temporanea", +"Error finding image: " => "Errore durante la ricerca dell'immagine: ", "Error uploading contacts to storage." => "Errore di invio dei contatti in archivio.", "There is no error, the file uploaded with success" => "Non ci sono errori, il file è stato inviato correttamente", "The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Il file inviato supera la direttiva upload_max_filesize nel php.ini", @@ -41,17 +44,46 @@ "The uploaded file was only partially uploaded" => "Il file è stato inviato solo parzialmente", "No file was uploaded" => "Nessun file è stato inviato", "Missing a temporary folder" => "Manca una cartella temporanea", +"Couldn't save temporary image: " => "Impossibile salvare l'immagine temporanea: ", +"Couldn't load temporary image: " => "Impossibile caricare l'immagine temporanea: ", +"No file was uploaded. Unknown error" => "Nessun file è stato inviato. Errore sconosciuto", "Contacts" => "Contatti", -"Drop a VCF file to import contacts." => "Rilascia un file VCF per importare i contatti.", -"Addressbook not found." => "Rubrica non trovata.", +"Sorry, this functionality has not been implemented yet" => "Siamo spiacenti, questa funzionalità non è stata ancora implementata", +"Not implemented" => "Non implementata", +"Couldn't get a valid address." => "Impossibile ottenere un indirizzo valido.", +"Error" => "Errore", +"This property has to be non-empty." => "Questa proprietà non può essere vuota.", +"Couldn't serialize elements." => "Impossibile serializzare gli elementi.", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "'deleteProperty' invocata senza l'argomento di tipo. Segnalalo a bugs.owncloud.org", +"Edit name" => "Modifica il nome", +"No files selected for upload." => "Nessun file selezionato per l'invio", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "Il file che stai cercando di inviare supera la dimensione massima per l'invio dei file su questo server.", +"Error loading profile picture." => "Errore durante il caricamento dell'immagine di profilo.", +"Select type" => "Seleziona il tipo", +"Some contacts are marked for deletion, but not deleted yet. Please wait for them to be deleted." => "Alcuni contatti sono marcati per l'eliminazione, ma non sono stati ancora rimossi. Attendi fino al completamento dell'operazione.", +"Do you want to merge these address books?" => "Vuoi unire queste rubriche?", +"Result: " => "Risultato: ", +" imported, " => " importato, ", +" failed." => " non riuscito.", +"Displayname cannot be empty." => "Il nome visualizzato non può essere vuoto.", +"Addressbook not found: " => "Rubrica non trovata:", "This is not your addressbook." => "Questa non è la tua rubrica.", "Contact could not be found." => "Il contatto non può essere trovato.", -"Address" => "Indirizzo", -"Telephone" => "Telefono", -"Email" => "Email", -"Organization" => "Organizzazione", +"Jabber" => "Jabber", +"AIM" => "AIM", +"MSN" => "MSN", +"Twitter" => "Twitter", +"GoogleTalk" => "GoogleTalk", +"Facebook" => "Facebook", +"XMPP" => "XMPP", +"ICQ" => "ICQ", +"Yahoo" => "Yahoo", +"Skype" => "Skype", +"QQ" => "QQ", +"GaduGadu" => "GaduGadu", "Work" => "Lavoro", "Home" => "Casa", +"Other" => "Altro", "Mobile" => "Cellulare", "Text" => "Testo", "Voice" => "Voce", @@ -60,25 +92,52 @@ "Video" => "Video", "Pager" => "Cercapersone", "Internet" => "Internet", +"Birthday" => "Compleanno", +"Business" => "Lavoro", +"Call" => "Chiama", +"Clients" => "Client", +"Deliverer" => "Corriere", +"Holidays" => "Festività ", +"Ideas" => "Idee", +"Journey" => "Viaggio", +"Jubilee" => "Anniversario", +"Meeting" => "Riunione", +"Personal" => "Personale", +"Projects" => "Progetti", +"Questions" => "Domande", "{name}'s Birthday" => "Data di nascita di {name}", "Contact" => "Contatto", "Add Contact" => "Aggiungi contatto", +"Import" => "Importa", +"Settings" => "Impostazioni", "Addressbooks" => "Rubriche", -"Configure Address Books" => "Configura rubrica", -"New Address Book" => "Nuova rubrica", -"Import from VCF" => "Importa da VCF", -"CardDav Link" => "Link CardDav", -"Download" => "Scarica", -"Edit" => "Modifica", -"Delete" => "Elimina", -"Download contact" => "Scarica contatto", -"Delete contact" => "Elimina contatto", +"Close" => "Chiudi", +"Keyboard shortcuts" => "Scorciatoie da tastiera", +"Navigation" => "Navigazione", +"Next contact in list" => "Contatto successivo in elenco", +"Previous contact in list" => "Contatto precedente in elenco", +"Expand/collapse current addressbook" => "Espandi/Contrai la rubrica corrente", +"Next addressbook" => "Rubrica successiva", +"Previous addressbook" => "Rubrica precedente", +"Actions" => "Azioni", +"Refresh contacts list" => "Aggiorna l'elenco dei contatti", +"Add new contact" => "Aggiungi un nuovo contatto", +"Add new addressbook" => "Aggiungi una nuova rubrica", +"Delete current contact" => "Elimina il contatto corrente", "Drop photo to upload" => "Rilascia una foto da inviare", +"Delete current photo" => "Elimina la foto corrente", +"Edit current photo" => "Modifica la foto corrente", +"Upload new photo" => "Invia una nuova foto", +"Select photo from ownCloud" => "Seleziona la foto da ownCloud", "Format custom, Short name, Full name, Reverse or Reverse with comma" => "Formato personalizzato, nome breve, nome completo, invertito o invertito con virgola", "Edit name details" => "Modifica dettagli del nome", +"Organization" => "Organizzazione", +"Delete" => "Elimina", "Nickname" => "Pseudonimo", "Enter nickname" => "Inserisci pseudonimo", -"Birthday" => "Compleanno", +"Web site" => "Sito web", +"http://www.somesite.com" => "http://www.somesite.com", +"Go to web site" => "Vai al sito web", "dd-mm-yyyy" => "gg-mm-aaaa", "Groups" => "Gruppi", "Separate groups with commas" => "Separa i gruppi con virgole", @@ -90,28 +149,33 @@ "Delete email address" => "Elimina l'indirizzo email", "Enter phone number" => "Inserisci il numero di telefono", "Delete phone number" => "Elimina il numero di telefono", +"Instant Messenger" => "Client di messaggistica istantanea", +"Delete IM" => "Elimina IM", "View on map" => "Visualizza sulla mappa", "Edit address details" => "Modifica dettagli dell'indirizzo", "Add notes here." => "Aggiungi qui le note.", "Add field" => "Aggiungi campo", -"Profile picture" => "Immagine del profilo", "Phone" => "Telefono", +"Email" => "Email", +"Instant Messaging" => "Messaggistica istantanea", +"Address" => "Indirizzo", "Note" => "Nota", -"Delete current photo" => "Elimina la foto corrente", -"Edit current photo" => "Modifica la foto corrente", -"Upload new photo" => "Invia una nuova foto", -"Select photo from ownCloud" => "Seleziona la foto da ownCloud", +"Download contact" => "Scarica contatto", +"Delete contact" => "Elimina contatto", +"The temporary image has been removed from cache." => "L'immagine temporanea è stata rimossa dalla cache.", "Edit address" => "Modifica indirizzo", "Type" => "Tipo", "PO Box" => "Casella postale", +"Street address" => "Indirizzo", +"Street and number" => "Via e numero", "Extended" => "Esteso", -"Street" => "Via", +"Apartment number etc." => "Numero appartamento ecc.", "City" => "Città ", "Region" => "Regione", +"E.g. state or province" => "Ad es. stato o provincia", "Zipcode" => "CAP", +"Postal code" => "CAP", "Country" => "Stato", -"Edit categories" => "Modifica categorie", -"Add" => "Aggiungi", "Addressbook" => "Rubrica", "Hon. prefixes" => "Prefissi onorifici", "Miss" => "Sig.na", @@ -132,26 +196,35 @@ "Esq." => "Esq.", "Jr." => "Jr.", "Sn." => "Sn.", -"New Addressbook" => "Nuova rubrica", -"Edit Addressbook" => "Modifica rubrica", -"Displayname" => "Nome visualizzato", -"Active" => "Attiva", -"Save" => "Salva", -"Submit" => "Invia", -"Cancel" => "Annulla", "Import a contacts file" => "Importa un file di contatti", "Please choose the addressbook" => "Scegli la rubrica", "create a new addressbook" => "crea una nuova rubrica", "Name of new addressbook" => "Nome della nuova rubrica", -"Import" => "Importa", "Importing contacts" => "Importazione contatti", +"Contacts imported successfully" => "Contatti importati correttamente", +"Close Dialog" => "Chiudi finestra", +"Import Addressbook" => "Importa rubrica", "Select address book to import to:" => "Seleziona la rubrica di destinazione:", +"Drop a VCF file to import contacts." => "Rilascia un file VCF per importare i contatti.", "Select from HD" => "Seleziona da disco", "You have no contacts in your addressbook." => "Non hai contatti nella rubrica.", "Add contact" => "Aggiungi contatto", -"Configure addressbooks" => "Configura rubriche", +"Select Address Books" => "Seleziona rubriche", +"Enter name" => "Inserisci il nome", +"Enter description" => "Inserisci una descrizione", "CardDAV syncing addresses" => "Indirizzi di sincronizzazione CardDAV", "more info" => "altre informazioni", "Primary address (Kontact et al)" => "Indirizzo principale (Kontact e altri)", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Show CardDav link" => "Mostra collegamento CardDav", +"Show read-only VCF link" => "Mostra collegamento VCF in sola lettura", +"Share" => "Condividi", +"Download" => "Scarica", +"Edit" => "Modifica", +"New Address Book" => "Nuova rubrica", +"Name" => "Nome", +"Description" => "Descrizione", +"Save" => "Salva", +"Cancel" => "Annulla", +"More..." => "Altro..." ); diff --git a/apps/contacts/l10n/ja_JP.php b/apps/contacts/l10n/ja_JP.php index d98e58f94da..8a6cce37cc6 100644 --- a/apps/contacts/l10n/ja_JP.php +++ b/apps/contacts/l10n/ja_JP.php @@ -1,36 +1,42 @@ <?php $TRANSLATIONS = array( -"Error (de)activating addressbook." => "アドレスブックã®æœ‰åŠ¹/無効化ã«å¤±æ•—ã—ã¾ã—ãŸã€‚", -"There was an error adding the contact." => "連絡先ã®è¿½åŠ ã§ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚", -"Cannot add empty property." => "é …ç›®ã®æ–°è¦è¿½åŠ ã«å¤±æ•—ã—ã¾ã—ãŸã€‚", -"At least one of the address fields has to be filled out." => "ä½æ‰€ã®é …ç›®ã®ã†ã¡ï¼‘ã¤ã¯å…¥åŠ›ã—ã¦ä¸‹ã•ã„。", -"Error adding contact property." => "連絡先ã®è¿½åŠ ã«å¤±æ•—ã—ã¾ã—ãŸã€‚", +"Error (de)activating addressbook." => "アドレス帳ã®æœ‰åŠ¹ï¼ç„¡åŠ¹åŒ–ã«å¤±æ•—ã—ã¾ã—ãŸã€‚", +"id is not set." => "idãŒè¨å®šã•ã‚Œã¦ã„ã¾ã›ã‚“。", +"Cannot update addressbook with an empty name." => "空白ã®åå‰ã§ã‚¢ãƒ‰ãƒ¬ã‚¹å¸³ã‚’æ›´æ–°ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。", +"Error updating addressbook." => "アドレス帳ã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸã€‚", "No ID provided" => "IDãŒæä¾›ã•ã‚Œã¦ã„ã¾ã›ã‚“", "Error setting checksum." => "ãƒã‚§ãƒƒã‚¯ã‚µãƒ ã®è¨å®šã‚¨ãƒ©ãƒ¼ã€‚", "No categories selected for deletion." => "削除ã™ã‚‹ã‚«ãƒ†ã‚´ãƒªãŒé¸æŠžã•ã‚Œã¦ã„ã¾ã›ã‚“。", -"No address books found." => "アドレスブックãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。", +"No address books found." => "アドレス帳ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。", "No contacts found." => "連絡先ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。", +"There was an error adding the contact." => "連絡先ã®è¿½åŠ ã§ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚", +"element name is not set." => "è¦ç´ åãŒè¨å®šã•ã‚Œã¦ã„ã¾ã›ã‚“。", +"Could not parse contact: " => "連絡先を解æžã§ãã¾ã›ã‚“ã§ã—ãŸ:", +"Cannot add empty property." => "é …ç›®ã®æ–°è¦è¿½åŠ ã«å¤±æ•—ã—ã¾ã—ãŸã€‚", +"At least one of the address fields has to be filled out." => "ä½æ‰€ã®é …ç›®ã®ã†ã¡ï¼‘ã¤ã¯å…¥åŠ›ã—ã¦ä¸‹ã•ã„。", +"Trying to add duplicate property: " => "é‡è¤‡ã™ã‚‹å±žæ€§ã‚’è¿½åŠ : ", +"Missing IM parameter." => "IMã®ãƒ‘ラメータãŒä¸è¶³ã—ã¦ã„ã¾ã™ã€‚", +"Unknown IM: " => "ä¸æ˜ŽãªIM:", +"Information about vCard is incorrect. Please reload the page." => "vCardã®æƒ…å ±ã«èª¤ã‚ŠãŒã‚ã‚Šã¾ã™ã€‚ページをリãƒãƒ¼ãƒ‰ã—ã¦ä¸‹ã•ã„。", +"Missing ID" => "IDãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“", "Error parsing VCard for ID: \"" => "VCardã‹ã‚‰IDã®æŠ½å‡ºã‚¨ãƒ©ãƒ¼: \"", -"Cannot add addressbook with an empty name." => "åå‰ã‚’空白ã«ã—ãŸã¾ã¾ã§ã‚¢ãƒ‰ãƒ¬ã‚¹ãƒ–ãƒƒã‚¯ã‚’è¿½åŠ ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。", -"Error adding addressbook." => "アドレスブックã®è¿½åŠ ã«å¤±æ•—ã—ã¾ã—ãŸã€‚", -"Error activating addressbook." => "アドレスブックã®æœ‰åŠ¹åŒ–ã«å¤±æ•—ã—ã¾ã—ãŸã€‚", +"checksum is not set." => "ãƒã‚§ãƒƒã‚¯ã‚µãƒ ãŒè¨å®šã•ã‚Œã¦ã„ã¾ã›ã‚“。", +"Information about vCard is incorrect. Please reload the page: " => "vCardã®æƒ…å ±ãŒæ£ã—ãã‚ã‚Šã¾ã›ã‚“。ページをå†èªã¿è¾¼ã¿ã—ã¦ãã ã•ã„: ", +"Something went FUBAR. " => "何ã‹ãŒFUBARã¸ç§»å‹•ã—ã¾ã—ãŸã€‚", "No contact ID was submitted." => "連絡先IDã¯ç™»éŒ²ã•ã‚Œã¾ã›ã‚“ã§ã—ãŸã€‚", "Error reading contact photo." => "連絡先写真ã®èªã¿è¾¼ã¿ã‚¨ãƒ©ãƒ¼ã€‚", "Error saving temporary file." => "一時ファイルã®ä¿å˜ã‚¨ãƒ©ãƒ¼ã€‚", "The loading photo is not valid." => "写真ã®èªã¿è¾¼ã¿ã¯ç„¡åŠ¹ã§ã™ã€‚", -"id is not set." => "idãŒè¨å®šã•ã‚Œã¦ã„ã¾ã›ã‚“。", -"Information about vCard is incorrect. Please reload the page." => "vCardã®æƒ…å ±ã«èª¤ã‚ŠãŒã‚ã‚Šã¾ã™ã€‚ページをリãƒãƒ¼ãƒ‰ã—ã¦ä¸‹ã•ã„。", -"Error deleting contact property." => "連絡先ã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸã€‚", "Contact ID is missing." => "コンタクトIDãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。", -"Missing contact id." => "コンタクトIDãŒè¨å®šã•ã‚Œã¦ã„ã¾ã›ã‚“。", "No photo path was submitted." => "写真ã®ãƒ‘スãŒç™»éŒ²ã•ã‚Œã¦ã„ã¾ã›ã‚“。", "File doesn't exist:" => "ファイルãŒå˜åœ¨ã—ã¾ã›ã‚“:", "Error loading image." => "ç”»åƒã®èªã¿è¾¼ã¿ã‚¨ãƒ©ãƒ¼ã€‚", -"element name is not set." => "è¦ç´ åãŒè¨å®šã•ã‚Œã¦ã„ã¾ã›ã‚“。", -"checksum is not set." => "ãƒã‚§ãƒƒã‚¯ã‚µãƒ ãŒè¨å®šã•ã‚Œã¦ã„ã¾ã›ã‚“。", -"Information about vCard is incorrect. Please reload the page: " => "vCardã®æƒ…å ±ãŒæ£ã—ãã‚ã‚Šã¾ã›ã‚“。ページをå†èªã¿è¾¼ã¿ã—ã¦ãã ã•ã„: ", -"Error updating contact property." => "連絡先ã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸã€‚", -"Cannot update addressbook with an empty name." => "空白ã®åå‰ã§ã‚¢ãƒ‰ãƒ¬ã‚¹ãƒ–ックを更新ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。", -"Error updating addressbook." => "アドレスブックã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸã€‚", +"Error getting contact object." => "コンタクトオブジェクトã®å–得エラー。", +"Error getting PHOTO property." => "写真属性ã®å–得エラー。", +"Error saving contact." => "コンタクトã®ä¿å˜ã‚¨ãƒ©ãƒ¼ã€‚", +"Error resizing image" => "ç”»åƒã®ãƒªã‚µã‚¤ã‚ºã‚¨ãƒ©ãƒ¼", +"Error cropping image" => "ç”»åƒã®åˆ‡ã‚ŠæŠœãエラー", +"Error creating temporary image" => "一時画åƒã®ç”Ÿæˆã‚¨ãƒ©ãƒ¼", +"Error finding image: " => "ç”»åƒæ¤œç´¢ã‚¨ãƒ©ãƒ¼: ", "Error uploading contacts to storage." => "ストレージã¸ã®é€£çµ¡å…ˆã®ã‚¢ãƒƒãƒ—ãƒãƒ¼ãƒ‰ã‚¨ãƒ©ãƒ¼ã€‚", "There is no error, the file uploaded with success" => "エラーã¯ã‚ã‚Šã¾ã›ã‚“。ファイルã®ã‚¢ãƒƒãƒ—ãƒãƒ¼ãƒ‰ã¯æˆåŠŸã—ã¾ã—ãŸ", "The uploaded file exceeds the upload_max_filesize directive in php.ini" => "アップãƒãƒ¼ãƒ‰ãƒ•ã‚¡ã‚¤ãƒ«ã¯ php.ini 内㮠upload_max_filesize ã®åˆ¶é™ã‚’超ãˆã¦ã„ã¾ã™", @@ -38,17 +44,46 @@ "The uploaded file was only partially uploaded" => "アップãƒãƒ¼ãƒ‰ãƒ•ã‚¡ã‚¤ãƒ«ã¯ä¸€éƒ¨åˆ†ã ã‘アップãƒãƒ¼ãƒ‰ã•ã‚Œã¾ã—ãŸ", "No file was uploaded" => "ファイルã¯ã‚¢ãƒƒãƒ—ãƒãƒ¼ãƒ‰ã•ã‚Œã¾ã›ã‚“ã§ã—ãŸ", "Missing a temporary folder" => "一時ä¿å˜ãƒ•ã‚©ãƒ«ãƒ€ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“", +"Couldn't save temporary image: " => "一時的ãªç”»åƒã®ä¿å˜ãŒã§ãã¾ã›ã‚“ã§ã—ãŸ: ", +"Couldn't load temporary image: " => "一時的ãªç”»åƒã®èªã¿è¾¼ã¿ãŒã§ãã¾ã›ã‚“ã§ã—ãŸ: ", +"No file was uploaded. Unknown error" => "ファイルã¯ä½•ã‚‚アップãƒãƒ¼ãƒ‰ã•ã‚Œã¦ã„ã¾ã›ã‚“。ä¸æ˜Žãªã‚¨ãƒ©ãƒ¼", "Contacts" => "連絡先", -"Drop a VCF file to import contacts." => "連絡先をインãƒãƒ¼ãƒˆã™ã‚‹VCFファイルをドãƒãƒƒãƒ—ã—ã¦ãã ã•ã„。", -"Addressbook not found." => "アドレスブックãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚", +"Sorry, this functionality has not been implemented yet" => "申ã—訳ã‚ã‚Šã¾ã›ã‚“。ã“ã®æ©Ÿèƒ½ã¯ã¾ã 実装ã•ã‚Œã¦ã„ã¾ã›ã‚“", +"Not implemented" => "未実装", +"Couldn't get a valid address." => "有効ãªã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’å–å¾—ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚", +"Error" => "エラー", +"This property has to be non-empty." => "ã“ã®å±žæ€§ã¯ç©ºã«ã§ãã¾ã›ã‚“。", +"Couldn't serialize elements." => "è¦ç´ をシリアライズã§ãã¾ã›ã‚“ã§ã—ãŸã€‚", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "'deleteProperty' ã¯åž‹ã®å¼•æ•°ç„¡ã—ã§å‘¼ã³å‡ºã•ã‚Œã¾ã—ãŸã€‚bugs.owncloud.org ã¸å ±å‘Šã—ã¦ãã ã•ã„。", +"Edit name" => "åå‰ã‚’編集", +"No files selected for upload." => "アップãƒãƒ¼ãƒ‰ã™ã‚‹ãƒ•ã‚¡ã‚¤ãƒ«ãŒé¸æŠžã•ã‚Œã¦ã„ã¾ã›ã‚“。", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "アップãƒãƒ¼ãƒ‰ã—よã†ã¨ã—ã¦ã„るファイルã¯ã€ã“ã®ã‚µãƒ¼ãƒã®æœ€å¤§ãƒ•ã‚¡ã‚¤ãƒ«ã‚¢ãƒƒãƒ—ãƒãƒ¼ãƒ‰ã‚µã‚¤ã‚ºã‚’超ãˆã¦ã„ã¾ã™ã€‚", +"Error loading profile picture." => "プãƒãƒ•ã‚¡ã‚¤ãƒ«ã®ç”»åƒã®èªã¿è¾¼ã¿ã‚¨ãƒ©ãƒ¼", +"Select type" => "タイプをé¸æŠž", +"Some contacts are marked for deletion, but not deleted yet. Please wait for them to be deleted." => "ã„ãã¤ã‹ã®ã‚³ãƒ³ã‚¿ã‚¯ãƒˆãŒå‰Šé™¤ã¨ãƒžãƒ¼ã‚¯ã•ã‚Œã¦ã„ã¾ã™ãŒã€ã¾ã 削除ã•ã‚Œã¦ã„ã¾ã›ã‚“。削除ã™ã‚‹ã¾ã§ãŠå¾…ã¡ãã ã•ã„。", +"Do you want to merge these address books?" => "ã“れらã®ã‚¢ãƒ‰ãƒ¬ã‚¹å¸³ã‚’マージã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ", +"Result: " => "çµæžœ: ", +" imported, " => " をインãƒãƒ¼ãƒˆã€ ", +" failed." => " ã¯å¤±æ•—ã—ã¾ã—ãŸã€‚", +"Displayname cannot be empty." => "表示åã¯ç©ºã«ã§ãã¾ã›ã‚“。", +"Addressbook not found: " => "アドレス帳ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“:", "This is not your addressbook." => "ã“ã‚Œã¯ã‚ãªãŸã®é›»è©±å¸³ã§ã¯ã‚ã‚Šã¾ã›ã‚“。", "Contact could not be found." => "連絡先を見ã¤ã‘る事ãŒã§ãã¾ã›ã‚“。", -"Address" => "ä½æ‰€", -"Telephone" => "電話番å·", -"Email" => "メールアドレス", -"Organization" => "所属", +"Jabber" => "Jabber", +"AIM" => "AIM", +"MSN" => "MSN", +"Twitter" => "Twitter", +"GoogleTalk" => "Googleトーク", +"Facebook" => "Facebook", +"XMPP" => "XMPP", +"ICQ" => "ICQ", +"Yahoo" => "Yahoo", +"Skype" => "Skype", +"QQ" => "QQ", +"GaduGadu" => "GaduGadu", "Work" => "勤務先", "Home" => "ä½å±…", +"Other" => "ãã®ä»–", "Mobile" => "æºå¸¯é›»è©±", "Text" => "TTY TDD", "Voice" => "音声番å·", @@ -57,58 +92,92 @@ "Video" => "テレビ電話", "Pager" => "ãƒã‚±ãƒ™ãƒ«", "Internet" => "インターãƒãƒƒãƒˆ", +"Birthday" => "誕生日", +"Business" => "ビジãƒã‚¹", +"Call" => "電話", +"Clients" => "顧客", +"Deliverer" => "é‹é€ä¼šç¤¾", +"Holidays" => "休日", +"Ideas" => "アイデア", +"Journey" => "æ—…è¡Œ", +"Jubilee" => "記念ç¥", +"Meeting" => "打ã¡åˆã‚ã›", +"Personal" => "個人", +"Projects" => "プãƒã‚¸ã‚§ã‚¯ãƒˆ", +"Questions" => "質å•", "{name}'s Birthday" => "{name}ã®èª•ç”Ÿæ—¥", "Contact" => "連絡先", "Add Contact" => "連絡先ã®è¿½åŠ ", -"Addressbooks" => "電話帳", -"Configure Address Books" => "アドレスブックをè¨å®š", -"New Address Book" => "æ–°è¦é›»è©±å¸³", -"Import from VCF" => "VCFã‹ã‚‰ã‚¤ãƒ³ãƒãƒ¼ãƒˆ", -"CardDav Link" => "CardDAVリンク", -"Download" => "ダウンãƒãƒ¼ãƒ‰", -"Edit" => "編集", -"Delete" => "削除", -"Download contact" => "連絡先ã®ãƒ€ã‚¦ãƒ³ãƒãƒ¼ãƒ‰", -"Delete contact" => "連絡先ã®å‰Šé™¤", +"Import" => "インãƒãƒ¼ãƒˆ", +"Settings" => "è¨å®š", +"Addressbooks" => "アドレス帳", +"Close" => "é–‰ã˜ã‚‹", +"Keyboard shortcuts" => "ã‚ーボードショートカット", +"Navigation" => "ナビゲーション", +"Next contact in list" => "リスト内ã®æ¬¡ã®ã‚³ãƒ³ã‚¿ã‚¯ãƒˆ", +"Previous contact in list" => "リスト内ã®å‰ã®ã‚³ãƒ³ã‚¿ã‚¯ãƒˆ", +"Expand/collapse current addressbook" => "ç¾åœ¨ã®ã‚¢ãƒ‰ãƒ¬ã‚¹å¸³ã‚’展開ã™ã‚‹ï¼æŠ˜ã‚ŠãŸãŸã‚€", +"Next addressbook" => "次ã®ã‚¢ãƒ‰ãƒ¬ã‚¹å¸³", +"Previous addressbook" => "å‰ã®ã‚¢ãƒ‰ãƒ¬ã‚¹å¸³", +"Actions" => "アクション", +"Refresh contacts list" => "連絡先リストをå†èªè¾¼ã™ã‚‹", +"Add new contact" => "æ–°ã—ã„ã‚³ãƒ³ã‚¿ã‚¯ãƒˆã‚’è¿½åŠ ", +"Add new addressbook" => "æ–°ã—ã„ã‚¢ãƒ‰ãƒ¬ã‚¹å¸³ã‚’è¿½åŠ ", +"Delete current contact" => "ç¾åœ¨ã®ã‚³ãƒ³ã‚¿ã‚¯ãƒˆã‚’削除", "Drop photo to upload" => "写真をドãƒãƒƒãƒ—ã—ã¦ã‚¢ãƒƒãƒ—ãƒãƒ¼ãƒ‰", +"Delete current photo" => "ç¾åœ¨ã®å†™çœŸã‚’削除", +"Edit current photo" => "ç¾åœ¨ã®å†™çœŸã‚’編集", +"Upload new photo" => "æ–°ã—ã„写真をアップãƒãƒ¼ãƒ‰", +"Select photo from ownCloud" => "ownCloudã‹ã‚‰å†™çœŸã‚’é¸æŠž", +"Format custom, Short name, Full name, Reverse or Reverse with comma" => "編集フォーマットã€ã‚·ãƒ§ãƒ¼ãƒˆãƒãƒ¼ãƒ ã€ãƒ•ãƒ«ãƒãƒ¼ãƒ ã€é€†é †ã€ã‚«ãƒ³ãƒžåŒºåˆ‡ã‚Šã®é€†é †", "Edit name details" => "åå‰ã®è©³ç´°ã‚’編集", +"Organization" => "所属", +"Delete" => "削除", "Nickname" => "ニックãƒãƒ¼ãƒ ", "Enter nickname" => "ニックãƒãƒ¼ãƒ を入力", -"Birthday" => "誕生日", +"Web site" => "ウェブサイト", +"http://www.somesite.com" => "http://www.somesite.com", +"Go to web site" => "Webサイトã¸ç§»å‹•", "dd-mm-yyyy" => "yyyy-mm-dd", "Groups" => "グループ", "Separate groups with commas" => "コンマã§ã‚°ãƒ«ãƒ¼ãƒ—を分割", "Edit groups" => "グループを編集", "Preferred" => "推奨", -"Please specify a valid email address." => "é€£çµ¡å…ˆã‚’è¿½åŠ ", +"Please specify a valid email address." => "有効ãªãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’指定ã—ã¦ãã ã•ã„。", "Enter email address" => "メールアドレスを入力", "Mail to address" => "アドレスã¸ãƒ¡ãƒ¼ãƒ«ã‚’é€ã‚‹", "Delete email address" => "メールアドレスを削除", "Enter phone number" => "電話番å·ã‚’入力", "Delete phone number" => "電話番å·ã‚’削除", +"Instant Messenger" => "インスタントメッセンジャー", +"Delete IM" => "IMを削除", "View on map" => "地図ã§è¡¨ç¤º", "Edit address details" => "ä½æ‰€ã®è©³ç´°ã‚’編集", "Add notes here." => "ã“ã“ã«ãƒ¡ãƒ¢ã‚’è¿½åŠ ã€‚", "Add field" => "é …ç›®ã‚’è¿½åŠ ", -"Profile picture" => "プãƒãƒ•ã‚£ãƒ¼ãƒ«å†™çœŸ", "Phone" => "電話番å·", +"Email" => "メールアドレス", +"Instant Messaging" => "インスタントメッセージ", +"Address" => "ä½æ‰€", "Note" => "メモ", -"Delete current photo" => "ç¾åœ¨ã®å†™çœŸã‚’削除", -"Edit current photo" => "ç¾åœ¨ã®å†™çœŸã‚’編集", -"Upload new photo" => "æ–°ã—ã„写真をアップãƒãƒ¼ãƒ‰", -"Select photo from ownCloud" => "ownCloudã‹ã‚‰å†™çœŸã‚’é¸æŠž", +"Download contact" => "連絡先ã®ãƒ€ã‚¦ãƒ³ãƒãƒ¼ãƒ‰", +"Delete contact" => "連絡先ã®å‰Šé™¤", +"The temporary image has been removed from cache." => "一時画åƒã¯ã‚ャッシュã‹ã‚‰å‰Šé™¤ã•ã‚Œã¾ã—ãŸã€‚", "Edit address" => "ä½æ‰€ã‚’編集", "Type" => "種類", "PO Box" => "ç§æ›¸ç®±", -"Extended" => "番地2", -"Street" => "番地1", +"Street address" => "ä½æ‰€1", +"Street and number" => "番地", +"Extended" => "ä½æ‰€2", +"Apartment number etc." => "アパートåç‰", "City" => "都市", "Region" => "都é“府県", +"E.g. state or province" => "例:æ±äº¬éƒ½ã€å¤§é˜ªåºœ", "Zipcode" => "郵便番å·", +"Postal code" => "郵便番å·", "Country" => "国å", -"Edit categories" => "カテゴリを編集", -"Add" => "è¿½åŠ ", -"Addressbook" => "アドレスブック", +"Addressbook" => "アドレス帳", +"Hon. prefixes" => "敬称", "Miss" => "Miss", "Ms" => "Ms", "Mr" => "Mr", @@ -118,7 +187,7 @@ "Given name" => "å", "Additional names" => "ミドルãƒãƒ¼ãƒ ", "Family name" => "姓", -"Hon. suffixes" => "ストレージã¸ã®é€£çµ¡å…ˆã®ã‚¢ãƒƒãƒ—ãƒãƒ¼ãƒ‰ã‚¨ãƒ©ãƒ¼ã€‚", +"Hon. suffixes" => "称å·", "J.D." => "J.D.", "M.D." => "M.D.", "D.O." => "D.O.", @@ -127,26 +196,29 @@ "Esq." => "Esq.", "Jr." => "Jr.", "Sn." => "Sn.", -"New Addressbook" => "アドレスブックã®æ–°è¦ä½œæˆ", -"Edit Addressbook" => "アドレスブックを編集", -"Displayname" => "表示å", -"Active" => "アクティブ", -"Save" => "ä¿å˜", -"Submit" => "é€ä¿¡", -"Cancel" => "å–り消ã—", "Import a contacts file" => "コンタクトファイルをインãƒãƒ¼ãƒˆ", -"Please choose the addressbook" => "アドレスブックをé¸æŠžã—ã¦ãã ã•ã„", -"create a new addressbook" => "æ–°ã—ã„アドレスブックを作æˆ", +"Please choose the addressbook" => "アドレス帳をé¸æŠžã—ã¦ãã ã•ã„", +"create a new addressbook" => "æ–°ã—ã„アドレス帳を作æˆ", "Name of new addressbook" => "æ–°ã—ã„アドレスブックã®åå‰", -"Import" => "インãƒãƒ¼ãƒˆ", "Importing contacts" => "コンタクトをインãƒãƒ¼ãƒˆ", -"Select address book to import to:" => "インãƒãƒ¼ãƒˆã™ã‚‹ã‚¢ãƒ‰ãƒ¬ã‚¹ãƒ–ックをé¸æŠž:", -"Select from HD" => "HDã‹ã‚‰é¸æŠž", -"You have no contacts in your addressbook." => "アドレスブックã«é€£çµ¡å…ˆãŒç™»éŒ²ã•ã‚Œã¦ã„ã¾ã›ã‚“。", +"You have no contacts in your addressbook." => "アドレス帳ã«é€£çµ¡å…ˆãŒç™»éŒ²ã•ã‚Œã¦ã„ã¾ã›ã‚“。", "Add contact" => "é€£çµ¡å…ˆã‚’è¿½åŠ ", -"Configure addressbooks" => "アドレス帳をè¨å®š", +"Select Address Books" => "アドレス帳をé¸æŠžã—ã¦ãã ã•ã„", +"Enter name" => "åå‰ã‚’入力", +"Enter description" => "説明を入力ã—ã¦ãã ã•ã„", "CardDAV syncing addresses" => "CardDAVåŒæœŸã‚¢ãƒ‰ãƒ¬ã‚¹", "more info" => "è©³ç´°æƒ…å ±", "Primary address (Kontact et al)" => "プライマリアドレス(Kontact 他)", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Show CardDav link" => "CarDavリンクを表示", +"Show read-only VCF link" => "èªã¿å–り専用ã®VCFリンクを表示", +"Share" => "共有", +"Download" => "ダウンãƒãƒ¼ãƒ‰", +"Edit" => "編集", +"New Address Book" => "æ–°è¦ã®ã‚¢ãƒ‰ãƒ¬ã‚¹å¸³", +"Name" => "åå‰", +"Description" => "説明", +"Save" => "ä¿å˜", +"Cancel" => "å–り消ã—", +"More..." => "ã‚‚ã£ã¨..." ); diff --git a/apps/contacts/l10n/ko.php b/apps/contacts/l10n/ko.php index fcdc1d08c62..4349224f53c 100644 --- a/apps/contacts/l10n/ko.php +++ b/apps/contacts/l10n/ko.php @@ -1,41 +1,64 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "주소ë¡ì„ (비)활성화하는 ë° ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤.", -"There was an error adding the contact." => "ì—°ë½ì²˜ë¥¼ 추가하는 중 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤.", -"Cannot add empty property." => "빈 ì†ì„±ì„ ì¶”ê°€í• ìˆ˜ 없습니다.", -"At least one of the address fields has to be filled out." => "최소한 í•˜ë‚˜ì˜ ì£¼ì†Œë¡ í•ëª©ì„ ìž…ë ¥í•´ì•¼ 합니다.", -"Error adding contact property." => "ì—°ë½ì²˜ ì†ì„±ì„ ì¶”ê°€í• ìˆ˜ 없습니다.", +"id is not set." => "ì•„ì´ë””ê°€ ì„¤ì •ë˜ì–´ 있지 않습니다. ", +"Cannot update addressbook with an empty name." => "주소ë¡ì— ì´ë¦„ëž€ì´ ë¹„ì–´ìžˆìœ¼ë©´ ì—…ë°ì´íŠ¸ë¥¼ í• ìˆ˜ 없습니다. ", +"Error updating addressbook." => "주소ë¡ì„ ì—…ë°ì´íŠ¸í• 수 없습니다.", "No ID provided" => "ì œê³µë˜ëŠ” ì•„ì´ë”” ì—†ìŒ", "Error setting checksum." => "오류 검사합계 ì„¤ì •", "No categories selected for deletion." => "ì‚ì œ ì¹´í…Œê³ ë¦¬ë¥¼ ì„ íƒí•˜ì§€ 않았습니다. ", "No address books found." => "주소ë¡ì„ ì°¾ì„ ìˆ˜ 없습니다.", "No contacts found." => "ì—°ë½ì²˜ë¥¼ ì°¾ì„ ìˆ˜ 없습니다.", +"There was an error adding the contact." => "ì—°ë½ì²˜ë¥¼ 추가하는 중 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤.", +"element name is not set." => "element ì´ë¦„ì´ ì„¤ì •ë˜ì§€ 않았습니다.", +"Cannot add empty property." => "빈 ì†ì„±ì„ ì¶”ê°€í• ìˆ˜ 없습니다.", +"At least one of the address fields has to be filled out." => "최소한 í•˜ë‚˜ì˜ ì£¼ì†Œë¡ í•ëª©ì„ ìž…ë ¥í•´ì•¼ 합니다.", +"Trying to add duplicate property: " => "중복 ì†ì„± 추가 ì‹œë„: ", +"Information about vCard is incorrect. Please reload the page." => "vCard ì •ë³´ê°€ 올바르지 않습니다. 페ì´ì§€ë¥¼ 새로 ê³ ì¹˜ì‹ì‹œì˜¤.", "Missing ID" => "ì•„ì´ë”” 분실", -"Cannot add addressbook with an empty name." => "ì„±ëª…ëž€ì´ ë¹„ì–´ 주소ë¡ì— 추가 í• ìˆ˜ 없습니다.", -"Error adding addressbook." => "주소ë¡ì„ ì¶”ê°€í• ìˆ˜ 없습니다.", -"Error activating addressbook." => "주소ë¡ì„ í™œì„±í™”í• ìˆ˜ 없습니다.", +"Error parsing VCard for ID: \"" => "ì•„ì´ë””ì— ëŒ€í•œ VCard ë¶„ì„ ì˜¤ë¥˜", +"checksum is not set." => "ì²´í¬ì„¬ì´ ì„¤ì •ë˜ì§€ 않았습니다.", +"Information about vCard is incorrect. Please reload the page: " => " vCardì— ëŒ€í•œ ì •ë³´ê°€ 잘못ë˜ì—ˆìŠµë‹ˆë‹¤. 페ì´ì§€ë¥¼ 다시 로드하세요:", "No contact ID was submitted." => "ì ‘ì† ì•„ì´ë””ê°€ 기입ë˜ì§€ 않았습니다.", "Error reading contact photo." => "사진 ì½ê¸° 오류", "Error saving temporary file." => "ìž„ì‹œ 파ì¼ì„ ì €ìž¥í•˜ëŠ” ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. ", "The loading photo is not valid." => "로딩 ì‚¬ì§„ì´ ìœ íš¨í•˜ì§€ 않습니다. ", -"id is not set." => "ì•„ì´ë””ê°€ ì„¤ì •ë˜ì–´ 있지 않습니다. ", -"Information about vCard is incorrect. Please reload the page." => "vCard ì •ë³´ê°€ 올바르지 않습니다. 페ì´ì§€ë¥¼ 새로 ê³ ì¹˜ì‹ì‹œì˜¤.", -"Error deleting contact property." => "ì—°ë½ì²˜ ì†ì„±ì„ ì‚ì œí• ìˆ˜ 없습니다.", "Contact ID is missing." => "ì ‘ì† ì•„ì´ë””ê°€ 없습니다. ", -"Missing contact id." => "ì ‘ì† ì•„ì´ë”” 분실", +"No photo path was submitted." => "사진 경로가 ì œì¶œë˜ì§€ 않았습니다. ", "File doesn't exist:" => "파ì¼ì´ 존재하지 않습니다. ", "Error loading image." => "로딩 ì´ë¯¸ì§€ 오류입니다.", -"Error updating contact property." => "ì—°ë½ì²˜ ì†ì„±ì„ ì—…ë°ì´íŠ¸í• 수 없습니다.", -"Error updating addressbook." => "주소ë¡ì„ ì—…ë°ì´íŠ¸í• 수 없습니다.", +"Error getting contact object." => "ì—°ë½ì²˜ 개체를 ê°€ì ¸ì˜¤ëŠ” 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. ", +"Error getting PHOTO property." => "사진 ì†ì„±ì„ ê°€ì ¸ì˜¤ëŠ” 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. ", +"Error saving contact." => "ì—°ë½ì²˜ ì €ìž¥ 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤.", +"Error resizing image" => "ì´ë¯¸ì§€ í¬ê¸° ì¡°ì • 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤.", +"Error cropping image" => "ì´ë¯¸ì§€ë¥¼ ìžë¥´ë˜ 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤.", +"Error creating temporary image" => "ìž„ì‹œ ì´ë¯¸ì§€ë¥¼ ìƒì„± 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤.", +"Error finding image: " => "ì´ë¯¸ì§€ë¥¼ ì°¾ë˜ ì¤‘ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤:", +"Error uploading contacts to storage." => "ìŠ¤í† ë¦¬ì§€ ì—러 업로드 ì—°ë½ì²˜.", +"There is no error, the file uploaded with success" => "ì˜¤ë¥˜ì—†ì´ íŒŒì¼ì—…로드 성공.", +"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "php.ini 형ì‹ìœ¼ë¡œ 업로드 ëœ ì´ íŒŒì¼ì€ MAX_FILE_SIZE를 초과하였다.", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "HTML형ì‹ìœ¼ë¡œ 업로드 ëœ ì´ íŒŒì¼ì€ MAX_FILE_SIZE를 초과하였다.", +"The uploaded file was only partially uploaded" => "ì´ ì—…ë¡œë“œëœ íŒŒì¼ì€ 부분ì 으로만 업로드 ë˜ì—ˆìŠµë‹ˆë‹¤.", "No file was uploaded" => "파ì¼ì´ 업로드 ë˜ì–´ìžˆì§€ 않습니다", "Missing a temporary folder" => "ìž„ì‹œ í´ë” 분실", +"Couldn't save temporary image: " => "ìž„ì‹œ ì´ë¯¸ì§€ë¥¼ ì €ìž¥í• ìˆ˜ 없습니다:", +"Couldn't load temporary image: " => "ìž„ì‹œ ì´ë¯¸ì§€ë¥¼ 불러올 수 없습니다. ", +"No file was uploaded. Unknown error" => "파ì¼ì´ 업로드 ë˜ì§€ 않았습니다. ì•Œ 수 없는 오류.", "Contacts" => "ì—°ë½ì²˜", -"Addressbook not found." => "주소ë¡ì„ ì°¾ì„ ìˆ˜ 없습니다.", +"Sorry, this functionality has not been implemented yet" => "죄송합니다. ì´ ê¸°ëŠ¥ì€ ì•„ì§ êµ¬í˜„ë˜ì§€ 않았습니다. ", +"Not implemented" => "구현ë˜ì§€ ì•ŠìŒ", +"Couldn't get a valid address." => "ìœ íš¨í•œ 주소를 ì–»ì„ ìˆ˜ 없습니다.", +"Error" => "오류", +"Couldn't serialize elements." => "요소를 ì§ë ¬í™” í• ìˆ˜ 없습니다.", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "'deleteProperty'ê°€ 문서형ì‹ì´ ì—†ì´ ë¶ˆë ¤ì™”ìŠµë‹ˆë‹¤. bugs.owncloud.orgì— ë³´ê³ í•´ì£¼ì„¸ìš”. ", +"Edit name" => "ì´ë¦„ 편집", +"No files selected for upload." => "업로드를 위한 파ì¼ì´ ì„ íƒë˜ì§€ 않았습니다. ", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "ì´ íŒŒì¼ì€ ì´ ì„œë²„ íŒŒì¼ ì—…ë¡œë“œ 최대 ìš©ëŸ‰ì„ ì´ˆê³¼ 합니다. ", +"Select type" => "ìœ í˜• ì„ íƒ", +"Result: " => "ê²°ê³¼:", +" imported, " => "불러오기,", +" failed." => "실패.", "This is not your addressbook." => "ë‚´ 주소ë¡ì´ 아닙니다.", "Contact could not be found." => "ì—°ë½ì²˜ë¥¼ ì°¾ì„ ìˆ˜ 없습니다.", -"Address" => "주소", -"Telephone" => "ì „í™” 번호", -"Email" => "ì „ìž ìš°íŽ¸", -"Organization" => "ì¡°ì§", "Work" => "ì§ìž¥", "Home" => "ìžíƒ", "Mobile" => "휴대í°", @@ -46,24 +69,24 @@ "Video" => "ì˜ìƒ 번호", "Pager" => "호출기", "Internet" => "ì¸í„°ë„·", +"Birthday" => "ìƒì¼", "{name}'s Birthday" => "{ì´ë¦„}ì˜ ìƒì¼", "Contact" => "ì—°ë½ì²˜", "Add Contact" => "ì—°ë½ì²˜ 추가", +"Import" => "ìž…ë ¥", "Addressbooks" => "주소ë¡", -"Configure Address Books" => "ì£¼ì†Œë¡ êµ¬ì„±", -"New Address Book" => "새 주소ë¡", -"Import from VCF" => "VCFì—ì„œ ê°€ì ¸ì˜¤ê¸°", -"CardDav Link" => "CardDav ë§í¬", -"Download" => "다운로드", -"Edit" => "편집", -"Delete" => "ì‚ì œ", -"Download contact" => "ì—°ë½ì²˜ 다운로드", -"Delete contact" => "ì—°ë½ì²˜ ì‚ì œ", +"Close" => "닫기", "Drop photo to upload" => "Drop photo to upload", +"Delete current photo" => "현재 사진 ì‚ì œ", +"Edit current photo" => "현재 사진 편집", +"Upload new photo" => "새로운 사진 업로드", +"Select photo from ownCloud" => "ownCloudì—ì„œ 사진 ì„ íƒ", +"Format custom, Short name, Full name, Reverse or Reverse with comma" => "Format custom, Short name, Full name, Reverse or Reverse with comma", "Edit name details" => "ì´ë¦„ 세부사í•ì„ 편집합니다. ", +"Organization" => "ì¡°ì§", +"Delete" => "ì‚ì œ", "Nickname" => "별명", "Enter nickname" => "별명 ìž…ë ¥", -"Birthday" => "ìƒì¼", "dd-mm-yyyy" => "ì¼-ì›”-ë…„", "Groups" => "그룹", "Separate groups with commas" => "쉼표로 그룹 구분", @@ -71,22 +94,62 @@ "Preferred" => "ì„ í˜¸í•¨", "Please specify a valid email address." => "올바른 ì´ë©”ì¼ ì£¼ì†Œë¥¼ ìž…ë ¥í•˜ì„¸ìš”.", "Enter email address" => "ì´ë©”ì¼ ì£¼ì†Œ ìž…ë ¥", +"Delete email address" => "ì´ë©”ì¼ ì£¼ì†Œ ì‚ì œ", +"Enter phone number" => "ì „í™”ë²ˆí˜¸ ìž…ë ¥", +"Delete phone number" => "ì „í™”ë²ˆí˜¸ ì‚ì œ", +"View on map" => "지ë„ì—ì„œ 보기", +"Edit address details" => "ìƒì„¸ 주소 ìˆ˜ì •", +"Add notes here." => "ì—¬ê¸°ì— ë…¸íŠ¸ 추가.", +"Add field" => "íŒŒì¼ ì¶”ê°€", "Phone" => "ì „í™” 번호", +"Email" => "ì „ìž ìš°íŽ¸", +"Address" => "주소", +"Note" => "노트", +"Download contact" => "ì—°ë½ì²˜ 다운로드", +"Delete contact" => "ì—°ë½ì²˜ ì‚ì œ", +"The temporary image has been removed from cache." => "ìž„ì‹œ ì´ë¯¸ì§€ê°€ ìºì‹œì—ì„œ ì œê±° ë˜ì—ˆìŠµë‹ˆë‹¤. ", +"Edit address" => "주소 ìˆ˜ì •", "Type" => "종류", "PO Box" => "사서함", "Extended" => "확장", -"Street" => "거리", "City" => "ë„ì‹œ", "Region" => "지ì—", "Zipcode" => "우편 번호", "Country" => "êµê°€", -"Add" => "추가", "Addressbook" => "주소ë¡", -"New Addressbook" => "새 주소ë¡", -"Edit Addressbook" => "ì£¼ì†Œë¡ íŽ¸ì§‘", -"Displayname" => "표시 ì´ë¦„", -"Active" => "활성", +"Hon. prefixes" => "Hon. prefixes", +"Miss" => "Miss", +"Ms" => "Ms", +"Mr" => "Mr", +"Sir" => "Sir", +"Mrs" => "Mrs", +"Dr" => "Dr", +"Given name" => "Given name", +"Additional names" => "추가 ì´ë¦„", +"Family name" => "성", +"Hon. suffixes" => "Hon. suffixes", +"J.D." => "J.D.", +"M.D." => "M.D.", +"D.O." => "D.O.", +"D.C." => "D.C.", +"Ph.D." => "Ph.D.", +"Esq." => "Esq.", +"Jr." => "Jr.", +"Sn." => "Sn.", +"Import a contacts file" => "ì—°ë½ì²˜ íŒŒì¼ ìž…ë ¥", +"Please choose the addressbook" => "주소ë¡ì„ ì„ íƒí•´ 주세요.", +"create a new addressbook" => "새 ì£¼ì†Œë¡ ë§Œë“¤ê¸°", +"Name of new addressbook" => "새 ì£¼ì†Œë¡ ì´ë¦„", +"Importing contacts" => "ì—°ë½ì²˜ ìž…ë ¥", +"You have no contacts in your addressbook." => "ë‹¹ì‹ ì˜ ì£¼ì†Œë¡ì—는 ì—°ë½ì²˜ê°€ 없습니다. ", +"Add contact" => "ì—°ë½ì²˜ 추가", +"CardDAV syncing addresses" => "CardDAV 주소 ë™ê¸°í™”", +"more info" => "ë” ë§Žì€ ì •ë³´", +"Primary address (Kontact et al)" => "기본 주소 (Kontact et al)", +"iOS/OS X" => "iOS/OS X", +"Download" => "다운로드", +"Edit" => "편집", +"New Address Book" => "새 주소ë¡", "Save" => "ì €ìž¥", -"Submit" => "보내기", "Cancel" => "취소" ); diff --git a/apps/contacts/l10n/lb.php b/apps/contacts/l10n/lb.php index 361e1cb93e1..22ca20e751b 100644 --- a/apps/contacts/l10n/lb.php +++ b/apps/contacts/l10n/lb.php @@ -1,44 +1,86 @@ <?php $TRANSLATIONS = array( +"Error (de)activating addressbook." => "Fehler beim (de)aktivéieren vum Adressbuch.", +"id is not set." => "ID ass net gesat.", +"Error updating addressbook." => "Fehler beim updaten vum Adressbuch.", +"No ID provided" => "Keng ID uginn", +"Error setting checksum." => "Fehler beim setzen vun der Checksum.", +"No categories selected for deletion." => "Keng Kategorien fir ze läschen ausgewielt.", +"No address books found." => "Keen Adressbuch fonnt.", +"No contacts found." => "Keng Kontakter fonnt.", +"There was an error adding the contact." => "Fehler beim bäisetzen vun engem Kontakt.", +"Cannot add empty property." => "Ka keng eidel Proprietéit bäisetzen.", +"Trying to add duplicate property: " => "Probéieren duebel Proprietéit bäi ze setzen:", "Information about vCard is incorrect. Please reload the page." => "Informatioun iwwert vCard ass net richteg. Lued d'Säit wegl nei.", +"Missing ID" => "ID fehlt", +"No contact ID was submitted." => "Kontakt ID ass net mat geschéckt ginn.", +"Error reading contact photo." => "Fehler beim liesen vun der Kontakt Photo.", +"Error saving temporary file." => "Fehler beim späicheren vum temporäre Fichier.", +"The loading photo is not valid." => "Déi geluede Photo ass net gülteg.", +"Contact ID is missing." => "Kontakt ID fehlt.", +"File doesn't exist:" => "Fichier existéiert net:", +"Error loading image." => "Fehler beim lueden vum Bild.", +"No file was uploaded" => "Et ass kee Fichier ropgeluede ginn", +"Contacts" => "Kontakter", +"Error" => "Fehler", +"Result: " => "Resultat: ", +" imported, " => " importéiert, ", "This is not your addressbook." => "Dat do ass net däin Adressbuch.", "Contact could not be found." => "Konnt den Kontakt net fannen.", -"Address" => "Adress", -"Telephone" => "Telefon's Nummer", -"Email" => "Email", -"Organization" => "Firma", "Work" => "Aarbecht", "Home" => "Doheem", "Mobile" => "GSM", "Text" => "SMS", "Voice" => "Voice", +"Message" => "Message", "Fax" => "Fax", "Video" => "Video", "Pager" => "Pager", +"Internet" => "Internet", +"Birthday" => "Gebuertsdag", +"{name}'s Birthday" => "{name} säi Gebuertsdag", "Contact" => "Kontakt", "Add Contact" => "Kontakt bäisetzen", "Addressbooks" => "Adressbicher ", -"New Address Book" => "Neit Adressbuch", -"CardDav Link" => "CardDav Link", -"Download" => "Download", -"Edit" => "Editéieren", +"Close" => "Zoumaachen", +"Organization" => "Firma", "Delete" => "Läschen", -"Delete contact" => "Kontakt läschen", -"Birthday" => "Gebuertsdag", +"Nickname" => "Spëtznumm", +"Enter nickname" => "Gëff e Spëtznumm an", +"dd-mm-yyyy" => "dd-mm-yyyy", +"Groups" => "Gruppen", +"Edit groups" => "Gruppen editéieren", +"Enter phone number" => "Telefonsnummer aginn", +"Delete phone number" => "Telefonsnummer läschen", +"View on map" => "Op da Kaart uweisen", +"Edit address details" => "Adress Detailer editéieren", "Phone" => "Telefon", +"Email" => "Email", +"Address" => "Adress", +"Note" => "Note", +"Download contact" => "Kontakt eroflueden", +"Delete contact" => "Kontakt läschen", "Type" => "Typ", "PO Box" => "Postleetzuel", "Extended" => "Erweidert", -"Street" => "Strooss", "City" => "Staat", "Region" => "Regioun", "Zipcode" => "Postleetzuel", "Country" => "Land", -"Add" => "Dobäisetzen", "Addressbook" => "Adressbuch", -"New Addressbook" => "Neit Adressbuch", -"Edit Addressbook" => "Adressbuch editéieren", -"Displayname" => "Ugewisene Numm", +"Mr" => "M", +"Sir" => "Sir", +"Mrs" => "Mme", +"Dr" => "Dr", +"Given name" => "Virnumm", +"Additional names" => "Weider Nimm", +"Family name" => "Famillje Numm", +"Ph.D." => "Ph.D.", +"Jr." => "Jr.", +"Sn." => "Sn.", +"iOS/OS X" => "iOS/OS X", +"Download" => "Download", +"Edit" => "Editéieren", +"New Address Book" => "Neit Adressbuch", "Save" => "Späicheren", -"Submit" => "Fortschécken", "Cancel" => "Ofbriechen" ); diff --git a/apps/contacts/l10n/lt_LT.php b/apps/contacts/l10n/lt_LT.php index 435927a247c..3b31aa7931e 100644 --- a/apps/contacts/l10n/lt_LT.php +++ b/apps/contacts/l10n/lt_LT.php @@ -1,49 +1,53 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "Klaida (de)aktyvuojant adresų knygÄ….", +"No contacts found." => "Kontaktų nerasta.", "There was an error adding the contact." => "Pridedant kontaktÄ… įvyko klaida.", -"Error adding addressbook." => "Klaida pridedant adresų knygÄ….", -"Error activating addressbook." => "Klaida aktyvuojant adresų knygÄ….", "Information about vCard is incorrect. Please reload the page." => "Informacija apie vCard yra neteisinga. ", +"Error reading contact photo." => "Klaida skaitant kontakto nuotraukÄ….", +"The loading photo is not valid." => "Netinkama įkeliama nuotrauka.", +"File doesn't exist:" => "Failas neegzistuoja:", +"Error loading image." => "Klaida įkeliant nuotraukÄ….", +"There is no error, the file uploaded with success" => "Failas įkeltas sÄ—kmingai, be klaidų", +"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Ä®keliamo failo dydis virÅ¡ija upload_max_filesize nustatymÄ… php.ini", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Ä®keliamo failo dydis virÅ¡ija MAX_FILE_SIZE nustatymÄ…, kuris naudojamas HTML formoje.", +"The uploaded file was only partially uploaded" => "Failas buvo įkeltas tik dalinai", +"No file was uploaded" => "Nebuvo įkeltas joks failas", "Contacts" => "Kontaktai", "This is not your addressbook." => "Tai ne jÅ«sų adresų knygelÄ—.", "Contact could not be found." => "Kontaktas nerastas", -"Address" => "Adresas", -"Telephone" => "Telefonas", -"Email" => "El. paÅ¡tas", -"Organization" => "Organizacija", "Work" => "Darbo", "Home" => "Namų", "Mobile" => "Mobilusis", "Text" => "ŽinuÄių", "Voice" => "Balso", +"Message" => "ŽinutÄ—", "Fax" => "Faksas", "Video" => "Vaizdo", "Pager" => "PraneÅ¡imų gaviklis", +"Internet" => "Internetas", +"Birthday" => "Gimtadienis", "Contact" => "Kontaktas", "Add Contact" => "PridÄ—ti kontaktÄ…", "Addressbooks" => "Adresų knygos", -"New Address Book" => "Nauja adresų knyga", -"CardDav Link" => "CardDAV nuoroda", -"Download" => "Atsisiųsti", -"Edit" => "Keisti", +"Organization" => "Organizacija", "Delete" => "Trinti", +"Nickname" => "Slapyvardis", +"Enter nickname" => "Ä®veskite slapyvardį", +"Phone" => "Telefonas", +"Email" => "El. paÅ¡tas", +"Address" => "Adresas", "Download contact" => "Atsisųsti kontaktÄ…", "Delete contact" => "IÅ¡trinti kontaktÄ…", -"Birthday" => "Gimtadienis", -"Phone" => "Telefonas", "Type" => "Tipas", "PO Box" => "PaÅ¡to dėžutÄ—", -"Street" => "GatvÄ—", "City" => "Miestas", "Region" => "Regionas", "Zipcode" => "PaÅ¡to indeksas", "Country" => "Å alis", -"Add" => "PridÄ—ti", "Addressbook" => "Adresų knyga", -"New Addressbook" => "Nauja adresų knyga", -"Edit Addressbook" => "Redaguoti adresų knygÄ…", -"Displayname" => "Rodomas vardas", -"Active" => "Aktyvus", +"Download" => "Atsisiųsti", +"Edit" => "Keisti", +"New Address Book" => "Nauja adresų knyga", "Save" => "IÅ¡saugoti", "Cancel" => "AtÅ¡aukti" ); diff --git a/apps/contacts/l10n/mk.php b/apps/contacts/l10n/mk.php index 2b81a5d877e..dbdd633e519 100644 --- a/apps/contacts/l10n/mk.php +++ b/apps/contacts/l10n/mk.php @@ -1,39 +1,39 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "Грешка (де)активирање на адреÑарот.", -"There was an error adding the contact." => "Имаше грешка при додавање на контактот.", -"Cannot add empty property." => "Ðеможе да Ñе додаде празна вредноÑÑ‚.", -"At least one of the address fields has to be filled out." => "Барем една од полињата за адреÑа треба да биде пополнето.", -"Trying to add duplicate property: " => "Се обидовте да внеÑете дупликат вредноÑÑ‚:", -"Error adding contact property." => "Грешка при додавање на вредноÑÑ‚ за контактот.", +"id is not set." => "ид не е поÑтавено.", +"Cannot update addressbook with an empty name." => "Ðеможе да Ñе ажурира адреÑар Ñо празно име.", +"Error updating addressbook." => "Грешка при ажурирање на адреÑарот.", "No ID provided" => "Ðема доÑтавено ИД", "Error setting checksum." => "Грешка во поÑтавување Ñума за проверка.", "No categories selected for deletion." => "Ðема избрано категории за бришење.", "No address books found." => "Ðе Ñе најдени адреÑари.", "No contacts found." => "Ðе Ñе најдени контакти.", +"There was an error adding the contact." => "Имаше грешка при додавање на контактот.", +"element name is not set." => "име за елементот не е поÑтавена.", +"Cannot add empty property." => "Ðеможе да Ñе додаде празна вредноÑÑ‚.", +"At least one of the address fields has to be filled out." => "Барем една од полињата за адреÑа треба да биде пополнето.", +"Trying to add duplicate property: " => "Се обидовте да внеÑете дупликат вредноÑÑ‚:", +"Information about vCard is incorrect. Please reload the page." => "Информацијата за vCard не е точна. Ве молам превчитајте ја Ñтраницава.", "Missing ID" => "ÐедоÑтаÑува ИД", "Error parsing VCard for ID: \"" => "Грешка при парÑирање VCard за ИД: \"", -"Cannot add addressbook with an empty name." => "Ðеможе да Ñе внеÑе адреÑар Ñо празно име.", -"Error adding addressbook." => "Грешки при додавање на адреÑарот.", -"Error activating addressbook." => "Грешка при активирање на адреÑарот.", +"checksum is not set." => "Ñумата за проверка не е поÑтавена.", +"Information about vCard is incorrect. Please reload the page: " => "Информацијата за vCard не е точна. Ве молам превчитајте ја Ñтраницава:", +"Something went FUBAR. " => "Ðешто Ñе раÑипа.", "No contact ID was submitted." => "Ðе беше доÑтавено ИД за контакт.", "Error reading contact photo." => "Грешка во читање на контакт фотографија.", "Error saving temporary file." => "Грешка во Ñнимање на привремена датотека.", "The loading photo is not valid." => "Фотографијата која Ñе вчитува е невалидна.", -"id is not set." => "ид не е поÑтавено.", -"Information about vCard is incorrect. Please reload the page." => "Информацијата за vCard не е точна. Ве молам превчитајте ја Ñтраницава.", -"Error deleting contact property." => "Греш при бришење на вредноÑта за контакт.", "Contact ID is missing." => "ИД за контакт недоÑтаÑува.", -"Missing contact id." => "ÐедоÑтаÑува ид за контакт.", "No photo path was submitted." => "Ðе беше поднеÑена патека за фотографија.", "File doesn't exist:" => "Ðе поÑтои датотеката:", "Error loading image." => "Грешка во вчитување на Ñлика.", -"element name is not set." => "име за елементот не е поÑтавена.", -"checksum is not set." => "Ñумата за проверка не е поÑтавена.", -"Information about vCard is incorrect. Please reload the page: " => "Информацијата за vCard не е точна. Ве молам превчитајте ја Ñтраницава:", -"Something went FUBAR. " => "Ðешто Ñе раÑипа.", -"Error updating contact property." => "Грешка при ажурирање на вредноÑта за контакт.", -"Cannot update addressbook with an empty name." => "Ðеможе да Ñе ажурира адреÑар Ñо празно име.", -"Error updating addressbook." => "Грешка при ажурирање на адреÑарот.", +"Error getting contact object." => "Грешка при преземањето на контакт објектот,", +"Error getting PHOTO property." => "Грешка при утврдувањето на карактериÑтиките на фотографијата.", +"Error saving contact." => "Грешка при Ñнимање на контактите.", +"Error resizing image" => "Грешка при Ñкалирање на фотографијата", +"Error cropping image" => "Грешка при Ñечење на фотографијата", +"Error creating temporary image" => "Грешка при креирањето на привремената фотографија", +"Error finding image: " => "Грешка при наоѓањето на фотографијата:", "Error uploading contacts to storage." => "Грешка во Ñнимање на контактите на диÑк.", "There is no error, the file uploaded with success" => "Датотеката беше уÑпешно подигната.", "The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Големината на датотеката ја надминува upload_max_filesize директивата во php.ini", @@ -41,15 +41,26 @@ "The uploaded file was only partially uploaded" => "Датотеката беше Ñамо делумно подигната.", "No file was uploaded" => "Ðе беше подигната датотека.", "Missing a temporary folder" => "ÐедоÑтаÑува привремена папка", +"Couldn't save temporary image: " => "Ðе можеше да Ñе Ñними привремената фотографија:", +"Couldn't load temporary image: " => "Ðе можеше да Ñе вчита привремената фотографија:", +"No file was uploaded. Unknown error" => "Ðиту еден фајл не Ñе вчита. Ðепозната грешка", "Contacts" => "Контакти", -"Drop a VCF file to import contacts." => "Довлечкај VCF датотека да Ñе внеÑат контакти.", -"Addressbook not found." => "ÐдреÑарот не е најден.", +"Sorry, this functionality has not been implemented yet" => "Жалам, оваа функционалноÑÑ‚ уште не е имплементирана", +"Not implemented" => "Ðе е имплементирано", +"Couldn't get a valid address." => "Ðе можев да добијам иÑправна адреÑа.", +"Error" => "Грешка", +"This property has to be non-empty." => "СвојÑтвото не Ñмее да биде празно.", +"Couldn't serialize elements." => "Ðе може да Ñе Ñеријализираат елементите.", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "'deleteProperty' повикан без тип на аргументот. Пријавете грешка/проблем на bugs.owncloud.org", +"Edit name" => "Уреди го името", +"No files selected for upload." => "Ðиту еден фајл не е избран за вчитување.", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "Датотеката која Ñе обидувате да ја префрлите ја надминува макÑималната големина дефинирана за Ð¿Ñ€ÐµÐ½Ð¾Ñ Ð½Ð° овој Ñервер.", +"Select type" => "Одбери тип", +"Result: " => "Резултат: ", +" imported, " => "увезено,", +" failed." => "неуÑпешно.", "This is not your addressbook." => "Ова не е во Вашиот адреÑар.", "Contact could not be found." => "Контактот неможе да биде најден.", -"Address" => "ÐдреÑа", -"Telephone" => "Телефон", -"Email" => "Е-пошта", -"Organization" => "Организација", "Work" => "Работа", "Home" => "Дома", "Mobile" => "Мобилен", @@ -60,25 +71,24 @@ "Video" => "Видео", "Pager" => "Пејџер", "Internet" => "Интернет", +"Birthday" => "Роденден", "{name}'s Birthday" => "Роденден на {name}", "Contact" => "Контакт", "Add Contact" => "Додади контакт", +"Import" => "ВнеÑи", "Addressbooks" => "ÐдреÑари", -"Configure Address Books" => "Конфигурирај адреÑар", -"New Address Book" => "Ðов адреÑар", -"Import from VCF" => "ВнеÑи од VCF", -"CardDav Link" => "Ð’Ñ€Ñка за CardDav", -"Download" => "Преземи", -"Edit" => "Уреди", -"Delete" => "Избриши", -"Download contact" => "Преземи го контактот", -"Delete contact" => "Избриши го контактот", +"Close" => "Затвои", "Drop photo to upload" => "Довлечкај фотографија за да Ñе подигне", +"Delete current photo" => "Избриши моментална фотографија", +"Edit current photo" => "Уреди моментална фотографија", +"Upload new photo" => "Подигни нова фотографија", +"Select photo from ownCloud" => "Изберете фотографија од ownCloud", "Format custom, Short name, Full name, Reverse or Reverse with comma" => "Прилагоден формат, кратко име, цело име, обратно или обратно Ñо запирка", "Edit name details" => "Уреди детали за име", +"Organization" => "Организација", +"Delete" => "Избриши", "Nickname" => "Прекар", "Enter nickname" => "ВнеÑи прекар", -"Birthday" => "Роденден", "dd-mm-yyyy" => "dd-mm-yyyy", "Groups" => "Групи", "Separate groups with commas" => "Одвоете ги групите Ñо запирка", @@ -94,24 +104,21 @@ "Edit address details" => "Уреди детали за адреÑа", "Add notes here." => "ВнеÑете забелешки тука.", "Add field" => "Додади поле", -"Profile picture" => "Фотографија за профил", "Phone" => "Телефон", +"Email" => "Е-пошта", +"Address" => "ÐдреÑа", "Note" => "Забелешка", -"Delete current photo" => "Избриши моментална фотографија", -"Edit current photo" => "Уреди моментална фотографија", -"Upload new photo" => "Подигни нова фотографија", -"Select photo from ownCloud" => "Изберете фотографија од ownCloud", +"Download contact" => "Преземи го контактот", +"Delete contact" => "Избриши го контактот", +"The temporary image has been removed from cache." => "Привремената Ñлика е отÑтранета од кешот.", "Edit address" => "Уреди адреÑа", "Type" => "Тип", "PO Box" => "ПоштенÑки фах", "Extended" => "Дополнително", -"Street" => "Улица", "City" => "Град", "Region" => "Регион", "Zipcode" => "ПоштенÑки код", "Country" => "Држава", -"Edit categories" => "Уреди категории", -"Add" => "Додади", "Addressbook" => "ÐдреÑар", "Hon. prefixes" => "ПрефикÑи за титула", "Miss" => "Г-ца", @@ -132,26 +139,26 @@ "Esq." => "Esq.", "Jr." => "Помлад.", "Sn." => "ПоÑтар.", -"New Addressbook" => "Ðов адреÑар", -"Edit Addressbook" => "Уреди адреÑар", -"Displayname" => "Прикажано име", -"Active" => "Ðктивно", -"Save" => "Сними", -"Submit" => "Прати", -"Cancel" => "Откажи", "Import a contacts file" => "ВнеÑи датотека Ñо контакти", "Please choose the addressbook" => "Ве молам изберете адреÑар", "create a new addressbook" => "креирај нов адреÑар", "Name of new addressbook" => "Име на новиот адреÑар", -"Import" => "ВнеÑи", "Importing contacts" => "ВнеÑување контакти", +"Contacts imported successfully" => "Контаките беа внеÑени уÑпешно", +"Close Dialog" => "Дијалог за затварање", +"Import Addressbook" => "ВнеÑи адреÑар", "Select address book to import to:" => "Изберете адреÑар да Ñе внеÑе:", +"Drop a VCF file to import contacts." => "Довлечкај VCF датотека да Ñе внеÑат контакти.", "Select from HD" => "Изберете од хард диÑк", "You have no contacts in your addressbook." => "Ðемате контакти во Вашиот адреÑар.", "Add contact" => "Додади контакт", -"Configure addressbooks" => "Уреди адреÑари", "CardDAV syncing addresses" => "ÐдреÑа за Ñинхронизација Ñо CardDAV", "more info" => "повеќе информации", "Primary address (Kontact et al)" => "Примарна адреÑа", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Download" => "Преземи", +"Edit" => "Уреди", +"New Address Book" => "Ðов адреÑар", +"Save" => "Сними", +"Cancel" => "Откажи" ); diff --git a/apps/contacts/l10n/ms_MY.php b/apps/contacts/l10n/ms_MY.php index 7d8a4c7d124..3fce9eae5a3 100644 --- a/apps/contacts/l10n/ms_MY.php +++ b/apps/contacts/l10n/ms_MY.php @@ -1,58 +1,178 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "Ralat nyahaktif buku alamat.", +"id is not set." => "ID tidak ditetapkan.", +"Cannot update addressbook with an empty name." => "Tidak boleh kemaskini buku alamat dengan nama yang kosong.", +"Error updating addressbook." => "Masalah mengemaskini buku alamat.", +"No ID provided" => "tiada ID diberi", +"Error setting checksum." => "Ralat menetapkan checksum.", +"No categories selected for deletion." => "Tiada kategori dipilih untuk dibuang.", +"No address books found." => "Tiada buku alamat dijumpai.", +"No contacts found." => "Tiada kenalan dijumpai.", "There was an error adding the contact." => "Terdapat masalah menambah maklumat.", +"element name is not set." => "nama elemen tidak ditetapkan.", "Cannot add empty property." => "Tidak boleh menambah ruang kosong.", "At least one of the address fields has to be filled out." => "Sekurangnya satu ruangan alamat perlu diisikan.", -"Error adding contact property." => "Terdapat masalah menambah maklumat.", -"Error adding addressbook." => "Masalah menambah buku alamat.", -"Error activating addressbook." => "Masalah mengaktifkan buku alamat.", +"Trying to add duplicate property: " => "Cuba untuk letak nilai duplikasi:", "Information about vCard is incorrect. Please reload the page." => "Maklumat vCard tidak tepat. Sila reload semula halaman ini.", -"Error deleting contact property." => "Masalah memadam maklumat.", -"Error updating contact property." => "Masalah mengemaskini maklumat.", -"Error updating addressbook." => "Masalah mengemaskini buku alamat.", +"Missing ID" => "ID Hilang", +"Error parsing VCard for ID: \"" => "Ralat VCard untuk ID: \"", +"checksum is not set." => "checksum tidak ditetapkan.", +"Information about vCard is incorrect. Please reload the page: " => "Maklumat tentang vCard tidak betul.", +"Something went FUBAR. " => "Sesuatu tidak betul.", +"No contact ID was submitted." => "Tiada ID kenalan yang diberi.", +"Error reading contact photo." => "Ralat pada foto kenalan.", +"Error saving temporary file." => "Ralat menyimpan fail sementara", +"The loading photo is not valid." => "Foto muatan tidak sah.", +"Contact ID is missing." => "ID Kenalan telah hilang.", +"No photo path was submitted." => "Tiada direktori gambar yang diberi.", +"File doesn't exist:" => "Fail tidak wujud:", +"Error loading image." => "Ralat pada muatan imej.", +"Error getting contact object." => "Ralat mendapatkan objek pada kenalan.", +"Error getting PHOTO property." => "Ralat mendapatkan maklumat gambar.", +"Error saving contact." => "Ralat menyimpan kenalan.", +"Error resizing image" => "Ralat mengubah saiz imej", +"Error cropping image" => "Ralat memotong imej", +"Error creating temporary image" => "Ralat mencipta imej sementara", +"Error finding image: " => "Ralat mencari imej: ", +"Error uploading contacts to storage." => "Ralat memuatnaik senarai kenalan.", +"There is no error, the file uploaded with success" => "Tiada ralat berlaku, fail berjaya dimuatnaik", +"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Saiz fail yang dimuatnaik melebihi upload_max_filesize yang ditetapkan dalam php.ini", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Saiz fail yang dimuatnaik melebihi MAX_FILE_SIZE yang ditetapkan dalam borang HTML", +"The uploaded file was only partially uploaded" => "Fail yang dimuatnaik tidak lengkap", +"No file was uploaded" => "Tiada fail dimuatnaik", +"Missing a temporary folder" => "Direktori sementara hilang", +"Couldn't save temporary image: " => "Tidak boleh menyimpan imej sementara: ", +"Couldn't load temporary image: " => "Tidak boleh membuka imej sementara: ", +"No file was uploaded. Unknown error" => "Tiada fail dimuatnaik. Ralat tidak diketahui.", "Contacts" => "Hubungan-hubungan", +"Sorry, this functionality has not been implemented yet" => "Maaf, fungsi ini masih belum boleh diguna lagi", +"Not implemented" => "Tidak digunakan", +"Couldn't get a valid address." => "Tidak boleh mendapat alamat yang sah.", +"Error" => "Ralat", +"This property has to be non-empty." => "Nilai ini tidak boleh kosong.", +"Couldn't serialize elements." => "Tidak boleh menggabungkan elemen.", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "'deleteProperty' dipanggil tanpa argumen taip. Sila maklumkan di bugs.owncloud.org", +"Edit name" => "Ubah nama", +"No files selected for upload." => "Tiada fail dipilih untuk muatnaik.", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "Fail yang ingin dimuatnaik melebihi saiz yang dibenarkan.", +"Select type" => "PIlih jenis", +"Result: " => "Hasil: ", +" imported, " => " import, ", +" failed." => " gagal.", +"Addressbook not found: " => "Buku alamat tidak ditemui:", "This is not your addressbook." => "Ini bukan buku alamat anda.", "Contact could not be found." => "Hubungan tidak dapat ditemui", -"Address" => "Alamat", -"Telephone" => "Telefon", -"Email" => "Emel", -"Organization" => "Organisasi", "Work" => "Kerja", "Home" => "Rumah", +"Other" => "Lain", "Mobile" => "Mudah alih", "Text" => "Teks", "Voice" => "Suara", +"Message" => "Mesej", "Fax" => "Fax", "Video" => "Video", "Pager" => "Alat Kelui", +"Internet" => "Internet", +"Birthday" => "Hari lahir", +"Business" => "Perniagaan", +"Clients" => "klien", +"Holidays" => "Hari kelepasan", +"Ideas" => "Idea", +"Journey" => "Perjalanan", +"Jubilee" => "Jubli", +"Meeting" => "Mesyuarat", +"Personal" => "Peribadi", +"Projects" => "Projek", +"{name}'s Birthday" => "Hari Lahir {name}", "Contact" => "Hubungan", "Add Contact" => "Tambah kenalan", +"Import" => "Import", +"Settings" => "Tetapan", "Addressbooks" => "Senarai Buku Alamat", -"New Address Book" => "Buku Alamat Baru", -"CardDav Link" => "Sambungan CardDav", -"Download" => "Muat naik", -"Edit" => "Sunting", +"Close" => "Tutup", +"Next addressbook" => "Buku alamat seterusnya", +"Previous addressbook" => "Buku alamat sebelumnya", +"Drop photo to upload" => "Letak foto disini untuk muatnaik", +"Delete current photo" => "Padam foto semasa", +"Edit current photo" => "Ubah foto semasa", +"Upload new photo" => "Muatnaik foto baru", +"Select photo from ownCloud" => "Pilih foto dari ownCloud", +"Format custom, Short name, Full name, Reverse or Reverse with comma" => "Format bebas, Nama pendek, Nama penuh, Unduran dengan koma", +"Edit name details" => "Ubah butiran nama", +"Organization" => "Organisasi", "Delete" => "Padam", -"Download contact" => "Muat turun hubungan", -"Delete contact" => "Padam hubungan", -"Birthday" => "Hari lahir", +"Nickname" => "Nama Samaran", +"Enter nickname" => "Masukkan nama samaran", +"dd-mm-yyyy" => "dd-mm-yyyy", +"Groups" => "Kumpulan", +"Separate groups with commas" => "Asingkan kumpulan dengan koma", +"Edit groups" => "Ubah kumpulan", "Preferred" => "Pilihan", +"Please specify a valid email address." => "Berikan alamat emel yang sah.", +"Enter email address" => "Masukkan alamat emel", +"Mail to address" => "Hantar ke alamat", +"Delete email address" => "Padam alamat emel", +"Enter phone number" => "Masukkan nombor telefon", +"Delete phone number" => "Padam nombor telefon", +"View on map" => "Lihat pada peta", +"Edit address details" => "Ubah butiran alamat", +"Add notes here." => "Letak nota disini.", +"Add field" => "Letak ruangan", "Phone" => "Telefon", +"Email" => "Emel", +"Address" => "Alamat", +"Note" => "Nota", +"Download contact" => "Muat turun hubungan", +"Delete contact" => "Padam hubungan", +"The temporary image has been removed from cache." => "Imej sementara telah dibuang dari cache.", +"Edit address" => "Ubah alamat", "Type" => "Jenis", "PO Box" => "Peti surat", "Extended" => "Sambungan", -"Street" => "Jalan", "City" => "bandar", "Region" => "Wilayah", "Zipcode" => "Poskod", "Country" => "Negara", -"Add" => "Tambah", "Addressbook" => "Buku alamat", -"New Addressbook" => "Buku Alamat Baru", -"Edit Addressbook" => "Kemaskini Buku Alamat", -"Displayname" => "Paparan nama", -"Active" => "Aktif", +"Hon. prefixes" => "Awalan nama", +"Miss" => "Cik", +"Ms" => "Cik", +"Mr" => "Encik", +"Sir" => "Tuan", +"Mrs" => "Puan", +"Dr" => "Dr", +"Given name" => "Nama diberi", +"Additional names" => "Nama tambahan", +"Family name" => "Nama keluarga", +"Hon. suffixes" => "Awalan nama", +"J.D." => "J.D.", +"M.D." => "M.D.", +"D.O." => "D.O.", +"D.C." => "D.C.", +"Ph.D." => "Ph.D.", +"Esq." => "Esq.", +"Jr." => "Jr.", +"Sn." => "Sn.", +"Import a contacts file" => "Import fail kenalan", +"Please choose the addressbook" => "Sila pilih buku alamat", +"create a new addressbook" => "Cipta buku alamat baru", +"Name of new addressbook" => "Nama buku alamat", +"Importing contacts" => "Import senarai kenalan", +"You have no contacts in your addressbook." => "Anda tidak mempunyai sebarang kenalan didalam buku alamat.", +"Add contact" => "Letak kenalan", +"Select Address Books" => "Pilih Buku Alamat", +"Enter name" => "Masukkan nama", +"Enter description" => "Masukkan keterangan", +"CardDAV syncing addresses" => "alamat selarian CardDAV", +"more info" => "maklumat lanjut", +"Primary address (Kontact et al)" => "Alamat utama", +"iOS/OS X" => "iOS/OS X", +"Download" => "Muat naik", +"Edit" => "Sunting", +"New Address Book" => "Buku Alamat Baru", +"Name" => "Nama", +"Description" => "Keterangan", "Save" => "Simpan", -"Submit" => "Hantar", -"Cancel" => "Batal" +"Cancel" => "Batal", +"More..." => "Lagi..." ); diff --git a/apps/contacts/l10n/nb_NO.php b/apps/contacts/l10n/nb_NO.php index 3f7731bede8..5f7c49c8b98 100644 --- a/apps/contacts/l10n/nb_NO.php +++ b/apps/contacts/l10n/nb_NO.php @@ -1,45 +1,51 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "Et problem oppsto med Ã¥ (de)aktivere adresseboken.", -"There was an error adding the contact." => "Et problem oppsto med Ã¥ legge til kontakten.", -"Cannot add empty property." => "Kan ikke legge til tomt felt.", -"At least one of the address fields has to be filled out." => "Minst en av adressefeltene mÃ¥ oppgis.", -"Error adding contact property." => "Et problem oppsto med Ã¥ legge til kontaktfeltet.", +"id is not set." => "id er ikke satt.", +"Cannot update addressbook with an empty name." => "Kan ikke oppdatere adressebøker uten navn.", +"Error updating addressbook." => "Et problem oppsto med Ã¥ oppdatere adresseboken.", "No ID provided" => "Ingen ID angitt", "No categories selected for deletion." => "Ingen kategorier valgt for sletting.", "No address books found." => "Ingen adressebok funnet.", "No contacts found." => "Ingen kontakter funnet.", +"There was an error adding the contact." => "Et problem oppsto med Ã¥ legge til kontakten.", +"Cannot add empty property." => "Kan ikke legge til tomt felt.", +"At least one of the address fields has to be filled out." => "Minst en av adressefeltene mÃ¥ oppgis.", +"Information about vCard is incorrect. Please reload the page." => "Informasjonen om vCard-filen er ikke riktig. Last inn siden pÃ¥ nytt.", "Missing ID" => "Manglende ID", -"Cannot add addressbook with an empty name." => "Kan ikke legge til en adressebok uten navn.", -"Error adding addressbook." => "Et problem oppsto med Ã¥ legge til adresseboken.", -"Error activating addressbook." => "Et problem oppsto med Ã¥ aktivere adresseboken.", +"Something went FUBAR. " => "Noe gikk fryktelig galt.", "Error reading contact photo." => "Klarte ikke Ã¥ lese kontaktbilde.", "Error saving temporary file." => "Klarte ikke Ã¥ lagre midlertidig fil.", "The loading photo is not valid." => "Bildet som lastes inn er ikke gyldig.", -"id is not set." => "id er ikke satt.", -"Information about vCard is incorrect. Please reload the page." => "Informasjonen om vCard-filen er ikke riktig. Last inn siden pÃ¥ nytt.", -"Error deleting contact property." => "Et problem oppsto med Ã¥ fjerne kontaktfeltet.", -"Missing contact id." => "Mangler kontakt-id.", +"Contact ID is missing." => "Kontakt-ID mangler.", "No photo path was submitted." => "Ingen filsti ble lagt inn.", "File doesn't exist:" => "Filen eksisterer ikke:", "Error loading image." => "Klarte ikke Ã¥ laste bilde.", -"Something went FUBAR. " => "Noe gikk fryktelig galt.", -"Error updating contact property." => "Et problem oppsto med Ã¥ legge til kontaktfeltet.", -"Cannot update addressbook with an empty name." => "Kan ikke oppdatere adressebøker uten navn.", -"Error updating addressbook." => "Et problem oppsto med Ã¥ oppdatere adresseboken.", +"Error saving contact." => "Klarte ikke Ã¥ lagre kontakt.", +"Error resizing image" => "Klarte ikke Ã¥ endre størrelse pÃ¥ bildet", +"Error cropping image" => "Klarte ikke Ã¥ beskjære bildet", +"Error creating temporary image" => "Klarte ikke Ã¥ lage et midlertidig bilde", +"Error finding image: " => "Kunne ikke finne bilde:", +"Error uploading contacts to storage." => "Klarte ikke Ã¥ laste opp kontakter til lagringsplassen", "There is no error, the file uploaded with success" => "Pust ut, ingen feil. Filen ble lastet opp problemfritt", "The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Filen du prøvde Ã¥ laste opp var større enn grensen upload_max_filesize i php.ini", "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Filen du prøvde Ã¥ laste opp var større enn grensen satt i MAX_FILE_SIZE i HTML-skjemaet.", "The uploaded file was only partially uploaded" => "Filen du prøvde Ã¥ laste opp ble kun delvis lastet opp", "No file was uploaded" => "Ingen filer ble lastet opp", "Missing a temporary folder" => "Mangler midlertidig mappe", +"Couldn't save temporary image: " => "Kunne ikke lagre midlertidig bilde:", +"Couldn't load temporary image: " => "Kunne ikke laste midlertidig bilde:", +"No file was uploaded. Unknown error" => "Ingen filer ble lastet opp. Ukjent feil.", "Contacts" => "Kontakter", -"Addressbook not found." => "Adresseboken ble ikke funnet.", +"Error" => "Feil", +"Edit name" => "Endre navn", +"No files selected for upload." => "Ingen filer valgt for opplasting.", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "Filen du prøver Ã¥ laste opp er for stor.", +"Select type" => "Velg type", +"Result: " => "Resultat:", +" imported, " => "importert,", +" failed." => "feilet.", "This is not your addressbook." => "Dette er ikke dine adressebok.", "Contact could not be found." => "Kontakten ble ikke funnet.", -"Address" => "Adresse", -"Telephone" => "Telefon", -"Email" => "E-post", -"Organization" => "Organisasjon", "Work" => "Arbeid", "Home" => "Hjem", "Mobile" => "Mobil", @@ -50,24 +56,23 @@ "Video" => "Video", "Pager" => "Pager", "Internet" => "Internett", -"{name}'s Birthday" => "bursdagen til {name}", +"Birthday" => "Bursdag", +"{name}'s Birthday" => "{name}s bursdag", "Contact" => "Kontakt", "Add Contact" => "Ny kontakt", +"Import" => "Importer", "Addressbooks" => "Adressebøker", -"Configure Address Books" => "Konfigurer adressebok", -"New Address Book" => "Ny adressebok", -"Import from VCF" => "Importer fra VDF", -"CardDav Link" => "CardDAV-lenke", -"Download" => "Hent ned", -"Edit" => "Rediger", -"Delete" => "Slett", -"Download contact" => "Hend ned kontakten", -"Delete contact" => "Slett kontakt", +"Close" => "Lukk", "Drop photo to upload" => "Dra bilder hit for Ã¥ laste opp", +"Delete current photo" => "Fjern nÃ¥værende bilde", +"Edit current photo" => "Rediger nÃ¥værende bilde", +"Upload new photo" => "Last opp nytt bilde", +"Select photo from ownCloud" => "Velg bilde fra ownCloud", "Edit name details" => "Endre detaljer rundt navn", +"Organization" => "Organisasjon", +"Delete" => "Slett", "Nickname" => "Kallenavn", "Enter nickname" => "Skriv inn kallenavn", -"Birthday" => "Bursdag", "dd-mm-yyyy" => "dd-mm-åååå", "Groups" => "Grupper", "Separate groups with commas" => "Skill gruppene med komma", @@ -83,25 +88,23 @@ "Edit address details" => "Endre detaljer rundt adresse", "Add notes here." => "Legg inn notater her.", "Add field" => "Legg til felt", -"Profile picture" => "Profilbilde", "Phone" => "Telefon", +"Email" => "E-post", +"Address" => "Adresse", "Note" => "Notat", -"Delete current photo" => "Fjern nÃ¥værende bilde", -"Edit current photo" => "Rediger nÃ¥værende bilde", -"Upload new photo" => "Last opp nytt bilde", -"Select photo from ownCloud" => "Velg bilde fra ownCloud", +"Download contact" => "Hend ned kontakten", +"Delete contact" => "Slett kontakt", +"The temporary image has been removed from cache." => "Det midlertidige bildet er fjernet fra cache.", "Edit address" => "Endre adresse", "Type" => "Type", "PO Box" => "Postboks", "Extended" => "Utvidet", -"Street" => "Gate", "City" => "By", "Region" => "OmrÃ¥det", "Zipcode" => "Postnummer", "Country" => "Land", -"Edit categories" => "Endre kategorier", -"Add" => "Ny", "Addressbook" => "Adressebok", +"Hon. prefixes" => "Ærestitler", "Miss" => "Frøken", "Mr" => "Herr", "Mrs" => "Fru", @@ -118,18 +121,21 @@ "Displayname" => "Visningsnavn", "Active" => "Aktiv", "Save" => "Lagre", -"Submit" => "Lagre", +"Submit" => "Send inn", "Cancel" => "Avbryt", "Import a contacts file" => "Importer en fil med kontakter.", "Please choose the addressbook" => "Vennligst velg adressebok", "create a new addressbook" => "Lag ny adressebok", "Name of new addressbook" => "Navn pÃ¥ ny adressebok", -"Import" => "Importer", "Importing contacts" => "Importerer kontakter", "You have no contacts in your addressbook." => "Du har ingen kontakter i din adressebok", "Add contact" => "Ny kontakt", -"Configure addressbooks" => "Konfigurer adressebøker", "CardDAV syncing addresses" => "Synkroniseringsadresse for CardDAV", "more info" => "mer info", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Download" => "Hent ned", +"Edit" => "Rediger", +"New Address Book" => "Ny adressebok", +"Save" => "Lagre", +"Cancel" => "Avbryt" ); diff --git a/apps/contacts/l10n/nl.php b/apps/contacts/l10n/nl.php index fd7e50ba4d5..bcf93c506a9 100644 --- a/apps/contacts/l10n/nl.php +++ b/apps/contacts/l10n/nl.php @@ -1,39 +1,32 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "Fout bij het (de)activeren van het adresboek.", -"There was an error adding the contact." => "Er was een fout bij het toevoegen van het contact.", -"Cannot add empty property." => "Kan geen lege eigenschap toevoegen.", -"At least one of the address fields has to be filled out." => "Minstens één van de adresvelden moet ingevuld worden.", -"Trying to add duplicate property: " => "Eigenschap bestaat al: ", -"Error adding contact property." => "Fout bij het toevoegen van de contacteigenschap.", +"id is not set." => "id is niet ingesteld.", +"Cannot update addressbook with an empty name." => "Kan adresboek zonder naam niet wijzigen", +"Error updating addressbook." => "Fout bij het updaten van het adresboek.", "No ID provided" => "Geen ID opgegeven", "Error setting checksum." => "Instellen controlegetal mislukt", "No categories selected for deletion." => "Geen categorieën geselecteerd om te verwijderen.", "No address books found." => "Geen adresboek gevonden", "No contacts found." => "Geen contracten gevonden", +"There was an error adding the contact." => "Er was een fout bij het toevoegen van het contact.", +"element name is not set." => "onderdeel naam is niet opgegeven.", +"Cannot add empty property." => "Kan geen lege eigenschap toevoegen.", +"At least one of the address fields has to be filled out." => "Minstens één van de adresvelden moet ingevuld worden.", +"Trying to add duplicate property: " => "Eigenschap bestaat al: ", +"Information about vCard is incorrect. Please reload the page." => "Informatie over de vCard is onjuist. Herlaad de pagina.", "Missing ID" => "Ontbrekend ID", "Error parsing VCard for ID: \"" => "Fout bij inlezen VCard voor ID: \"", -"Cannot add addressbook with an empty name." => "Kan geen adresboek toevoegen zonder naam.", -"Error adding addressbook." => "Fout bij het toevoegen van het adresboek.", -"Error activating addressbook." => "Fout bij het activeren van het adresboek.", +"checksum is not set." => "controlegetal is niet opgegeven.", +"Information about vCard is incorrect. Please reload the page: " => "Informatie over vCard is fout. Herlaad de pagina: ", +"Something went FUBAR. " => "Er ging iets totaal verkeerd. ", "No contact ID was submitted." => "Geen contact ID opgestuurd.", "Error reading contact photo." => "Lezen van contact foto mislukt.", "Error saving temporary file." => "Tijdelijk bestand opslaan mislukt.", "The loading photo is not valid." => "De geladen foto is niet goed.", -"id is not set." => "id is niet ingesteld.", -"Information about vCard is incorrect. Please reload the page." => "Informatie over de vCard is onjuist. Herlaad de pagina.", -"Error deleting contact property." => "Fout bij het verwijderen van de contacteigenschap.", "Contact ID is missing." => "Contact ID ontbreekt.", -"Missing contact id." => "Ontbrekende contact id.", "No photo path was submitted." => "Geen fotopad opgestuurd.", "File doesn't exist:" => "Bestand bestaat niet:", "Error loading image." => "Fout bij laden plaatje.", -"element name is not set." => "onderdeel naam is niet opgegeven.", -"checksum is not set." => "controlegetal is niet opgegeven.", -"Information about vCard is incorrect. Please reload the page: " => "Informatie over vCard is fout. Herlaad de pagina: ", -"Something went FUBAR. " => "Er ging iets totaal verkeerd. ", -"Error updating contact property." => "Fout bij het updaten van de contacteigenschap.", -"Cannot update addressbook with an empty name." => "Kan adresboek zonder naam niet wijzigen", -"Error updating addressbook." => "Fout bij het updaten van het adresboek.", "Error uploading contacts to storage." => "Fout bij opslaan van contacten.", "There is no error, the file uploaded with success" => "De upload van het bestand is goedgegaan.", "The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Het bestand overschrijdt de upload_max_filesize instelling in php.ini", @@ -42,14 +35,8 @@ "No file was uploaded" => "Er is geen bestand geüpload", "Missing a temporary folder" => "Er ontbreekt een tijdelijke map", "Contacts" => "Contacten", -"Drop a VCF file to import contacts." => "Sleep een VCF bestand om de contacten te importeren.", -"Addressbook not found." => "Adresboek niet gevonden.", "This is not your addressbook." => "Dit is niet uw adresboek.", "Contact could not be found." => "Contact kon niet worden gevonden.", -"Address" => "Adres", -"Telephone" => "Telefoon", -"Email" => "E-mail", -"Organization" => "Organisatie", "Work" => "Werk", "Home" => "Thuis", "Mobile" => "Mobiel", @@ -60,25 +47,23 @@ "Video" => "Video", "Pager" => "Pieper", "Internet" => "Internet", +"Birthday" => "Verjaardag", "{name}'s Birthday" => "{name}'s verjaardag", "Contact" => "Contact", "Add Contact" => "Contact toevoegen", +"Import" => "Importeer", "Addressbooks" => "Adresboeken", -"Configure Address Books" => "Instellen adresboeken", -"New Address Book" => "Nieuw Adresboek", -"Import from VCF" => "Importeer uit VCF", -"CardDav Link" => "CardDav Link", -"Download" => "Download", -"Edit" => "Bewerken", -"Delete" => "Verwijderen", -"Download contact" => "Download contact", -"Delete contact" => "Verwijder contact", "Drop photo to upload" => "Verwijder foto uit upload", +"Delete current photo" => "Verwijdere huidige foto", +"Edit current photo" => "Wijzig huidige foto", +"Upload new photo" => "Upload nieuwe foto", +"Select photo from ownCloud" => "Selecteer foto uit ownCloud", "Format custom, Short name, Full name, Reverse or Reverse with comma" => "Formateer aangepast, Korte naam, Volledige naam, Achteruit of Achteruit met komma", "Edit name details" => "Wijzig naam gegevens", +"Organization" => "Organisatie", +"Delete" => "Verwijderen", "Nickname" => "Roepnaam", "Enter nickname" => "Voer roepnaam in", -"Birthday" => "Verjaardag", "dd-mm-yyyy" => "dd-mm-yyyy", "Groups" => "Groepen", "Separate groups with commas" => "Gebruik komma bij meerder groepen", @@ -94,49 +79,45 @@ "Edit address details" => "Wijzig adres gegevens", "Add notes here." => "Voeg notitie toe", "Add field" => "Voeg veld toe", -"Profile picture" => "Profiel foto", "Phone" => "Telefoon", +"Email" => "E-mail", +"Address" => "Adres", "Note" => "Notitie", -"Delete current photo" => "Verwijdere huidige foto", -"Edit current photo" => "Wijzig huidige foto", -"Upload new photo" => "Upload nieuwe foto", -"Select photo from ownCloud" => "Selecteer foto uit ownCloud", +"Download contact" => "Download contact", +"Delete contact" => "Verwijder contact", "Edit address" => "Wijzig adres", "Type" => "Type", "PO Box" => "Postbus", "Extended" => "Uitgebreide", -"Street" => "Straat", "City" => "Stad", "Region" => "Regio", "Zipcode" => "Postcode", "Country" => "Land", -"Edit categories" => "Wijzig categorieën", -"Add" => "Voeg toe", "Addressbook" => "Adresboek", "Hon. prefixes" => "Hon. prefixes", "Given name" => "Voornaam", "Additional names" => "Extra namen", "Family name" => "Achternaam", -"New Addressbook" => "Nieuw adresboek", -"Edit Addressbook" => "Wijzig adresboek", -"Displayname" => "Weergavenaam", -"Active" => "Actief", -"Save" => "Opslaan", -"Submit" => "Opslaan", -"Cancel" => "Anuleren", "Import a contacts file" => "Importeer een contacten bestand", "Please choose the addressbook" => "Kies een adresboek", "create a new addressbook" => "Maak een nieuw adresboek", "Name of new addressbook" => "Naam van nieuw adresboek", -"Import" => "Importeer", "Importing contacts" => "Importeren van contacten", +"Contacts imported successfully" => "Contacten zijn geïmporteerd", +"Close Dialog" => "Sluit venster", +"Import Addressbook" => "Importeer adresboek", "Select address book to import to:" => "Selecteer adresboek voor import:", +"Drop a VCF file to import contacts." => "Sleep een VCF bestand om de contacten te importeren.", "Select from HD" => "Selecteer van schijf", "You have no contacts in your addressbook." => "Je hebt geen contacten in je adresboek", "Add contact" => "Contactpersoon toevoegen", -"Configure addressbooks" => "Bewerken adresboeken", "CardDAV syncing addresses" => "CardDAV synchroniseert de adressen", "more info" => "meer informatie", "Primary address (Kontact et al)" => "Standaardadres", -"iOS/OS X" => "IOS/OS X" +"iOS/OS X" => "IOS/OS X", +"Download" => "Download", +"Edit" => "Bewerken", +"New Address Book" => "Nieuw Adresboek", +"Save" => "Opslaan", +"Cancel" => "Anuleren" ); diff --git a/apps/contacts/l10n/nn_NO.php b/apps/contacts/l10n/nn_NO.php index 5b3fc5b1ab3..2e3ab16da3e 100644 --- a/apps/contacts/l10n/nn_NO.php +++ b/apps/contacts/l10n/nn_NO.php @@ -1,22 +1,13 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "Ein feil oppstod ved (de)aktivering av adressebok.", +"Error updating addressbook." => "Eit problem oppstod ved Ã¥ oppdatere adresseboka.", "There was an error adding the contact." => "Det kom ei feilmelding dÃ¥ kontakta vart lagt til.", "Cannot add empty property." => "Kan ikkje leggja til tomt felt.", "At least one of the address fields has to be filled out." => "Minst eit av adressefelta mÃ¥ fyllast ut.", -"Error adding contact property." => "Eit problem oppstod ved Ã¥ leggja til kontakteltet.", -"Error adding addressbook." => "Eit problem oppstod ved Ã¥ leggja til adresseboka.", -"Error activating addressbook." => "Eit problem oppstod ved aktivering av adresseboka.", "Information about vCard is incorrect. Please reload the page." => "Informasjonen om vCard-et er feil, ver venleg og last sida pÃ¥ nytt.", -"Error deleting contact property." => "Eit problem oppstod ved Ã¥ slette kontaktfeltet.", -"Error updating contact property." => "Eit problem oppstod ved Ã¥ endre kontaktfeltet.", -"Error updating addressbook." => "Eit problem oppstod ved Ã¥ oppdatere adresseboka.", "Contacts" => "Kotaktar", "This is not your addressbook." => "Dette er ikkje di adressebok.", "Contact could not be found." => "Fann ikkje kontakten.", -"Address" => "Adresse", -"Telephone" => "Telefonnummer", -"Email" => "Epost", -"Organization" => "Organisasjon", "Work" => "Arbeid", "Home" => "Heime", "Mobile" => "Mobil", @@ -25,34 +16,29 @@ "Fax" => "Faks", "Video" => "Video", "Pager" => "Personsøkjar", +"Birthday" => "Bursdag", "Contact" => "Kontakt", "Add Contact" => "Legg til kontakt", "Addressbooks" => "Adressebøker", -"New Address Book" => "Ny adressebok", -"CardDav Link" => "CardDav lenkje", -"Download" => "Last ned", -"Edit" => "Endra", +"Organization" => "Organisasjon", "Delete" => "Slett", -"Download contact" => "Last ned kontakt", -"Delete contact" => "Slett kontakt", -"Birthday" => "Bursdag", "Preferred" => "Føretrekt", "Phone" => "Telefonnummer", +"Email" => "Epost", +"Address" => "Adresse", +"Download contact" => "Last ned kontakt", +"Delete contact" => "Slett kontakt", "Type" => "Skriv", "PO Box" => "Postboks", "Extended" => "Utvida", -"Street" => "Gate", "City" => "Stad", "Region" => "Region/fylke", "Zipcode" => "Postnummer", "Country" => "Land", -"Add" => "Legg til", "Addressbook" => "Adressebok", -"New Addressbook" => "Ny adressebok", -"Edit Addressbook" => "Endre adressebok", -"Displayname" => "Visningsnamn", -"Active" => "Aktiv", +"Download" => "Last ned", +"Edit" => "Endra", +"New Address Book" => "Ny adressebok", "Save" => "Lagre", -"Submit" => "Send", "Cancel" => "Kanseller" ); diff --git a/apps/contacts/l10n/pl.php b/apps/contacts/l10n/pl.php index a99f1906958..924622ebb0f 100644 --- a/apps/contacts/l10n/pl.php +++ b/apps/contacts/l10n/pl.php @@ -1,39 +1,40 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "BÅ‚Ä…d (de)aktywowania książki adresowej.", -"There was an error adding the contact." => "WystÄ…piÅ‚ bÅ‚Ä…d podczas dodawania kontaktu.", -"Cannot add empty property." => "Nie można dodać pustego elementu.", -"At least one of the address fields has to be filled out." => "Należy wypeÅ‚nić przynajmniej jedno pole adresu.", -"Trying to add duplicate property: " => "Próba dodania z duplikowanej wÅ‚aÅ›ciwoÅ›ci:", -"Error adding contact property." => "BÅ‚Ä…d dodawania elementu.", +"id is not set." => "id nie ustawione.", +"Cannot update addressbook with an empty name." => "Nie można zaktualizować książki adresowej z pustÄ… nazwÄ….", +"Error updating addressbook." => "BÅ‚Ä…d uaktualniania książki adresowej.", "No ID provided" => "Brak opatrzonego ID ", "Error setting checksum." => "BÅ‚Ä…d ustawieÅ„ sumy kontrolnej", "No categories selected for deletion." => "Nie zaznaczono kategorii do usuniÄ™cia", "No address books found." => "Nie znaleziono książek adresowych", "No contacts found." => "Nie znaleziono kontaktów.", +"There was an error adding the contact." => "WystÄ…piÅ‚ bÅ‚Ä…d podczas dodawania kontaktu.", +"element name is not set." => "nazwa elementu nie jest ustawiona.", +"Could not parse contact: " => "Nie można parsować kontaktu:", +"Cannot add empty property." => "Nie można dodać pustego elementu.", +"At least one of the address fields has to be filled out." => "Należy wypeÅ‚nić przynajmniej jedno pole adresu.", +"Trying to add duplicate property: " => "Próba dodania z duplikowanej wÅ‚aÅ›ciwoÅ›ci:", +"Information about vCard is incorrect. Please reload the page." => "Informacje o vCard sÄ… nieprawidÅ‚owe. ProszÄ™ odÅ›wieżyć stronÄ™.", "Missing ID" => "Brak ID", "Error parsing VCard for ID: \"" => "WystÄ…piÅ‚ bÅ‚Ä…d podczas przetwarzania VCard ID: \"", -"Cannot add addressbook with an empty name." => "Nie można dodać książki adresowej z pusta nazwÄ…", -"Error adding addressbook." => "BÅ‚Ä…d dodawania książki adresowej.", -"Error activating addressbook." => "BÅ‚Ä…d aktywowania książki adresowej.", +"checksum is not set." => "checksum-a nie ustawiona", +"Information about vCard is incorrect. Please reload the page: " => "Informacje na temat vCard sÄ… niepoprawne. ProszÄ™ przeÅ‚aduj stronÄ™:", +"Something went FUBAR. " => "Gdyby coÅ› poszÅ‚o FUBAR.", "No contact ID was submitted." => "ID kontaktu nie zostaÅ‚ utworzony.", "Error reading contact photo." => "BÅ‚Ä…d odczytu zdjÄ™cia kontaktu.", "Error saving temporary file." => "WystÄ…piÅ‚ bÅ‚Ä…d podczas zapisywania pliku tymczasowego.", "The loading photo is not valid." => "Wczytywane zdjÄ™cie nie jest poprawne.", -"id is not set." => "id nie ustawione.", -"Information about vCard is incorrect. Please reload the page." => "Informacje o vCard sÄ… nieprawidÅ‚owe. ProszÄ™ odÅ›wieżyć stronÄ™.", -"Error deleting contact property." => "BÅ‚Ä…d usuwania elementu.", "Contact ID is missing." => "Brak kontaktu id.", -"Missing contact id." => "Brak kontaktu id.", "No photo path was submitted." => "Åšcieżka do zdjÄ™cia nie zostaÅ‚a podana.", "File doesn't exist:" => "Plik nie istnieje:", "Error loading image." => "BÅ‚Ä…d Å‚adowania obrazu.", -"element name is not set." => "nazwa elementu nie jest ustawiona.", -"checksum is not set." => "checksum-a nie ustawiona", -"Information about vCard is incorrect. Please reload the page: " => "Informacje na temat vCard sÄ… niepoprawne. ProszÄ™ przeÅ‚aduj stronÄ™:", -"Something went FUBAR. " => "Gdyby coÅ› poszÅ‚o FUBAR.", -"Error updating contact property." => "BÅ‚Ä…d uaktualniania elementu.", -"Cannot update addressbook with an empty name." => "Nie można zaktualizować książki adresowej z pustÄ… nazwÄ….", -"Error updating addressbook." => "BÅ‚Ä…d uaktualniania książki adresowej.", +"Error getting contact object." => "BÅ‚Ä…d pobrania kontaktu.", +"Error getting PHOTO property." => "BÅ‚Ä…d uzyskiwania wÅ‚aÅ›ciwoÅ›ci ZDJĘCIA.", +"Error saving contact." => "BÅ‚Ä…d zapisu kontaktu.", +"Error resizing image" => "BÅ‚Ä…d zmiany rozmiaru obrazu", +"Error cropping image" => "BÅ‚Ä…d przycinania obrazu", +"Error creating temporary image" => "BÅ‚Ä…d utworzenia obrazu tymczasowego", +"Error finding image: " => "BÅ‚Ä…d znajdowanie obrazu: ", "Error uploading contacts to storage." => "WystÄ…piÅ‚ bÅ‚Ä…d podczas wysyÅ‚ania kontaktów do magazynu.", "There is no error, the file uploaded with success" => "Nie byÅ‚o bÅ‚Ä™dów, plik wyczytano poprawnie.", "The uploaded file exceeds the upload_max_filesize directive in php.ini" => "ZaÅ‚adowany plik przekracza wielkość upload_max_filesize w php.ini ", @@ -41,17 +42,46 @@ "The uploaded file was only partially uploaded" => "ZaÅ‚adowany plik tylko częściowo zostaÅ‚ wysÅ‚any.", "No file was uploaded" => "Plik nie zostaÅ‚ zaÅ‚adowany", "Missing a temporary folder" => "Brak folderu tymczasowego", +"Couldn't save temporary image: " => "Nie można zapisać obrazu tymczasowego: ", +"Couldn't load temporary image: " => "Nie można wczytać obrazu tymczasowego: ", +"No file was uploaded. Unknown error" => "Plik nie zostaÅ‚ zaÅ‚adowany. Nieznany bÅ‚Ä…d", "Contacts" => "Kontakty", -"Drop a VCF file to import contacts." => "Upuść plik VCF do importu kontaktów.", -"Addressbook not found." => "Nie znaleziono książki adresowej", +"Sorry, this functionality has not been implemented yet" => "Niestety, ta funkcja nie zostaÅ‚a jeszcze zaimplementowana", +"Not implemented" => "Nie wdrożono", +"Couldn't get a valid address." => "Nie można pobrać prawidÅ‚owego adresu.", +"Error" => "BÅ‚Ä…d", +"This property has to be non-empty." => "Ta wÅ‚aÅ›ciwość nie może być pusta.", +"Couldn't serialize elements." => "Nie można serializować elementów.", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "\"deleteProperty' wywoÅ‚ana bez argumentu typu. ProszÄ™ raportuj na bugs.owncloud.org", +"Edit name" => "ZmieÅ„ nazwÄ™", +"No files selected for upload." => "Å»adne pliki nie zostaÅ‚y zaznaczone do wysÅ‚ania.", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "Plik, który próbujesz wysÅ‚ać przekracza maksymalny rozmiar pliku przekazywania na tym serwerze.", +"Error loading profile picture." => "BÅ‚Ä…d wczytywania zdjÄ™cia profilu.", +"Select type" => "Wybierz typ", +"Some contacts are marked for deletion, but not deleted yet. Please wait for them to be deleted." => "Niektóre kontakty sÄ… zaznaczone do usuniÄ™cia, ale nie sÄ… usuniÄ™te jeszcze. ProszÄ™ czekać na ich usuniÄ™cie.", +"Do you want to merge these address books?" => "Czy chcesz scalić te książki adresowe?", +"Result: " => "Wynik: ", +" imported, " => " importowane, ", +" failed." => " nie powiodÅ‚o siÄ™.", +"Displayname cannot be empty." => "Nazwa nie może być pusta.", +"Addressbook not found: " => "Nie znaleziono książki adresowej:", "This is not your addressbook." => "To nie jest Twoja książka adresowa.", "Contact could not be found." => "Nie można odnaleźć kontaktu.", -"Address" => "Adres", -"Telephone" => "Telefon", -"Email" => "E-mail", -"Organization" => "Organizacja", +"Jabber" => "Jabber", +"AIM" => "AIM", +"MSN" => "MSN", +"Twitter" => "Twitter", +"GoogleTalk" => "GoogleTalk", +"Facebook" => "Facebook", +"XMPP" => "XMPP", +"ICQ" => "ICQ", +"Yahoo" => "Yahoo", +"Skype" => "Skype", +"QQ" => "QQ", +"GaduGadu" => "GG", "Work" => "Praca", "Home" => "Dom", +"Other" => "Inne", "Mobile" => "Komórka", "Text" => "PoÅ‚Ä…czenie tekstowe", "Voice" => "PoÅ‚Ä…czenie gÅ‚osowe", @@ -60,25 +90,52 @@ "Video" => "PoÅ‚Ä…czenie wideo", "Pager" => "Pager", "Internet" => "Internet", +"Birthday" => "Urodziny", +"Business" => "Biznesowe", +"Call" => "WywoÅ‚anie", +"Clients" => "Klienci", +"Deliverer" => "DorÄ™czanie", +"Holidays" => "ÅšwiÄ™ta", +"Ideas" => "PomysÅ‚y", +"Journey" => "Podróż", +"Jubilee" => "Jubileusz", +"Meeting" => "Spotkanie", +"Personal" => "Osobiste", +"Projects" => "Projekty", +"Questions" => "Pytania", "{name}'s Birthday" => "{name} Urodzony", "Contact" => "Kontakt", "Add Contact" => "Dodaj kontakt", +"Import" => "Import", +"Settings" => "Ustawienia", "Addressbooks" => "Książki adresowe", -"Configure Address Books" => "Konfiguruj książkÄ™ adresowÄ…", -"New Address Book" => "Nowa książka adresowa", -"Import from VCF" => "Importuj z VFC", -"CardDav Link" => "WyÅ›wietla odnoÅ›nik CardDav", -"Download" => "Pobiera książkÄ™ adresowÄ…", -"Edit" => "Edytuje książkÄ™ adresowÄ…", -"Delete" => "Usuwa książkÄ™ adresowÄ…", -"Download contact" => "Pobiera kontakt", -"Delete contact" => "Usuwa kontakt", +"Close" => "Zamknij", +"Keyboard shortcuts" => "Skróty klawiatury", +"Navigation" => "Nawigacja", +"Next contact in list" => "NastÄ™pny kontakt na liÅ›cie", +"Previous contact in list" => "Poprzedni kontakt na liÅ›cie", +"Expand/collapse current addressbook" => "RozwiÅ„/ZwiÅ„ bieżącÄ… książkÄ™ adresowÄ…", +"Next addressbook" => "NastÄ™pna książka adresowa", +"Previous addressbook" => "Poprzednia książka adresowa", +"Actions" => "Akcje", +"Refresh contacts list" => "OdÅ›wież listÄ™ kontaktów", +"Add new contact" => "Dodaj nowy kontakt", +"Add new addressbook" => "Dodaj nowa książkÄ™ adresowÄ…", +"Delete current contact" => "UsuÅ„ obecny kontakt", "Drop photo to upload" => "Upuść fotografiÄ™ aby zaÅ‚adować", +"Delete current photo" => "UsuÅ„ aktualne zdjÄ™cie", +"Edit current photo" => "Edytuj aktualne zdjÄ™cie", +"Upload new photo" => "Wczytaj nowe zdjÄ™cie", +"Select photo from ownCloud" => "Wybierz zdjÄ™cie z ownCloud", "Format custom, Short name, Full name, Reverse or Reverse with comma" => "Format niestandardowy, krótkie nazwy, imiÄ™ i nazwisko, Odwracać lub Odwrócić z przecinkiem", "Edit name details" => "Edytuj szczegóły nazwy", +"Organization" => "Organizacja", +"Delete" => "Usuwa książkÄ™ adresowÄ…", "Nickname" => "Nazwa", "Enter nickname" => "Wpisz nazwÄ™", -"Birthday" => "Urodziny", +"Web site" => "Strona www", +"http://www.somesite.com" => "http://www.jakasstrona.pl", +"Go to web site" => "Idż do strony www", "dd-mm-yyyy" => "dd-mm-rrrr", "Groups" => "Grupy", "Separate groups with commas" => "Oddziel grupy przecinkami", @@ -94,24 +151,26 @@ "Edit address details" => "Edytuj szczegóły adresu", "Add notes here." => "Dodaj notatkÄ™ tutaj.", "Add field" => "Dodaj pole", -"Profile picture" => "ZdjÄ™cie profilu", "Phone" => "Telefon", +"Email" => "E-mail", +"Address" => "Adres", "Note" => "Uwaga", -"Delete current photo" => "UsuÅ„ aktualne zdjÄ™cie", -"Edit current photo" => "Edytuj aktualne zdjÄ™cie", -"Upload new photo" => "Wczytaj nowe zdjÄ™cie", -"Select photo from ownCloud" => "Wybierz zdjÄ™cie z ownCloud", +"Download contact" => "Pobiera kontakt", +"Delete contact" => "Usuwa kontakt", +"The temporary image has been removed from cache." => "Tymczasowy obraz zostaÅ‚ usuniÄ™ty z pamiÄ™ci podrÄ™cznej.", "Edit address" => "Edytuj adres", "Type" => "Typ", "PO Box" => "Skrzynka pocztowa", +"Street address" => "Ulica", +"Street and number" => "Ulica i numer", "Extended" => "Rozszerzony", -"Street" => "Ulica", +"Apartment number etc." => "Numer lokalu", "City" => "Miasto", "Region" => "Region", +"E.g. state or province" => "Np. stanu lub prowincji", "Zipcode" => "Kod pocztowy", +"Postal code" => "Kod pocztowy", "Country" => "Kraj", -"Edit categories" => "Edytuj kategorie", -"Add" => "Dodaj", "Addressbook" => "Książka adresowa", "Hon. prefixes" => "Prefiksy Hon.", "Miss" => "Panna", @@ -132,26 +191,29 @@ "Esq." => "Esq.", "Jr." => "Jr.", "Sn." => "Sn.", -"New Addressbook" => "Nowa książka adresowa", -"Edit Addressbook" => "Edytowanie książki adresowej", -"Displayname" => "WyÅ›wietlana nazwa", -"Active" => "Aktywna", -"Save" => "Zapisz", -"Submit" => "Potwierdź", -"Cancel" => "Anuluj", "Import a contacts file" => "Importuj plik z kontaktami", "Please choose the addressbook" => "ProszÄ™ wybrać książkÄ™ adresowÄ…", "create a new addressbook" => "utwórz nowÄ… książkÄ™ adresowÄ…", "Name of new addressbook" => "Nazwa nowej książki adresowej", -"Import" => "Import", "Importing contacts" => "importuj kontakty", -"Select address book to import to:" => "Zaznacz książkÄ™ adresowÄ… do importu do:", -"Select from HD" => "Wybierz z HD", "You have no contacts in your addressbook." => "Nie masz żadnych kontaktów w swojej książce adresowej.", "Add contact" => "Dodaj kontakt", -"Configure addressbooks" => "Konfiguruj książkÄ™ adresowÄ…", +"Select Address Books" => "Wybierz książki adresowe", +"Enter name" => "Wpisz nazwÄ™", +"Enter description" => "Wprowadź opis", "CardDAV syncing addresses" => "adres do synchronizacji CardDAV", "more info" => "wiÄ™cej informacji", "Primary address (Kontact et al)" => "Pierwszy adres", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Show CardDav link" => "Pokaż link CardDAV", +"Show read-only VCF link" => "Pokaż tylko do odczytu Å‚Ä…cze VCF", +"Share" => "UdostÄ™pnij", +"Download" => "Pobiera książkÄ™ adresowÄ…", +"Edit" => "Edytuje książkÄ™ adresowÄ…", +"New Address Book" => "Nowa książka adresowa", +"Name" => "Nazwa", +"Description" => "Opis", +"Save" => "Zapisz", +"Cancel" => "Anuluj", +"More..." => "WiÄ™cej..." ); diff --git a/apps/contacts/l10n/pt_BR.php b/apps/contacts/l10n/pt_BR.php index e0da0a771a5..de43e6cbb0f 100644 --- a/apps/contacts/l10n/pt_BR.php +++ b/apps/contacts/l10n/pt_BR.php @@ -1,39 +1,39 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "Erro ao (des)ativar agenda.", -"There was an error adding the contact." => "Ocorreu um erro ao adicionar o contato.", -"Cannot add empty property." => "Não é possÃvel adicionar propriedade vazia.", -"At least one of the address fields has to be filled out." => "Pelo menos um dos campos de endereço tem que ser preenchido.", -"Trying to add duplicate property: " => "Tentando adiciona propriedade duplicada:", -"Error adding contact property." => "Erro ao adicionar propriedade de contato.", +"id is not set." => "ID não definido.", +"Cannot update addressbook with an empty name." => "Não é possÃvel atualizar sua agenda com um nome em branco.", +"Error updating addressbook." => "Erro ao atualizar agenda.", "No ID provided" => "Nenhum ID fornecido", "Error setting checksum." => "Erro ajustando checksum.", "No categories selected for deletion." => "Nenhum categoria selecionada para remoção.", "No address books found." => "Nenhuma agenda de endereços encontrada.", "No contacts found." => "Nenhum contato encontrado.", +"There was an error adding the contact." => "Ocorreu um erro ao adicionar o contato.", +"element name is not set." => "nome do elemento não definido.", +"Cannot add empty property." => "Não é possÃvel adicionar propriedade vazia.", +"At least one of the address fields has to be filled out." => "Pelo menos um dos campos de endereço tem que ser preenchido.", +"Trying to add duplicate property: " => "Tentando adiciona propriedade duplicada:", +"Information about vCard is incorrect. Please reload the page." => "Informações sobre vCard é incorreta. Por favor, recarregue a página.", "Missing ID" => "Faltando ID", "Error parsing VCard for ID: \"" => "Erro de identificação VCard para ID:", -"Cannot add addressbook with an empty name." => "Não é possivel adicionar uma agenda de endereços com o nome em branco.", -"Error adding addressbook." => "Erro ao adicionar agenda.", -"Error activating addressbook." => "Erro ao ativar agenda.", +"checksum is not set." => "checksum não definido.", +"Information about vCard is incorrect. Please reload the page: " => "Informação sobre vCard incorreto. Por favor, recarregue a página:", +"Something went FUBAR. " => "Something went FUBAR. ", "No contact ID was submitted." => "Nenhum ID do contato foi submetido.", "Error reading contact photo." => "Erro de leitura na foto do contato.", "Error saving temporary file." => "Erro ao salvar arquivo temporário.", "The loading photo is not valid." => "Foto carregada não é válida.", -"id is not set." => "ID não definido.", -"Information about vCard is incorrect. Please reload the page." => "Informações sobre vCard é incorreta. Por favor, recarregue a página.", -"Error deleting contact property." => "Erro ao excluir propriedade de contato.", "Contact ID is missing." => "ID do contato está faltando.", -"Missing contact id." => "Faltando ID do contato.", "No photo path was submitted." => "Nenhum caminho para foto foi submetido.", "File doesn't exist:" => "Arquivo não existe:", "Error loading image." => "Erro ao carregar imagem.", -"element name is not set." => "nome do elemento não definido.", -"checksum is not set." => "checksum não definido.", -"Information about vCard is incorrect. Please reload the page: " => "Informação sobre vCard incorreto. Por favor, recarregue a página:", -"Something went FUBAR. " => "Something went FUBAR. ", -"Error updating contact property." => "Erro ao atualizar propriedades do contato.", -"Cannot update addressbook with an empty name." => "Não é possÃvel atualizar sua agenda com um nome em branco.", -"Error updating addressbook." => "Erro ao atualizar agenda.", +"Error getting contact object." => "Erro ao obter propriedade de contato.", +"Error getting PHOTO property." => "Erro ao obter propriedade da FOTO.", +"Error saving contact." => "Erro ao salvar contato.", +"Error resizing image" => "Erro ao modificar tamanho da imagem", +"Error cropping image" => "Erro ao recortar imagem", +"Error creating temporary image" => "Erro ao criar imagem temporária", +"Error finding image: " => "Erro ao localizar imagem:", "Error uploading contacts to storage." => "Erro enviando contatos para armazenamento.", "There is no error, the file uploaded with success" => "Arquivo enviado com sucesso", "The uploaded file exceeds the upload_max_filesize directive in php.ini" => "O arquivo enviado excede a diretiva upload_max_filesize em php.ini", @@ -41,15 +41,26 @@ "The uploaded file was only partially uploaded" => "O arquivo foi parcialmente carregado", "No file was uploaded" => "Nenhum arquivo carregado", "Missing a temporary folder" => "Diretório temporário não encontrado", +"Couldn't save temporary image: " => "Não foi possÃvel salvar a imagem temporária:", +"Couldn't load temporary image: " => "Não foi possÃvel carregar a imagem temporária:", +"No file was uploaded. Unknown error" => "Nenhum arquivo foi transferido. Erro desconhecido", "Contacts" => "Contatos", -"Drop a VCF file to import contacts." => "Arraste um arquivo VCF para importar contatos.", -"Addressbook not found." => "Lista de endereços não encontrado.", +"Sorry, this functionality has not been implemented yet" => "Desculpe, esta funcionalidade não foi implementada ainda", +"Not implemented" => "não implementado", +"Couldn't get a valid address." => "Não foi possÃvel obter um endereço válido.", +"Error" => "Erro", +"This property has to be non-empty." => "Esta propriedade não pode estar vazia.", +"Couldn't serialize elements." => "Não foi possÃvel serializar elementos.", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "\"deleteProperty\" chamado sem argumento de tipo. Por favor, informe a bugs.owncloud.org", +"Edit name" => "Editar nome", +"No files selected for upload." => "Nenhum arquivo selecionado para carregar.", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "O arquivo que você está tentando carregar excede o tamanho máximo para este servidor.", +"Select type" => "Selecione o tipo", +"Result: " => "Resultado:", +" imported, " => "importado,", +" failed." => "falhou.", "This is not your addressbook." => "Esta não é a sua agenda de endereços.", "Contact could not be found." => "Contato não pôde ser encontrado.", -"Address" => "Endereço", -"Telephone" => "Telefone", -"Email" => "E-mail", -"Organization" => "Organização", "Work" => "Trabalho", "Home" => "Home", "Mobile" => "Móvel", @@ -60,25 +71,24 @@ "Video" => "VÃdeo", "Pager" => "Pager", "Internet" => "Internet", +"Birthday" => "Aniversário", "{name}'s Birthday" => "Aniversário de {name}", "Contact" => "Contato", "Add Contact" => "Adicionar Contato", +"Import" => "Importar", "Addressbooks" => "Agendas de Endereço", -"Configure Address Books" => "Configurar Livro de Endereços", -"New Address Book" => "Nova agenda", -"Import from VCF" => "Importar de VCF", -"CardDav Link" => "Link CardDav", -"Download" => "Baixar", -"Edit" => "Editar", -"Delete" => "Excluir", -"Download contact" => "Baixar contato", -"Delete contact" => "Apagar contato", +"Close" => "Fechar.", "Drop photo to upload" => "Arraste a foto para ser carregada", +"Delete current photo" => "Deletar imagem atual", +"Edit current photo" => "Editar imagem atual", +"Upload new photo" => "Carregar nova foto", +"Select photo from ownCloud" => "Selecionar foto do OwnCloud", "Format custom, Short name, Full name, Reverse or Reverse with comma" => "Formato personalizado, Nome curto, Nome completo, Inverter ou Inverter com vÃrgula", "Edit name details" => "Editar detalhes do nome", +"Organization" => "Organização", +"Delete" => "Excluir", "Nickname" => "Apelido", "Enter nickname" => "Digite o apelido", -"Birthday" => "Aniversário", "dd-mm-yyyy" => "dd-mm-aaaa", "Groups" => "Grupos", "Separate groups with commas" => "Separe grupos por virgula", @@ -94,24 +104,21 @@ "Edit address details" => "Editar detalhes de endereço", "Add notes here." => "Adicionar notas", "Add field" => "Adicionar campo", -"Profile picture" => "Imagem do Perfil", "Phone" => "Telefone", +"Email" => "E-mail", +"Address" => "Endereço", "Note" => "Nota", -"Delete current photo" => "Deletar imagem atual", -"Edit current photo" => "Editar imagem atual", -"Upload new photo" => "Carregar nova foto", -"Select photo from ownCloud" => "Selecionar foto do OwnCloud", +"Download contact" => "Baixar contato", +"Delete contact" => "Apagar contato", +"The temporary image has been removed from cache." => "A imagem temporária foi removida cache.", "Edit address" => "Editar endereço", "Type" => "Digite", "PO Box" => "Caixa Postal", "Extended" => "Estendido", -"Street" => "Rua", "City" => "Cidade", "Region" => "Região", "Zipcode" => "CEP", "Country" => "PaÃs", -"Edit categories" => "Editar categorias", -"Add" => "Adicionar", "Addressbook" => "Agenda de Endereço", "Hon. prefixes" => "Exmo. Prefixos ", "Miss" => "Senhorita", @@ -132,26 +139,20 @@ "Esq." => "Esq.", "Jr." => "Jr.", "Sn." => "Sn.", -"New Addressbook" => "Nova Agenda de Endereço", -"Edit Addressbook" => "Editar Agenda de Endereço", -"Displayname" => "Nome de exibição", -"Active" => "Ativo", -"Save" => "Salvar", -"Submit" => "Enviar", -"Cancel" => "Cancelar", "Import a contacts file" => "Importar arquivos de contato.", "Please choose the addressbook" => "Por favor, selecione uma agenda de endereços", "create a new addressbook" => "Criar nova agenda de endereços", "Name of new addressbook" => "Nome da nova agenda de endereços", -"Import" => "Importar", "Importing contacts" => "Importar contatos", -"Select address book to import to:" => "Selecione agenda de endereços para importar ao destino:", -"Select from HD" => "Selecione do disco rigÃdo", "You have no contacts in your addressbook." => "Voce não tem contatos em sua agenda de endereços.", "Add contact" => "Adicionar contatos", -"Configure addressbooks" => "Configurar agenda de endereços", "CardDAV syncing addresses" => "Sincronizando endereços CardDAV", "more info" => "leia mais", "Primary address (Kontact et al)" => "Endereço primário(Kontact et al)", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Download" => "Baixar", +"Edit" => "Editar", +"New Address Book" => "Nova agenda", +"Save" => "Salvar", +"Cancel" => "Cancelar" ); diff --git a/apps/contacts/l10n/pt_PT.php b/apps/contacts/l10n/pt_PT.php index 7a4861abf9d..38708c86206 100644 --- a/apps/contacts/l10n/pt_PT.php +++ b/apps/contacts/l10n/pt_PT.php @@ -1,52 +1,89 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "Erro a (des)ativar o livro de endereços", -"There was an error adding the contact." => "Erro ao adicionar contato", -"Cannot add empty property." => "Não é possivel adicionar uma propriedade vazia", -"At least one of the address fields has to be filled out." => "Pelo menos um dos campos de endereço precisa de estar preenchido", -"Trying to add duplicate property: " => "A tentar adicionar propriedade duplicada: ", -"Error adding contact property." => "Erro ao adicionar propriedade do contato", +"id is not set." => "id não está definido", +"Cannot update addressbook with an empty name." => "Não é possivel actualizar o livro de endereços com o nome vazio.", +"Error updating addressbook." => "Erro a atualizar o livro de endereços", "No ID provided" => "Nenhum ID inserido", "Error setting checksum." => "Erro a definir checksum.", "No categories selected for deletion." => "Nenhuma categoria selecionada para eliminar.", "No address books found." => "Nenhum livro de endereços encontrado.", "No contacts found." => "Nenhum contacto encontrado.", +"There was an error adding the contact." => "Erro ao adicionar contato", +"element name is not set." => "o nome do elemento não está definido.", +"Could not parse contact: " => "Incapaz de processar contacto", +"Cannot add empty property." => "Não é possivel adicionar uma propriedade vazia", +"At least one of the address fields has to be filled out." => "Pelo menos um dos campos de endereço precisa de estar preenchido", +"Trying to add duplicate property: " => "A tentar adicionar propriedade duplicada: ", +"Missing IM parameter." => "Falta o parâmetro de mensagens instantâneas (IM)", +"Unknown IM: " => "Mensagens instantâneas desconhecida (IM)", +"Information about vCard is incorrect. Please reload the page." => "A informação sobre o vCard está incorreta. Por favor refresque a página", "Missing ID" => "Falta ID", "Error parsing VCard for ID: \"" => "Erro a analisar VCard para o ID: \"", -"Cannot add addressbook with an empty name." => "Não é possivel adicionar Livro de endereços com nome vazio.", -"Error adding addressbook." => "Erro ao adicionar livro de endereços", -"Error activating addressbook." => "Erro ao ativar livro de endereços", +"checksum is not set." => "Checksum não está definido.", +"Information about vCard is incorrect. Please reload the page: " => "A informação sobre o VCard está incorrecta. Por favor refresque a página: ", +"Something went FUBAR. " => "Algo provocou um FUBAR. ", "No contact ID was submitted." => "Nenhum ID de contacto definido.", "Error reading contact photo." => "Erro a ler a foto do contacto.", "Error saving temporary file." => "Erro a guardar ficheiro temporário.", "The loading photo is not valid." => "A foto carregada não é valida.", -"id is not set." => "id não está definido", -"Information about vCard is incorrect. Please reload the page." => "A informação sobre o vCard está incorreta. Por favor refresque a página", -"Error deleting contact property." => "Erro ao apagar propriedade do contato", "Contact ID is missing." => "Falta o ID do contacto.", -"Missing contact id." => "Falta o ID do contacto.", "No photo path was submitted." => "Nenhum caminho da foto definido.", "File doesn't exist:" => "O ficheiro não existe:", "Error loading image." => "Erro a carregar a imagem.", -"element name is not set." => "o nome do elemento não está definido.", -"checksum is not set." => "Checksum não está definido.", -"Information about vCard is incorrect. Please reload the page: " => "A informação sobre o VCard está incorrecta. Por favor refresque a página: ", -"Something went FUBAR. " => "Algo provocou um FUBAR. ", -"Error updating contact property." => "Erro ao atualizar propriedade do contato", -"Cannot update addressbook with an empty name." => "Não é possivel actualizar o livro de endereços com o nome vazio.", -"Error updating addressbook." => "Erro a atualizar o livro de endereços", +"Error getting contact object." => "Erro a obter o objecto dos contactos", +"Error getting PHOTO property." => "Erro a obter a propriedade Foto", +"Error saving contact." => "Erro a guardar o contacto.", +"Error resizing image" => "Erro a redimensionar a imagem", +"Error cropping image" => "Erro a recorar a imagem", +"Error creating temporary image" => "Erro a criar a imagem temporária", +"Error finding image: " => "Erro enquanto pesquisava pela imagem: ", "Error uploading contacts to storage." => "Erro a carregar os contactos para o armazenamento.", "There is no error, the file uploaded with success" => "Não ocorreu erros, o ficheiro foi submetido com sucesso", +"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "O tamanho do ficheiro carregado excede o parametro upload_max_filesize em php.ini", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "O tamanho do ficheiro carregado ultrapassa o valor MAX_FILE_SIZE definido no formulário HTML", +"The uploaded file was only partially uploaded" => "O ficheiro seleccionado foi apenas carregado parcialmente", "No file was uploaded" => "Nenhum ficheiro foi submetido", +"Missing a temporary folder" => "Está a faltar a pasta temporária", +"Couldn't save temporary image: " => "Não foi possÃvel guardar a imagem temporária: ", +"Couldn't load temporary image: " => "Não é possÃvel carregar a imagem temporária: ", +"No file was uploaded. Unknown error" => "Nenhum ficheiro foi carregado. Erro desconhecido", "Contacts" => "Contactos", -"Addressbook not found." => "Livro de endereços não encontrado.", +"Sorry, this functionality has not been implemented yet" => "Desculpe, esta funcionalidade ainda não está implementada", +"Not implemented" => "Não implementado", +"Couldn't get a valid address." => "Não foi possÃvel obter um endereço válido.", +"Error" => "Erro", +"This property has to be non-empty." => "Esta propriedade não pode estar vazia.", +"Couldn't serialize elements." => "Não foi possivel serializar os elementos", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "'deleteProperty' chamada sem argumento definido. Por favor report o problema em bugs.owncloud.org", +"Edit name" => "Editar nome", +"No files selected for upload." => "Nenhum ficheiro seleccionado para enviar.", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "O tamanho do ficheiro que está a tentar carregar ultrapassa o limite máximo definido para ficheiros no servidor.", +"Error loading profile picture." => "Erro ao carregar imagem de perfil.", +"Select type" => "Seleccionar tipo", +"Some contacts are marked for deletion, but not deleted yet. Please wait for them to be deleted." => "Alguns contactos forma marcados para apagar, mas ainda não foram apagados. Por favor espere que ele sejam apagados.", +"Do you want to merge these address books?" => "Quer fundir estes Livros de endereços?", +"Result: " => "Resultado: ", +" imported, " => " importado, ", +" failed." => " falhou.", +"Displayname cannot be empty." => "Displayname não pode ser vazio", +"Addressbook not found: " => "Livro de endereços não encontrado.", "This is not your addressbook." => "Esta não é a sua lista de contactos", "Contact could not be found." => "O contacto não foi encontrado", -"Address" => "Morada", -"Telephone" => "Telefone", -"Email" => "Email", -"Organization" => "Organização", +"Jabber" => "Jabber", +"AIM" => "AIM", +"MSN" => "MSN", +"Twitter" => "Twitter", +"GoogleTalk" => "GoogleTalk", +"Facebook" => "Facebook", +"XMPP" => "XMPP", +"ICQ" => "ICQ", +"Yahoo" => "Yahoo", +"Skype" => "Skype", +"QQ" => "QQ", +"GaduGadu" => "GaduGadu", "Work" => "Emprego", "Home" => "Casa", +"Other" => "Outro", "Mobile" => "Telemovel", "Text" => "Texto", "Voice" => "Voz", @@ -55,23 +92,52 @@ "Video" => "VÃdeo", "Pager" => "Pager", "Internet" => "Internet", +"Birthday" => "Aniversário", +"Business" => "Empresa", +"Call" => "Telefonar", +"Clients" => "Clientes", +"Deliverer" => "Fornecedor", +"Holidays" => "Férias", +"Ideas" => "Ideias", +"Journey" => "Viagem", +"Jubilee" => "Jubileu", +"Meeting" => "Encontro", +"Personal" => "Pessoal", +"Projects" => "Projectos", +"Questions" => "Questões", "{name}'s Birthday" => "Aniversário de {name}", "Contact" => "Contacto", "Add Contact" => "Adicionar Contacto", +"Import" => "Importar", +"Settings" => "Configurações", "Addressbooks" => "Livros de endereços", -"Configure Address Books" => "Configurar livros de endereços", -"New Address Book" => "Novo livro de endereços", -"Import from VCF" => "Importar de VCF", -"CardDav Link" => "Endereço CardDav", -"Download" => "Transferir", -"Edit" => "Editar", -"Delete" => "Apagar", -"Download contact" => "Transferir contacto", -"Delete contact" => "Apagar contato", +"Close" => "Fechar", +"Keyboard shortcuts" => "Atalhos de teclado", +"Navigation" => "Navegação", +"Next contact in list" => "Próximo contacto na lista", +"Previous contact in list" => "Contacto anterior na lista", +"Expand/collapse current addressbook" => "Expandir/encolher o livro de endereços atual", +"Next addressbook" => "Próximo livro de endereços", +"Previous addressbook" => "Livro de endereços anterior", +"Actions" => "Ações", +"Refresh contacts list" => "Recarregar lista de contactos", +"Add new contact" => "Adicionar novo contacto", +"Add new addressbook" => "Adicionar novo Livro de endereços", +"Delete current contact" => "Apagar o contacto atual", +"Drop photo to upload" => "Arraste e solte fotos para carregar", +"Delete current photo" => "Eliminar a foto actual", +"Edit current photo" => "Editar a foto actual", +"Upload new photo" => "Carregar nova foto", +"Select photo from ownCloud" => "Selecionar uma foto da ownCloud", +"Format custom, Short name, Full name, Reverse or Reverse with comma" => "Formate personalizado, Nome curto, Nome completo, Reverso ou Reverso com virgula", "Edit name details" => "Editar detalhes do nome", +"Organization" => "Organização", +"Delete" => "Apagar", "Nickname" => "Alcunha", "Enter nickname" => "Introduza alcunha", -"Birthday" => "Aniversário", +"Web site" => "Página web", +"http://www.somesite.com" => "http://www.somesite.com", +"Go to web site" => "Ir para página web", "dd-mm-yyyy" => "dd-mm-aaaa", "Groups" => "Grupos", "Separate groups with commas" => "Separe os grupos usando virgulas", @@ -83,47 +149,76 @@ "Delete email address" => "Eliminar o endereço de correio", "Enter phone number" => "Insira o número de telefone", "Delete phone number" => "Eliminar o número de telefone", +"Instant Messenger" => "Mensageiro instantâneo", +"Delete IM" => "Apagar mensageiro instantâneo (IM)", "View on map" => "Ver no mapa", "Edit address details" => "Editar os detalhes do endereço", "Add notes here." => "Insira notas aqui.", "Add field" => "Adicionar campo", -"Profile picture" => "Foto do perfil", "Phone" => "Telefone", +"Email" => "Email", +"Instant Messaging" => "Mensagens Instantâneas", +"Address" => "Morada", "Note" => "Nota", -"Delete current photo" => "Eliminar a foto actual", -"Edit current photo" => "Editar a foto actual", -"Select photo from ownCloud" => "Selecionar uma foto da ownCloud", +"Download contact" => "Transferir contacto", +"Delete contact" => "Apagar contato", +"The temporary image has been removed from cache." => "A imagem temporária foi retirada do cache.", "Edit address" => "Editar endereço", "Type" => "Tipo", "PO Box" => "Apartado", +"Street address" => "Endereço da Rua", +"Street and number" => "Rua e número", "Extended" => "Extendido", -"Street" => "Rua", +"Apartment number etc." => "Número de Apartamento, etc.", "City" => "Cidade", "Region" => "Região", +"E.g. state or province" => "Por Ex. Estado ou provÃncia", "Zipcode" => "Código Postal", +"Postal code" => "Código Postal", "Country" => "PaÃs", -"Edit categories" => "Editar categorias", -"Add" => "Adicionar", "Addressbook" => "Livro de endereços", +"Hon. prefixes" => "Prefixos honoráveis", +"Miss" => "Menina", "Ms" => "Sra", "Mr" => "Sr", "Sir" => "Sr", +"Mrs" => "Senhora", "Dr" => "Dr", +"Given name" => "Nome introduzido", "Additional names" => "Nomes adicionais", "Family name" => "Nome de familia", -"New Addressbook" => "Novo livro de endereços", -"Edit Addressbook" => "Editar livro de endereços", -"Displayname" => "Nome de exibição", -"Active" => "Ativo", -"Save" => "Guardar", -"Submit" => "Submeter", -"Cancel" => "Cancelar", +"Hon. suffixes" => "Sufixos Honoráveis", +"J.D." => "D.J.", +"M.D." => "D.M.", +"D.O." => "Dr.", +"D.C." => "Dr.", +"Ph.D." => "Dr.", +"Esq." => "Esq.", +"Jr." => "Jr.", +"Sn." => "r.", "Import a contacts file" => "Importar um ficheiro de contactos", "Please choose the addressbook" => "Por favor seleccione o livro de endereços", "create a new addressbook" => "Criar um novo livro de endereços", -"Import" => "Importar", +"Name of new addressbook" => "Nome do novo livro de endereços", "Importing contacts" => "A importar os contactos", +"You have no contacts in your addressbook." => "Não tem contactos no seu livro de endereços.", "Add contact" => "Adicionar contacto", +"Select Address Books" => "Selecionar Livros de contactos", +"Enter name" => "Introduzir nome", +"Enter description" => "Introduzir descrição", +"CardDAV syncing addresses" => "CardDAV a sincronizar endereços", "more info" => "mais informação", -"iOS/OS X" => "iOS/OS X" +"Primary address (Kontact et al)" => "Endereço primario (Kontact et al)", +"iOS/OS X" => "iOS/OS X", +"Show CardDav link" => "Mostrar ligação CardDAV", +"Show read-only VCF link" => "Mostrar ligações VCF só de leitura", +"Share" => "Partilhar", +"Download" => "Transferir", +"Edit" => "Editar", +"New Address Book" => "Novo livro de endereços", +"Name" => "Nome", +"Description" => "Descrição", +"Save" => "Guardar", +"Cancel" => "Cancelar", +"More..." => "Mais..." ); diff --git a/apps/contacts/l10n/ro.php b/apps/contacts/l10n/ro.php index 01609756718..291e8d54f7b 100644 --- a/apps/contacts/l10n/ro.php +++ b/apps/contacts/l10n/ro.php @@ -1,42 +1,31 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "(Dez)activarea agendei a întâmpinat o eroare.", -"There was an error adding the contact." => "O eroare a împiedicat adăugarea contactului.", -"Cannot add empty property." => "Nu se poate adăuga un câmp gol.", -"At least one of the address fields has to be filled out." => "Cel puÈ›in unul din câmpurile adresei trebuie completat.", -"Error adding contact property." => "Contactul nu a putut fi adăugat.", +"id is not set." => "ID-ul nu este stabilit", +"Error updating addressbook." => "Eroare la actualizarea agendei.", "No ID provided" => "Nici un ID nu a fost furnizat", "Error setting checksum." => "Eroare la stabilirea sumei de control", "No categories selected for deletion." => "Nici o categorie selectată pentru È™tergere", "No address books found." => "Nici o carte de adrese găsită", "No contacts found." => "Nici un contact găsit", +"There was an error adding the contact." => "O eroare a împiedicat adăugarea contactului.", +"element name is not set." => "numele elementului nu este stabilit.", +"Cannot add empty property." => "Nu se poate adăuga un câmp gol.", +"At least one of the address fields has to be filled out." => "Cel puÈ›in unul din câmpurile adresei trebuie completat.", +"Information about vCard is incorrect. Please reload the page." => "InformaÈ›iile cărÈ›ii de vizită sunt incorecte. Te rog reîncarcă pagina.", "Missing ID" => "ID lipsă", "Error parsing VCard for ID: \"" => "Eroare la prelucrarea VCard-ului pentru ID:\"", -"Cannot add addressbook with an empty name." => "Nu e posibil de adăugat o carte de adrese fără nume", -"Error adding addressbook." => "Agenda nu a putut fi adăugată.", -"Error activating addressbook." => "Eroare la activarea agendei.", +"checksum is not set." => "suma de control nu este stabilită.", "No contact ID was submitted." => "Nici un ID de contact nu a fost transmis", "Error reading contact photo." => "Eroare la citerea fotografiei de contact", "Error saving temporary file." => "Eroare la salvarea fiÈ™ierului temporar.", "The loading photo is not valid." => "Fotografia care se încarcă nu este validă.", -"id is not set." => "ID-ul nu este stabilit", -"Information about vCard is incorrect. Please reload the page." => "InformaÈ›iile cărÈ›ii de vizită sunt incorecte. Te rog reîncarcă pagina.", -"Error deleting contact property." => "Eroare la È™tergerea proprietăților contactului.", "Contact ID is missing." => "ID-ul de contact lipseÈ™te.", -"Missing contact id." => "ID de contact lipsă.", "No photo path was submitted." => "Nici o adresă către fotografie nu a fost transmisă", "File doesn't exist:" => "FiÈ™ierul nu există:", "Error loading image." => "Eroare la încărcarea imaginii.", -"element name is not set." => "numele elementului nu este stabilit.", -"checksum is not set." => "suma de control nu este stabilită.", -"Error updating contact property." => "Eroare la actualizarea proprietăților contactului.", -"Error updating addressbook." => "Eroare la actualizarea agendei.", "Contacts" => "Contacte", "This is not your addressbook." => "Nu se găseÈ™te în agendă.", "Contact could not be found." => "Contactul nu a putut fi găsit.", -"Address" => "Adresă", -"Telephone" => "Telefon", -"Email" => "Email", -"Organization" => "OrganizaÈ›ie", "Work" => "Servicu", "Home" => "Acasă", "Mobile" => "Mobil", @@ -47,21 +36,16 @@ "Video" => "Video", "Pager" => "Pager", "Internet" => "Internet", +"Birthday" => "Zi de naÈ™tere", "{name}'s Birthday" => "Ziua de naÈ™tere a {name}", "Contact" => "Contact", "Add Contact" => "Adaugă contact", "Addressbooks" => "Agende", -"New Address Book" => "Agendă nouă", -"CardDav Link" => "Legătură CardDev", -"Download" => "Descarcă", -"Edit" => "Editează", -"Delete" => "Șterge", -"Download contact" => "Descarcă acest contact", -"Delete contact" => "Șterge contact", "Edit name details" => "Introdu detalii despre nume", +"Organization" => "OrganizaÈ›ie", +"Delete" => "Șterge", "Nickname" => "Pseudonim", "Enter nickname" => "Introdu pseudonim", -"Birthday" => "Zi de naÈ™tere", "dd-mm-yyyy" => "zz-ll-aaaa", "Groups" => "Grupuri", "Separate groups with commas" => "Separă grupurile cu virgule", @@ -72,21 +56,21 @@ "Mail to address" => "Trimite mesaj la e-mail", "Delete email address" => "Șterge e-mail", "Phone" => "Telefon", +"Email" => "Email", +"Address" => "Adresă", +"Download contact" => "Descarcă acest contact", +"Delete contact" => "Șterge contact", "Type" => "Tip", "PO Box" => "CP", "Extended" => "Extins", -"Street" => "Stradă", "City" => "OraÈ™", "Region" => "Regiune", "Zipcode" => "Cod poÈ™tal", "Country" => "Èšară", -"Add" => "Adaugă", "Addressbook" => "Agendă", -"New Addressbook" => "Agendă nouă", -"Edit Addressbook" => "Modifică agenda", -"Displayname" => "Nume afiÈ™at", -"Active" => "Activ", +"Download" => "Descarcă", +"Edit" => "Editează", +"New Address Book" => "Agendă nouă", "Save" => "Salvează", -"Submit" => "Trimite", "Cancel" => "Anulează" ); diff --git a/apps/contacts/l10n/ru.php b/apps/contacts/l10n/ru.php index 01a0e2642b9..010c4507f4e 100644 --- a/apps/contacts/l10n/ru.php +++ b/apps/contacts/l10n/ru.php @@ -1,34 +1,41 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "Ошибка (де)активации адреÑной книги.", -"There was an error adding the contact." => "Произошла ошибка при добавлении контакта.", -"Cannot add empty property." => "Ðевозможно добавить пуÑтой параметр.", -"At least one of the address fields has to be filled out." => "Как минимум одно поле адреÑа должно быть заполнено.", -"Error adding contact property." => "Ошибка Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ð¸ к контакту.", +"id is not set." => "id не уÑтановлен.", +"Cannot update addressbook with an empty name." => "ÐÐµÐ»ÑŒÐ·Ñ Ð¾Ð±Ð½Ð¾Ð²Ð¸Ñ‚ÑŒ адреÑную книгу Ñ Ð¿ÑƒÑтым именем.", +"Error updating addressbook." => "Ошибка Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð°Ð´Ñ€ÐµÑной книги.", "No ID provided" => "ID не предоÑтавлен", "Error setting checksum." => "Ошибка уÑтановки контрольной Ñуммы.", "No categories selected for deletion." => "Категории Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð½Ðµ уÑтановлены.", "No address books found." => "ÐдреÑные книги не найдены.", "No contacts found." => "Контакты не найдены.", +"There was an error adding the contact." => "Произошла ошибка при добавлении контакта.", +"element name is not set." => "Ð¸Ð¼Ñ Ñлемента не уÑтановлено.", +"Could not parse contact: " => "Ðевозможно раÑпознать контакт:", +"Cannot add empty property." => "Ðевозможно добавить пуÑтой параметр.", +"At least one of the address fields has to be filled out." => "Как минимум одно поле адреÑа должно быть заполнено.", +"Trying to add duplicate property: " => "При попытке добавить дубликат:", +"Unknown IM: " => "ÐеизвеÑтный IM:", +"Information about vCard is incorrect. Please reload the page." => "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ vCard некорректна. ПожалуйÑта, обновите Ñтраницу.", "Missing ID" => "ОтÑутÑтвует ID", "Error parsing VCard for ID: \"" => "Ошибка обработки VCard Ð´Ð»Ñ ID: \"", -"Cannot add addressbook with an empty name." => "ÐÐµÐ»ÑŒÐ·Ñ Ð´Ð¾Ð±Ð°Ð²Ð¸Ñ‚ÑŒ адреÑную книгу без имени.", -"Error adding addressbook." => "Ошибка Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð°Ð´Ñ€ÐµÑной книги.", -"Error activating addressbook." => "Ошибка активации адреÑной книги.", +"checksum is not set." => "ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»ÑŒÐ½Ð°Ñ Ñумма не уÑтановлена.", +"Information about vCard is incorrect. Please reload the page: " => "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ vCard не корректна. Перезагрузите Ñтраницу: ", +"Something went FUBAR. " => "Что-то пошло FUBAR.", +"No contact ID was submitted." => "Ðет контакта ID", "Error reading contact photo." => "Ошибка Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ñ„Ð¾Ñ‚Ð¾Ð³Ñ€Ð°Ñ„Ð¸Ð¸ контакта.", "Error saving temporary file." => "Ошибка ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð²Ñ€ÐµÐ¼ÐµÐ½Ð½Ð¾Ð³Ð¾ файла.", "The loading photo is not valid." => "Ð—Ð°Ð³Ñ€ÑƒÐ¶Ð°ÐµÐ¼Ð°Ñ Ñ„Ð¾Ñ‚Ð¾Ð³Ñ€Ð°Ñ„Ð¸Ñ Ð¸Ñпорчена.", -"id is not set." => "id не уÑтановлен.", -"Information about vCard is incorrect. Please reload the page." => "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ vCard некорректна. ПожалуйÑта, обновите Ñтраницу.", -"Error deleting contact property." => "Ошибка ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ð¸ из контакта.", "Contact ID is missing." => "ID контакта отÑутÑтвует.", +"No photo path was submitted." => "Ðет фото по адреÑу.", "File doesn't exist:" => "Файл не ÑущеÑтвует:", "Error loading image." => "Ошибка загрузки картинки.", -"element name is not set." => "Ð¸Ð¼Ñ Ñлемента не уÑтановлено.", -"checksum is not set." => "ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»ÑŒÐ½Ð°Ñ Ñумма не уÑтановлена.", -"Information about vCard is incorrect. Please reload the page: " => "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ vCard не корректна. Перезагрузите Ñтраницу: ", -"Error updating contact property." => "Ошибка Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ð¸ контакта.", -"Cannot update addressbook with an empty name." => "ÐÐµÐ»ÑŒÐ·Ñ Ð¾Ð±Ð½Ð¾Ð²Ð¸Ñ‚ÑŒ адреÑную книгу Ñ Ð¿ÑƒÑтым именем.", -"Error updating addressbook." => "Ошибка Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð°Ð´Ñ€ÐµÑной книги.", +"Error getting contact object." => "Ошибка при получении контактов", +"Error getting PHOTO property." => "Ошибка при получении ФОТО.", +"Error saving contact." => "Ошибка при Ñохранении контактов.", +"Error resizing image" => "Ошибка Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ€Ð°Ð·Ð¼ÐµÑ€Ð° изображений", +"Error cropping image" => "Ошибка обрезки изображений", +"Error creating temporary image" => "Ошибка ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð²Ñ€ÐµÐ¼ÐµÐ½Ð½Ñ‹Ñ… изображений", +"Error finding image: " => "Ошибка поиÑка изображений:", "Error uploading contacts to storage." => "Ошибка загрузки контактов в хранилище.", "There is no error, the file uploaded with success" => "Файл загружен уÑпешно.", "The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Загружаемый файл первоÑходит значение переменной upload_max_filesize, уÑтановленно в php.ini", @@ -36,16 +43,46 @@ "The uploaded file was only partially uploaded" => "Файл загружен чаÑтично", "No file was uploaded" => "Файл не был загружен", "Missing a temporary folder" => "ОтÑутÑтвует Ð²Ñ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ Ð¿Ð°Ð¿ÐºÐ°", +"Couldn't save temporary image: " => "Ðе удалоÑÑŒ Ñохранить временное изображение:", +"Couldn't load temporary image: " => "Ðе удалоÑÑŒ загрузить временное изображение:", +"No file was uploaded. Unknown error" => "Файл не был загружен. ÐеизвеÑÑ‚Ð½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°", "Contacts" => "Контакты", -"Addressbook not found." => "ÐдреÑÐ½Ð°Ñ ÐºÐ½Ð¸Ð³Ð° не найдена.", +"Sorry, this functionality has not been implemented yet" => "К Ñожалению, Ñта Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð½Ðµ была реализована", +"Not implemented" => "Ðе реализовано", +"Couldn't get a valid address." => "Ðе удалоÑÑŒ получить адреÑ.", +"Error" => "Ошибка", +"This property has to be non-empty." => "Ðто ÑвойÑтво должно быть не пуÑтым.", +"Couldn't serialize elements." => "Ðе удалоÑÑŒ Ñериализовать Ñлементы.", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "'deleteProperty' called without type argument. Please report at bugs.owncloud.org", +"Edit name" => "Изменить имÑ", +"No files selected for upload." => "Ðет выбранных файлов Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸.", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "Файл, который вы пытаетеÑÑŒ загрузить превышать макÑимальный размер загружаемых файлов на Ñтом Ñервере.", +"Error loading profile picture." => "Ошибка загрузки Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ñ„Ð¸Ð»Ñ.", +"Select type" => "Выберите тип", +"Some contacts are marked for deletion, but not deleted yet. Please wait for them to be deleted." => "Ðекоторые контакты помечены на удаление, но ещё не удалены. Подождите, пока они удалÑÑŽÑ‚ÑÑ.", +"Do you want to merge these address books?" => "Ð’Ñ‹ хотите Ñоединить Ñти адреÑные книги?", +"Result: " => "Результат:", +" imported, " => "импортировано, ", +" failed." => "не удалоÑÑŒ.", +"Displayname cannot be empty." => "Отображаемое Ð¸Ð¼Ñ Ð½Ðµ может быть пуÑтым.", +"Addressbook not found: " => "ÐдреÑÐ½Ð°Ñ ÐºÐ½Ð¸Ð³Ð° не найдена:", "This is not your addressbook." => "Ðто не ваша адреÑÐ½Ð°Ñ ÐºÐ½Ð¸Ð³Ð°.", "Contact could not be found." => "Контакт не найден.", -"Address" => "ÐдреÑ", -"Telephone" => "Телефон", -"Email" => "Ящик Ñл. почты", -"Organization" => "ОрганизациÑ", +"Jabber" => "Jabber", +"AIM" => "AIM", +"MSN" => "MSN", +"Twitter" => "Twitter", +"GoogleTalk" => "GoogleTalk", +"Facebook" => "Facebook", +"XMPP" => "XMPP", +"ICQ" => "ICQ", +"Yahoo" => "Yahoo", +"Skype" => "Skype", +"QQ" => "QQ", +"GaduGadu" => "GaduGadu", "Work" => "Рабочий", "Home" => "Домашний", +"Other" => "Другое", "Mobile" => "Мобильный", "Text" => "ТекÑÑ‚", "Voice" => "ГолоÑ", @@ -54,25 +91,54 @@ "Video" => "Видео", "Pager" => "Пейджер", "Internet" => "Интернет", +"Birthday" => "День рождениÑ", +"Business" => "БизнеÑ", +"Call" => "Вызов", +"Clients" => "Клиенты", +"Holidays" => "Праздники", +"Ideas" => "Идеи", +"Journey" => "Поездка", +"Jubilee" => "Юбилей", +"Meeting" => "Ð’Ñтреча", +"Personal" => "Личный", +"Projects" => "Проекты", +"Questions" => "ВопроÑÑ‹", "{name}'s Birthday" => "День Ñ€Ð¾Ð¶Ð´ÐµÐ½Ð¸Ñ {name}", "Contact" => "Контакт", "Add Contact" => "Добавить Контакт", +"Import" => "Импорт", +"Settings" => "ÐаÑтройки", "Addressbooks" => "ÐдреÑные книги", -"Configure Address Books" => "ÐаÑтроить ÐдреÑную книгу", -"New Address Book" => "ÐÐ¾Ð²Ð°Ñ Ð°Ð´Ñ€ÐµÑÐ½Ð°Ñ ÐºÐ½Ð¸Ð³Ð°", -"Import from VCF" => "Импортировать из VCF", -"CardDav Link" => "СÑылка CardDAV", -"Download" => "Скачать", -"Edit" => "Редактировать", -"Delete" => "Удалить", -"Download contact" => "Скачать контакт", -"Delete contact" => "Удалить контакт", +"Close" => "Закрыть", +"Keyboard shortcuts" => "ГорÑчие клавиши", +"Navigation" => "ÐавигациÑ", +"Next contact in list" => "Следующий контакт в ÑпиÑке", +"Previous contact in list" => "Предыдущий контакт в ÑпиÑке", +"Expand/collapse current addressbook" => "Развернуть/Ñвернуть текущую адреÑную книгу", +"Next addressbook" => "Ð¡Ð»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ Ð°Ð´Ñ€ÐµÑÐ½Ð°Ñ ÐºÐ½Ð¸Ð³Ð°", +"Previous addressbook" => "ÐŸÑ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð°Ñ Ð°Ð´Ñ€ÐµÑÐ½Ð°Ñ ÐºÐ½Ð¸Ð³Ð°", +"Actions" => "ДейÑтвиÑ", +"Refresh contacts list" => "Обновить ÑпиÑок контактов", +"Add new contact" => "Добавить новый контакт", +"Add new addressbook" => "Добавить новую адреÑную книгу", +"Delete current contact" => "Удалить текущий контакт", "Drop photo to upload" => "ПеретÑните фотографии Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸", +"Delete current photo" => "Удалить текущую фотографию", +"Edit current photo" => "Редактировать текущую фотографию", +"Upload new photo" => "Загрузить новую фотографию", +"Select photo from ownCloud" => "Выбрать фотографию из ownCloud", +"Format custom, Short name, Full name, Reverse or Reverse with comma" => "Формат Краткое имÑ, Полное имÑ", +"Edit name details" => "Изменить детали имени", +"Organization" => "ОрганизациÑ", +"Delete" => "Удалить", "Nickname" => "ПÑевдоним", "Enter nickname" => "Введите пÑевдоним", -"Birthday" => "День рождениÑ", +"Web site" => "Веб-Ñайт", +"http://www.somesite.com" => "http://www.somesite.com", +"Go to web site" => "Перейти на веб-Ñайт", "dd-mm-yyyy" => "dd-mm-yyyy", "Groups" => "Группы", +"Separate groups with commas" => "Разделить группы запÑтыми", "Edit groups" => "Редактировать группы", "Preferred" => "Предпочитаемый", "Please specify a valid email address." => "Укажите дейÑтвительный Ð°Ð´Ñ€ÐµÑ Ñлектронной почты.", @@ -81,47 +147,71 @@ "Delete email address" => "Удалить Ð°Ð´Ñ€ÐµÑ Ñлектронной почты", "Enter phone number" => "ВвеÑти номер телефона", "Delete phone number" => "Удалить номер телефона", +"Delete IM" => "Удалить IM", "View on map" => "Показать на карте", "Edit address details" => "ВвеÑти детали адреÑа", "Add notes here." => "Добавьте заметки здеÑÑŒ.", "Add field" => "Добавить поле", -"Profile picture" => "Фото профилÑ", "Phone" => "Телефон", +"Email" => "Ящик Ñл. почты", +"Address" => "ÐдреÑ", "Note" => "Заметка", -"Delete current photo" => "Удалить текущую фотографию", -"Edit current photo" => "Редактировать текущую фотографию", -"Upload new photo" => "Загрузить новую фотографию", -"Select photo from ownCloud" => "Выбрать фотографию из ownCloud", +"Download contact" => "Скачать контакт", +"Delete contact" => "Удалить контакт", +"The temporary image has been removed from cache." => "Временный образ был удален из кÑша.", "Edit address" => "Редактировать адреÑ", "Type" => "Тип", "PO Box" => "ÐО", +"Street address" => "Улица", +"Street and number" => "Улица и дом", "Extended" => "РаÑширенный", -"Street" => "Улица", +"Apartment number etc." => "Ðомер квартиры и Ñ‚.д.", "City" => "Город", "Region" => "ОблаÑÑ‚ÑŒ", +"E.g. state or province" => "Ðапример, облаÑÑ‚ÑŒ или район", "Zipcode" => "Почтовый индекÑ", +"Postal code" => "Почтовый индекÑ", "Country" => "Страна", -"Edit categories" => "Редактировать категрии", -"Add" => "Добавить", "Addressbook" => "ÐдреÑÐ½Ð°Ñ ÐºÐ½Ð¸Ð³Ð°", +"Hon. prefixes" => "Уважительные префикÑÑ‹", +"Miss" => "МиÑÑ", +"Ms" => "Г-жа", +"Mr" => "Г-н", +"Sir" => "СÑÑ€", +"Mrs" => "Г-жа", +"Dr" => "Доктор", "Given name" => "ИмÑ", "Additional names" => "Дополнительные имена (отчеÑтво)", "Family name" => "ФамилиÑ", -"New Addressbook" => "ÐÐ¾Ð²Ð°Ñ Ð°Ð´Ñ€ÐµÑÐ½Ð°Ñ ÐºÐ½Ð¸Ð³Ð°", -"Edit Addressbook" => "Редактировать адреÑную книгу", -"Displayname" => "Отображаемое имÑ", -"Active" => "Ðктивно", -"Save" => "Сохранить", -"Submit" => "Отправить", -"Cancel" => "Отменить", +"Hon. suffixes" => "Hon. suffixes", +"J.D." => "Уважительные ÑуффикÑÑ‹", +"M.D." => "M.D.", +"D.O." => "D.O.", +"D.C." => "D.C.", +"Ph.D." => "Ph.D.", +"Esq." => "Esq.", +"Jr." => "Jr.", +"Sn." => "Sn.", "Import a contacts file" => "Загрузить файл контактов", "Please choose the addressbook" => "Выберите адреÑную книгу", "create a new addressbook" => "Ñоздать новую адреÑную книгу", "Name of new addressbook" => "Ð˜Ð¼Ñ Ð½Ð¾Ð²Ð¾Ð¹ адреÑной книги", -"Import" => "Импорт", "Importing contacts" => "Импорт контактов", -"You have no contacts in your addressbook." => "Ð’ адреÑной книге еÑÑ‚ÑŒ контакты.", +"You have no contacts in your addressbook." => "Ð’ адреÑной книге нет контактов.", "Add contact" => "Добавить контакт", -"Configure addressbooks" => "ÐаÑтроить адреÑную книгу", -"more info" => "Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ" +"Select Address Books" => "Выбрать адреÑную книгу", +"Enter name" => "Введите имÑ", +"Enter description" => "Ввдите опиÑание", +"CardDAV syncing addresses" => "CardDAV Ñинхронизации адреÑов", +"more info" => "Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ", +"Primary address (Kontact et al)" => "Первичный Ð°Ð´Ñ€ÐµÑ (Kontact и др.)", +"iOS/OS X" => "iOS/OS X", +"Download" => "Скачать", +"Edit" => "Редактировать", +"New Address Book" => "ÐÐ¾Ð²Ð°Ñ Ð°Ð´Ñ€ÐµÑÐ½Ð°Ñ ÐºÐ½Ð¸Ð³Ð°", +"Name" => "ИмÑ", +"Description" => "ОпиÑание", +"Save" => "Сохранить", +"Cancel" => "Отменить", +"More..." => "Ещё..." ); diff --git a/apps/contacts/l10n/sk_SK.php b/apps/contacts/l10n/sk_SK.php index 6e5ebe3bd3c..54ae324d93c 100644 --- a/apps/contacts/l10n/sk_SK.php +++ b/apps/contacts/l10n/sk_SK.php @@ -1,39 +1,39 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "Chyba (de)aktivácie adresára.", -"There was an error adding the contact." => "Vyskytla sa chyba pri pridávanà kontaktu.", -"Cannot add empty property." => "Nemôžem pridaÅ¥ prázdny údaj.", -"At least one of the address fields has to be filled out." => "Musà byÅ¥ uvedený aspoň jeden adresný údaj.", -"Trying to add duplicate property: " => "Pokúšate sa pridaÅ¥ rovnaký atribút:", -"Error adding contact property." => "Chyba pridania údaju kontaktu", +"id is not set." => "ID nie je nastavené.", +"Cannot update addressbook with an empty name." => "Nedá sa upraviÅ¥ adresár s prázdnym menom.", +"Error updating addressbook." => "Chyba aktualizácie adresára.", "No ID provided" => "ID nezadané", "Error setting checksum." => "Chyba pri nastavovanà kontrolného súÄtu.", "No categories selected for deletion." => "Žiadne kategórie neboli vybraté na odstránenie.", "No address books found." => "Žiadny adresár nenájdený.", "No contacts found." => "Žiadne kontakty nenájdené.", +"There was an error adding the contact." => "Vyskytla sa chyba pri pridávanà kontaktu.", +"element name is not set." => "meno elementu nie je nastavené.", +"Cannot add empty property." => "Nemôžem pridaÅ¥ prázdny údaj.", +"At least one of the address fields has to be filled out." => "Musà byÅ¥ uvedený aspoň jeden adresný údaj.", +"Trying to add duplicate property: " => "Pokúšate sa pridaÅ¥ rovnaký atribút:", +"Information about vCard is incorrect. Please reload the page." => "Informácie o vCard sú neplatné. ProsÃm obnovte stránku.", "Missing ID" => "Chýba ID", "Error parsing VCard for ID: \"" => "Chyba pri vyňatà ID z VCard:", -"Cannot add addressbook with an empty name." => "Nedá sa pridaÅ¥ adresár s prázdnym menom.", -"Error adding addressbook." => "Chyba poÄas pridávania adresára.", -"Error activating addressbook." => "Chyba aktivovania adresára.", +"checksum is not set." => "kontrolný súÄet nie je nastavený.", +"Information about vCard is incorrect. Please reload the page: " => "Informácia o vCard je nesprávna. Obnovte stránku, prosÃm.", +"Something went FUBAR. " => "NieÄo sa pokazilo.", "No contact ID was submitted." => "Nebolo nastavené ID kontaktu.", "Error reading contact photo." => "Chyba pri ÄÃtanà fotky kontaktu.", "Error saving temporary file." => "Chyba pri ukladanà doÄasného súboru.", "The loading photo is not valid." => "NaÄÃtaná fotka je vadná.", -"id is not set." => "ID nie je nastavené.", -"Information about vCard is incorrect. Please reload the page." => "Informácie o vCard sú neplatné. ProsÃm obnovte stránku.", -"Error deleting contact property." => "Chyba odstránenia údaju kontaktu.", "Contact ID is missing." => "Chýba ID kontaktu.", -"Missing contact id." => "Chýba ID kontaktu.", "No photo path was submitted." => "Žiadna fotka nebola poslaná.", "File doesn't exist:" => "Súbor neexistuje:", "Error loading image." => "Chyba pri nahrávanà obrázka.", -"element name is not set." => "meno elementu nie je nastavené.", -"checksum is not set." => "kontrolný súÄet nie je nastavený.", -"Information about vCard is incorrect. Please reload the page: " => "Informácia o vCard je nesprávna. Obnovte stránku, prosÃm.", -"Something went FUBAR. " => "NieÄo sa pokazilo.", -"Error updating contact property." => "Chyba aktualizovania údaju kontaktu.", -"Cannot update addressbook with an empty name." => "Nedá sa upraviÅ¥ adresár s prázdnym menom.", -"Error updating addressbook." => "Chyba aktualizácie adresára.", +"Error getting contact object." => "Chyba poÄas prevzatia objektu kontakt.", +"Error getting PHOTO property." => "Chyba poÄas zÃskavania fotky.", +"Error saving contact." => "Chyba poÄas ukladania kontaktu.", +"Error resizing image" => "Chyba poÄas zmeny obrázku.", +"Error cropping image" => "Chyba poÄas orezania obrázku.", +"Error creating temporary image" => "Chyba poÄas vytvárania doÄasného obrázku.", +"Error finding image: " => "Chyba vyhľadania obrázku: ", "Error uploading contacts to storage." => "Chyba pri ukladanà kontaktov na úložisko.", "There is no error, the file uploaded with success" => "Nevyskytla sa žiadna chyba, súbor úspeÅ¡ne uložené.", "The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Ukladaný súbor prekraÄuje nastavenie upload_max_filesize v php.ini", @@ -41,17 +41,29 @@ "The uploaded file was only partially uploaded" => "Ukladaný súbor sa nahral len ÄiastoÄne", "No file was uploaded" => "Žiadny súbor nebol uložený", "Missing a temporary folder" => "Chýba doÄasný prieÄinok", +"Couldn't save temporary image: " => "Nemôžem uložiÅ¥ doÄasný obrázok: ", +"Couldn't load temporary image: " => "Nemôžem naÄÃtaÅ¥ doÄasný obrázok: ", +"No file was uploaded. Unknown error" => "Žiaden súbor nebol odoslaný. Neznáma chyba", "Contacts" => "Kontakty", -"Drop a VCF file to import contacts." => "Pretiahnite VCF súbor pre import kontaktov.", -"Addressbook not found." => "Adresár sa nenaÅ¡iel.", +"Sorry, this functionality has not been implemented yet" => "Bohužiaľ, táto funkcia eÅ¡te nebola implementovaná", +"Not implemented" => "Neimplementované", +"Couldn't get a valid address." => "Nemôžem zÃskaÅ¥ platnú adresu.", +"Error" => "Chyba", +"This property has to be non-empty." => "Tento parameter nemôže byÅ¥ prázdny.", +"Couldn't serialize elements." => "Nemôžem previesÅ¥ prvky.", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "'deleteProperty' zavolané bez argument. ProsÃm oznámte chybu na bugs.owncloud.org", +"Edit name" => "UpraviÅ¥ meno", +"No files selected for upload." => "Žiadne súbory neboli vybrané k nahratiu", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "Súbor, ktorý sa pokúšate nahraÅ¥, presahuje maximálnu povolenú veľkosÅ¥.", +"Select type" => "VybraÅ¥ typ", +"Result: " => "Výsledok: ", +" imported, " => " importovaných, ", +" failed." => " zlyhaných.", "This is not your addressbook." => "Toto nie je váš adresár.", "Contact could not be found." => "Kontakt nebol nájdený.", -"Address" => "Adresa", -"Telephone" => "Telefón", -"Email" => "E-mail", -"Organization" => "Organizácia", "Work" => "Práca", "Home" => "Domov", +"Other" => "Iné", "Mobile" => "Mobil", "Text" => "SMS", "Voice" => "Odkazová schránka", @@ -60,25 +72,39 @@ "Video" => "Video", "Pager" => "Pager", "Internet" => "Internet", +"Birthday" => "Narodeniny", +"Business" => "Biznis", +"Clients" => "Klienti", +"Holidays" => "Prázdniny", +"Meeting" => "Stretnutie", +"Projects" => "Projekty", +"Questions" => "Otázky", "{name}'s Birthday" => "Narodeniny {name}", "Contact" => "Kontakt", "Add Contact" => "PridaÅ¥ Kontakt.", +"Import" => "ImportovaÅ¥", "Addressbooks" => "Adresáre", -"Configure Address Books" => "NastaviÅ¥ adresáre", -"New Address Book" => "Nový adresár", -"Import from VCF" => "ImportovaÅ¥ z VCF", -"CardDav Link" => "CardDav odkaz", -"Download" => "StiahnuÅ¥", -"Edit" => "UpraviÅ¥", -"Delete" => "OdstrániÅ¥", -"Download contact" => "StiahnuÅ¥ kontakt", -"Delete contact" => "OdstrániÅ¥ kontakt", +"Close" => "ZatvoriÅ¥", +"Keyboard shortcuts" => "Klávesové skratky", +"Navigation" => "Navigácia", +"Next contact in list" => "ÄŽalÅ¡Ã kontakt v zozname", +"Previous contact in list" => "Predchádzajúci kontakt v zozname", +"Actions" => "Akcie", +"Refresh contacts list" => "Obnov zoznam kontaktov", +"Add new contact" => "Pridaj nový kontakt", +"Add new addressbook" => "Pridaj nový adresár", +"Delete current contact" => "Vymaž súÄasný kontakt", "Drop photo to upload" => "Pretiahnite sem fotku pre nahratie", +"Delete current photo" => "OdstrániÅ¥ súÄasnú fotku", +"Edit current photo" => "UpraviÅ¥ súÄasnú fotku", +"Upload new photo" => "NahraÅ¥ novú fotku", +"Select photo from ownCloud" => "VybraÅ¥ fotku z ownCloud", "Format custom, Short name, Full name, Reverse or Reverse with comma" => "Formát vlastný, krátke meno, celé meno, obrátené alebo obrátené s Äiarkami", "Edit name details" => "UpraviÅ¥ podrobnosti mena", +"Organization" => "Organizácia", +"Delete" => "OdstrániÅ¥", "Nickname" => "Prezývka", "Enter nickname" => "Zadajte prezývku", -"Birthday" => "Narodeniny", "dd-mm-yyyy" => "dd. mm. yyyy", "Groups" => "Skupiny", "Separate groups with commas" => "Oddelte skupiny Äiarkami", @@ -94,24 +120,24 @@ "Edit address details" => "UpraviÅ¥ podrobnosti adresy", "Add notes here." => "Tu môžete pridaÅ¥ poznámky.", "Add field" => "PridaÅ¥ pole", -"Profile picture" => "Profilová fotka", "Phone" => "Telefón", +"Email" => "E-mail", +"Address" => "Adresa", "Note" => "Poznámka", -"Delete current photo" => "OdstrániÅ¥ súÄasnú fotku", -"Edit current photo" => "UpraviÅ¥ súÄasnú fotku", -"Upload new photo" => "NahraÅ¥ novú fotku", -"Select photo from ownCloud" => "VybraÅ¥ fotku z ownCloud", +"Download contact" => "StiahnuÅ¥ kontakt", +"Delete contact" => "OdstrániÅ¥ kontakt", +"The temporary image has been removed from cache." => "DoÄasný obrázok bol odstránený z cache.", "Edit address" => "UpraviÅ¥ adresu", "Type" => "Typ", "PO Box" => "PO Box", +"Street address" => "Ulica", +"Street and number" => "Ulica a ÄÃslo", "Extended" => "RozÅ¡Ãrené", -"Street" => "Ulica", "City" => "Mesto", "Region" => "Región", "Zipcode" => "PSÄŒ", +"Postal code" => "PSÄŒ", "Country" => "Krajina", -"Edit categories" => "UpraviÅ¥ kategórie", -"Add" => "PridaÅ¥", "Addressbook" => "Adresár", "Hon. prefixes" => "Tituly pred", "Miss" => "SleÄna", @@ -126,30 +152,33 @@ "Hon. suffixes" => "Tituly za", "J.D." => "JUDr.", "M.D." => "MUDr.", +"D.O." => "D.O.", +"D.C." => "D.C.", "Ph.D." => "Ph.D.", "Esq." => "Esq.", "Jr." => "ml.", "Sn." => "st.", -"New Addressbook" => "Nový Adresár", -"Edit Addressbook" => "UpraviÅ¥ Adresár", -"Displayname" => "Zobrazené meno", -"Active" => "AktÃvny", -"Save" => "UložiÅ¥", -"Submit" => "OdoslaÅ¥", -"Cancel" => "ZruÅ¡iÅ¥", "Import a contacts file" => "ImportovaÅ¥ súbor kontaktu", "Please choose the addressbook" => "ProsÃm zvolte adresár", "create a new addressbook" => "vytvoriÅ¥ nový adresár", "Name of new addressbook" => "Meno nového adresára", -"Import" => "ImportovaÅ¥", "Importing contacts" => "Importovanie kontaktov", +"Contacts imported successfully" => "Kontakty úspeÅ¡ne importované", +"Close Dialog" => "ZatvoriÅ¥ ponuku", +"Import Addressbook" => "Importovanie adresára", "Select address book to import to:" => "Vyberte adresár, do ktorého chcete importovaÅ¥:", +"Drop a VCF file to import contacts." => "Pretiahnite VCF súbor pre import kontaktov.", "Select from HD" => "Vyberte z pevného disku", "You have no contacts in your addressbook." => "Nemáte žiadne kontakty v adresári.", "Add contact" => "PridaÅ¥ kontakt", -"Configure addressbooks" => "NastaviÅ¥ adresáre", +"Enter name" => "Zadaj meno", "CardDAV syncing addresses" => "Adresy pre synchronizáciu s CardDAV", "more info" => "viac informáciÃ", "Primary address (Kontact et al)" => "Predvolená adresa (Kontakt etc)", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Download" => "StiahnuÅ¥", +"Edit" => "UpraviÅ¥", +"New Address Book" => "Nový adresár", +"Save" => "UložiÅ¥", +"Cancel" => "ZruÅ¡iÅ¥" ); diff --git a/apps/contacts/l10n/sl.php b/apps/contacts/l10n/sl.php index bfba23fabcf..e404b7a5e94 100644 --- a/apps/contacts/l10n/sl.php +++ b/apps/contacts/l10n/sl.php @@ -1,39 +1,42 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "Napaka med (de)aktivacijo imenika.", -"There was an error adding the contact." => "Med dodajanjem stika je priÅ¡lo do napake", -"Cannot add empty property." => "Ne morem dodati prazne lastnosti.", -"At least one of the address fields has to be filled out." => "Vsaj eno izmed polj je Å¡e potrebno izpolniti.", -"Trying to add duplicate property: " => "PoskuÅ¡am dodati podvojeno lastnost:", -"Error adding contact property." => "Napaka pri dodajanju informacije o stiku.", +"id is not set." => "id ni nastavljen.", +"Cannot update addressbook with an empty name." => "Ne morem posodobiti imenika s praznim imenom.", +"Error updating addressbook." => "Napaka pri posodabljanju imenika.", "No ID provided" => "ID ni bil podan", "Error setting checksum." => "Napaka pri nastavljanju nadzorne vsote.", "No categories selected for deletion." => "Nobena kategorija ni bila izbrana za izbris.", "No address books found." => "Ni bilo najdenih imenikov.", "No contacts found." => "Ni bilo najdenih stikov.", +"There was an error adding the contact." => "Med dodajanjem stika je priÅ¡lo do napake", +"element name is not set." => "ime elementa ni nastavljeno.", +"Could not parse contact: " => "Ne morem razÄleniti stika:", +"Cannot add empty property." => "Ne morem dodati prazne lastnosti.", +"At least one of the address fields has to be filled out." => "Vsaj eno izmed polj je Å¡e potrebno izpolniti.", +"Trying to add duplicate property: " => "PoskuÅ¡am dodati podvojeno lastnost:", +"Missing IM parameter." => "ManjkajoÄ IM parameter.", +"Unknown IM: " => "Neznan IM:", +"Information about vCard is incorrect. Please reload the page." => "Informacije o vCard niso pravilne. Prosimo, Äe ponovno naložite stran.", "Missing ID" => "ManjkajoÄ ID", "Error parsing VCard for ID: \"" => "Napaka pri razÄlenjevanju VCard za ID: \"", -"Cannot add addressbook with an empty name." => "Ne morem dodati imenika s praznim imenom.", -"Error adding addressbook." => "Napaka pri dodajanju imenika.", -"Error activating addressbook." => "Napaka pri aktiviranju imenika.", +"checksum is not set." => "nadzorna vsota ni nastavljena.", +"Information about vCard is incorrect. Please reload the page: " => "Informacija o vCard je napaÄna. Prosimo, Äe ponovno naložite stran: ", +"Something went FUBAR. " => "Nekaj je Å¡lo v franže. ", "No contact ID was submitted." => "ID stika ni bil poslan.", "Error reading contact photo." => "Napaka pri branju slike stika.", "Error saving temporary file." => "Napaka pri shranjevanju zaÄasne datoteke.", "The loading photo is not valid." => "Slika, ki se nalaga ni veljavna.", -"id is not set." => "id ni nastavljen.", -"Information about vCard is incorrect. Please reload the page." => "Informacije o vCard niso pravilne. Prosimo, Äe ponovno naložite stran.", -"Error deleting contact property." => "Napaka pri brisanju lastnosti stika.", "Contact ID is missing." => "Manjka ID stika.", -"Missing contact id." => "Manjka id stika.", "No photo path was submitted." => "Pot slike ni bila poslana.", "File doesn't exist:" => "Datoteka ne obstaja:", "Error loading image." => "Napaka pri nalaganju slike.", -"element name is not set." => "ime elementa ni nastavljeno.", -"checksum is not set." => "nadzorna vsota ni nastavljena.", -"Information about vCard is incorrect. Please reload the page: " => "Informacija o vCard je napaÄna. Prosimo, Äe ponovno naložite stran: ", -"Something went FUBAR. " => "Nekaj je Å¡lo v franže. ", -"Error updating contact property." => "Napaka pri posodabljanju lastnosti stika.", -"Cannot update addressbook with an empty name." => "Ne morem posodobiti imenika s praznim imenom.", -"Error updating addressbook." => "Napaka pri posodabljanju imenika.", +"Error getting contact object." => "Napaka pri pridobivanju kontakta predmeta.", +"Error getting PHOTO property." => "Napaka pri pridobivanju lastnosti fotografije.", +"Error saving contact." => "Napaka pri shranjevanju stika.", +"Error resizing image" => "Napaka pri spreminjanju velikosti slike", +"Error cropping image" => "Napaka pri obrezovanju slike", +"Error creating temporary image" => "Napaka pri ustvarjanju zaÄasne slike", +"Error finding image: " => "Napaka pri iskanju datoteke: ", "Error uploading contacts to storage." => "Napaka pri nalaganju stikov v hrambo.", "There is no error, the file uploaded with success" => "Datoteka je bila uspeÅ¡no naložena.", "The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Naložena datoteka presega velikost, ki jo doloÄa parameter upload_max_filesize v datoteki php.ini", @@ -41,17 +44,46 @@ "The uploaded file was only partially uploaded" => "Datoteka je bila le delno naložena", "No file was uploaded" => "Nobena datoteka ni bila naložena", "Missing a temporary folder" => "Manjka zaÄasna mapa", +"Couldn't save temporary image: " => "ZaÄasne slike ni bilo mogoÄe shraniti: ", +"Couldn't load temporary image: " => "ZaÄasne slike ni bilo mogoÄe naložiti: ", +"No file was uploaded. Unknown error" => "Nobena datoteka ni bila naložena. Neznana napaka", "Contacts" => "Stiki", -"Drop a VCF file to import contacts." => "Za uvoz stikov spustite VCF datoteko tukaj.", -"Addressbook not found." => "Imenik ni bil najden.", +"Sorry, this functionality has not been implemented yet" => "Žal ta funkcionalnost Å¡e ni podprta", +"Not implemented" => "Ni podprto", +"Couldn't get a valid address." => "Ne morem dobiti veljavnega naslova.", +"Error" => "Napaka", +"This property has to be non-empty." => "Ta lastnost ne sme biti prazna", +"Couldn't serialize elements." => "Predmetov ni bilo mogoÄe dati v zaporedje.", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "\"deleteProperty\" je bila klicana brez vrste argumenta. Prosimo, Äe oddate poroÄilo o napaki na bugs.owncloud.org", +"Edit name" => "Uredi ime", +"No files selected for upload." => "Nobena datoteka ni bila izbrana za nalaganje.", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "Datoteka, ki jo poskuÅ¡ate naložiti, presega najveÄjo dovoljeno velikost za nalaganje na tem strežniku.", +"Error loading profile picture." => "Napaka pri nalaganju slike profila.", +"Select type" => "Izberite vrsto", +"Some contacts are marked for deletion, but not deleted yet. Please wait for them to be deleted." => "Nekateri stiki so oznaÄeni za izbris, vendar Å¡e niso izbrisani. Prosimo, Äe poÄakate na njihov izbris.", +"Do you want to merge these address books?" => "Ali želite združiti adresarje?", +"Result: " => "Rezultati: ", +" imported, " => " uvoženih, ", +" failed." => " je spodletelo.", +"Displayname cannot be empty." => "Ime za prikaz ne more biti prazno.", +"Addressbook not found: " => "Adresar ni bil najden:", "This is not your addressbook." => "To ni vaÅ¡ imenik.", "Contact could not be found." => "Stika ni bilo mogoÄe najti.", -"Address" => "Naslov", -"Telephone" => "Telefon", -"Email" => "E-poÅ¡ta", -"Organization" => "Organizacija", +"Jabber" => "Jabber", +"AIM" => "AIM", +"MSN" => "MSN", +"Twitter" => "Twitter", +"GoogleTalk" => "GoogleTalk", +"Facebook" => "Facebook", +"XMPP" => "XMPP", +"ICQ" => "ICQ", +"Yahoo" => "Yahoo", +"Skype" => "Skype", +"QQ" => "QQ", +"GaduGadu" => "GaduGadu", "Work" => "Delo", "Home" => "Doma", +"Other" => "Drugo", "Mobile" => "Mobilni telefon", "Text" => "Besedilo", "Voice" => "Glas", @@ -60,25 +92,52 @@ "Video" => "Video", "Pager" => "Pozivnik", "Internet" => "Internet", +"Birthday" => "Rojstni dan", +"Business" => "Poslovno", +"Call" => "Klic", +"Clients" => "Stranka", +"Deliverer" => "Dostavljalec", +"Holidays" => "Prazniki", +"Ideas" => "Ideje", +"Journey" => "Potovanje", +"Jubilee" => "Jubilej", +"Meeting" => "Sestanek", +"Personal" => "Osebno", +"Projects" => "Projekti", +"Questions" => "VpraÅ¡anja", "{name}'s Birthday" => "{name} - rojstni dan", "Contact" => "Stik", "Add Contact" => "Dodaj stik", +"Import" => "Uvozi", +"Settings" => "Nastavitve", "Addressbooks" => "Imeniki", -"Configure Address Books" => "Nastavi imenike", -"New Address Book" => "Nov imenik", -"Import from VCF" => "Uvozi iz VCF", -"CardDav Link" => "CardDav povezava", -"Download" => "Prenesi", -"Edit" => "Uredi", -"Delete" => "IzbriÅ¡i", -"Download contact" => "Prenesi stik", -"Delete contact" => "IzbriÅ¡i stik", +"Close" => "Zapri", +"Keyboard shortcuts" => "Bližnjice na tipkovnici", +"Navigation" => "Krmarjenje", +"Next contact in list" => "Naslednji stik na seznamu", +"Previous contact in list" => "Predhodni stik na seznamu", +"Expand/collapse current addressbook" => "RazÅ¡iri/skrÄi trenutni adresar", +"Next addressbook" => "Naslednji adresar", +"Previous addressbook" => "Predhodni adresar", +"Actions" => "Dejanja", +"Refresh contacts list" => "Osveži seznam stikov", +"Add new contact" => "Dodaj nov stik", +"Add new addressbook" => "Dodaj nov adresar", +"Delete current contact" => "IzbriÅ¡i trenutni stik", "Drop photo to upload" => "Spustite sliko tukaj, da bi jo naložili", +"Delete current photo" => "IzbriÅ¡i trenutno sliko", +"Edit current photo" => "Uredi trenutno sliko", +"Upload new photo" => "Naloži novo sliko", +"Select photo from ownCloud" => "Izberi sliko iz ownCloud", "Format custom, Short name, Full name, Reverse or Reverse with comma" => "Format po meri, Kratko ime, Polno ime, Obratno ali Obratno z vejico", "Edit name details" => "Uredite podrobnosti imena", +"Organization" => "Organizacija", +"Delete" => "IzbriÅ¡i", "Nickname" => "Vzdevek", "Enter nickname" => "Vnesite vzdevek", -"Birthday" => "Rojstni dan", +"Web site" => "Spletna stran", +"http://www.somesite.com" => "http://www.nekastran.si", +"Go to web site" => "Pojdi na spletno stran", "dd-mm-yyyy" => "dd. mm. yyyy", "Groups" => "Skupine", "Separate groups with commas" => "Skupine loÄite z vejicami", @@ -90,28 +149,33 @@ "Delete email address" => "IzbriÅ¡i e-poÅ¡tni naslov", "Enter phone number" => "VpiÅ¡i telefonsko Å¡tevilko", "Delete phone number" => "IzbriÅ¡i telefonsko Å¡tevilko", +"Instant Messenger" => "TakojÅ¡ni sporoÄilnik", +"Delete IM" => "IzbriÅ¡i IM", "View on map" => "Prikaz na zemljevidu", "Edit address details" => "Uredi podrobnosti", "Add notes here." => "Opombe dodajte tukaj.", "Add field" => "Dodaj polje", -"Profile picture" => "Slika profila", "Phone" => "Telefon", +"Email" => "E-poÅ¡ta", +"Instant Messaging" => "Neposredno sporoÄanje", +"Address" => "Naslov", "Note" => "Opomba", -"Delete current photo" => "IzbriÅ¡i trenutno sliko", -"Edit current photo" => "Uredi trenutno sliko", -"Upload new photo" => "Naloži novo sliko", -"Select photo from ownCloud" => "Izberi sliko iz ownCloud", +"Download contact" => "Prenesi stik", +"Delete contact" => "IzbriÅ¡i stik", +"The temporary image has been removed from cache." => "ZaÄasna slika je bila odstranjena iz predpomnilnika.", "Edit address" => "Uredi naslov", "Type" => "Vrsta", "PO Box" => "PoÅ¡tni predal", +"Street address" => "UliÄni naslov", +"Street and number" => "Ulica in Å¡telika", "Extended" => "RazÅ¡irjeno", -"Street" => "Ulica", +"Apartment number etc." => "Å tevilka stanovanja itd.", "City" => "Mesto", "Region" => "Regija", +"E.g. state or province" => "Npr. dežela ali pokrajina", "Zipcode" => "PoÅ¡tna Å¡t.", +"Postal code" => "PoÅ¡tna Å¡tevilka", "Country" => "Dežela", -"Edit categories" => "Uredi kategorije", -"Add" => "Dodaj", "Addressbook" => "Imenik", "Hon. prefixes" => "Predpone", "Miss" => "gdÄ.", @@ -132,26 +196,35 @@ "Esq." => "Esq.", "Jr." => "mlajÅ¡i", "Sn." => "starejÅ¡i", -"New Addressbook" => "Nov imenik", -"Edit Addressbook" => "Uredi imenik", -"Displayname" => "Ime za prikaz", -"Active" => "Aktiven", -"Save" => "Shrani", -"Submit" => "Potrdi", -"Cancel" => "PrekliÄi", "Import a contacts file" => "Uvozi datoteko s stiki", "Please choose the addressbook" => "Prosimo, Äe izberete imenik", "create a new addressbook" => "Ustvari nov imenik", "Name of new addressbook" => "Ime novega imenika", -"Import" => "Uvozi", "Importing contacts" => "Uvažam stike", +"Contacts imported successfully" => "Stiki so bili uspeÅ¡no uvoženi", +"Close Dialog" => "Zapri dialog", +"Import Addressbook" => "Uvozi imenik", "Select address book to import to:" => "Izberite imenik v katerega boste uvažali:", +"Drop a VCF file to import contacts." => "Za uvoz stikov spustite VCF datoteko tukaj.", "Select from HD" => "Izberi iz HD", "You have no contacts in your addressbook." => "V vaÅ¡em imeniku ni stikov.", "Add contact" => "Dodaj stik", -"Configure addressbooks" => "Nastavi imenike", +"Select Address Books" => "Izberite adresarje", +"Enter name" => "Vnesite ime", +"Enter description" => "Vnesite opis", "CardDAV syncing addresses" => "CardDAV naslovi za sinhronizacijo", "more info" => "veÄ informacij", "Primary address (Kontact et al)" => "Primarni naslov (za kontakt et al)", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Show CardDav link" => "Pokaži CardDav povezavo", +"Show read-only VCF link" => "Pokaži VCF povezavo samo za branje", +"Share" => "Souporaba", +"Download" => "Prenesi", +"Edit" => "Uredi", +"New Address Book" => "Nov imenik", +"Name" => "Ime", +"Description" => "Opis", +"Save" => "Shrani", +"Cancel" => "PrekliÄi", +"More..." => "VeÄ..." ); diff --git a/apps/contacts/l10n/sr.php b/apps/contacts/l10n/sr.php index aad88f3603c..f54741c182f 100644 --- a/apps/contacts/l10n/sr.php +++ b/apps/contacts/l10n/sr.php @@ -3,10 +3,6 @@ "Contacts" => "Контакти", "This is not your addressbook." => "Ово није ваш адреÑар.", "Contact could not be found." => "Контакт Ñе не може наћи.", -"Address" => "ÐдреÑа", -"Telephone" => "Телефон", -"Email" => "Е-маил", -"Organization" => "Организација", "Work" => "ПоÑао", "Home" => "Кућа", "Mobile" => "Мобилни", @@ -15,33 +11,29 @@ "Fax" => "ФакÑ", "Video" => "Видео", "Pager" => "Пејџер", +"Birthday" => "Рођендан", "Contact" => "Контакт", "Add Contact" => "Додај контакт", "Addressbooks" => "ÐдреÑар", -"New Address Book" => "Ðови адреÑар", -"Download" => "Преузимање", -"Edit" => "Уреди", +"Organization" => "Организација", "Delete" => "Обриши", -"Download contact" => "Преузми контакт", -"Delete contact" => "Обриши контакт", -"Birthday" => "Рођендан", "Preferred" => "Пожељан", "Phone" => "Телефон", +"Email" => "Е-маил", +"Address" => "ÐдреÑа", +"Download contact" => "Преузми контакт", +"Delete contact" => "Обриши контакт", "Type" => "Тип", "PO Box" => "ПоштанÑки број", "Extended" => "Прошири", -"Street" => "Улица", "City" => "Град", "Region" => "Регија", "Zipcode" => "Зип код", "Country" => "Земља", -"Add" => "Додај", "Addressbook" => "ÐдреÑар", -"New Addressbook" => "Ðови адреÑар", -"Edit Addressbook" => "Уреди адреÑар", -"Displayname" => "Приказано име", -"Active" => "Ðктиван", +"Download" => "Преузимање", +"Edit" => "Уреди", +"New Address Book" => "Ðови адреÑар", "Save" => "Сними", -"Submit" => "Пошаљи", "Cancel" => "Откажи" ); diff --git a/apps/contacts/l10n/sr@latin.php b/apps/contacts/l10n/sr@latin.php index a76c1047907..f06539ab43e 100644 --- a/apps/contacts/l10n/sr@latin.php +++ b/apps/contacts/l10n/sr@latin.php @@ -2,10 +2,6 @@ "Information about vCard is incorrect. Please reload the page." => "Podaci o vKarti su neispravni. Ponovo uÄitajte stranicu.", "This is not your addressbook." => "Ovo nije vaÅ¡ adresar.", "Contact could not be found." => "Kontakt se ne može naći.", -"Address" => "Adresa", -"Telephone" => "Telefon", -"Email" => "E-mail", -"Organization" => "Organizacija", "Work" => "Posao", "Home" => "Kuća", "Mobile" => "Mobilni", @@ -14,16 +10,18 @@ "Fax" => "Faks", "Video" => "Video", "Pager" => "Pejdžer", +"Birthday" => "RoÄ‘endan", "Add Contact" => "Dodaj kontakt", -"Edit" => "Uredi", +"Organization" => "Organizacija", "Delete" => "ObriÅ¡i", -"Birthday" => "RoÄ‘endan", "Phone" => "Telefon", +"Email" => "E-mail", +"Address" => "Adresa", "PO Box" => "PoÅ¡tanski broj", "Extended" => "ProÅ¡iri", -"Street" => "Ulica", "City" => "Grad", "Region" => "Regija", "Zipcode" => "Zip kod", -"Country" => "Zemlja" +"Country" => "Zemlja", +"Edit" => "Uredi" ); diff --git a/apps/contacts/l10n/sv.php b/apps/contacts/l10n/sv.php index 8d5d37d2484..a7721753b4b 100644 --- a/apps/contacts/l10n/sv.php +++ b/apps/contacts/l10n/sv.php @@ -1,35 +1,42 @@ <?php $TRANSLATIONS = array( -"Error (de)activating addressbook." => "Fel när (av)aktivera adressbok", -"There was an error adding the contact." => "Det uppstod ett fel när kontakt skulle läggas till", -"Cannot add empty property." => "Kan inte lägga till en tom egenskap", -"At least one of the address fields has to be filled out." => "Minst ett fält mÃ¥ste fyllas i", -"Error adding contact property." => "Fel när kontaktegenskap skulle läggas till", +"Error (de)activating addressbook." => "Fel (av)aktivera adressbok.", +"id is not set." => "ID är inte satt.", +"Cannot update addressbook with an empty name." => "Kan inte uppdatera adressboken med ett tomt namn.", +"Error updating addressbook." => "Fel uppstod när adressbok skulle uppdateras.", "No ID provided" => "Inget ID angett", "Error setting checksum." => "Fel uppstod när kontrollsumma skulle sättas.", "No categories selected for deletion." => "Inga kategorier valda för borttaging", "No address books found." => "Ingen adressbok funnen.", "No contacts found." => "Inga kontakter funna.", +"There was an error adding the contact." => "Det uppstod ett fel när kontakten skulle läggas till.", +"element name is not set." => "elementnamn ej angett.", +"Could not parse contact: " => "Kunde inte läsa kontakt:", +"Cannot add empty property." => "Kan inte lägga till en tom egenskap.", +"At least one of the address fields has to be filled out." => "Minst ett fält mÃ¥ste fyllas i.", +"Trying to add duplicate property: " => "Försöker lägga till dubblett:", +"Missing IM parameter." => "IM parameter saknas.", +"Unknown IM: " => "Okänt IM:", +"Information about vCard is incorrect. Please reload the page." => "Information om vCard är felaktigt. Vänligen ladda om sidan.", "Missing ID" => "ID saknas", -"Cannot add addressbook with an empty name." => "Kan inte lägga till adressbok med ett tomt namn.", -"Error adding addressbook." => "Fel när adressbok skulle läggas till", -"Error activating addressbook." => "Fel uppstod när adressbok skulle aktiveras", +"Error parsing VCard for ID: \"" => "Fel vid läsning av VCard för ID: \"", +"checksum is not set." => "kontrollsumma är inte satt.", +"Information about vCard is incorrect. Please reload the page: " => "Informationen om vCard är fel. Ladda om sidan:", +"Something went FUBAR. " => "NÃ¥got gick fel.", "No contact ID was submitted." => "Inget kontakt-ID angavs.", -"Error reading contact photo." => "Fel uppstod när ", +"Error reading contact photo." => "Fel uppstod vid läsning av kontaktfoto.", "Error saving temporary file." => "Fel uppstod när temporär fil skulle sparas.", "The loading photo is not valid." => "Det laddade fotot är inte giltigt.", -"id is not set." => "ID är inte satt.", -"Information about vCard is incorrect. Please reload the page." => "Information om vCard är felaktigt. Vänligen ladda om sidan.", -"Error deleting contact property." => "Fel uppstod när kontaktegenskap skulle tas bort", "Contact ID is missing." => "Kontakt-ID saknas.", -"Missing contact id." => "Saknar kontakt-ID.", "No photo path was submitted." => "Ingen sökväg till foto angavs.", "File doesn't exist:" => "Filen existerar inte.", "Error loading image." => "Fel uppstod när bild laddades.", -"checksum is not set." => "kontrollsumma är inte satt.", -"Information about vCard is incorrect. Please reload the page: " => "Informationen om vCard är fel. Ladda om sidan:", -"Error updating contact property." => "Fel uppstod när kontaktegenskap skulle uppdateras", -"Cannot update addressbook with an empty name." => "Kan inte uppdatera adressboken med ett tomt namn.", -"Error updating addressbook." => "Fel uppstod när adressbok skulle uppdateras", +"Error getting contact object." => "Fel vid hämtning av kontakt.", +"Error getting PHOTO property." => "Fel vid hämtning av egenskaper för FOTO.", +"Error saving contact." => "Fel vid sparande av kontakt.", +"Error resizing image" => "Fel vid storleksförändring av bilden", +"Error cropping image" => "Fel vid beskärning av bilden", +"Error creating temporary image" => "Fel vid skapande av tillfällig bild", +"Error finding image: " => "Kunde inte hitta bild:", "Error uploading contacts to storage." => "Fel uppstod när kontakt skulle lagras.", "There is no error, the file uploaded with success" => "Inga fel uppstod. Filen laddades upp utan problem.", "The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Den uppladdade filen överskrider upload_max_filesize direktivet i php.ini", @@ -37,17 +44,46 @@ "The uploaded file was only partially uploaded" => "Den uppladdade filen var bara delvist uppladdad", "No file was uploaded" => "Ingen fil laddades upp", "Missing a temporary folder" => "En temporär mapp saknas", +"Couldn't save temporary image: " => "Kunde inte spara tillfällig bild:", +"Couldn't load temporary image: " => "Kunde inte ladda tillfällig bild:", +"No file was uploaded. Unknown error" => "Ingen fil uppladdad. Okänt fel", "Contacts" => "Kontakter", -"Drop a VCF file to import contacts." => "Släpp en VCF-fil för att importera kontakter.", -"Addressbook not found." => "Hittade inte adressboken", +"Sorry, this functionality has not been implemented yet" => "Tyvärr är denna funktion inte införd än", +"Not implemented" => "Inte införd", +"Couldn't get a valid address." => "Kunde inte hitta en giltig adress.", +"Error" => "Fel", +"This property has to be non-empty." => "Denna egenskap fÃ¥r inte vara tom.", +"Couldn't serialize elements." => "Kunde inte serialisera element.", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "\"deleteProperty\" anropades utan typargument. Vänligen rapportera till bugs.owncloud.org", +"Edit name" => "Ändra namn", +"No files selected for upload." => "Inga filer valda för uppladdning", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "Filen du försöker ladda upp är större än den maximala storleken för filuppladdning pÃ¥ denna server.", +"Error loading profile picture." => "Fel vid hämtning av profilbild.", +"Select type" => "Välj typ", +"Some contacts are marked for deletion, but not deleted yet. Please wait for them to be deleted." => "Vissa kontakter är markerade för radering, men är inte raderade än. Vänta tills dom är raderade.", +"Do you want to merge these address books?" => "Vill du slÃ¥ samman dessa adressböcker?", +"Result: " => "Resultat:", +" imported, " => "importerad,", +" failed." => "misslyckades.", +"Displayname cannot be empty." => "Visningsnamn fÃ¥r inte vara tomt.", +"Addressbook not found: " => "Adressboken hittades inte:", "This is not your addressbook." => "Det här är inte din adressbok.", "Contact could not be found." => "Kontakt kunde inte hittas.", -"Address" => "Adress", -"Telephone" => "Telefon", -"Email" => "E-post", -"Organization" => "Organisation", +"Jabber" => "Jabber", +"AIM" => "AIM", +"MSN" => "MSN", +"Twitter" => "Twitter", +"GoogleTalk" => "GoogleTalk", +"Facebook" => "Facebook", +"XMPP" => "XMPP", +"ICQ" => "ICQ", +"Yahoo" => "Yahoo", +"Skype" => "Skype", +"QQ" => "QQ", +"GaduGadu" => "GaduGadu", "Work" => "Arbete", "Home" => "Hem", +"Other" => "Annat", "Mobile" => "Mobil", "Text" => "Text", "Voice" => "Röst", @@ -56,88 +92,133 @@ "Video" => "Video", "Pager" => "Personsökare", "Internet" => "Internet", +"Birthday" => "Födelsedag", +"Business" => "Företag", +"Call" => "Ring", +"Clients" => "Kunder", +"Deliverer" => "Leverera", +"Holidays" => "Helgdagar", +"Ideas" => "Idéer", +"Journey" => "Resa", +"Jubilee" => "Jubileum", +"Meeting" => "Möte", +"Personal" => "Privat", +"Projects" => "Projekt", +"Questions" => "FrÃ¥gor", "{name}'s Birthday" => "{name}'s födelsedag", "Contact" => "Kontakt", "Add Contact" => "Lägg till kontakt", +"Import" => "Importera", +"Settings" => "Inställningar", "Addressbooks" => "Adressböcker", -"Configure Address Books" => "Konfigurera adressböcker", -"New Address Book" => "Ny adressbok", -"Import from VCF" => "Importera frÃ¥n VCF", -"CardDav Link" => "CardDAV länk", -"Download" => "Nedladdning", -"Edit" => "Redigera", -"Delete" => "Radera", -"Download contact" => "Ladda ner kontakt", -"Delete contact" => "Radera kontakt", +"Close" => "Stäng", +"Keyboard shortcuts" => "Kortkommandon", +"Navigation" => "Navigering", +"Next contact in list" => "Nästa kontakt i listan", +"Previous contact in list" => "FöregÃ¥ende kontakt i listan", +"Expand/collapse current addressbook" => "Visa/dölj aktuell adressbok", +"Next addressbook" => "Nästa adressbok", +"Previous addressbook" => "FöregÃ¥ende adressbok", +"Actions" => "Ã…tgärder", +"Refresh contacts list" => "Uppdatera kontaktlistan", +"Add new contact" => "Lägg till ny kontakt", +"Add new addressbook" => "Lägg till ny adressbok", +"Delete current contact" => "Radera denna kontakt", "Drop photo to upload" => "Släpp foto för att ladda upp", +"Delete current photo" => "Ta bort aktuellt foto", +"Edit current photo" => "Redigera aktuellt foto", +"Upload new photo" => "Ladda upp ett nytt foto", +"Select photo from ownCloud" => "Välj foto frÃ¥n ownCloud", "Format custom, Short name, Full name, Reverse or Reverse with comma" => " anpassad, korta namn, hela namn, bakÃ¥t eller bakÃ¥t med komma", "Edit name details" => "Redigera detaljer för namn", +"Organization" => "Organisation", +"Delete" => "Radera", "Nickname" => "Smeknamn", "Enter nickname" => "Ange smeknamn", -"Birthday" => "Födelsedag", +"Web site" => "Webbplats", +"http://www.somesite.com" => "http://www.somesite.com", +"Go to web site" => "GÃ¥ till webbplats", "dd-mm-yyyy" => "dd-mm-åååå", "Groups" => "Grupper", "Separate groups with commas" => "Separera grupperna med kommatecken", "Edit groups" => "Editera grupper", "Preferred" => "Föredragen", -"Please specify a valid email address." => "Vänligen ange en giltig e-postadress", +"Please specify a valid email address." => "Vänligen ange en giltig e-postadress.", "Enter email address" => "Ange e-postadress", "Mail to address" => "Posta till adress.", "Delete email address" => "Ta bort e-postadress", -"Enter phone number" => "Ange ett telefonnummer", +"Enter phone number" => "Ange telefonnummer", "Delete phone number" => "Ta bort telefonnummer", +"Instant Messenger" => "Instant Messenger", +"Delete IM" => "Radera IM", "View on map" => "Visa pÃ¥ karta", "Edit address details" => "Redigera detaljer för adress", "Add notes here." => "Lägg till noteringar här.", "Add field" => "Lägg till fält", -"Profile picture" => "Profilbild", "Phone" => "Telefon", +"Email" => "E-post", +"Instant Messaging" => "Instant Messaging", +"Address" => "Adress", "Note" => "Notering", -"Delete current photo" => "Ta bort aktuellt foto", -"Edit current photo" => "Redigera aktuellt foto", -"Upload new photo" => "Ladda upp ett nytt foto", -"Select photo from ownCloud" => "Välj foto frÃ¥n ownCloud", +"Download contact" => "Ladda ner kontakt", +"Delete contact" => "Radera kontakt", +"The temporary image has been removed from cache." => "Den tillfälliga bilden har raderats frÃ¥n cache.", "Edit address" => "Editera adress", "Type" => "Typ", "PO Box" => "Postbox", +"Street address" => "Gatuadress", +"Street and number" => "Gata och nummer", "Extended" => "Utökad", -"Street" => "Gata", +"Apartment number etc." => "Lägenhetsnummer", "City" => "Stad", "Region" => "Län", +"E.g. state or province" => "T.ex. stat eller provins", "Zipcode" => "Postnummer", +"Postal code" => "Postnummer", "Country" => "Land", -"Edit categories" => "Editera kategorier", -"Add" => "Ny", "Addressbook" => "Adressbok", -"Miss" => "Herr", -"Ms" => "Ingen adressbok funnen.", -"Mr" => "Fru", -"Mrs" => "Fröken", -"Dr" => "Dr", +"Hon. prefixes" => "Ledande titlar", +"Miss" => "Fröken", +"Ms" => "Fru", +"Mr" => "Herr", +"Sir" => "Herr", +"Mrs" => "Fru", +"Dr" => "Dr.", "Given name" => "Förnamn", "Additional names" => "Mellannamn", "Family name" => "Efternamn", +"Hon. suffixes" => "Efterställda titlar", +"J.D." => "Kand. Jur.", +"M.D." => "M.D.", +"D.O." => "D.O.", +"D.C." => "D.C.", "Ph.D." => "Fil.dr.", -"New Addressbook" => "Ny adressbok", -"Edit Addressbook" => "Redigera adressbok", -"Displayname" => "Visningsnamn", -"Active" => "Aktiv", -"Save" => "Spara", -"Submit" => "Skicka in", -"Cancel" => "Avbryt", +"Esq." => "Esq.", +"Jr." => "Jr.", +"Sn." => "Sn.", "Import a contacts file" => "Importera en kontaktfil", "Please choose the addressbook" => "Vänligen välj adressboken", "create a new addressbook" => "skapa en ny adressbok", "Name of new addressbook" => "Namn för ny adressbok", -"Import" => "Importera", "Importing contacts" => "Importerar kontakter", -"Select address book to import to:" => "Importera till adressbok:", -"Select from HD" => "Välj frÃ¥n hÃ¥rddisk", "You have no contacts in your addressbook." => "Du har inga kontakter i din adressbok.", "Add contact" => "Lägg till en kontakt", -"Configure addressbooks" => "Konfigurera adressböcker", +"Select Address Books" => "Välj adressböcker", +"Enter name" => "Ange namn", +"Enter description" => "Ange beskrivning", "CardDAV syncing addresses" => "CardDAV synkningsadresser", -"more info" => "mera information", +"more info" => "mer information", "Primary address (Kontact et al)" => "Primär adress (Kontakt o.a.)", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Show CardDav link" => "Visa CardDav-länk", +"Show read-only VCF link" => "Visa skrivskyddad VCF-länk", +"Share" => "Dela", +"Download" => "Nedladdning", +"Edit" => "Redigera", +"New Address Book" => "Ny adressbok", +"Name" => "Namn", +"Description" => "Beskrivning", +"Save" => "Spara", +"Cancel" => "Avbryt", +"More..." => "Mer..." ); diff --git a/apps/contacts/l10n/th_TH.php b/apps/contacts/l10n/th_TH.php index acf382c6a43..6afc64e61d3 100644 --- a/apps/contacts/l10n/th_TH.php +++ b/apps/contacts/l10n/th_TH.php @@ -1,39 +1,40 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดใน (ยà¸à¹€à¸¥à¸´à¸)à¸à¸²à¸£à¹€à¸›à¸´à¸”ใช้งานสมุดบันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆ", -"There was an error adding the contact." => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¹€à¸žà¸´à¹ˆà¸¡à¸£à¸²à¸¢à¸Šà¸·à¹ˆà¸à¸œà¸¹à¹‰à¸•à¸´à¸”ต่à¸à¹ƒà¸«à¸¡à¹ˆ", -"Cannot add empty property." => "ไม่สามารถเพิ่มรายละเà¸à¸µà¸¢à¸”ที่ไม่มีข้à¸à¸¡à¸¹à¸¥à¹„ด้", -"At least one of the address fields has to be filled out." => "à¸à¸¢à¹ˆà¸²à¸‡à¸™à¹‰à¸à¸¢à¸—ี่สุดช่à¸à¸‡à¸‚้à¸à¸¡à¸¹à¸¥à¸—ี่à¸à¸¢à¸¹à¹ˆà¸ˆà¸°à¸•à¹‰à¸à¸‡à¸–ูà¸à¸à¸£à¸à¸à¸¥à¸‡à¹„ป", -"Trying to add duplicate property: " => "พยายามที่จะเพิ่มทรัพยาà¸à¸£à¸—ี่ซ้ำซ้à¸à¸™à¸à¸±à¸™: ", -"Error adding contact property." => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¹€à¸žà¸´à¹ˆà¸¡à¸£à¸²à¸¢à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¸²à¸£à¸•à¸´à¸”ต่à¸", +"id is not set." => "ยังไม่ได้à¸à¸³à¸«à¸™à¸”รหัส", +"Cannot update addressbook with an empty name." => "ไม่สามารถà¸à¸±à¸žà¹€à¸”ทสมุดบันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆà¹‚ดยไม่มีชื่à¸à¹„ด้", +"Error updating addressbook." => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¸à¸±à¸žà¹€à¸”ทสมุดบันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆ", "No ID provided" => "ยังไม่ได้ใส่รหัส", "Error setting checksum." => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่า checksum", "No categories selected for deletion." => "ยังไม่ได้เลืà¸à¸à¸«à¸¡à¸§à¸”หมู่ที่ต้à¸à¸‡à¸à¸²à¸£à¸¥à¸š", "No address books found." => "ไม่พบสมุดบันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆà¸—ี่ต้à¸à¸‡à¸à¸²à¸£", "No contacts found." => "ไม่พบข้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸•à¸´à¸”ต่à¸à¸—ี่ต้à¸à¸‡à¸à¸²à¸£", +"There was an error adding the contact." => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¹€à¸žà¸´à¹ˆà¸¡à¸£à¸²à¸¢à¸Šà¸·à¹ˆà¸à¸œà¸¹à¹‰à¸•à¸´à¸”ต่à¸à¹ƒà¸«à¸¡à¹ˆ", +"element name is not set." => "ยังไม่ได้à¸à¸³à¸«à¸™à¸”ชื่à¸", +"Could not parse contact: " => "ไม่สามารถà¹à¸ˆà¸à¹à¸ˆà¸‡à¸£à¸²à¸¢à¸Šà¸·à¹ˆà¸à¸œà¸¹à¹‰à¸•à¸´à¸”ต่à¸à¹„ด้", +"Cannot add empty property." => "ไม่สามารถเพิ่มรายละเà¸à¸µà¸¢à¸”ที่ไม่มีข้à¸à¸¡à¸¹à¸¥à¹„ด้", +"At least one of the address fields has to be filled out." => "à¸à¸¢à¹ˆà¸²à¸‡à¸™à¹‰à¸à¸¢à¸—ี่สุดช่à¸à¸‡à¸‚้à¸à¸¡à¸¹à¸¥à¸—ี่à¸à¸¢à¸¹à¹ˆà¸ˆà¸°à¸•à¹‰à¸à¸‡à¸–ูà¸à¸à¸£à¸à¸à¸¥à¸‡à¹„ป", +"Trying to add duplicate property: " => "พยายามที่จะเพิ่มทรัพยาà¸à¸£à¸—ี่ซ้ำซ้à¸à¸™à¸à¸±à¸™: ", +"Information about vCard is incorrect. Please reload the page." => "ข้à¸à¸¡à¸¹à¸¥à¹€à¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸š vCard ไม่ถูà¸à¸•à¹‰à¸à¸‡ à¸à¸£à¸¸à¸“าโหลดหน้าเวปใหม่à¸à¸µà¸à¸„รั้ง", "Missing ID" => "รหัสสูà¸à¸«à¸²à¸¢", "Error parsing VCard for ID: \"" => "พบข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¹à¸¢à¸à¸£à¸«à¸±à¸ª VCard:\"", -"Cannot add addressbook with an empty name." => "ไม่สามารถเพิ่มสมุดบันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆà¹‚ดยไม่มีชื่à¸à¹„ด้", -"Error adding addressbook." => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¹€à¸žà¸´à¹ˆà¸¡à¸ªà¸¡à¸¸à¸”บันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆà¹ƒà¸«à¸¡à¹ˆ", -"Error activating addressbook." => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¹€à¸›à¸´à¸”ใช้งานสมุดบันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆ", +"checksum is not set." => "ยังไม่ได้à¸à¸³à¸«à¸™à¸”ค่า checksum", +"Information about vCard is incorrect. Please reload the page: " => "ข้à¸à¸¡à¸¹à¸¥ vCard ไม่ถูà¸à¸•à¹‰à¸à¸‡ à¸à¸£à¸¸à¸“าโหลดหน้าเว็บใหม่à¸à¸µà¸à¸„รั้ง: ", +"Something went FUBAR. " => "มีบางà¸à¸¢à¹ˆà¸²à¸‡à¹€à¸à¸´à¸”à¸à¸²à¸£ FUBAR. ", "No contact ID was submitted." => "ไม่มีรหัสข้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸•à¸´à¸”ต่à¸à¸–ูà¸à¸ªà¹ˆà¸‡à¸¡à¸²", "Error reading contact photo." => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¸à¹ˆà¸²à¸™à¸£à¸¹à¸›à¸ าพขà¸à¸‡à¸‚้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸•à¸´à¸”ต่à¸", "Error saving temporary file." => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¸šà¸±à¸™à¸—ึà¸à¹„ฟล์ชั่วคราว", "The loading photo is not valid." => "โหลดรูปภาพไม่ถูà¸à¸•à¹‰à¸à¸‡", -"id is not set." => "ยังไม่ได้à¸à¸³à¸«à¸™à¸”รหัส", -"Information about vCard is incorrect. Please reload the page." => "ข้à¸à¸¡à¸¹à¸¥à¹€à¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸š vCard ไม่ถูà¸à¸•à¹‰à¸à¸‡ à¸à¸£à¸¸à¸“าโหลดหน้าเวปใหม่à¸à¸µà¸à¸„รั้ง", -"Error deleting contact property." => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¸¥à¸šà¸£à¸²à¸¢à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¸²à¸£à¸•à¸´à¸”ต่à¸", "Contact ID is missing." => "รหัสข้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸•à¸´à¸”ต่à¸à¹€à¸à¸´à¸”à¸à¸²à¸£à¸ªà¸¹à¸à¸«à¸²à¸¢", -"Missing contact id." => "รหัสข้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸•à¸´à¸”ต่à¸à¹€à¸à¸´à¸”à¸à¸²à¸£à¸ªà¸¹à¸à¸«à¸²à¸¢", "No photo path was submitted." => "ไม่พบตำà¹à¸«à¸™à¹ˆà¸‡à¸žà¸²à¸˜à¸‚à¸à¸‡à¸£à¸¹à¸›à¸ าพ", "File doesn't exist:" => "ไม่มีไฟล์ดังà¸à¸¥à¹ˆà¸²à¸§", "Error loading image." => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¹‚หลดรูปภาพ", -"element name is not set." => "ยังไม่ได้à¸à¸³à¸«à¸™à¸”ชื่à¸", -"checksum is not set." => "ยังไม่ได้à¸à¸³à¸«à¸™à¸”ค่า checksum", -"Information about vCard is incorrect. Please reload the page: " => "ข้à¸à¸¡à¸¹à¸¥ vCard ไม่ถูà¸à¸•à¹‰à¸à¸‡ à¸à¸£à¸¸à¸“าโหลดหน้าเว็บใหม่à¸à¸µà¸à¸„รั้ง: ", -"Something went FUBAR. " => "มีบางà¸à¸¢à¹ˆà¸²à¸‡à¹€à¸à¸´à¸”à¸à¸²à¸£ FUBAR. ", -"Error updating contact property." => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¸à¸±à¸žà¹€à¸”ทข้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸•à¸´à¸”ต่à¸", -"Cannot update addressbook with an empty name." => "ไม่สามารถà¸à¸±à¸žà¹€à¸”ทสมุดบันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆà¹‚ดยไม่มีชื่à¸à¹„ด้", -"Error updating addressbook." => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¸à¸±à¸žà¹€à¸”ทสมุดบันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆ", +"Error getting contact object." => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¸”ึงข้à¸à¸¡à¸¹à¸¥à¸•à¸´à¸”ต่à¸", +"Error getting PHOTO property." => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¸”ึงคุณสมบัติขà¸à¸‡à¸£à¸¹à¸›à¸ าพ", +"Error saving contact." => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¸šà¸±à¸™à¸—ึà¸à¸‚้à¸à¸¡à¸¹à¸¥à¸œà¸¹à¹‰à¸•à¸´à¸”ต่à¸", +"Error resizing image" => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¸›à¸£à¸±à¸šà¸‚นาดรูปภาพ", +"Error cropping image" => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¸„รà¸à¸šà¸•à¸±à¸”ภาพ", +"Error creating temporary image" => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¸ªà¸£à¹‰à¸²à¸‡à¸£à¸¹à¸›à¸ าพชั่วคราว", +"Error finding image: " => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¸„้นหารูปภาพ: ", "Error uploading contacts to storage." => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¸à¸±à¸žà¹‚หลดข้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸•à¸´à¸”ต่à¸à¹„ปยังพื้นที่จัดเà¸à¹‡à¸šà¸‚้à¸à¸¡à¸¹à¸¥", "There is no error, the file uploaded with success" => "ไม่พบข้à¸à¸œà¸´à¸”พลาดใดๆ, ไฟล์ถูà¸à¸à¸±à¸žà¹‚หลดเรียบร้à¸à¸¢à¹à¸¥à¹‰à¸§", "The uploaded file exceeds the upload_max_filesize directive in php.ini" => "ไฟล์ที่à¸à¸±à¸žà¹‚หลดมีขนาดไฟล์ใหà¸à¹ˆà¹€à¸à¸´à¸™à¸ˆà¸³à¸™à¸§à¸™à¸—ี่à¸à¸³à¸«à¸™à¸”ไว้ในคำสั่ง upload_max_filesize ที่à¸à¸¢à¸¹à¹ˆà¹ƒà¸™à¹„ฟล์ php.ini", @@ -41,17 +42,34 @@ "The uploaded file was only partially uploaded" => "ไฟล์ถูà¸à¸à¸±à¸žà¹‚หลดได้เพียงบางส่วนเท่านั้น", "No file was uploaded" => "ไม่มีไฟล์ที่ถูà¸à¸à¸±à¸žà¹‚หลด", "Missing a temporary folder" => "โฟลเดà¸à¸£à¹Œà¸Šà¸±à¹ˆà¸§à¸„ราวเà¸à¸´à¸”à¸à¸²à¸£à¸ªà¸¹à¸à¸«à¸²à¸¢", +"Couldn't save temporary image: " => "ไม่สามารถบันทึà¸à¸£à¸¹à¸›à¸ าพชั่วคราวได้: ", +"Couldn't load temporary image: " => "ไม่สามารถโหลดรูปภาพชั่วคราวได้: ", +"No file was uploaded. Unknown error" => "ยังไม่มีไฟล์ใดที่ถูà¸à¸à¸±à¸žà¹‚หลด เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดที่ไม่ทราบสาเหตุ", "Contacts" => "ข้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸•à¸´à¸”ต่à¸", -"Drop a VCF file to import contacts." => "วางไฟล์ VCF ที่ต้à¸à¸‡à¸à¸²à¸£à¸™à¸³à¹€à¸‚้าข้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸•à¸´à¸”ต่à¸", -"Addressbook not found." => "ไม่พบสมุดบันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆà¸—ี่ต้à¸à¸‡à¸à¸²à¸£", +"Sorry, this functionality has not been implemented yet" => "ขà¸à¸à¸ ัย, ฟังà¸à¹Œà¸Šà¸±à¹ˆà¸™à¸à¸²à¸£à¸—ำงานนี้ยังไม่ได้ถูà¸à¸”ำเนินà¸à¸²à¸£", +"Not implemented" => "ยังไม่ได้ถูà¸à¸”ำเนินà¸à¸²à¸£", +"Couldn't get a valid address." => "ไม่สามารถดึงที่à¸à¸¢à¸¹à¹ˆà¸—ี่ถูà¸à¸•à¹‰à¸à¸‡à¹„ด้", +"Error" => "พบข้à¸à¸œà¸´à¸”พลาด", +"This property has to be non-empty." => "คุณสมบัตินี้ต้à¸à¸‡à¹„ม่มีข้à¸à¸¡à¸¹à¸¥à¸§à¹ˆà¸²à¸‡à¸à¸¢à¸¹à¹ˆ", +"Couldn't serialize elements." => "ไม่สามารถทำสัà¸à¸¥à¸±à¸à¸©à¸“์à¸à¸‡à¸„์ประà¸à¸à¸šà¸•à¹ˆà¸²à¸‡à¹†à¹ƒà¸«à¹‰à¹€à¸›à¹‡à¸™à¸•à¸±à¸§à¹€à¸¥à¸‚ตามลำดับได้", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "'deleteProperty' ถูà¸à¹€à¸£à¸µà¸¢à¸à¹ƒà¸Šà¹‰à¹‚ดยไม่มีà¸à¸²à¸£à¹Œà¸à¸´à¸§à¹€à¸¡à¸™à¸•à¹Œ à¸à¸£à¸¸à¸“าà¹à¸ˆà¹‰à¸‡à¹„ด้ที่ bugs.owncloud.org", +"Edit name" => "à¹à¸à¹‰à¹„ขชื่à¸", +"No files selected for upload." => "ยังไม่ได้เลืà¸à¸à¹„ฟล์ำสำหรับà¸à¸±à¸žà¹‚หลด", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "ไฟล์ที่คุณà¸à¸³à¸¥à¸±à¸‡à¸žà¸¢à¸²à¸¢à¸²à¸¡à¸—ี่จะà¸à¸±à¸žà¹‚หลดมีขนาดเà¸à¸´à¸™à¸ˆà¸³à¸™à¸§à¸™à¸ªà¸¹à¸‡à¸ªà¸¸à¸”ที่สามารถà¸à¸±à¸žà¹‚หลดได้สำหรับเซิร์ฟเวà¸à¸£à¹Œà¸™à¸µà¹‰", +"Error loading profile picture." => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¹‚หลดรูปภาพประจำตัว", +"Select type" => "เลืà¸à¸à¸Šà¸™à¸´à¸”", +"Some contacts are marked for deletion, but not deleted yet. Please wait for them to be deleted." => "ข้à¸à¸¡à¸¹à¸¥à¸œà¸¹à¹‰à¸•à¸´à¸”ต่à¸à¸šà¸²à¸‡à¸£à¸²à¸¢à¸à¸²à¸£à¹„ด้ถูà¸à¸—ำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸ªà¸³à¸«à¸£à¸±à¸šà¸¥à¸šà¸—ิ้งเà¸à¸²à¹„ว้, à¹à¸•à¹ˆà¸¢à¸±à¸‡à¹„ม่ได้ถูà¸à¸¥à¸šà¸—ิ้ง, à¸à¸£à¸¸à¸“ารà¸à¹ƒà¸«à¹‰à¸£à¸²à¸¢à¸à¸²à¸£à¸”ังà¸à¸¥à¹ˆà¸²à¸§à¸–ูà¸à¸¥à¸šà¸—ิ้งเสียà¸à¹ˆà¸à¸™", +"Do you want to merge these address books?" => "คุณต้à¸à¸‡à¸à¸²à¸£à¸œà¸ªà¸²à¸™à¸‚้à¸à¸¡à¸¹à¸¥à¸ªà¸¡à¸¸à¸”บันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆà¹€à¸«à¸¥à¹ˆà¸²à¸™à¸µà¹‰à¸«à¸£à¸·à¸à¹„ม่?", +"Result: " => "ผลลัพธ์: ", +" imported, " => " นำเข้าข้à¸à¸¡à¸¹à¸¥à¹à¸¥à¹‰à¸§, ", +" failed." => " ล้มเหลว.", +"Displayname cannot be empty." => "ชื่à¸à¸—ี่ใช้à¹à¸ªà¸”งไม่สามารถเว้นว่างได้", +"Addressbook not found: " => "ไม่พบสมุดบันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆà¸—ี่ต้à¸à¸‡à¸à¸²à¸£", "This is not your addressbook." => "นี่ไม่ใช่สมุดบันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆà¸‚à¸à¸‡à¸„ุณ", "Contact could not be found." => "ไม่พบข้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸•à¸´à¸”ต่à¸", -"Address" => "ที่à¸à¸¢à¸¹à¹ˆ", -"Telephone" => "โทรศัพท์", -"Email" => "à¸à¸µà¹€à¸¡à¸¥à¹Œ", -"Organization" => "หน่วยงาน", "Work" => "ที่ทำงาน", "Home" => "บ้าน", +"Other" => "à¸à¸·à¹ˆà¸™à¹†", "Mobile" => "มืà¸à¸–ืà¸", "Text" => "ข้à¸à¸„วาม", "Voice" => "เสียงพูด", @@ -60,25 +78,52 @@ "Video" => "วีดีโà¸", "Pager" => "เพจเจà¸à¸£à¹Œ", "Internet" => "à¸à¸´à¸™à¹€à¸—à¸à¸£à¹Œà¹€à¸™à¹‡à¸•", +"Birthday" => "วันเà¸à¸´à¸”", +"Business" => "ธุรà¸à¸´à¸ˆ", +"Call" => "โทร", +"Clients" => "ลูà¸à¸„้า", +"Deliverer" => "ผู้จัดส่ง", +"Holidays" => "วันหยุด", +"Ideas" => "ไà¸à¹€à¸”ีย", +"Journey" => "à¸à¸²à¸£à¹€à¸”ินทาง", +"Jubilee" => "งานเฉลิมฉลà¸à¸‡", +"Meeting" => "ประชุม", +"Personal" => "ส่วนตัว", +"Projects" => "โปรเจค", +"Questions" => "คำถาม", "{name}'s Birthday" => "วันเà¸à¸´à¸”ขà¸à¸‡ {name}", "Contact" => "ข้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸•à¸´à¸”ต่à¸", "Add Contact" => "เพิ่มรายชื่à¸à¸œà¸¹à¹‰à¸•à¸´à¸”ต่à¸à¹ƒà¸«à¸¡à¹ˆ", +"Import" => "นำเข้า", +"Settings" => "ตั้งค่า", "Addressbooks" => "สมุดบันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆ", -"Configure Address Books" => "à¸à¸³à¸«à¸™à¸”ค่าสมุดบันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆ", -"New Address Book" => "สร้างสมุดบันทึà¸à¸‚้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸•à¸´à¸”ต่à¸à¹ƒà¸«à¸¡à¹ˆ", -"Import from VCF" => "นำเข้าจาภVCF", -"CardDav Link" => "ลิงค์ CardDav", -"Download" => "ดาวน์โหลด", -"Edit" => "à¹à¸à¹‰à¹„ข", -"Delete" => "ลบ", -"Download contact" => "ดาวน์โหลดข้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸•à¸´à¸”ต่à¸", -"Delete contact" => "ลบข้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸•à¸´à¸”ต่à¸", +"Close" => "ปิด", +"Keyboard shortcuts" => "ปุ่มลัด", +"Navigation" => "ระบบเมนู", +"Next contact in list" => "ข้à¸à¸¡à¸¹à¸¥à¸œà¸¹à¹‰à¸•à¸´à¸”ต่à¸à¸–ัดไปในรายà¸à¸²à¸£", +"Previous contact in list" => "ข้à¸à¸¡à¸¹à¸¥à¸œà¸¹à¹‰à¸•à¸´à¸”ต่à¸à¸à¹ˆà¸à¸™à¸«à¸™à¹‰à¸²à¹ƒà¸™à¸£à¸²à¸¢à¸à¸²à¸£", +"Expand/collapse current addressbook" => "ขยาย/ย่ภสมุดบันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆà¸›à¸±à¸ˆà¸ˆà¸¸à¸šà¸±à¸™", +"Next addressbook" => "สมุดบันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆà¸–ัดไป", +"Previous addressbook" => "สมุดบันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆà¸à¹ˆà¸à¸™à¸«à¸™à¹‰à¸²", +"Actions" => "à¸à¸²à¸£à¸à¸£à¸°à¸—ำ", +"Refresh contacts list" => "รีเฟรชรายชื่à¸à¸œà¸¹à¹‰à¸•à¸´à¸”ต่à¸à¹ƒà¸«à¸¡à¹ˆ", +"Add new contact" => "เพิ่มข้à¸à¸¡à¸¹à¸¥à¸œà¸¹à¹‰à¸•à¸´à¸”ต่à¸à¹ƒà¸«à¸¡à¹ˆ", +"Add new addressbook" => "เพิ่มสมุดบันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆà¹ƒà¸«à¸¡à¹ˆ", +"Delete current contact" => "ลบข้à¸à¸¡à¸¹à¸¥à¸œà¸¹à¹‰à¸•à¸´à¸”ต่à¸à¸›à¸±à¸ˆà¸ˆà¸¸à¸šà¸±à¸™", "Drop photo to upload" => "วางรูปภาพที่ต้à¸à¸‡à¸à¸²à¸£à¸à¸±à¸žà¹‚หลด", +"Delete current photo" => "ลบรูปภาพปัจจุบัน", +"Edit current photo" => "à¹à¸à¹‰à¹„ขรูปภาพปัจจุบัน", +"Upload new photo" => "à¸à¸±à¸žà¹‚หลดรูปภาพใหม่", +"Select photo from ownCloud" => "เลืà¸à¸à¸£à¸¹à¸›à¸ าพจาภownCloud", "Format custom, Short name, Full name, Reverse or Reverse with comma" => "à¸à¸³à¸«à¸™à¸”รูปà¹à¸šà¸šà¸‚à¸à¸‡à¸Šà¸·à¹ˆà¸à¸¢à¹ˆà¸, ชื่à¸à¸ˆà¸£à¸´à¸‡, ย้à¸à¸™à¸„่าà¸à¸¥à¸±à¸µà¸šà¸”้วยคà¸à¸¡à¸¡à¹ˆà¸²à¹€à¸à¸‡", "Edit name details" => "à¹à¸à¹‰à¹„ขรายละเà¸à¸µà¸¢à¸”ขà¸à¸‡à¸Šà¸·à¹ˆà¸", +"Organization" => "หน่วยงาน", +"Delete" => "ลบ", "Nickname" => "ชื่à¸à¹€à¸¥à¹ˆà¸™", "Enter nickname" => "à¸à¸£à¸à¸à¸Šà¸·à¹ˆà¸à¹€à¸¥à¹ˆà¸™", -"Birthday" => "วันเà¸à¸´à¸”", +"Web site" => "เว็บไซต์", +"http://www.somesite.com" => "http://www.somesite.com", +"Go to web site" => "ไปที่เว็บไซต์", "dd-mm-yyyy" => "dd-mm-yyyy", "Groups" => "à¸à¸¥à¸¸à¹ˆà¸¡", "Separate groups with commas" => "คั่นระหว่างรายชื่à¸à¸à¸¥à¸¸à¹ˆà¸¡à¸”้วยเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸ˆà¸¸à¸¥à¸ าีคหรืà¸à¸„à¸à¸¡à¸¡à¹ˆà¸²", @@ -94,24 +139,26 @@ "Edit address details" => "à¹à¸à¹‰à¹„ขรายละเà¸à¸µà¸¢à¸”ที่à¸à¸¢à¸¹à¹ˆ", "Add notes here." => "เพิ่มหมายเหตุà¸à¸³à¸à¸±à¸šà¹„ว้ที่นี่", "Add field" => "เพิ่มช่à¸à¸‡à¸£à¸±à¸šà¸‚้à¸à¸¡à¸¹à¸¥", -"Profile picture" => "รูปภาพโปรไฟล์", "Phone" => "โทรศัพท์", +"Email" => "à¸à¸µà¹€à¸¡à¸¥à¹Œ", +"Address" => "ที่à¸à¸¢à¸¹à¹ˆ", "Note" => "หมายเหตุ", -"Delete current photo" => "ลบรูปภาพปัจจุบัน", -"Edit current photo" => "à¹à¸à¹‰à¹„ขรูปภาพปัจจุบัน", -"Upload new photo" => "à¸à¸±à¸žà¹‚หลดรูปภาพใหม่", -"Select photo from ownCloud" => "เลืà¸à¸à¸£à¸¹à¸›à¸ าพจาภownCloud", +"Download contact" => "ดาวน์โหลดข้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸•à¸´à¸”ต่à¸", +"Delete contact" => "ลบข้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸•à¸´à¸”ต่à¸", +"The temporary image has been removed from cache." => "รูปภาพชั่วคราวดังà¸à¸¥à¹ˆà¸²à¸§à¹„ด้ถูà¸à¸¥à¸šà¸à¸à¸à¸ˆà¸²à¸à¸«à¸™à¹ˆà¸§à¸¢à¸„วามจำà¹à¸„ชà¹à¸¥à¹‰à¸§", "Edit address" => "à¹à¸à¹‰à¹„ขที่à¸à¸¢à¸¹à¹ˆ", "Type" => "ประเภท", "PO Box" => "ตู้ ปณ.", +"Street address" => "ที่à¸à¸¢à¸¹à¹ˆ", +"Street and number" => "ถนนà¹à¸¥à¸°à¸«à¸¡à¸²à¸¢à¹€à¸¥à¸‚", "Extended" => "เพิ่ม", -"Street" => "ถนน", +"Apartment number etc." => "หมายเลขà¸à¸žà¸²à¸£à¹Œà¸—เมนต์ ฯลฯ", "City" => "เมืà¸à¸‡", "Region" => "ภูมิภาค", +"E.g. state or province" => "เช่น รัภหรืภจังหวัด", "Zipcode" => "รหัสไปรษณีย์", +"Postal code" => "รหัสไปรษณีย์", "Country" => "ประเทศ", -"Edit categories" => "à¹à¸à¹‰à¹„ขหมวดหมู่", -"Add" => "เพิ่ม", "Addressbook" => "สมุดบันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆ", "Hon. prefixes" => "คำนำหน้าชื่à¸à¸„นรัà¸", "Miss" => "นางสาว", @@ -132,26 +179,35 @@ "Esq." => "Esq.", "Jr." => "จูเนียร์", "Sn." => "ซีเนียร์", -"New Addressbook" => "สร้างสมุดบันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆà¹ƒà¸«à¸¡à¹ˆ", -"Edit Addressbook" => "à¹à¸à¹‰à¹„ขสมุดบันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆ", -"Displayname" => "ชื่à¸à¸—ี่ต้à¸à¸‡à¸à¸²à¸£à¹ƒà¸«à¹‰à¹à¸ªà¸”ง", -"Active" => "เปิดใช้", -"Save" => "บันทึà¸", -"Submit" => "ส่งข้à¸à¸¡à¸¹à¸¥", -"Cancel" => "ยà¸à¹€à¸¥à¸´à¸", "Import a contacts file" => "นำเข้าไฟล์ข้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸•à¸´à¸”ต่à¸", "Please choose the addressbook" => "à¸à¸£à¸¸à¸“าเลืà¸à¸à¸ªà¸¡à¸¸à¸”บันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆ", "create a new addressbook" => "สร้างสมุดบันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆà¹ƒà¸«à¸¡à¹ˆ", "Name of new addressbook" => "à¸à¸³à¸«à¸™à¸”ชื่à¸à¸‚à¸à¸‡à¸ªà¸¡à¸¸à¸”ที่à¸à¸¢à¸¹à¹ˆà¸—ี่สร้างใหม่", -"Import" => "นำเข้า", "Importing contacts" => "นำเข้าข้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸•à¸´à¸”ต่à¸", +"Contacts imported successfully" => "ข้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸•à¸´à¸”ต่à¸à¸–ูà¸à¸™à¸³à¹€à¸‚้าข้à¸à¸¡à¸¹à¸¥à¹€à¸£à¸µà¸¢à¸šà¸£à¹‰à¸à¸¢à¹à¸¥à¹‰à¸§", +"Close Dialog" => "ปิดà¸à¸¥à¹ˆà¸à¸‡à¸‚้à¸à¸„วาม", +"Import Addressbook" => "นำเข้าข้à¸à¸¡à¸¹à¸¥à¸ªà¸¡à¸¸à¸”บันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆ", "Select address book to import to:" => "เลืà¸à¸à¸ªà¸¡à¸¸à¸”บันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆà¸—ี่ต้à¸à¸‡à¸à¸²à¸£à¸™à¸³à¹€à¸‚้า:", +"Drop a VCF file to import contacts." => "วางไฟล์ VCF ที่ต้à¸à¸‡à¸à¸²à¸£à¸™à¸³à¹€à¸‚้าข้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸•à¸´à¸”ต่à¸", "Select from HD" => "เลืà¸à¸à¸ˆà¸²à¸à¸®à¸²à¸£à¹Œà¸”ดิส", "You have no contacts in your addressbook." => "คุณยังไม่มีข้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸•à¸´à¸”ต่à¸à¹ƒà¸”ๆในสมุดบันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆà¸‚à¸à¸‡à¸„ุณ", "Add contact" => "เพิ่มชื่à¸à¸œà¸¹à¹‰à¸•à¸´à¸”ต่à¸", -"Configure addressbooks" => "à¸à¸³à¸«à¸™à¸”ค่าสมุดบันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆ", +"Select Address Books" => "เลืà¸à¸à¸ªà¸¡à¸¸à¸”บันทึà¸à¸—ี่à¸à¸¢à¸¹à¹ˆ", +"Enter name" => "à¸à¸£à¸à¸à¸Šà¸·à¹ˆà¸", +"Enter description" => "à¸à¸£à¸à¸à¸„ำà¸à¸˜à¸´à¸šà¸²à¸¢", "CardDAV syncing addresses" => "ที่à¸à¸¢à¸¹à¹ˆà¸—ี่ใช้เชื่à¸à¸¡à¸‚้à¸à¸¡à¸¹à¸¥à¸à¸±à¸š CardDAV", "more info" => "ข้à¸à¸¡à¸¹à¸¥à¹€à¸žà¸´à¹ˆà¸¡à¹€à¸•à¸´à¸¡", "Primary address (Kontact et al)" => "ที่à¸à¸¢à¸¹à¹ˆà¸«à¸¥à¸±à¸ (สำหรับติดต่à¸)", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Show CardDav link" => "à¹à¸ªà¸”งลิงà¸à¹Œ CardDav", +"Show read-only VCF link" => "à¹à¸ªà¸”งลิงà¸à¹Œ VCF สำหรับà¸à¹ˆà¸²à¸™à¹€à¸—่านั้น", +"Share" => "à¹à¸Šà¸£à¹Œ", +"Download" => "ดาวน์โหลด", +"Edit" => "à¹à¸à¹‰à¹„ข", +"New Address Book" => "สร้างสมุดบันทึà¸à¸‚้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸•à¸´à¸”ต่à¸à¹ƒà¸«à¸¡à¹ˆ", +"Name" => "ชื่à¸", +"Description" => "คำà¸à¸˜à¸´à¸šà¸²à¸¢", +"Save" => "บันทึà¸", +"Cancel" => "ยà¸à¹€à¸¥à¸´à¸", +"More..." => "เพิ่มเติม..." ); diff --git a/apps/contacts/l10n/tr.php b/apps/contacts/l10n/tr.php index 65e8e34c305..e2a769410f1 100644 --- a/apps/contacts/l10n/tr.php +++ b/apps/contacts/l10n/tr.php @@ -1,39 +1,40 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "Adres defteri etkisizleÅŸtirilirken hata oluÅŸtu.", -"There was an error adding the contact." => "KiÅŸi eklenirken hata oluÅŸtu.", -"Cannot add empty property." => "BoÅŸ özellik eklenemiyor.", -"At least one of the address fields has to be filled out." => "En az bir adres alanı doldurulmalı.", -"Trying to add duplicate property: " => "Yinelenen özellik eklenmeye çalışılıyor: ", -"Error adding contact property." => "KiÅŸi özelliÄŸi eklenirken hata oluÅŸtu.", +"id is not set." => "id atanmamış.", +"Cannot update addressbook with an empty name." => "Adres defterini boÅŸ bir isimle güncelleyemezsiniz.", +"Error updating addressbook." => "Adres defteri güncellenirken hata oluÅŸtu.", "No ID provided" => "ID verilmedi", "Error setting checksum." => "Ä°mza oluÅŸturulurken hata.", "No categories selected for deletion." => "Silmek için bir kategori seçilmedi.", "No address books found." => "Adres defteri bulunamadı.", "No contacts found." => "BaÄŸlantı bulunamadı.", +"There was an error adding the contact." => "KiÅŸi eklenirken hata oluÅŸtu.", +"element name is not set." => "eleman ismi atanmamış.", +"Could not parse contact: " => "KiÅŸi bilgisi ayrıştırılamadı.", +"Cannot add empty property." => "BoÅŸ özellik eklenemiyor.", +"At least one of the address fields has to be filled out." => "En az bir adres alanı doldurulmalı.", +"Trying to add duplicate property: " => "Yinelenen özellik eklenmeye çalışılıyor: ", +"Information about vCard is incorrect. Please reload the page." => "vCard bilgileri doÄŸru deÄŸil. Lütfen sayfayı yenileyin.", "Missing ID" => "Eksik ID", "Error parsing VCard for ID: \"" => "ID için VCard ayrıştırılamadı:\"", -"Cannot add addressbook with an empty name." => "Adres defterini isimsiz ekleyemezsiniz.", -"Error adding addressbook." => "Adres defteri eklenirken hata oluÅŸtu.", -"Error activating addressbook." => "Adres defteri etkinleÅŸtirilirken hata oluÅŸtu.", +"checksum is not set." => "checksum atanmamış.", +"Information about vCard is incorrect. Please reload the page: " => "vCard hakkındaki bilgi hatalı. Lütfen sayfayı yeniden yükleyin: ", +"Something went FUBAR. " => "Bir ÅŸey FUBAR gitti.", "No contact ID was submitted." => "BaÄŸlantı ID'si girilmedi.", "Error reading contact photo." => "BaÄŸlantı fotoÄŸrafı okunamadı.", "Error saving temporary file." => "Geçici dosya kaydetme hatası.", "The loading photo is not valid." => "Yüklenecek fotograf geçerli deÄŸil.", -"id is not set." => "id atanmamış.", -"Information about vCard is incorrect. Please reload the page." => "vCard bilgileri doÄŸru deÄŸil. Lütfen sayfayı yenileyin.", -"Error deleting contact property." => "KiÅŸi özelliÄŸi silinirken hata oluÅŸtu.", "Contact ID is missing." => "BaÄŸlantı ID'si eksik.", -"Missing contact id." => "Eksik baÄŸlantı id'si.", "No photo path was submitted." => "FotoÄŸraf girilmedi.", "File doesn't exist:" => "Dosya mevcut deÄŸil:", "Error loading image." => "Ä°maj yükleme hatası.", -"element name is not set." => "eleman ismi atanmamış.", -"checksum is not set." => "checksum atanmamış.", -"Information about vCard is incorrect. Please reload the page: " => "vCard hakkındaki bilgi hatalı. Lütfen sayfayı yeniden yükleyin: ", -"Something went FUBAR. " => "Bir ÅŸey FUBAR gitti.", -"Error updating contact property." => "KiÅŸi özelliÄŸi güncellenirken hata oluÅŸtu.", -"Cannot update addressbook with an empty name." => "Adres defterini boÅŸ bir isimle güncelleyemezsiniz.", -"Error updating addressbook." => "Adres defteri güncellenirken hata oluÅŸtu.", +"Error getting contact object." => "BaÄŸlantı nesnesini kaydederken hata.", +"Error getting PHOTO property." => "Resim özelleÄŸini alırken hata oluÅŸtu.", +"Error saving contact." => "BaÄŸlantıyı kaydederken hata", +"Error resizing image" => "Görüntü yeniden boyutlandırılamadı.", +"Error cropping image" => "Görüntü kırpılamadı.", +"Error creating temporary image" => "Geçici resim oluÅŸtururken hata oluÅŸtu", +"Error finding image: " => "Resim ararken hata oluÅŸtu:", "Error uploading contacts to storage." => "BaÄŸlantıları depoya yükleme hatası", "There is no error, the file uploaded with success" => "Dosya baÅŸarıyla yüklendi, hata oluÅŸmadı", "The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Dosyanın boyutu php.ini dosyasındaki upload_max_filesize limitini aşıyor", @@ -41,17 +42,29 @@ "The uploaded file was only partially uploaded" => "Dosya kısmen karşıya yüklenebildi", "No file was uploaded" => "Hiç dosya gönderilmedi", "Missing a temporary folder" => "Geçici dizin eksik", +"Couldn't save temporary image: " => "Geçici resmi saklayamadı : ", +"Couldn't load temporary image: " => "Geçici resmi yükleyemedi :", +"No file was uploaded. Unknown error" => "Dosya yüklenmedi. Bilinmeyen hata", "Contacts" => "KiÅŸiler", -"Drop a VCF file to import contacts." => "BaÄŸlantıları içe aktarmak için bir VCF dosyası bırakın.", -"Addressbook not found." => "Adres defteri bulunamadı.", +"Sorry, this functionality has not been implemented yet" => "Ãœzgünüz, bu özellik henüz tamamlanmadı.", +"Not implemented" => "Tamamlanmadı.", +"Couldn't get a valid address." => "Geçerli bir adres alınamadı.", +"Error" => "Hata", +"This property has to be non-empty." => "Bu özellik boÅŸ bırakılmamalı.", +"Couldn't serialize elements." => "Öğeler seri hale getiremedi", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "'deleteProperty' tip argümanı olmadan çaÄŸrıldı. Lütfen bugs.owncloud.org a rapor ediniz.", +"Edit name" => "Ä°smi düzenle", +"No files selected for upload." => "Yükleme için dosya seçilmedi.", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "Yüklemeye çalıştığınız dosya sunucudaki dosya yükleme maksimum boyutunu aÅŸmaktadır. ", +"Select type" => "Tür seç", +"Result: " => "Sonuç: ", +" imported, " => " içe aktarıldı, ", +" failed." => " hatalı.", "This is not your addressbook." => "Bu sizin adres defteriniz deÄŸil.", "Contact could not be found." => "KiÅŸi bulunamadı.", -"Address" => "Adres", -"Telephone" => "Telefon", -"Email" => "Eposta", -"Organization" => "Organizasyon", "Work" => "Ä°ÅŸ", "Home" => "Ev", +"Other" => "DiÄŸer", "Mobile" => "Mobil", "Text" => "Metin", "Voice" => "Ses", @@ -60,25 +73,49 @@ "Video" => "Video", "Pager" => "Sayfalayıcı", "Internet" => "Ä°nternet", +"Birthday" => "DoÄŸum günü", +"Business" => "Ä°ÅŸ", +"Call" => "ÇaÄŸrı", +"Clients" => "Müşteriler", +"Deliverer" => "Dağıtıcı", +"Holidays" => "Tatiller", +"Ideas" => "Fikirler", +"Journey" => "Seyahat", +"Jubilee" => "Yıl Dönümü", +"Meeting" => "Toplantı", +"Personal" => "KiÅŸisel", +"Projects" => "Projeler", +"Questions" => "Sorular", "{name}'s Birthday" => "{name}'nin DoÄŸumgünü", "Contact" => "KiÅŸi", "Add Contact" => "KiÅŸi Ekle", +"Import" => "İçe aktar", "Addressbooks" => "Adres defterleri", -"Configure Address Books" => "Adres Defterlerini Yapılandır", -"New Address Book" => "Yeni Adres Defteri", -"Import from VCF" => "VCF'den içeri aktar", -"CardDav Link" => "CardDav BaÄŸlantısı", -"Download" => "Ä°ndir", -"Edit" => "Düzenle", -"Delete" => "Sil", -"Download contact" => "KiÅŸiyi indir", -"Delete contact" => "KiÅŸiyi sil", +"Close" => "Kapat", +"Keyboard shortcuts" => "Klavye kısayolları", +"Navigation" => "Dolaşım", +"Next contact in list" => "Listedeki sonraki kiÅŸi", +"Previous contact in list" => "Listedeki önceki kiÅŸi", +"Expand/collapse current addressbook" => "Åžuanki adres defterini geniÅŸlet/daralt", +"Actions" => "Eylemler", +"Refresh contacts list" => "KiÅŸi listesini tazele", +"Add new contact" => "Yeni kiÅŸi ekle", +"Add new addressbook" => "Yeni adres defteri ekle", +"Delete current contact" => "Åžuanki kiÅŸiyi sil", "Drop photo to upload" => "FotoÄŸrafı yüklenmesi için bırakın", +"Delete current photo" => "Mevcut fotoÄŸrafı sil", +"Edit current photo" => "Mevcut fotoÄŸrafı düzenle", +"Upload new photo" => "Yeni fotoÄŸraf yükle", +"Select photo from ownCloud" => "ownCloud'dan bir fotoÄŸraf seç", "Format custom, Short name, Full name, Reverse or Reverse with comma" => "Biçin özel, Kısa isim, Tam isim, Ters veya noktalı ters", "Edit name details" => "Ä°sim detaylarını düzenle", +"Organization" => "Organizasyon", +"Delete" => "Sil", "Nickname" => "Takma ad", "Enter nickname" => "Takma adı girin", -"Birthday" => "DoÄŸum günü", +"Web site" => "Web sitesi", +"http://www.somesite.com" => "http://www.somesite.com", +"Go to web site" => "Web sitesine git", "dd-mm-yyyy" => "gg-aa-yyyy", "Groups" => "Gruplar", "Separate groups with commas" => "Grupları birbirinden virgülle ayırın", @@ -94,24 +131,26 @@ "Edit address details" => "Adres detaylarını düzenle", "Add notes here." => "Notları buraya ekleyin.", "Add field" => "Alan ekle", -"Profile picture" => "Profil resmi", "Phone" => "Telefon", +"Email" => "Eposta", +"Address" => "Adres", "Note" => "Not", -"Delete current photo" => "Mevcut fotoÄŸrafı sil", -"Edit current photo" => "Mevcut fotoÄŸrafı düzenle", -"Upload new photo" => "Yeni fotoÄŸraf yükle", -"Select photo from ownCloud" => "ownCloud'dan bir fotoÄŸraf seç", +"Download contact" => "KiÅŸiyi indir", +"Delete contact" => "KiÅŸiyi sil", +"The temporary image has been removed from cache." => "Geçici resim ön bellekten silinmiÅŸtir.", "Edit address" => "Adresi düzenle", "Type" => "Tür", "PO Box" => "Posta Kutusu", +"Street address" => "Sokak adresi", +"Street and number" => "Sokak ve Numara", "Extended" => "Uzatılmış", -"Street" => "Sokak", +"Apartment number etc." => "Apartman numarası vb.", "City" => "Åžehir", "Region" => "Bölge", +"E.g. state or province" => "Örn. eyalet veya il", "Zipcode" => "Posta kodu", +"Postal code" => "Posta kodu", "Country" => "Ãœlke", -"Edit categories" => "Kategorileri düzenle", -"Add" => "Ekle", "Addressbook" => "Adres defteri", "Hon. prefixes" => "Kısaltmalar", "Miss" => "Bayan", @@ -132,26 +171,29 @@ "Esq." => "Esq.", "Jr." => "Jr.", "Sn." => "Sn.", -"New Addressbook" => "Yeni Adres defteri", -"Edit Addressbook" => "Adres Defterini Düzenle", -"Displayname" => "Görünen adı", -"Active" => "Aktif", -"Save" => "Kaydet", -"Submit" => "Gönder", -"Cancel" => "Ä°ptal", "Import a contacts file" => "BaÄŸlantı dosyasını içeri aktar", "Please choose the addressbook" => "Yeni adres defterini seç", "create a new addressbook" => "Yeni adres defteri oluÅŸtur", "Name of new addressbook" => "Yeni adres defteri için isim", -"Import" => "İçe aktar", "Importing contacts" => "BaÄŸlantıları içe aktar", +"Contacts imported successfully" => "BaÄŸlantılar baÅŸarıyla içe aktarıldı", +"Close Dialog" => "DiyaloÄŸu kapat", +"Import Addressbook" => "Adres defterini içeri aktar", "Select address book to import to:" => "İçe aktarılacak adres defterini seçin:", +"Drop a VCF file to import contacts." => "BaÄŸlantıları içe aktarmak için bir VCF dosyası bırakın.", "Select from HD" => "HD'den seç", "You have no contacts in your addressbook." => "Adres defterinizde hiç baÄŸlantı yok.", "Add contact" => "BaÄŸlatı ekle", -"Configure addressbooks" => "Adres defterini yapılandır", +"Select Address Books" => "Adres deftelerini seçiniz", +"Enter name" => "Ä°sim giriniz", +"Enter description" => "Tanım giriniz", "CardDAV syncing addresses" => "CardDAV adresleri eÅŸzamanlıyor", "more info" => "daha fazla bilgi", "Primary address (Kontact et al)" => "Birincil adres (BaÄŸlantı ve arkadaÅŸları)", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Download" => "Ä°ndir", +"Edit" => "Düzenle", +"New Address Book" => "Yeni Adres Defteri", +"Save" => "Kaydet", +"Cancel" => "Ä°ptal" ); diff --git a/apps/contacts/l10n/uk.php b/apps/contacts/l10n/uk.php index 4c27acf543e..dcd9efff344 100644 --- a/apps/contacts/l10n/uk.php +++ b/apps/contacts/l10n/uk.php @@ -1,29 +1,24 @@ <?php $TRANSLATIONS = array( "At least one of the address fields has to be filled out." => "Має бути заповнено щонайменше одне поле.", -"Error adding addressbook." => "Помилка при додаванні адреÑної книги.", "This is not your addressbook." => "Це не ваша адреÑна книга.", -"Address" => "ÐдреÑа", -"Telephone" => "Телефон", -"Email" => "Ел.пошта", -"Organization" => "ОрганізаціÑ", "Mobile" => "Мобільний", "Text" => "ТекÑÑ‚", "Voice" => "ГолоÑ", "Fax" => "ФакÑ", "Video" => "Відео", "Pager" => "Пейджер", +"Birthday" => "День народженнÑ", "Add Contact" => "Додати контакт", -"New Address Book" => "Ðова адреÑна книга", -"Download" => "Завантажити", +"Organization" => "ОрганізаціÑ", "Delete" => "Видалити", -"Delete contact" => "Видалити контакт", -"Birthday" => "День народженнÑ", "Phone" => "Телефон", +"Email" => "Ел.пошта", +"Address" => "ÐдреÑа", +"Delete contact" => "Видалити контакт", "Extended" => "Розширено", -"Street" => "ВулицÑ", "City" => "МіÑто", "Zipcode" => "Поштовий індекÑ", "Country" => "Країна", -"Add" => "Додати", -"Displayname" => "Відображуване ім'Ñ" +"Download" => "Завантажити", +"New Address Book" => "Ðова адреÑна книга" ); diff --git a/apps/contacts/l10n/vi.php b/apps/contacts/l10n/vi.php new file mode 100644 index 00000000000..d484fb405ca --- /dev/null +++ b/apps/contacts/l10n/vi.php @@ -0,0 +1,41 @@ +<?php $TRANSLATIONS = array( +"id is not set." => "id không được thiết láºp.", +"No ID provided" => "Không có ID được cung cấp", +"No address books found." => "Không tìm thấy sổ địa chỉ.", +"No contacts found." => "Không tìm thấy danh sách", +"element name is not set." => "tên phần tá» không được thiết láºp.", +"Missing ID" => "Missing ID", +"Error reading contact photo." => "Lá»—i Ä‘á»c liên lạc hình ảnh.", +"The loading photo is not valid." => "Các hình ảnh tải không hợp lệ.", +"File doesn't exist:" => "Táºp tin không tồn tại", +"Error loading image." => "Lá»—i khi tải hình ảnh.", +"Error uploading contacts to storage." => "Lá»—i tải lên danh sách địa chỉ để lÆ°u trữ.", +"There is no error, the file uploaded with success" => "Không có lá»—i, các táºp tin tải lên thà nh công", +"Contacts" => "Liên lạc", +"Work" => "Công việc", +"Home" => "Nhà ", +"Mobile" => "Di Ä‘á»™ng", +"Fax" => "Fax", +"Video" => "Video", +"Pager" => "số trang", +"Birthday" => "Ngà y sinh nháºt", +"Contact" => "Danh sách", +"Add Contact" => "Thêm liên lạc", +"Addressbooks" => "Sổ địa chỉ", +"Organization" => "Tổ chức", +"Delete" => "Xóa", +"Phone" => "Äiện thoại", +"Email" => "Email", +"Address" => "Äịa chỉ", +"Delete contact" => "Xóa liên lạc", +"PO Box" => "Hòm thÆ° bÆ°u Ä‘iện", +"City" => "Thà nh phố", +"Region" => "Vùng/miá»n", +"Zipcode" => "Mã bÆ°u Ä‘iện", +"Country" => "Quốc gia", +"Addressbook" => "Sổ địa chỉ", +"Download" => "Tải vá»", +"Edit" => "Sá»a", +"Save" => "LÆ°u", +"Cancel" => "Hủy" +); diff --git a/apps/contacts/l10n/zh_CN.php b/apps/contacts/l10n/zh_CN.php index dcf9b99bc8b..b3bac7aedfb 100644 --- a/apps/contacts/l10n/zh_CN.php +++ b/apps/contacts/l10n/zh_CN.php @@ -1,46 +1,66 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "(å–消)激活地å€ç°¿é”™è¯¯ã€‚", -"There was an error adding the contact." => "æ·»åŠ è”系人时出错。", -"Cannot add empty property." => "æ— æ³•æ·»åŠ ç©ºå±žæ€§ã€‚", -"At least one of the address fields has to be filled out." => "至少需è¦å¡«å†™ä¸€é¡¹åœ°å€ã€‚", -"Trying to add duplicate property: " => "è¯•å›¾æ·»åŠ é‡å¤å±žæ€§ï¼š ", -"Error adding contact property." => "æ·»åŠ è”系人属性错误。", +"id is not set." => "没有设置 id。", +"Cannot update addressbook with an empty name." => "æ— æ³•ä½¿ç”¨ä¸€ä¸ªç©ºå称更新地å€ç°¿", +"Error updating addressbook." => "更新地å€ç°¿é”™è¯¯", "No ID provided" => "未æä¾› ID", "Error setting checksum." => "è®¾ç½®æ ¡éªŒå€¼é”™è¯¯ã€‚", "No categories selected for deletion." => "未选ä¸è¦åˆ 除的分类。", "No address books found." => "找ä¸åˆ°åœ°å€ç°¿ã€‚", "No contacts found." => "找ä¸åˆ°è”系人。", +"There was an error adding the contact." => "æ·»åŠ è”系人时出错。", +"element name is not set." => "å…ƒç´ å称未设置", +"Cannot add empty property." => "æ— æ³•æ·»åŠ ç©ºå±žæ€§ã€‚", +"At least one of the address fields has to be filled out." => "至少需è¦å¡«å†™ä¸€é¡¹åœ°å€ã€‚", +"Trying to add duplicate property: " => "è¯•å›¾æ·»åŠ é‡å¤å±žæ€§ï¼š ", +"Information about vCard is incorrect. Please reload the page." => "vCard çš„ä¿¡æ¯ä¸æ£ç¡®ã€‚请é‡æ–°åŠ 载页é¢ã€‚", "Missing ID" => "缺少 ID", "Error parsing VCard for ID: \"" => "æ— æ³•è§£æžå¦‚下IDçš„ VCard:“", -"Cannot add addressbook with an empty name." => "æ— æ³•æ— å§“å的地å€ç°¿ã€‚", -"Error adding addressbook." => "æ·»åŠ åœ°å€ç°¿é”™è¯¯ã€‚", -"Error activating addressbook." => "激活地å€ç°¿é”™è¯¯ã€‚", +"checksum is not set." => "æœªè®¾ç½®æ ¡éªŒå€¼ã€‚", +"Information about vCard is incorrect. Please reload the page: " => "vCard ä¿¡æ¯ä¸æ£ç¡®ã€‚请刷新页é¢: ", +"Something went FUBAR. " => "有一些信æ¯æ— 法被处ç†ã€‚", "No contact ID was submitted." => "未æ交è”系人 ID。", "Error reading contact photo." => "读å–è”系人照片错误。", "Error saving temporary file." => "ä¿å˜ä¸´æ—¶æ–‡ä»¶é”™è¯¯ã€‚", "The loading photo is not valid." => "装入的照片ä¸æ£ç¡®ã€‚", -"id is not set." => "没有设置 id。", -"Information about vCard is incorrect. Please reload the page." => "vCard çš„ä¿¡æ¯ä¸æ£ç¡®ã€‚请é‡æ–°åŠ 载页é¢ã€‚", -"Error deleting contact property." => "åˆ é™¤è”系人属性错误。", "Contact ID is missing." => "缺少è”系人 ID。", -"Missing contact id." => "缺少è”系人 ID。", "No photo path was submitted." => "未æ供照片路径。", "File doesn't exist:" => "文件ä¸å˜åœ¨:", "Error loading image." => "åŠ è½½å›¾ç‰‡é”™è¯¯ã€‚", -"checksum is not set." => "æœªè®¾ç½®æ ¡éªŒå€¼ã€‚", -"Error updating contact property." => "æ›´æ–°è”系人属性错误。", -"Error updating addressbook." => "更新地å€ç°¿é”™è¯¯", +"Error getting contact object." => "获å–è”ç³»äººç›®æ ‡æ—¶å‡ºé”™ã€‚", +"Error getting PHOTO property." => "获å–照片属性时出错。", +"Error saving contact." => "ä¿å˜è”系人时出错。", +"Error resizing image" => "缩放图åƒæ—¶å‡ºé”™", +"Error cropping image" => "è£åˆ‡å›¾åƒæ—¶å‡ºé”™", +"Error creating temporary image" => "创建临时图åƒæ—¶å‡ºé”™", +"Error finding image: " => "查找图åƒæ—¶å‡ºé”™: ", +"Error uploading contacts to storage." => "ä¸Šä¼ è”系人到å˜å‚¨ç©ºé—´æ—¶å‡ºé”™", +"There is no error, the file uploaded with success" => "æ–‡ä»¶ä¸Šä¼ æˆåŠŸï¼Œæ²¡æœ‰é”™è¯¯å‘生", +"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "ä¸Šä¼ çš„æ–‡ä»¶é•¿åº¦è¶…å‡ºäº† php.ini ä¸ upload_max_filesize çš„é™åˆ¶", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "ä¸Šä¼ çš„æ–‡ä»¶é•¿åº¦è¶…å‡ºäº† HTML 表å•ä¸ MAX_FILE_SIZE çš„é™åˆ¶", "The uploaded file was only partially uploaded" => "å·²ä¸Šä¼ æ–‡ä»¶åªä¸Šä¼ 了部分", "No file was uploaded" => "æ²¡æœ‰æ–‡ä»¶è¢«ä¸Šä¼ ", "Missing a temporary folder" => "缺少临时目录", +"Couldn't save temporary image: " => "æ— æ³•ä¿å˜ä¸´æ—¶å›¾åƒ: ", +"Couldn't load temporary image: " => "æ— æ³•åŠ è½½ä¸´æ—¶å›¾åƒ: ", +"No file was uploaded. Unknown error" => "æ²¡æœ‰æ–‡ä»¶è¢«ä¸Šä¼ ã€‚æœªçŸ¥é”™è¯¯", "Contacts" => "è”系人", -"Addressbook not found." => "未找到地å€ç°¿ã€‚", +"Sorry, this functionality has not been implemented yet" => "抱æ‰ï¼Œè¿™ä¸ªåŠŸèƒ½æš‚时还没有被实现", +"Not implemented" => "未实现", +"Couldn't get a valid address." => "æ— æ³•èŽ·å–一个åˆæ³•çš„地å€ã€‚", +"Error" => "错误", +"This property has to be non-empty." => "这个属性必须是éžç©ºçš„", +"Couldn't serialize elements." => "æ— æ³•åºåˆ—åŒ–å…ƒç´ ", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "'deleteProperty' 调用时没有类型声明。请到 bugs.owncloud.org 汇报错误", +"Edit name" => "编辑å称", +"No files selected for upload." => "æ²¡æœ‰é€‰æ‹©æ–‡ä»¶ä»¥ä¸Šä¼ ", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "æ‚¨è¯•å›¾ä¸Šä¼ çš„æ–‡ä»¶è¶…å‡ºäº†è¯¥æœåŠ¡å™¨çš„最大文件é™åˆ¶", +"Select type" => "选择类型", +"Result: " => "结果: ", +" imported, " => " 已导入, ", +" failed." => " 失败。", "This is not your addressbook." => "è¿™ä¸æ˜¯æ‚¨çš„地å€ç°¿ã€‚", "Contact could not be found." => "æ— æ³•æ‰¾åˆ°è”系人。", -"Address" => "地å€", -"Telephone" => "电è¯", -"Email" => "电å邮件", -"Organization" => "组织", "Work" => "工作", "Home" => "家åº", "Mobile" => "移动电è¯", @@ -51,24 +71,24 @@ "Video" => "视频", "Pager" => "ä¼ å‘¼æœº", "Internet" => "互è”网", +"Birthday" => "生日", "{name}'s Birthday" => "{name} 的生日", "Contact" => "è”系人", "Add Contact" => "æ·»åŠ è”系人", +"Import" => "导入", "Addressbooks" => "地å€ç°¿", -"Configure Address Books" => "é…置地å€ç°¿", -"New Address Book" => "新建地å€ç°¿", -"Import from VCF" => "从 VCF 导入", -"CardDav Link" => "CardDav 链接", -"Download" => "下载", -"Edit" => "编辑", -"Delete" => "åˆ é™¤", -"Download contact" => "下载è”系人", -"Delete contact" => "åˆ é™¤è”系人", +"Close" => "å…³é—", "Drop photo to upload" => "æ‹–æ‹½å›¾ç‰‡è¿›è¡Œä¸Šä¼ ", +"Delete current photo" => "åˆ é™¤å½“å‰ç…§ç‰‡", +"Edit current photo" => "编辑当å‰ç…§ç‰‡", +"Upload new photo" => "ä¸Šä¼ æ–°ç…§ç‰‡", +"Select photo from ownCloud" => "从 ownCloud 选择照片", +"Format custom, Short name, Full name, Reverse or Reverse with comma" => "è‡ªå®šä¹‰æ ¼å¼ï¼Œç®€ç§°ï¼Œå…¨å,姓在å‰ï¼Œå§“在å‰å¹¶ç”¨é€—å·åˆ†å‰²", "Edit name details" => "编辑å称详情", +"Organization" => "组织", +"Delete" => "åˆ é™¤", "Nickname" => "昵称", "Enter nickname" => "输入昵称", -"Birthday" => "生日", "dd-mm-yyyy" => "yyyy-mm-dd", "Groups" => "分组", "Separate groups with commas" => "用逗å·éš”开分组", @@ -84,44 +104,55 @@ "Edit address details" => "编辑地å€ç»†èŠ‚。", "Add notes here." => "æ·»åŠ æ³¨é‡Šã€‚", "Add field" => "æ·»åŠ å—段", -"Profile picture" => "è”系人图片", "Phone" => "电è¯", +"Email" => "电å邮件", +"Address" => "地å€", "Note" => "注释", -"Delete current photo" => "åˆ é™¤å½“å‰ç…§ç‰‡", -"Edit current photo" => "编辑当å‰ç…§ç‰‡", -"Upload new photo" => "ä¸Šä¼ æ–°ç…§ç‰‡", -"Select photo from ownCloud" => "从 ownCloud 选择照片", +"Download contact" => "下载è”系人", +"Delete contact" => "åˆ é™¤è”系人", +"The temporary image has been removed from cache." => "临时图åƒæ–‡ä»¶å·²ä»Žç¼“å˜ä¸åˆ 除", "Edit address" => "编辑地å€", "Type" => "类型", "PO Box" => "邮箱", "Extended" => "扩展", -"Street" => "è¡—é“", "City" => "城市", "Region" => "地区", "Zipcode" => "邮编", "Country" => "国家", -"Edit categories" => "编辑分类", -"Add" => "æ·»åŠ ", "Addressbook" => "地å€ç°¿", +"Hon. prefixes" => "å誉å—首", +"Miss" => "å°å§", +"Ms" => "女士", +"Mr" => "先生", +"Sir" => "先生", +"Mrs" => "夫人", +"Dr" => "åšå£«", "Given name" => "å", +"Additional names" => "其他å称", "Family name" => "姓", -"New Addressbook" => "新建地å€ç°¿", -"Edit Addressbook" => "编辑地å€ç°¿", -"Displayname" => "显示å称", -"Active" => "激活", -"Save" => "ä¿å˜", -"Submit" => "æ交", -"Cancel" => "å–消", +"Hon. suffixes" => "å誉åŽç¼€", +"J.D." => "法律åšå£«", +"M.D." => "医å¦åšå£«", +"D.O." => "骨科医å¦åšå£«", +"D.C." => "教育å¦åšå£«", +"Ph.D." => "哲å¦åšå£«", +"Esq." => "先生", +"Jr." => "å°", +"Sn." => "è€", "Import a contacts file" => "导入è”系人文件", "Please choose the addressbook" => "请选择地å€ç°¿", "create a new addressbook" => "创建新地å€ç°¿", "Name of new addressbook" => "新地å€ç°¿å称", -"Import" => "导入", "Importing contacts" => "导入è”系人", +"You have no contacts in your addressbook." => "您的地å€ç°¿ä¸æ²¡æœ‰è”系人。", "Add contact" => "æ·»åŠ è”系人", -"Configure addressbooks" => "é…置地å€ç°¿", "CardDAV syncing addresses" => "CardDAV åŒæ¥åœ°å€", "more info" => "更多信æ¯", "Primary address (Kontact et al)" => "é¦–é€‰åœ°å€ (Kontact ç‰)", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Download" => "下载", +"Edit" => "编辑", +"New Address Book" => "新建地å€ç°¿", +"Save" => "ä¿å˜", +"Cancel" => "å–消" ); diff --git a/apps/contacts/l10n/zh_TW.php b/apps/contacts/l10n/zh_TW.php index d4c2efc47aa..0d007ef9c82 100644 --- a/apps/contacts/l10n/zh_TW.php +++ b/apps/contacts/l10n/zh_TW.php @@ -1,27 +1,21 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "在啟用或關閉電話簿時發生錯誤", +"Error updating addressbook." => "電話簿更新ä¸ç™¼ç”ŸéŒ¯èª¤", +"No ID provided" => "未æä¾› ID", +"No contacts found." => "沒有找到è¯çµ¡äºº", "There was an error adding the contact." => "æ·»åŠ é€šè¨ŠéŒ„ç™¼ç”ŸéŒ¯èª¤", "Cannot add empty property." => "ä¸å¯æ·»åŠ 空白內容", "At least one of the address fields has to be filled out." => "è‡³å°‘å¿…é ˆå¡«å¯«ä¸€æ¬„åœ°å€", "Error adding contact property." => "æ·»åŠ é€šè¨ŠéŒ„å…§å®¹ä¸ç™¼ç”ŸéŒ¯èª¤", "No ID provided" => "未æä¾› ID", -"No contacts found." => "沒有找到è¯çµ¡äºº", -"Missing ID" => "éºå¤±ID", "Error adding addressbook." => "æ·»åŠ é›»è©±ç°¿ä¸ç™¼ç”ŸéŒ¯èª¤", "Error activating addressbook." => "啟用電話簿ä¸ç™¼ç”ŸéŒ¯èª¤", "Information about vCard is incorrect. Please reload the page." => "有關 vCard 的資訊ä¸æ£ç¢ºï¼Œè«‹é‡æ–°è¼‰å…¥æ¤é 。", -"Error deleting contact property." => "刪除通訊錄內容ä¸ç™¼ç”ŸéŒ¯èª¤", -"Error updating contact property." => "更新通訊錄內容ä¸ç™¼ç”ŸéŒ¯èª¤", -"Error updating addressbook." => "電話簿更新ä¸ç™¼ç”ŸéŒ¯èª¤", +"Missing ID" => "éºå¤±ID", "No file was uploaded" => "沒有已上傳的檔案", "Contacts" => "通訊錄", -"Addressbook not found." => "找ä¸åˆ°é€šè¨ŠéŒ„", "This is not your addressbook." => "這ä¸æ˜¯ä½ 的電話簿", "Contact could not be found." => "通訊錄未發ç¾", -"Address" => "地å€", -"Telephone" => "電話", -"Email" => "é›»å郵件", -"Organization" => "組織", "Work" => "å…¬å¸", "Home" => "ä½å®…", "Mobile" => "行動電話", @@ -32,23 +26,16 @@ "Video" => "影片", "Pager" => "呼å«å™¨", "Internet" => "網際網路", +"Birthday" => "生日", "{name}'s Birthday" => "{name}的生日", "Contact" => "通訊錄", "Add Contact" => "æ·»åŠ é€šè¨ŠéŒ„", "Addressbooks" => "電話簿", -"Configure Address Books" => "è¨å®šé€šè¨ŠéŒ„", -"New Address Book" => "新電話簿", -"Import from VCF" => "從VCF匯入", -"CardDav Link" => "CardDav è¯çµ", -"Download" => "下載", -"Edit" => "編輯", -"Delete" => "刪除", -"Download contact" => "下載通訊錄", -"Delete contact" => "刪除通訊錄", "Edit name details" => "編輯姓å詳細資訊", +"Organization" => "組織", +"Delete" => "刪除", "Nickname" => "綽號", "Enter nickname" => "輸入綽號", -"Birthday" => "生日", "dd-mm-yyyy" => "dd-mm-yyyy", "Groups" => "群組", "Separate groups with commas" => "用逗號分隔群組", @@ -63,18 +50,19 @@ "Edit address details" => "é›»å郵件ä½å€è©³ç´°è³‡è¨Š", "Add notes here." => "在這裡新增註解", "Add field" => "新增欄ä½", -"Profile picture" => "個人資料照片", "Phone" => "電話", +"Email" => "é›»å郵件", +"Address" => "地å€", "Note" => "註解", +"Download contact" => "下載通訊錄", +"Delete contact" => "刪除通訊錄", "Type" => "é¡žåž‹", "PO Box" => "通訊地å€", "Extended" => "分機", -"Street" => "è¡—é“", "City" => "城市", "Region" => "地å€", "Zipcode" => "郵éžå€è™Ÿ", "Country" => "國家", -"Add" => "æ·»åŠ ", "Addressbook" => "電話簿", "Mr" => "先生", "Sir" => "先生", @@ -83,11 +71,9 @@ "Given name" => "給定å(å)", "Additional names" => "é¡å¤–å", "Family name" => "家æ—å(姓)", -"New Addressbook" => "新電話簿", -"Edit Addressbook" => "編輯電話簿", -"Displayname" => "顯示å稱", -"Active" => "作用ä¸", +"Download" => "下載", +"Edit" => "編輯", +"New Address Book" => "新電話簿", "Save" => "儲å˜", -"Submit" => "æ出", "Cancel" => "å–消" ); diff --git a/apps/contacts/lib/addressbook.php b/apps/contacts/lib/addressbook.php index 974ecb4047d..b487260d327 100644 --- a/apps/contacts/lib/addressbook.php +++ b/apps/contacts/lib/addressbook.php @@ -37,25 +37,68 @@ /** * This class manages our addressbooks. */ -class OC_Contacts_Addressbook{ +class OC_Contacts_Addressbook { /** * @brief Returns the list of addressbooks for a specific user. * @param string $uid - * @return array + * @param boolean $active Only return addressbooks with this $active state, default(=false) is don't care + * @return array or false. */ - public static function all($uid){ - $stmt = OCP\DB::prepare( 'SELECT * FROM `*PREFIX*contacts_addressbooks` WHERE `userid` = ? ORDER BY `displayname`' ); - $result = $stmt->execute(array($uid)); + public static function all($uid, $active=false) { + $values = array($uid); + $active_where = ''; + if ($active) { + $active_where = ' AND `active` = ?'; + $values[] = 1; + } + try { + $stmt = OCP\DB::prepare( 'SELECT * FROM `*PREFIX*contacts_addressbooks` WHERE `userid` = ? ' . $active_where . ' ORDER BY `displayname`' ); + $result = $stmt->execute($values); + } catch(Exception $e) { + OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.' exception: '.$e->getMessage(), OCP\Util::ERROR); + OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.' uid: '.$uid, OCP\Util::DEBUG); + return false; + } $addressbooks = array(); - while( $row = $result->fetchRow()){ + while( $row = $result->fetchRow()) { $addressbooks[] = $row; } - + $addressbooks = array_merge($addressbooks, OCP\Share::getItemsSharedWith('addressbook', OC_Share_Backend_Addressbook::FORMAT_ADDRESSBOOKS)); + if(!$active && !count($addressbooks)) { + $id = self::addDefault($uid); + return array(self::find($id),); + } return $addressbooks; } /** + * @brief Get active addressbook IDs for a user. + * @param integer $uid User id. If null current user will be used. + * @return array + */ + public static function activeIds($uid = null) { + if(is_null($uid)) { + $uid = OCP\USER::getUser(); + } + $activeaddressbooks = self::all($uid, true); + $ids = array(); + foreach($activeaddressbooks as $addressbook) { + $ids[] = $addressbook['id']; + } + return $ids; + } + + /** + * @brief Returns the list of active addressbooks for a specific user. + * @param string $uid + * @return array + */ + public static function active($uid) { + return self::all($uid, true); + } + + /** * @brief Returns the list of addressbooks for a principal (DAV term of user) * @param string $principaluri * @return array @@ -68,9 +111,9 @@ class OC_Contacts_Addressbook{ /** * @brief Gets the data of one address book * @param integer $id - * @return associative array + * @return associative array or false. */ - public static function find($id){ + public static function find($id) { try { $stmt = OCP\DB::prepare( 'SELECT * FROM `*PREFIX*contacts_addressbooks` WHERE `id` = ?' ); $result = $stmt->execute(array($id)); @@ -80,6 +123,23 @@ class OC_Contacts_Addressbook{ OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', id: '.$id, OCP\Util::DEBUG); return false; } + + return $result->fetchRow(); + } + + /** + * @brief Adds default address book + * @return $id ID of the newly created addressbook or false on error. + */ + public static function addDefault($uid = null) { + if(is_null($uid)) { + $uid = OCP\USER::getUser(); + } + $id = self::add($uid, 'Contacts', 'Default Address Book'); + if($id !== false) { + self::setActive($id, true); + } + return $id; } /** @@ -89,17 +149,29 @@ class OC_Contacts_Addressbook{ * @param string $description * @return insertid */ - public static function add($userid,$name,$description=''){ - $all = self::all($userid); + public static function add($uid,$name,$description='') { + try { + $stmt = OCP\DB::prepare( 'SELECT `uri` FROM `*PREFIX*contacts_addressbooks` WHERE `userid` = ? ' ); + $result = $stmt->execute(array($uid)); + } catch(Exception $e) { + OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.' exception: '.$e->getMessage(), OCP\Util::ERROR); + OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.' uid: '.$uid, OCP\Util::DEBUG); + return false; + } $uris = array(); - foreach($all as $i){ - $uris[] = $i['uri']; + while($row = $result->fetchRow()){ + $uris[] = $row['uri']; } $uri = self::createURI($name, $uris ); - - $stmt = OCP\DB::prepare( 'INSERT INTO `*PREFIX*contacts_addressbooks` (`userid`,`displayname`,`uri`,`description`,`ctag`) VALUES(?,?,?,?,?)' ); - $result = $stmt->execute(array($userid,$name,$uri,$description,1)); + try { + $stmt = OCP\DB::prepare( 'INSERT INTO `*PREFIX*contacts_addressbooks` (`userid`,`displayname`,`uri`,`description`,`ctag`) VALUES(?,?,?,?,?)' ); + $result = $stmt->execute(array($uid,$name,$uri,$description,1)); + } catch(Exception $e) { + OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR); + OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', uid: '.$uid, OCP\Util::DEBUG); + return false; + } return OCP\DB::insertid('*PREFIX*contacts_addressbooks'); } @@ -110,13 +182,20 @@ class OC_Contacts_Addressbook{ * @param string $uri * @param string $name * @param string $description - * @return insertid + * @return insertid or false */ - public static function addFromDAVData($principaluri,$uri,$name,$description){ - $userid = self::extractUserID($principaluri); + public static function addFromDAVData($principaluri,$uri,$name,$description) { + $uid = self::extractUserID($principaluri); - $stmt = OCP\DB::prepare( 'INSERT INTO `*PREFIX*contacts_addressbooks` (`userid`,`displayname`,`uri`,`description`,`ctag`) VALUES(?,?,?,?,?)' ); - $result = $stmt->execute(array($userid,$name,$uri,$description,1)); + try { + $stmt = OCP\DB::prepare('INSERT INTO `*PREFIX*contacts_addressbooks` (`userid`,`displayname`,`uri`,`description`,`ctag`) VALUES(?,?,?,?,?)'); + $result = $stmt->execute(array($uid,$name,$uri,$description,1)); + } catch(Exception $e) { + OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR); + OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', uid: '.$uid, OCP\Util::DEBUG); + OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', uri: '.$uri, OCP\Util::DEBUG); + return false; + } return OCP\DB::insertid('*PREFIX*contacts_addressbooks'); } @@ -128,134 +207,51 @@ class OC_Contacts_Addressbook{ * @param string $description * @return boolean */ - public static function edit($id,$name,$description){ + public static function edit($id,$name,$description) { // Need these ones for checking uri $addressbook = self::find($id); - - if(is_null($name)){ - $name = $addressbook['name']; - } - if(is_null($description)){ - $description = $addressbook['description']; - } - - $stmt = OCP\DB::prepare( 'UPDATE `*PREFIX*contacts_addressbooks` SET `displayname`=?,`description`=?, `ctag`=`ctag`+1 WHERE `id`=?' ); - $result = $stmt->execute(array($name,$description,$id)); - - return true; - } - - public static function cleanArray($array, $remove_null_number = true){ - $new_array = array(); - - $null_exceptions = array(); - - foreach ($array as $key => $value){ - $value = trim($value); - - if($remove_null_number){ - $null_exceptions[] = '0'; - } - - if(!in_array($value, $null_exceptions) && $value != "") { - $new_array[] = $value; + if ($addressbook['userid'] != OCP\User::getUser()) { + $sharedAddressbook = OCP\Share::getItemSharedWithBySource('addressbook', $id); + if (!$sharedAddressbook || !($sharedAddressbook['permissions'] & OCP\Share::PERMISSION_UPDATE)) { + return false; } } - return $new_array; - } - - /** - * @brief Get active addressbooks for a user. - * @param integer $uid User id. If null current user will be used. - * @return array - */ - public static function activeIds($uid = null){ - if(is_null($uid)){ - $uid = OCP\USER::getUser(); - } - $prefbooks = OCP\Config::getUserValue($uid,'contacts','openaddressbooks',null); - $prefbooks = explode(';',$prefbooks); - for ($i = 0; $i < count($prefbooks); $i++) { - if(!$prefbooks[$i] || !self::find($prefbooks[$i])) { - unset($prefbooks[$i]); - } + if(is_null($name)) { + $name = $addressbook['name']; } - if(!$prefbooks){ - OCP\Util::writeLog('contacts','OC_Contacts_Addressbook:activeIds:, No active addressbooks',OCP\Util::DEBUG); - $addressbooks = OC_Contacts_Addressbook::all($uid); - if(count($addressbooks) == 0){ - OCP\Util::writeLog('contacts','OC_Contacts_Addressbook:activeIds:, No addressbooks',OCP\Util::DEBUG); - $id = self::add($uid,'default','Default Address Book'); - OCP\Util::writeLog('contacts','OC_Contacts_Addressbook:activeIds:, Created addressbook: '.$id,OCP\Util::DEBUG); - self::setActive($id, true); - $addressbooks = OC_Contacts_Addressbook::all($uid); - } - $prefbooks[] = $addressbooks[0]['id']; - OCP\Config::setUserValue($uid,'contacts','openaddressbooks',implode(';',$prefbooks)); + if(is_null($description)) { + $description = $addressbook['description']; } - return $prefbooks; - } - /** - * @brief Returns the list of active addressbooks for a specific user. - * @param string $uid - * @return array - */ - public static function active($uid){ - $active = self::activeIds($uid); - $addressbooks = array(); - if(!$active) { - return $addressbooks; - } - $ids_sql = join(',', array_fill(0, count($active), '?')); - $prep = 'SELECT * FROM `*PREFIX*contacts_addressbooks` WHERE `id` IN ('.$ids_sql.') ORDER BY `displayname`'; try { - $stmt = OCP\DB::prepare( $prep ); - $result = $stmt->execute($active); - while( $row = $result->fetchRow()){ - $addressbooks[] = $row; - } + $stmt = OCP\DB::prepare('UPDATE `*PREFIX*contacts_addressbooks` SET `displayname`=?,`description`=?, `ctag`=`ctag`+1 WHERE `id`=?'); + $result = $stmt->execute(array($name,$description,$id)); } catch(Exception $e) { - OCP\Util::writeLog('contacts','OC_Contacts_Addressbook:active:, exception: '.$e->getMessage(),OCP\Util::DEBUG); - OCP\Util::writeLog('contacts','OC_Contacts_Addressbook:active, ids: '.join(',', $active),OCP\Util::DEBUG); - OCP\Util::writeLog('contacts','OC_Contacts_Addressbook::active, SQL:'.$prep,OCP\Util::DEBUG); - return array(); + OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR); + OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', id: '.$id, OCP\Util::DEBUG); + return false; } - return $addressbooks; + return true; } /** * @brief Activates an addressbook * @param integer $id - * @param integer $name + * @param boolean $active * @return boolean */ - public static function setActive($id,$active=true){ - // Need these ones for checking uri - //$addressbook = self::find($id); - - if(is_null($id)){ - $id = 0; - } - - $openaddressbooks = self::activeIds(); - if($active) { - if(!in_array($id, $openaddressbooks)) { - $openaddressbooks[] = $id; - } - } else { - if(in_array($id, $openaddressbooks)) { - unset($openaddressbooks[array_search($id, $openaddressbooks)]); - } + public static function setActive($id,$active) { + $sql = 'UPDATE `*PREFIX*contacts_addressbooks` SET `active` = ? WHERE `id` = ?'; + OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', id: '.$id.', active: '.intval($active), OCP\Util::ERROR); + try { + $stmt = OCP\DB::prepare($sql); + $stmt->execute(array(intval($active), $id)); + return true; + } catch(Exception $e) { + OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception for '.$id.': '.$e->getMessage(), OCP\Util::ERROR); + return false; } - // NOTE: Ugly hack... - $openaddressbooks = self::cleanArray($openaddressbooks, false); - sort($openaddressbooks, SORT_NUMERIC); - // FIXME: I alway end up with a ';' prepending when imploding the array..? - OCP\Config::setUserValue(OCP\USER::getUser(),'contacts','openaddressbooks',implode(';', $openaddressbooks)); - - return true; } /** @@ -263,9 +259,16 @@ class OC_Contacts_Addressbook{ * @param integer $id ID of the address book. * @return boolean */ - public static function isActive($id){ - //OCP\Util::writeLog('contacts','OC_Contacts_Addressbook::isActive('.$id.'):'.in_array($id, self::activeIds()), OCP\Util::DEBUG); - return in_array($id, self::activeIds()); + public static function isActive($id) { + $sql = 'SELECT `active` FROM `*PREFIX*contacts_addressbooks` WHERE `id` = ?'; + try { + $stmt = OCP\DB::prepare( $sql ); + $result = $stmt->execute(array($id)); + $row = $result->fetchRow(); + return (bool)$row['active']; + } catch(Exception $e) { + OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR); + } } /** @@ -273,11 +276,22 @@ class OC_Contacts_Addressbook{ * @param integer $id * @return boolean */ - public static function delete($id){ - // FIXME: There's no error checking at all. + public static function delete($id) { + $addressbook = self::find($id); + if ($addressbook['userid'] != OCP\User::getUser()) { + $sharedAddressbook = OCP\Share::getItemSharedWithBySource('addressbook', $id); + if (!$sharedAddressbook || !($sharedAddressbook['permissions'] & OCP\Share::PERMISSION_DELETE)) { + return false; + } + } self::setActive($id, false); - $stmt = OCP\DB::prepare( 'DELETE FROM `*PREFIX*contacts_addressbooks` WHERE `id` = ?' ); - $stmt->execute(array($id)); + try { + $stmt = OCP\DB::prepare( 'DELETE FROM `*PREFIX*contacts_addressbooks` WHERE `id` = ?' ); + $stmt->execute(array($id)); + } catch(Exception $e) { + OCP\Util::writeLog('contacts', __CLASS__.'::'.__METHOD__.', exception for '.$id.': '.$e->getMessage(), OCP\Util::ERROR); + return false; + } $cards = OC_Contacts_VCard::all($id); foreach($cards as $card){ @@ -292,7 +306,7 @@ class OC_Contacts_Addressbook{ * @param integer $id * @return boolean */ - public static function touch($id){ + public static function touch($id) { $stmt = OCP\DB::prepare( 'UPDATE `*PREFIX*contacts_addressbooks` SET `ctag` = `ctag` + 1 WHERE `id` = ?' ); $stmt->execute(array($id)); @@ -305,11 +319,11 @@ class OC_Contacts_Addressbook{ * @param array $existing existing addressbook URIs * @return string new name */ - public static function createURI($name,$existing){ - $name = strtolower($name); + public static function createURI($name,$existing) { + $name = str_replace(' ', '_', strtolower($name)); $newname = $name; $i = 1; - while(in_array($newname,$existing)){ + while(in_array($newname, $existing)) { $newname = $name.$i; $i = $i + 1; } @@ -320,8 +334,8 @@ class OC_Contacts_Addressbook{ * @brief gets the userid from a principal path * @return string */ - public static function extractUserID($principaluri){ - list($prefix,$userid) = Sabre_DAV_URLUtil::splitPath($principaluri); + public static function extractUserID($principaluri) { + list($prefix, $userid) = Sabre_DAV_URLUtil::splitPath($principaluri); return $userid; } } diff --git a/apps/contacts/lib/app.php b/apps/contacts/lib/app.php index 29428763d60..e8e1937404f 100644 --- a/apps/contacts/lib/app.php +++ b/apps/contacts/lib/app.php @@ -10,7 +10,6 @@ * This class manages our app actions */ OC_Contacts_App::$l10n = OC_L10N::get('contacts'); -OC_Contacts_App::$categories = new OC_VCategories('contacts'); class OC_Contacts_App { /* * @brief language object for calendar app @@ -23,17 +22,39 @@ class OC_Contacts_App { public static $categories = null; public static function getAddressbook($id) { + // TODO: Throw an exception instead of returning json. $addressbook = OC_Contacts_Addressbook::find( $id ); - if( $addressbook === false || $addressbook['userid'] != OCP\USER::getUser()) { + if($addressbook === false || $addressbook['userid'] != OCP\USER::getUser()) { if ($addressbook === false) { - OCP\Util::writeLog('contacts', 'Addressbook not found: '. $id, OCP\Util::ERROR); - OCP\JSON::error(array('data' => array( 'message' => self::$l10n->t('Addressbook not found.')))); - } - else { - OCP\Util::writeLog('contacts', 'Addressbook('.$id.') is not from '.OCP\USER::getUser(), OCP\Util::ERROR); - OCP\JSON::error(array('data' => array( 'message' => self::$l10n->t('This is not your addressbook.')))); + OCP\Util::writeLog('contacts', + 'Addressbook not found: '. $id, + OCP\Util::ERROR); + //throw new Exception('Addressbook not found: '. $id); + OCP\JSON::error( + array( + 'data' => array( + 'message' => self::$l10n->t('Addressbook not found: ' . $id) + ) + ) + ); + } else { + $sharedAddressbook = OCP\Share::getItemSharedWithBySource('addressbook', $id, OC_Share_Backend_Addressbook::FORMAT_ADDRESSBOOKS); + if ($sharedAddressbook) { + return $sharedAddressbook; + } else { + OCP\Util::writeLog('contacts', + 'Addressbook('.$id.') is not from '.OCP\USER::getUser(), + OCP\Util::ERROR); + //throw new Exception('This is not your addressbook.'); + OCP\JSON::error( + array( + 'data' => array( + 'message' => self::$l10n->t('This is not your addressbook.') + ) + ) + ); + } } - exit(); } return $addressbook; } @@ -41,8 +62,17 @@ class OC_Contacts_App { public static function getContactObject($id) { $card = OC_Contacts_VCard::find( $id ); if( $card === false ) { - OCP\Util::writeLog('contacts', 'Contact could not be found: '.$id, OCP\Util::ERROR); - OCP\JSON::error(array('data' => array( 'message' => self::$l10n->t('Contact could not be found.').' '.$id))); + OCP\Util::writeLog('contacts', + 'Contact could not be found: '.$id, + OCP\Util::ERROR); + OCP\JSON::error( + array( + 'data' => array( + 'message' => self::$l10n->t('Contact could not be found.') + .' '.print_r($id, true) + ) + ) + ); exit(); } @@ -58,22 +88,6 @@ class OC_Contacts_App { $card = self::getContactObject( $id ); $vcard = OC_VObject::parse($card['carddata']); - // Try to fix cards with missing 'N' field from pre ownCloud 4. Hot damn, this is ugly... - if(!is_null($vcard) && !$vcard->__isset('N')) { - $version = OCP\App::getAppVersion('contacts'); - if($version >= 5) { - OCP\Util::writeLog('contacts','OC_Contacts_App::getContactVCard. Deprecated check for missing N field', OCP\Util::DEBUG); - } - OCP\Util::writeLog('contacts','getContactVCard, Missing N field', OCP\Util::DEBUG); - if($vcard->__isset('FN')) { - OCP\Util::writeLog('contacts','getContactVCard, found FN field: '.$vcard->__get('FN'), OCP\Util::DEBUG); - $n = implode(';', array_reverse(array_slice(explode(' ', $vcard->__get('FN')), 0, 2))).';;;'; - $vcard->setString('N', $n); - OC_Contacts_VCard::edit( $id, $vcard); - } else { // Else just add an empty 'N' field :-P - $vcard->setString('N', 'Unknown;Name;;;'); - } - } if (!is_null($vcard) && !isset($vcard->REV)) { $rev = new DateTime('@'.$card['lastmodified']); $vcard->setString('REV', $rev->format(DateTime::W3C)); @@ -95,14 +109,77 @@ class OC_Contacts_App { /** * @return array of vcard prop => label */ - public static function getAddPropertyOptions() { + public static function getIMOptions($im = null) { $l10n = self::$l10n; - return array( - 'ADR' => $l10n->t('Address'), - 'TEL' => $l10n->t('Telephone'), - 'EMAIL' => $l10n->t('Email'), - 'ORG' => $l10n->t('Organization'), - ); + $ims = array( + 'jabber' => array( + 'displayname' => (string)$l10n->t('Jabber'), + 'xname' => 'X-JABBER', + 'protocol' => 'xmpp', + ), + 'aim' => array( + 'displayname' => (string)$l10n->t('AIM'), + 'xname' => 'X-AIM', + 'protocol' => 'aim', + ), + 'msn' => array( + 'displayname' => (string)$l10n->t('MSN'), + 'xname' => 'X-MSN', + 'protocol' => 'msn', + ), + 'twitter' => array( + 'displayname' => (string)$l10n->t('Twitter'), + 'xname' => 'X-TWITTER', + 'protocol' => null, + ), + 'googletalk' => array( + 'displayname' => (string)$l10n->t('GoogleTalk'), + 'xname' => null, + 'protocol' => 'xmpp', + ), + 'facebook' => array( + 'displayname' => (string)$l10n->t('Facebook'), + 'xname' => null, + 'protocol' => 'xmpp', + ), + 'xmpp' => array( + 'displayname' => (string)$l10n->t('XMPP'), + 'xname' => null, + 'protocol' => 'xmpp', + ), + 'icq' => array( + 'displayname' => (string)$l10n->t('ICQ'), + 'xname' => 'X-ICQ', + 'protocol' => 'icq', + ), + 'yahoo' => array( + 'displayname' => (string)$l10n->t('Yahoo'), + 'xname' => 'X-YAHOO', + 'protocol' => 'ymsgr', + ), + 'skype' => array( + 'displayname' => (string)$l10n->t('Skype'), + 'xname' => 'X-SKYPE', + 'protocol' => 'skype', + ), + 'qq' => array( + 'displayname' => (string)$l10n->t('QQ'), + 'xname' => 'X-SKYPE', + 'protocol' => 'x-apple', + ), + 'gadugadu' => array( + 'displayname' => (string)$l10n->t('GaduGadu'), + 'xname' => 'X-SKYPE', + 'protocol' => 'x-apple', + ), + ); + if(is_null($im)) { + return $ims; + } else { + $ims['ymsgr'] = $ims['yahoo']; + $ims['gtalk'] = $ims['googletalk']; + return isset($ims[$im]) ? $ims[$im] : null; + } } /** @@ -111,54 +188,83 @@ class OC_Contacts_App { public static function getTypesOfProperty($prop) { $l = self::$l10n; switch($prop) { - case 'ADR': - return array( - 'WORK' => $l->t('Work'), - 'HOME' => $l->t('Home'), - ); - case 'TEL': - return array( - 'HOME' => $l->t('Home'), - 'CELL' => $l->t('Mobile'), - 'WORK' => $l->t('Work'), - 'TEXT' => $l->t('Text'), - 'VOICE' => $l->t('Voice'), - 'MSG' => $l->t('Message'), - 'FAX' => $l->t('Fax'), - 'VIDEO' => $l->t('Video'), - 'PAGER' => $l->t('Pager'), - ); - case 'EMAIL': - return array( - 'WORK' => $l->t('Work'), - 'HOME' => $l->t('Home'), - 'INTERNET' => $l->t('Internet'), - ); + case 'ADR': + case 'IMPP': + return array( + 'WORK' => $l->t('Work'), + 'HOME' => $l->t('Home'), + 'OTHER' => $l->t('Other'), + ); + case 'TEL': + return array( + 'HOME' => $l->t('Home'), + 'CELL' => $l->t('Mobile'), + 'WORK' => $l->t('Work'), + 'TEXT' => $l->t('Text'), + 'VOICE' => $l->t('Voice'), + 'MSG' => $l->t('Message'), + 'FAX' => $l->t('Fax'), + 'VIDEO' => $l->t('Video'), + 'PAGER' => $l->t('Pager'), + 'OTHER' => $l->t('Other'), + ); + case 'EMAIL': + return array( + 'WORK' => $l->t('Work'), + 'HOME' => $l->t('Home'), + 'INTERNET' => $l->t('Internet'), + ); } } - /* + /** * @brief returns the vcategories object of the user * @return (object) $vcategories */ protected static function getVCategories() { if (is_null(self::$categories)) { - self::$categories = new OC_VCategories('contacts'); + self::$categories = new OC_VCategories('contacts', + null, + self::getDefaultCategories()); } return self::$categories; } - - /* + + /** * @brief returns the categories for the user * @return (Array) $categories */ public static function getCategories() { - $categories = self::$categories->categories(); + $categories = self::getVCategories()->categories(); if(count($categories) == 0) { self::scanCategories(); $categories = self::$categories->categories(); } - return $categories; + return ($categories ? $categories : self::getDefaultCategories()); + } + + /** + * @brief returns the default categories of ownCloud + * @return (array) $categories + */ + public static function getDefaultCategories(){ + return array( + (string)self::$l10n->t('Birthday'), + (string)self::$l10n->t('Business'), + (string)self::$l10n->t('Call'), + (string)self::$l10n->t('Clients'), + (string)self::$l10n->t('Deliverer'), + (string)self::$l10n->t('Holidays'), + (string)self::$l10n->t('Ideas'), + (string)self::$l10n->t('Journey'), + (string)self::$l10n->t('Jubilee'), + (string)self::$l10n->t('Meeting'), + (string)self::$l10n->t('Other'), + (string)self::$l10n->t('Personal'), + (string)self::$l10n->t('Projects'), + (string)self::$l10n->t('Questions'), + (string)self::$l10n->t('Work'), + ); } /** @@ -173,16 +279,25 @@ class OC_Contacts_App { foreach($vcaddressbooks as $vcaddressbook) { $vcaddressbookids[] = $vcaddressbook['id']; } - $vccontacts = OC_Contacts_VCard::all($vcaddressbookids); - } - } - if(is_array($vccontacts) && count($vccontacts) > 0) { - $cards = array(); - foreach($vccontacts as $vccontact) { - $cards[] = $vccontact['carddata']; + $start = 0; + $batchsize = 10; + while($vccontacts = + OC_Contacts_VCard::all($vcaddressbookids, $start, $batchsize)) { + $cards = array(); + foreach($vccontacts as $vccontact) { + $cards[] = $vccontact['carddata']; + } + OCP\Util::writeLog('contacts', + __CLASS__.'::'.__METHOD__ + .', scanning: '.$batchsize.' starting from '.$start, + OCP\Util::DEBUG); + // only reset on first batch. + self::getVCategories()->rescan($cards, + true, + ($start == 0 ? true : false)); + $start += $batchsize; + } } - - self::$categories->rescan($cards); } } @@ -194,6 +309,18 @@ class OC_Contacts_App { self::getVCategories()->loadFromVObject($contact, true); } + /** + * @brief Get the last modification time. + * @param $vcard OC_VObject + * $return DateTime | null + */ + public static function lastModified($vcard) { + $rev = $vcard->getAsString('REV'); + if ($rev) { + return DateTime::createFromFormat(DateTime::W3C, $rev); + } + } + public static function setLastModifiedHeader($contact) { $rev = $contact->getAsString('REV'); if ($rev) { diff --git a/apps/contacts/lib/hooks.php b/apps/contacts/lib/hooks.php index 3afad1ef7d3..8b7adc60f27 100644 --- a/apps/contacts/lib/hooks.php +++ b/apps/contacts/lib/hooks.php @@ -21,6 +21,15 @@ */ /** + * The following signals are being emitted: + * + * OC_Contacts_VCard::post_moveToAddressbook(array('aid' => $aid, 'id' => $id)) + * OC_Contacts_VCard::pre_deleteVCard(array('aid' => $aid, 'id' => $id, 'uri' = $uri)); (NOTE: the values can be null depending on which method emits them) + * OC_Contacts_VCard::post_updateVCard($id) + * OC_Contacts_VCard::post_createVCard($newid) + */ + +/** * This class contains all hooks. */ class OC_Contacts_Hooks{ @@ -30,7 +39,7 @@ class OC_Contacts_Hooks{ * @return array */ static public function createUser($parameters) { - OC_Contacts_Addressbook::add($parameters['uid'],'default','Default Address Book'); + OC_Contacts_Addressbook::addDefault($parameters['uid']); return true; } @@ -52,8 +61,8 @@ class OC_Contacts_Hooks{ static public function getCalenderSources($parameters) { $base_url = OCP\Util::linkTo('calendar', 'ajax/events.php').'?calendar_id='; foreach(OC_Contacts_Addressbook::all(OCP\USER::getUser()) as $addressbook) { - $parameters['sources'][] = - array( + $parameters['sources'][] + = array( 'url' => $base_url.'birthday_'. $addressbook['id'], 'backgroundColor' => '#cccccc', 'borderColor' => '#888', @@ -66,7 +75,7 @@ class OC_Contacts_Hooks{ static public function getBirthdayEvents($parameters) { $name = $parameters['calendar_id']; - if (strpos('birthday_', $name) != 0) { + if (strpos($name, 'birthday_') != 0) { return; } $info = explode('_', $name); @@ -81,17 +90,25 @@ class OC_Contacts_Hooks{ if ($birthday) { $date = new DateTime($birthday); $vevent = new OC_VObject('VEVENT'); - $vevent->setDateTime('LAST-MODIFIED', new DateTime($vcard->REV)); - $vevent->setDateTime('DTSTART', $date, Sabre_VObject_Element_DateTime::DATE); + //$vevent->setDateTime('LAST-MODIFIED', new DateTime($vcard->REV)); + $vevent->setDateTime('DTSTART', $date, + Sabre_VObject_Element_DateTime::DATE); $vevent->setString('DURATION', 'P1D'); + $vevent->setString('UID', substr(md5(rand().time()), 0, 10)); // DESCRIPTION? $vevent->setString('RRULE', 'FREQ=YEARLY'); - $title = str_replace('{name}', $vcard->getAsString('FN'), OC_Contacts_App::$l10n->t('{name}\'s Birthday')); + $title = str_replace('{name}', + $vcard->getAsString('FN'), + OC_Contacts_App::$l10n->t('{name}\'s Birthday')); $parameters['events'][] = array( 'id' => 0,//$card['id'], 'vevent' => $vevent, 'repeating' => true, 'summary' => $title, + 'calendardata' => "BEGIN:VCALENDAR\nVERSION:2.0\n" + . "PRODID:ownCloud Contacts " + . OCP\App::getAppVersion('contacts') . "\n" + . $vevent->serialize() . "END:VCALENDAR" ); } } diff --git a/apps/contacts/lib/sabre/addressbook.php b/apps/contacts/lib/sabre/addressbook.php new file mode 100644 index 00000000000..e9eb77472b6 --- /dev/null +++ b/apps/contacts/lib/sabre/addressbook.php @@ -0,0 +1,128 @@ +<?php +/** + * ownCloud - Addressbook + * + * @author Thomas Tanghus + * @copyright 2012 Thomas Tanghus (thomas@tanghus.net) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +/** + * This class overrides __construct to get access to $addressBookInfo and + * $carddavBackend, Sabre_CardDAV_AddressBook::getACL() to return read/write + * permissions based on user and shared state and it overrides + * Sabre_CardDAV_AddressBook::getChild() and Sabre_CardDAV_AddressBook::getChildren() + * to instantiate OC_Connector_Sabre_CardDAV_Cards. +*/ +class OC_Connector_Sabre_CardDAV_AddressBook extends Sabre_CardDAV_AddressBook { + + /** + * CardDAV backend + * + * @var Sabre_CardDAV_Backend_Abstract + */ + private $carddavBackend; + + /** + * Constructor + * + * @param Sabre_CardDAV_Backend_Abstract $carddavBackend + * @param array $addressBookInfo + */ + public function __construct( + Sabre_CardDAV_Backend_Abstract $carddavBackend, + array $addressBookInfo) { + + $this->carddavBackend = $carddavBackend; + $this->addressBookInfo = $addressBookInfo; + parent::__construct($carddavBackend, $addressBookInfo); + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + public function getACL() { + + $readprincipal = $this->getOwner(); + $writeprincipal = $this->getOwner(); + $uid = OC_Contacts_Addressbook::extractUserID($this->getOwner()); + + if($uid != OCP\USER::getUser()) { + $sharedAddressbook = OCP\Share::getItemSharedWithBySource('addressbook', $this->addressBookInfo['id']); + if ($sharedAddressbook && ($sharedAddressbook['permissions'] & OCP\Share::PERMISSION_READ)) { + $readprincipal = 'principals/' . OCP\USER::getUser(); + } + if ($sharedAddressbook && ($sharedAddressbook['permissions'] & OCP\Share::PERMISSION_UPDATE)) { + $writeprincipal = 'principals/' . OCP\USER::getUser(); + } + } + + return array( + array( + 'privilege' => '{DAV:}read', + 'principal' => $readprincipal, + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $writeprincipal, + 'protected' => true, + ), + + ); + + } + + /** + * Returns a card + * + * @param string $name + * @return OC_Connector_Sabre_DAV_Card + */ + public function getChild($name) { + + $obj = $this->carddavBackend->getCard($this->addressBookInfo['id'],$name); + if (!$obj) throw new Sabre_DAV_Exception_NotFound('Card not found'); + return new OC_Connector_Sabre_CardDAV_Card($this->carddavBackend,$this->addressBookInfo,$obj); + + } + + /** + * Returns the full list of cards + * + * @return array + */ + public function getChildren() { + + $objs = $this->carddavBackend->getCards($this->addressBookInfo['id']); + $children = array(); + foreach($objs as $obj) { + $children[] = new OC_Connector_Sabre_CardDAV_Card($this->carddavBackend,$this->addressBookInfo,$obj); + } + return $children; + + } +}
\ No newline at end of file diff --git a/apps/contacts/lib/sabre/addressbookroot.php b/apps/contacts/lib/sabre/addressbookroot.php new file mode 100644 index 00000000000..69cd6021284 --- /dev/null +++ b/apps/contacts/lib/sabre/addressbookroot.php @@ -0,0 +1,45 @@ +<?php +/** + * ownCloud - Addressbook + * + * @author Thomas Tanghus + * @copyright 2012 Thomas Tanghus (thomas@tanghus.net) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +/** + * This class overrides Sabre_CardDAV_AddressBookRoot::getChildForPrincipal() + * to instantiate OC_Connector_CardDAV_UserAddressBooks. +*/ +class OC_Connector_Sabre_CardDAV_AddressBookRoot extends Sabre_CardDAV_AddressBookRoot { + + /** + * This method returns a node for a principal. + * + * The passed array contains principal information, and is guaranteed to + * at least contain a uri item. Other properties may or may not be + * supplied by the authentication backend. + * + * @param array $principal + * @return Sabre_DAV_INode + */ + public function getChildForPrincipal(array $principal) { + + return new OC_Connector_Sabre_CardDAV_UserAddressBooks($this->carddavBackend, $principal['uri']); + + } + +}
\ No newline at end of file diff --git a/apps/contacts/lib/connector_sabre.php b/apps/contacts/lib/sabre/backend.php index c967e906601..04737eb3ab4 100644 --- a/apps/contacts/lib/connector_sabre.php +++ b/apps/contacts/lib/sabre/backend.php @@ -40,7 +40,8 @@ class OC_Connector_Sabre_CardDAV extends Sabre_CardDAV_Backend_Abstract { 'uri' => $i['uri'], 'principaluri' => 'principals/'.$i['userid'], '{DAV:}displayname' => $i['displayname'], - '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => $i['description'], + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' + => $i['description'], '{http://calendarserver.org/ns/}getctag' => $i['ctag'], ); } @@ -69,7 +70,8 @@ class OC_Connector_Sabre_CardDAV extends Sabre_CardDAV_Backend_Abstract { case '{DAV:}displayname' : $name = $newvalue; break; - case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' : + case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV + . '}addressbook-description' : $description = $newvalue; break; default : @@ -79,7 +81,7 @@ class OC_Connector_Sabre_CardDAV extends Sabre_CardDAV_Backend_Abstract { } } - OC_Contacts_Addressbook::edit($addressbookid,$name,$description); + OC_Contacts_Addressbook::edit($addressbookid, $name, $description); return true; @@ -104,16 +106,23 @@ class OC_Connector_Sabre_CardDAV extends Sabre_CardDAV_Backend_Abstract { case '{DAV:}displayname' : $displayname = $newvalue; break; - case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' : + case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV + . '}addressbook-description' : $description = $newvalue; break; default : - throw new Sabre_DAV_Exception_BadRequest('Unknown property: ' . $property); + throw new Sabre_DAV_Exception_BadRequest('Unknown property: ' + . $property); } } - OC_Contacts_Addressbook::addFromDAVData($principaluri,$url,$name,$description); + OC_Contacts_Addressbook::addFromDAVData( + $principaluri, + $url, + $name, + $description + ); } /** @@ -136,9 +145,12 @@ class OC_Connector_Sabre_CardDAV extends Sabre_CardDAV_Backend_Abstract { $data = OC_Contacts_VCard::all($addressbookid); $cards = array(); foreach($data as $i){ + //OCP\Util::writeLog('contacts', __METHOD__.', uri: ' . $i['uri'], OCP\Util::DEBUG); $cards[] = array( 'id' => $i['id'], - 'carddata' => $i['carddata'], + //'carddata' => $i['carddata'], + 'size' => strlen($i['carddata']), + 'etag' => md5($i['carddata']), 'uri' => $i['uri'], 'lastmodified' => $i['lastmodified'] ); } @@ -154,7 +166,7 @@ class OC_Connector_Sabre_CardDAV extends Sabre_CardDAV_Backend_Abstract { * @return array */ public function getCard($addressbookid, $carduri) { - return OC_Contacts_VCard::findWhereDAVDataIs($addressbookid,$carduri); + return OC_Contacts_VCard::findWhereDAVDataIs($addressbookid, $carduri); } @@ -180,7 +192,9 @@ class OC_Connector_Sabre_CardDAV extends Sabre_CardDAV_Backend_Abstract { * @return bool */ public function updateCard($addressbookid, $carduri, $carddata) { - return OC_Contacts_VCard::editFromDAVData($addressbookid, $carduri, $carddata); + return OC_Contacts_VCard::editFromDAVData( + $addressbookid, $carduri, $carddata + ); } /** diff --git a/apps/contacts/lib/sabre/card.php b/apps/contacts/lib/sabre/card.php new file mode 100644 index 00000000000..f4414a25f7c --- /dev/null +++ b/apps/contacts/lib/sabre/card.php @@ -0,0 +1,94 @@ +<?php +/** + * ownCloud - Addressbook + * + * @author Thomas Tanghus + * @copyright 2012 Thomas Tanghus (thomas@tanghus.net) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +/** + * This class overrides Sabre_CardDAV_Card::getACL() + * to return read/write permissions based on user and shared state. +*/ +class OC_Connector_Sabre_CardDAV_Card extends Sabre_CardDAV_Card { + + /** + * Array with information about the containing addressbook + * + * @var array + */ + private $addressBookInfo; + + /** + * Constructor + * + * @param Sabre_CardDAV_Backend_Abstract $carddavBackend + * @param array $addressBookInfo + * @param array $cardData + */ + public function __construct(Sabre_CardDAV_Backend_Abstract $carddavBackend, array $addressBookInfo, array $cardData) { + + $this->addressBookInfo = $addressBookInfo; + parent::__construct($carddavBackend, $addressBookInfo, $cardData); + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + public function getACL() { + + $readprincipal = $this->getOwner(); + $writeprincipal = $this->getOwner(); + $uid = OC_Contacts_Addressbook::extractUserID($this->getOwner()); + + if($uid != OCP\USER::getUser()) { + $sharedAddressbook = OCP\Share::getItemSharedWithBySource('addressbook', $this->addressBookInfo['id']); + if ($sharedAddressbook && ($sharedAddressbook['permissions'] & OCP\Share::PERMISSION_READ)) { + $readprincipal = 'principals/' . OCP\USER::getUser(); + } + if ($sharedAddressbook && ($sharedAddressbook['permissions'] & OCP\Share::PERMISSION_UPDATE)) { + $writeprincipal = 'principals/' . OCP\USER::getUser(); + } + } + + return array( + array( + 'privilege' => '{DAV:}read', + 'principal' => $readprincipal, + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $writeprincipal, + 'protected' => true, + ), + + ); + + } + +}
\ No newline at end of file diff --git a/apps/contacts/lib/sabre/useraddressbooks.php b/apps/contacts/lib/sabre/useraddressbooks.php new file mode 100644 index 00000000000..328b433bd6e --- /dev/null +++ b/apps/contacts/lib/sabre/useraddressbooks.php @@ -0,0 +1,45 @@ +<?php +/** + * ownCloud - Addressbook + * + * @author Thomas Tanghus + * @copyright 2012 Thomas Tanghus (thomas@tanghus.net) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +/** + * This class overrides Sabre_CardDAV_UserAddressBooks::getChildren() + * to instantiate OC_Connector_Sabre_CardDAV_AddressBooks. +*/ +class OC_Connector_Sabre_CardDAV_UserAddressBooks extends Sabre_CardDAV_UserAddressBooks { + + /** + * Returns a list of addressbooks + * + * @return array + */ + public function getChildren() { + + $addressbooks = $this->carddavBackend->getAddressbooksForUser($this->principalUri); + $objs = array(); + foreach($addressbooks as $addressbook) { + $objs[] = new OC_Connector_Sabre_CardDAV_AddressBook($this->carddavBackend, $addressbook); + } + return $objs; + + } + +}
\ No newline at end of file diff --git a/apps/contacts/lib/sabre/vcfexportplugin.php b/apps/contacts/lib/sabre/vcfexportplugin.php new file mode 100644 index 00000000000..f0ba60b303f --- /dev/null +++ b/apps/contacts/lib/sabre/vcfexportplugin.php @@ -0,0 +1,100 @@ +<?php + +/** + * VCF Exporter + * + * This plugin adds the ability to export entire address books as .vcf files. + * This is useful for clients that don't support CardDAV yet. They often do + * support vcf files. + * + * @package Sabre + * @subpackage CardDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class OC_Connector_Sabre_CardDAV_VCFExportPlugin extends Sabre_DAV_ServerPlugin { + + /** + * Reference to Server class + * + * @var Sabre_DAV_Server + */ + private $server; + + /** + * Initializes the plugin and registers event handlers + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + $this->server = $server; + $this->server->subscribeEvent('beforeMethod', array($this,'beforeMethod'), 90); + + } + + /** + * 'beforeMethod' event handles. This event handles intercepts GET requests ending + * with ?export + * + * @param string $method + * @param string $uri + * @return bool + */ + public function beforeMethod($method, $uri) { + + if ($method!='GET') return; + if ($this->server->httpRequest->getQueryString()!='export') return; + + // splitting uri + list($uri) = explode('?', $uri, 2); + + $node = $this->server->tree->getNodeForPath($uri); + + if (!($node instanceof Sabre_CardDAV_IAddressBook)) return; + + // Checking ACL, if available. + if ($aclPlugin = $this->server->getPlugin('acl')) { + $aclPlugin->checkPrivileges($uri, '{DAV:}read'); + } + + $this->server->httpResponse->setHeader('Content-Type', 'text/directory'); + $this->server->httpResponse->sendStatus(200); + + $nodes = $this->server->getPropertiesForPath($uri, array( + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}address-data', + ), 1); + + $this->server->httpResponse->sendBody($this->generateVCF($nodes)); + + // Returning false to break the event chain + return false; + + } + + /** + * Merges all vcard objects, and builds one big vcf export + * + * @param array $nodes + * @return string + */ + public function generateVCF(array $nodes) { + $objects = array(); + + foreach($nodes as $node) { + + if (!isset($node[200]['{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}address-data'])) { + continue; + } + $nodeData = $node[200]['{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}address-data']; + $objects[] = $nodeData; + + } + + return implode("\r\n", $objects); + + } + +} diff --git a/apps/contacts/lib/search.php b/apps/contacts/lib/search.php index 144138a7c2c..53aa2b48496 100644 --- a/apps/contacts/lib/search.php +++ b/apps/contacts/lib/search.php @@ -2,24 +2,17 @@ class OC_Search_Provider_Contacts extends OC_Search_Provider{ function search($query){ $addressbooks = OC_Contacts_Addressbook::all(OCP\USER::getUser(), 1); -// if(count($calendars)==0 || !OCP\App::isEnabled('contacts')){ -// //return false; -// } - // NOTE: Does the following do anything - $results=array(); - $searchquery=array(); - if(substr_count($query, ' ') > 0){ - $searchquery = explode(' ', $query); - }else{ - $searchquery[] = $query; + if(count($addressbooks)==0 || !OCP\App::isEnabled('contacts')) { + return array(); } + $results=array(); $l = new OC_l10n('contacts'); foreach($addressbooks as $addressbook){ $vcards = OC_Contacts_VCard::all($addressbook['id']); foreach($vcards as $vcard){ - if(substr_count(strtolower($vcard['fullname']), strtolower($query)) > 0){ - $link = OCP\Util::linkTo('contacts', 'index.php').'?id='.urlencode($vcard['id']); - $results[]=new OC_Search_Result($vcard['fullname'],'', $link,$l->t('Contact'));//$name,$text,$link,$type + if(substr_count(strtolower($vcard['fullname']), strtolower($query)) > 0) { + $link = OCP\Util::linkTo('contacts', 'index.php').'&id='.urlencode($vcard['id']); + $results[]=new OC_Search_Result($vcard['fullname'], '', $link, (string)$l->t('Contact'));//$name,$text,$link,$type } } } diff --git a/apps/contacts/lib/share/addressbook.php b/apps/contacts/lib/share/addressbook.php new file mode 100644 index 00000000000..d35f0ce9aea --- /dev/null +++ b/apps/contacts/lib/share/addressbook.php @@ -0,0 +1,94 @@ +<?php +/** + * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_Share_Backend_Addressbook implements OCP\Share_Backend_Collection { + const FORMAT_ADDRESSBOOKS = 1; + + /** + * @brief Get the source of the item to be stored in the database + * @param string Item + * @param string Owner of the item + * @return mixed|array|false Source + * + * Return an array if the item is file dependent, the array needs two keys: 'item' and 'file' + * Return false if the item does not exist for the user + * + * The formatItems() function will translate the source returned back into the item + */ + public function isValidSource($itemSource, $uidOwner) { + $addressbook = OC_Contacts_Addressbook::find( $itemSource ); + if( $addressbook === false || $addressbook['userid'] != $uidOwner) { + return false; + } + return true; + } + + /** + * @brief Get a unique name of the item for the specified user + * @param string Item + * @param string|false User the item is being shared with + * @param array|null List of similar item names already existing as shared items + * @return string Target name + * + * This function needs to verify that the user does not already have an item with this name. + * If it does generate a new name e.g. name_# + */ + public function generateTarget($itemSource, $shareWith, $exclude = null) { + $addressbook = OC_Contacts_Addressbook::find( $itemSource ); + $user_addressbooks = array(); + foreach(OC_Contacts_Addressbook::all($shareWith) as $user_addressbook) { + $user_addressbooks[] = $user_addressbook['displayname']; + } + $name = $addressbook['userid']."'s ".$addressbook['displayname']; + $suffix = ''; + while (in_array($name.$suffix, $user_addressbooks)) { + $suffix++; + } + + return $name.$suffix; + } + + /** + * @brief Converts the shared item sources back into the item in the specified format + * @param array Shared items + * @param int Format + * @return ? + * + * The items array is a 3-dimensional array with the item_source as the first key and the share id as the second key to an array with the share info. + * The key/value pairs included in the share info depend on the function originally called: + * If called by getItem(s)Shared: id, item_type, item, item_source, share_type, share_with, permissions, stime, file_source + * If called by getItem(s)SharedWith: id, item_type, item, item_source, item_target, share_type, share_with, permissions, stime, file_source, file_target + * This function allows the backend to control the output of shared items with custom formats. + * It is only called through calls to the public getItem(s)Shared(With) functions. + */ + public function formatItems($items, $format, $parameters = null) { + $addressbooks = array(); + if ($format == self::FORMAT_ADDRESSBOOKS) { + foreach ($items as $item) { + $addressbook = OC_Contacts_Addressbook::find($item['item_source']); + if ($addressbook) { + $addressbook['displayname'] = $item['item_target']; + $addressbook['permissions'] = $item['permissions']; + $addressbooks[] = $addressbook; + } + } + } + return $addressbooks; + } + + public function getChildren($itemSource) { + $query = OCP\DB::prepare('SELECT id FROM *PREFIX*contacts_cards WHERE addressbookid = ?'); + $result = $query->execute(array($itemSource)); + $sources = array(); + while ($contact = $result->fetchRow()) { + $sources[] = $contact['id']; + } + return $sources; + } + +} diff --git a/apps/contacts/lib/share/contact.php b/apps/contacts/lib/share/contact.php new file mode 100644 index 00000000000..718c8f9025f --- /dev/null +++ b/apps/contacts/lib/share/contact.php @@ -0,0 +1,53 @@ +<?php +/** +* ownCloud +* +* @author Michael Gapczynski +* @copyright 2012 Michael Gapczynski mtgap@owncloud.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +class OC_Share_Backend_Contact implements OCP\Share_Backend { + + const FORMAT_CONTACT = 0; + + private static $contact; + + public function isValidSource($itemSource, $uidOwner) { + self::$contact = OC_Contacts_VCard::find($itemSource); + if (self::$contact) { + return true; + } + return false; + } + + public function generateTarget($itemSource, $shareWith, $exclude = null) { + // TODO Get default addressbook and check for conflicts + return self::$contact['fullname']; + } + + public function formatItems($items, $format, $parameters = null) { + $contacts = array(); + if ($format == self::FORMAT_CONTACT) { + foreach ($items as $item) { + $contact = OC_Contacts_VCard::find($item['item_source']); + $contact['fullname'] = $item['item_target']; + $contacts[] = $contact; + } + } + return $contacts; + } + +}
\ No newline at end of file diff --git a/apps/contacts/lib/vcard.php b/apps/contacts/lib/vcard.php index a0491c6323c..fbb1c734f64 100644 --- a/apps/contacts/lib/vcard.php +++ b/apps/contacts/lib/vcard.php @@ -38,45 +38,50 @@ /** * This class manages our vCards */ -class OC_Contacts_VCard{ +class OC_Contacts_VCard { /** * @brief Returns all cards of an address book * @param integer $id - * @return array + * @return array|false * * The cards are associative arrays. You'll find the original vCard in * ['carddata'] */ - public static function all($id){ + public static function all($id, $start=null, $num=null){ + //FIXME jfd: use limit & offset as OC_DB::prepare parameters for oracle support + $limitsql = ''; + if(!is_null($num)) { + $limitsql = ' LIMIT '.$num; + } + if(!is_null($start) && !is_null($num)) { + $limitsql .= ' OFFSET '.$start.' '; + } $result = null; - if(is_array($id) && count($id) > 1) { + if(is_array($id) && count($id)) { $id_sql = join(',', array_fill(0, count($id), '?')); - $prep = 'SELECT * FROM `*PREFIX*contacts_cards` WHERE `addressbookid` IN ('.$id_sql.') ORDER BY `fullname`'; + $prep = 'SELECT * FROM `*PREFIX*contacts_cards` WHERE `addressbookid` IN ('.$id_sql.') ORDER BY `fullname`'.$limitsql; try { $stmt = OCP\DB::prepare( $prep ); $result = $stmt->execute($id); } catch(Exception $e) { - OCP\Util::writeLog('contacts','OC_Contacts_VCard:all:, exception: '.$e->getMessage(),OCP\Util::ERROR); - OCP\Util::writeLog('contacts','OC_Contacts_VCard:all, ids: '.count($id).' '.join(',', $id),OCP\Util::DEBUG); - OCP\Util::writeLog('contacts','SQL:'.$prep,OCP\Util::DEBUG); - } - } elseif($id) { - if(is_array($id)) { - if(count($id) == 0) { - return array(); - } - $id = $id[0]; + OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR); + OCP\Util::writeLog('contacts', __METHOD__.', ids: '.join(',', $id), OCP\Util::DEBUG); + OCP\Util::writeLog('contacts', __METHOD__.'SQL:'.$prep, OCP\Util::DEBUG); + return false; } + } elseif(is_int($id) || is_string($id)) { try { - $stmt = OCP\DB::prepare( 'SELECT * FROM `*PREFIX*contacts_cards` WHERE `addressbookid` = ? ORDER BY `fullname`' ); + $sql = 'SELECT * FROM `*PREFIX*contacts_cards` WHERE `addressbookid` = ? ORDER BY `fullname`'.$limitsql; + $stmt = OCP\DB::prepare( $sql ); $result = $stmt->execute(array($id)); } catch(Exception $e) { - OCP\Util::writeLog('contacts','OC_Contacts_VCard:all:, exception: '.$e->getMessage(),OCP\Util::ERROR); - OCP\Util::writeLog('contacts','OC_Contacts_VCard:all, id: '. $id,OCP\Util::DEBUG); + OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR); + OCP\Util::writeLog('contacts', __METHOD__.', ids: '. $id, OCP\Util::DEBUG); + return false; } } else { - OCP\Util::writeLog('contacts','OC_Contacts_VCard:all: No ID given.',OCP\Util::ERROR); - return array(); + OCP\Util::writeLog('contacts', __METHOD__.'. Addressbook id(s) argument is empty: '. print_r($id, true), OCP\Util::DEBUG); + return false; } $cards = array(); if(!is_null($result)) { @@ -91,11 +96,17 @@ class OC_Contacts_VCard{ /** * @brief Returns a card * @param integer $id - * @return associative array + * @return associative array or false. */ public static function find($id){ - $stmt = OCP\DB::prepare( 'SELECT * FROM `*PREFIX*contacts_cards` WHERE `id` = ?' ); - $result = $stmt->execute(array($id)); + try { + $stmt = OCP\DB::prepare( 'SELECT * FROM `*PREFIX*contacts_cards` WHERE `id` = ?' ); + $result = $stmt->execute(array($id)); + } catch(Exception $e) { + OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR); + OCP\Util::writeLog('contacts', __METHOD__.', id: '. $id, OCP\Util::DEBUG); + return false; + } return $result->fetchRow(); } @@ -104,11 +115,17 @@ class OC_Contacts_VCard{ * @brief finds a card by its DAV Data * @param integer $aid Addressbook id * @param string $uri the uri ('filename') - * @return associative array + * @return associative array or false. */ public static function findWhereDAVDataIs($aid,$uri){ - $stmt = OCP\DB::prepare( 'SELECT * FROM `*PREFIX*contacts_cards` WHERE `addressbookid` = ? AND `uri` = ?' ); - $result = $stmt->execute(array($aid,$uri)); + try { + $stmt = OCP\DB::prepare( 'SELECT * FROM `*PREFIX*contacts_cards` WHERE `addressbookid` = ? AND `uri` = ?' ); + $result = $stmt->execute(array($aid,$uri)); + } catch(Exception $e) { + OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR); + OCP\Util::writeLog('contacts', __METHOD__.', aid: '.$aid.' uri'.$uri, OCP\Util::DEBUG); + return false; + } return $result->fetchRow(); } @@ -158,13 +175,19 @@ class OC_Contacts_VCard{ protected static function trueUID($aid, &$uid) { $stmt = OCP\DB::prepare( 'SELECT * FROM `*PREFIX*contacts_cards` WHERE `addressbookid` = ? AND `uri` = ?' ); $uri = $uid.'.vcf'; - $result = $stmt->execute(array($aid,$uri)); - if($result->numRows() > 0){ + try { + $result = $stmt->execute(array($aid,$uri)); + } catch(Exception $e) { + OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR); + OCP\Util::writeLog('contacts', __METHOD__.', aid: '.$aid.' uid'.$uid, OCP\Util::DEBUG); + return false; + } + if($result->numRows() > 0) { while(true) { - $tmpuid = substr(md5(rand().time()),0,10); + $tmpuid = substr(md5(rand().time()), 0, 10); $uri = $tmpuid.'.vcf'; - $result = $stmt->execute(array($aid,$uri)); - if($result->numRows() > 0){ + $result = $stmt->execute(array($aid, $uri)); + if($result->numRows() > 0) { continue; } else { $uid = $tmpuid; @@ -190,7 +213,7 @@ class OC_Contacts_VCard{ // Add version if needed if($version && $version < '3.0') { $upgrade = true; - OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateValuesFromAdd. Updating from version: '.$version,OCP\Util::DEBUG); + OCP\Util::writeLog('contacts', 'OC_Contacts_VCard::updateValuesFromAdd. Updating from version: '.$version, OCP\Util::DEBUG); } foreach($vcard->children as &$property){ // Decode string properties and remove obsolete properties. @@ -203,29 +226,29 @@ class OC_Contacts_VCard{ } // Fix format of type parameters. if($upgrade && in_array($property->name, $typeprops)) { - OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateValuesFromAdd. before: '.$property->serialize(),OCP\Util::DEBUG); + OCP\Util::writeLog('contacts', 'OC_Contacts_VCard::updateValuesFromAdd. before: '.$property->serialize(), OCP\Util::DEBUG); self::formatPropertyTypes($property); - OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateValuesFromAdd. after: '.$property->serialize(),OCP\Util::DEBUG); + OCP\Util::writeLog('contacts', 'OC_Contacts_VCard::updateValuesFromAdd. after: '.$property->serialize(), OCP\Util::DEBUG); } - if($property->name == 'FN'){ + if($property->name == 'FN') { $fn = $property->value; } - if($property->name == 'N'){ + if($property->name == 'N') { $n = $property->value; } - if($property->name == 'UID'){ + if($property->name == 'UID') { $uid = $property->value; } - if($property->name == 'ORG'){ + if($property->name == 'ORG') { $org = $property->value; } - if($property->name == 'EMAIL' && is_null($email)){ // only use the first email as substitute for missing N or FN. + if($property->name == 'EMAIL' && is_null($email)) { // only use the first email as substitute for missing N or FN. $email = $property->value; } } // Check for missing 'N', 'FN' and 'UID' properties if(!$fn) { - if($n && $n != ';;;;'){ + if($n && $n != ';;;;') { $fn = join(' ', array_reverse(array_slice(explode(';', $n), 0, 2))); } elseif($email) { $fn = $email; @@ -235,21 +258,21 @@ class OC_Contacts_VCard{ $fn = 'Unknown Name'; } $vcard->setString('FN', $fn); - OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateValuesFromAdd. Added missing \'FN\' field: '.$fn,OCP\Util::DEBUG); + OCP\Util::writeLog('contacts', 'OC_Contacts_VCard::updateValuesFromAdd. Added missing \'FN\' field: '.$fn, OCP\Util::DEBUG); } - if(!$n || $n == ';;;;'){ // Fix missing 'N' field. Ugly hack ahead ;-) + if(!$n || $n == ';;;;') { // Fix missing 'N' field. Ugly hack ahead ;-) $slice = array_reverse(array_slice(explode(' ', $fn), 0, 2)); // Take 2 first name parts of 'FN' and reverse. if(count($slice) < 2) { // If not enought, add one more... $slice[] = ""; } $n = implode(';', $slice).';;;'; $vcard->setString('N', $n); - OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateValuesFromAdd. Added missing \'N\' field: '.$n,OCP\Util::DEBUG); + OCP\Util::writeLog('contacts', 'OC_Contacts_VCard::updateValuesFromAdd. Added missing \'N\' field: '.$n, OCP\Util::DEBUG); } if(!$uid) { $vcard->setUID(); $uid = $vcard->getAsString('UID'); - OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateValuesFromAdd. Added missing \'UID\' field: '.$uid,OCP\Util::DEBUG); + OCP\Util::writeLog('contacts', 'OC_Contacts_VCard::updateValuesFromAdd. Added missing \'UID\' field: '.$uid, OCP\Util::DEBUG); } if(self::trueUID($aid, $uid)) { $vcard->setString('UID', $uid); @@ -260,23 +283,29 @@ class OC_Contacts_VCard{ /** * @brief Adds a card - * @param integer $aid Addressbook id - * @param OC_VObject $card vCard file - * @param string $uri the uri of the card, default based on the UID - * @return insertid on success or null if no card. + * @param $aid integer Addressbook id + * @param $card OC_VObject vCard file + * @param $uri string the uri of the card, default based on the UID + * @param $isChecked boolean If the vCard should be checked for validity and version. + * @return insertid on success or false. */ - public static function add($aid, OC_VObject $card, $uri=null, $isnew=false){ - if(is_null($card)){ - OCP\Util::writeLog('contacts','OC_Contacts_VCard::add. No vCard supplied', OCP\Util::ERROR); + public static function add($aid, OC_VObject $card, $uri=null, $isChecked=false){ + if(is_null($card)) { + OCP\Util::writeLog('contacts', 'OC_Contacts_VCard::add. No vCard supplied', OCP\Util::ERROR); return null; }; - - if(!$isnew) { + $addressbook = OC_Contacts_Addressbook::find($aid); + if ($addressbook['userid'] != OCP\User::getUser()) { + $sharedAddressbook = OCP\Share::getItemSharedWithBySource('addressbook', $aid); + if (!$sharedAddressbook || !($sharedAddressbook['permissions'] & OCP\Share::PERMISSION_CREATE)) { + return false; + } + } + if(!$isChecked) { OC_Contacts_App::loadCategoriesFromVCard($card); self::updateValuesFromAdd($aid, $card); } - - $card->setString('VERSION','3.0'); + $card->setString('VERSION', '3.0'); // Add product ID is missing. $prodid = trim($card->getAsString('PRODID')); if(!$prodid) { @@ -298,11 +327,17 @@ class OC_Contacts_VCard{ $data = $card->serialize(); $stmt = OCP\DB::prepare( 'INSERT INTO `*PREFIX*contacts_cards` (`addressbookid`,`fullname`,`carddata`,`uri`,`lastmodified`) VALUES(?,?,?,?,?)' ); - $result = $stmt->execute(array($aid,$fn,$data,$uri,time())); + try { + $result = $stmt->execute(array($aid,$fn,$data,$uri,time())); + } catch(Exception $e) { + OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR); + OCP\Util::writeLog('contacts', __METHOD__.', aid: '.$aid.' uri'.$uri, OCP\Util::DEBUG); + return false; + } $newid = OCP\DB::insertid('*PREFIX*contacts_cards'); OC_Contacts_Addressbook::touch($aid); - + OC_Hook::emit('OC_Contacts_VCard', 'post_createVCard', $newid); return $newid; } @@ -327,15 +362,26 @@ class OC_Contacts_VCard{ $now = new DateTime; foreach($objects as $object) { $vcard = OC_VObject::parse($object[1]); - if(!is_null($vcard)){ + if(!is_null($vcard)) { + $oldcard = self::find($object[0]); + if (!$oldcard) { + return false; + } + $addressbook = OC_Contacts_Addressbook::find($oldcard['addressbookid']); + if ($addressbook['userid'] != OCP\User::getUser()) { + $sharedContact = OCP\Share::getItemSharedWithBySource('contact', $object[0], OCP\Share::FORMAT_NONE, null, true); + if (!$sharedContact || !($sharedContact['permissions'] & OCP\Share::PERMISSION_UPDATE)) { + return false; + } + } $vcard->setString('REV', $now->format(DateTime::W3C)); $data = $vcard->serialize(); try { $result = $stmt->execute(array($data,time(),$object[0])); //OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateDataByID, id: '.$object[0].': '.$object[1],OCP\Util::DEBUG); } catch(Exception $e) { - OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateDataByID:, exception: '.$e->getMessage(),OCP\Util::DEBUG); - OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateDataByID, id: '.$object[0],OCP\Util::DEBUG); + OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR); + OCP\Util::writeLog('contacts', __METHOD__.', id: '.$object[0], OCP\Util::DEBUG); } } } @@ -349,11 +395,20 @@ class OC_Contacts_VCard{ */ public static function edit($id, OC_VObject $card){ $oldcard = self::find($id); - + if (!$oldcard) { + return false; + } if(is_null($card)) { return false; } - + // NOTE: Owner checks are being made in the ajax files, which should be done inside the lib files to prevent any redundancies with sharing checks + $addressbook = OC_Contacts_Addressbook::find($oldcard['addressbookid']); + if ($addressbook['userid'] != OCP\User::getUser()) { + $sharedContact = OCP\Share::getItemSharedWithBySource('contact', $id, OCP\Share::FORMAT_NONE, null, true); + if (!$sharedContact || !($sharedContact['permissions'] & OCP\Share::PERMISSION_UPDATE)) { + throw new Exception(OC_Contacts_App::$l10n->t('You do not have the permissions to edit this contact.')); + } + } OC_Contacts_App::loadCategoriesFromVCard($card); $fn = $card->getAsString('FN'); @@ -366,10 +421,17 @@ class OC_Contacts_VCard{ $data = $card->serialize(); $stmt = OCP\DB::prepare( 'UPDATE `*PREFIX*contacts_cards` SET `fullname` = ?,`carddata` = ?, `lastmodified` = ? WHERE `id` = ?' ); - $result = $stmt->execute(array($fn,$data,time(),$id)); + try { + $result = $stmt->execute(array($fn,$data,time(),$id)); + } catch(Exception $e) { + OCP\Util::writeLog('contacts', __METHOD__.', exception: ' + . $e->getMessage(), OCP\Util::ERROR); + OCP\Util::writeLog('contacts', __METHOD__.', id'.$id, OCP\Util::DEBUG); + return false; + } OC_Contacts_Addressbook::touch($oldcard['addressbookid']); - + OC_Hook::emit('OC_Contacts_VCard', 'post_updateVCard', $id); return true; } @@ -380,14 +442,25 @@ class OC_Contacts_VCard{ * @param string $data vCard file * @return boolean */ - public static function editFromDAVData($aid,$uri,$data){ - $oldcard = self::findWhereDAVDataIs($aid,$uri); + public static function editFromDAVData($aid, $uri, $data){ + $oldcard = self::findWhereDAVDataIs($aid, $uri); $card = OC_VObject::parse($data); if(!$card) { - OCP\Util::writeLog('contacts','OC_Contacts_VCard::editFromDAVData. Unable to parse VCARD, uri: '.$uri,OCP\Util::ERROR); + OCP\Util::writeLog('contacts', __METHOD__. + ', Unable to parse VCARD, uri: '.$uri, OCP\Util::ERROR); + return false; + } + try { + self::edit($oldcard['id'], $card); + return true; + } catch(Exception $e) { + OCP\Util::writeLog('contacts', __METHOD__.', exception: ' + . $e->getMessage() . ', ' + . OCP\USER::getUser(), OCP\Util::ERROR); + OCP\Util::writeLog('contacts', __METHOD__.', uri' + . $uri, OCP\Util::DEBUG); return false; } - return self::edit($oldcard['id'], $card); } /** @@ -396,9 +469,36 @@ class OC_Contacts_VCard{ * @return boolean */ public static function delete($id){ - // FIXME: Add error checking. - $stmt = OCP\DB::prepare( 'DELETE FROM `*PREFIX*contacts_cards` WHERE `id` = ?' ); - $stmt->execute(array($id)); + $card = self::find($id); + if (!$card) { + return false; + } + $addressbook = OC_Contacts_Addressbook::find($card['addressbookid']); + if ($addressbook['userid'] != OCP\User::getUser()) { + $sharedContact = OCP\Share::getItemSharedWithBySource('contact', + $id, OCP\Share::FORMAT_NONE, null, true); + if (!$sharedContact + || !($sharedContact['permissions'] & OCP\Share::PERMISSION_DELETE)) { + throw new Exception( + OC_Contacts_App::$l10n->t( + 'You do not have the permissions to delete this contact.' + ) + ); + } + } + OC_Hook::emit('OC_Contacts_VCard', 'pre_deleteVCard', + array('aid' => null, 'id' => $id, 'uri' => null) + ); + $stmt = OCP\DB::prepare('DELETE FROM `*PREFIX*contacts_cards` WHERE `id` = ?'); + try { + $stmt->execute(array($id)); + } catch(Exception $e) { + OCP\Util::writeLog('contacts', __METHOD__. + ', exception: ' . $e->getMessage(), OCP\Util::ERROR); + OCP\Util::writeLog('contacts', __METHOD__.', id: ' + . $id, OCP\Util::DEBUG); + return false; + } return true; } @@ -410,9 +510,27 @@ class OC_Contacts_VCard{ * @return boolean */ public static function deleteFromDAVData($aid,$uri){ - // FIXME: Add error checking. Deleting a card gives an Kontact/Akonadi error. + $addressbook = OC_Contacts_Addressbook::find($aid); + if ($addressbook['userid'] != OCP\User::getUser()) { + $query = OCP\DB::prepare( 'SELECT id FROM *PREFIX*contacts_cards WHERE addressbookid = ? AND uri = ?' ); + $id = $query->execute(array($aid, $uri))->fetchOne(); + if (!$id) { + return false; + } + $sharedContact = OCP\Share::getItemSharedWithBySource('contact', $id, OCP\Share::FORMAT_NONE, null, true); + if (!$sharedContact || !($sharedContact['permissions'] & OCP\Share::PERMISSION_DELETE)) { + return false; + } + } + OC_Hook::emit('OC_Contacts_VCard', 'pre_deleteVCard', array('aid' => $aid, 'id' => null, 'uri' => $uri)); $stmt = OCP\DB::prepare( 'DELETE FROM `*PREFIX*contacts_cards` WHERE `addressbookid` = ? AND `uri`=?' ); - $stmt->execute(array($aid,$uri)); + try { + $stmt->execute(array($aid,$uri)); + } catch(Exception $e) { + OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR); + OCP\Util::writeLog('contacts', __METHOD__.', aid: '.$aid.' uri: '.$uri, OCP\Util::DEBUG); + return false; + } OC_Contacts_Addressbook::touch($aid); return true; @@ -439,14 +557,14 @@ class OC_Contacts_VCard{ * @return array */ public static function unescapeDelimiters($value, $delimiter=';') { - $array = explode($delimiter,$value); + $array = explode($delimiter, $value); for($i=0;$i<count($array);$i++) { - if(substr($array[$i],-1,1)=="\\") { + if(substr($array[$i], -1, 1)=="\\") { if(isset($array[$i+1])) { - $array[$i] = substr($array[$i],0,count($array[$i])-2).$delimiter.$array[$i+1]; + $array[$i] = substr($array[$i], 0, count($array[$i])-2).$delimiter.$array[$i+1]; unset($array[$i+1]); } else { - $array[$i] = substr($array[$i],0,count($array[$i])-2).$delimiter; + $array[$i] = substr($array[$i], 0, count($array[$i])-2).$delimiter; } $i = $i - 1; } @@ -462,16 +580,25 @@ class OC_Contacts_VCard{ * * look at code ... */ - public static function structureContact($object){ + public static function structureContact($object) { $details = array(); - foreach($object->children as $property){ + + foreach($object->children as $property) { + $pname = $property->name; $temp = self::structureProperty($property); if(!is_null($temp)) { - if(array_key_exists($property->name,$details)){ - $details[$property->name][] = $temp; + // Get Apple X-ABLabels + if(isset($object->{$property->group . '.X-ABLABEL'})) { + $temp['label'] = $object->{$property->group . '.X-ABLABEL'}->value; + if($temp['label'] == '_$!<Other>!$_') { + $temp['label'] = OC_Contacts_App::$l10n->t('Other'); + } + } + if(array_key_exists($pname, $details)) { + $details[$pname][] = $temp; } else{ - $details[$property->name] = array($temp); + $details[$pname] = array($temp); } } } @@ -491,10 +618,10 @@ class OC_Contacts_VCard{ * NOTE: $value is not escaped anymore. It shouldn't make any difference * but we should look out for any problems. */ - public static function structureProperty($property){ + public static function structureProperty($property) { $value = $property->value; //$value = htmlspecialchars($value); - if($property->name == 'ADR' || $property->name == 'N'){ + if($property->name == 'ADR' || $property->name == 'N') { $value = self::unescapeDelimiters($value); } elseif($property->name == 'BDAY') { if(strpos($value, '-') === false) { @@ -505,6 +632,9 @@ class OC_Contacts_VCard{ } } } + if(is_string($value)) { + $value = strtr($value, array('\,' => ',', '\;' => ';')); + } $temp = array( 'name' => $property->name, 'value' => $value, @@ -514,17 +644,17 @@ class OC_Contacts_VCard{ // Faulty entries by kaddressbook // Actually TYPE=PREF is correct according to RFC 2426 // but this way is more handy in the UI. Tanghus. - if($parameter->name == 'TYPE' && $parameter->value == 'PREF'){ + if($parameter->name == 'TYPE' && strtoupper($parameter->value) == 'PREF') { $parameter->name = 'PREF'; $parameter->value = '1'; } // NOTE: Apparently Sabre_VObject_Reader can't always deal with value list parameters // like TYPE=HOME,CELL,VOICE. Tanghus. - if (in_array($property->name, array('TEL', 'EMAIL')) && $parameter->name == 'TYPE'){ - if (isset($temp['parameters'][$parameter->name])){ + if (in_array($property->name, array('TEL', 'EMAIL')) && $parameter->name == 'TYPE') { + if (isset($temp['parameters'][$parameter->name])) { $temp['parameters'][$parameter->name][] = $parameter->value; } - else{ + else { $temp['parameters'][$parameter->name] = array($parameter->value); } } @@ -542,9 +672,29 @@ class OC_Contacts_VCard{ * @return boolean * */ - public static function moveToAddressBook($aid, $id){ + public static function moveToAddressBook($aid, $id, $isAddressbook = false) { OC_Contacts_App::getAddressbook($aid); // check for user ownership. + $addressbook = OC_Contacts_Addressbook::find($aid); + if ($addressbook['userid'] != OCP\User::getUser()) { + $sharedAddressbook = OCP\Share::getItemSharedWithBySource('addressbook', $aid); + if (!$sharedAddressbook || !($sharedAddressbook['permissions'] & OCP\Share::PERMISSION_CREATE)) { + return false; + } + } if(is_array($id)) { + foreach ($id as $index => $cardId) { + $card = self::find($cardId); + if (!$card) { + unset($id[$index]); + } + $oldAddressbook = OC_Contacts_Addressbook::find($card['addressbookid']); + if ($oldAddressbook['userid'] != OCP\User::getUser()) { + $sharedContact = OCP\Share::getItemSharedWithBySource('contact', $cardId, OCP\Share::FORMAT_NONE, null, true); + if (!$sharedContact || !($sharedContact['permissions'] & OCP\Share::PERMISSION_DELETE)) { + unset($id[$index]); + } + } + } $id_sql = join(',', array_fill(0, count($id), '?')); $prep = 'UPDATE `*PREFIX*contacts_cards` SET `addressbookid` = ? WHERE `id` IN ('.$id_sql.')'; try { @@ -553,24 +703,39 @@ class OC_Contacts_VCard{ $vals = array_merge((array)$aid, $id); $result = $stmt->execute($vals); } catch(Exception $e) { - OCP\Util::writeLog('contacts','OC_Contacts_VCard::moveToAddressBook:, exception: '.$e->getMessage(),OCP\Util::DEBUG); - OCP\Util::writeLog('contacts','OC_Contacts_VCard::moveToAddressBook, ids: '.join(',', $vals),OCP\Util::DEBUG); - OCP\Util::writeLog('contacts','SQL:'.$prep,OCP\Util::DEBUG); + OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), OCP\Util::ERROR); + OCP\Util::writeLog('contacts', __METHOD__.', ids: '.join(',', $vals), OCP\Util::DEBUG); + OCP\Util::writeLog('contacts', __METHOD__.', SQL:'.$prep, OCP\Util::DEBUG); return false; } } else { - try { + $stmt = null; + if($isAddressbook) { + $stmt = OCP\DB::prepare( 'UPDATE `*PREFIX*contacts_cards` SET `addressbookid` = ? WHERE `addressbookid` = ?' ); + } else { + $card = self::find($id); + if (!$card) { + return false; + } + $oldAddressbook = OC_Contacts_Addressbook::find($card['addressbookid']); + if ($oldAddressbook['userid'] != OCP\User::getUser()) { + $sharedContact = OCP\Share::getItemSharedWithBySource('contact', $id, OCP\Share::FORMAT_NONE, null, true); + if (!$sharedContact || !($sharedContact['permissions'] & OCP\Share::PERMISSION_DELETE)) { + return false; + } + } $stmt = OCP\DB::prepare( 'UPDATE `*PREFIX*contacts_cards` SET `addressbookid` = ? WHERE `id` = ?' ); + } + try { $result = $stmt->execute(array($aid, $id)); } catch(Exception $e) { - OCP\Util::writeLog('contacts','OC_Contacts_VCard::moveToAddressBook:, exception: '.$e->getMessage(),OCP\Util::DEBUG); - OCP\Util::writeLog('contacts','OC_Contacts_VCard::moveToAddressBook, id: '.$id,OCP\Util::DEBUG); + OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), OCP\Util::DEBUG); + OCP\Util::writeLog('contacts', __METHOD__.' id: '.$id, OCP\Util::DEBUG); return false; } } - + OC_Hook::emit('OC_Contacts_VCard', 'post_moveToAddressbook', array('aid' => $aid, 'id' => $id)); OC_Contacts_Addressbook::touch($aid); return true; } - } diff --git a/apps/contacts/photo.php b/apps/contacts/photo.php index 4660d61f618..f5c8e6fe4bd 100644 --- a/apps/contacts/photo.php +++ b/apps/contacts/photo.php @@ -13,51 +13,63 @@ OCP\User::checkLoggedIn(); OCP\App::checkAppEnabled('contacts'); -function getStandardImage(){ +function getStandardImage() { //OCP\Response::setExpiresHeader('P10D'); OCP\Response::enableCaching(); OCP\Response::redirect(OCP\Util::imagePath('contacts', 'person_large.png')); } $id = isset($_GET['id']) ? $_GET['id'] : null; -$caching = isset($_GET['refresh']) ? 0 : null; +$etag = null; +$caching = null; if(is_null($id)) { getStandardImage(); } if(!extension_loaded('gd') || !function_exists('gd_info')) { - OCP\Util::writeLog('contacts','photo.php. GD module not installed',OCP\Util::DEBUG); + OCP\Util::writeLog('contacts', + 'photo.php. GD module not installed', OCP\Util::DEBUG); getStandardImage(); } $contact = OC_Contacts_App::getContactVCard($id); $image = new OC_Image(); -if(!$image) { +if (!$image) { getStandardImage(); } // invalid vcard -if( is_null($contact)) { - OCP\Util::writeLog('contacts','photo.php. The VCard for ID '.$id.' is not RFC compatible',OCP\Util::ERROR); +if (is_null($contact)) { + OCP\Util::writeLog('contacts', + 'photo.php. The VCard for ID ' . $id . ' is not RFC compatible', + OCP\Util::ERROR); } else { - OCP\Response::enableCaching($caching); - OC_Contacts_App::setLastModifiedHeader($contact); - // Photo :-) - if($image->loadFromBase64($contact->getAsString('PHOTO'))) { + if ($image->loadFromBase64($contact->getAsString('PHOTO'))) { // OK - OCP\Response::setETagHeader(md5($contact->getAsString('PHOTO'))); + $etag = md5($contact->getAsString('PHOTO')); } else // Logo :-/ - if($image->loadFromBase64($contact->getAsString('LOGO'))) { + if ($image->loadFromBase64($contact->getAsString('LOGO'))) { // OK - OCP\Response::setETagHeader(md5($contact->getAsString('LOGO'))); + $etag = md5($contact->getAsString('LOGO')); } if ($image->valid()) { + $modified = OC_Contacts_App::lastModified($contact); + // Force refresh if modified within the last minute. + if(!is_null($modified)) { + $caching = (time() - $modified->format('U') > 60) ? null : 0; + } + OCP\Response::enableCaching($caching); + if(!is_null($modified)) { + OCP\Response::setLastModifiedHeader($modified); + } + if($etag) { + OCP\Response::setETagHeader($etag); + } $max_size = 200; - if($image->width() > $max_size || - $image->height() > $max_size) { + if ($image->width() > $max_size || $image->height() > $max_size) { $image->resize($max_size); } } @@ -65,8 +77,7 @@ if( is_null($contact)) { if (!$image->valid()) { // Not found :-( getStandardImage(); - //$image->loadFromFile('img/person_large.png'); } header('Content-Type: '.$image->mimeType()); $image->show(); -//echo OC_Contacts_App::$l10n->t('This card does not contain a photo.'); + diff --git a/apps/contacts/settings.php b/apps/contacts/settings.php index c88fed0b4d6..5f639399c95 100644 --- a/apps/contacts/settings.php +++ b/apps/contacts/settings.php @@ -1,6 +1,6 @@ <?php $tmpl = new OCP\Template( 'contacts', 'settings'); +$tmpl->assign('addressbooks', OC_Contacts_Addressbook::all(OCP\USER::getUser()), false); -return $tmpl->fetchPage(); -?> +$tmpl->printPage(); diff --git a/apps/contacts/templates/index.php b/apps/contacts/templates/index.php index 7d212e71ba8..744381026ab 100644 --- a/apps/contacts/templates/index.php +++ b/apps/contacts/templates/index.php @@ -1,29 +1,74 @@ +<div id='notification'></div> <script type='text/javascript'> var totalurl = '<?php echo OCP\Util::linkToRemote('carddav'); ?>addressbooks'; var categories = <?php echo json_encode($_['categories']); ?>; + var id = '<?php echo $_['id']; ?>'; var lang = '<?php echo OCP\Config::getUserValue(OCP\USER::getUser(), 'core', 'lang', 'en'); ?>'; </script> -<div id="leftcontent" class="leftcontent"> - <ul id="contacts"> - <?php echo $this->inc("part.contacts"); ?> - </ul> -</div> -<div id="bottomcontrols"> - <form> - <button class="svg" id="contacts_newcontact" title="<?php echo $l->t('Add Contact'); ?>"><img class="svg" src="<?php echo OCP\Util::linkTo('contacts', 'img/contact-new.svg'); ?>" alt="<?php echo $l->t('Add Contact'); ?>" /></button> - <button class="svg" id="chooseaddressbook" title="<?php echo $l->t('Addressbooks'); ?>"><img class="svg" src="core/img/actions/settings.svg" alt="<?php echo $l->t('Addressbooks'); ?>" /></button> - </form> +<div id="leftcontent"> + <div class="hidden" id="statusbar"></div> + <div id="contacts"> + </div> + <div id="uploadprogressbar"></div> + <div id="bottomcontrols"> + <button class="svg" id="contacts_newcontact" title="<?php echo $l->t('Add Contact'); ?>"><img class="svg" src="<?php echo OCP\Util::imagePath('contacts', 'contact-new.svg'); ?>" alt="<?php echo $l->t('Add Contact'); ?>" /></button> + <button class="svg import tip" title="<?php echo $l->t('Import'); ?>"> + <img class="svg" src="<?php echo OCP\Util::imagePath('core', 'actions/upload.svg') ?>" alt="<?php echo $l->t('Import'); ?>" /> + </button> + <button class="svg settings tip" title="<?php echo $l->t('Settings'); ?>"><img class="svg" src="<?php echo OCP\Util::imagePath('core', 'actions/settings.svg') ?>" alt="<?php echo $l->t('Addressbooks'); ?>" /></button> + <form id="import_upload_form" action="<?php echo OCP\Util::linkTo('contacts', 'ajax/uploadimport.php'); ?>" method="post" enctype="multipart/form-data" target="import_upload_target"> + <input class="float" id="import_upload_start" type="file" accept="text/directory,text/vcard,text/x-vcard" name="importfile" /> + <input type="hidden" name="MAX_FILE_SIZE" value="<?php echo $_['uploadMaxFilesize'] ?>" id="max_upload"> + </form> + <iframe name="import_upload_target" id='import_upload_target' src=""></iframe> + </div> </div> <div id="rightcontent" class="rightcontent" data-id="<?php echo $_['id']; ?>"> <?php - if ($_['id']){ + if($_['has_contacts']) { echo $this->inc('part.contact'); } else{ echo $this->inc('part.no_contacts'); } ?> + <div class="hidden popup" id="ninjahelp"> + <a class="close" tabindex="0" role="button" title="<?php echo $l->t('Close'); ?>"></a> + <h2><?php echo $l->t('Keyboard shortcuts'); ?></h2> + <div class="help-section"> + <h3><?php echo $l->t('Navigation'); ?></h3> + <dl> + <dt>j/Down</dt> + <dd><?php echo $l->t('Next contact in list'); ?></dd> + <dt>k/Up</dt> + <dd><?php echo $l->t('Previous contact in list'); ?></dd> + <dt>o</dt> + <dd><?php echo $l->t('Expand/collapse current addressbook'); ?></dd> + <dt>n/PageDown</dt> + <dd><?php echo $l->t('Next addressbook'); ?></dd> + <dt>p/PageUp</dt> + <dd><?php echo $l->t('Previous addressbook'); ?></dd> + </dl> + </div> + <div class="help-section"> + <h3><?php echo $l->t('Actions'); ?></h3> + <dl> + <dt>r</dt> + <dd><?php echo $l->t('Refresh contacts list'); ?></dd> + <dt>a</dt> + <dd><?php echo $l->t('Add new contact'); ?></dd> + <!-- dt>Shift-a</dt> + <dd><?php echo $l->t('Add new addressbook'); ?></dd --> + <dt>Shift-Delete</dt> + <dd><?php echo $l->t('Delete current contact'); ?></dd> + </dl> + </div> + </div> </div> <!-- Dialogs --> <div id="dialog_holder"></div> <!-- End of Dialogs --> +<menu type="context" id="addressbookmenu"> + <menuitem label="Delete" icon="<?php echo OCP\Util::imagePath('core', 'actions/delete.svg') ?>" onclick="alert('Really? ' + $(this).attr('data-id'))"></menuitem> + <menuitem label="Rename" icon="<?php echo OCP\Util::imagePath('core', 'actions/rename.svg') ?>" onclick="alert('Can\'t do that')"></menuitem> +</menu> diff --git a/apps/contacts/templates/part.chooseaddressbook.php b/apps/contacts/templates/part.chooseaddressbook.php deleted file mode 100644 index a0ec053ab91..00000000000 --- a/apps/contacts/templates/part.chooseaddressbook.php +++ /dev/null @@ -1,25 +0,0 @@ -<div id="chooseaddressbook_dialog" title="<?php echo $l->t("Configure Address Books"); ?>"> -<table width="100%" style="border: 0;"> -<?php -$option_addressbooks = OC_Contacts_Addressbook::all(OCP\USER::getUser()); -for($i = 0; $i < count($option_addressbooks); $i++){ - echo "<tr>"; - $tmpl = new OCP\Template('contacts', 'part.chooseaddressbook.rowfields'); - $tmpl->assign('addressbook', $option_addressbooks[$i]); - $tmpl->assign('active', OC_Contacts_Addressbook::isActive($option_addressbooks[$i]['id'])); - $tmpl->printpage(); - echo "</tr>"; -} -?> -<tr> - <td colspan="5" style="padding: 0.5em;"> - <a class="button" href="#" onclick="Contacts.UI.Addressbooks.newAddressbook(this);"><?php echo $l->t('New Address Book') ?></a> - <a class="button" href="#" onclick="Contacts.UI.Addressbooks.importAddressbook(this);"><?php echo $l->t('Import from VCF') ?></a> - </td> -</tr> -<tr> - <td colspan="5"> - <p style="margin: 0 auto;width: 90%;"><input style="display:none;width: 90%;float: left;" type="text" id="carddav_url" onmouseover="$('#carddav_url').select();" title="<?php echo $l->t("CardDav Link"); ?>"><a class="action delete" id="carddav_url_close" style="height: 20px;vertical-align: middle;display: none;" title="close" onclick="$('#carddav_url').hide();$('#carddav_url_close').hide();"/></a></p> - </td> -</tr> -</table> diff --git a/apps/contacts/templates/part.chooseaddressbook.rowfields.php b/apps/contacts/templates/part.chooseaddressbook.rowfields.php deleted file mode 100644 index 780920ea3c2..00000000000 --- a/apps/contacts/templates/part.chooseaddressbook.rowfields.php +++ /dev/null @@ -1,18 +0,0 @@ -<td width="20px"> - <input id="active_<?php echo $_['addressbook']["id"]; ?>" type="checkbox" onClick="Contacts.UI.Addressbooks.activation(this, <?php echo $_['addressbook']["id"]; ?>)" <?php echo (OC_Contacts_Addressbook::isActive($_['addressbook']["id"]) ? ' checked="checked"' : ''); ?>> -</td> -<td> - <label for="active_<?php echo $_['addressbook']["id"]; ?>"><?php echo htmlspecialchars($_['addressbook']["displayname"]); ?></label> -</td> -<td width="20px"> - <a onclick="Contacts.UI.showCardDAVUrl('<?php echo OCP\USER::getUser(); ?>', '<?php echo rawurlencode($_['addressbook']["uri"]); ?>');" title="<?php echo $l->t("CardDav Link"); ?>" class="svg action globe"></a> -</td> -<td width="20px"> - <a href="<?php echo OCP\Util::linkTo('contacts', 'export.php'); ?>?bookid=<?php echo $_['addressbook']["id"]; ?>" title="<?php echo $l->t("Download"); ?>" class="svg action download"></a> -</td> -<td width="20px"> - <a title="<?php echo $l->t("Edit"); ?>" class="svg action edit" onclick="Contacts.UI.Addressbooks.editAddressbook(this, <?php echo $_['addressbook']["id"]; ?>);"></a> -</td> -<td width="20px"> - <a onclick="Contacts.UI.Addressbooks.deleteAddressbook(this, <?php echo $_['addressbook']["id"]; ?>);" title="<?php echo $l->t("Delete"); ?>" class="svg action delete"></a> -</td> diff --git a/apps/contacts/templates/part.contact.php b/apps/contacts/templates/part.contact.php index 5b07ba3c337..87ed07c0ec8 100644 --- a/apps/contacts/templates/part.contact.php +++ b/apps/contacts/templates/part.contact.php @@ -1,25 +1,34 @@ +<div id="appsettings" class="popup bottomleft hidden"></div> <?php $id = isset($_['id']) ? $_['id'] : ''; ?> <div id="card"> <form class="float" id="file_upload_form" action="<?php echo OCP\Util::linkTo('contacts', 'ajax/uploadphoto.php'); ?>" method="post" enctype="multipart/form-data" target="file_upload_target"> - <input type="hidden" name="id" value="<?php echo $_['id'] ?>"> <input type="hidden" name="requesttoken" value="<?php echo $_['requesttoken'] ?>"> + <input type="hidden" name="id" value="<?php echo $_['id'] ?>"> <input type="hidden" name="MAX_FILE_SIZE" value="<?php echo $_['uploadMaxFilesize'] ?>" id="max_upload"> <input type="hidden" class="max_human_file_size" value="(max <?php echo $_['uploadMaxHumanFilesize']; ?>)"> <input id="file_upload_start" type="file" accept="image/*" name="imagefile" /> </form> - <div id="contact_photo" class="contactsection"> + <div id="contact_photo"> <iframe name="file_upload_target" id='file_upload_target' src=""></iframe> <div class="tip propertycontainer" id="contacts_details_photo_wrapper" title="<?php echo $l->t('Drop photo to upload'); ?> (max <?php echo $_['uploadMaxHumanFilesize']; ?>)" data-element="PHOTO"> + <ul id="phototools" class="transparent hidden"> + <li><a class="svg delete" title="<?php echo $l->t('Delete current photo'); ?>"></a></li> + <li><a class="svg edit" title="<?php echo $l->t('Edit current photo'); ?>"></a></li> + <li><a class="svg upload" title="<?php echo $l->t('Upload new photo'); ?>"></a></li> + <li><a class="svg cloud" title="<?php echo $l->t('Select photo from ownCloud'); ?>"></a></li> + </ul> </div> </div> <!-- contact_photo --> - <div id="contact_identity" class="contactsection"> <form method="post"> + + <div id="contact_identity"> <input type="hidden" name="id" value="<?php echo $_['id'] ?>"> + <input type="hidden" name="requesttoken" value="<?php echo $_['requesttoken'] ?>"> <fieldset id="ident" class="contactpart"> <span class="propertycontainer" data-element="N"><input type="hidden" id="n" class="contacts_property" name="value" value="" /></span> <span id="name" class="propertycontainer" data-element="FN"> @@ -31,6 +40,8 @@ $id = isset($_['id']) ? $_['id'] : ''; <dd class="propertycontainer hidden" id="org_value" data-element="ORG"><input id="org" required="required" type="text" class="contacts_property big" name="value" value="" placeholder="<?php echo $l->t('Organization'); ?>" /><a role="button" class="action delete" title="<?php echo $l->t('Delete'); ?>"></a></dd> <dt class="hidden" id="nickname_label" data-element="NICKNAME"><label for="nickname"><?php echo $l->t('Nickname'); ?></label></dt> <dd class="propertycontainer hidden" id="nickname_value" data-element="NICKNAME"><input id="nickname" required="required" type="text" class="contacts_property big" name="value" value="" placeholder="<?php echo $l->t('Enter nickname'); ?>" /><a role="button" class="action delete" title="<?php echo $l->t('Delete'); ?>"></a></dd> + <dt class="hidden" id="url_label" data-element="URL"><label for="url"><?php echo $l->t('Web site'); ?></label></dt> + <dd class="propertycontainer hidden" id="url_value" data-element="URL"><input id="url" required="required" type="url" class="contacts_property big" name="value" value="" placeholder="<?php echo $l->t('http://www.somesite.com'); ?>" /><a role="button" class="action globe" title="<?php echo $l->t('Go to web site'); ?>"><a role="button" class="action delete" title="<?php echo $l->t('Delete'); ?>"></a></dd> <dt class="hidden" id="bday_label" data-element="BDAY"><label for="bday"><?php echo $l->t('Birthday'); ?></label></dt> <dd class="propertycontainer hidden" id="bday_value" data-element="BDAY"><input id="bday" required="required" name="value" type="text" class="contacts_property big" value="" placeholder="<?php echo $l->t('dd-mm-yyyy'); ?>" /><a role="button" class="action delete" title="<?php echo $l->t('Delete'); ?>"></a></dd> <dt class="hidden" id="categories_label" data-element="CATEGORIES"><label for="categories"><?php echo $l->t('Groups'); ?></label></dt> @@ -39,73 +50,88 @@ $id = isset($_['id']) ? $_['id'] : ''; <a role="button" class="action delete" title="<?php echo $l->t('Delete'); ?>"></a><a role="button" class="action edit" title="<?php echo $l->t('Edit groups'); ?>"></a></dd> </dl> </fieldset> - </form> </div> <!-- contact_identity --> - <!-- div class="delimiter"></div --> - <div id="contact_communication" class="contactsection"> - <form method="post"> - <!-- email addresses --> - <div id="emails"> - <ul id="emaillist" class="propertylist"> - <li class="template hidden" data-element="EMAIL"> - <input type="checkbox" class="contacts_property tip" name="parameters[TYPE][]" value="PREF" title="<?php echo $l->t('Preferred'); ?>" /> - <input type="email" required="required" class="nonempty contacts_property" name="value" value="" x-moz-errormessage="<?php echo $l->t('Please specify a valid email address.'); ?>" placeholder="<?php echo $l->t('Enter email address'); ?>" /> - <select class="hidden" multiple="multiple" name="parameters[TYPE][]"> - <?php echo OCP\html_select_options($_['email_types'], array()) ?> - </select> - <span class="listactions"><a class="action mail" title="<?php echo $l->t('Mail to address'); ?>"></a> - <a role="button" class="action delete" title="<?php echo $l->t('Delete email address'); ?>"></a></span></li> - </ul> - </div> <!-- email addresses--> + <!-- email addresses --> + <div id="emails" class="hidden contactsection"> + <ul id="emaillist" class="propertylist"> + <li class="template hidden" data-element="EMAIL"> + <input type="checkbox" class="contacts_property tip" name="parameters[TYPE][]" value="PREF" title="<?php echo $l->t('Preferred'); ?>" /> + <input type="email" required="required" class="nonempty contacts_property" name="value" value="" x-moz-errormessage="<?php echo $l->t('Please specify a valid email address.'); ?>" placeholder="<?php echo $l->t('Enter email address'); ?>" /> + <select multiple="multiple" name="parameters[TYPE][]"> + <?php echo OCP\html_select_options($_['email_types'], array()) ?> + </select> + <span class="listactions"><a class="action mail" title="<?php echo $l->t('Mail to address'); ?>"></a> + <a role="button" class="action delete" title="<?php echo $l->t('Delete email address'); ?>"></a></span></li> + </ul> + </div> <!-- email addresses--> - <!-- Phone numbers --> - <div id="phones"> - <ul id="phonelist" class="propertylist"> - <li class="template hidden" data-element="TEL"> - <input type="checkbox" class="contacts_property tip" name="parameters[TYPE][]" value="PREF" title="<?php echo $l->t('Preferred'); ?>" /> - <input type="text" required="required" class="nonempty contacts_property" name="value" value="" placeholder="<?php echo $l->t('Enter phone number'); ?>" /> - <select multiple="multiple" name="parameters[TYPE][]"> - <?php echo OCP\html_select_options($_['phone_types'], array()) ?> - </select> - <a role="button" class="action delete" title="<?php echo $l->t('Delete phone number'); ?>"></a></li> - </ul> - </div> <!-- Phone numbers --> + <!-- Phone numbers --> + <div id="phones" class="hidden contactsection"> + <ul id="phonelist" class="propertylist"> + <li class="template hidden" data-element="TEL"> + <input type="checkbox" class="contacts_property tip" name="parameters[TYPE][]" value="PREF" title="<?php echo $l->t('Preferred'); ?>" /> + <input type="text" required="required" class="nonempty contacts_property" name="value" value="" + placeholder="<?php echo $l->t('Enter phone number'); ?>" /> + <select multiple="multiple" name="parameters[TYPE][]"> + <?php echo OCP\html_select_options($_['phone_types'], array()) ?> + </select> + <a role="button" class="action delete" title="<?php echo $l->t('Delete phone number'); ?>"></a></li> + </ul> + </div> <!-- Phone numbers --> - <!-- Addresses --> - <div id="addresses" class="hidden"> - <div id="addressdisplay"> - <dl class="addresscard template hidden" data-element="ADR"><dt> - <input class="adr contacts_property" name="value" type="hidden" value="" /> - <input type="hidden" class="adr_type contacts_property" name="parameters[TYPE][]" value="" /> - <span class="adr_type_label"></span><a class="action globe" title="<?php echo $l->t('View on map'); ?>"></a><a class="action edit" title="<?php echo $l->t('Edit address details'); ?>"></a><a role="button" class="action delete" title="Delete address"></a> - </dt><dd><ul class="addresslist"></ul></dd></dl> + <!-- IMPP --> + <div id="ims" class="hidden contactsection"> + <ul id="imlist" class="propertylist"> + <li class="template hidden" data-element="IMPP"> + <div class="select_wrapper"> + <select class="impp" name="parameters[X-SERVICE-TYPE]"> + <?php echo OCP\html_select_options($_['im_protocols'], array()) ?> + </select> + </div> + <div class="select_wrapper"> + <select class="types" name="parameters[TYPE][]"> + <option></option> + <?php echo OCP\html_select_options($_['impp_types'], array()) ?> + </select> + </div> + <input type="checkbox" class="contacts_property impp tip" name="parameters[TYPE][]" value="PREF" title="<?php echo $l->t('Preferred'); ?>" /> + <input type="text" required="required" class="nonempty contacts_property" name="value" value="" + placeholder="<?php echo $l->t('Instant Messenger'); ?>" /> + <a role="button" class="action delete" title="<?php echo $l->t('Delete IM'); ?>"></a></li> + </ul> + </div> <!-- IMPP --> - </div> <!-- addressdisplay --> - </div> <!-- Addresses --> - </form> - </div> <!-- contact_communication --> + <!-- Addresses --> + <div id="addresses" class="hidden contactsection"> + <dl class="addresscard template hidden" data-element="ADR"><dt> + <input class="adr contacts_property" name="value" type="hidden" value="" /> + <input type="hidden" class="adr_type contacts_property" name="parameters[TYPE][]" value="" /> + <span class="adr_type_label"></span><a class="action globe" title="<?php echo $l->t('View on map'); ?>"></a><a class="action edit" title="<?php echo $l->t('Edit address details'); ?>"></a><a role="button" class="action delete" title="Delete address"></a> + </dt><dd><ul class="addresslist"></ul></dd></dl> + </div> <!-- Addresses --> - <div id="contact_note" class="contactsection"> - <form class="float" method="post"> + <div id="contact_note" class="hidden contactsection"> <div id="note" class="propertycontainer" data-element="NOTE"> <textarea class="contacts_property" name="value" required="required" placeholder="<?php echo $l->t('Add notes here.'); ?>" cols="60" wrap="hard"></textarea> </div> - </form> </div> <!-- contact_note --> + </form> + <div id="actionbar"> <div id="contacts_propertymenu"> <button class="button" id="contacts_propertymenu_button"><?php echo $l->t('Add field'); ?></button> <ul id="contacts_propertymenu_dropdown" role="menu" class="hidden"> - <li><a role="menuitem" data-type="PHOTO"><?php echo $l->t('Profile picture'); ?></a></li> <li><a role="menuitem" data-type="ORG"><?php echo $l->t('Organization'); ?></a></li> <li><a role="menuitem" data-type="NICKNAME"><?php echo $l->t('Nickname'); ?></a></li> <li><a role="menuitem" data-type="BDAY"><?php echo $l->t('Birthday'); ?></a></li> <li><a role="menuitem" data-type="TEL"><?php echo $l->t('Phone'); ?></a></li> <li><a role="menuitem" data-type="EMAIL"><?php echo $l->t('Email'); ?></a></li> + <li><a role="menuitem" data-type="IMPP"><?php echo $l->t('Instant Messaging'); ?></a></li> <li><a role="menuitem" data-type="ADR"><?php echo $l->t('Address'); ?></a></li> <li><a role="menuitem" data-type="NOTE"><?php echo $l->t('Note'); ?></a></li> + <li><a role="menuitem" data-type="URL"><?php echo $l->t('Web site'); ?></a></li> <li><a role="menuitem" data-type="CATEGORIES"><?php echo $l->t('Groups'); ?></a></li> </ul> </div> @@ -117,19 +143,3 @@ $id = isset($_['id']) ? $_['id'] : ''; <div id="edit_photo_dialog" title="Edit photo"> <div id="edit_photo_dialog_img"></div> </div> -<script type="text/javascript"> -$(document).ready(function(){ - if('<?php echo $id; ?>'!='') { - $.getJSON(OC.filePath('contacts', 'ajax', 'contactdetails.php'),{'id':'<?php echo $id; ?>'},function(jsondata){ - if(jsondata.status == 'success'){ - $('#leftcontent li[data-id="<?php echo $id; ?>"]').addClass('active'); - Contacts.UI.Card.loadContact(jsondata.data); - Contacts.UI.loadHandlers(); - } - else{ - OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); - } - }); - } -}); -</script> diff --git a/apps/contacts/templates/part.contactphoto.php b/apps/contacts/templates/part.contactphoto.php deleted file mode 100644 index bddf4cc8a81..00000000000 --- a/apps/contacts/templates/part.contactphoto.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -$id = $_['id']; -$wattr = isset($_['width'])?'width="'.$_['width'].'"':''; -$hattr = isset($_['height'])?'height="'.$_['height'].'"':''; -$rand = isset($_['refresh'])?'&refresh='.rand():''; -?> -<ul id="phototools" class="transparent hidden"> - <li><a class="svg delete" title="<?php echo $l->t('Delete current photo'); ?>"></a></li> - <li><a class="svg edit" title="<?php echo $l->t('Edit current photo'); ?>"></a></li> - <li><a class="svg upload" title="<?php echo $l->t('Upload new photo'); ?>"></a></li> - <li><a class="svg cloud" title="<?php echo $l->t('Select photo from ownCloud'); ?>"></a></li> -</ul> -<img class="loading" id="contacts_details_photo" <?php echo $wattr; ?> <?php echo $hattr; ?> src="<?php echo OCP\Util::linkToAbsolute('contacts', 'photo.php'); ?>?id=<?php echo $id.$rand; ?>" /> -<progress id="contacts_details_photo_progress" style="display:none;" value="0" max="100">0 %</progress> - - diff --git a/apps/contacts/templates/part.contacts.php b/apps/contacts/templates/part.contacts.php deleted file mode 100644 index 57517505405..00000000000 --- a/apps/contacts/templates/part.contacts.php +++ /dev/null @@ -1,12 +0,0 @@ -<?php foreach( $_['contacts'] as $contact ): - $display = trim($contact['fullname']); - if(!$display) { - $vcard = OC_Contacts_App::getContactVCard($contact['id']); - if(!is_null($vcard)) { - $struct = OC_Contacts_VCard::structureContact($vcard); - $display = isset($struct['EMAIL'][0])?$struct['EMAIL'][0]['value']:'[UNKNOWN]'; - } - } -?> - <li role="button" book-id="<?php echo $contact['addressbookid']; ?>" data-id="<?php echo $contact['id']; ?>"><a href="index.php?id=<?php echo $contact['id']; ?>"><?php echo htmlspecialchars($display); ?></a></li> -<?php endforeach; ?> diff --git a/apps/contacts/templates/part.cropphoto.php b/apps/contacts/templates/part.cropphoto.php index e1072179130..3f5817622b2 100644 --- a/apps/contacts/templates/part.cropphoto.php +++ b/apps/contacts/templates/part.cropphoto.php @@ -1,10 +1,9 @@ <?php $id = $_['id']; -$tmp_path = $_['tmp_path']; +$tmpkey = $_['tmpkey']; $requesttoken = $_['requesttoken']; -OCP\Util::writeLog('contacts','templates/part.cropphoto.php: tmp_path: '.$tmp_path.', exists: '.file_exists($tmp_path), OCP\Util::DEBUG); ?> -<script language="Javascript"> +<script type="text/javascript"> jQuery(function($) { $('#cropbox').Jcrop({ onChange: showCoords, @@ -39,7 +38,8 @@ OCP\Util::writeLog('contacts','templates/part.cropphoto.php: tmp_path: '.$tmp_pa return true; });*/ </script> -<img id="cropbox" src="<?php echo OCP\Util::linkToAbsolute('contacts', 'dynphoto.php'); ?>?tmp_path=<?php echo urlencode($tmp_path); ?>" /> +<?php if(OC_Cache::hasKey($tmpkey)) { ?> +<img id="cropbox" src="<?php echo OCP\Util::linkToAbsolute('contacts', 'tmpphoto.php'); ?>?tmpkey=<?php echo $tmpkey; ?>" /> <form id="cropform" class="coords" method="post" @@ -48,8 +48,8 @@ OCP\Util::writeLog('contacts','templates/part.cropphoto.php: tmp_path: '.$tmp_pa action="<?php echo OCP\Util::linkToAbsolute('contacts', 'ajax/savecrop.php'); ?>"> <input type="hidden" id="id" name="id" value="<?php echo $id; ?>" /> - <input type="hidden" id="requesttoken" name="requesttoken" value="<?php echo $requesttoken; ?>" /> - <input type="hidden" id="tmp_path" name="tmp_path" value="<?php echo $tmp_path; ?>" /> + <input type="hidden" name="requesttoken" value="<?php echo $requesttoken; ?>"> + <input type="hidden" id="tmpkey" name="tmpkey" value="<?php echo $tmpkey; ?>" /> <fieldset id="coords"> <input type="hidden" id="x1" name="x1" value="" /> <input type="hidden" id="y1" name="y1" value="" /> @@ -60,5 +60,8 @@ OCP\Util::writeLog('contacts','templates/part.cropphoto.php: tmp_path: '.$tmp_pa </fieldset> <iframe name="crop_target" id='crop_target' src=""></iframe> </form> - - +<?php +} else { + echo $l->t('The temporary image has been removed from cache.'); +} +?> diff --git a/apps/contacts/templates/part.edit_address_dialog.php b/apps/contacts/templates/part.edit_address_dialog.php index 8b3425033cc..81e24ba4d0e 100644 --- a/apps/contacts/templates/part.edit_address_dialog.php +++ b/apps/contacts/templates/part.edit_address_dialog.php @@ -1,13 +1,9 @@ <?php $adr = isset($_['adr'])?$_['adr']:array(); -$id = $_['id']; -$types = array(); -foreach(isset($adr['parameters']['TYPE'])?array($adr['parameters']['TYPE']):array() as $type) { - $types[] = strtoupper($type); -} +$id = isset($_['id'])?$_['id']:array(); +$types = isset($_['types'])?$_['types']:array(); ?> <div id="edit_address_dialog" title="<?php echo $l->t('Edit address'); ?>"> -<!-- ?php print_r($types); ? --> <fieldset id="address"> <dl class="form"> <dt> @@ -22,46 +18,46 @@ foreach(isset($adr['parameters']['TYPE'])?array($adr['parameters']['TYPE']):arra <label class="label" for="adr_pobox"><?php echo $l->t('PO Box'); ?></label> </dt> <dd> - <input type="text" id="adr_pobox" name="value[ADR][0]" placeholder="<?php echo $l->t('PO Box'); ?>" value="<?php echo isset($adr['value'][0])?$adr['value'][0]:''; ?>"> + <input type="text" id="adr_pobox" name="value[ADR][0]" placeholder="<?php echo $l->t('PO Box'); ?>" value="<?php echo isset($adr[0])?$adr[0]:''; ?>"> </dd> - <dd> <dt> - <label class="label" for="adr_extended"><?php echo $l->t('Extended'); ?></label> + <label class="label" for="adr_street"><?php echo $l->t('Street address'); ?></label> </dt> <dd> - <input type="text" id="adr_extended" name="value[ADR][1]" placeholder="<?php echo $l->t('Extended'); ?>" value="<?php echo isset($adr['value'][1])?$adr['value'][1]:''; ?>"> + <input type="text" id="adr_street" name="value[ADR][2]" placeholder="<?php echo $l->t('Street and number'); ?>" value="<?php echo isset($adr[2])?$adr[2]:''; ?>"> </dd> <dt> - <label class="label" for="adr_street"><?php echo $l->t('Street'); ?></label> + <label class="label" for="adr_extended"><?php echo $l->t('Extended'); ?></label> </dt> <dd> - <input type="text" id="adr_street" name="value[ADR][2]" placeholder="<?php echo $l->t('Street'); ?>" value="<?php echo isset($adr['value'][2])?$adr['value'][2]:''; ?>"> + <input type="text" id="adr_extended" name="value[ADR][1]" placeholder="<?php echo $l->t('Apartment number etc.'); ?>" value="<?php echo isset($adr[1])?$adr[1]:''; ?>"> </dd> <dt> <label class="label" for="adr_city"><?php echo $l->t('City'); ?></label> </dt> <dd> - <input type="text" id="adr_city" name="value[ADR][3]" placeholder="<?php echo $l->t('City'); ?>" value="<?php echo isset($adr['value'][3])?$adr['value'][3]:''; ?>"> + <input type="text" id="adr_city" name="value[ADR][3]" placeholder="<?php echo $l->t('City'); ?>" value="<?php echo isset($adr[3])?$adr[3]:''; ?>"> </dd> <dt> <label class="label" for="adr_region"><?php echo $l->t('Region'); ?></label> </dt> <dd> - <input type="text" id="adr_region" name="value[ADR][4]" placeholder="<?php echo $l->t('Region'); ?>" value="<?php echo isset($adr['value'][4])?$adr['value'][4]:''; ?>"> + <input type="text" id="adr_region" name="value[ADR][4]" placeholder="<?php echo $l->t('E.g. state or province'); ?>" value="<?php echo isset($adr[4])?$adr[4]:''; ?>"> </dd> <dt> <label class="label" for="adr_zipcode"><?php echo $l->t('Zipcode'); ?></label> </dt> <dd> - <input type="text" id="adr_zipcode" name="value[ADR][5]" placeholder="<?php echo $l->t('Zipcode'); ?>" value="<?php echo isset($adr['value'][5])?$adr['value'][5]:''; ?>"> + <input type="text" id="adr_zipcode" name="value[ADR][5]" placeholder="<?php echo $l->t('Postal code'); ?>" value="<?php echo isset($adr[5])?$adr[5]:''; ?>"> </dd> <dt> <label class="label" for="adr_country"><?php echo $l->t('Country'); ?></label> </dt> <dd> - <input type="text" id="adr_country" name="value[ADR][6]" placeholder="<?php echo $l->t('Country'); ?>" value="<?php echo isset($adr['value'][6])?$adr['value'][6]:''; ?>"> + <input type="text" id="adr_country" name="value[ADR][6]" placeholder="<?php echo $l->t('Country'); ?>" value="<?php echo isset($adr[6])?$adr[6]:''; ?>"> </dd> </dl> + <div style="width: 100%; text-align:center;">Powered by <a href="http://geonames.org/" target="_blank">geonames.org</a></div> </fieldset> </form> </div> diff --git a/apps/contacts/templates/part.edit_categories_dialog.php b/apps/contacts/templates/part.edit_categories_dialog.php deleted file mode 100644 index 8997fa586bd..00000000000 --- a/apps/contacts/templates/part.edit_categories_dialog.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -$categories = isset($_['categories'])?$_['categories']:array(); -?> -<div id="edit_categories_dialog" title="<?php echo $l->t('Edit categories'); ?>"> -<!-- ?php print_r($types); ? --> - <form method="post" id="categoryform"> - <div class="scrollarea"> - <ul id="categorylist"> - <?php foreach($categories as $category) { ?> - <li><input type="checkbox" name="categories[]" value="<?php echo $category; ?>" /><?php echo $category; ?></li> - <?php } ?> - </ul> - </div> - <div class="bottombuttons"><input type="text" id="category_addinput" name="category" /><button id="category_addbutton" disabled="disabled"><?php echo $l->t('Add'); ?></button></div> - </form> -</div> diff --git a/apps/contacts/templates/part.edit_name_dialog.php b/apps/contacts/templates/part.edit_name_dialog.php index be45f9a5b06..f984c232a30 100644 --- a/apps/contacts/templates/part.edit_name_dialog.php +++ b/apps/contacts/templates/part.edit_name_dialog.php @@ -22,7 +22,7 @@ $addressbooks = isset($_['addressbooks'])?$_['addressbooks']:null; <?php }} ?> <dt><label for="pre"><?php echo $l->t('Hon. prefixes'); ?></label></dt> <dd> - <input name="pre" id="pre" value="<?php echo isset($name['value'][3]) ? $name['value'][3] : ''; ?>" type="text" list="prefixes" /> + <input name="pre" id="pre" value="<?php echo isset($name[3]) ? $name[3] : ''; ?>" type="text" list="prefixes" /> <datalist id="prefixes"> <option value="<?php echo $l->t('Miss'); ?>"> <option value="<?php echo $l->t('Ms'); ?>"> @@ -33,14 +33,14 @@ $addressbooks = isset($_['addressbooks'])?$_['addressbooks']:null; </datalist> </dd> <dt><label for="giv"><?php echo $l->t('Given name'); ?></label></dt> - <dd><input name="giv" id="giv" value="<?php echo isset($name['value'][1]) ? $name['value'][1] : ''; ?>" type="text" /></dd> + <dd><input name="giv" id="giv" value="<?php echo isset($name[1]) ? $name[1] : ''; ?>" type="text" /></dd> <dt><label for="add"><?php echo $l->t('Additional names'); ?></label></dt> - <dd><input name="add" id="add" value="<?php echo isset($name['value'][2]) ? $name['value'][2] : ''; ?>" type="text" /></dd> + <dd><input name="add" id="add" value="<?php echo isset($name[2]) ? $name[2] : ''; ?>" type="text" /></dd> <dt><label for="fam"><?php echo $l->t('Family name'); ?></label></dt> - <dd><input name="fam" id="fam" value="<?php echo isset($name['value'][0]) ? $name['value'][0] : ''; ?>" type="text" /></dd> + <dd><input name="fam" id="fam" value="<?php echo isset($name[0]) ? $name[0] : ''; ?>" type="text" /></dd> <dt><label for="suf"><?php echo $l->t('Hon. suffixes'); ?></label></dt> <dd> - <input name="suf" id="suf" value="<?php echo isset($name['value'][4]) ? $name['value'][4] : ''; ?>" type="text" list="suffixes" /> + <input name="suf" id="suf" value="<?php echo isset($name[4]) ? $name[4] : ''; ?>" type="text" list="suffixes" /> <datalist id="suffixes"> <option value="<?php echo $l->t('J.D.'); ?>"> <option value="<?php echo $l->t('M.D.'); ?>"> diff --git a/apps/contacts/templates/part.editaddressbook.php b/apps/contacts/templates/part.editaddressbook.php deleted file mode 100644 index c1c585687c4..00000000000 --- a/apps/contacts/templates/part.editaddressbook.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php -/** - * Copyright (c) 2011 Bart Visscher <bartv@thisnet.nl> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ -?> -<td id="<?php echo $_['new'] ? 'new' : 'edit' ?>addressbook_dialog" title="<?php echo $_['new'] ? $l->t("New Addressbook") : $l->t("Edit Addressbook"); ?>" colspan="6"> -<table width="100%" style="border: 0;"> -<tr> - <th><?php echo $l->t('Displayname') ?></th> - <td> - <input id="displayname_<?php echo $_['addressbook']['id'] ?>" type="text" value="<?php echo htmlspecialchars($_['addressbook']['displayname']) ?>"> - </td> -</tr> -<?php if (!$_['new']): ?> -<tr> - <td></td> - <td> - <input id="edit_active_<?php echo $_['addressbook']['id'] ?>" type="checkbox"<?php echo OC_Contacts_Addressbook::isActive($_['addressbook']['id']) ? ' checked="checked"' : '' ?>> - <label for="edit_active_<?php echo $_['addressbook']['id'] ?>"> - <?php echo $l->t('Active') ?> - </label> - </td> -</tr> -<?php endif; ?> -</table> -<input style="float: left;" type="button" onclick="Contacts.UI.Addressbooks.submit(this, <?php echo $_['new'] ? "'new'" : $_['addressbook']['id'] ?>);" value="<?php echo $_['new'] ? $l->t("Save") : $l->t("Submit"); ?>"> -<input style="float: left;" type="button" onclick="Contacts.UI.Addressbooks.cancel(this, <?php echo $_['new'] ? "'new'" : $_['addressbook']['id'] ?>);" value="<?php echo $l->t("Cancel"); ?>"> -</td> diff --git a/apps/contacts/templates/part.import.php b/apps/contacts/templates/part.import.php index b8793042997..32c8dc50dd6 100644 --- a/apps/contacts/templates/part.import.php +++ b/apps/contacts/templates/part.import.php @@ -2,7 +2,7 @@ <div id="form_container"> <input type="hidden" id="filename" value="<?php echo $_['filename'];?>"> <input type="hidden" id="path" value="<?php echo $_['path'];?>"> - <input type="hidden" id="progressfile" value="<?php echo md5(session_id()) . '.txt';?>"> + <input type="hidden" id="progresskey" value="<?php echo rand() ?>"> <p class="bold" style="text-align:center;"><?php echo $l->t('Please choose the addressbook'); ?></p> <select style="width:100%;" id="contacts" name="contacts"> <?php diff --git a/apps/contacts/templates/part.importaddressbook.php b/apps/contacts/templates/part.importaddressbook.php deleted file mode 100644 index c7c6555f680..00000000000 --- a/apps/contacts/templates/part.importaddressbook.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php -/** - * Copyright (c) 2012 Thomas Tanghus <thomas@tanghus.net> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ -?> -<td id="importaddressbook_dialog" colspan="6"> -<table> -<tr> - <th><?php echo $l->t('Select address book to import to:') ?></th> - <td> - <form id="import_upload_form" action="<?php echo OCP\Util::linkTo('contacts', 'ajax/uploadimport.php'); ?>" method="post" enctype="multipart/form-data" target="import_upload_target"> - <input type="hidden" name="requesttoken" value="<?php echo $_['requesttoken'] ?>"> - <select id="book" name="book" class="float"> - <?php - $contacts_options = OC_Contacts_Addressbook::all(OCP\USER::getUser()); - echo OCP\html_select_options($contacts_options, $contacts_options[0]['id'], array('value'=>'id', 'label'=>'displayname')); - ?> - </select> - <span id="import_drop_target" class="droptarget float"><?php echo $l->t("Drop a VCF file<br />to import contacts."); ?> (Max. <?php echo $_['uploadMaxHumanFilesize']; ?>)</span> - <a class="svg upload float" title="<?php echo $l->t('Select from HD'); ?>"> - <input class="float" id="import_upload_start" type="file" accept="text/*" name="importfile" /></a> - <input type="hidden" name="MAX_FILE_SIZE" value="<?php echo $_['uploadMaxFilesize'] ?>" id="max_upload"> - </form> - </td> -</tr> -</table> - -<input id="close_button" style="float: left;" type="button" onclick="Contacts.UI.Addressbooks.cancel(this);" value="<?php echo $l->t("Cancel"); ?>"> -<iframe name="import_upload_target" id='import_upload_target' src=""></iframe> -</td> -<script type="text/javascript"> -Contacts.UI.Addressbooks.loadImportHandlers(); -</script>
\ No newline at end of file diff --git a/apps/contacts/templates/part.no_contacts.php b/apps/contacts/templates/part.no_contacts.php index 5faa481bc3c..1802939483b 100644 --- a/apps/contacts/templates/part.no_contacts.php +++ b/apps/contacts/templates/part.no_contacts.php @@ -1,7 +1,7 @@ +<div id="appsettings" class="popup bottomleft hidden"></div> <div id="firstrun"> <?php echo $l->t('You have no contacts in your addressbook.') ?> <div id="selections"> - <input type="button" value="<?php echo $l->t('Add contact') ?>" onclick="Contacts.UI.Card.editNew()" /> - <input type="button" value="<?php echo $l->t('Configure addressbooks') ?>" onclick="Contacts.UI.Addressbooks.overview()" /> + <input type="button" value="<?php echo $l->t('Add contact') ?>" onclick="OC.Contacts.Card.editNew()" /> </div> </div> diff --git a/apps/contacts/templates/part.selectaddressbook.php b/apps/contacts/templates/part.selectaddressbook.php index 4b152a6e1d3..812a3b891ff 100644 --- a/apps/contacts/templates/part.selectaddressbook.php +++ b/apps/contacts/templates/part.selectaddressbook.php @@ -1,16 +1,33 @@ <div id="selectaddressbook_dialog" title="<?php echo $l->t("Select Address Books"); ?>"> +<script type="text/javascript"> +$(document).ready(function() { + $('input.name,input.desc').on('focus', function(e) { + $('#book_new').prop('checked', true); + }); +}); +</script> <form> -<table> +<table style="width: 100%"> <?php foreach($_['addressbooks'] as $idx => $addressbook) { ?> <tr> <td> <input id="book_<?php echo $addressbook['id']; ?>" name="book" type="radio" value="<?php echo $addressbook['id']; ?>" <?php echo ($idx==0?'checked="checked"':'')?>> </td> <td> - <label for="book_<?php echo $addressbook['id']; ?>"><?php echo $addressbook['name']; ?></label> + <label for="book_<?php echo $addressbook['id']; ?>"><?php echo $addressbook['displayname']; ?></label> </td> + <td><?php echo $addressbook['description']; ?></td> </tr> <?php } ?> + <tr> + <td> + <input id="book_new" name="book" type="radio" value="new"> + </td> + <th> + <input type="text" class="name" name="displayname" placeholder="<?php echo $l->t("Enter name"); ?>" /> + </th> + <td><input type="text" class="desc" name="description" placeholder="<?php echo $l->t("Enter description"); ?>" /></td> + </tr> </table> </form> </div> diff --git a/apps/contacts/templates/settings.php b/apps/contacts/templates/settings.php index 216003b6c69..e3536c7b461 100644 --- a/apps/contacts/templates/settings.php +++ b/apps/contacts/templates/settings.php @@ -1,13 +1,55 @@ -<form id="contacts"> +<form id="contacts-settings"> <fieldset class="personalblock"> - <legend><?php echo $l->t('Contacts'); ?></legend> <?php echo $l->t('CardDAV syncing addresses'); ?> (<a href="http://owncloud.org/synchronisation/" target="_blank"><?php echo $l->t('more info'); ?></a>) <dl> <dt><?php echo $l->t('Primary address (Kontact et al)'); ?></dt> <dd><code><?php echo OCP\Util::linkToRemote('carddav'); ?></code></dd> <dt><?php echo $l->t('iOS/OS X'); ?></dt> <dd><code><?php echo OCP\Util::linkToRemote('carddav'); ?>principals/<?php echo OCP\USER::getUser(); ?></code>/</dd> + <dt class="hidden"><?php echo $l->t('Addressbooks'); ?></dt> + <dd class="addressbooks-settings hidden"> + <table> + <?php foreach($_['addressbooks'] as $addressbook) { ?> + <tr class="addressbook" data-id="<?php echo $addressbook['id'] ?>" data-uri="<?php echo $addressbook['uri'] ?>"> + <td class="active"> + <input type="checkbox" <?php echo (($addressbook['active']) == '1' ? ' checked="checked"' : ''); ?> /> + </td> + <td class="name"><?php echo $addressbook['displayname'] ?></td> + <td class="description"><?php echo $addressbook['description'] ?></td> + <td class="action"> + <a class="svg action globe" title="<?php echo $l->t('Show CardDav link'); ?>"></a> + </td> + <td class="action"> + <a class="svg action cloud" title="<?php echo $l->t('Show read-only VCF link'); ?>"></a> + </td> + <td class="action"> + <a class="svg action share" data-item-type="addressbook" data-item="<?php echo $addressbook['id'] ?>" title="<?php echo $l->t("Share"); ?>"></a> + </td> + <td class="action"> + <a class="svg action download" title="<?php echo $l->t('Download'); ?>" + href="<?php echo OCP\Util::linkToAbsolute('contacts', 'export.php'); ?>?bookid=<?php echo $addressbook['id'] ?>"></a> + </td> + <td class="action"> + <a class="svg action edit" title="<?php echo $l->t("Edit"); ?>"></a> + </td> + <td class="action"> + <a class="svg action delete" title="<?php echo $l->t("Delete"); ?>"></a> + </td> + </tr> + <?php } ?> + </table> + <div class="actions" style="width: 100%;"> + <input class="active hidden" type="checkbox" /> + <button class="new"><?php echo $l->t('New Address Book') ?></button> + <input class="name hidden" type="text" autofocus="autofocus" placeholder="<?php echo $l->t('Name'); ?>" /> + <input class="description hidden" type="text" placeholder="<?php echo $l->t('Description'); ?>" /> + <button class="save hidden"><?php echo $l->t('Save') ?></button> + <button class="cancel hidden"><?php echo $l->t('Cancel') ?></button> + </div> + </dd> </dl> - Powered by <a href="http://geonames.org/" target="_blank">geonames.org webservice</a> + <div style="width: 100%; clear: both;"> + <button class="moreless"><?php echo $l->t('More...') ?></button> + </div> </fieldset> </form> diff --git a/apps/contacts/thumbnail.php b/apps/contacts/thumbnail.php index a64b09b6711..1e3714ae6c6 100644 --- a/apps/contacts/thumbnail.php +++ b/apps/contacts/thumbnail.php @@ -20,32 +20,33 @@ * */ -// Init owncloud - OCP\JSON::checkLoggedIn(); //OCP\User::checkLoggedIn(); OCP\App::checkAppEnabled('contacts'); +session_write_close(); -function getStandardImage(){ - //OCP\Response::setExpiresHeader('P10D'); +function getStandardImage() { OCP\Response::enableCaching(); OCP\Response::redirect(OCP\Util::imagePath('contacts', 'person.png')); } if(!extension_loaded('gd') || !function_exists('gd_info')) { - OCP\Util::writeLog('contacts','thumbnail.php. GD module not installed',OCP\Util::DEBUG); + OCP\Util::writeLog('contacts', + 'thumbnail.php. GD module not installed', OCP\Util::DEBUG); getStandardImage(); exit(); } $id = $_GET['id']; -$caching = isset($_GET['refresh']) ? 0 : null; +$caching = null; $contact = OC_Contacts_App::getContactVCard($id); // invalid vcard -if(is_null($contact)){ - OCP\Util::writeLog('contacts','thumbnail.php. The VCard for ID '.$id.' is not RFC compatible',OCP\Util::ERROR); +if(is_null($contact)) { + OCP\Util::writeLog('contacts', + 'thumbnail.php. The VCard for ID ' . $id . ' is not RFC compatible', + OCP\Util::ERROR); getStandardImage(); exit(); } @@ -58,25 +59,40 @@ $thumbnail_size = 23; $image = new OC_Image(); $photo = $contact->getAsString('PHOTO'); if($photo) { - OCP\Response::setETagHeader(md5($photo)); - if($image->loadFromBase64($photo)) { if($image->centerCrop()) { if($image->resize($thumbnail_size)) { + $modified = OC_Contacts_App::lastModified($contact); + // Force refresh if modified within the last minute. + if(!is_null($modified)) { + $caching = (time() - $modified->format('U') > 60) ? null : 0; + } + OCP\Response::enableCaching($caching); + if(!is_null($modified)) { + OCP\Response::setLastModifiedHeader($modified); + } + OCP\Response::setETagHeader(md5($photo)); if($image->show()) { - // done exit(); } else { - OCP\Util::writeLog('contacts','thumbnail.php. Couldn\'t display thumbnail for ID '.$id,OCP\Util::ERROR); + OCP\Util::writeLog('contacts', + 'thumbnail.php. Couldn\'t display thumbnail for ID ' . $id, + OCP\Util::ERROR); } } else { - OCP\Util::writeLog('contacts','thumbnail.php. Couldn\'t resize thumbnail for ID '.$id,OCP\Util::ERROR); + OCP\Util::writeLog('contacts', + 'thumbnail.php. Couldn\'t resize thumbnail for ID ' . $id, + OCP\Util::ERROR); } }else{ - OCP\Util::writeLog('contacts','thumbnail.php. Couldn\'t crop thumbnail for ID '.$id,OCP\Util::ERROR); + OCP\Util::writeLog('contacts', + 'thumbnail.php. Couldn\'t crop thumbnail for ID ' . $id, + OCP\Util::ERROR); } } else { - OCP\Util::writeLog('contacts','thumbnail.php. Couldn\'t load image string for ID '.$id,OCP\Util::ERROR); + OCP\Util::writeLog('contacts', + 'thumbnail.php. Couldn\'t load image string for ID ' . $id, + OCP\Util::ERROR); } } getStandardImage(); diff --git a/apps/contacts/dynphoto.php b/apps/contacts/tmpphoto.php index ea6cef227e1..156d5c80308 100644 --- a/apps/contacts/dynphoto.php +++ b/apps/contacts/tmpphoto.php @@ -20,15 +20,14 @@ * */ -// Init owncloud - -$tmp_path = $_GET['tmp_path']; +$tmpkey = $_GET['tmpkey']; $maxsize = isset($_GET['maxsize']) ? $_GET['maxsize'] : -1; header("Cache-Control: no-cache, no-store, must-revalidate"); -OCP\Util::writeLog('contacts','dynphoto.php: tmp_path: '.$tmp_path.', exists: '.file_exists($tmp_path), OCP\Util::DEBUG); +OCP\Util::writeLog('contacts', 'tmpphoto.php: tmpkey: '.$tmpkey, OCP\Util::DEBUG); -$image = new OC_Image($tmp_path); +$image = new OC_Image(); +$image->loadFromData(OC_Cache::get($tmpkey)); if($maxsize != -1) { $image->resize($maxsize); } diff --git a/apps/external/ajax/setsites.php b/apps/external/ajax/setsites.php index 0dbac3abb29..0f9e061d0e0 100644 --- a/apps/external/ajax/setsites.php +++ b/apps/external/ajax/setsites.php @@ -1,7 +1,7 @@ <?php /** - * Copyright (c) 2011, Frank Karlitschek <karlitschek@kde.org> + * 2012 Frank Karlitschek frank@owncloud.org * This file is licensed under the Affero General Public License version 3 or later. * See the COPYING-README file. */ @@ -23,4 +23,3 @@ else OCP\Config::setAppValue('external', 'sites', json_encode($sites)); echo 'true'; -?> diff --git a/apps/external/appinfo/app.php b/apps/external/appinfo/app.php index b569fc305ba..1a02f3a1be8 100644 --- a/apps/external/appinfo/app.php +++ b/apps/external/appinfo/app.php @@ -4,7 +4,7 @@ * ownCloud - External plugin * * @author Frank Karlitschek - * @copyright 2011 Frank Karlitschek karlitschek@kde.org + * @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -26,8 +26,6 @@ OCP\Util::addStyle( 'external', 'style'); OCP\App::registerAdmin('external', 'settings'); -OCP\App::register(array('order' => 70, 'id' => 'external', 'name' => 'External')); - $sites = OC_External::getSites(); for ($i = 0; $i < sizeof($sites); $i++) { OCP\App::addNavigationEntry( diff --git a/apps/external/img/external.png b/apps/external/img/external.png Binary files differindex 75d1366326b..9e56f2919fd 100644 --- a/apps/external/img/external.png +++ b/apps/external/img/external.png diff --git a/apps/external/index.php b/apps/external/index.php index d63be3ad1d5..3b6f06e1bff 100644 --- a/apps/external/index.php +++ b/apps/external/index.php @@ -4,7 +4,7 @@ * ownCloud - External plugin * * @author Frank Karlitschek - * @copyright 2011 Frank Karlitschek karlitschek@kde.org + * @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -40,4 +40,3 @@ if (isset($_GET['id'])) { $tmpl->printPage(); } } -?> diff --git a/apps/external/lib/external.php b/apps/external/lib/external.php index 9fff5d5569a..d13a7cf6dd7 100644 --- a/apps/external/lib/external.php +++ b/apps/external/lib/external.php @@ -32,5 +32,3 @@ class OC_External { } } - -?> diff --git a/apps/external/settings.php b/apps/external/settings.php index c109733e548..488444c119a 100644 --- a/apps/external/settings.php +++ b/apps/external/settings.php @@ -7,4 +7,3 @@ OCP\Util::addscript( "external", "admin" ); $tmpl = new OCP\Template( 'external', 'settings'); return $tmpl->fetchPage(); -?> diff --git a/apps/files/admin.php b/apps/files/admin.php index b0c2b35c696..d05eb7267b7 100644 --- a/apps/files/admin.php +++ b/apps/files/admin.php @@ -51,8 +51,10 @@ $allowZipDownload = intval(OCP\Config::getSystemValue('allowZipDownload', true)) OCP\App::setActiveNavigationEntry( "files_administration" ); +$htaccessWritable=is_writable(OC::$SERVERROOT.'/.htaccess'); + $tmpl = new OCP\Template( 'files', 'admin' ); -$tmpl->assign( 'htaccessWorking', $htaccessWorking ); +$tmpl->assign( 'uploadChangable', $htaccessWorking and $htaccessWritable ); $tmpl->assign( 'uploadMaxFilesize', $maxUploadFilesize); $tmpl->assign( 'maxPossibleUploadSize', OCP\Util::humanFileSize(PHP_INT_MAX)); $tmpl->assign( 'allowZipDownload', $allowZipDownload); diff --git a/apps/files/ajax/autocomplete.php b/apps/files/ajax/autocomplete.php index 7ff34da96b3..e504bb24bf8 100644 --- a/apps/files/ajax/autocomplete.php +++ b/apps/files/ajax/autocomplete.php @@ -52,5 +52,3 @@ if(OC_Filesystem::file_exists($base) and OC_Filesystem::is_dir($base)){ } } OCP\JSON::encodedPrint($files); - -?> diff --git a/apps/files/ajax/delete.php b/apps/files/ajax/delete.php index 161d820f735..695f803884e 100644 --- a/apps/files/ajax/delete.php +++ b/apps/files/ajax/delete.php @@ -26,5 +26,3 @@ if($success) { } else { OCP\JSON::error(array("data" => array( "message" => "Could not delete:\n" . $filesWithError ))); } - -?> diff --git a/apps/files/ajax/download.php b/apps/files/ajax/download.php index e9373f5f6ac..b9a4ddaf5e7 100644 --- a/apps/files/ajax/download.php +++ b/apps/files/ajax/download.php @@ -34,4 +34,3 @@ $files = $_GET["files"]; $dir = $_GET["dir"]; OC_Files::get($dir, $files, $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false); -?> diff --git a/apps/files/ajax/list.php b/apps/files/ajax/list.php index ceb8d158580..dae0c1a828d 100644 --- a/apps/files/ajax/list.php +++ b/apps/files/ajax/list.php @@ -38,9 +38,7 @@ foreach( OC_Files::getdirectorycontent( $dir ) as $i ){ } $list = new OCP\Template( "files", "part.list", "" ); -$list->assign( "files", $files ); +$list->assign( "files", $files, false ); $data = array('files' => $list->fetchPage()); OCP\JSON::success(array('data' => $data)); - -?> diff --git a/apps/files/ajax/mimeicon.php b/apps/files/ajax/mimeicon.php index 57898cd82d9..dbb8b60112a 100644 --- a/apps/files/ajax/mimeicon.php +++ b/apps/files/ajax/mimeicon.php @@ -1,11 +1,3 @@ <?php -// no need for apps -$RUNTIME_NOAPPS=false; - -// Init owncloud - - print OC_Helper::mimetypeIcon($_GET['mime']); - -?> diff --git a/apps/files/ajax/move.php b/apps/files/ajax/move.php index 56171dd0ed3..3d4003a8edc 100644 --- a/apps/files/ajax/move.php +++ b/apps/files/ajax/move.php @@ -17,5 +17,3 @@ if(OC_Files::move($dir,$file,$target,$file)){ }else{ OCP\JSON::error(array("data" => array( "message" => "Could not move $file" ))); } - -?> diff --git a/apps/files/ajax/newfile.php b/apps/files/ajax/newfile.php index 7236deb65c9..cc9208ad08f 100644 --- a/apps/files/ajax/newfile.php +++ b/apps/files/ajax/newfile.php @@ -1,16 +1,25 @@ <?php // Init owncloud +global $eventSource; +if(!OC_User::isLoggedIn()){ + exit; +} -OCP\JSON::checkLoggedIn(); -OCP\JSON::callCheck(); +session_write_close(); // Get the params -$dir = isset( $_POST['dir'] ) ? stripslashes($_POST['dir']) : ''; -$filename = isset( $_POST['filename'] ) ? stripslashes($_POST['filename']) : ''; -$content = isset( $_POST['content'] ) ? $_POST['content'] : ''; -$source = isset( $_POST['source'] ) ? stripslashes($_POST['source']) : ''; +$dir = isset( $_REQUEST['dir'] ) ? stripslashes($_REQUEST['dir']) : ''; +$filename = isset( $_REQUEST['filename'] ) ? stripslashes($_REQUEST['filename']) : ''; +$content = isset( $_REQUEST['content'] ) ? $_REQUEST['content'] : ''; +$source = isset( $_REQUEST['source'] ) ? stripslashes($_REQUEST['source']) : ''; + +if($source){ + $eventSource=new OC_EventSource(); +}else{ + OC_JSON::callCheck(); +} if($filename == '') { OCP\JSON::error(array("data" => array( "message" => "Empty Filename" ))); @@ -21,22 +30,49 @@ if(strpos($filename,'/')!==false){ exit(); } +function progress($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max){ + static $filesize = 0; + static $lastsize = 0; + global $eventSource; + + switch($notification_code) { + case STREAM_NOTIFY_FILE_SIZE_IS: + $filesize = $bytes_max; + break; + + case STREAM_NOTIFY_PROGRESS: + if ($bytes_transferred > 0) { + if (!isset($filesize)) { + } else { + $progress = (int)(($bytes_transferred/$filesize)*100); + if($progress>$lastsize){//limit the number or messages send + $eventSource->send('progress',$progress); + } + $lastsize=$progress; + } + } + break; + } +} + if($source){ if(substr($source,0,8)!='https://' and substr($source,0,7)!='http://'){ OCP\JSON::error(array("data" => array( "message" => "Not a valid source" ))); exit(); } - $sourceStream=fopen($source,'rb'); + + $ctx = stream_context_create(null, array('notification' =>'progress')); + $sourceStream=fopen($source,'rb', false, $ctx); $target=$dir.'/'.$filename; $result=OC_Filesystem::file_put_contents($target,$sourceStream); if($result){ $mime=OC_Filesystem::getMimetype($target); - OCP\JSON::success(array("data" => array('mime'=>$mime))); - exit(); + $eventSource->send('success',$mime); }else{ - OCP\JSON::error(array("data" => array( "message" => "Error while downloading ".$source. ' to '.$target ))); - exit(); + $eventSource->send('error',"Error while downloading ".$source. ' to '.$target); } + $eventSource->close(); + exit(); }else{ if($content){ if(OC_Filesystem::file_put_contents($dir.'/'.$filename,$content)){ diff --git a/apps/files/ajax/rawlist.php b/apps/files/ajax/rawlist.php index 7cb02f79673..d159f6e152f 100644 --- a/apps/files/ajax/rawlist.php +++ b/apps/files/ajax/rawlist.php @@ -22,5 +22,3 @@ foreach( OC_Files::getdirectorycontent( $dir, $mimetype ) as $i ){ } OCP\JSON::success(array('data' => $files)); - -?> diff --git a/apps/files/ajax/rename.php b/apps/files/ajax/rename.php index 8e98308eb5c..45448279fa1 100644 --- a/apps/files/ajax/rename.php +++ b/apps/files/ajax/rename.php @@ -18,5 +18,3 @@ if( OC_Files::move( $dir, $file, $dir, $newname )) { else{ OCP\JSON::error(array("data" => array( "message" => "Unable to rename file" ))); } - -?> diff --git a/apps/files/ajax/scan.php b/apps/files/ajax/scan.php index d695ce81617..eef38858516 100644 --- a/apps/files/ajax/scan.php +++ b/apps/files/ajax/scan.php @@ -10,11 +10,17 @@ if(!$checkOnly){ $eventSource=new OC_EventSource(); } +session_write_close(); //create the file cache if necesary if($force or !OC_FileCache::inCache('')){ if(!$checkOnly){ OCP\DB::beginTransaction(); + + if(OC_Cache::isFast()){ + OC_Cache::clear('fileid/'); //make sure the old fileid's don't mess things up + } + OC_FileCache::scan($dir,$eventSource); OC_FileCache::clean(); OCP\DB::commit(); diff --git a/apps/files/ajax/timezone.php b/apps/files/ajax/timezone.php index 268ae594835..0be441a36a2 100644 --- a/apps/files/ajax/timezone.php +++ b/apps/files/ajax/timezone.php @@ -1,6 +1,6 @@ <?php // FIXME: this should start a secure session if forcessl is enabled // see lib/base.php for an example - @session_start(); + //session_start(); $_SESSION['timezone'] = $_GET['time']; -?> +
\ No newline at end of file diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php index d6c799af32c..74e6eb560d8 100644 --- a/apps/files/ajax/upload.php +++ b/apps/files/ajax/upload.php @@ -7,6 +7,7 @@ OCP\JSON::setContentTypeHeader('text/plain'); OCP\JSON::checkLoggedIn(); +OCP\JSON::callCheck(); if (!isset($_FILES['files'])) { OCP\JSON::error(array("data" => array( "message" => "No file was uploaded. Unknown error" ))); @@ -48,7 +49,7 @@ if(strpos($dir,'..') === false){ for($i=0;$i<$fileCount;$i++){ $target = OCP\Files::buildNotExistingFileName(stripslashes($dir), $files['name'][$i]); if(is_uploaded_file($files['tmp_name'][$i]) and OC_Filesystem::fromTmpFile($files['tmp_name'][$i],$target)){ - $meta=OC_FileCache::getCached($target); + $meta=OC_FileCache_Cached::get($target); $result[]=array( "status" => "success", 'mime'=>$meta['mimetype'],'size'=>$meta['size'],'name'=>basename($target)); } } @@ -59,5 +60,3 @@ if(strpos($dir,'..') === false){ } OCP\JSON::error(array('data' => array('error' => $error, "file" => $fileName))); - -?> diff --git a/apps/files/appinfo/app.php b/apps/files/appinfo/app.php index 5c0d3c8db83..db3b213ab8d 100644 --- a/apps/files/appinfo/app.php +++ b/apps/files/appinfo/app.php @@ -1,7 +1,6 @@ <?php $l=OC_L10N::get('files'); -OCP\App::register( array( "order" => 2, "id" => "files", "name" => "Files" )); OCP\App::registerAdmin('files','admin'); OCP\App::addNavigationEntry( array( "id" => "files_index", "order" => 0, "href" => OCP\Util::linkTo( "files", "index.php" ), "icon" => OCP\Util::imagePath( "core", "places/home.svg" ), "name" => $l->t("Files") )); diff --git a/apps/files/appinfo/filesync.php b/apps/files/appinfo/filesync.php new file mode 100644 index 00000000000..707ee2435c0 --- /dev/null +++ b/apps/files/appinfo/filesync.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +/** + * filesync can be called with a PUT method. + * PUT takes a stream starting with a 2 byte blocksize, + * followed by binary md5 of the blocks. Everything in big-endian. + * The return is a json encoded with: + * - 'transferid' + * - 'needed' chunks + * - 'last' checked chunk + * The URL is made of 3 parts, the service url (remote.php/filesync/), the sync + * type and the path in ownCloud. + * At the moment the only supported sync type is 'oc_chunked'. + * The final URL will look like http://.../remote.php/filesync/oc_chunked/path/to/file + */ + +// only need filesystem apps +$RUNTIME_APPTYPES=array('filesystem','authentication'); +OC_App::loadApps($RUNTIME_APPTYPES); +if(!OC_User::isLoggedIn()){ + if(!isset($_SERVER['PHP_AUTH_USER'])){ + header('WWW-Authenticate: Basic realm="ownCloud Server"'); + header('HTTP/1.0 401 Unauthorized'); + echo 'Valid credentials must be supplied'; + exit(); + } else { + if(!OC_User::login($_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"])){ + exit(); + } + } +} + +list($type,$file) = explode('/', substr($path_info,1+strlen($service)+1), 2); + +if ($type != 'oc_chunked') { + OC_Response::setStatus(OC_Response::STATUS_NOT_FOUND); + die; +} + +if (!OC_Filesystem::is_file($file)) { + OC_Response::setStatus(OC_Response::STATUS_NOT_FOUND); + die; +} + +switch($_SERVER['REQUEST_METHOD']) { + case 'PUT': + $input = fopen("php://input", "r"); + $org_file = OC_Filesystem::fopen($file, 'rb'); + $info = array( + 'name' => basename($file), + ); + $sync = new OC_FileChunking($info); + $result = $sync->signature_split($org_file, $input); + echo json_encode($result); + break; + default: + OC_Response::setStatus(OC_Response::STATUS_NOT_FOUND); +} diff --git a/apps/files/appinfo/info.xml b/apps/files/appinfo/info.xml index 105df092ce5..e58f83c5a01 100644 --- a/apps/files/appinfo/info.xml +++ b/apps/files/appinfo/info.xml @@ -15,5 +15,6 @@ <remote> <files>appinfo/remote.php</files> <webdav>appinfo/remote.php</webdav> + <filesync>appinfo/filesync.php</filesync> </remote> </info> diff --git a/apps/files/appinfo/remote.php b/apps/files/appinfo/remote.php index b66843556bb..a84216b61b7 100644 --- a/apps/files/appinfo/remote.php +++ b/apps/files/appinfo/remote.php @@ -5,7 +5,7 @@ * * @author Frank Karlitschek * @author Jakob Sack - * @copyright 2010 Frank Karlitschek karlitschek@kde.org + * @copyright 2012 Frank Karlitschek frank@owncloud.org * @copyright 2011 Jakob Sack kde@jakobsack.de * * This library is free software; you can redistribute it and/or diff --git a/apps/files/appinfo/update.php b/apps/files/appinfo/update.php index c3e51debb52..5514aed197f 100644 --- a/apps/files/appinfo/update.php +++ b/apps/files/appinfo/update.php @@ -1,13 +1,13 @@ <?php -// fix webdav properties, remove namespace information between curly bracket update for OC4 -$installedVersion=OCP\Config::getAppValue('files', 'installed_version'); -if (version_compare($installedVersion, '1.1.2', '<')) { - $query = OC_DB::prepare( "SELECT propertyname, propertypath, userid FROM `*PREFIX*properties`" ); - $result = $query->execute(); - while( $row = $result->fetchRow()){ - $query = OC_DB::prepare( 'UPDATE `*PREFIX*properties` SET `propertyname` = ? WHERE `userid` = ? AND `propertypath` = ?' ); - $query->execute( array( preg_replace("/^{.*}/", "", $row["propertyname"]),$row["userid"], $row["propertypath"] )); +// fix webdav properties, remove namespace information between curly bracket (update from OC4 to OC5) +$installedVersion=OCP\Config::getAppValue('files', 'installed_version');
+if (version_compare($installedVersion, '1.1.4', '<')) { + $query = OC_DB::prepare( "SELECT propertyname, propertypath, userid FROM `*PREFIX*properties`" );
+ $result = $query->execute();
+ while( $row = $result->fetchRow()){
+ $query = OC_DB::prepare( 'UPDATE *PREFIX*properties SET propertyname = ? WHERE userid = ? AND propertypath = ?' );
+ $query->execute( array( preg_replace("/^{.*}/", "", $row["propertyname"]),$row["userid"], $row["propertypath"] ));
} } diff --git a/apps/files/appinfo/version b/apps/files/appinfo/version index 8428158dc5b..e25d8d9f357 100644 --- a/apps/files/appinfo/version +++ b/apps/files/appinfo/version @@ -1 +1 @@ -1.1.2
\ No newline at end of file +1.1.5 diff --git a/apps/files/css/files.css b/apps/files/css/files.css index dc298e4d440..7298a7ef6e6 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -88,4 +88,4 @@ a.action>img { max-height:16px; max-width:16px; vertical-align:text-bottom; } #navigation>ul>li:first-child { background:url('%webroot%/core/img/breadcrumb-start.svg') no-repeat 12.5em 0px; width:12.5em; padding-right:1em; position:fixed; } #navigation>ul>li:first-child+li { padding-top:2.9em; } -#scanning-message{ top:40%; left:40%; position:absolute; display:none; } +#scanning-message{ top:40%; left:40%; position:absolute; display:none; }
\ No newline at end of file diff --git a/apps/files/download.php b/apps/files/download.php index 2b5d4e2d876..4e2478d1ad7 100644 --- a/apps/files/download.php +++ b/apps/files/download.php @@ -46,4 +46,3 @@ header('Content-Length: '.OC_Filesystem::filesize($filename)); @ob_end_clean(); OC_Filesystem::readfile( $filename ); -?> diff --git a/apps/files/index.php b/apps/files/index.php index f6a1c4bfb4c..6d53527025a 100644 --- a/apps/files/index.php +++ b/apps/files/index.php @@ -74,12 +74,12 @@ foreach( explode( '/', $dir ) as $i ){ // make breadcrumb und filelist markup $list = new OCP\Template( 'files', 'part.list', '' ); -$list->assign( 'files', $files ); -$list->assign( 'baseURL', OCP\Util::linkTo('files', 'index.php').'&dir='); -$list->assign( 'downloadURL', OCP\Util::linkTo('files', 'download.php').'?file='); +$list->assign( 'files', $files, false ); +$list->assign( 'baseURL', OCP\Util::linkTo('files', 'index.php').'&dir=', false); +$list->assign( 'downloadURL', OCP\Util::linkTo('files', 'download.php').'?file=', false); $breadcrumbNav = new OCP\Template( 'files', 'part.breadcrumb', '' ); -$breadcrumbNav->assign( 'breadcrumb', $breadcrumb ); -$breadcrumbNav->assign( 'baseURL', OCP\Util::linkTo('files', 'index.php').'&dir='); +$breadcrumbNav->assign( 'breadcrumb', $breadcrumb, false ); +$breadcrumbNav->assign( 'baseURL', OCP\Util::linkTo('files', 'index.php').'&dir=', false); $upload_max_filesize = OCP\Util::computerFileSize(ini_get('upload_max_filesize')); $post_max_size = OCP\Util::computerFileSize(ini_get('post_max_size')); @@ -90,14 +90,12 @@ $freeSpace=max($freeSpace,0); $maxUploadFilesize = min($maxUploadFilesize ,$freeSpace); $tmpl = new OCP\Template( 'files', 'index', 'user' ); -$tmpl->assign( 'fileList', $list->fetchPage() ); -$tmpl->assign( 'breadcrumb', $breadcrumbNav->fetchPage() ); -$tmpl->assign( 'dir', $dir); -$tmpl->assign( 'readonly', !OC_Filesystem::is_writable($dir.'/')); +$tmpl->assign( 'fileList', $list->fetchPage(), false ); +$tmpl->assign( 'breadcrumb', $breadcrumbNav->fetchPage(), false ); +$tmpl->assign( 'dir', OC_Filesystem::normalizePath($dir)); +$tmpl->assign( 'isCreatable', OC_Filesystem::isCreatable($dir.'/')); $tmpl->assign( 'files', $files ); $tmpl->assign( 'uploadMaxFilesize', $maxUploadFilesize); $tmpl->assign( 'uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize)); $tmpl->assign( 'allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); $tmpl->printPage(); - -?> diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index b6f4d0b0896..39e848cec80 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -1,19 +1,28 @@ FileActions={ + PERMISSION_CREATE:4, + PERMISSION_READ:1, + PERMISSION_UPDATE:2, + PERMISSION_DELETE:8, + PERMISSION_SHARE:16, actions:{}, defaults:{}, icons:{}, currentFile:null, - register:function(mime,name,icon,action){ + register:function(mime,name,permissions,icon,action){ if(!FileActions.actions[mime]){ FileActions.actions[mime]={}; } - FileActions.actions[mime][name]=action; + if (!FileActions.actions[mime][name]) { + FileActions.actions[mime][name] = {}; + } + FileActions.actions[mime][name]['action'] = action; + FileActions.actions[mime][name]['permissions'] = permissions; FileActions.icons[name]=icon; }, setDefault:function(mime,name){ FileActions.defaults[mime]=name; }, - get:function(mime,type){ + get:function(mime,type,permissions){ var actions={}; if(FileActions.actions.all){ actions=$.extend( actions, FileActions.actions.all ) @@ -32,9 +41,15 @@ FileActions={ actions=$.extend( actions, FileActions.actions[type] ) } } - return actions; + var filteredActions = {}; + $.each(actions, function(name, action) { + if (action.permissions & permissions) { + filteredActions[name] = action.action; + } + }); + return filteredActions; }, - getDefault:function(mime,type){ + getDefault:function(mime,type,permissions){ if(mime){ var mimePart=mime.substr(0,mime.indexOf('/')); } @@ -48,22 +63,20 @@ FileActions={ }else{ name=FileActions.defaults.all; } - var actions=this.get(mime,type); + var actions=this.get(mime,type,permissions); return actions[name]; }, - display:function(parent, filename, type){ + display:function(parent){ FileActions.currentFile=parent; $('#fileList span.fileactions, #fileList td.date a.action').remove(); - var actions=FileActions.get(FileActions.getCurrentMimeType(),FileActions.getCurrentType()); + var actions=FileActions.get(FileActions.getCurrentMimeType(),FileActions.getCurrentType(), FileActions.getCurrentPermissions()); var file=FileActions.getCurrentFile(); if($('tr').filterAttr('data-file',file).data('renaming')){ return; } parent.children('a.name').append('<span class="fileactions" />'); - var defaultAction=FileActions.getDefault(FileActions.getCurrentMimeType(),FileActions.getCurrentType()); + var defaultAction=FileActions.getDefault(FileActions.getCurrentMimeType(),FileActions.getCurrentType(), FileActions.getCurrentPermissions()); for(name in actions){ - // no rename and share action for the 'Shared' dir - if((name=='Rename' || name =='Share') && type=='dir' && filename=='Shared') { continue; } if((name=='Download' || actions[name]!=defaultAction) && name!='Delete'){ var img=FileActions.icons[name]; if(img.call){ @@ -86,16 +99,12 @@ FileActions={ parent.find('a.name>span.fileactions').append(element); } } - if(actions['Delete'] && (type!='dir' || filename != 'Shared')){ // no delete action for the 'Shared' dir + if(actions['Delete']){ var img=FileActions.icons['Delete']; if(img.call){ img=img(file); } - if ($('#dir').val().indexOf('Shared') != -1) { - var html='<a href="#" original-title="' + t('files', 'Unshare') + '" class="action delete" style="display:none" />'; - } else { - var html='<a href="#" original-title="' + t('files', 'Delete') + '" class="action delete" style="display:none" />'; - } + var html='<a href="#" original-title="' + t('files', 'Delete') + '" class="action delete" style="display:none" />'; var element=$(html); if(img){ element.append($('<img src="'+img+'"/>')); @@ -131,6 +140,9 @@ FileActions={ }, getCurrentType:function(){ return FileActions.currentFile.parent().attr('data-type'); + }, + getCurrentPermissions:function() { + return FileActions.currentFile.parent().data('permissions'); } } @@ -140,12 +152,12 @@ $(document).ready(function(){ } else { var downloadScope = 'file'; } - FileActions.register(downloadScope,'Download',function(){return OC.imagePath('core','actions/download')},function(filename){ + FileActions.register(downloadScope,'Download', FileActions.PERMISSION_READ, function(){return OC.imagePath('core','actions/download')},function(filename){ window.location=OC.filePath('files', 'ajax', 'download.php') + encodeURIComponent('?files='+encodeURIComponent(filename)+'&dir='+encodeURIComponent($('#dir').val())); }); }); -FileActions.register('all','Delete',function(){return OC.imagePath('core','actions/delete')},function(filename){ +FileActions.register('all','Delete', FileActions.PERMISSION_DELETE, function(){return OC.imagePath('core','actions/delete')},function(filename){ if(Files.cancelUpload(filename)) { if(filename.substr){ filename=[filename]; @@ -163,11 +175,11 @@ FileActions.register('all','Delete',function(){return OC.imagePath('core','actio $('.tipsy').remove(); }); -FileActions.register('all','Rename',function(){return OC.imagePath('core','actions/rename')},function(filename){ +FileActions.register('all','Rename', FileActions.PERMISSION_UPDATE, function(){return OC.imagePath('core','actions/rename')},function(filename){ FileList.rename(filename); }); -FileActions.register('dir','Open','',function(filename){ +FileActions.register('dir','Open', FileActions.PERMISSION_READ, '', function(filename){ window.location=OC.linkTo('files', 'index.php') + '&dir='+encodeURIComponent($('#dir').val()).replace(/%2F/g, '/')+'/'+encodeURIComponent(filename); }); diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index c3eb906f39e..e0cf516411a 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -136,24 +136,39 @@ FileList={ event.stopPropagation(); event.preventDefault(); var newname=input.val(); - tr.attr('data-file',newname); - td.children('a.name').empty(); + if (newname != name) { + if ($('tr').filterAttr('data-file', newname).length > 0) { + $('#notification').html(newname+' '+t('files', 'already exists')+'<span class="replace">'+t('files', 'replace')+'</span><span class="cancel">'+t('files', 'cancel')+'</span>'); + $('#notification').data('oldName', name); + $('#notification').data('newName', newname); + $('#notification').fadeIn(); + newname = name; + } else { + $.get(OC.filePath('files','ajax','rename.php'), { dir : $('#dir').val(), newname: newname, file: name },function(result) { + if (!result || result.status == 'error') { + OC.dialogs.alert(result.data.message, 'Error moving file'); + newname = name; + } + }); + } + + } + tr.attr('data-file', newname); var path = td.children('a.name').attr('href'); td.children('a.name').attr('href', path.replace(encodeURIComponent(name), encodeURIComponent(newname))); - if(newname.indexOf('.')>0){ - basename=newname.substr(0,newname.lastIndexOf('.')); - }else{ - basename=newname; + if (newname.indexOf('.') > 0) { + var basename=newname.substr(0,newname.lastIndexOf('.')); + } else { + var basename=newname; } + td.children('a.name').empty(); var span=$('<span class="nametext"></span>'); span.text(basename); td.children('a.name').append(span); - if(newname.indexOf('.')>0){ + if (newname.indexOf('.') > 0) { span.append($('<span class="extension">'+newname.substr(newname.lastIndexOf('.'))+'</span>')); } - $.get(OC.filePath('files','ajax','rename.php'), { dir : $('#dir').val(), newname: newname, file: name },function(){ - tr.data('renaming',false); - }); + tr.data('renaming',false); return false; }); input.click(function(event){ @@ -164,19 +179,71 @@ FileList={ form.trigger('submit'); }); }, + replace:function(oldName, newName) { + // Finish any existing actions + if (FileList.lastAction || !FileList.useUndo) { + FileList.lastAction(); + } + var tr = $('tr').filterAttr('data-file', oldName); + tr.hide(); + FileList.replaceCanceled = false; + FileList.replaceOldName = oldName; + FileList.replaceNewName = newName; + FileList.lastAction = function() { + FileList.finishReplace(); + }; + $('#notification').html(t('files', 'replaced')+' '+newName+' '+t('files', 'with')+' '+oldName+'<span class="undo">'+t('files', 'undo')+'</span>'); + $('#notification').fadeIn(); + }, + finishReplace:function() { + if (!FileList.replaceCanceled && FileList.replaceOldName && FileList.replaceNewName) { + // Delete the file being replaced and rename the replacement + FileList.deleteCanceled = false; + FileList.deleteFiles = [FileList.replaceNewName]; + FileList.finishDelete(function() { + $.ajax({url: OC.filePath('files', 'ajax', 'rename.php'), async: false, data: { dir: $('#dir').val(), newname: FileList.replaceNewName, file: FileList.replaceOldName }, success: function(result) { + if (result && result.status == 'success') { + var tr = $('tr').filterAttr('data-file', FileList.replaceOldName); + tr.attr('data-file', FileList.replaceNewName); + var td = tr.children('td.filename'); + td.children('a.name .span').text(FileList.replaceNewName); + var path = td.children('a.name').attr('href'); + td.children('a.name').attr('href', path.replace(encodeURIComponent(FileList.replaceOldName), encodeURIComponent(FileList.replaceNewName))); + if (FileList.replaceNewName.indexOf('.') > 0) { + var basename = FileList.replaceNewName.substr(0, FileList.replaceNewName.lastIndexOf('.')); + } else { + var basename = FileList.replaceNewName; + } + td.children('a.name').empty(); + var span = $('<span class="nametext"></span>'); + span.text(basename); + td.children('a.name').append(span); + if (FileList.replaceNewName.indexOf('.') > 0) { + span.append($('<span class="extension">'+FileList.replaceNewName.substr(FileList.replaceNewName.lastIndexOf('.'))+'</span>')); + } + tr.show(); + } else { + OC.dialogs.alert(result.data.message, 'Error moving file'); + } + FileList.replaceCanceled = true; + FileList.replaceOldName = null; + FileList.replaceNewName = null; + FileList.lastAction = null; + }}); + }, true); + } + }, do_delete:function(files){ - if(FileList.deleteFiles || !FileList.useUndo){//finish any ongoing deletes first + // Finish any existing actions + if (FileList.lastAction || !FileList.useUndo) { if(!FileList.deleteFiles) { FileList.prepareDeletion(files); } - FileList.finishDelete(function(){ - FileList.do_delete(files); - }); + FileList.lastAction(); return; } FileList.prepareDeletion(files); - $('#notification').text(t('files','undo deletion')); - $('#notification').data('deletefile',true); + $('#notification').html(t('files', 'deleted')+' '+files+'<span class="undo">'+t('files', 'undo')+'</span>'); $('#notification').fadeIn(); }, finishDelete:function(ready,sync){ @@ -194,6 +261,7 @@ FileList={ }); FileList.deleteCanceled=true; FileList.deleteFiles=null; + FileList.lastAction = null; if(ready){ ready(); } @@ -215,25 +283,42 @@ FileList={ procesSelection(); FileList.deleteCanceled=false; FileList.deleteFiles=files; + FileList.lastAction = function() { + FileList.finishDelete(null, true); + }; } } $(document).ready(function(){ $('#notification').hide(); - $('#notification').click(function(){ - if($('#notification').data('deletefile')) - { + $('#notification .undo').live('click', function(){ + if (FileList.deleteFiles) { $.each(FileList.deleteFiles,function(index,file){ $('tr').filterAttr('data-file',file).show(); -// alert(file); }); FileList.deleteCanceled=true; FileList.deleteFiles=null; + } else if (FileList.replaceOldName && FileList.replaceNewName) { + $('tr').filterAttr('data-file', FileList.replaceOldName).show(); + FileList.replaceCanceled = true; + FileList.replaceOldName = null; + FileList.replaceNewName = null; } + FileList.lastAction = null; + $('#notification').fadeOut(); + }); + $('#notification .replace').live('click', function() { + $('#notification').fadeOut('400', function() { + FileList.replace($('#notification').data('oldName'), $('#notification').data('newName')); + }); + }); + $('#notification .cancel').live('click', function() { $('#notification').fadeOut(); }); FileList.useUndo=('onbeforeunload' in window) $(window).bind('beforeunload', function (){ - FileList.finishDelete(null,true); + if (FileList.lastAction) { + FileList.lastAction(); + } }); }); diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 46dbe28f049..049afea4f61 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -56,7 +56,7 @@ $(document).ready(function() { // Sets the file-action buttons behaviour : $('tr').live('mouseenter',function(event) { - FileActions.display($(this).children('td.filename'), $(this).attr('data-file'), $(this).attr('data-type')); + FileActions.display($(this).children('td.filename')); }); $('tr').live('mouseleave',function(event) { FileActions.hide(); @@ -106,7 +106,8 @@ $(document).ready(function() { if(!renaming && !FileList.isLoading(filename)){ var mime=$(this).parent().parent().data('mime'); var type=$(this).parent().parent().data('type'); - var action=FileActions.getDefault(mime,type); + var permissions = $(this).parent().parent().data('permissions'); + var action=FileActions.getDefault(mime,type, permissions); if(action){ action(filename); } @@ -194,6 +195,11 @@ $(document).ready(function() { var totalSize=0; if(files){ for(var i=0;i<files.length;i++){ + if(files[i].size ==0 && files[i].type== '') + { + OC.dialogs.alert(t('files', 'Unable to upload your file as it is a directory or has 0 bytes'), t('files', 'Upload Error')); + return; + } totalSize+=files[i].size; if(FileList.deleteFiles && FileList.deleteFiles.indexOf(files[i].name)!=-1){//finish delete if we are uploading a deleted file FileList.finishDelete(function(){ @@ -213,115 +219,6 @@ $(document).ready(function() { } }); }else{ - if($.support.xhrFileUpload) { - for(var i=0;i<files.length;i++){ - var fileName = files[i].name - var dropTarget = $(e.originalEvent.target).closest('tr'); - if(dropTarget && dropTarget.attr('data-type') === 'dir') { // drag&drop upload to folder - var dirName = dropTarget.attr('data-file') - var jqXHR = $('.file_upload_start').fileupload('send', {files: files[i], - formData: function(form) { - var formArray = form.serializeArray(); - formArray[1]['value'] = dirName; - return formArray; - }}).success(function(result, textStatus, jqXHR) { - var response; - response=jQuery.parseJSON(result); - if(response[0] == undefined || response[0].status != 'success') { - $('#notification').text(t('files', response.data.message)); - $('#notification').fadeIn(); - } - var file=response[0]; - delete uploadingFiles[dirName][file.name]; - var currentUploads = parseInt(uploadtext.attr('currentUploads')); - currentUploads -= 1; - uploadtext.attr('currentUploads', currentUploads); - if(currentUploads === 0) { - var img = OC.imagePath('core', 'filetypes/folder.png'); - var tr=$('tr').filterAttr('data-file',dirName); - tr.find('td.filename').attr('style','background-image:url('+img+')'); - uploadtext.text(''); - uploadtext.hide(); - } else { - uploadtext.text(currentUploads + ' files uploading') - } - }) - .error(function(jqXHR, textStatus, errorThrown) { - if(errorThrown === 'abort') { - var currentUploads = parseInt(uploadtext.attr('currentUploads')); - currentUploads -= 1; - uploadtext.attr('currentUploads', currentUploads); - if(currentUploads === 0) { - var img = OC.imagePath('core', 'filetypes/folder.png'); - var tr=$('tr').filterAttr('data-file',dirName); - tr.find('td.filename').attr('style','background-image:url('+img+')'); - uploadtext.text(''); - uploadtext.hide(); - } else { - uploadtext.text(currentUploads + ' files uploading') - } - $('#notification').hide(); - $('#notification').text(t('files', 'Upload cancelled.')); - $('#notification').fadeIn(); - } - }); - //TODO test with filenames containing slashes - if(uploadingFiles[dirName] === undefined) { - uploadingFiles[dirName] = {}; - } - uploadingFiles[dirName][fileName] = jqXHR; - } else { - var jqXHR = $('.file_upload_start').fileupload('send', {files: files[i]}) - .success(function(result, textStatus, jqXHR) { - var response; - response=jQuery.parseJSON(result); - if(response[0] != undefined && response[0].status == 'success') { - var file=response[0]; - delete uploadingFiles[file.name]; - $('tr').filterAttr('data-file',file.name).data('mime',file.mime); - var size = $('tr').filterAttr('data-file',file.name).find('td.filesize').text(); - if(size==t('files','Pending')){ - $('tr').filterAttr('data-file',file.name).find('td.filesize').text(file.size); - } - FileList.loadingDone(file.name); - } else { - $('#notification').text(t('files', response.data.message)); - $('#notification').fadeIn(); - $('#fileList > tr').not('[data-mime]').fadeOut(); - $('#fileList > tr').not('[data-mime]').remove(); - } - }) - .error(function(jqXHR, textStatus, errorThrown) { - if(errorThrown === 'abort') { - $('#notification').hide(); - $('#notification').text(t('files', 'Upload cancelled.')); - $('#notification').fadeIn(); - } - }); - uploadingFiles[files[i].name] = jqXHR; - } - } - }else{ - data.submit().success(function(data, status) { - response = jQuery.parseJSON(data[0].body.innerText); - if(response[0] != undefined && response[0].status == 'success') { - var file=response[0]; - delete uploadingFiles[file.name]; - $('tr').filterAttr('data-file',file.name).data('mime',file.mime); - var size = $('tr').filterAttr('data-file',file.name).find('td.filesize').text(); - if(size==t('files','Pending')){ - $('tr').filterAttr('data-file',file.name).find('td.filesize').text(file.size); - } - FileList.loadingDone(file.name); - } else { - $('#notification').text(t('files', response.data.message)); - $('#notification').fadeIn(); - $('#fileList > tr').not('[data-mime]').fadeOut(); - $('#fileList > tr').not('[data-mime]').remove(); - } - }); - } - var date=new Date(); if(files){ for(var i=0;i<files.length;i++){ @@ -331,7 +228,7 @@ $(document).ready(function() { var size=t('files','Pending'); } if(files && !dirName){ - FileList.addFile(getUniqueName(files[i].name),size,date,true); + FileList.addFile(getUniqueName(files[i].name),size,date,true); } else if(dirName) { var uploadtext = $('tr').filterAttr('data-type', 'dir').filterAttr('data-file', dirName).find('.uploadtext') var currentUploads = parseInt(uploadtext.attr('currentUploads')); @@ -350,7 +247,115 @@ $(document).ready(function() { } }else{ var filename=this.value.split('\\').pop(); //ie prepends C:\fakepath\ in front of the filename - FileList.addFile(getUniqueName(filename),'Pending',date,true); + FileList.addFile(getUniqueName(filename),'Pending',date,true); + } + if($.support.xhrFileUpload) { + for(var i=0;i<files.length;i++){ + var fileName = files[i].name + var dropTarget = $(e.originalEvent.target).closest('tr'); + if(dropTarget && dropTarget.attr('data-type') === 'dir') { // drag&drop upload to folder + var dirName = dropTarget.attr('data-file') + var jqXHR = $('.file_upload_start').fileupload('send', {files: files[i], + formData: function(form) { + var formArray = form.serializeArray(); + formArray[1]['value'] = dirName; + return formArray; + }}).success(function(result, textStatus, jqXHR) { + var response; + response=jQuery.parseJSON(result); + if(response[0] == undefined || response[0].status != 'success') { + $('#notification').text(t('files', response.data.message)); + $('#notification').fadeIn(); + } + var file=response[0]; + delete uploadingFiles[dirName][file.name]; + var currentUploads = parseInt(uploadtext.attr('currentUploads')); + currentUploads -= 1; + uploadtext.attr('currentUploads', currentUploads); + if(currentUploads === 0) { + var img = OC.imagePath('core', 'filetypes/folder.png'); + var tr=$('tr').filterAttr('data-file',dirName); + tr.find('td.filename').attr('style','background-image:url('+img+')'); + uploadtext.text(''); + uploadtext.hide(); + } else { + uploadtext.text(currentUploads + ' files uploading') + } + }) + .error(function(jqXHR, textStatus, errorThrown) { + if(errorThrown === 'abort') { + var currentUploads = parseInt(uploadtext.attr('currentUploads')); + currentUploads -= 1; + uploadtext.attr('currentUploads', currentUploads); + if(currentUploads === 0) { + var img = OC.imagePath('core', 'filetypes/folder.png'); + var tr=$('tr').filterAttr('data-file',dirName); + tr.find('td.filename').attr('style','background-image:url('+img+')'); + uploadtext.text(''); + uploadtext.hide(); + } else { + uploadtext.text(currentUploads + ' files uploading') + } + $('#notification').hide(); + $('#notification').text(t('files', 'Upload cancelled.')); + $('#notification').fadeIn(); + } + }); + //TODO test with filenames containing slashes + if(uploadingFiles[dirName] === undefined) { + uploadingFiles[dirName] = {}; + } + uploadingFiles[dirName][fileName] = jqXHR; + } else { + var jqXHR = $('.file_upload_start').fileupload('send', {files: files[i]}) + .success(function(result, textStatus, jqXHR) { + var response; + response=jQuery.parseJSON(result); + if(response[0] != undefined && response[0].status == 'success') { + var file=response[0]; + delete uploadingFiles[file.name]; + $('tr').filterAttr('data-file',file.name).data('mime',file.mime); + var size = $('tr').filterAttr('data-file',file.name).find('td.filesize').text(); + if(size==t('files','Pending')){ + $('tr').filterAttr('data-file',file.name).find('td.filesize').text(file.size); + } + FileList.loadingDone(file.name); + } else { + $('#notification').text(t('files', response.data.message)); + $('#notification').fadeIn(); + $('#fileList > tr').not('[data-mime]').fadeOut(); + $('#fileList > tr').not('[data-mime]').remove(); + } + }) + .error(function(jqXHR, textStatus, errorThrown) { + if(errorThrown === 'abort') { + $('#notification').hide(); + $('#notification').text(t('files', 'Upload cancelled.')); + $('#notification').fadeIn(); + } + }); + uploadingFiles[files[i].name] = jqXHR; + } + } + }else{ + data.submit().success(function(data, status) { + response = jQuery.parseJSON(data[0].body.innerText); + if(response[0] != undefined && response[0].status == 'success') { + var file=response[0]; + delete uploadingFiles[file.name]; + $('tr').filterAttr('data-file',file.name).data('mime',file.mime); + var size = $('tr').filterAttr('data-file',file.name).find('td.filesize').text(); + if(size==t('files','Pending')){ + $('tr').filterAttr('data-file',file.name).find('td.filesize').text(file.size); + } + FileList.loadingDone(file.name); + } else { + $('#notification').text(t('files', response.data.message)); + $('#notification').fadeIn(); + $('#fileList > tr').not('[data-mime]').fadeOut(); + $('#fileList > tr').not('[data-mime]').remove(); + } + }); } } }, @@ -447,7 +452,7 @@ $(document).ready(function() { $(this).append(input); input.focus(); input.change(function(){ - var name=$(this).val(); + var name=getUniqueName($(this).val()); if(type != 'web' && name.indexOf('/')!=-1){ $('#notification').text(t('files','Invalid name, \'/\' is not allowed.')); $('#notification').fadeIn(); @@ -458,14 +463,18 @@ $(document).ready(function() { $.post( OC.filePath('files','ajax','newfile.php'), {dir:$('#dir').val(),filename:name,content:" \n"}, - function(data){ - var date=new Date(); - FileList.addFile(name,0,date); - var tr=$('tr').filterAttr('data-file',name); - tr.data('mime','text/plain'); - getMimeIcon('text/plain',function(path){ - tr.find('td.filename').attr('style','background-image:url('+path+')'); - }); + function(result){ + if (result.status == 'success') { + var date=new Date(); + FileList.addFile(name,0,date); + var tr=$('tr').filterAttr('data-file',name); + tr.data('mime','text/plain'); + getMimeIcon('text/plain',function(path){ + tr.find('td.filename').attr('style','background-image:url('+path+')'); + }); + } else { + OC.dialogs.alert(result.data.message, 'Error'); + } } ); break; @@ -473,9 +482,13 @@ $(document).ready(function() { $.post( OC.filePath('files','ajax','newfolder.php'), {dir:$('#dir').val(),foldername:name}, - function(data){ - var date=new Date(); - FileList.addDir(name,0,date); + function(result){ + if (result.status == 'success') { + var date=new Date(); + FileList.addDir(name,0,date); + } else { + OC.dialogs.alert(result.data.message, 'Error'); + } } ); break; @@ -492,23 +505,28 @@ $(document).ready(function() { }else{//or the domain localName=(localName.match(/:\/\/(.[^/]+)/)[1]).replace('www.',''); } - $.post( - OC.filePath('files','ajax','newfile.php'), - {dir:$('#dir').val(),source:name,filename:localName}, - function(result){ - if(result.status == 'success'){ - var date=new Date(); - FileList.addFile(localName,0,date); - var tr=$('tr').filterAttr('data-file',localName); - tr.data('mime',result.data.mime); - getMimeIcon(result.data.mime,function(path){ - tr.find('td.filename').attr('style','background-image:url('+path+')'); - }); - }else{ + localName = getUniqueName(localName); + $('#uploadprogressbar').progressbar({value:0}); + $('#uploadprogressbar').fadeIn(); - } - } - ); + var eventSource=new OC.EventSource(OC.filePath('files','ajax','newfile.php'),{dir:$('#dir').val(),source:name,filename:localName}); + eventSource.listen('progress',function(progress){ + $('#uploadprogressbar').progressbar('value',progress); + }); + eventSource.listen('success',function(mime){ + $('#uploadprogressbar').fadeOut(); + var date=new Date(); + FileList.addFile(localName,0,date); + var tr=$('tr').filterAttr('data-file',localName); + tr.data('mime',mime); + getMimeIcon(mime,function(path){ + tr.find('td.filename').attr('style','background-image:url('+path+')'); + }); + }); + eventSource.listen('error',function(error){ + $('#uploadprogressbar').fadeOut(); + alert(error); + }); break; } var li=$(this).parent(); @@ -524,6 +542,69 @@ $(document).ready(function() { scanFiles(); } }, "json"); + + var lastWidth = 0; + var breadcrumbs = []; + var breadcrumbsWidth = $('#navigation').get(0).offsetWidth; + var hiddenBreadcrumbs = 0; + + $.each($('.crumb'), function(index, breadcrumb) { + breadcrumbs[index] = breadcrumb; + breadcrumbsWidth += $(breadcrumb).get(0).offsetWidth; + }); + + if ($('#controls .actions').length > 0) { + breadcrumbsWidth += $('#controls .actions').get(0).offsetWidth; + } + + function resizeBreadcrumbs(firstRun) { + var width = $(this).width(); + if (width != lastWidth) { + if ((width < lastWidth || firstRun) && width < breadcrumbsWidth) { + if (hiddenBreadcrumbs == 0) { + breadcrumbsWidth -= $(breadcrumbs[1]).get(0).offsetWidth; + $(breadcrumbs[1]).find('a').hide(); + $(breadcrumbs[1]).append('<span>...</span>'); + breadcrumbsWidth += $(breadcrumbs[1]).get(0).offsetWidth; + hiddenBreadcrumbs = 2; + } + var i = hiddenBreadcrumbs; + while (width < breadcrumbsWidth && i > 1 && i < breadcrumbs.length - 1) { + breadcrumbsWidth -= $(breadcrumbs[i]).get(0).offsetWidth; + $(breadcrumbs[i]).hide(); + hiddenBreadcrumbs = i; + i++ + } + } else if (width > lastWidth && hiddenBreadcrumbs > 0) { + var i = hiddenBreadcrumbs; + while (width > breadcrumbsWidth && i > 0) { + if (hiddenBreadcrumbs == 1) { + breadcrumbsWidth -= $(breadcrumbs[1]).get(0).offsetWidth; + $(breadcrumbs[1]).find('span').remove(); + $(breadcrumbs[1]).find('a').show(); + breadcrumbsWidth += $(breadcrumbs[1]).get(0).offsetWidth; + } else { + $(breadcrumbs[i]).show(); + breadcrumbsWidth += $(breadcrumbs[i]).get(0).offsetWidth; + if (breadcrumbsWidth > width) { + breadcrumbsWidth -= $(breadcrumbs[i]).get(0).offsetWidth; + $(breadcrumbs[i]).hide(); + break; + } + } + i--; + hiddenBreadcrumbs = i; + } + } + lastWidth = width; + } + } + + $(window).resize(function() { + resizeBreadcrumbs(false); + }); + + resizeBreadcrumbs(true); }); function scanFiles(force,dir){ @@ -733,7 +814,10 @@ getMimeIcon.cache={}; function getUniqueName(name){ if($('tr').filterAttr('data-file',name).length>0){ var parts=name.split('.'); - var extension=parts.pop(); + var extension = ""; + if (parts.length > 1) { + extension=parts.pop(); + } var base=parts.join('.'); numMatch=base.match(/\((\d+)\)/); var num=2; @@ -743,7 +827,10 @@ function getUniqueName(name){ base.pop(); base=base.join('(').trim(); } - name=base+' ('+num+').'+extension; + name=base+' ('+num+')'; + if (extension) { + name = name+'.'+extension; + } return getUniqueName(name); } return name; diff --git a/apps/files/l10n/ar.php b/apps/files/l10n/ar.php index 91e748e3002..724152dc11d 100644 --- a/apps/files/l10n/ar.php +++ b/apps/files/l10n/ar.php @@ -6,6 +6,7 @@ "No file was uploaded" => "لم يتم ترÙيع أي من الملÙات", "Missing a temporary folder" => "المجلد المؤقت غير موجود", "Files" => "الملÙات", +"Delete" => "Ù…ØذوÙ", "Size" => "Øجم", "Modified" => "معدل", "Maximum upload size" => "الØد الأقصى Ù„Øجم الملÙات التي يمكن رÙعها", @@ -16,6 +17,8 @@ "Nothing in here. Upload something!" => "لا يوجد شيء هنا. إرÙع بعض الملÙات!", "Name" => "الاسم", "Download" => "تØميل", +"Size" => "Øجم", +"Modified" => "معدل", "Delete" => "Ù…ØذوÙ", "Upload too large" => "Øجم الترÙيع أعلى من المسموØ", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Øجم الملÙات التي تريد ترÙيعها أعلى من Ø§Ù„Ù…Ø³Ù…ÙˆØ Ø¹Ù„Ù‰ الخادم." diff --git a/apps/files/l10n/bg_BG.php b/apps/files/l10n/bg_BG.php index 83b36087310..89fc4544344 100644 --- a/apps/files/l10n/bg_BG.php +++ b/apps/files/l10n/bg_BG.php @@ -5,15 +5,33 @@ "The uploaded file was only partially uploaded" => "Файлът е качен чаÑтично", "No file was uploaded" => "Фахлът не бе качен", "Missing a temporary folder" => "ЛипÑва временната папка", +"Failed to write to disk" => "Грешка при Ð·Ð°Ð¿Ð¸Ñ Ð½Ð° диÑка", "Files" => "Файлове", +"Delete" => "Изтриване", +"Upload Error" => "Грешка при качване", +"Upload cancelled." => "Качването е отменено.", +"Invalid name, '/' is not allowed." => "Ðеправилно име – \"/\" не е позволено.", "Size" => "Размер", "Modified" => "Променено", +"folder" => "папка", +"folders" => "папки", +"file" => "файл", "Maximum upload size" => "МакÑ. размер за качване", +"0 is unlimited" => "0 означава без ограничение", +"New" => "Ðов", +"Text file" => "ТекÑтов файл", +"Folder" => "Папка", +"From url" => "От url-адреÑ", "Upload" => "Качване", +"Cancel upload" => "Отказване на качването", "Nothing in here. Upload something!" => "ÐÑма нищо, качете нещо!", "Name" => "Име", +"Share" => "СподелÑне", "Download" => "ИзтеглÑне", +"Size" => "Размер", +"Modified" => "Променено", "Delete" => "Изтриване", "Upload too large" => "Файлът е прекалено голÑм", -"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Файловете които Ñе опитвате да качите Ñа по-големи от позволеното за Ñървъра." +"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Файловете които Ñе опитвате да качите Ñа по-големи от позволеното за Ñървъра.", +"Files are being scanned, please wait." => "Файловете Ñе претърÑват, изчакайте." ); diff --git a/apps/files/l10n/ca.php b/apps/files/l10n/ca.php index baa6b3b7d45..e48148421b8 100644 --- a/apps/files/l10n/ca.php +++ b/apps/files/l10n/ca.php @@ -7,8 +7,26 @@ "Missing a temporary folder" => "S'ha perdut un fitxer temporal", "Failed to write to disk" => "Ha fallat en escriure al disc", "Files" => "Fitxers", +"Delete" => "Suprimeix", +"already exists" => "ja existeix", +"replace" => "substitueix", +"cancel" => "cancel·la", +"replaced" => "substituït", +"with" => "per", +"undo" => "desfés", +"deleted" => "esborrat", +"generating ZIP-file, it may take some time." => "s'estan generant fitxers ZIP, pot trigar una estona.", +"Unable to upload your file as it is a directory or has 0 bytes" => "No es pot pujar el fitxer perquè és una carpeta o té 0 bytes", +"Upload Error" => "Error en la pujada", +"Pending" => "Pendents", +"Upload cancelled." => "La pujada s'ha cancel·lat.", +"Invalid name, '/' is not allowed." => "El nom no és và lid, no es permet '/'.", "Size" => "Mida", "Modified" => "Modificat", +"folder" => "carpeta", +"folders" => "carpetes", +"file" => "fitxer", +"files" => "fitxers", "File handling" => "Gestió de fitxers", "Maximum upload size" => "Mida mà xima de pujada", "max. possible: " => "mà xim possible:", @@ -26,6 +44,9 @@ "Name" => "Nom", "Share" => "Comparteix", "Download" => "Baixa", +"Size" => "Mida", +"Modified" => "Modificat", +"Delete all" => "Esborra-ho tot", "Delete" => "Suprimeix", "Upload too large" => "La pujada és massa gran", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Els fitxers que esteu intentant pujar excedeixen la mida mà xima de pujada del servidor", diff --git a/apps/files/l10n/cs_CZ.php b/apps/files/l10n/cs_CZ.php index 9a4be26dadb..38f235343c3 100644 --- a/apps/files/l10n/cs_CZ.php +++ b/apps/files/l10n/cs_CZ.php @@ -7,8 +7,26 @@ "Missing a temporary folder" => "Chybà adresář pro soÄasné soubory", "Failed to write to disk" => "Zápis na disk se nezdaÅ™il", "Files" => "Soubory", +"Delete" => "Vymazat", +"already exists" => "již existuje", +"replace" => "zamÄ›nit", +"cancel" => "storno", +"replaced" => "zamÄ›nÄ›no", +"with" => "s", +"undo" => "zpÄ›t", +"deleted" => "smazáno", +"generating ZIP-file, it may take some time." => "generuji ZIP soubor, může to chvÃli trvat", +"Unable to upload your file as it is a directory or has 0 bytes" => "Nemohu nahrát váš soubor neboÅ¥ to je adresář a nebo má nulovou délku.", +"Upload Error" => "Chyba pÅ™i nahrávánÃ", +"Pending" => "OÄekává se", +"Upload cancelled." => "Nahrávánà zruÅ¡eno", +"Invalid name, '/' is not allowed." => "Å patné jméno, znak '/' nenà povolen", "Size" => "Velikost", "Modified" => "ZmÄ›nÄ›no", +"folder" => "adresář", +"folders" => "adresáře", +"file" => "soubor", +"files" => "soubory", "File handling" => "Nastavenà chovánà k souborům", "Maximum upload size" => "Maximálnà velikost ukládaných souborů", "max. possible: " => "nejvÄ›tÅ¡Ã možná:", @@ -26,6 +44,9 @@ "Name" => "Název", "Share" => "SdÃlet", "Download" => "Stáhnout", +"Size" => "Velikost", +"Modified" => "ZmÄ›nÄ›no", +"Delete all" => "Smazat vÅ¡e", "Delete" => "Vymazat", "Upload too large" => "PÅ™ÃliÅ¡ velký soubor", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Soubory, které se snažÃte uložit, pÅ™ekraÄujà maximálnà velikosti uploadu na tomto serveru.", diff --git a/apps/files/l10n/da.php b/apps/files/l10n/da.php index 3683ab8484d..8cefa27e64f 100644 --- a/apps/files/l10n/da.php +++ b/apps/files/l10n/da.php @@ -7,8 +7,26 @@ "Missing a temporary folder" => "Mangler en midlertidig mappe", "Failed to write to disk" => "Fejl ved skrivning til disk.", "Files" => "Filer", +"Delete" => "Slet", +"already exists" => "findes allerede", +"replace" => "erstat", +"cancel" => "fortryd", +"replaced" => "erstattet", +"with" => "med", +"undo" => "fortryd", +"deleted" => "Slettet", +"generating ZIP-file, it may take some time." => "genererer ZIP-fil, det kan tage lidt tid.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Kunne ikke uploade din fil, da det enten er en mappe eller er tom", +"Upload Error" => "Fejl ved upload", +"Pending" => "Afventer", +"Upload cancelled." => "Upload afbrudt.", +"Invalid name, '/' is not allowed." => "Ugyldigt navn, '/' er ikke tilladt.", "Size" => "Størrelse", "Modified" => "Ændret", +"folder" => "mappe", +"folders" => "mapper", +"file" => "fil", +"files" => "filer", "File handling" => "FilhÃ¥ndtering", "Maximum upload size" => "Maksimal upload-størrelse", "max. possible: " => "max. mulige: ", @@ -26,6 +44,8 @@ "Name" => "Navn", "Share" => "Del", "Download" => "Download", +"Size" => "Størrelse", +"Modified" => "Ændret", "Delete" => "Slet", "Upload too large" => "Upload for stor", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Filerne, du prøver at uploade, er større end den maksimale størrelse for fil-upload pÃ¥ denne server.", diff --git a/apps/files/l10n/de.php b/apps/files/l10n/de.php index 9c5310c43b8..4a85912d31f 100644 --- a/apps/files/l10n/de.php +++ b/apps/files/l10n/de.php @@ -5,12 +5,30 @@ "The uploaded file was only partially uploaded" => "Die Datei wurde nur teilweise hochgeladen.", "No file was uploaded" => "Es wurde keine Datei hochgeladen.", "Missing a temporary folder" => "Temporärer Ordner fehlt.", -"Failed to write to disk" => "Fehler beim Schreiben auf Festplatte", -"Files" => "Files", +"Failed to write to disk" => "Fehler beim Schreiben auf die Festplatte", +"Files" => "Dateien", +"Delete" => "Löschen", +"already exists" => "ist bereits vorhanden", +"replace" => "ersetzen", +"cancel" => "abbrechen", +"replaced" => "ersetzt", +"with" => "mit", +"undo" => "rückgängig machen", +"deleted" => "gelöscht", +"generating ZIP-file, it may take some time." => "Erstelle ZIP-Datei. Dies kann eine Weile dauern.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Ihre Datei kann nicht hochgeladen werden, da sie ein Verzeichnis ist oder 0 Bytes hat.", +"Upload Error" => "Fehler beim Hochladen", +"Pending" => "Ausstehend", +"Upload cancelled." => "Hochladen abgebrochen.", +"Invalid name, '/' is not allowed." => "Ungültiger Name, \"/\" ist nicht erlaubt.", "Size" => "Größe", "Modified" => "Bearbeitet", +"folder" => "Ordner", +"folders" => "Ordner", +"file" => "Datei", +"files" => "Dateien", "File handling" => "Dateibehandlung", -"Maximum upload size" => "Maximum upload size", +"Maximum upload size" => "Maximale Upload-Größe", "max. possible: " => "maximal möglich:", "Needed for multi-file and folder downloads." => "Für Mehrfachdateien- und Ordnerdownloads benötigt:", "Enable ZIP-download" => "ZIP-Download aktivieren", @@ -22,13 +40,16 @@ "From url" => "Von der URL", "Upload" => "Hochladen", "Cancel upload" => "Upload abbrechen", -"Nothing in here. Upload something!" => "Alles leer. Lad’ was hoch!", +"Nothing in here. Upload something!" => "Alles leer. Lade etwas hoch!", "Name" => "Name", "Share" => "Teilen", "Download" => "Herunterladen", +"Size" => "Größe", +"Modified" => "Bearbeitet", +"Delete all" => "Alle löschen", "Delete" => "Löschen", "Upload too large" => "Upload zu groß", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Die Datei überschreitet die Maximalgröße für Uploads auf diesem Server.", -"Files are being scanned, please wait." => "Daten werden gescannt, bitte warten.", +"Files are being scanned, please wait." => "Dateien werden gescannt, bitte warten.", "Current scanning" => "Scannen" ); diff --git a/apps/files/l10n/el.php b/apps/files/l10n/el.php index 93be0246156..4e93489fd39 100644 --- a/apps/files/l10n/el.php +++ b/apps/files/l10n/el.php @@ -7,8 +7,26 @@ "Missing a temporary folder" => "Λείπει Îνας Ï€ÏοσωÏινός φάκελος", "Failed to write to disk" => "Η εγγÏαφή στο δίσκο απÎτυχε", "Files" => "ΑÏχεία", +"Delete" => "ΔιαγÏαφή", +"already exists" => "υπάÏχει ήδη", +"replace" => "αντικατÎστησε", +"cancel" => "ακÏÏωση", +"replaced" => "αντικαταστάθηκε", +"with" => "με", +"undo" => "αναίÏεση", +"deleted" => "διαγÏάφηκε", +"generating ZIP-file, it may take some time." => "παÏαγωγή αÏχείου ZIP, ίσως διαÏκÎσει αÏκετά.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Αδυναμία στην μεταφόÏτωση του αÏχείου σας Î±Ï†Î¿Ï ÎµÎ¯Î½Î±Î¹ φάκελος ή Îχει 0 bytes", +"Upload Error" => "Σφάλμα ΜεταφόÏτωσης", +"Pending" => "Εν αναμονή", +"Upload cancelled." => "Η μεταφόÏτωση ακυÏώθηκε.", +"Invalid name, '/' is not allowed." => "Μη ÎγκυÏο όνομα, το '/' δεν επιτÏÎπεται.", "Size" => "ÎœÎγεθος", "Modified" => "ΤÏοποποιήθηκε", +"folder" => "φάκελος", +"folders" => "φάκελοι", +"file" => "αÏχείο", +"files" => "αÏχεία", "File handling" => "ΔιαχείÏιση αÏχείων", "Maximum upload size" => "ÎœÎγιστο μÎγεθος μεταφόÏτωσης", "max. possible: " => "μÎγιστο δυνατό:", @@ -26,6 +44,9 @@ "Name" => "Όνομα", "Share" => "ΔιαμοίÏασε", "Download" => "Λήψη", +"Size" => "ÎœÎγεθος", +"Modified" => "ΤÏοποποιήθηκε", +"Delete all" => "ΔιαγÏαφή όλων", "Delete" => "ΔιαγÏαφή", "Upload too large" => "Î Î¿Î»Ï Î¼ÎµÎ³Î¬Î»Î¿ το αÏχείο Ï€Ïος μεταφόÏτωση", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Τα αÏχεία που Ï€Ïοσπαθείτε να ανεβάσετε υπεÏβαίνουν το μÎγιστο μÎγεθος μεταφόÏτωσης αÏχείων σε αυτόν το διακομιστή.", diff --git a/apps/files/l10n/eo.php b/apps/files/l10n/eo.php index 64c380d600f..2976a2127c3 100644 --- a/apps/files/l10n/eo.php +++ b/apps/files/l10n/eo.php @@ -7,8 +7,26 @@ "Missing a temporary folder" => "Mankas tempa dosierujo", "Failed to write to disk" => "Malsukcesis skribo al disko", "Files" => "Dosieroj", +"Delete" => "Forigi", +"already exists" => "jam ekzistas", +"replace" => "anstataÅigi", +"cancel" => "nuligi", +"replaced" => "anstataÅigita", +"with" => "kun", +"undo" => "malfari", +"deleted" => "forigita", +"generating ZIP-file, it may take some time." => "generanta ZIP-dosiero, Äi povas daÅri iom da tempo", +"Unable to upload your file as it is a directory or has 0 bytes" => "Ne eblis alÅuti vian dosieron ĉar Äi estas dosierujo aÅ havas 0 duumokojn", +"Upload Error" => "AlÅuta eraro", +"Pending" => "Traktotaj", +"Upload cancelled." => "La alÅuto nuliÄis.", +"Invalid name, '/' is not allowed." => "Nevalida nomo, “/†ne estas permesata.", "Size" => "Grando", "Modified" => "Modifita", +"folder" => "dosierujo", +"folders" => "dosierujoj", +"file" => "dosiero", +"files" => "dosieroj", "File handling" => "Dosieradministro", "Maximum upload size" => "Maksimuma alÅutogrando", "max. possible: " => "maks. ebla: ", @@ -26,6 +44,9 @@ "Name" => "Nomo", "Share" => "Kunhavigi", "Download" => "ElÅuti", +"Size" => "Grando", +"Modified" => "Modifita", +"Delete all" => "Forigi ĉion", "Delete" => "Forigi", "Upload too large" => "ElÅuto tro larÄa", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "La dosieroj, kiujn vi provas alÅuti, transpasas la maksimuman grandon por dosieralÅutoj en ĉi tiu servilo.", diff --git a/apps/files/l10n/es.php b/apps/files/l10n/es.php index 67bfb4702e8..6fcf9086945 100644 --- a/apps/files/l10n/es.php +++ b/apps/files/l10n/es.php @@ -7,8 +7,26 @@ "Missing a temporary folder" => "Falta un directorio temporal", "Failed to write to disk" => "La escritura en disco ha fallado", "Files" => "Archivos", +"Delete" => "Eliminado", +"already exists" => "ya existe", +"replace" => "reemplazar", +"cancel" => "cancelar", +"replaced" => "reemplazado", +"with" => "con", +"undo" => "deshacer", +"deleted" => "borrado", +"generating ZIP-file, it may take some time." => "generando un fichero ZIP, puede llevar un tiempo.", +"Unable to upload your file as it is a directory or has 0 bytes" => "No ha sido posible subir tu archivo porque es un directorio o tiene 0 bytes", +"Upload Error" => "Error al subir el archivo", +"Pending" => "Pendiente", +"Upload cancelled." => "Subida cancelada.", +"Invalid name, '/' is not allowed." => "Nombre no válido, '/' no está permitido.", "Size" => "Tamaño", "Modified" => "Modificado", +"folder" => "carpeta", +"folders" => "carpetas", +"file" => "archivo", +"files" => "archivos", "File handling" => "Tratamiento de archivos", "Maximum upload size" => "Tamaño máximo de subida", "max. possible: " => "máx. posible:", @@ -26,6 +44,9 @@ "Name" => "Nombre", "Share" => "Compartir", "Download" => "Descargar", +"Size" => "Tamaño", +"Modified" => "Modificado", +"Delete all" => "Eliminar todo", "Delete" => "Eliminado", "Upload too large" => "El archivo es demasiado grande", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Los archivos que estás intentando subir sobrepasan el tamaño máximo permitido por este servidor.", diff --git a/apps/files/l10n/et_EE.php b/apps/files/l10n/et_EE.php index 89bb96581c9..c455a8ffe6a 100644 --- a/apps/files/l10n/et_EE.php +++ b/apps/files/l10n/et_EE.php @@ -7,8 +7,19 @@ "Missing a temporary folder" => "Ajutiste failide kaust puudub", "Failed to write to disk" => "Kettale kirjutamine ebaõnnestus", "Files" => "Failid", +"Delete" => "Kustuta", +"generating ZIP-file, it may take some time." => "ZIP-faili loomine, see võib veidi aega võtta.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Sinu faili üleslaadimine ebaõnnestus, kuna see on kaust või selle suurus on 0 baiti", +"Upload Error" => "Ãœleslaadimise viga", +"Pending" => "Ootel", +"Upload cancelled." => "Ãœleslaadimine tühistati.", +"Invalid name, '/' is not allowed." => "Vigane nimi, '/' pole lubatud.", "Size" => "Suurus", "Modified" => "Muudetud", +"folder" => "kaust", +"folders" => "kausta", +"file" => "fail", +"files" => "faili", "File handling" => "Failide käsitlemine", "Maximum upload size" => "Maksimaalne üleslaadimise suurus", "max. possible: " => "maks. võimalik: ", @@ -26,6 +37,9 @@ "Name" => "Nimi", "Share" => "Jaga", "Download" => "Lae alla", +"Size" => "Suurus", +"Modified" => "Muudetud", +"Delete all" => "Kustuta kõik", "Delete" => "Kustuta", "Upload too large" => "Ãœleslaadimine on liiga suur", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Failid, mida sa proovid üles laadida, ületab serveri poolt üleslaetavatele failidele määratud maksimaalse suuruse.", diff --git a/apps/files/l10n/eu.php b/apps/files/l10n/eu.php index a7ca21f496b..99c918e2209 100644 --- a/apps/files/l10n/eu.php +++ b/apps/files/l10n/eu.php @@ -7,8 +7,26 @@ "Missing a temporary folder" => "Aldi baterako karpeta falta da", "Failed to write to disk" => "Errore bat izan da diskoan idazterakoan", "Files" => "Fitxategiak", +"Delete" => "Ezabatu", +"already exists" => "dagoeneko existitzen da", +"replace" => "ordeztu", +"cancel" => "ezeztatu", +"replaced" => "ordeztua", +"with" => "honekin", +"undo" => "desegin", +"deleted" => "ezabatuta", +"generating ZIP-file, it may take some time." => "ZIP-fitxategia sortzen ari da, denbora har dezake", +"Unable to upload your file as it is a directory or has 0 bytes" => "Ezin da zure fitxategia igo, karpeta bat da edo 0 byt ditu", +"Upload Error" => "Igotzean errore bat suertatu da", +"Pending" => "Zain", +"Upload cancelled." => "Igoera ezeztatuta", +"Invalid name, '/' is not allowed." => "Baliogabeko izena, '/' ezin da erabili. ", "Size" => "Tamaina", "Modified" => "Aldatuta", +"folder" => "karpeta", +"folders" => "Karpetak", +"file" => "fitxategia", +"files" => "fitxategiak", "File handling" => "Fitxategien kudeaketa", "Maximum upload size" => "Igo daitekeen gehienezko tamaina", "max. possible: " => "max, posiblea:", @@ -26,6 +44,9 @@ "Name" => "Izena", "Share" => "Elkarbanatu", "Download" => "Deskargatu", +"Size" => "Tamaina", +"Modified" => "Aldatuta", +"Delete all" => "Ezabatu dena", "Delete" => "Ezabatu", "Upload too large" => "Igotakoa handiegia da", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Igotzen saiatzen ari zaren fitxategiak zerbitzari honek igotzeko onartzen duena baino handiagoak dira.", diff --git a/apps/files/l10n/fa.php b/apps/files/l10n/fa.php index 4bb46e1fcf9..78d8d776915 100644 --- a/apps/files/l10n/fa.php +++ b/apps/files/l10n/fa.php @@ -7,8 +7,26 @@ "Missing a temporary folder" => "یک پوشه موقت Ú¯Ù… شده است", "Failed to write to disk" => "نوشتن بر روی دیسک سخت ناموÙÙ‚ بود", "Files" => "Ùایل ها", +"Delete" => "پاک کردن", +"already exists" => "وجود دارد", +"replace" => "جایگزین", +"cancel" => "لغو", +"replaced" => "جایگزین‌شده", +"with" => "همراه", +"undo" => "بازگشت", +"deleted" => "Øذ٠شده", +"generating ZIP-file, it may take some time." => "در Øال ساخت Ùایل Ùشرده ممکن است زمان زیادی به طول بیانجامد", +"Unable to upload your file as it is a directory or has 0 bytes" => "ناتوان در بارگذاری یا Ùایل یک پوشه است یا 0بایت دارد", +"Upload Error" => "خطا در بار گذاری", +"Pending" => "در انتظار", +"Upload cancelled." => "بار گذاری لغو شد", +"Invalid name, '/' is not allowed." => "نام نامناسب '/' غیرÙعال است", "Size" => "اندازه", "Modified" => "تغییر یاÙته", +"folder" => "پوشه", +"folders" => "پوشه ها", +"file" => "پرونده", +"files" => "پرونده ها", "File handling" => "اداره پرونده ها", "Maximum upload size" => "Øداکثر اندازه بارگزاری", "max. possible: " => "Øداکثرمقدارممکن:", @@ -26,6 +44,9 @@ "Name" => "نام", "Share" => "به اشتراک گذاری", "Download" => "بارگیری", +"Size" => "اندازه", +"Modified" => "تغییر یاÙته", +"Delete all" => "پاک کردن همه", "Delete" => "پاک کردن", "Upload too large" => "Øجم بارگذاری بسیار زیاد است", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Ùایلها بیش از Øد تعیین شده در این سرور هستند\nمترجم:با تغییر Ùایل php,ini میتوان این Ù…Øدودیت را برطر٠کرد", diff --git a/apps/files/l10n/fi_FI.php b/apps/files/l10n/fi_FI.php index c99d4408f60..902ea859a31 100644 --- a/apps/files/l10n/fi_FI.php +++ b/apps/files/l10n/fi_FI.php @@ -7,10 +7,29 @@ "Missing a temporary folder" => "Väliaikaiskansiota ei ole olemassa", "Failed to write to disk" => "Levylle kirjoitus epäonnistui", "Files" => "Tiedostot", +"Delete" => "Poista", +"already exists" => "on jo olemassa", +"replace" => "korvaa", +"cancel" => "peru", +"replaced" => "korvattu", +"with" => "käyttäen", +"undo" => "kumoa", +"deleted" => "poistettu", +"generating ZIP-file, it may take some time." => "luodaan ZIP-tiedostoa, tämä saattaa kestää hetken.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Tiedoston lähetys epäonnistui, koska sen koko on 0 tavua tai kyseessä on kansio", +"Upload Error" => "Lähetysvirhe.", +"Pending" => "Odottaa", +"Upload cancelled." => "Lähetys peruttu.", +"Invalid name, '/' is not allowed." => "Virheellinen nimi, merkki '/' ei ole sallittu.", "Size" => "Koko", "Modified" => "Muutettu", +"folder" => "kansio", +"folders" => "kansiota", +"file" => "tiedosto", +"files" => "tiedostoa", "File handling" => "Tiedostonhallinta", "Maximum upload size" => "Lähetettävän tiedoston suurin sallittu koko", +"max. possible: " => "suurin mahdollinen:", "Needed for multi-file and folder downloads." => "Tarvitaan useampien tiedostojen ja kansioiden latausta varten.", "Enable ZIP-download" => "Ota ZIP-paketin lataaminen käytöön", "0 is unlimited" => "0 on rajoittamaton", @@ -25,7 +44,6 @@ "Name" => "Nimi", "Share" => "Jaa", "Download" => "Lataa", -"Delete" => "Poista", "Upload too large" => "Lähetettävä tiedosto on liian suuri", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Lähetettäväksi valitsemasi tiedostot ylittävät palvelimen salliman tiedostokoon rajan.", "Files are being scanned, please wait." => "Tiedostoja tarkistetaan, odota hetki." diff --git a/apps/files/l10n/fr.php b/apps/files/l10n/fr.php index ab2f4ea13df..9f9763636c7 100644 --- a/apps/files/l10n/fr.php +++ b/apps/files/l10n/fr.php @@ -7,8 +7,26 @@ "Missing a temporary folder" => "Il manque un répertoire temporaire", "Failed to write to disk" => "Erreur d'écriture sur le disque", "Files" => "Fichiers", +"Delete" => "Supprimer", +"already exists" => "existe déjà ", +"replace" => "remplacer", +"cancel" => "annuler", +"replaced" => "remplacé", +"with" => "avec", +"undo" => "annuler", +"deleted" => "supprimé", +"generating ZIP-file, it may take some time." => "Générer un fichier ZIP, cela peut prendre du temps", +"Unable to upload your file as it is a directory or has 0 bytes" => "Impossible de charger vos fichiers car il s'agit d'un dossier ou le fichier fait 0 octet.", +"Upload Error" => "Erreur de chargement", +"Pending" => "En cours", +"Upload cancelled." => "Chargement annulé", +"Invalid name, '/' is not allowed." => "Nom invalide, '/' n'est pas autorisé.", "Size" => "Taille", "Modified" => "Modifié", +"folder" => "dossier", +"folders" => "dossiers", +"file" => "fichier", +"files" => "fichiers", "File handling" => "Gestion des fichiers", "Maximum upload size" => "Taille max. d'envoi", "max. possible: " => "Max. possible :", @@ -26,6 +44,9 @@ "Name" => "Nom", "Share" => "Partager", "Download" => "Téléchargement", +"Size" => "Taille", +"Modified" => "Modifié", +"Delete all" => "Supprimer tout", "Delete" => "Supprimer", "Upload too large" => "Fichier trop volumineux", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Les fichiers que vous essayez d'envoyer dépassent la taille maximale permise par ce serveur.", diff --git a/apps/files/l10n/gl.php b/apps/files/l10n/gl.php index 36eafaefe27..bf86fc9b809 100644 --- a/apps/files/l10n/gl.php +++ b/apps/files/l10n/gl.php @@ -7,8 +7,26 @@ "Missing a temporary folder" => "Falta un cartafol temporal", "Failed to write to disk" => "Erro ao escribir no disco", "Files" => "Ficheiros", +"Delete" => "Eliminar", +"already exists" => "xa existe", +"replace" => "substituÃr", +"cancel" => "cancelar", +"replaced" => "substituÃdo", +"with" => "con", +"undo" => "desfacer", +"deleted" => "eliminado", +"generating ZIP-file, it may take some time." => "xerando ficheiro ZIP, pode levar un anaco.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Non se puido subir o ficheiro pois ou é un directorio ou ten 0 bytes", +"Upload Error" => "Erro na subida", +"Pending" => "Pendentes", +"Upload cancelled." => "Subida cancelada.", +"Invalid name, '/' is not allowed." => "Nome non válido, '/' non está permitido.", "Size" => "Tamaño", "Modified" => "Modificado", +"folder" => "cartafol", +"folders" => "cartafoles", +"file" => "ficheiro", +"files" => "ficheiros", "File handling" => "Manexo de ficheiro", "Maximum upload size" => "Tamaño máximo de envÃo", "max. possible: " => "máx. posible: ", @@ -26,6 +44,8 @@ "Name" => "Nome", "Share" => "Compartir", "Download" => "Descargar", +"Size" => "Tamaño", +"Modified" => "Modificado", "Delete" => "Eliminar", "Upload too large" => "EnvÃo demasiado grande", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Os ficheiros que trata de subir superan o tamaño máximo permitido neste servidor", diff --git a/apps/files/l10n/he.php b/apps/files/l10n/he.php index 0876eb952c7..5e3df214c4f 100644 --- a/apps/files/l10n/he.php +++ b/apps/files/l10n/he.php @@ -7,8 +7,19 @@ "Missing a temporary folder" => "תיקייה ×–×ž× ×™×ª חסרה", "Failed to write to disk" => "הכתיבה ×œ×›×•× ×Ÿ × ×›×©×œ×”", "Files" => "קבצי×", +"Delete" => "מחיקה", +"generating ZIP-file, it may take some time." => "יוצר קובץ ZIP, ×× × ×”×ž×ª×Ÿ.", +"Unable to upload your file as it is a directory or has 0 bytes" => "×œ× ×™×›×•×œ להעלות ×ת הקובץ מכיוון שזו תקיה ×ו שמשקל הקובץ 0 בתי×", +"Upload Error" => "שגי×ת העל××”", +"Pending" => "ממתין", +"Upload cancelled." => "ההעל××” בוטלה.", +"Invalid name, '/' is not allowed." => "×©× ×œ× ×—×•×§×™, '/' ×סור לשימוש.", "Size" => "גודל", "Modified" => "זמן ×©×™× ×•×™", +"folder" => "תקיה", +"folders" => "תקיות", +"file" => "קובץ", +"files" => "קבצי×", "File handling" => "טיפול בקבצי×", "Maximum upload size" => "גודל העל××” מקסימלי", "max. possible: " => "המרבי ×”×פשרי: ", @@ -26,6 +37,8 @@ "Name" => "ש×", "Share" => "שיתוף", "Download" => "הורדה", +"Size" => "גודל", +"Modified" => "זמן ×©×™× ×•×™", "Delete" => "מחיקה", "Upload too large" => "העל××” גדולה מידי", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "×”×§×‘×¦×™× ×©× ×™×¡×™×ª להעלות חרגו מהגודל המקסימלי להעל×ת ×§×‘×¦×™× ×¢×œ שרת ×–×”.", diff --git a/apps/files/l10n/hr.php b/apps/files/l10n/hr.php index 3281d20a61d..a3a6785294e 100644 --- a/apps/files/l10n/hr.php +++ b/apps/files/l10n/hr.php @@ -7,8 +7,19 @@ "Missing a temporary folder" => "Nedostaje privremena mapa", "Failed to write to disk" => "Neuspjelo pisanje na disk", "Files" => "Datoteke", +"Delete" => "BriÅ¡i", +"generating ZIP-file, it may take some time." => "generiranje ZIP datoteke, ovo može potrajati.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Nemoguće poslati datoteku jer je prazna ili je direktorij", +"Upload Error" => "PogreÅ¡ka pri slanju", +"Pending" => "U tijeku", +"Upload cancelled." => "Slanje poniÅ¡teno.", +"Invalid name, '/' is not allowed." => "Neispravan naziv, znak '/' nije dozvoljen.", "Size" => "VeliÄina", "Modified" => "Zadnja promjena", +"folder" => "mapa", +"folders" => "mape", +"file" => "datoteka", +"files" => "datoteke", "File handling" => "datoteka za rukovanje", "Maximum upload size" => "Maksimalna veliÄina prijenosa", "max. possible: " => "maksimalna moguća: ", @@ -26,6 +37,8 @@ "Name" => "Naziv", "Share" => "podjeli", "Download" => "Preuzmi", +"Size" => "VeliÄina", +"Modified" => "Zadnja promjena", "Delete" => "BriÅ¡i", "Upload too large" => "Prijenos je preobiman", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Datoteke koje pokuÅ¡avate prenijeti prelaze maksimalnu veliÄinu za prijenos datoteka na ovom poslužitelju.", diff --git a/apps/files/l10n/hu_HU.php b/apps/files/l10n/hu_HU.php index 41255cb4096..0a66cbda4a1 100644 --- a/apps/files/l10n/hu_HU.php +++ b/apps/files/l10n/hu_HU.php @@ -7,8 +7,26 @@ "Missing a temporary folder" => "Hiányzik az ideiglenes könyvtár", "Failed to write to disk" => "Nem Ãrható lemezre", "Files" => "Fájlok", +"Delete" => "Törlés", +"already exists" => "már létezik", +"replace" => "cserél", +"cancel" => "mégse", +"replaced" => "kicserélve", +"with" => "-val/-vel", +"undo" => "visszavon", +"deleted" => "törölve", +"generating ZIP-file, it may take some time." => "ZIP-fájl generálása, ez eltarthat egy ideig.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Nem tölthetÅ‘ fel, mert mappa volt, vagy 0 byte méretű", +"Upload Error" => "Feltöltési hiba", +"Pending" => "Folyamatban", +"Upload cancelled." => "Feltöltés megszakÃtva", +"Invalid name, '/' is not allowed." => "Érvénytelen név, a '/' nem megengedett", "Size" => "Méret", "Modified" => "MódosÃtva", +"folder" => "mappa", +"folders" => "mappák", +"file" => "fájl", +"files" => "fájlok", "File handling" => "Fájlkezelés", "Maximum upload size" => "Maximális feltölthetÅ‘ fájlméret", "max. possible: " => "max. lehetséges", @@ -26,6 +44,9 @@ "Name" => "Név", "Share" => "Megosztás", "Download" => "Letöltés", +"Size" => "Méret", +"Modified" => "MódosÃtva", +"Delete all" => "Mindent töröl", "Delete" => "Törlés", "Upload too large" => "Feltöltés túl nagy", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "A fájlokat amit próbálsz feltölteni meghaladta a legnagyobb fájlméretet ezen a szerveren.", diff --git a/apps/files/l10n/ia.php b/apps/files/l10n/ia.php index 645eba48a18..40df7af98f4 100644 --- a/apps/files/l10n/ia.php +++ b/apps/files/l10n/ia.php @@ -2,6 +2,7 @@ "The uploaded file was only partially uploaded" => "Le file incargate solmente esseva incargate partialmente", "No file was uploaded" => "Nulle file esseva incargate", "Files" => "Files", +"Delete" => "Deler", "Size" => "Dimension", "Modified" => "Modificate", "Maximum upload size" => "Dimension maxime de incargamento", @@ -12,6 +13,8 @@ "Nothing in here. Upload something!" => "Nihil hic. Incarga alcun cosa!", "Name" => "Nomine", "Download" => "Discargar", +"Size" => "Dimension", +"Modified" => "Modificate", "Delete" => "Deler", "Upload too large" => "Incargamento troppo longe" ); diff --git a/apps/files/l10n/id.php b/apps/files/l10n/id.php index 1f9dc3290aa..c66f7861256 100644 --- a/apps/files/l10n/id.php +++ b/apps/files/l10n/id.php @@ -5,6 +5,7 @@ "Missing a temporary folder" => "Kehilangan folder temporer", "Failed to write to disk" => "Gagal menulis ke disk", "Files" => "Berkas", +"Delete" => "Hapus", "Size" => "Ukuran", "Modified" => "Dimodifikasi", "File handling" => "Penanganan berkas", @@ -24,6 +25,8 @@ "Name" => "Nama", "Share" => "Bagikan", "Download" => "Unduh", +"Size" => "Ukuran", +"Modified" => "Dimodifikasi", "Delete" => "Hapus", "Upload too large" => "Unggahan terlalu besar", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Berkas yang anda coba unggah melebihi ukuran maksimum untuk pengunggahan berkas di server ini.", diff --git a/apps/files/l10n/it.php b/apps/files/l10n/it.php index 82871826c18..9bf02fb188d 100644 --- a/apps/files/l10n/it.php +++ b/apps/files/l10n/it.php @@ -7,8 +7,26 @@ "Missing a temporary folder" => "Cartella temporanea mancante", "Failed to write to disk" => "Scrittura su disco non riuscita", "Files" => "File", +"Delete" => "Elimina", +"already exists" => "esiste già ", +"replace" => "sostituisci", +"cancel" => "annulla", +"replaced" => "sostituito", +"with" => "con", +"undo" => "annulla", +"deleted" => "eliminati", +"generating ZIP-file, it may take some time." => "creazione file ZIP, potrebbe richiedere del tempo.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Impossibile inviare il file poiché è una cartella o ha dimensione 0 byte", +"Upload Error" => "Errore di invio", +"Pending" => "In corso", +"Upload cancelled." => "Invio annullato", +"Invalid name, '/' is not allowed." => "Nome non valido", "Size" => "Dimensione", "Modified" => "Modificato", +"folder" => "cartella", +"folders" => "cartelle", +"file" => "file", +"files" => "file", "File handling" => "Gestione file", "Maximum upload size" => "Dimensione massima upload", "max. possible: " => "numero mass.: ", @@ -26,6 +44,9 @@ "Name" => "Nome", "Share" => "Condividi", "Download" => "Scarica", +"Size" => "Dimensione", +"Modified" => "Modificato", +"Delete all" => "Elimina tutto", "Delete" => "Elimina", "Upload too large" => "Il file caricato è troppo grande", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "I file che stai provando a caricare superano la dimensione massima consentita su questo server.", diff --git a/apps/files/l10n/ja_JP.php b/apps/files/l10n/ja_JP.php index c04d0836dfb..868a2cfe70c 100644 --- a/apps/files/l10n/ja_JP.php +++ b/apps/files/l10n/ja_JP.php @@ -7,8 +7,26 @@ "Missing a temporary folder" => "テンãƒãƒ©ãƒªãƒ•ã‚©ãƒ«ãƒ€ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“", "Failed to write to disk" => "ディスクã¸ã®æ›¸ãè¾¼ã¿ã«å¤±æ•—ã—ã¾ã—ãŸ", "Files" => "ファイル", +"Delete" => "削除", +"already exists" => "æ—¢ã«å˜åœ¨ã—ã¾ã™", +"replace" => "ç½®ãæ›ãˆ", +"cancel" => "ã‚ャンセル", +"replaced" => "ç½®æ›ï¼š", +"with" => "â†", +"undo" => "å…ƒã«æˆ»ã™", +"deleted" => "削除", +"generating ZIP-file, it may take some time." => "ZIPファイルを生æˆä¸ã§ã™ã€ã—ã°ã‚‰ããŠå¾…ã¡ãã ã•ã„。", +"Unable to upload your file as it is a directory or has 0 bytes" => "アップãƒãƒ¼ãƒ‰ä½¿ç”¨ã¨ã—ã¦ã„るファイルãŒãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã€ã‚‚ã—ãã¯ã‚µã‚¤ã‚ºãŒ0ãƒã‚¤ãƒˆã®ãŸã‚ã€ã‚¢ãƒƒãƒ—ãƒãƒ¼ãƒ‰ã§ãã¾ã›ã‚“。", +"Upload Error" => "アップãƒãƒ¼ãƒ‰ã‚¨ãƒ©ãƒ¼", +"Pending" => "ä¿ç•™", +"Upload cancelled." => "アップãƒãƒ¼ãƒ‰ã¯ã‚ャンセルã•ã‚Œã¾ã—ãŸã€‚", +"Invalid name, '/' is not allowed." => "無効ãªåå‰ã€'/' ã¯ä½¿ç”¨ã§ãã¾ã›ã‚“。", "Size" => "サイズ", "Modified" => "更新日時", +"folder" => "フォルダ", +"folders" => "フォルダ", +"file" => "ファイル", +"files" => "ファイル", "File handling" => "ファイルæ“作", "Maximum upload size" => "最大アップãƒãƒ¼ãƒ‰ã‚µã‚¤ã‚º", "max. possible: " => "最大容é‡: ", @@ -26,6 +44,8 @@ "Name" => "åå‰", "Share" => "共有", "Download" => "ダウンãƒãƒ¼ãƒ‰", +"Size" => "サイズ", +"Modified" => "更新日時", "Delete" => "削除", "Upload too large" => "ファイルサイズãŒå¤§ãã™ãŽã¾ã™", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "アップãƒãƒ¼ãƒ‰ã—よã†ã¨ã—ã¦ã„るファイルã¯ã€ã‚µãƒ¼ãƒã§è¦å®šã•ã‚ŒãŸæœ€å¤§ã‚µã‚¤ã‚ºã‚’超ãˆã¦ã„ã¾ã™ã€‚", diff --git a/apps/files/l10n/ko.php b/apps/files/l10n/ko.php index 70575f0975d..66b0c65d8c3 100644 --- a/apps/files/l10n/ko.php +++ b/apps/files/l10n/ko.php @@ -7,8 +7,18 @@ "Missing a temporary folder" => "ìž„ì‹œ í´ë”ê°€ 사ë¼ì§", "Failed to write to disk" => "디스í¬ì— 쓰지 못했습니다", "Files" => "파ì¼", +"Delete" => "ì‚ì œ", +"generating ZIP-file, it may take some time." => "ZIPíŒŒì¼ ìƒì„±ì— ì‹œê°„ì´ ê±¸ë¦´ 수 있습니다.", +"Upload Error" => "업로드 ì—러", +"Pending" => "보류 중", +"Upload cancelled." => "업로드 취소.", +"Invalid name, '/' is not allowed." => "ìž˜ëª»ëœ ì´ë¦„, '/' ì€ í—ˆìš©ì´ ë˜ì§€ 않습니다.", "Size" => "í¬ê¸°", "Modified" => "ìˆ˜ì •ë¨", +"folder" => "í´ë”", +"folders" => "í´ë”", +"file" => "파ì¼", +"files" => "파ì¼", "File handling" => "íŒŒì¼ ì²˜ë¦¬", "Maximum upload size" => "최대 업로드 í¬ê¸°", "max. possible: " => "최대. 가능한:", @@ -26,6 +36,9 @@ "Name" => "ì´ë¦„", "Share" => "ê³µìœ ", "Download" => "다운로드", +"Size" => "í¬ê¸°", +"Modified" => "ìˆ˜ì •ë¨", +"Delete all" => "ëª¨ë‘ ì‚ì œ", "Delete" => "ì‚ì œ", "Upload too large" => "업로드 용량 초과", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "ì´ íŒŒì¼ì´ 서버ì—ì„œ 허용하는 최대 업로드 가능 용량보다 í½ë‹ˆë‹¤.", diff --git a/apps/files/l10n/lb.php b/apps/files/l10n/lb.php index 548ef4895d4..d3f1207cfba 100644 --- a/apps/files/l10n/lb.php +++ b/apps/files/l10n/lb.php @@ -7,6 +7,7 @@ "Missing a temporary folder" => "Et feelt en temporären Dossier", "Failed to write to disk" => "Konnt net op den Disk schreiwen", "Files" => "Dateien", +"Delete" => "Läschen", "Size" => "Gréisst", "Modified" => "Geännert", "File handling" => "Fichier handling", @@ -26,6 +27,8 @@ "Name" => "Numm", "Share" => "Share", "Download" => "Eroflueden", +"Size" => "Gréisst", +"Modified" => "Geännert", "Delete" => "Läschen", "Upload too large" => "Upload ze grouss", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Déi Dateien déi Dir probéiert erop ze lueden sinn méi grouss wei déi Maximal Gréisst déi op dësem Server erlaabt ass.", diff --git a/apps/files/l10n/lt_LT.php b/apps/files/l10n/lt_LT.php index e876e743a48..9b2b364c9b8 100644 --- a/apps/files/l10n/lt_LT.php +++ b/apps/files/l10n/lt_LT.php @@ -7,9 +7,25 @@ "Missing a temporary folder" => "NÄ—ra laikinojo katalogo", "Failed to write to disk" => "Nepavyko įraÅ¡yti į diskÄ…", "Files" => "Failai", +"Delete" => "IÅ¡trinti", +"cancel" => "atÅ¡aukti", +"generating ZIP-file, it may take some time." => "kuriamas ZIP archyvas, tai gali užtrukti Å¡iek tiek laiko.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Neįmanoma įkelti failo - jo dydis gali bÅ«ti 0 bitų arba tai katalogas", +"Upload Error" => "Ä®kÄ—limo klaida", +"Pending" => "Laukiantis", +"Upload cancelled." => "Ä®kÄ—limas atÅ¡auktas.", +"Invalid name, '/' is not allowed." => "Pavadinime negali bÅ«ti naudojamas ženklas \"/\".", "Size" => "Dydis", "Modified" => "Pakeista", -"Maximum upload size" => "Maksimalus failo dydis", +"folder" => "katalogas", +"folders" => "katalogai", +"file" => "failas", +"files" => "failai", +"File handling" => "Failų tvarkymas", +"Maximum upload size" => "Maksimalus įkeliamo failo dydis", +"Enable ZIP-download" => "Ä®jungti atsisiuntimÄ… ZIP archyvu", +"0 is unlimited" => "0 yra neribotas", +"Maximum input size for ZIP files" => "Maksimalus ZIP archyvo failo dydis", "New" => "Naujas", "Text file" => "Teksto failas", "Folder" => "Katalogas", @@ -20,7 +36,11 @@ "Name" => "Pavadinimas", "Share" => "Dalintis", "Download" => "Atsisiųsti", +"Size" => "Dydis", +"Modified" => "Pakeista", "Delete" => "IÅ¡trinti", "Upload too large" => "Ä®kÄ—limui failas per didelis", -"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Bandomų įkelti failų dydis virÅ¡ija maksimalų leidžiamÄ… Å¡iame serveryje" +"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Bandomų įkelti failų dydis virÅ¡ija maksimalų leidžiamÄ… Å¡iame serveryje", +"Files are being scanned, please wait." => "Skenuojami failai, praÅ¡ome palaukti.", +"Current scanning" => "Å iuo metu skenuojama" ); diff --git a/apps/files/l10n/lv.php b/apps/files/l10n/lv.php new file mode 100644 index 00000000000..54bd79f5526 --- /dev/null +++ b/apps/files/l10n/lv.php @@ -0,0 +1,20 @@ +<?php $TRANSLATIONS = array( +"Files" => "Faili", +"Delete" => "IzdzÄ“st", +"Upload Error" => "AugÅ¡uplÄdÄ“Å¡anas laikÄ radÄs kļūda", +"Pending" => "Gaida savu kÄrtu", +"Upload cancelled." => "AugÅ¡uplÄde ir atcelta", +"Invalid name, '/' is not allowed." => "Å is simbols '/', nav atļauts.", +"Size" => "IzmÄ“rs", +"Modified" => "IzmainÄ«ts", +"folder" => "mape", +"folders" => "mapes", +"file" => "fails", +"files" => "faili", +"Maximum upload size" => "MaksimÄlais failu augÅ¡uplÄdes apjoms", +"Upload" => "AugÅ¡uplÄdet", +"Nothing in here. Upload something!" => "Te vÄ“l nekas nav. RÄ«kojies, sÄc augÅ¡uplÄdÄ“t", +"Name" => "Nosaukums", +"Download" => "LejuplÄdÄ“t", +"Upload too large" => "Fails ir par lielu lai to augÅ¡uplÄdetu" +); diff --git a/apps/files/l10n/mk.php b/apps/files/l10n/mk.php index d3efa4d0225..b060c087656 100644 --- a/apps/files/l10n/mk.php +++ b/apps/files/l10n/mk.php @@ -7,8 +7,19 @@ "Missing a temporary folder" => "Ðе поÑтои привремена папка", "Failed to write to disk" => "ÐеуÑпеав да запишам на диÑк", "Files" => "Датотеки", +"Delete" => "Избриши", +"generating ZIP-file, it may take some time." => "Се генерира ZIP фајлот, ќе треба извеÑно време.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Ðе може да Ñе преземе вашата датотека бидејќи фолдерот во кој Ñе наоѓа фајлот има големина од 0 бајти", +"Upload Error" => "Грешка при преземање", +"Pending" => "Чека", +"Upload cancelled." => "Преземањето е прекинато.", +"Invalid name, '/' is not allowed." => "неиÑправно име, '/' не е дозволено.", "Size" => "Големина", "Modified" => "Променето", +"folder" => "фолдер", +"folders" => "фолдери", +"file" => "датотека", +"files" => "датотеки", "File handling" => "Ракување Ñо датотеки", "Maximum upload size" => "МакÑимална големина за подигање", "max. possible: " => "макÑ. можно:", @@ -26,6 +37,9 @@ "Name" => "Име", "Share" => "Сподели", "Download" => "Преземи", +"Size" => "Големина", +"Modified" => "Променето", +"Delete all" => "Избриши ÑÑ", "Delete" => "Избриши", "Upload too large" => "Датотеката е премногу голема", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Датотеките кои Ñе обидувате да ги подигнете ја надминуваат макÑималната големина за подигнување датотеки на овој Ñервер.", diff --git a/apps/files/l10n/ms_MY.php b/apps/files/l10n/ms_MY.php index 1a1d72a01b6..456a30a9d1a 100644 --- a/apps/files/l10n/ms_MY.php +++ b/apps/files/l10n/ms_MY.php @@ -5,18 +5,49 @@ "The uploaded file was only partially uploaded" => "Sebahagian daripada fail telah dimuat naik. ", "No file was uploaded" => "Tiada fail yang dimuat naik", "Missing a temporary folder" => "Folder sementara hilang", +"Failed to write to disk" => "Gagal untuk disimpan", "Files" => "fail", +"Delete" => "Padam", +"already exists" => "Sudah wujud", +"replace" => "ganti", +"cancel" => "Batal", +"replaced" => "diganti", +"with" => "dengan", +"deleted" => "dihapus", +"generating ZIP-file, it may take some time." => "sedang menghasilkan fail ZIP, mungkin mengambil sedikit masa.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Tidak boleh memuatnaik fail anda kerana mungkin ianya direktori atau saiz fail 0 bytes", +"Upload Error" => "Muat naik ralat", +"Pending" => "Dalam proses", +"Upload cancelled." => "Muatnaik dibatalkan.", +"Invalid name, '/' is not allowed." => "penggunaa nama tidak sah, '/' tidak dibenarkan.", "Size" => "Saiz", "Modified" => "Dimodifikasi", +"folder" => "direktori", +"folders" => "direktori", +"file" => "fail", +"files" => "fail", +"File handling" => "Pengendalian fail", "Maximum upload size" => "Saiz maksimum muat naik", +"max. possible: " => "maksimum:", +"Needed for multi-file and folder downloads." => "Diperlukan untuk muatturun fail pelbagai ", +"Enable ZIP-download" => "Aktifkan muatturun ZIP", +"0 is unlimited" => "0 adalah tanpa had", +"Maximum input size for ZIP files" => "Saiz maksimum input untuk fail ZIP", "New" => "Baru", "Text file" => "Fail teks", "Folder" => "Folder", +"From url" => "Dari url", "Upload" => "Muat naik", +"Cancel upload" => "Batal muat naik", "Nothing in here. Upload something!" => "Tiada apa-apa di sini. Muat naik sesuatu!", "Name" => "Nama ", +"Share" => "Kongsi", "Download" => "Muat turun", +"Size" => "Saiz", +"Modified" => "Dimodifikasi", "Delete" => "Padam", "Upload too large" => "Muat naik terlalu besar", -"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Fail yang cuba dimuat naik melebihi saiz maksimum fail upload server" +"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Fail yang cuba dimuat naik melebihi saiz maksimum fail upload server", +"Files are being scanned, please wait." => "Fail sedang diimbas, harap bersabar.", +"Current scanning" => "Imbasan semasa" ); diff --git a/apps/files/l10n/nb_NO.php b/apps/files/l10n/nb_NO.php index 0f4cc33ae38..4a2bf36fd59 100644 --- a/apps/files/l10n/nb_NO.php +++ b/apps/files/l10n/nb_NO.php @@ -7,8 +7,15 @@ "Missing a temporary folder" => "Mangler en midlertidig mappe", "Failed to write to disk" => "Klarte ikke Ã¥ skrive til disk", "Files" => "Filer", +"Delete" => "Slett", +"generating ZIP-file, it may take some time." => "opprettet ZIP-fil, dette kan ta litt tid", +"Pending" => "Ventende", "Size" => "Størrelse", "Modified" => "Endret", +"folder" => "mappe", +"folders" => "mapper", +"file" => "fil", +"files" => "filer", "File handling" => "FilhÃ¥ndtering", "Maximum upload size" => "Maksimum opplastingsstørrelse", "max. possible: " => "max. mulige:", @@ -26,6 +33,9 @@ "Name" => "Navn", "Share" => "Del", "Download" => "Last ned", +"Size" => "Størrelse", +"Modified" => "Endret", +"Delete all" => "Slett alle", "Delete" => "Slett", "Upload too large" => "Opplasting for stor", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Filene du prøver Ã¥ laste opp er for store for Ã¥ laste opp til denne serveren.", diff --git a/apps/files/l10n/nl.php b/apps/files/l10n/nl.php index 119f6034f15..96b82d2481d 100644 --- a/apps/files/l10n/nl.php +++ b/apps/files/l10n/nl.php @@ -7,8 +7,19 @@ "Missing a temporary folder" => "Een tijdelijke map mist", "Failed to write to disk" => "Schrijven naar schijf mislukt", "Files" => "Bestanden", +"Delete" => "Verwijder", +"generating ZIP-file, it may take some time." => "aanmaken ZIP-file, dit kan enige tijd duren.", +"Unable to upload your file as it is a directory or has 0 bytes" => "uploaden van de file mislukt, het is of een directory of de bestandsgrootte is 0 bytes", +"Upload Error" => "Upload Fout", +"Pending" => "Wachten", +"Upload cancelled." => "Uploaden geannuleerd.", +"Invalid name, '/' is not allowed." => "Ongeldige naam, '/' is niet toegestaan.", "Size" => "Bestandsgrootte", "Modified" => "Laatst aangepast", +"folder" => "map", +"folders" => "mappen", +"file" => "bestand", +"files" => "bestanden", "File handling" => "Bestand", "Maximum upload size" => "Maximale bestandsgrootte voor uploads", "max. possible: " => "max. mogelijk: ", @@ -26,6 +37,9 @@ "Name" => "Naam", "Share" => "Delen", "Download" => "Download", +"Size" => "Bestandsgrootte", +"Modified" => "Laatst aangepast", +"Delete all" => "Alles verwijderen", "Delete" => "Verwijder", "Upload too large" => "Bestanden te groot", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "De bestanden die u probeert te uploaden zijn groter dan de maximaal toegestane bestandsgrootte voor deze server.", diff --git a/apps/files/l10n/nn_NO.php b/apps/files/l10n/nn_NO.php index 002e85de5cc..a2263b3df2f 100644 --- a/apps/files/l10n/nn_NO.php +++ b/apps/files/l10n/nn_NO.php @@ -6,6 +6,7 @@ "No file was uploaded" => "Ingen filer vart lasta opp", "Missing a temporary folder" => "Manglar ei mellombels mappe", "Files" => "Filer", +"Delete" => "Slett", "Size" => "Storleik", "Modified" => "Endra", "Maximum upload size" => "Maksimal opplastingsstorleik", @@ -16,6 +17,8 @@ "Nothing in here. Upload something!" => "Ingenting her. Last noko opp!", "Name" => "Namn", "Download" => "Last ned", +"Size" => "Storleik", +"Modified" => "Endra", "Delete" => "Slett", "Upload too large" => "For stor opplasting", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Filene du prøver Ã¥ laste opp er større enn maksgrensa til denne tenaren." diff --git a/apps/files/l10n/pl.php b/apps/files/l10n/pl.php index bfdd04659dd..7b67fd47224 100644 --- a/apps/files/l10n/pl.php +++ b/apps/files/l10n/pl.php @@ -7,8 +7,26 @@ "Missing a temporary folder" => "Brak katalogu tymczasowego", "Failed to write to disk" => "BÅ‚Ä…d zapisu na dysk", "Files" => "Pliki", +"Delete" => "Usuwa element", +"already exists" => "Już istnieje", +"replace" => "zastap", +"cancel" => "anuluj", +"replaced" => "zastÄ…pione", +"with" => "z", +"undo" => "wróć", +"deleted" => "skasuj", +"generating ZIP-file, it may take some time." => "Generowanie pliku ZIP, może potrwać pewien czas.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Nie można wczytać pliku jeÅ›li jest katalogiem lub ma 0 bajtów", +"Upload Error" => "BÅ‚Ä…d wczytywania", +"Pending" => "OczekujÄ…ce", +"Upload cancelled." => "Wczytywanie anulowane.", +"Invalid name, '/' is not allowed." => "NieprawidÅ‚owa nazwa '/' jest niedozwolone.", "Size" => "Rozmiar", "Modified" => "Czas modyfikacji", +"folder" => "folder", +"folders" => "foldery", +"file" => "plik", +"files" => "pliki", "File handling" => "ZarzÄ…dzanie plikami", "Maximum upload size" => "Maksymalny rozmiar wysyÅ‚anego pliku", "max. possible: " => "max. możliwych", @@ -26,6 +44,8 @@ "Name" => "Nazwa", "Share" => "Współdziel", "Download" => "Pobiera element", +"Size" => "Rozmiar", +"Modified" => "Czas modyfikacji", "Delete" => "Usuwa element", "Upload too large" => "WysyÅ‚any plik ma za duży rozmiar", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Pliki które próbujesz przesÅ‚ać, przekraczajÄ… maksymalnÄ…, dopuszczalnÄ… wielkość.", diff --git a/apps/files/l10n/pt_BR.php b/apps/files/l10n/pt_BR.php index b70406f5171..b606c8ef649 100644 --- a/apps/files/l10n/pt_BR.php +++ b/apps/files/l10n/pt_BR.php @@ -7,8 +7,19 @@ "Missing a temporary folder" => "Pasta temporária não encontrada", "Failed to write to disk" => "Falha ao escrever no disco", "Files" => "Arquivos", +"Delete" => "Excluir", +"generating ZIP-file, it may take some time." => "gerando arquivo ZIP, isso pode levar um tempo.", +"Unable to upload your file as it is a directory or has 0 bytes" => "ImpossÃvel enviar seus arquivo como diretório ou ele tem 0 bytes.", +"Upload Error" => "Erro de envio", +"Pending" => "Pendente", +"Upload cancelled." => "Envio cancelado.", +"Invalid name, '/' is not allowed." => "Nome inválido, '/' não é permitido.", "Size" => "Tamanho", "Modified" => "Modificado", +"folder" => "pasta", +"folders" => "pastas", +"file" => "arquivo", +"files" => "arquivos", "File handling" => "Tratamento de Arquivo", "Maximum upload size" => "Tamanho máximo para carregar", "max. possible: " => "max. possÃvel:", @@ -26,6 +37,9 @@ "Name" => "Nome", "Share" => "Compartilhar", "Download" => "Baixar", +"Size" => "Tamanho", +"Modified" => "Modificado", +"Delete all" => "Deletar Tudo", "Delete" => "Excluir", "Upload too large" => "Arquivo muito grande", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Os arquivos que você está tentando carregar excedeu o tamanho máximo para arquivos no servidor.", diff --git a/apps/files/l10n/pt_PT.php b/apps/files/l10n/pt_PT.php index ac224302825..eaf5a69a041 100644 --- a/apps/files/l10n/pt_PT.php +++ b/apps/files/l10n/pt_PT.php @@ -7,8 +7,26 @@ "Missing a temporary folder" => "Falta uma pasta temporária", "Failed to write to disk" => "Falhou a escrita no disco", "Files" => "Ficheiros", +"Delete" => "Apagar", +"already exists" => "Já existe", +"replace" => "substituir", +"cancel" => "cancelar", +"replaced" => "substituido", +"with" => "com", +"undo" => "desfazer", +"deleted" => "apagado", +"generating ZIP-file, it may take some time." => "a gerar o ficheiro ZIP, poderá demorar algum tempo.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Não é possivel fazer o upload do ficheiro devido a ser uma pasta ou ter 0 bytes", +"Upload Error" => "Erro no upload", +"Pending" => "Pendente", +"Upload cancelled." => "O upload foi cancelado.", +"Invalid name, '/' is not allowed." => "nome inválido, '/' não permitido.", "Size" => "Tamanho", "Modified" => "Modificado", +"folder" => "pasta", +"folders" => "pastas", +"file" => "ficheiro", +"files" => "ficheiros", "File handling" => "Manuseamento de ficheiros", "Maximum upload size" => "Tamanho máximo de envio", "max. possible: " => "max. possivel: ", @@ -26,6 +44,8 @@ "Name" => "Nome", "Share" => "Partilhar", "Download" => "Transferir", +"Size" => "Tamanho", +"Modified" => "Modificado", "Delete" => "Apagar", "Upload too large" => "Envio muito grande", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Os ficheiro que estás a tentar enviar excedem o tamanho máximo de envio neste servidor.", diff --git a/apps/files/l10n/ro.php b/apps/files/l10n/ro.php index e6f0294fd3f..58a6d2454f5 100644 --- a/apps/files/l10n/ro.php +++ b/apps/files/l10n/ro.php @@ -7,6 +7,7 @@ "Missing a temporary folder" => "LipseÈ™te un dosar temporar", "Failed to write to disk" => "Eroare la scriere pe disc", "Files" => "FiÈ™iere", +"Delete" => "Șterge", "Size" => "Dimensiune", "Modified" => "Modificat", "File handling" => "Manipulare fiÈ™iere", @@ -26,7 +27,6 @@ "Name" => "Nume", "Share" => "Partajează", "Download" => "Descarcă", -"Delete" => "Șterge", "Upload too large" => "FiÈ™ierul încărcat este prea mare", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "FiÈ™ierul care l-ai încărcat a depășită limita maximă admisă la încărcare pe acest server.", "Files are being scanned, please wait." => "FiÈ™ierele sunt scanate, te rog aÈ™teptă.", diff --git a/apps/files/l10n/ru.php b/apps/files/l10n/ru.php index 45648012a14..203a4f81ede 100644 --- a/apps/files/l10n/ru.php +++ b/apps/files/l10n/ru.php @@ -7,8 +7,26 @@ "Missing a temporary folder" => "Ðевозможно найти временную папку", "Failed to write to disk" => "Ошибка запиÑи на диÑк", "Files" => "Файлы", +"Delete" => "Удалить", +"already exists" => "уже ÑущеÑтвует", +"replace" => "заменить", +"cancel" => "отмена", +"replaced" => "заменён", +"with" => "Ñ", +"undo" => "отмена", +"deleted" => "удален", +"generating ZIP-file, it may take some time." => "Ñоздание ZIP-файла, Ñто может занÑÑ‚ÑŒ некоторое времÑ.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Ðе удаетÑÑ Ð·Ð°Ð³Ñ€ÑƒÐ·Ð¸Ñ‚ÑŒ файл размером 0 байт в каталог", +"Upload Error" => "Ошибка загрузки", +"Pending" => "Ожидание", +"Upload cancelled." => "Загрузка отменена.", +"Invalid name, '/' is not allowed." => "Ðеверное имÑ, '/' не допуÑкаетÑÑ.", "Size" => "Размер", "Modified" => "Изменен", +"folder" => "папка", +"folders" => "папки", +"file" => "файл", +"files" => "файлы", "File handling" => "Управление файлами", "Maximum upload size" => "МакÑимальный размер файла", "max. possible: " => "макÑ. возможно: ", @@ -26,7 +44,6 @@ "Name" => "Ðазвание", "Share" => "ПоделитьÑÑ", "Download" => "Скачать", -"Delete" => "Удалить", "Upload too large" => "Файл Ñлишком большой", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Файлы, которые Ð’Ñ‹ пытаетеÑÑŒ закачать, превышают лимит Ð´Ð»Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð² на Ñтом Ñервере.", "Files are being scanned, please wait." => "Подождите, файлы ÑканируютÑÑ.", diff --git a/apps/files/l10n/sk_SK.php b/apps/files/l10n/sk_SK.php index 2a7e84af6c4..9e9a543d386 100644 --- a/apps/files/l10n/sk_SK.php +++ b/apps/files/l10n/sk_SK.php @@ -7,8 +7,19 @@ "Missing a temporary folder" => "Chýbajúci doÄasný prieÄinok", "Failed to write to disk" => "Zápis na disk sa nepodaril", "Files" => "Súbory", +"Delete" => "OdstrániÅ¥", +"generating ZIP-file, it may take some time." => "generujem ZIP-súbor, môže to chvÃľu trvaÅ¥.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Nemôžem nahraÅ¥ súbor lebo je to prieÄinok alebo má 0 bajtov.", +"Upload Error" => "Chyba nahrávania", +"Pending" => "ÄŒaká sa", +"Upload cancelled." => "Nahrávanie zruÅ¡ené", +"Invalid name, '/' is not allowed." => "Chybný názov, \"/\" nie je povolené", "Size" => "VeľkosÅ¥", "Modified" => "Upravené", +"folder" => "prieÄinok", +"folders" => "prieÄinky", +"file" => "súbor", +"files" => "súbory", "File handling" => "Nastavenie správanie k súborom", "Maximum upload size" => "Maximálna veľkosÅ¥ nahratia", "max. possible: " => "najväÄÅ¡ie možné:", @@ -26,7 +37,7 @@ "Name" => "Meno", "Share" => "ZdielaÅ¥", "Download" => "StiahnuÅ¥", -"Delete" => "OdstrániÅ¥", +"Delete all" => "OdstrániÅ¥ vÅ¡etko", "Upload too large" => "Nahrávanie prÃliÅ¡ veľké", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Súbory ktoré sa snažÃte nahraÅ¥ presahujú maximálnu veľkosÅ¥ pre nahratie súborov na tento server.", "Files are being scanned, please wait." => "Súbory sa práve prehľadávajú, prosÃm Äakajte.", diff --git a/apps/files/l10n/sl.php b/apps/files/l10n/sl.php index b0ea2dc373c..b94735c3915 100644 --- a/apps/files/l10n/sl.php +++ b/apps/files/l10n/sl.php @@ -7,8 +7,26 @@ "Missing a temporary folder" => "Manjka zaÄasna mapa", "Failed to write to disk" => "Pisanje na disk je spodletelo", "Files" => "Datoteke", +"Delete" => "IzbriÅ¡i", +"already exists" => "že obstaja", +"replace" => "nadomesti", +"cancel" => "ekliÄi", +"replaced" => "nadomeÅ¡Äen", +"with" => "z", +"undo" => "razveljavi", +"deleted" => "izbrisano", +"generating ZIP-file, it may take some time." => "Ustvarjam ZIP datoteko. To lahko traja nekaj Äasa.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Nalaganje ni mogoÄe, saj gre za mapo, ali pa ima datoteka velikost 0 bajtov.", +"Upload Error" => "Napaka pri nalaganju", +"Pending" => "Na Äakanju", +"Upload cancelled." => "Nalaganje je bilo preklicano.", +"Invalid name, '/' is not allowed." => "Neveljavno ime. Znak '/' ni dovoljen.", "Size" => "Velikost", "Modified" => "Spremenjeno", +"folder" => "mapa", +"folders" => "mape", +"file" => "datoteka", +"files" => "datoteke", "File handling" => "Rokovanje z datotekami", "Maximum upload size" => "NajveÄja velikost za nalaganje", "max. possible: " => "najveÄ mogoÄe:", @@ -26,7 +44,7 @@ "Name" => "Ime", "Share" => "Souporaba", "Download" => "Prejmi", -"Delete" => "IzbriÅ¡i", +"Delete all" => "IzbriÅ¡i vse", "Upload too large" => "Nalaganje ni mogoÄe, ker je preveliko", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Datoteke, ki jih želite naložiti, presegajo najveÄjo dovoljeno velikost na tem strežniku.", "Files are being scanned, please wait." => "Preiskujem datoteke, prosimo poÄakajte.", diff --git a/apps/files/l10n/sr.php b/apps/files/l10n/sr.php index 68c50d5282a..84164e25c62 100644 --- a/apps/files/l10n/sr.php +++ b/apps/files/l10n/sr.php @@ -6,6 +6,7 @@ "No file was uploaded" => "Ðиједан фајл није поÑлат", "Missing a temporary folder" => "ÐедоÑтаје привремена фаÑцикла", "Files" => "Фајлови", +"Delete" => "Обриши", "Size" => "Величина", "Modified" => "Задња измена", "Maximum upload size" => "МакÑимална величина пошиљке", @@ -16,7 +17,6 @@ "Nothing in here. Upload something!" => "Овде нема ничег. Пошаљите нешто!", "Name" => "Име", "Download" => "Преузми", -"Delete" => "Обриши", "Upload too large" => "Пошиљка је превелика", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Фајлови које желите да пошаљете превазилазе ограничење макÑималне величине пошиљке на овом Ñерверу." ); diff --git a/apps/files/l10n/sr@latin.php b/apps/files/l10n/sr@latin.php index 63843fc8b55..96c567aec41 100644 --- a/apps/files/l10n/sr@latin.php +++ b/apps/files/l10n/sr@latin.php @@ -6,6 +6,7 @@ "No file was uploaded" => "Nijedan fajl nije poslat", "Missing a temporary folder" => "Nedostaje privremena fascikla", "Files" => "Fajlovi", +"Delete" => "ObriÅ¡i", "Size" => "VeliÄina", "Modified" => "Zadnja izmena", "Maximum upload size" => "Maksimalna veliÄina poÅ¡iljke", @@ -13,7 +14,6 @@ "Nothing in here. Upload something!" => "Ovde nema niÄeg. PoÅ¡aljite neÅ¡to!", "Name" => "Ime", "Download" => "Preuzmi", -"Delete" => "ObriÅ¡i", "Upload too large" => "PoÅ¡iljka je prevelika", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Fajlovi koje želite da poÅ¡aljete prevazilaze ograniÄenje maksimalne veliÄine poÅ¡iljke na ovom serveru." ); diff --git a/apps/files/l10n/sv.php b/apps/files/l10n/sv.php index f508f22f3cf..a62b7522511 100644 --- a/apps/files/l10n/sv.php +++ b/apps/files/l10n/sv.php @@ -2,20 +2,38 @@ "There is no error, the file uploaded with success" => "Inga fel uppstod. Filen laddades upp utan problem", "The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Den uppladdade filen överskrider upload_max_filesize direktivet i php.ini", "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Den uppladdade filen överstiger MAX_FILE_SIZE direktivet som anges i HTML-formulär", -"The uploaded file was only partially uploaded" => "Den uppladdade filen var endast delvist uppladdad", +"The uploaded file was only partially uploaded" => "Den uppladdade filen var endast delvis uppladdad", "No file was uploaded" => "Ingen fil blev uppladdad", "Missing a temporary folder" => "Saknar en tillfällig mapp", "Failed to write to disk" => "Misslyckades spara till disk", "Files" => "Filer", +"Delete" => "Radera", +"already exists" => "finns redan", +"replace" => "ersätt", +"cancel" => "avbryt", +"replaced" => "ersatt", +"with" => "med", +"undo" => "Ã¥ngra", +"deleted" => "raderad", +"generating ZIP-file, it may take some time." => "genererar ZIP-fil, det kan ta lite tid.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Kunde inte ladda upp dina filer eftersom det antingen är en mapp eller har 0 bytes.", +"Upload Error" => "Uppladdningsfel", +"Pending" => "Väntar", +"Upload cancelled." => "Uppladdning avbruten.", +"Invalid name, '/' is not allowed." => "Ogiltigt namn, '/' är inte tillÃ¥ten.", "Size" => "Storlek", "Modified" => "Ändrad", +"folder" => "mapp", +"folders" => "mappar", +"file" => "fil", +"files" => "filer", "File handling" => "Filhantering", "Maximum upload size" => "Maximal storlek att ladda upp", "max. possible: " => "max. möjligt:", -"Needed for multi-file and folder downloads." => "Krävs för nerladdning av flera mappar och filer", +"Needed for multi-file and folder downloads." => "Krävs för nerladdning av flera mappar och filer.", "Enable ZIP-download" => "Aktivera ZIP-nerladdning", -"0 is unlimited" => "0 är lika med oändligt", -"Maximum input size for ZIP files" => "Största tillÃ¥tna storlek för ZIP filer", +"0 is unlimited" => "0 är oändligt", +"Maximum input size for ZIP files" => "Största tillÃ¥tna storlek för ZIP-filer", "New" => "Ny", "Text file" => "Textfil", "Folder" => "Mapp", @@ -25,10 +43,9 @@ "Nothing in here. Upload something!" => "Ingenting här. Ladda upp nÃ¥got!", "Name" => "Namn", "Share" => "Dela", -"Download" => "Ladda ned", -"Delete" => "Ta bort", +"Download" => "Ladda ner", "Upload too large" => "För stor uppladdning", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Filerna du försöker ladda upp överstiger den maximala storleken för filöverföringar pÃ¥ servern.", -"Files are being scanned, please wait." => "Filerna skannas, var god vänta", -"Current scanning" => "Aktuell avsökning" +"Files are being scanned, please wait." => "Filer skannas, var god vänta", +"Current scanning" => "Aktuell skanning" ); diff --git a/apps/files/l10n/th_TH.php b/apps/files/l10n/th_TH.php index ec538262e02..eca0e29a182 100644 --- a/apps/files/l10n/th_TH.php +++ b/apps/files/l10n/th_TH.php @@ -7,8 +7,26 @@ "Missing a temporary folder" => "à¹à¸Ÿà¹‰à¸¡à¹€à¸à¸à¸ªà¸²à¸£à¸Šà¸±à¹ˆà¸§à¸„ราวเà¸à¸´à¸”à¸à¸²à¸£à¸ªà¸¹à¸à¸«à¸²à¸¢", "Failed to write to disk" => "เขียนข้à¸à¸¡à¸¹à¸¥à¸¥à¸‡à¹à¸œà¹ˆà¸™à¸”ิสà¸à¹Œà¸¥à¹‰à¸¡à¹€à¸«à¸¥à¸§", "Files" => "ไฟล์", +"Delete" => "ลบ", +"already exists" => "มีà¸à¸¢à¸¹à¹ˆà¹à¸¥à¹‰à¸§", +"replace" => "à¹à¸—นที่", +"cancel" => "ยà¸à¹€à¸¥à¸´à¸", +"replaced" => "à¹à¸—นที่à¹à¸¥à¹‰à¸§", +"with" => "à¸à¸±à¸š", +"undo" => "เลิà¸à¸—ำ", +"deleted" => "ลบà¹à¸¥à¹‰à¸§", +"generating ZIP-file, it may take some time." => "à¸à¸³à¸¥à¸±à¸‡à¸ªà¸£à¹‰à¸²à¸‡à¹„ฟล์บีบà¸à¸±à¸” ZIP à¸à¸²à¸ˆà¹ƒà¸Šà¹‰à¹€à¸§à¸¥à¸²à¸ªà¸±à¸à¸„รู่", +"Unable to upload your file as it is a directory or has 0 bytes" => "ไม่สามารถà¸à¸±à¸žà¹‚หลดไฟล์ขà¸à¸‡à¸„ุณได้ เนื่à¸à¸‡à¸ˆà¸²à¸à¹„ฟล์ดังà¸à¸¥à¹ˆà¸²à¸§à¹€à¸›à¹‡à¸™à¹„ดเร็à¸à¸—à¸à¸£à¸µà¹ˆà¸«à¸£à¸·à¸à¸¡à¸µà¸‚นาด 0 ไบต์", +"Upload Error" => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¸à¸±à¸žà¹‚หลด", +"Pending" => "à¸à¸¢à¸¹à¹ˆà¸£à¸°à¸«à¸§à¹ˆà¸²à¸‡à¸”ำเนินà¸à¸²à¸£", +"Upload cancelled." => "à¸à¸²à¸£à¸à¸±à¸žà¹‚หลดถูà¸à¸¢à¸à¹€à¸¥à¸´à¸", +"Invalid name, '/' is not allowed." => "ชื่à¸à¸—ี่ใช้ไม่ถูà¸à¸•à¹‰à¸à¸‡ '/' ไม่à¸à¸™à¸¸à¸à¸²à¸•à¹ƒà¸«à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™", "Size" => "ขนาด", "Modified" => "ปรับปรุงล่าสุด", +"folder" => "โฟลเดà¸à¸£à¹Œ", +"folders" => "โฟลเดà¸à¸£à¹Œ", +"file" => "ไฟล์", +"files" => "ไฟล์", "File handling" => "à¸à¸²à¸£à¸ˆà¸±à¸”à¸à¸²à¹„ฟล์", "Maximum upload size" => "ขนาดไฟล์สูงสุดที่à¸à¸±à¸žà¹‚หลดได้", "max. possible: " => "จำนวนสูงสุดที่สามารถทำได้: ", @@ -26,7 +44,7 @@ "Name" => "ชื่à¸", "Share" => "à¹à¸Šà¸£à¹Œ", "Download" => "ดาวน์โหลด", -"Delete" => "ลบ", +"Delete all" => "ลบทั้งหมด", "Upload too large" => "ไฟล์ที่à¸à¸±à¸žà¹‚หลดมีขนาดใหà¸à¹ˆà¹€à¸à¸´à¸™à¹„ป", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "ไฟล์ที่คุณพยายามที่จะà¸à¸±à¸žà¹‚หลดมีขนาดเà¸à¸´à¸™à¸à¸§à¹ˆà¸²à¸‚นาดสูงสุดที่à¸à¸³à¸«à¸™à¸”ไว้ให้à¸à¸±à¸žà¹‚หลดได้สำหรับเซิร์ฟเวà¸à¸£à¹Œà¸™à¸µà¹‰", "Files are being scanned, please wait." => "ไฟล์à¸à¸³à¸¥à¸±à¸‡à¸à¸¢à¸¹à¹ˆà¸£à¸°à¸«à¸§à¹ˆà¸²à¸‡à¸à¸²à¸£à¸ªà¹à¸à¸™, à¸à¸£à¸¸à¸“ารà¸à¸ªà¸±à¸à¸„รู่.", diff --git a/apps/files/l10n/tr.php b/apps/files/l10n/tr.php index b8de8249a1d..528eede6645 100644 --- a/apps/files/l10n/tr.php +++ b/apps/files/l10n/tr.php @@ -7,8 +7,21 @@ "Missing a temporary folder" => "Geçici bir klasör eksik", "Failed to write to disk" => "Diske yazılamadı", "Files" => "Dosyalar", +"Delete" => "Sil", +"undo" => "geri al", +"deleted" => "silindi", +"generating ZIP-file, it may take some time." => "ZIP dosyası oluÅŸturuluyor, biraz sürebilir.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Dosyanızın boyutu 0 byte olduÄŸundan veya bir dizin olduÄŸundan yüklenemedi", +"Upload Error" => "Yükleme hatası", +"Pending" => "Bekliyor", +"Upload cancelled." => "Yükleme iptal edildi.", +"Invalid name, '/' is not allowed." => "Geçersiz isim, '/' iÅŸaretine izin verilmiyor.", "Size" => "Boyut", "Modified" => "DeÄŸiÅŸtirilme", +"folder" => "dizin", +"folders" => "dizinler", +"file" => "dosya", +"files" => "dosyalar", "File handling" => "Dosya taşıma", "Maximum upload size" => "Maksimum yükleme boyutu", "max. possible: " => "mümkün olan en fazla: ", @@ -26,7 +39,7 @@ "Name" => "Ad", "Share" => "PaylaÅŸ", "Download" => "Ä°ndir", -"Delete" => "Sil", +"Delete all" => "Hepsini sil", "Upload too large" => "Yüklemeniz çok büyük", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Yüklemeye çalıştığınız dosyalar bu sunucudaki maksimum yükleme boyutunu aşıyor.", "Files are being scanned, please wait." => "Dosyalar taranıyor, lütfen bekleyin.", diff --git a/apps/files/l10n/uk.php b/apps/files/l10n/uk.php index 8b21cb26a3c..dc7e9d18b70 100644 --- a/apps/files/l10n/uk.php +++ b/apps/files/l10n/uk.php @@ -6,17 +6,36 @@ "No file was uploaded" => "Ðе відвантажено жодного файлу", "Missing a temporary folder" => "ВідÑутній тимчаÑовий каталог", "Files" => "Файли", +"Delete" => "Видалити", +"undo" => "відмінити", +"deleted" => "видалені", +"generating ZIP-file, it may take some time." => "Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ZIP-файлу, це може зайнÑти певний чаÑ.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Ðеможливо завантажити ваш файл тому, що він тека або файл розміром 0 байт", +"Upload Error" => "Помилка завантаженнÑ", +"Pending" => "ОчікуваннÑ", +"Upload cancelled." => "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð¿ÐµÑ€ÐµÑ€Ð²Ð°Ð½Ð¾.", +"Invalid name, '/' is not allowed." => "Ðекоректне ім'Ñ, '/' не дозволено.", "Size" => "Розмір", "Modified" => "Змінено", +"folder" => "тека", +"folders" => "теки", +"file" => "файл", +"files" => "файли", "Maximum upload size" => "МакÑимальний розмір відвантажень", +"max. possible: " => "макÑ.можливе:", +"0 is unlimited" => "0 Ñ” безліміт", "New" => "Створити", "Text file" => "ТекÑтовий файл", "Folder" => "Папка", +"From url" => "З URL", "Upload" => "Відвантажити", +"Cancel upload" => "Перервати завантаженнÑ", "Nothing in here. Upload something!" => "Тут нічого немає. Відвантажте що-небудь!", "Name" => "Ім'Ñ", +"Share" => "ПоділитиÑÑ", "Download" => "Завантажити", -"Delete" => "Видалити", "Upload too large" => "Файл занадто великий", -"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Файли,що ви намагаєтеÑÑŒ відвантажити перевищують макÑимальний дозволений розмір файлів на цьому Ñервері." +"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Файли,що ви намагаєтеÑÑŒ відвантажити перевищують макÑимальний дозволений розмір файлів на цьому Ñервері.", +"Files are being scanned, please wait." => "Файли ÑкануютьÑÑ, зачекайте, будь-лаÑка.", +"Current scanning" => "Поточне ÑкануваннÑ" ); diff --git a/apps/files/l10n/vi.php b/apps/files/l10n/vi.php new file mode 100644 index 00000000000..c2284d5feb9 --- /dev/null +++ b/apps/files/l10n/vi.php @@ -0,0 +1,31 @@ +<?php $TRANSLATIONS = array( +"Files" => "Táºp tin", +"Delete" => "Xóa", +"Upload Error" => "Tải lên lá»—i", +"Pending" => "Chá»", +"Upload cancelled." => "Hủy tải lên", +"Invalid name, '/' is not allowed." => "Tên không hợp lệ ,không được phép dùng '/'", +"Size" => "KÃch cỡ", +"Modified" => "Thay đổi", +"folder" => "folder", +"folders" => "folders", +"file" => "file", +"files" => "files", +"File handling" => "Xá» lý táºp tin", +"Maximum upload size" => "KÃch thÆ°á»›c tối Ä‘a ", +"Enable ZIP-download" => "Cho phép ZIP-download", +"0 is unlimited" => "0 là không giá»›i hạn", +"Maximum input size for ZIP files" => "KÃch thÆ°á»›c tối Ä‘a cho các táºp tin ZIP", +"New" => "Má»›i", +"Text file" => "Táºp tin văn bản", +"Folder" => "Folder", +"From url" => "Từ url", +"Upload" => "Tải lên", +"Cancel upload" => "Hủy upload", +"Nothing in here. Upload something!" => "Không có gì ở đây .Hãy tải lên má»™t cái gì đó !", +"Name" => "Tên", +"Share" => "Chia sẻ", +"Download" => "Tải xuống", +"Upload too large" => "File tải lên quá lá»›n", +"Files are being scanned, please wait." => "Táºp tin Ä‘ang được quét ,vui lòng chá»." +); diff --git a/apps/files/l10n/zh_CN.GB2312.php b/apps/files/l10n/zh_CN.GB2312.php new file mode 100644 index 00000000000..6703e6f9088 --- /dev/null +++ b/apps/files/l10n/zh_CN.GB2312.php @@ -0,0 +1,51 @@ +<?php $TRANSLATIONS = array( +"There is no error, the file uploaded with success" => "没有任何错误,æ–‡ä»¶ä¸Šä¼ æˆåŠŸäº†", +"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "ä¸Šä¼ çš„æ–‡ä»¶è¶…è¿‡äº†php.ini指定的upload_max_filesize", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "ä¸Šä¼ çš„æ–‡ä»¶è¶…è¿‡äº†HTML表å•æŒ‡å®šçš„MAX_FILE_SIZE", +"The uploaded file was only partially uploaded" => "文件åªæœ‰éƒ¨åˆ†è¢«ä¸Šä¼ ", +"No file was uploaded" => "æ²¡æœ‰ä¸Šä¼ å®Œæˆçš„文件", +"Missing a temporary folder" => "丢失了一个临时文件夹", +"Failed to write to disk" => "写ç£ç›˜å¤±è´¥", +"Files" => "文件", +"Delete" => "åˆ é™¤", +"already exists" => "å·²ç»å˜åœ¨äº†", +"replace" => "替æ¢", +"cancel" => "å–消", +"replaced" => "替æ¢è¿‡äº†", +"with" => "éšç€", +"undo" => "撤销", +"deleted" => "åˆ é™¤", +"generating ZIP-file, it may take some time." => "æ£åœ¨ç”ŸæˆZIP文件,è¿™å¯èƒ½éœ€è¦ç‚¹æ—¶é—´", +"Unable to upload your file as it is a directory or has 0 bytes" => "ä¸èƒ½ä¸Šä¼ ä½ æŒ‡å®šçš„æ–‡ä»¶,å¯èƒ½å› 为它是个文件夹或者大å°ä¸º0", +"Upload Error" => "ä¸Šä¼ é”™è¯¯", +"Pending" => "Pending", +"Upload cancelled." => "ä¸Šä¼ å–消了", +"Invalid name, '/' is not allowed." => "éžæ³•æ–‡ä»¶å,\"/\"是ä¸è¢«è®¸å¯çš„", +"Size" => "大å°", +"Modified" => "修改日期", +"folder" => "文件夹", +"folders" => "文件夹", +"file" => "文件", +"files" => "文件", +"File handling" => "文件处ç†ä¸", +"Maximum upload size" => "æœ€å¤§ä¸Šä¼ å¤§å°", +"max. possible: " => "最大å¯èƒ½", +"Needed for multi-file and folder downloads." => "需è¦å¤šæ–‡ä»¶å’Œæ–‡ä»¶å¤¹ä¸‹è½½.", +"Enable ZIP-download" => "支æŒZIP下载", +"0 is unlimited" => "0æ˜¯æ— é™çš„", +"Maximum input size for ZIP files" => "最大的ZIP文件输入大å°", +"New" => "新建", +"Text file" => "文本文档", +"Folder" => "文件夹", +"From url" => "从URL:", +"Upload" => "ä¸Šä¼ ", +"Cancel upload" => "å–æ¶ˆä¸Šä¼ ", +"Nothing in here. Upload something!" => "这里没有东西.ä¸Šä¼ ç‚¹ä»€ä¹ˆ!", +"Name" => "åå—", +"Share" => "分享", +"Download" => "下载", +"Upload too large" => "ä¸Šä¼ çš„æ–‡ä»¶å¤ªå¤§äº†", +"The files you are trying to upload exceed the maximum size for file uploads on this server." => "ä½ æ£åœ¨è¯•å›¾ä¸Šä¼ 的文件超过了æ¤æœåŠ¡å™¨æ”¯æŒçš„最大的文件大å°.", +"Files are being scanned, please wait." => "æ£åœ¨æ‰«æ文件,请ç¨å€™.", +"Current scanning" => "æ£åœ¨æ‰«æ" +); diff --git a/apps/files/l10n/zh_CN.php b/apps/files/l10n/zh_CN.php index c2e6541f2a2..c6fc9f907d6 100644 --- a/apps/files/l10n/zh_CN.php +++ b/apps/files/l10n/zh_CN.php @@ -7,8 +7,25 @@ "Missing a temporary folder" => "缺少临时目录", "Failed to write to disk" => "写入ç£ç›˜å¤±è´¥", "Files" => "文件", +"Delete" => "åˆ é™¤", +"already exists" => "å·²ç»å˜åœ¨", +"replace" => "替æ¢", +"cancel" => "å–消", +"replaced" => "å·²ç»æ›¿æ¢", +"undo" => "撤销", +"deleted" => "å·²ç»åˆ 除", +"generating ZIP-file, it may take some time." => "æ£åœ¨ç”Ÿæˆ ZIP 文件,å¯èƒ½éœ€è¦ä¸€äº›æ—¶é—´", +"Unable to upload your file as it is a directory or has 0 bytes" => "æ— æ³•ä¸Šä¼ æ–‡ä»¶ï¼Œå› ä¸ºå®ƒæ˜¯ä¸€ä¸ªç›®å½•æˆ–è€…å¤§å°ä¸º 0 å—节", +"Upload Error" => "ä¸Šä¼ é”™è¯¯", +"Pending" => "æ“作ç‰å¾…ä¸", +"Upload cancelled." => "ä¸Šä¼ å·²å–消", +"Invalid name, '/' is not allowed." => "éžæ³•çš„å称,ä¸å…许使用‘/’。", "Size" => "大å°", "Modified" => "修改日期", +"folder" => "文件夹", +"folders" => "文件夹", +"file" => "文件", +"files" => "文件", "File handling" => "文件处ç†", "Maximum upload size" => "æœ€å¤§ä¸Šä¼ å¤§å°", "max. possible: " => "最大å¯èƒ½: ", @@ -26,7 +43,7 @@ "Name" => "å称", "Share" => "共享", "Download" => "下载", -"Delete" => "åˆ é™¤", +"Delete all" => "åˆ é™¤æ‰€æœ‰", "Upload too large" => "ä¸Šä¼ æ–‡ä»¶è¿‡å¤§", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "您æ£å°è¯•ä¸Šä¼ 的文件超过了æ¤æœåŠ¡å™¨å¯ä»¥ä¸Šä¼ 的最大大å°", "Files are being scanned, please wait." => "文件æ£åœ¨è¢«æ‰«æ,请ç¨å€™ã€‚", diff --git a/apps/files/l10n/zh_TW.php b/apps/files/l10n/zh_TW.php index 91a1344ca37..9151a4805df 100644 --- a/apps/files/l10n/zh_TW.php +++ b/apps/files/l10n/zh_TW.php @@ -7,6 +7,7 @@ "Missing a temporary folder" => "éºå¤±æš«å˜è³‡æ–™å¤¾", "Failed to write to disk" => "寫入硬碟失敗", "Files" => "檔案", +"Delete" => "刪除", "Size" => "大å°", "Modified" => "修改", "File handling" => "檔案處ç†", @@ -26,7 +27,7 @@ "Name" => "å稱", "Share" => "分享", "Download" => "下載", -"Delete" => "刪除", +"Delete all" => "全部刪除", "Upload too large" => "上傳éŽå¤§", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "ä½ è©¦åœ–ä¸Šå‚³çš„æª”æ¡ˆå·²è¶…éŽä¼ºæœå™¨çš„最大容é‡é™åˆ¶ã€‚ ", "Files are being scanned, please wait." => "æ£åœ¨æŽƒæ檔案,請ç¨ç‰ã€‚", diff --git a/apps/files/settings.php b/apps/files/settings.php index e5a66239ebd..cd6dd8c1616 100644 --- a/apps/files/settings.php +++ b/apps/files/settings.php @@ -56,5 +56,3 @@ $tmpl = new OCP\Template( "files", "index", "user" ); $tmpl->assign( 'files', $files ); $tmpl->assign( "breadcrumb", $breadcrumb ); $tmpl->printPage(); - -?> diff --git a/apps/files/templates/admin.php b/apps/files/templates/admin.php index c89070ce52e..23021ec6647 100644 --- a/apps/files/templates/admin.php +++ b/apps/files/templates/admin.php @@ -3,7 +3,7 @@ <form name="filesForm" action='#' method='post'> <fieldset class="personalblock"> <legend><strong><?php echo $l->t('File handling');?></strong></legend> - <?php if($_['htaccessWorking']):?> + <?php if($_['uploadChangable']):?> <label for="maxUploadSize"><?php echo $l->t( 'Maximum upload size' ); ?> </label><input name='maxUploadSize' id="maxUploadSize" value='<?php echo $_['uploadMaxFilesize'] ?>'/>(<?php echo $l->t('max. possible: '); echo $_['maxPossibleUploadSize'] ?>)<br/> <?php endif;?> <input type="checkbox" name="allowZipDownload" id="allowZipDownload" value="1" title="<?php echo $l->t( 'Needed for multi-file and folder downloads.' ); ?>"<?php if ($_['allowZipDownload']) echo ' checked="checked"'; ?> /> <label for="allowZipDownload"><?php echo $l->t( 'Enable ZIP-download' ); ?></label> <br/> diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php index c0a40081fe3..bcf683ae4a8 100644 --- a/apps/files/templates/index.php +++ b/apps/files/templates/index.php @@ -1,8 +1,8 @@ <!--[if IE 8]><style>input[type="checkbox"]{padding:0;}table td{position:static !important;}</style><![endif]--> <div id="controls"> <?php echo($_['breadcrumb']); ?> - <?php if (!isset($_['readonly']) || !$_['readonly']):?> - <div class="actions <?php if (isset($_['files']) and ! $_['readonly'] and count($_['files'])==0):?>emptyfolder<?php endif; ?>"> + <?php if ($_['isCreatable']):?> + <div class="actions <?php if (isset($_['files']) and count($_['files'])==0):?>emptyfolder<?php endif; ?>"> <div id='new' class='button'> <a><?php echo $l->t('New');?></a> <ul class="popup popupTop"> @@ -15,7 +15,7 @@ <form data-upload-id='1' class="file_upload_form" action="<?php echo OCP\Util::linkTo('files', 'ajax/upload.php'); ?>" method="post" enctype="multipart/form-data" target="file_upload_target_1"> <input type="hidden" name="MAX_FILE_SIZE" value="<?php echo $_['uploadMaxFilesize'] ?>" id="max_upload"> <input type="hidden" class="max_human_file_size" value="(max <?php echo $_['uploadMaxHumanFilesize']; ?>)"> - <input type="hidden" name="dir" value="<?php echo htmlentities($_['dir'],ENT_COMPAT,'utf-8') ?>" id="dir"> + <input type="hidden" name="dir" value="<?php echo $_['dir'] ?>" id="dir"> <button class="file_upload_filename"> <img class='svg action' alt="Upload" src="<?php echo OCP\image_path("core", "actions/upload-white.svg"); ?>" /></button> <input class="file_upload_start" type="file" name='files[]'/> <a href="#" class="file_upload_button_wrapper" onclick="return false;" title="<?php echo $l->t('Upload'); echo ' max. '.$_['uploadMaxHumanFilesize'] ?>"></a> @@ -35,7 +35,7 @@ </div> <div id='notification'></div> -<?php if (isset($_['files']) and ! $_['readonly'] and count($_['files'])==0):?> +<?php if (isset($_['files']) and $_['isCreatable'] and count($_['files'])==0):?> <div id="emptyfolder"><?php echo $l->t('Nothing in here. Upload something!')?></div> <?php endif; ?> @@ -43,7 +43,7 @@ <thead> <tr> <th id='headerName'> - <?php if(!isset($_['readonly']) || !$_['readonly']) { ?><input type="checkbox" id="select_all" /><?php } ?> + <input type="checkbox" id="select_all" /> <span class='name'><?php echo $l->t( 'Name' ); ?></span> <span class='selectedActions'> <!-- <a href="" class="share"><img class='svg' alt="Share" src="<?php echo OCP\image_path("core", "actions/share.svg"); ?>" /> <?php echo $l->t('Share')?></a> --> @@ -53,10 +53,10 @@ </span> </th> <th id="headerSize"><?php echo $l->t( 'Size' ); ?></th> - <th id="headerDate"><span id="modified"><?php echo $l->t( 'Modified' ); ?></span><span class="selectedActions"><a href="" class="delete"><?php echo $l->t('Delete all')?> <img class="svg" alt="<?php echo $l->t('Delete')?>" src="<?php echo OCP\image_path("core", "actions/delete.svg"); ?>" /></a></span></th> + <th id="headerDate"><span id="modified"><?php echo $l->t( 'Modified' ); ?></span><span class="selectedActions"><a href="" class="delete"><?php echo $l->t('Delete')?> <img class="svg" alt="<?php echo $l->t('Delete')?>" src="<?php echo OCP\image_path("core", "actions/delete.svg"); ?>" /></a></span></th> </tr> </thead> - <tbody id="fileList" data-readonly="<?php echo $_['readonly'];?>"> + <tbody id="fileList"> <?php echo($_['fileList']); ?> </tbody> </table> diff --git a/apps/files/templates/part.breadcrumb.php b/apps/files/templates/part.breadcrumb.php index 43fe2d1fa95..22d9bb4490d 100644 --- a/apps/files/templates/part.breadcrumb.php +++ b/apps/files/templates/part.breadcrumb.php @@ -1,6 +1,6 @@ <?php for($i=0; $i<count($_["breadcrumb"]); $i++): $crumb = $_["breadcrumb"][$i]; ?> <div class="crumb <?php if($i == count($_["breadcrumb"])-1) echo 'last';?> svg" data-dir='<?php echo $crumb["dir"];?>' style='background-image:url("<?php echo OCP\image_path('core','breadcrumb.png');?>")'> - <a href="<?php echo $_['baseURL'].$crumb["dir"]; ?>"><?php echo htmlentities($crumb["name"],ENT_COMPAT,'utf-8'); ?></a> + <a href="<?php echo $_['baseURL'].$crumb["dir"]; ?>"><?php echo OCP\Util::sanitizeHTML($crumb["name"]); ?></a> </div> <?php endfor;?> diff --git a/apps/files/templates/part.list.php b/apps/files/templates/part.list.php index 4506630c16d..6667b5488af 100644 --- a/apps/files/templates/part.list.php +++ b/apps/files/templates/part.list.php @@ -1,5 +1,4 @@ <?php foreach($_['files'] as $file): - $write = ($file['writable']) ? 'true' : 'false'; $simple_file_size = OCP\simple_file_size($file['size']); $simple_size_color = intval(200-$file['size']/(1024*1024)*2); // the bigger the file, the darker the shade of grey; megabytes*2 if($simple_size_color<0) $simple_size_color = 0; @@ -10,7 +9,7 @@ $name = str_replace('%2F','/', $name); $directory = str_replace('+','%20',urlencode($file['directory'])); $directory = str_replace('%2F','/', $directory); ?> - <tr data-file="<?php echo $name;?>" data-type="<?php echo ($file['type'] == 'dir')?'dir':'file'?>" data-mime="<?php echo $file['mimetype']?>" data-size='<?php echo $file['size'];?>' data-write='<?php echo $write;?>'> + <tr data-file="<?php echo $name;?>" data-type="<?php echo ($file['type'] == 'dir')?'dir':'file'?>" data-mime="<?php echo $file['mimetype']?>" data-size='<?php echo $file['size'];?>' data-permissions='<?php echo $file['permissions']; ?>'> <td class="filename svg" style="background-image:url(<?php if($file['type'] == 'dir') echo OCP\mimetype_icon('dir'); else echo OCP\mimetype_icon($file['mimetype']); ?>)"> <?php if(!isset($_['readonly']) || !$_['readonly']) { ?><input type="checkbox" /><?php } ?> <a class="name" href="<?php if($file['type'] == 'dir') echo $_['baseURL'].$directory.'/'.$name; else echo $_['downloadURL'].$directory.'/'.$name; ?>" title=""> diff --git a/apps/files_archive/js/archive.js b/apps/files_archive/js/archive.js index 9fb9853e299..6bcbe092662 100644 --- a/apps/files_archive/js/archive.js +++ b/apps/files_archive/js/archive.js @@ -7,11 +7,11 @@ $(document).ready(function() { if(typeof FileActions!=='undefined'){ - FileActions.register('application/zip','Open','',function(filename){ + FileActions.register('application/zip','Open', FileActions.PERMISSION_READ, '',function(filename){ window.location=OC.linkTo('files', 'index.php')+'&dir='+encodeURIComponent($('#dir').val()).replace(/%2F/g, '/')+'/'+encodeURIComponent(filename); }); FileActions.setDefault('application/zip','Open'); - FileActions.register('application/x-gzip','Open','',function(filename){ + FileActions.register('application/x-gzip','Open', FileActions.PERMISSION_READ, '',function(filename){ window.location=OC.linkTo('files', 'index.php')+'&dir='+encodeURIComponent($('#dir').val()).replace(/%2F/g, '/')+'/'+encodeURIComponent(filename); }); FileActions.setDefault('application/x-gzip','Open'); diff --git a/apps/files_archive/lib/storage.php b/apps/files_archive/lib/storage.php index 86761663611..3c14c3e1fdf 100644 --- a/apps/files_archive/lib/storage.php +++ b/apps/files_archive/lib/storage.php @@ -38,22 +38,30 @@ class OC_Filestorage_Archive extends OC_Filestorage_Common{ return $this->archive->remove($path.'/'); } public function opendir($path){ + if(substr($path,-1)!=='/'){ + $path.='/'; + } $path=$this->stripPath($path); - $content=$this->archive->getFolder($path); - foreach($content as &$file){ + $files=$this->archive->getFolder($path); + $content=array(); + foreach($files as $file){ if(substr($file,-1)=='/'){ $file=substr($file,0,-1); } + if($file and strpos($file,'/')===false){ + $content[]=$file; + } } $id=md5($this->path.$path); OC_FakeDirStream::$dirs[$id]=$content; return opendir('fakedir://'.$id); } public function stat($path){ - $ctime=filectime($this->path); + $ctime=-1; $path=$this->stripPath($path); if($path==''){ $stat=stat($this->path); + $stat['size']=0; }else{ if($this->is_dir($path)){ $stat=array('size'=>0); @@ -62,6 +70,9 @@ class OC_Filestorage_Archive extends OC_Filestorage_Common{ $stat=array(); $stat['mtime']=$this->archive->mtime($path); $stat['size']=$this->archive->filesize($path); + if(!$stat['mtime']){ + $stat['mtime']=time(); + } } } $stat['ctime']=$ctime; @@ -78,10 +89,10 @@ class OC_Filestorage_Archive extends OC_Filestorage_Common{ return $this->archive->fileExists($path.'/')?'dir':'file'; } } - public function is_readable($path){ + public function isReadable($path){ return is_readable($this->path); } - public function is_writable($path){ + public function isUpdatable($path){ return is_writable($this->path); } public function file_exists($path){ @@ -111,6 +122,19 @@ class OC_Filestorage_Archive extends OC_Filestorage_Common{ return false;//not supported } } + private function toTmpFile($path){ + $tmpFile=OC_Helper::tmpFile($extension); + $this->archive->extractFile($path,$tmpFile); + return $tmpFile; + } + public function file_put_contents($path,$data) { + $path=$this->stripPath($path); + return $this->archive->addFile($path,$data); + } + public function file_get_contents($path) { + $path=$this->stripPath($path); + return $this->archive->getFile($path); + } /** * automount paths from file hooks @@ -143,4 +167,8 @@ class OC_Filestorage_Archive extends OC_Filestorage_Common{ public function rename($path1,$path2){ return $this->archive->rename($path1,$path2); } + + public function hasUpdated($path,$time){ + return $this->filemtime($this->path)>$time; + } } diff --git a/apps/files_encryption/js/settings.js b/apps/files_encryption/js/settings.js index 37d62265c94..8cc433246cb 100644 --- a/apps/files_encryption/js/settings.js +++ b/apps/files_encryption/js/settings.js @@ -17,8 +17,8 @@ $(document).ready(function(){ OC.AppConfig.setValue('files_encryption','type_blacklist',blackList); } - $('#enbale_encryption').change(function(){ - var checked=$('#enbale_encryption').is(':checked'); + $('#enable_encryption').change(function(){ + var checked=$('#enable_encryption').is(':checked'); OC.AppConfig.setValue('files_encryption','enable_encryption',(checked)?'true':'false'); }) })
\ No newline at end of file diff --git a/apps/files_encryption/l10n/.gitkeep b/apps/files_encryption/l10n/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/apps/files_encryption/l10n/.gitkeep diff --git a/apps/files_encryption/l10n/ca.php b/apps/files_encryption/l10n/ca.php new file mode 100644 index 00000000000..8e087b34620 --- /dev/null +++ b/apps/files_encryption/l10n/ca.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Encryption" => "Encriptatge", +"Exclude the following file types from encryption" => "Exclou els tipus de fitxers següents de l'encriptatge", +"None" => "Cap", +"Enable Encryption" => "Activa l'encriptatge" +); diff --git a/apps/files_encryption/l10n/cs_CZ.php b/apps/files_encryption/l10n/cs_CZ.php new file mode 100644 index 00000000000..15cf7705709 --- /dev/null +++ b/apps/files_encryption/l10n/cs_CZ.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Encryption" => "KryptovánÃ", +"Exclude the following file types from encryption" => "PÅ™i kryptovánà vynechat následujÃcà typy souborů", +"None" => "Žádný", +"Enable Encryption" => "Povolit kryptovánÃ" +); diff --git a/apps/files_encryption/l10n/de.php b/apps/files_encryption/l10n/de.php new file mode 100644 index 00000000000..d486a82322b --- /dev/null +++ b/apps/files_encryption/l10n/de.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Encryption" => "Verschlüsselung", +"Exclude the following file types from encryption" => "Die folgenden Dateitypen von der Verschlüsselung ausnehmen", +"None" => "Keine", +"Enable Encryption" => "Verschlüsselung aktivieren" +); diff --git a/apps/files_encryption/l10n/el.php b/apps/files_encryption/l10n/el.php new file mode 100644 index 00000000000..40a7c6a3672 --- /dev/null +++ b/apps/files_encryption/l10n/el.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Encryption" => "ΚÏυπτογÏάφηση", +"Exclude the following file types from encryption" => "ΕξαίÏεση των παÏακάτω Ï„Ïπων αÏχείων από την κÏυπτογÏάφηση", +"None" => "Καμία", +"Enable Encryption" => "ΕνεÏγοποίηση ΚÏυπτογÏάφησης" +); diff --git a/apps/files_encryption/l10n/eo.php b/apps/files_encryption/l10n/eo.php new file mode 100644 index 00000000000..af3c9ae98e4 --- /dev/null +++ b/apps/files_encryption/l10n/eo.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Encryption" => "Ĉifrado", +"Exclude the following file types from encryption" => "Malinkluzivigi la jenajn dosiertipojn el ĉifrado", +"None" => "Nenio", +"Enable Encryption" => "Kapabligi ĉifradon" +); diff --git a/apps/files_encryption/l10n/es.php b/apps/files_encryption/l10n/es.php new file mode 100644 index 00000000000..26ace24e336 --- /dev/null +++ b/apps/files_encryption/l10n/es.php @@ -0,0 +1,3 @@ +<?php $TRANSLATIONS = array( +"Encryption" => "Cifrado" +); diff --git a/apps/files_encryption/l10n/et_EE.php b/apps/files_encryption/l10n/et_EE.php new file mode 100644 index 00000000000..a7cd9395bf0 --- /dev/null +++ b/apps/files_encryption/l10n/et_EE.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Encryption" => "Krüpteerimine", +"Exclude the following file types from encryption" => "Järgnevaid failitüüpe ära krüpteeri", +"None" => "Pole", +"Enable Encryption" => "Luba krüpteerimine" +); diff --git a/apps/files_encryption/l10n/fa.php b/apps/files_encryption/l10n/fa.php new file mode 100644 index 00000000000..0faa3f3aae7 --- /dev/null +++ b/apps/files_encryption/l10n/fa.php @@ -0,0 +1,5 @@ +<?php $TRANSLATIONS = array( +"Encryption" => "رمزگذاری", +"None" => "هیچ‌کدام", +"Enable Encryption" => "Ùعال کردن رمزگذاری" +); diff --git a/apps/files_encryption/l10n/fi_FI.php b/apps/files_encryption/l10n/fi_FI.php new file mode 100644 index 00000000000..5796499a26c --- /dev/null +++ b/apps/files_encryption/l10n/fi_FI.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Encryption" => "Salaus", +"Exclude the following file types from encryption" => "Jätä seuraavat tiedostotyypit salaamatta", +"None" => "Ei mitään", +"Enable Encryption" => "Käytä salausta" +); diff --git a/apps/files_encryption/l10n/fr.php b/apps/files_encryption/l10n/fr.php new file mode 100644 index 00000000000..c9367d1a312 --- /dev/null +++ b/apps/files_encryption/l10n/fr.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Encryption" => "Chiffrement", +"Exclude the following file types from encryption" => "Ne pas chiffrer les fichiers dont les types sont les suivants", +"None" => "Aucun", +"Enable Encryption" => "Activer le chiffrement" +); diff --git a/apps/files_encryption/l10n/it.php b/apps/files_encryption/l10n/it.php new file mode 100644 index 00000000000..5136b061797 --- /dev/null +++ b/apps/files_encryption/l10n/it.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Encryption" => "Cifratura", +"Exclude the following file types from encryption" => "Escludi i seguenti tipi di file dalla cifratura", +"None" => "Nessuna", +"Enable Encryption" => "Abilita cifratura" +); diff --git a/apps/files_encryption/l10n/ja_JP.php b/apps/files_encryption/l10n/ja_JP.php new file mode 100644 index 00000000000..2c3e5410de3 --- /dev/null +++ b/apps/files_encryption/l10n/ja_JP.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Encryption" => "æš—å·åŒ–", +"Exclude the following file types from encryption" => "æš—å·åŒ–ã‹ã‚‰é™¤å¤–ã™ã‚‹ãƒ•ã‚¡ã‚¤ãƒ«ã‚¿ã‚¤ãƒ—", +"None" => "ãªã—", +"Enable Encryption" => "æš—å·åŒ–を有効ã«ã™ã‚‹" +); diff --git a/apps/files_encryption/l10n/lt_LT.php b/apps/files_encryption/l10n/lt_LT.php new file mode 100644 index 00000000000..b939df164c8 --- /dev/null +++ b/apps/files_encryption/l10n/lt_LT.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Encryption" => "Å ifravimas", +"Exclude the following file types from encryption" => "NeÅ¡ifruoti pasirinkto tipo failų", +"None" => "Nieko", +"Enable Encryption" => "Ä®jungti Å¡ifravimÄ…" +); diff --git a/apps/files_encryption/l10n/nb_NO.php b/apps/files_encryption/l10n/nb_NO.php new file mode 100644 index 00000000000..e65df7b6ce3 --- /dev/null +++ b/apps/files_encryption/l10n/nb_NO.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Encryption" => "Kryptering", +"Exclude the following file types from encryption" => "Ekskluder følgende filer fra kryptering", +"None" => "Ingen", +"Enable Encryption" => "SlÃ¥ pÃ¥ kryptering" +); diff --git a/apps/files_encryption/l10n/pl.php b/apps/files_encryption/l10n/pl.php new file mode 100644 index 00000000000..5cfc707450e --- /dev/null +++ b/apps/files_encryption/l10n/pl.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Encryption" => "Szyfrowanie", +"Exclude the following file types from encryption" => "WyÅ‚Ä…cz nastÄ™pujÄ…ce typy plików z szyfrowania", +"None" => "Brak", +"Enable Encryption" => "WÅ‚Ä…cz szyfrowanie" +); diff --git a/apps/files_encryption/l10n/sl.php b/apps/files_encryption/l10n/sl.php new file mode 100644 index 00000000000..65807910e10 --- /dev/null +++ b/apps/files_encryption/l10n/sl.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Encryption" => "Å ifriranje", +"Exclude the following file types from encryption" => "Naslednje vrste datotek naj se ne Å¡ifrirajo", +"None" => "Brez", +"Enable Encryption" => "OmogoÄi Å¡ifriranje" +); diff --git a/apps/files_encryption/l10n/sv.php b/apps/files_encryption/l10n/sv.php new file mode 100644 index 00000000000..0a477f83460 --- /dev/null +++ b/apps/files_encryption/l10n/sv.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Encryption" => "Kryptering", +"Exclude the following file types from encryption" => "Exkludera följande filtyper frÃ¥n kryptering", +"None" => "Ingen", +"Enable Encryption" => "Aktivera kryptering" +); diff --git a/apps/files_encryption/l10n/th_TH.php b/apps/files_encryption/l10n/th_TH.php new file mode 100644 index 00000000000..c2685de6e3a --- /dev/null +++ b/apps/files_encryption/l10n/th_TH.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Encryption" => "à¸à¸²à¸£à¹€à¸‚้ารหัส", +"Exclude the following file types from encryption" => "ไม่ต้à¸à¸‡à¸£à¸§à¸¡à¸Šà¸™à¸´à¸”ขà¸à¸‡à¹„ฟล์ดังต่à¸à¹„ปนี้จาà¸à¸à¸²à¸£à¹€à¸‚้ารหัส", +"None" => "ไม่ต้à¸à¸‡", +"Enable Encryption" => "เปิดใช้งานà¸à¸²à¸£à¹€à¸‚้ารหัส" +); diff --git a/apps/files_encryption/lib/crypt.php b/apps/files_encryption/lib/crypt.php index 7d39d926678..849e88ee0b2 100644 --- a/apps/files_encryption/lib/crypt.php +++ b/apps/files_encryption/lib/crypt.php @@ -3,7 +3,7 @@ * ownCloud * * @author Frank Karlitschek - * @copyright 2010 Frank Karlitschek karlitschek@kde.org + * @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -58,6 +58,7 @@ class OC_Crypt { $_SESSION['enckey']=OC_Crypt::decrypt($key, $password); } + /** * get the blowfish encryption handeler for a key * @param string $key (optional) diff --git a/apps/files_encryption/lib/cryptstream.php b/apps/files_encryption/lib/cryptstream.php index 9949c1896a8..46471911d94 100644 --- a/apps/files_encryption/lib/cryptstream.php +++ b/apps/files_encryption/lib/cryptstream.php @@ -31,9 +31,7 @@ class OC_CryptStream{ public static $sourceStreams=array(); private $source; private $path; - private $readBuffer;//for streams that dont support seeking private $meta=array();//header/meta for source stream - private $count; private $writeCache; private $size; private static $rootView; @@ -100,7 +98,6 @@ class OC_CryptStream{ public function stream_write($data){ $length=strlen($data); - $written=0; $currentPos=ftell($this->source); if($this->writeCache){ $data=$this->writeCache.$data; @@ -170,7 +167,7 @@ class OC_CryptStream{ public function stream_close(){ $this->flush(); if($this->meta['mode']!='r' and $this->meta['mode']!='rb'){ - OC_FileCache::put($this->path,array('encrypted'=>true,'size'=>$this->size),'/'); + OC_FileCache::put($this->path,array('encrypted'=>true,'size'=>$this->size),''); } return fclose($this->source); } diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index 633ae6b93d6..f25e4a662f6 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -59,7 +59,7 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ * @return bool */ private static function isEncrypted($path){ - $metadata=OC_FileCache::getCached($path,'/'); + $metadata=OC_FileCache_Cached::get($path,''); return isset($metadata['encrypted']) and (bool)$metadata['encrypted']; } @@ -68,14 +68,14 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ if (!is_resource($data)) {//stream put contents should have been converter to fopen $size=strlen($data); $data=OC_Crypt::blockEncrypt($data); - OC_FileCache::put($path,array('encrypted'=>true,'size'=>$size),'/'); + OC_FileCache::put($path,array('encrypted'=>true,'size'=>$size),''); } } } public function postFile_get_contents($path,$data){ if(self::isEncrypted($path)){ - $cached=OC_FileCache::getCached($path,'/'); + $cached=OC_FileCache_Cached::get($path,''); $data=OC_Crypt::blockDecrypt($data,'',$cached['size']); } return $data; @@ -113,7 +113,7 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ public function postStat($path,$data){ if(self::isEncrypted($path)){ - $cached=OC_FileCache::getCached($path,'/'); + $cached=OC_FileCache_Cached::get($path,''); $data['size']=$cached['size']; } return $data; @@ -121,7 +121,7 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ public function postFileSize($path,$size){ if(self::isEncrypted($path)){ - $cached=OC_FileCache::getCached($path,'/'); + $cached=OC_FileCache_Cached::get($path,''); return $cached['size']; }else{ return $size; diff --git a/apps/files_encryption/templates/settings.php b/apps/files_encryption/templates/settings.php index 25b5a06f56c..79780d694cf 100644 --- a/apps/files_encryption/templates/settings.php +++ b/apps/files_encryption/templates/settings.php @@ -7,6 +7,6 @@ <option selected="selected" value="<?php echo $type;?>"><?php echo $type;?></option> <?php endforeach;?> </select> - <input type='checkbox' id='enbale_encryption' <?php if($_['encryption_enabled']){echo 'checked="checked"';} ?>></input><label for='enbale_encryption'><?php echo $l->t('Enable Encryption')?></label> + <input type='checkbox' id='enable_encryption' <?php if($_['encryption_enabled']){echo 'checked="checked"';} ?>></input><label for='enable_encryption'><?php echo $l->t('Enable Encryption')?></label> </fieldset> </form> diff --git a/apps/files_encryption/tests/proxy.php b/apps/files_encryption/tests/proxy.php index fcfc4cfb9f0..5463836a209 100644 --- a/apps/files_encryption/tests/proxy.php +++ b/apps/files_encryption/tests/proxy.php @@ -11,6 +11,8 @@ class Test_CryptProxy extends UnitTestCase { private $oldKey; public function setUp(){ + $user=OC_User::getUser(); + $this->oldConfig=OCP\Config::getAppValue('files_encryption','enable_encryption','true'); OCP\Config::setAppValue('files_encryption','enable_encryption','true'); $this->oldKey=isset($_SESSION['enckey'])?$_SESSION['enckey']:null; @@ -30,10 +32,12 @@ class Test_CryptProxy extends UnitTestCase { OC_Filesystem::clearMounts(); OC_Filesystem::mount('OC_Filestorage_Temporary',array(),'/'); + OC_Filesystem::init('/'.$user.'/files'); + //set up the users home folder in the temp storage $rootView=new OC_FilesystemView(''); - $rootView->mkdir('/'.OC_User::getUser()); - $rootView->mkdir('/'.OC_User::getUser().'/files'); + $rootView->mkdir('/'.$user); + $rootView->mkdir('/'.$user.'/files'); } public function tearDown(){ diff --git a/apps/files_external/ajax/addMountPoint.php b/apps/files_external/ajax/addMountPoint.php new file mode 100644 index 00000000000..e08f805942f --- /dev/null +++ b/apps/files_external/ajax/addMountPoint.php @@ -0,0 +1,13 @@ +<?php + +OCP\JSON::checkAppEnabled('files_external'); +OCP\JSON::callCheck(); + +if ($_POST['isPersonal'] == 'true') { + OCP\JSON::checkLoggedIn(); + $isPersonal = true; +} else { + OCP\JSON::checkAdminUser(); + $isPersonal = false; +} +OC_Mount_Config::addMountPoint($_POST['mountPoint'], $_POST['class'], $_POST['classOptions'], $_POST['mountType'], $_POST['applicable'], $isPersonal); diff --git a/apps/files_external/ajax/addRootCertificate.php b/apps/files_external/ajax/addRootCertificate.php new file mode 100644 index 00000000000..d28a7d24b2d --- /dev/null +++ b/apps/files_external/ajax/addRootCertificate.php @@ -0,0 +1,28 @@ +<?php + +OCP\JSON::checkAppEnabled('files_external'); + +$view = \OCP\Files::getStorage("files_external"); +$from = $_FILES['rootcert_import']['tmp_name']; +$path = \OCP\Config::getSystemValue('datadirectory').$view->getAbsolutePath("").'uploads/'; +if(!file_exists($path)) mkdir($path,0700,true); +$to = $path.$_FILES['rootcert_import']['name']; +move_uploaded_file($from, $to); + +//check if it is a PEM certificate, otherwise convert it if possible +$fh = fopen($to, 'r'); +$data = fread($fh, filesize($to)); +fclose($fh); +if (!strpos($data, 'BEGIN CERTIFICATE')) { + $pem = chunk_split(base64_encode($data), 64, "\n");
+ $pem = "-----BEGIN CERTIFICATE-----\n".$pem."-----END CERTIFICATE-----\n"; + $fh = fopen($to, 'w'); + fwrite($fh, $pem); + fclose($fh); +} + +OC_Mount_Config::createCertificateBundle(); + +header("Location: settings/personal.php"); +exit; +?>
\ No newline at end of file diff --git a/apps/files_external/ajax/dropbox.php b/apps/files_external/ajax/dropbox.php new file mode 100644 index 00000000000..5f2ff17e625 --- /dev/null +++ b/apps/files_external/ajax/dropbox.php @@ -0,0 +1,41 @@ +<?php + +require_once 'Dropbox/autoload.php'; + +OCP\JSON::checkAppEnabled('files_external'); +OCP\JSON::checkLoggedIn(); +if (isset($_POST['app_key']) && isset($_POST['app_secret'])) { + $oauth = new Dropbox_OAuth_Curl($_POST['app_key'], $_POST['app_secret']); + if (isset($_POST['step'])) { + switch ($_POST['step']) { + case 1: + try { + if (isset($_POST['callback'])) { + $callback = $_POST['callback']; + } else { + $callback = null; + } + $token = $oauth->getRequestToken(); + OCP\JSON::success(array('data' => array('url' => $oauth->getAuthorizeUrl($callback), 'request_token' => $token['token'], 'request_token_secret' => $token['token_secret']))); + } catch (Exception $exception) { + OCP\JSON::error(array('data' => array('message' => 'Fetching request tokens failed. Verify that your Dropbox app key and secret are correct.'))); + } + break; + case 2: + if (isset($_POST['request_token']) && isset($_POST['request_token_secret'])) { + try { + $oauth->setToken($_POST['request_token'], $_POST['request_token_secret']); + $token = $oauth->getAccessToken(); + OCP\JSON::success(array('access_token' => $token['token'], 'access_token_secret' => $token['token_secret'])); + } catch (Exception $exception) { + OCP\JSON::error(array('data' => array('message' => 'Fetching access tokens failed. Verify that your Dropbox app key and secret are correct.'))); + } + } + break; + } + } +} else { + OCP\JSON::error(array('data' => array('message' => 'Please provide a valid Dropbox app key and secret.'))); +} + +?>
\ No newline at end of file diff --git a/apps/files_external/ajax/google.php b/apps/files_external/ajax/google.php new file mode 100644 index 00000000000..23ecfc3708d --- /dev/null +++ b/apps/files_external/ajax/google.php @@ -0,0 +1,51 @@ +<?php + +require_once 'Google/common.inc.php'; + +OCP\JSON::checkAppEnabled('files_external'); +OCP\JSON::checkLoggedIn(); +$consumer = new OAuthConsumer('anonymous', 'anonymous'); +$sigMethod = new OAuthSignatureMethod_HMAC_SHA1(); +if (isset($_POST['step'])) { + switch ($_POST['step']) { + case 1: + if (isset($_POST['callback'])) { + $callback = $_POST['callback']; + } else { + $callback = null; + } + $scope = 'https://docs.google.com/feeds/ https://docs.googleusercontent.com/ https://spreadsheets.google.com/feeds/'; + $url = 'https://www.google.com/accounts/OAuthGetRequestToken?scope='.urlencode($scope); + $params = array('scope' => $scope, 'oauth_callback' => $callback); + $request = OAuthRequest::from_consumer_and_token($consumer, null, 'GET', $url, $params); + $request->sign_request($sigMethod, $consumer, null); + $response = send_signed_request('GET', $url, array($request->to_header()), null, false); + $token = array(); + parse_str($response, $token); + if (isset($token['oauth_token']) && isset($token['oauth_token_secret'])) { + $authUrl = 'https://www.google.com/accounts/OAuthAuthorizeToken?oauth_token='.$token['oauth_token']; + OCP\JSON::success(array('data' => array('url' => $authUrl, 'request_token' => $token['oauth_token'], 'request_token_secret' => $token['oauth_token_secret']))); + } else { + OCP\JSON::error(array('data' => array('message' => 'Fetching request tokens failed. Error: '.$response))); + } + break; + case 2: + if (isset($_POST['oauth_verifier']) && isset($_POST['request_token']) && isset($_POST['request_token_secret'])) { + $token = new OAuthToken($_POST['request_token'], $_POST['request_token_secret']); + $url = 'https://www.google.com/accounts/OAuthGetAccessToken'; + $request = OAuthRequest::from_consumer_and_token($consumer, $token, 'GET', $url, array('oauth_verifier' => $_POST['oauth_verifier'])); + $request->sign_request($sigMethod, $consumer, $token); + $response = send_signed_request('GET', $url, array($request->to_header()), null, false); + $token = array(); + parse_str($response, $token); + if (isset($token['oauth_token']) && isset($token['oauth_token_secret'])) { + OCP\JSON::success(array('access_token' => $token['oauth_token'], 'access_token_secret' => $token['oauth_token_secret'])); + } else { + OCP\JSON::error(array('data' => array('message' => 'Fetching access tokens failed. Error: '.$response))); + } + } + break; + } +} + +?>
\ No newline at end of file diff --git a/apps/files_external/ajax/removeMountPoint.php b/apps/files_external/ajax/removeMountPoint.php new file mode 100644 index 00000000000..aa446426202 --- /dev/null +++ b/apps/files_external/ajax/removeMountPoint.php @@ -0,0 +1,13 @@ +<?php + +OCP\JSON::checkAppEnabled('files_external'); +OCP\JSON::callCheck(); + +if ($_POST['isPersonal'] == 'true') { + OCP\JSON::checkLoggedIn(); + $isPersonal = true; +} else { + OCP\JSON::checkAdminUser(); + $isPersonal = false; +} +OC_Mount_Config::removeMountPoint($_POST['mountPoint'], $_POST['mountType'], $_POST['applicable'], $isPersonal); diff --git a/apps/files_external/ajax/removeRootCertificate.php b/apps/files_external/ajax/removeRootCertificate.php new file mode 100644 index 00000000000..f78f85b8fe4 --- /dev/null +++ b/apps/files_external/ajax/removeRootCertificate.php @@ -0,0 +1,12 @@ +<?php + +OCP\JSON::checkAppEnabled('files_external'); +OCP\JSON::checkLoggedIn(); +OCP\JSON::callCheck(); + +$view = \OCP\Files::getStorage("files_external"); +$cert = $_POST['cert']; +$file = \OCP\Config::getSystemValue('datadirectory').$view->getAbsolutePath("").'uploads/'.$cert; +unlink($file); +OC_Mount_Config::createCertificateBundle(); +?>
\ No newline at end of file diff --git a/apps/files_external/appinfo/app.php b/apps/files_external/appinfo/app.php index 87846f17138..837d35c9c63 100644 --- a/apps/files_external/appinfo/app.php +++ b/apps/files_external/appinfo/app.php @@ -6,9 +6,17 @@ * See the COPYING-README file. */ - OC::$CLASSPATH['OC_FileStorage_StreamWrapper']='apps/files_external/lib/streamwrapper.php'; +OC::$CLASSPATH['OC_FileStorage_StreamWrapper']='apps/files_external/lib/streamwrapper.php'; OC::$CLASSPATH['OC_Filestorage_FTP']='apps/files_external/lib/ftp.php'; OC::$CLASSPATH['OC_Filestorage_DAV']='apps/files_external/lib/webdav.php'; OC::$CLASSPATH['OC_Filestorage_Google']='apps/files_external/lib/google.php'; OC::$CLASSPATH['OC_Filestorage_SWIFT']='apps/files_external/lib/swift.php'; OC::$CLASSPATH['OC_Filestorage_SMB']='apps/files_external/lib/smb.php'; +OC::$CLASSPATH['OC_Filestorage_AmazonS3']='apps/files_external/lib/amazons3.php'; +OC::$CLASSPATH['OC_Filestorage_Dropbox']='apps/files_external/lib/dropbox.php'; +OC::$CLASSPATH['OC_Mount_Config']='apps/files_external/lib/config.php'; + +OCP\App::registerAdmin('files_external', 'settings'); +if (OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes') == 'yes') { + OCP\App::registerPersonal('files_external', 'personal'); +} diff --git a/apps/files_external/appinfo/info.xml b/apps/files_external/appinfo/info.xml index c324730593f..e0301365d16 100644 --- a/apps/files_external/appinfo/info.xml +++ b/apps/files_external/appinfo/info.xml @@ -4,7 +4,7 @@ <name>External storage support</name> <description>Mount external storage sources</description> <licence>AGPL</licence> - <author>Robin Appelman</author> + <author>Robin Appelman, Michael Gapczynski</author> <require>4</require> <shipped>true</shipped> <types> diff --git a/apps/files_external/css/settings.css b/apps/files_external/css/settings.css new file mode 100644 index 00000000000..ca4b1c3ba89 --- /dev/null +++ b/apps/files_external/css/settings.css @@ -0,0 +1,7 @@ +.error { color: #FF3B3B; } +td.mountPoint, td.backend { width:10em; } +td.remove>img { visibility:hidden; padding-top:0.8em; } +tr:hover>td.remove>img { visibility:visible; cursor:pointer; } +#addMountPoint>td { border:none; } +#addMountPoint>td.applicable { visibility:hidden; } +#selectBackend { margin-left:-10px; }
\ No newline at end of file diff --git a/apps/files_external/js/dropbox.js b/apps/files_external/js/dropbox.js new file mode 100644 index 00000000000..92194792f42 --- /dev/null +++ b/apps/files_external/js/dropbox.js @@ -0,0 +1,80 @@ +$(document).ready(function() { + + $('#externalStorage tbody tr.OC_Filestorage_Dropbox').each(function() { + var configured = $(this).find('[data-parameter="configured"]'); + if ($(configured).val() == 'true') { + $(this).find('.configuration input').attr('disabled', 'disabled'); + $(this).find('.configuration').append('<span id="access" style="padding-left:0.5em;">Access granted</span>'); + } else { + var app_key = $(this).find('.configuration [data-parameter="app_key"]').val(); + var app_secret = $(this).find('.configuration [data-parameter="app_secret"]').val(); + var config = $(this).find('.configuration'); + if (app_key != '' && app_secret != '') { + var pos = window.location.search.indexOf('oauth_token') + 12 + var token = $(this).find('.configuration [data-parameter="token"]'); + if (pos != -1 && window.location.search.substr(pos, $(token).val().length) == $(token).val()) { + var token_secret = $(this).find('.configuration [data-parameter="token_secret"]'); + var tr = $(this); + $.post(OC.filePath('files_external', 'ajax', 'dropbox.php'), { step: 2, app_key: app_key, app_secret: app_secret, request_token: $(token).val(), request_token_secret: $(token_secret).val() }, function(result) { + if (result && result.status == 'success') { + $(token).val(result.access_token); + $(token_secret).val(result.access_token_secret); + OC.MountConfig.saveStorage(tr); + $(tr).find('.configuration input').attr('disabled', 'disabled'); + $(tr).find('.configuration').append('<span id="access" style="padding-left:0.5em;">Access granted</span>'); + } else { + OC.dialogs.alert(result.data.message, 'Error configuring Dropbox storage'); + } + }); + } + } else if ($(this).find('.mountPoint input').val() != '' && $(config).find('[data-parameter="app_key"]').val() != '' && $(config).find('[data-parameter="app_secret"]').val() != '' && $(this).find('.dropbox').length == 0) { + $(this).find('.configuration').append('<a class="button dropbox">Grant access</a>'); + } + } + }); + + $('#externalStorage tbody tr input').live('keyup', function() { + var tr = $(this).parent().parent(); + if ($(tr).hasClass('OC_Filestorage_Dropbox') && $(tr).find('[data-parameter="configured"]').val() != 'true') { + var config = $(tr).find('.configuration'); + if ($(tr).find('.mountPoint input').val() != '' && $(config).find('[data-parameter="app_key"]').val() != '' && $(config).find('[data-parameter="app_secret"]').val() != '') { + if ($(tr).find('.dropbox').length == 0) { + $(config).append('<a class="button dropbox">Grant access</a>'); + } else { + $(tr).find('.dropbox').show(); + } + } else if ($(tr).find('.dropbox').length > 0) { + $(tr).find('.dropbox').hide(); + } + } + }); + + $('.dropbox').live('click', function(event) { + event.preventDefault(); + var app_key = $(this).parent().find('[data-parameter="app_key"]').val(); + var app_secret = $(this).parent().find('[data-parameter="app_secret"]').val(); + if (app_key != '' && app_secret != '') { + var tr = $(this).parent().parent(); + var configured = $(this).parent().find('[data-parameter="configured"]'); + var token = $(this).parent().find('[data-parameter="token"]'); + var token_secret = $(this).parent().find('[data-parameter="token_secret"]'); + $.post(OC.filePath('files_external', 'ajax', 'dropbox.php'), { step: 1, app_key: app_key, app_secret: app_secret, callback: window.location.href }, function(result) { + if (result && result.status == 'success') { + $(configured).val('false'); + $(token).val(result.data.request_token); + $(token_secret).val(result.data.request_token_secret); + if (OC.MountConfig.saveStorage(tr)) { + window.location = result.data.url; + } else { + OC.dialogs.alert('Fill out all required fields', 'Error configuring Dropbox storage'); + } + } else { + OC.dialogs.alert(result.data.message, 'Error configuring Dropbox storage'); + } + }); + } else { + OC.dialogs.alert('Please provide a valid Dropbox app key and secret.', 'Error configuring Dropbox storage') + } + }); + +}); diff --git a/apps/files_external/js/google.js b/apps/files_external/js/google.js new file mode 100644 index 00000000000..7c62297df4d --- /dev/null +++ b/apps/files_external/js/google.js @@ -0,0 +1,76 @@ +$(document).ready(function() { + + $('#externalStorage tbody tr.OC_Filestorage_Google').each(function() { + var configured = $(this).find('[data-parameter="configured"]'); + if ($(configured).val() == 'true') { + $(this).find('.configuration').append('<span id="access" style="padding-left:0.5em;">Access granted</span>'); + } else { + var token = $(this).find('[data-parameter="token"]'); + var token_secret = $(this).find('[data-parameter="token_secret"]'); + var params = {}; + window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m, key, value) { + params[key] = value; + }); + if (params['oauth_token'] !== undefined && params['oauth_verifier'] !== undefined && decodeURIComponent(params['oauth_token']) == $(token).val()) { + var tr = $(this); + $.post(OC.filePath('files_external', 'ajax', 'google.php'), { step: 2, oauth_verifier: params['oauth_verifier'], request_token: $(token).val(), request_token_secret: $(token_secret).val() }, function(result) { + if (result && result.status == 'success') { + $(token).val(result.access_token); + $(token_secret).val(result.access_token_secret); + $(configured).val('true'); + OC.MountConfig.saveStorage(tr); + $(tr).find('.configuration').append('<span id="access" style="padding-left:0.5em;">Access granted</span>'); + } else { + OC.dialogs.alert(result.data.message, 'Error configuring Google Drive storage'); + } + }); + } else if ($(this).find('.google').length == 0) { + $(this).find('.configuration').append('<a class="button google">Grant access</a>'); + } + } + }); + + $('#externalStorage tbody tr').live('change', function() { + if ($(this).hasClass('OC_Filestorage_Google') && $(this).find('[data-parameter="configured"]').val() != 'true') { + if ($(this).find('.mountPoint input').val() != '') { + if ($(this).find('.google').length == 0) { + $(this).find('.configuration').append('<a class="button google">Grant access</a>'); + } + } + } + }); + + $('#externalStorage tbody tr .mountPoint input').live('keyup', function() { + var tr = $(this).parent().parent(); + if ($(tr).hasClass('OC_Filestorage_Google') && $(tr).find('[data-parameter="configured"]').val() != 'true' && $(tr).find('.google').length > 0) { + if ($(this).val() != '') { + $(tr).find('.google').show(); + } else { + $(tr).find('.google').hide(); + } + } + }); + + $('.google').live('click', function(event) { + event.preventDefault(); + var tr = $(this).parent().parent(); + var configured = $(this).parent().find('[data-parameter="configured"]'); + var token = $(this).parent().find('[data-parameter="token"]'); + var token_secret = $(this).parent().find('[data-parameter="token_secret"]'); + $.post(OC.filePath('files_external', 'ajax', 'google.php'), { step: 1, callback: window.location.href }, function(result) { + if (result && result.status == 'success') { + $(configured).val('false'); + $(token).val(result.data.request_token); + $(token_secret).val(result.data.request_token_secret); + if (OC.MountConfig.saveStorage(tr)) { + window.location = result.data.url; + } else { + OC.dialogs.alert('Fill out all required fields', 'Error configuring Google Drive storage'); + } + } else { + OC.dialogs.alert(result.data.message, 'Error configuring Google Drive storage'); + } + }); + }); + +}); diff --git a/apps/files_external/js/settings.js b/apps/files_external/js/settings.js new file mode 100644 index 00000000000..23f02bbefcb --- /dev/null +++ b/apps/files_external/js/settings.js @@ -0,0 +1,182 @@ +OC.MountConfig={ + saveStorage:function(tr) { + var mountPoint = $(tr).find('.mountPoint input').val(); + if (mountPoint == '') { + return false; + } + var backendClass = $(tr).find('.backend').data('class'); + var configuration = $(tr).find('.configuration input'); + var addMountPoint = true; + if (configuration.length < 1) { + return false; + } + var classOptions = {}; + $.each(configuration, function(index, input) { + if ($(input).val() == '' && !$(input).hasClass('optional')) { + addMountPoint = false; + return false; + } + if ($(input).is(':checkbox')) { + if ($(input).is(':checked')) { + classOptions[$(input).data('parameter')] = true; + } else { + classOptions[$(input).data('parameter')] = false; + } + } else { + classOptions[$(input).data('parameter')] = $(input).val(); + } + }); + if (addMountPoint) { + if ($('#externalStorage').data('admin') === true) { + var isPersonal = false; + var multiselect = $(tr).find('.chzn-select').val(); + var oldGroups = $(tr).find('.applicable').data('applicable-groups'); + var oldUsers = $(tr).find('.applicable').data('applicable-users'); + $.each(multiselect, function(index, value) { + var pos = value.indexOf('(group)'); + if (pos != -1) { + var mountType = 'group'; + var applicable = value.substr(0, pos); + if ($.inArray(applicable, oldGroups) != -1) { + oldGroups.splice($.inArray(applicable, oldGroups), 1); + } + } else { + var mountType = 'user'; + var applicable = value; + if ($.inArray(applicable, oldUsers) != -1) { + oldUsers.splice($.inArray(applicable, oldUsers), 1); + } + } + $.post(OC.filePath('files_external', 'ajax', 'addMountPoint.php'), { mountPoint: mountPoint, class: backendClass, classOptions: classOptions, mountType: mountType, applicable: applicable, isPersonal: isPersonal }); + }); + var mountType = 'group'; + $.each(oldGroups, function(index, applicable) { + $.post(OC.filePath('files_external', 'ajax', 'removeMountPoint.php'), { mountPoint: mountPoint, mountType: mountType, applicable: applicable, isPersonal: isPersonal }); + }); + var mountType = 'user'; + $.each(oldUsers, function(index, applicable) { + $.post(OC.filePath('files_external', 'ajax', 'removeMountPoint.php'), { mountPoint: mountPoint, mountType: mountType, applicable: applicable, isPersonal: isPersonal }); + }); + } else { + var isPersonal = true; + var mountType = 'user'; + var applicable = OC.currentUser; + $.post(OC.filePath('files_external', 'ajax', 'addMountPoint.php'), { mountPoint: mountPoint, class: backendClass, classOptions: classOptions, mountType: mountType, applicable: applicable, isPersonal: isPersonal }); + } + return true; + } + } +} + +$(document).ready(function() { + $('.chzn-select').chosen(); + + $('#selectBackend').live('change', function() { + var tr = $(this).parent().parent(); + $('#externalStorage tbody').append($(tr).clone()); + $('#externalStorage tbody tr').last().find('.mountPoint input').val(''); + var selected = $(this).find('option:selected').text(); + var backendClass = $(this).val(); + $(this).parent().text(selected); + if ($(tr).find('.mountPoint input').val() == '') { + $(tr).find('.mountPoint input').val(suggestMountPoint(selected.replace(/\s+/g, ''))); + } + $(tr).addClass(backendClass); + $(tr).find('.backend').data('class', backendClass); + var configurations = $(this).data('configurations'); + var td = $(tr).find('td.configuration'); + $.each(configurations, function(backend, parameters) { + if (backend == backendClass) { + $.each(parameters['configuration'], function(parameter, placeholder) { + if (placeholder.indexOf('*') != -1) { + td.append('<input type="password" data-parameter="'+parameter+'" placeholder="'+placeholder.substring(1)+'" />'); + } else if (placeholder.indexOf('!') != -1) { + td.append('<label><input type="checkbox" data-parameter="'+parameter+'" />'+placeholder.substring(1)+'</label>'); + } else if (placeholder.indexOf('&') != -1) { + td.append('<input type="text" class="optional" data-parameter="'+parameter+'" placeholder="'+placeholder.substring(1)+'" />'); + } else if (placeholder.indexOf('#') != -1) { + td.append('<input type="hidden" data-parameter="'+parameter+'" />'); + } else { + td.append('<input type="text" data-parameter="'+parameter+'" placeholder="'+placeholder+'" />'); + } + }); + if (parameters['custom'] && $('#externalStorage tbody tr.'+backendClass).length == 1) { + OC.addScript('files_external', parameters['custom']); + } + return false; + } + }); + $('.chz-select').chosen(); + $(tr).find('td').last().attr('class', 'remove'); + $(tr).find('td').last().removeAttr('style'); + $(tr).removeAttr('id'); + $(this).remove(); + }); + + function suggestMountPoint(defaultMountPoint) { + var i = 1; + var append = ''; + var match = true; + while (match && i < 20) { + match = false; + $('#externalStorage tbody td.mountPoint input').each(function(index, mountPoint) { + if ($(mountPoint).val() == defaultMountPoint+append) { + match = true; + return false; + } + }); + if (match) { + append = i; + i++; + } else { + break; + } + } + return defaultMountPoint+append; + } + + $('#externalStorage td').live('change', function() { + OC.MountConfig.saveStorage($(this).parent()); + }); + + $('td.remove>img').live('click', function() { + var tr = $(this).parent().parent(); + var mountPoint = $(tr).find('.mountPoint input').val(); + if (!mountPoint) { + var row=this.parentNode.parentNode; + $.post(OC.filePath('files_external', 'ajax', 'removeRootCertificate.php'), { cert: row.id }); + $(tr).remove(); + return true; + } + if ($('#externalStorage').data('admin') === true) { + var isPersonal = false; + var multiselect = $(tr).find('.chzn-select').val(); + $.each(multiselect, function(index, value) { + var pos = value.indexOf('(group)'); + if (pos != -1) { + var mountType = 'group'; + var applicable = value.substr(0, pos); + } else { + var mountType = 'user'; + var applicable = value; + } + $.post(OC.filePath('files_external', 'ajax', 'removeMountPoint.php'), { mountPoint: mountPoint, mountType: mountType, applicable: applicable, isPersonal: isPersonal }); + }); + } else { + var mountType = 'user'; + var applicable = OC.currentUser; + var isPersonal = true; + } + $.post(OC.filePath('files_external', 'ajax', 'removeMountPoint.php'), { mountPoint: mountPoint, mountType: mountType, applicable: applicable, isPersonal: isPersonal }); + $(tr).remove(); + }); + + $('#allowUserMounting').bind('change', function() { + if (this.checked) { + OC.AppConfig.setValue('files_external', 'allow_user_mounting', 'yes'); + } else { + OC.AppConfig.setValue('files_external', 'allow_user_mounting', 'no'); + } + }); + +});
\ No newline at end of file diff --git a/apps/files_external/l10n/.gitkeep b/apps/files_external/l10n/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/apps/files_external/l10n/.gitkeep diff --git a/apps/files_external/l10n/ca.php b/apps/files_external/l10n/ca.php new file mode 100644 index 00000000000..aa93379c358 --- /dev/null +++ b/apps/files_external/l10n/ca.php @@ -0,0 +1,18 @@ +<?php $TRANSLATIONS = array( +"External Storage" => "Emmagatzemament extern", +"Mount point" => "Punt de muntatge", +"Backend" => "Dorsal", +"Configuration" => "Configuració", +"Options" => "Options", +"Applicable" => "Aplicable", +"Add mount point" => "Afegeix punt de muntatge", +"None set" => "Cap d'establert", +"All Users" => "Tots els usuaris", +"Groups" => "Grups", +"Users" => "Usuaris", +"Delete" => "Elimina", +"SSL root certificates" => "Certificats SSL root", +"Import Root Certificate" => "Importa certificat root", +"Enable User External Storage" => "Habilita l'emmagatzemament extern d'usuari", +"Allow users to mount their own external storage" => "Permet als usuaris muntar el seu emmagatzemament extern propi" +); diff --git a/apps/files_external/l10n/cs_CZ.php b/apps/files_external/l10n/cs_CZ.php new file mode 100644 index 00000000000..8f11d7da119 --- /dev/null +++ b/apps/files_external/l10n/cs_CZ.php @@ -0,0 +1,13 @@ +<?php $TRANSLATIONS = array( +"External Storage" => "Externà úložiÅ¡tÄ›", +"Mount point" => "PÅ™Ãpojný bod", +"Configuration" => "Konfigurace", +"Options" => "NastavenÃ", +"Add mount point" => "PÅ™idat pÅ™Ãpojný bod", +"All Users" => "VÅ¡ichni uživatelé", +"Groups" => "Skupiny", +"Users" => "Uživatelé", +"Delete" => "Smazat", +"Enable User External Storage" => "Zapnout uživatelské externà úložiÅ¡tÄ›", +"Allow users to mount their own external storage" => "Povolit uživatelů pÅ™ipojit jejich vlastnà externà úložiÅ¡tÄ›" +); diff --git a/apps/files_external/l10n/de.php b/apps/files_external/l10n/de.php new file mode 100644 index 00000000000..4306ad33dc3 --- /dev/null +++ b/apps/files_external/l10n/de.php @@ -0,0 +1,18 @@ +<?php $TRANSLATIONS = array( +"External Storage" => "Externer Speicher", +"Mount point" => "Mount-Point", +"Backend" => "Backend", +"Configuration" => "Konfiguration", +"Options" => "Optionen", +"Applicable" => "Zutreffend", +"Add mount point" => "Mount-Point hinzufügen", +"None set" => "Nicht definiert", +"All Users" => "Alle Benutzer", +"Groups" => "Gruppen", +"Users" => "Benutzer", +"Delete" => "Löschen", +"SSL root certificates" => "SSL-Root-Zertifikate", +"Import Root Certificate" => "Root-Zertifikate importieren", +"Enable User External Storage" => "Externen Speicher für Benutzer aktivieren", +"Allow users to mount their own external storage" => "Erlaubt Benutzern ihre eigenen externen Speicher einzubinden" +); diff --git a/apps/files_external/l10n/el.php b/apps/files_external/l10n/el.php new file mode 100644 index 00000000000..a4a910ce636 --- /dev/null +++ b/apps/files_external/l10n/el.php @@ -0,0 +1,9 @@ +<?php $TRANSLATIONS = array( +"External Storage" => "ΕξωτεÏική αποθήκευση", +"Configuration" => "Ρυθμίσεις", +"Options" => "ΕπιλογÎÏ‚", +"All Users" => "Όλοι οι χÏήστες", +"Groups" => "Ομάδες", +"Users" => "ΧÏήστες", +"Delete" => "ΔιαγÏαφή" +); diff --git a/apps/files_external/l10n/eo.php b/apps/files_external/l10n/eo.php new file mode 100644 index 00000000000..3cb8255c522 --- /dev/null +++ b/apps/files_external/l10n/eo.php @@ -0,0 +1,18 @@ +<?php $TRANSLATIONS = array( +"External Storage" => "Malena memorilo", +"Mount point" => "Surmetingo", +"Backend" => "Motoro", +"Configuration" => "Agordo", +"Options" => "Malneproj", +"Applicable" => "Aplikebla", +"Add mount point" => "Aldoni surmetingon", +"None set" => "Nenio agordita", +"All Users" => "Ĉiuj uzantoj", +"Groups" => "Grupoj", +"Users" => "Uzantoj", +"Delete" => "Forigi", +"SSL root certificates" => "Radikaj SSL-atestoj", +"Import Root Certificate" => "Enporti radikan ateston", +"Enable User External Storage" => "Kapabligi malenan memorilon de uzanto", +"Allow users to mount their own external storage" => "Permesi al uzantoj surmeti siajn proprajn malenajn memorilojn" +); diff --git a/apps/files_external/l10n/es.php b/apps/files_external/l10n/es.php new file mode 100644 index 00000000000..c1c605735fe --- /dev/null +++ b/apps/files_external/l10n/es.php @@ -0,0 +1,13 @@ +<?php $TRANSLATIONS = array( +"External Storage" => "Almacenamiento externo", +"Backend" => "Motor", +"Configuration" => "Configuración", +"Options" => "Opciones", +"Applicable" => "Aplicable", +"Add mount point" => "Añadir punto de montaje", +"None set" => "No se ha configurado", +"All Users" => "Todos los usuarios", +"Groups" => "Grupos", +"Users" => "Usuarios", +"Delete" => "Eliiminar" +); diff --git a/apps/files_external/l10n/et_EE.php b/apps/files_external/l10n/et_EE.php new file mode 100644 index 00000000000..a6907e775bc --- /dev/null +++ b/apps/files_external/l10n/et_EE.php @@ -0,0 +1,12 @@ +<?php $TRANSLATIONS = array( +"External Storage" => "Väline salvestuskoht", +"Mount point" => "Ãœhenduspunkt", +"Configuration" => "Seadistamine", +"Options" => "Valikud", +"Add mount point" => "Lisa ühenduspunkt", +"None set" => "Pole määratud", +"All Users" => "Kõik kasutajad", +"Groups" => "Grupid", +"Users" => "Kasutajad", +"Delete" => "Kustuta" +); diff --git a/apps/files_external/l10n/fi_FI.php b/apps/files_external/l10n/fi_FI.php new file mode 100644 index 00000000000..7dca49791e4 --- /dev/null +++ b/apps/files_external/l10n/fi_FI.php @@ -0,0 +1,16 @@ +<?php $TRANSLATIONS = array( +"External Storage" => "Erillinen tallennusväline", +"Mount point" => "Liitospiste", +"Backend" => "Taustaosa", +"Configuration" => "Asetukset", +"Options" => "Valinnat", +"Add mount point" => "Lisää liitospiste", +"None set" => "Ei asetettu", +"All Users" => "Kaikki käyttäjät", +"Groups" => "Ryhmät", +"Users" => "Käyttäjät", +"Delete" => "Poista", +"SSL root certificates" => "SSL-juurivarmenteet", +"Import Root Certificate" => "Tuo juurivarmenne", +"Allow users to mount their own external storage" => "Salli käyttäjien liittää omia erillisiä tallennusvälineitä" +); diff --git a/apps/files_external/l10n/fr.php b/apps/files_external/l10n/fr.php new file mode 100644 index 00000000000..b3e76c63500 --- /dev/null +++ b/apps/files_external/l10n/fr.php @@ -0,0 +1,18 @@ +<?php $TRANSLATIONS = array( +"External Storage" => "Stockage externe", +"Mount point" => "Point de montage", +"Backend" => "Infrastructure", +"Configuration" => "Configuration", +"Options" => "Options", +"Applicable" => "Disponible", +"Add mount point" => "Ajouter un point de montage", +"None set" => "Aucun spécifié", +"All Users" => "Tous les utilisateurs", +"Groups" => "Groupes", +"Users" => "Utilisateurs", +"Delete" => "Supprimer", +"SSL root certificates" => "Certificats racine SSL", +"Import Root Certificate" => "Importer un certificat racine", +"Enable User External Storage" => "Activer le stockage externe pour les utilisateurs", +"Allow users to mount their own external storage" => "Autoriser les utilisateurs à monter leur propre stockage externe" +); diff --git a/apps/files_external/l10n/it.php b/apps/files_external/l10n/it.php new file mode 100644 index 00000000000..927499b0172 --- /dev/null +++ b/apps/files_external/l10n/it.php @@ -0,0 +1,18 @@ +<?php $TRANSLATIONS = array( +"External Storage" => "Archiviazione esterna", +"Mount point" => "Punto di mount", +"Backend" => "Motore", +"Configuration" => "Configurazione", +"Options" => "Opzioni", +"Applicable" => "Applicabile", +"Add mount point" => "Aggiungi punto di mount", +"None set" => "Nessuna impostazione", +"All Users" => "Tutti gli utenti", +"Groups" => "Gruppi", +"Users" => "Utenti", +"Delete" => "Elimina", +"SSL root certificates" => "Certificato principale per SSL", +"Import Root Certificate" => "Importa certificato principale", +"Enable User External Storage" => "Abilita la memoria esterna dell'utente", +"Allow users to mount their own external storage" => "Consente agli utenti di montare la propria memoria esterna" +); diff --git a/apps/files_external/l10n/ja_JP.php b/apps/files_external/l10n/ja_JP.php new file mode 100644 index 00000000000..c7d38d88321 --- /dev/null +++ b/apps/files_external/l10n/ja_JP.php @@ -0,0 +1,18 @@ +<?php $TRANSLATIONS = array( +"External Storage" => "外部ストレージ", +"Mount point" => "マウントãƒã‚¤ãƒ³ãƒˆ", +"Backend" => "ãƒãƒƒã‚¯ã‚¨ãƒ³ãƒ‰", +"Configuration" => "è¨å®š", +"Options" => "オプション", +"Applicable" => "é©ç”¨ç¯„囲", +"Add mount point" => "マウントãƒã‚¤ãƒ³ãƒˆã‚’è¿½åŠ ", +"None set" => "未è¨å®š", +"All Users" => "ã™ã¹ã¦ã®ãƒ¦ãƒ¼ã‚¶", +"Groups" => "グループ", +"Users" => "ユーザ", +"Delete" => "削除", +"SSL root certificates" => "SSLルート証明書", +"Import Root Certificate" => "ルート証明書をインãƒãƒ¼ãƒˆ", +"Enable User External Storage" => "ユーザã®å¤–部ストレージを有効ã«ã™ã‚‹", +"Allow users to mount their own external storage" => "ユーザã«å¤–部ストレージã®ãƒžã‚¦ãƒ³ãƒˆã‚’許å¯ã™ã‚‹" +); diff --git a/apps/files_external/l10n/lt_LT.php b/apps/files_external/l10n/lt_LT.php new file mode 100644 index 00000000000..00022aa3d38 --- /dev/null +++ b/apps/files_external/l10n/lt_LT.php @@ -0,0 +1,9 @@ +<?php $TRANSLATIONS = array( +"Configuration" => "KonfigÅ«racija", +"Options" => "Nustatymai", +"None set" => "Nieko nepasirinkta", +"All Users" => "Visi vartotojai", +"Groups" => "GrupÄ—s", +"Users" => "Vartotojai", +"Delete" => "IÅ¡trinti" +); diff --git a/apps/files_external/l10n/pl.php b/apps/files_external/l10n/pl.php new file mode 100644 index 00000000000..df0ed54e80f --- /dev/null +++ b/apps/files_external/l10n/pl.php @@ -0,0 +1,18 @@ +<?php $TRANSLATIONS = array( +"External Storage" => "ZewnÄ™trzna zasoby dyskowe", +"Mount point" => "Punkt montowania", +"Backend" => "Zaplecze", +"Configuration" => "Konfiguracja", +"Options" => "Opcje", +"Applicable" => "Zastosowanie", +"Add mount point" => "Dodaj punkt montowania", +"None set" => "Nie ustawione", +"All Users" => "Wszyscy uzytkownicy", +"Groups" => "Grupy", +"Users" => "Użytkownicy", +"Delete" => "UsuÅ„", +"SSL root certificates" => "Główny certyfikat SSL", +"Import Root Certificate" => "Importuj główny certyfikat", +"Enable User External Storage" => "WÅ‚Ä…cz zewnÄ™trzne zasoby dyskowe użytkownika", +"Allow users to mount their own external storage" => "Zezwalaj użytkownikom na montowanie ich wÅ‚asnych zewnÄ™trznych zasobów dyskowych" +); diff --git a/apps/files_external/l10n/sl.php b/apps/files_external/l10n/sl.php new file mode 100644 index 00000000000..d588844467e --- /dev/null +++ b/apps/files_external/l10n/sl.php @@ -0,0 +1,18 @@ +<?php $TRANSLATIONS = array( +"External Storage" => "Zunanja podatkovna shramba", +"Mount point" => "Priklopna toÄka", +"Backend" => "Zaledje", +"Configuration" => "Nastavitve", +"Options" => "Možnosti", +"Applicable" => "Se uporablja", +"Add mount point" => "Dodaj priklopno toÄko", +"None set" => "Ni nastavljeno", +"All Users" => "Vsi uporabniki", +"Groups" => "Skupine", +"Users" => "Uporabniki", +"Delete" => "IzbriÅ¡i", +"SSL root certificates" => "SSL korenski certifikati", +"Import Root Certificate" => "Uvozi korenski certifikat", +"Enable User External Storage" => "OmogoÄi uporabo zunanje podatkovne shrambe za uporabnike", +"Allow users to mount their own external storage" => "Dovoli uporabnikom priklop lastne zunanje podatkovne shrambe" +); diff --git a/apps/files_external/l10n/sv.php b/apps/files_external/l10n/sv.php new file mode 100644 index 00000000000..0838df6a32b --- /dev/null +++ b/apps/files_external/l10n/sv.php @@ -0,0 +1,18 @@ +<?php $TRANSLATIONS = array( +"External Storage" => "Extern lagring", +"Mount point" => "Monteringspunkt", +"Backend" => "Källa", +"Configuration" => "Konfiguration", +"Options" => "Alternativ", +"Applicable" => "Tillämplig", +"Add mount point" => "Lägg till monteringspunkt", +"None set" => "Ingen angiven", +"All Users" => "Alla användare", +"Groups" => "Grupper", +"Users" => "Användare", +"Delete" => "Radera", +"SSL root certificates" => "SSL rotcertifikat", +"Import Root Certificate" => "Importera rotcertifikat", +"Enable User External Storage" => "Aktivera extern lagring för användare", +"Allow users to mount their own external storage" => "TillÃ¥t användare att montera egen extern lagring" +); diff --git a/apps/files_external/l10n/th_TH.php b/apps/files_external/l10n/th_TH.php new file mode 100644 index 00000000000..84a233046eb --- /dev/null +++ b/apps/files_external/l10n/th_TH.php @@ -0,0 +1,18 @@ +<?php $TRANSLATIONS = array( +"External Storage" => "พื้นทีจัดเà¸à¹‡à¸šà¸‚้à¸à¸¡à¸¹à¸¥à¸ˆà¸²à¸à¸ ายนà¸à¸", +"Mount point" => "จุดชี้ตำà¹à¸«à¸™à¹ˆà¸‡", +"Backend" => "ด้านหลังระบบ", +"Configuration" => "à¸à¸²à¸£à¸à¸³à¸«à¸™à¸”ค่า", +"Options" => "ตัวเลืà¸à¸", +"Applicable" => "สามารถใช้งานได้", +"Add mount point" => "เพิ่มจุดชี้ตำà¹à¸«à¸™à¹ˆà¸‡", +"None set" => "ยังไม่มีà¸à¸²à¸£à¸à¸³à¸«à¸™à¸”", +"All Users" => "ผู้ใช้งานทั้งหมด", +"Groups" => "à¸à¸¥à¸¸à¹ˆà¸¡", +"Users" => "ผู้ใช้งาน", +"Delete" => "ลบ", +"SSL root certificates" => "ใบรับรà¸à¸‡à¸„วามปลà¸à¸”ภัยด้วยระบบ SSL จาภRoot", +"Import Root Certificate" => "นำเข้าข้à¸à¸¡à¸¹à¸¥à¹ƒà¸šà¸£à¸±à¸šà¸£à¸à¸‡à¸„วามปลà¸à¸”ภัยจาภRoot", +"Enable User External Storage" => "เปิดให้มีà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸žà¸·à¹‰à¸™à¸—ี่จัดเà¸à¹‡à¸šà¸‚้à¸à¸¡à¸¹à¸¥à¸‚à¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸ˆà¸²à¸à¸ ายนà¸à¸à¹„ด้", +"Allow users to mount their own external storage" => "à¸à¸™à¸¸à¸à¸²à¸•à¹ƒà¸«à¹‰à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸ªà¸²à¸¡à¸²à¸£à¸–ชี้ตำà¹à¸«à¸™à¹ˆà¸‡à¹„ปที่พื้นที่จัดเà¸à¹‡à¸šà¸‚้à¸à¸¡à¸¹à¸¥à¸ ายนà¸à¸à¸‚à¸à¸‡à¸•à¸™à¹€à¸à¸‡à¹„ด้" +); diff --git a/apps/files_external/lib/amazons3.php b/apps/files_external/lib/amazons3.php new file mode 100644 index 00000000000..3c2e3330175 --- /dev/null +++ b/apps/files_external/lib/amazons3.php @@ -0,0 +1,236 @@ +<?php + +/** +* ownCloud +* +* @author Michael Gapczynski +* @copyright 2012 Michael Gapczynski mtgap@owncloud.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +require_once 'aws-sdk/sdk.class.php'; + +class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common { + + private $s3; + private $bucket; + private $objects = array(); + + private static $tempFiles = array(); + + // TODO options: storage class, encryption server side, encrypt before upload? + + public function __construct($params) { + $this->s3 = new AmazonS3(array('key' => $params['key'], 'secret' => $params['secret'])); + $this->bucket = $params['bucket']; + } + + private function getObject($path) { + if (array_key_exists($path, $this->objects)) { + return $this->objects[$path]; + } else { + $response = $this->s3->get_object_metadata($this->bucket, $path); + if ($response) { + $this->objects[$path] = $response; + return $response; + // This object could be a folder, a '/' must be at the end of the path + } else if (substr($path, -1) != '/') { + $response = $this->s3->get_object_metadata($this->bucket, $path.'/'); + if ($response) { + $this->objects[$path] = $response; + return $response; + } + } + } + return false; + } + + public function mkdir($path) { + // Folders in Amazon S3 are 0 byte objects with a '/' at the end of the name + if (substr($path, -1) != '/') { + $path .= '/'; + } + $response = $this->s3->create_object($this->bucket, $path, array('body' => '')); + return $response->isOK(); + } + + public function rmdir($path) { + if (substr($path, -1) != '/') { + $path .= '/'; + } + return $this->unlink($path); + } + + public function opendir($path) { + if ($path == '' || $path == '/') { + // Use the '/' delimiter to only fetch objects inside the folder + $opt = array('delimiter' => '/'); + } else { + if (substr($path, -1) != '/') { + $path .= '/'; + } + $opt = array('delimiter' => '/', 'prefix' => $path); + } + $response = $this->s3->list_objects($this->bucket, $opt); + if ($response->isOK()) { + $files = array(); + foreach ($response->body->Contents as $object) { + // The folder being opened also shows up in the list of objects, don't add it to the files + if ($object->Key != $path) { + $files[] = basename($object->Key); + } + } + // Sub folders show up as CommonPrefixes + foreach ($response->body->CommonPrefixes as $object) { + $files[] = basename($object->Prefix); + } + OC_FakeDirStream::$dirs['amazons3'.$path] = $files; + return opendir('fakedir://amazons3'.$path); + } + return false; + } + + public function stat($path) { + if ($path == '' || $path == '/') { + $stat['size'] = $this->s3->get_bucket_filesize($this->bucket); + $stat['atime'] = time(); + $stat['mtime'] = $stat['atime']; + $stat['ctime'] = $stat['atime']; + } else if ($object = $this->getObject($path)) { + $stat['size'] = $object['Size']; + $stat['atime'] = time(); + $stat['mtime'] = strtotime($object['LastModified']); + $stat['ctime'] = $stat['mtime']; + } + if (isset($stat)) { + return $stat; + } + return false; + } + + public function filetype($path) { + if ($path == '' || $path == '/') { + return 'dir'; + } else if ($object = $this->getObject($path)) { + // Amazon S3 doesn't have typical folders, this is an alternative method to detect a folder + if (substr($object['Key'], -1) == '/' && $object['Size'] == 0) { + return 'dir'; + } else { + return 'file'; + } + } + return false; + } + + public function isReadable($path) { + // TODO Check acl and determine who grantee is + return true; + } + + public function isUpdatable($path) { + // TODO Check acl and determine who grantee is + return true; + } + + public function file_exists($path) { + if ($this->filetype($path) == 'dir' && substr($path, -1) != '/') { + $path .= '/'; + } + return $this->s3->if_object_exists($this->bucket, $path); + } + + public function unlink($path) { + $response = $this->s3->delete_object($this->bucket, $path); + return $response->isOK(); + } + + public function fopen($path, $mode) { + switch ($mode) { + case 'r': + case 'rb': + $tmpFile = OC_Helper::tmpFile(); + $handle = fopen($tmpFile, 'w'); + $response = $this->s3->get_object($this->bucket, $path, array('fileDownload' => $handle)); + if ($response->isOK()) { + return fopen($tmpFile, 'r'); + } + break; + case 'w': + case 'wb': + case 'a': + case 'ab': + case 'r+': + case 'w+': + case 'wb+': + case 'a+': + case 'x': + case 'x+': + case 'c': + case 'c+': + if (strrpos($path, '.') !== false) { + $ext = substr($path, strrpos($path, '.')); + } else { + $ext = ''; + } + $tmpFile = OC_Helper::tmpFile($ext); + OC_CloseStreamWrapper::$callBacks[$tmpFile] = array($this, 'writeBack'); + if ($this->file_exists($path)) { + $source = $this->fopen($path, 'r'); + file_put_contents($tmpFile, $source); + } + self::$tempFiles[$tmpFile] = $path; + return fopen('close://'.$tmpFile, $mode); + } + return false; + } + + public function writeBack($tmpFile) { + if (isset(self::$tempFiles[$tmpFile])) { + $handle = fopen($tmpFile, 'r'); + $response = $this->s3->create_object($this->bucket, self::$tempFiles[$tmpFile], array('fileUpload' => $handle)); + if ($response->isOK()) { + unlink($tmpFile); + } + } + } + + public function getMimeType($path) { + if ($this->filetype($path) == 'dir') { + return 'httpd/unix-directory'; + } else if ($object = $this->getObject($path)) { + return $object['ContentType']; + } + return false; + } + + public function free_space($path) { + // Infinite? + return false; + } + + public function touch($path, $mtime = null) { + if (is_null($mtime)) { + $mtime = time(); + } + if ($this->filetype($path) == 'dir' && substr($path, -1) != '/') { + $path .= '/'; + } + $response = $this->s3->update_object($this->bucket, $path, array('meta' => array('LastModified' => $mtime))); + return $response->isOK(); + } + +} + +?>
\ No newline at end of file diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php new file mode 100755 index 00000000000..f1bc16e2537 --- /dev/null +++ b/apps/files_external/lib/config.php @@ -0,0 +1,283 @@ +<?php
+/**
+* ownCloud
+*
+* @author Michael Gapczynski
+* @copyright 2012 Michael Gapczynski mtgap@owncloud.com
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or any later version.
+*
+* This library 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 AFFERO GENERAL PUBLIC LICENSE for more details.
+*
+* You should have received a copy of the GNU Affero General Public
+* License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+* Class to configure the config/mount.php and data/$user/mount.php files
+*/
+class OC_Mount_Config {
+
+ const MOUNT_TYPE_GLOBAL = 'global';
+ const MOUNT_TYPE_GROUP = 'group';
+ const MOUNT_TYPE_USER = 'user';
+
+ /**
+ * Get details on each of the external storage backends, used for the mount config UI
+ * If a custom UI is needed, add the key 'custom' and a javascript file with that name will be loaded
+ * If the configuration parameter should be secret, add a '*' to the beginning of the value
+ * If the configuration parameter is a boolean, add a '!' to the beginning of the value
+ * If the configuration parameter is optional, add a '&' to the beginning of the value
+ * If the configuration parameter is hidden, add a '#' to the begining of the value
+ * @return array
+ */
+ public static function getBackends() {
+ return array(
+ 'OC_Filestorage_Local' => array('backend' => 'Local', 'configuration' => array('datadir' => 'Location')),
+ 'OC_Filestorage_AmazonS3' => array('backend' => 'Amazon S3', 'configuration' => array('key' => 'Key', 'secret' => '*Secret', 'bucket' => 'Bucket')),
+ 'OC_Filestorage_Dropbox' => array('backend' => 'Dropbox', 'configuration' => array('configured' => '#configured','app_key' => 'App key', 'app_secret' => 'App secret', 'token' => '#token', 'token_secret' => '#token_secret'), 'custom' => 'dropbox'),
+ 'OC_Filestorage_FTP' => array('backend' => 'FTP', 'configuration' => array('host' => 'URL', 'user' => 'Username', 'password' => '*Password', 'root' => '&Root', 'secure' => '!Secure ftps://')),
+ 'OC_Filestorage_Google' => array('backend' => 'Google Drive', 'configuration' => array('configured' => '#configured', 'token' => '#token', 'token_secret' => '#token secret'), 'custom' => 'google'),
+ 'OC_Filestorage_SWIFT' => array('backend' => 'OpenStack Swift', 'configuration' => array('host' => 'URL', 'user' => 'Username', 'token' => '*Token', 'root' => '&Root', 'secure' => '!Secure ftps://')),
+ 'OC_Filestorage_SMB' => array('backend' => 'SMB', 'configuration' => array('host' => 'URL', 'user' => 'Username', 'password' => '*Password', 'share' => 'Share', 'root' => '&Root')),
+ 'OC_Filestorage_DAV' => array('backend' => 'WebDAV', 'configuration' => array('host' => 'URL', 'user' => 'Username', 'password' => '*Password', 'root' => '&Root', 'secure' => '!Secure https://'))
+ );
+ }
+
+ /**
+ * Get the system mount points
+ * The returned array is not in the same format as getUserMountPoints()
+ * @return array
+ */
+ public static function getSystemMountPoints() {
+ $mountPoints = self::readData(false);
+ $backends = self::getBackends();
+ $system = array();
+ if (isset($mountPoints[self::MOUNT_TYPE_GROUP])) {
+ foreach ($mountPoints[self::MOUNT_TYPE_GROUP] as $group => $mounts) {
+ foreach ($mounts as $mountPoint => $mount) {
+ // Remove '/$user/files/' from mount point
+ $mountPoint = substr($mountPoint, 13);
+ // Merge the mount point into the current mount points
+ if (isset($system[$mountPoint]) && $system[$mountPoint]['configuration'] == $mount['options']) {
+ $system[$mountPoint]['applicable']['groups'] = array_merge($system[$mountPoint]['applicable']['groups'], array($group));
+ } else {
+ $system[$mountPoint] = array('class' => $mount['class'], 'backend' => $backends[$mount['class']]['backend'], 'configuration' => $mount['options'], 'applicable' => array('groups' => array($group), 'users' => array()));
+ }
+ }
+ }
+ }
+ if (isset($mountPoints[self::MOUNT_TYPE_USER])) {
+ foreach ($mountPoints[self::MOUNT_TYPE_USER] as $user => $mounts) {
+ foreach ($mounts as $mountPoint => $mount) {
+ // Remove '/$user/files/' from mount point
+ $mountPoint = substr($mountPoint, 13);
+ // Merge the mount point into the current mount points
+ if (isset($system[$mountPoint]) && $system[$mountPoint]['configuration'] == $mount['options']) {
+ $system[$mountPoint]['applicable']['users'] = array_merge($system[$mountPoint]['applicable']['users'], array($user));
+ } else {
+ $system[$mountPoint] = array('class' => $mount['class'], 'backend' => $backends[$mount['class']]['backend'], 'configuration' => $mount['options'], 'applicable' => array('groups' => array(), 'users' => array($user)));
+ }
+ }
+ }
+ }
+ return $system;
+ }
+
+ /**
+ * Get the personal mount points of the current user
+ * The returned array is not in the same format as getUserMountPoints()
+ * @return array
+ */
+ public static function getPersonalMountPoints() {
+ $mountPoints = self::readData(true);
+ $backends = self::getBackends();
+ $uid = OCP\User::getUser();
+ $personal = array();
+ if (isset($mountPoints[self::MOUNT_TYPE_USER][$uid])) {
+ foreach ($mountPoints[self::MOUNT_TYPE_USER][$uid] as $mountPoint => $mount) {
+ // Remove '/uid/files/' from mount point
+ $personal[substr($mountPoint, strlen($uid) + 8)] = array('class' => $mount['class'], 'backend' => $backends[$mount['class']]['backend'], 'configuration' => $mount['options']);
+ }
+ }
+ return $personal;
+ }
+
+
+ /**
+ * Add a mount point to the filesystem
+ * @param string Mount point
+ * @param string Backend class
+ * @param array Backend parameters for the class
+ * @param string MOUNT_TYPE_GROUP | MOUNT_TYPE_USER
+ * @param string User or group to apply mount to
+ * @param bool Personal or system mount point i.e. is this being called from the personal or admin page
+ * @return bool
+ */
+ public static function addMountPoint($mountPoint, $class, $classOptions, $mountType, $applicable, $isPersonal = false) {
+ if ($isPersonal) {
+ // Verify that the mount point applies for the current user
+ // Prevent non-admin users from mounting local storage
+ if ($applicable != OCP\User::getUser() || $class == 'OC_Filestorage_Local') {
+ return false;
+ }
+ $mountPoint = '/'.$applicable.'/files/'.ltrim($mountPoint, '/');
+ } else {
+ $mountPoint = '/$user/files/'.ltrim($mountPoint, '/');
+ }
+ $mount = array($applicable => array($mountPoint => array('class' => $class, 'options' => $classOptions)));
+ $mountPoints = self::readData($isPersonal);
+ // Merge the new mount point into the current mount points
+ if (isset($mountPoints[$mountType])) {
+ if (isset($mountPoints[$mountType][$applicable])) {
+ $mountPoints[$mountType][$applicable] = array_merge($mountPoints[$mountType][$applicable], $mount[$applicable]);
+ } else {
+ $mountPoints[$mountType] = array_merge($mountPoints[$mountType], $mount);
+ }
+ } else {
+ $mountPoints[$mountType] = $mount;
+ }
+ self::writeData($isPersonal, $mountPoints);
+ return true;
+ }
+
+ /**
+ *
+ * @param string Mount point
+ * @param string MOUNT_TYPE_GROUP | MOUNT_TYPE_USER
+ * @param string User or group to remove mount from
+ * @param bool Personal or system mount point
+ * @return bool
+ */
+ public static function removeMountPoint($mountPoint, $mountType, $applicable, $isPersonal = false) {
+ // Verify that the mount point applies for the current user
+ if ($isPersonal) {
+ if ($applicable != OCP\User::getUser()) {
+ return false;
+ }
+ $mountPoint = '/'.$applicable.'/files/'.ltrim($mountPoint, '/');
+ } else {
+ $mountPoint = '/$user/files/'.ltrim($mountPoint, '/');
+ }
+ $mountPoints = self::readData($isPersonal);
+ // Remove mount point
+ unset($mountPoints[$mountType][$applicable][$mountPoint]);
+ // Unset parent arrays if empty
+ if (empty($mountPoints[$mountType][$applicable])) {
+ unset($mountPoints[$mountType][$applicable]);
+ if (empty($mountPoints[$mountType])) {
+ unset($mountPoints[$mountType]);
+ }
+ }
+ self::writeData($isPersonal, $mountPoints);
+ return true;
+ }
+
+ /**
+ * Read the mount points in the config file into an array
+ * @param bool Personal or system config file
+ * @return array
+ */
+ private static function readData($isPersonal) {
+ if ($isPersonal) {
+ $file = OC::$SERVERROOT.'/data/'.OCP\User::getUser().'/mount.php';
+ } else {
+ $file = OC::$SERVERROOT.'/config/mount.php';
+ }
+ if (is_file($file)) {
+ $mountPoints = include($file);
+ if (is_array($mountPoints)) {
+ return $mountPoints;
+ }
+ }
+ return array();
+ }
+
+ /**
+ * Write the mount points to the config file
+ * @param bool Personal or system config file
+ * @param array Mount points
+ */
+ private static function writeData($isPersonal, $data) {
+ if ($isPersonal) {
+ $file = OC::$SERVERROOT.'/data/'.OCP\User::getUser().'/mount.php';
+ } else {
+ $file = OC::$SERVERROOT.'/config/mount.php';
+ }
+ $content = "<?php return array (\n";
+ if (isset($data[self::MOUNT_TYPE_GROUP])) {
+ $content .= "\t'group' => array (\n";
+ foreach ($data[self::MOUNT_TYPE_GROUP] as $group => $mounts) {
+ $content .= "\t\t'".$group."' => array (\n";
+ foreach ($mounts as $mountPoint => $mount) {
+ $content .= "\t\t\t'".$mountPoint."' => ".str_replace("\n", '', var_export($mount, true)).",\n";
+
+ }
+ $content .= "\t\t),\n";
+ }
+ $content .= "\t),\n";
+ }
+ if (isset($data[self::MOUNT_TYPE_USER])) {
+ $content .= "\t'user' => array (\n";
+ foreach ($data[self::MOUNT_TYPE_USER] as $user => $mounts) {
+ $content .= "\t\t'".$user."' => array (\n";
+ foreach ($mounts as $mountPoint => $mount) {
+ $content .= "\t\t\t'".$mountPoint."' => ".str_replace("\n", '', var_export($mount, true)).",\n";
+ }
+ $content .= "\t\t),\n";
+ }
+ $content .= "\t),\n";
+ }
+ $content .= ");\n?>";
+ @file_put_contents($file, $content);
+ }
+
+ /**
+ * Returns all user uploaded ssl root certificates
+ * @return array
+ */
+ public static function getCertificates() {
+ $view = \OCP\Files::getStorage('files_external');
+ $path=\OCP\Config::getSystemValue('datadirectory').$view->getAbsolutePath("").'uploads/';
+ if (!is_dir($path)) mkdir($path);
+ $result = array();
+ $handle = opendir($path);
+ while (false !== ($file = readdir($handle))) {
+ if($file != '.' && $file != '..') $result[] = $file;
+ }
+ return $result;
+ }
+
+ /**
+ * creates certificate bundle
+ */
+ public static function createCertificateBundle() {
+ $view = \OCP\Files::getStorage("files_external");
+ $path = \OCP\Config::getSystemValue('datadirectory').$view->getAbsolutePath("");
+
+ $certs = OC_Mount_Config::getCertificates();
+ $fh_certs = fopen($path."/rootcerts.crt", 'w');
+ foreach ($certs as $cert) {
+ $file=$path.'/uploads/'.$cert;
+ $fh = fopen($file, "r");
+ $data = fread($fh, filesize($file));
+ fclose($fh);
+ if (strpos($data, 'BEGIN CERTIFICATE')) {
+ fwrite($fh_certs, $data);
+ }
+ }
+
+ fclose($fh_certs);
+
+ return true;
+ }
+
+}
+
+?>
\ No newline at end of file diff --git a/apps/files_external/lib/dropbox.php b/apps/files_external/lib/dropbox.php new file mode 100755 index 00000000000..b90563a5065 --- /dev/null +++ b/apps/files_external/lib/dropbox.php @@ -0,0 +1,255 @@ +<?php + +/** +* ownCloud +* +* @author Michael Gapczynski +* @copyright 2012 Michael Gapczynski mtgap@owncloud.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +require_once 'Dropbox/autoload.php'; + +class OC_Filestorage_Dropbox extends OC_Filestorage_Common { + + private $dropbox; + private $metaData = array(); + + private static $tempFiles = array(); + + public function __construct($params) { + if (isset($params['configured']) && $params['configured'] == 'true' && isset($params['app_key']) && isset($params['app_secret']) && isset($params['token']) && isset($params['token_secret'])) { + $oauth = new Dropbox_OAuth_Curl($params['app_key'], $params['app_secret']); + $oauth->setToken($params['token'], $params['token_secret']); + $this->dropbox = new Dropbox_API($oauth, 'dropbox'); + } else { + throw new Exception('Creating OC_Filestorage_Dropbox storage failed'); + } + } + + private function getMetaData($path, $list = false) { + if (!$list && isset($this->metaData[$path])) { + return $this->metaData[$path]; + } else { + if ($list) { + try { + $response = $this->dropbox->getMetaData($path); + } catch (Exception $exception) { + OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR); + return false; + } + if ($response && isset($response['contents'])) { + $contents = $response['contents']; + // Cache folder's contents + foreach ($contents as $file) { + $this->metaData[$path.'/'.basename($file['path'])] = $file; + } + unset($response['contents']); + $this->metaData[$path] = $response; + } + $this->metaData[$path] = $response; + // Return contents of folder only + return $contents; + } else { + try { + $response = $this->dropbox->getMetaData($path, 'false'); + $this->metaData[$path] = $response; + return $response; + } catch (Exception $exception) { + OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR); + return false; + } + } + } + } + + public function mkdir($path) { + try { + $this->dropbox->createFolder($path); + return true; + } catch (Exception $exception) { + OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR); + return false; + } + } + + public function rmdir($path) { + return $this->unlink($path); + } + + public function opendir($path) { + if ($contents = $this->getMetaData($path, true)) { + $files = array(); + foreach ($contents as $file) { + $files[] = basename($file['path']); + } + OC_FakeDirStream::$dirs['dropbox'.$path] = $files; + return opendir('fakedir://dropbox'.$path); + } + return false; + } + + public function stat($path) { + if ($metaData = $this->getMetaData($path)) { + $stat['size'] = $metaData['bytes']; + $stat['atime'] = time(); + $stat['mtime'] = (isset($metaData['modified'])) ? strtotime($metaData['modified']) : time(); + $stat['ctime'] = $stat['mtime']; + return $stat; + } + return false; + } + + public function filetype($path) { + if ($path == '' || $path == '/') { + return 'dir'; + } else if ($metaData = $this->getMetaData($path)) { + if ($metaData['is_dir'] == 'true') { + return 'dir'; + } else { + return 'file'; + } + } + return false; + } + + public function isReadable($path) { + return $this->file_exists($path); + } + + public function isUpdatable($path) { + return $this->file_exists($path); + } + + public function file_exists($path) { + if ($path == '' || $path == '/') { + return true; + } + if ($this->getMetaData($path)) { + return true; + } + return false; + } + + public function unlink($path) { + try { + $this->dropbox->delete($path); + return true; + } catch (Exception $exception) { + OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR); + return false; + } + } + + public function rename($path1, $path2) { + try { + $this->dropbox->move($path1, $path2); + return true; + } catch (Exception $exception) { + OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR); + return false; + } + } + + public function copy($path1, $path2) { + try { + $this->dropbox->copy($path1, $path2); + return true; + } catch (Exception $exception) { + OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR); + return false; + } + } + + public function fopen($path, $mode) { + switch ($mode) { + case 'r': + case 'rb': + $tmpFile = OC_Helper::tmpFile(); + try { + $data = $this->dropbox->getFile($path); + file_put_contents($tmpFile, $data); + return fopen($tmpFile, 'r'); + } catch (Exception $exception) { + OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR); + return false; + } + case 'w': + case 'wb': + case 'a': + case 'ab': + case 'r+': + case 'w+': + case 'wb+': + case 'a+': + case 'x': + case 'x+': + case 'c': + case 'c+': + if (strrpos($path, '.') !== false) { + $ext = substr($path, strrpos($path, '.')); + } else { + $ext = ''; + } + $tmpFile = OC_Helper::tmpFile($ext); + OC_CloseStreamWrapper::$callBacks[$tmpFile] = array($this, 'writeBack'); + if ($this->file_exists($path)) { + $source = $this->fopen($path, 'r'); + file_put_contents($tmpFile, $source); + } + self::$tempFiles[$tmpFile] = $path; + return fopen('close://'.$tmpFile, $mode); + } + return false; + } + + public function writeBack($tmpFile) { + if (isset(self::$tempFiles[$tmpFile])) { + $handle = fopen($tmpFile, 'r'); + try { + $this->dropbox->putFile(self::$tempFiles[$tmpFile], $handle); + unlink($tmpFile); + } catch (Exception $exception) { + OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR); + } + } + } + + public function getMimeType($path) { + if ($this->filetype($path) == 'dir') { + return 'httpd/unix-directory'; + } else if ($metaData = $this->getMetaData($path)) { + return $metaData['mime_type']; + } + return false; + } + + public function free_space($path) { + try { + $info = $this->dropbox->getAccountInfo(); + return $info['quota_info']['quota'] - $info['quota_info']['normal']; + } catch (Exception $exception) { + OCP\Util::writeLog('files_external', $exception->getMessage(), OCP\Util::ERROR); + return false; + } + } + + public function touch($path, $mtime = null) { + return false; + } + +} + +?>
\ No newline at end of file diff --git a/apps/files_external/lib/google.php b/apps/files_external/lib/google.php index d2285a6d82c..73317bbf714 100644 --- a/apps/files_external/lib/google.php +++ b/apps/files_external/lib/google.php @@ -20,7 +20,7 @@ * License along with this library. If not, see <http://www.gnu.org/licenses/>. */ -require_once 'common.inc.php'; +require_once 'Google/common.inc.php'; class OC_Filestorage_Google extends OC_Filestorage_Common { @@ -31,16 +31,20 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { private static $tempFiles = array(); - public function __construct($arguments) { - $consumer_key = isset($arguments['consumer_key']) ? $arguments['consumer_key'] : 'anonymous'; - $consumer_secret = isset($arguments['consumer_secret']) ? $arguments['consumer_secret'] : 'anonymous'; - $this->consumer = new OAuthConsumer($consumer_key, $consumer_secret); - $this->oauth_token = new OAuthToken($arguments['token'], $arguments['token_secret']); - $this->sig_method = new OAuthSignatureMethod_HMAC_SHA1(); - $this->entries = array(); + public function __construct($params) { + if (isset($params['configured']) && $params['configured'] == 'true' && isset($params['token']) && isset($params['token_secret'])) { + $consumer_key = isset($params['consumer_key']) ? $params['consumer_key'] : 'anonymous'; + $consumer_secret = isset($params['consumer_secret']) ? $params['consumer_secret'] : 'anonymous'; + $this->consumer = new OAuthConsumer($consumer_key, $consumer_secret); + $this->oauth_token = new OAuthToken($params['token'], $params['token_secret']); + $this->sig_method = new OAuthSignatureMethod_HMAC_SHA1(); + $this->entries = array(); + } else { + throw new Exception('Creating OC_Filestorage_Google storage failed'); + } } - private function sendRequest($uri, $httpMethod, $postData = null, $extraHeaders = null, $isDownload = false, $returnHeaders = false, $isContentXML = true) { + private function sendRequest($uri, $httpMethod, $postData = null, $extraHeaders = null, $isDownload = false, $returnHeaders = false, $isContentXML = true, $returnHTTPCode = false) { $uri = trim($uri); // create an associative array from each key/value url query param pair. $params = array(); @@ -108,6 +112,8 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { if ($httpCode <= 308) { if ($isDownload) { return $tmpFile; + } else if ($returnHTTPCode) { + return array('result' => $result, 'code' => $httpCode); } else { return $result; } @@ -176,7 +182,7 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { if ($collection == '/' || $collection == '\.' || $collection == '.') { $uri = 'https://docs.google.com/feeds/default/private/full'; // Get parent content link - } else if ($dom = $this->getResource(basename($dir))) { + } else if ($dom = $this->getResource(basename($collection))) { $uri = $dom->getElementsByTagName('content')->item(0)->getAttribute('src'); } if (isset($uri)) { @@ -235,8 +241,8 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { $this->entries[$name] = $entry; } } - OC_FakeDirStream::$dirs['google'] = $files; - return opendir('fakedir://google'); + OC_FakeDirStream::$dirs['google'.$path] = $files; + return opendir('fakedir://google'.$path); } public function stat($path) { @@ -278,11 +284,11 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { return false; } - public function is_readable($path) { + public function isReadable($path) { return true; } - public function is_writable($path) { + public function isUpdatable($path) { if ($path == '' || $path == '/') { return true; } else if ($entry = $this->getResource($path)) { @@ -339,7 +345,7 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { break; } } - $title = basename($path); + $title = basename($path2); // Construct post data $postData = '<?xml version="1.0" encoding="UTF-8"?>'; $postData .= '<entry xmlns="http://www.w3.org/2005/Atom" xmlns:docs="http://schemas.google.com/docs/2007" xmlns:gd="http://schemas.google.com/g/2005" gd:etag='.$etag.'>'; @@ -350,13 +356,13 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { } else { // Move to different collection if ($collectionEntry = $this->getResource($collection)) { - $feedUri = $colelctionEntry->getElementsByTagName('content')->item(0)->getAttribute('src'); + $feedUri = $collectionEntry->getElementsByTagName('content')->item(0)->getAttribute('src'); // Construct post data $postData = '<?xml version="1.0" encoding="UTF-8"?>'; $postData .= '<entry xmlns="http://www.w3.org/2005/Atom">'; $postData .= '<id>'.$entry->getElementsByTagName('id')->item(0).'</id>'; $postData .= '</entry>'; - $this->sendRequest($uri, 'POST', $postData); + $this->sendRequest($feedUri, 'POST', $postData); return true; } } @@ -422,10 +428,9 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { } } if (!isset($uploadUri) && $entry) { - $etag = $entry->getAttribute('gd:etag'); $links = $entry->getElementsByTagName('link'); foreach ($links as $link) { - if ($link->getAttribute('rel') == 'http://schemas.google.com/g/2005#resumable-edit-media') { + if ($link->getAttribute('rel') == 'http://schemas.google.com/g/2005#resumable-create-media') { $uploadUri = $link->getAttribute('href'); break; } @@ -461,12 +466,12 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { } } $end = $i + $chunkSize - 1; - $headers = array('Content-Length: '.$chunkSize, 'Content-Type: '.$mimetype, 'Content Range: bytes '.$i.'-'.$end.'/'.$size); + $headers = array('Content-Length: '.$chunkSize, 'Content-Type: '.$mimetype, 'Content-Range: bytes '.$i.'-'.$end.'/'.$size); $postData = fread($handle, $chunkSize); - $result = $this->sendRequest($uploadUri, 'PUT', $postData, $headers, false, true, false); - if ($result) { - // Get next location to upload file chunk - if (preg_match('@^Location: (.*)$@m', $result, $matches)) { + $result = $this->sendRequest($uploadUri, 'PUT', $postData, $headers, false, true, false, true); + if ($result['code'] == '308') { + if (preg_match('@^Location: (.*)$@m', $result['result'], $matches)) { + // Get next location to upload file chunk $uploadUri = trim($matches[1]); } $i += $chunkSize; diff --git a/apps/files_external/lib/smb.php b/apps/files_external/lib/smb.php index 96470002c35..8a5e993b1d0 100644 --- a/apps/files_external/lib/smb.php +++ b/apps/files_external/lib/smb.php @@ -15,21 +15,19 @@ class OC_FileStorage_SMB extends OC_FileStorage_StreamWrapper{ private $root; private $share; - private static $tempFiles=array(); - public function __construct($params){ $this->host=$params['host']; $this->user=$params['user']; $this->password=$params['password']; $this->share=$params['share']; $this->root=isset($params['root'])?$params['root']:'/'; + if(!$this->root || $this->root[0]!='/'){ + $this->root='/'.$this->root; + } if(substr($this->root,-1,1)!='/'){ $this->root.='/'; } - if(substr($this->root,0,1)!='/'){ - $this->root='/'.$this->root; - } - if(substr($this->share,0,1)!='/'){ + if(!$this->share || $this->share[0]!='/'){ $this->share='/'.$this->share; } if(substr($this->share,-1,1)=='/'){ diff --git a/apps/files_external/lib/streamwrapper.php b/apps/files_external/lib/streamwrapper.php index 7d56445361e..467c5a5b845 100644 --- a/apps/files_external/lib/streamwrapper.php +++ b/apps/files_external/lib/streamwrapper.php @@ -32,11 +32,11 @@ abstract class OC_FileStorage_StreamWrapper extends OC_Filestorage_Common{ return filetype($this->constructUrl($path)); } - public function is_readable($path){ + public function isReadable($path){ return true;//not properly supported } - public function is_writable($path){ + public function isUpdatable($path){ return true;//not properly supported } diff --git a/apps/files_external/lib/swift.php b/apps/files_external/lib/swift.php index 58b95a6ae01..94ccde1ff8f 100644 --- a/apps/files_external/lib/swift.php +++ b/apps/files_external/lib/swift.php @@ -353,11 +353,11 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ } } - public function is_readable($path){ + public function isReadable($path){ return true; } - public function is_writable($path){ + public function isUpdatable($path){ return true; } diff --git a/apps/files_external/lib/webdav.php b/apps/files_external/lib/webdav.php index d136f04f3eb..e3f73c5c0a7 100644 --- a/apps/files_external/lib/webdav.php +++ b/apps/files_external/lib/webdav.php @@ -20,10 +20,14 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{ private static $tempFiles=array(); public function __construct($params){ - $this->host=$params['host']; + $host = $params['host']; + //remove leading http[s], will be generated in createBaseUri() + if (substr($host,0,8) == "https://") $host = substr($host, 8); + else if (substr($host,0,7) == "http://") $host = substr($host, 7); + $this->host=$host; $this->user=$params['user']; $this->password=$params['password']; - $this->secure=isset($params['secure'])?(bool)$params['secure']:false; + $this->secure=(isset($params['secure']) && $params['secure'] == 'true')?true:false; $this->root=isset($params['root'])?$params['root']:'/'; if(!$this->root || $this->root[0]!='/'){ $this->root='/'.$this->root; @@ -37,8 +41,15 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{ 'userName' => $this->user, 'password' => $this->password, ); - $this->client = new Sabre_DAV_Client($settings); + $this->client = new OC_Connector_Sabre_Client($settings); + + if($caview = \OCP\Files::getStorage('files_external')) { + $certPath=\OCP\Config::getSystemValue('datadirectory').$caview->getAbsolutePath("").'rootcerts.crt'; + if (file_exists($certPath)) { + $this->client->addTrustedCertificates($certPath); + } + } //create the root folder if necesary $this->mkdir(''); } @@ -46,7 +57,7 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{ private function createBaseUri(){ $baseUri='http'; if($this->secure){ - $baseUri.'s'; + $baseUri.='s'; } $baseUri.='://'.$this->host.$this->root; return $baseUri; @@ -66,16 +77,13 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{ $path=$this->cleanPath($path); try{ $response=$this->client->propfind($path, array(),1); - $stripLength=strlen($this->root)+strlen($path); $id=md5('webdav'.$this->root.$path); OC_FakeDirStream::$dirs[$id]=array(); - foreach($response as $file=>$data){ - //strip root and path - $file=trim(substr($file,$stripLength)); - $file=trim($file,'/'); - if($file){ - OC_FakeDirStream::$dirs[$id][]=$file; - } + $files=array_keys($response); + array_shift($files);//the first entry is the current directory + foreach($files as $file){ + $file = urldecode(basename($file)); + OC_FakeDirStream::$dirs[$id][]=$file; } return opendir('fakedir://'.$id); }catch(Exception $e){ @@ -90,22 +98,24 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{ $responseType=$response["{DAV:}resourcetype"]->resourceType; return (count($responseType)>0 and $responseType[0]=="{DAV:}collection")?'dir':'file'; }catch(Exception $e){ + error_log($e->getMessage()); + \OCP\Util::writeLog("webdav client", \OCP\Util::sanitizeHTML($e->getMessage()), \OCP\Util::ERROR); return false; } } - public function is_readable($path){ + public function isReadable($path){ return true;//not properly supported } - public function is_writable($path){ + public function isUpdatable($path){ return true;//not properly supported } public function file_exists($path){ $path=$this->cleanPath($path); try{ - $response=$this->client->propfind($path, array('{DAV:}resourcetype')); + $this->client->propfind($path, array('{DAV:}resourcetype')); return true;//no 404 exception }catch(Exception $e){ return false; @@ -186,7 +196,7 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{ $mtime=time(); } $path=$this->cleanPath($path); - $this->client->proppatch($path, array('{DAV:}lastmodified' => $mtime,)); + $this->client->proppatch($path, array('{DAV:}lastmodified' => $mtime)); } public function getFile($path,$target){ @@ -240,15 +250,11 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{ $path=$this->cleanPath($path); try{ $response=$this->client->propfind($path, array('{DAV:}getlastmodified','{DAV:}getcontentlength')); - if(isset($response['{DAV:}getlastmodified']) and isset($response['{DAV:}getcontentlength'])){ - return array( - 'mtime'=>strtotime($response['{DAV:}getlastmodified']), - 'size'=>(int)$response['{DAV:}getcontentlength'], - 'ctime'=>-1, - ); - }else{ - return array(); - } + return array( + 'mtime'=>strtotime($response['{DAV:}getlastmodified']), + 'size'=>(int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0, + 'ctime'=>-1, + ); }catch(Exception $e){ return array(); } diff --git a/apps/files_external/personal.php b/apps/files_external/personal.php new file mode 100755 index 00000000000..dec501741b6 --- /dev/null +++ b/apps/files_external/personal.php @@ -0,0 +1,35 @@ +<?php + +/** +* ownCloud +* +* @author Michael Gapczynski +* @copyright 2012 Michael Gapczynski mtgap@owncloud.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +OCP\Util::addScript('files_external', 'settings'); +OCP\Util::addStyle('files_external', 'settings'); +$backends = OC_Mount_Config::getBackends(); +// Remove local storage +unset($backends['OC_Filestorage_Local']); +$tmpl = new OCP\Template('files_external', 'settings'); +$tmpl->assign('isAdminPage', false, false); +$tmpl->assign('mounts', OC_Mount_Config::getPersonalMountPoints()); +$tmpl->assign('certs', OC_Mount_Config::getCertificates()); +$tmpl->assign('backends', $backends); +return $tmpl->fetchPage(); + +?>
\ No newline at end of file diff --git a/apps/files_external/settings.php b/apps/files_external/settings.php new file mode 100644 index 00000000000..acc9036b299 --- /dev/null +++ b/apps/files_external/settings.php @@ -0,0 +1,34 @@ +<?php + +/** +* ownCloud +* +* @author Michael Gapczynski +* @copyright 2012 Michael Gapczynski mtgap@owncloud.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +OCP\Util::addScript('files_external', 'settings'); +OCP\Util::addStyle('files_external', 'settings'); +$tmpl = new OCP\Template('files_external', 'settings'); +$tmpl->assign('isAdminPage', true, false); +$tmpl->assign('mounts', OC_Mount_Config::getSystemMountPoints()); +$tmpl->assign('backends', OC_Mount_Config::getBackends()); +$tmpl->assign('groups', OC_Group::getGroups()); +$tmpl->assign('users', OCP\User::getUsers()); +$tmpl->assign('allowUserMounting', OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes')); +return $tmpl->fetchPage(); + +?>
\ No newline at end of file diff --git a/apps/files_external/templates/settings.php b/apps/files_external/templates/settings.php new file mode 100644 index 00000000000..397f0d951b0 --- /dev/null +++ b/apps/files_external/templates/settings.php @@ -0,0 +1,112 @@ +<form id="files_external" method="post" enctype="multipart/form-data" action="<?php echo OCP\Util::linkTo('files_external', 'ajax/addRootCertificate.php'); ?>"> + <fieldset class="personalblock"> + <legend><strong><?php echo $l->t('External Storage'); ?></strong></legend> + <table id="externalStorage" data-admin='<?php echo json_encode($_['isAdminPage']); ?>'> + <thead> + <tr> + <th><?php echo $l->t('Mount point'); ?></th> + <th><?php echo $l->t('Backend'); ?></th> + <th><?php echo $l->t('Configuration'); ?></th> + <!--<th><?php echo $l->t('Options'); ?></th> --> + <?php if ($_['isAdminPage']) echo '<th>'.$l->t('Applicable').'</th>'; ?> + <th> </th> + </tr> + </thead> + <tbody width="100%"> + <?php $_['mounts'] = array_merge($_['mounts'], array('' => array())); ?> + <?php foreach ($_['mounts'] as $mountPoint => $mount): ?> + <tr <?php echo ($mountPoint != '') ? 'class="'.$mount['class'].'"' : 'id="addMountPoint"'; ?>> + <td class="mountPoint"><input type="text" name="mountPoint" value="<?php echo $mountPoint; ?>" placeholder="<?php echo $l->t('Mount point'); ?>" /></td> + <?php if ($mountPoint == ''): ?> + <td class="backend"> + <select id="selectBackend" data-configurations='<?php echo json_encode($_['backends']); ?>'> + <option value="" disabled selected style="display:none;"><?php echo $l->t('Add mount point'); ?></option> + <?php foreach ($_['backends'] as $class => $backend): ?> + <option value="<?php echo $class; ?>"><?php echo $backend['backend']; ?></option> + <?php endforeach; ?> + </select> + </td> + <?php else: ?> + <td class="backend" data-class="<?php echo $mount['class']; ?>"><?php echo $mount['backend']; ?></td> + <?php endif; ?> + <td class ="configuration" width="100%"> + <?php if (isset($mount['configuration'])): ?> + <?php foreach ($mount['configuration'] as $parameter => $value): ?> + <?php if (isset($_['backends'][$mount['class']]['configuration'][$parameter])): ?> + <?php $placeholder = $_['backends'][$mount['class']]['configuration'][$parameter]; ?> + <?php if (strpos($placeholder, '*') !== false): ?> + <input type="password" data-parameter="<?php echo $parameter; ?>" value="<?php echo $value; ?>" placeholder="<?php echo substr($placeholder, 1); ?>" /> + <?php elseif(strpos($placeholder, '!') !== false): ?> + <label><input type="checkbox" data-parameter="<?php echo $parameter; ?>" <?php if ($value == 'true') echo ' checked="checked"'; ?> /><?php echo substr($placeholder, 1); ?></label> + <?php elseif (strpos($placeholder, '&') !== false): ?> + <input type="text" class="optional" data-parameter="<?php echo $parameter; ?>" value="<?php echo $value; ?>" placeholder="<?php echo substr($placeholder, 5); ?>" /> + <?php elseif (strpos($placeholder, '#') !== false): ?> + <input type="hidden" data-parameter="<?php echo $parameter; ?>" value="<?php echo $value; ?>" /> + <?php else: ?> + <input type="text" data-parameter="<?php echo $parameter; ?>" value="<?php echo $value; ?>" placeholder="<?php echo $placeholder; ?>" /> + <?php endif; ?> + <?php endif; ?> + <?php endforeach; ?> + <?php if (isset($_['backends'][$mount['class']]['custom'])) OCP\Util::addScript('files_external', $_['backends'][$mount['class']]['custom']); ?> + <?php endif; ?> + </td> + <!--<td class="options"> + <select class="selectOptions" title="<?php echo $l->t('None set')?>" multiple="multiple" disabled> + <?php if (OCP\App::isEnabled('files_encryption')) echo '<option value="Encrypt">Encrypt</option>'; ?> + <?php if (OCP\App::isEnabled('files_versions')) echo '<option value="Version control">Version control</option>'; ?> + <?php if (OCP\App::isEnabled('files_sharing')) echo '<option value="Allow sharing">Allow sharing</option>'; ?> + </select> + </td>--> + <?php if ($_['isAdminPage']): ?> + <td class="applicable" align="right" data-applicable-groups='<?php if (isset($mount['applicable']['groups'])) echo json_encode($mount['applicable']['groups']); ?>' data-applicable-users='<?php if (isset($mount['applicable']['users'])) echo json_encode($mount['applicable']['users']); ?>'> + <select class="chzn-select" multiple style="width:20em;" data-placeholder="<?php echo $l->t('None set'); ?>"> + <option value="all"><?php echo $l->t('All Users'); ?></option> + <optgroup label="<?php echo $l->t('Groups'); ?>"> + <?php foreach ($_['groups'] as $group): ?> + <option value="<?php echo $group; ?>(group)" <?php if (isset($mount['applicable']['groups']) && in_array($group, $mount['applicable']['groups'])) echo 'selected="selected"'; ?>><?php echo $group; ?></option> + <?php endforeach; ?> + </optgroup> + <optgroup label="<?php echo $l->t('Users'); ?>"> + <?php foreach ($_['users'] as $user): ?> + <option value="<?php echo $user; ?>" <?php if (isset($mount['applicable']['users']) && in_array($user, $mount['applicable']['users'])) echo 'selected="selected"'; ?>"><?php echo $user; ?></option> + <?php endforeach; ?> + </optgroup> + </select> + </td> + <?php endif; ?> + <td <?php echo ($mountPoint != '') ? 'class="remove"' : 'style="visibility:hidden;"'; ?>><img alt="<?php echo $l->t('Delete'); ?>" title="<?php echo $l->t('Delete'); ?>" class="svg action" src="<?php echo image_path('core', 'actions/delete.svg'); ?>" /></td> + </tr> + <?php endforeach; ?> + </tbody> + </table> + <br /> + + <?php if (!$_['isAdminPage']): ?> + <table id="sslCertificate" data-admin='<?php echo json_encode($_['isAdminPage']); ?>'> + <thead> + <tr> + <th><?php echo $l->t('SSL root certificates'); ?></th> + <th> </th> + </tr> + </thead> + <tbody width="100%"> + <?php foreach ($_['certs'] as $rootCert): ?> + <tr id="<?php echo $rootCert ?>"> + <td class="rootCert"><?php echo $rootCert ?></td> + <td <?php echo ($rootCert != '') ? 'class="remove"' : 'style="visibility:hidden;"'; ?>><img alt="<?php echo $l->t('Delete'); ?>" title="<?php echo $l->t('Delete'); ?>" class="svg action" src="<?php echo image_path('core', 'actions/delete.svg'); ?>" /></td> + </tr> + <?php endforeach; ?> + </tbody> + </table> + <input type="file" id="rootcert_import" name="rootcert_import" style="width:230px;"> + <input type="submit" name="cert_import" value="<?php echo $l->t('Import Root Certificate'); ?>" /> + <?php endif; ?> + + <?php if ($_['isAdminPage']): ?> + <br /> + <input type="checkbox" name="allowUserMounting" id="allowUserMounting" value="1" <?php if ($_['allowUserMounting'] == 'yes') echo ' checked="checked"'; ?> /> + <label for="allowUserMounting"><?php echo $l->t('Enable User External Storage'); ?></label><br/> + <em><?php echo $l->t('Allow users to mount their own external storage'); ?></em> + <?php endif; ?> + </fieldset> +</form> diff --git a/apps/files_external/tests/amazons3.php b/apps/files_external/tests/amazons3.php new file mode 100644 index 00000000000..b9b4cf65bd6 --- /dev/null +++ b/apps/files_external/tests/amazons3.php @@ -0,0 +1,47 @@ +<?php + +/** +* ownCloud +* +* @author Michael Gapczynski +* @copyright 2012 Michael Gapczynski mtgap@owncloud.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +$config = include('apps/files_external/tests/config.php'); +if (!is_array($config) or !isset($config['amazons3']) or !$config['amazons3']['run']) { + abstract class Test_Filestorage_AmazonS3 extends Test_FileStorage{} + return; +} else { + class Test_Filestorage_AmazonS3 extends Test_FileStorage { + + private $config; + private $id; + + public function setUp() { + $id = uniqid(); + $this->config = include('apps/files_external/tests/config.php'); + $this->config['amazons3']['bucket'] = $id; // Make sure we have a new empty bucket to work in + $this->instance = new OC_Filestorage_AmazonS3($this->config['amazons3']); + } + + public function tearDown() { + $s3 = new AmazonS3(array('key' => $this->config['amazons3']['key'], 'secret' => $this->config['amazons3']['secret'])); + if ($s3->delete_all_objects($this->id)) { + $s3->delete_bucket($this->id); + } + } + } +} diff --git a/apps/files_external/tests/config.php b/apps/files_external/tests/config.php index 1b47618e472..e58a87fabdf 100644 --- a/apps/files_external/tests/config.php +++ b/apps/files_external/tests/config.php @@ -37,4 +37,10 @@ return array( 'share'=>'/test', 'root'=>'/test/', ), + 'amazons3'=>array( + 'run'=>false, + 'key'=>'test', + 'secret'=>'test', + 'bucket'=>'bucket', + ), ); diff --git a/apps/files_external/tests/ftp.php b/apps/files_external/tests/ftp.php index 68481b4e66b..97796bca128 100644 --- a/apps/files_external/tests/ftp.php +++ b/apps/files_external/tests/ftp.php @@ -13,7 +13,6 @@ if(!is_array($config) or !isset($config['ftp']) or !$config['ftp']['run']){ }else{ class Test_Filestorage_FTP extends Test_FileStorage { private $config; - private $id; public function setUp(){ $id=uniqid(); diff --git a/apps/files_external/tests/google.php b/apps/files_external/tests/google.php index 08116f0e748..806db5a6aaa 100644 --- a/apps/files_external/tests/google.php +++ b/apps/files_external/tests/google.php @@ -28,7 +28,6 @@ if(!is_array($config) or !isset($config['google']) or !$config['google']['run']) class Test_Filestorage_Google extends Test_FileStorage { private $config; - private $id; public function setUp(){ $id=uniqid(); diff --git a/apps/files_external/tests/smb.php b/apps/files_external/tests/smb.php index e1495b7480d..001ef842276 100644 --- a/apps/files_external/tests/smb.php +++ b/apps/files_external/tests/smb.php @@ -14,7 +14,6 @@ if(!is_array($config) or !isset($config['smb']) or !$config['smb']['run']){ }else{ class Test_Filestorage_SMB extends Test_FileStorage { private $config; - private $id; public function setUp(){ $id=uniqid(); diff --git a/apps/files_external/tests/swift.php b/apps/files_external/tests/swift.php index f0bde6ed605..1520c9473d3 100644 --- a/apps/files_external/tests/swift.php +++ b/apps/files_external/tests/swift.php @@ -13,7 +13,6 @@ if(!is_array($config) or !isset($config['swift']) or !$config['swift']['run']){ }else{ class Test_Filestorage_SWIFT extends Test_FileStorage { private $config; - private $id; public function setUp(){ $id=uniqid(); diff --git a/apps/files_external/tests/test.php b/apps/files_external/tests/test.php new file mode 100644 index 00000000000..bd24404f3b9 --- /dev/null +++ b/apps/files_external/tests/test.php @@ -0,0 +1,7 @@ +<?php +require_once 'files_external/lib/config.php'; +echo "<pre>"; +print_r(OC_Mount_Config::getSystemMountPoints()); +echo "</pre>"; +// OC_Mount_Config::addMountPoint('Photos', 'OC_Filestorage_SWIFT', array('host' => 'gapinthecloud.com', 'user' => 'Gap', 'token' => '23423afdasFJEW22', 'secure' => 'true', 'root' => ''), OC_Mount_Config::MOUNT_TYPE_GROUP, 'admin', false); +?> diff --git a/apps/files_external/tests/webdav.php b/apps/files_external/tests/webdav.php index 144659819b6..14abbef2cbf 100644 --- a/apps/files_external/tests/webdav.php +++ b/apps/files_external/tests/webdav.php @@ -13,7 +13,6 @@ if(!is_array($config) or !isset($config['webdav']) or !$config['webdav']['run']) }else{ class Test_Filestorage_DAV extends Test_FileStorage { private $config; - private $id; public function setUp(){ $id=uniqid(); diff --git a/apps/files_imageviewer/appinfo/app.php b/apps/files_imageviewer/appinfo/app.php index 6c8d8c30cad..6184585cff3 100644 --- a/apps/files_imageviewer/appinfo/app.php +++ b/apps/files_imageviewer/appinfo/app.php @@ -4,5 +4,3 @@ OCP\Util::addscript( 'files_imageviewer', 'lightbox' ); OCP\Util::addscript('files_imageviewer', 'jquery.mousewheel-3.0.4.pack'); OCP\Util::addscript('files_imageviewer', 'jquery.fancybox-1.3.4.pack'); OCP\Util::addStyle( 'files_imageviewer', 'jquery.fancybox-1.3.4' ); - -?> diff --git a/apps/files_imageviewer/css/jquery.fancybox-1.3.4.css b/apps/files_imageviewer/css/jquery.fancybox-1.3.4.css index 5fdf7af14cb..6e982805a44 100644 --- a/apps/files_imageviewer/css/jquery.fancybox-1.3.4.css +++ b/apps/files_imageviewer/css/jquery.fancybox-1.3.4.css @@ -35,7 +35,7 @@ left: 0;
width: 40px;
height: 480px;
- background-image: url('%appswebroot%/apps/files_imageviewer/img/fancybox.png');
+ background-image: url('%appswebroot%/files_imageviewer/img/fancybox.png');
}
#fancybox-overlay {
@@ -99,7 +99,7 @@ right: -15px;
width: 30px;
height: 30px;
- background: transparent url('%appswebroot%/apps/files_imageviewer/img/fancybox.png') -40px 0px;
+ background: transparent url('%appswebroot%/files_imageviewer/img/fancybox.png') -40px 0px;
cursor: pointer;
z-index: 1103;
display: none;
@@ -137,7 +137,7 @@ width: 35%;
cursor: pointer;
outline: none;
- background: transparent url('%appswebroot%/apps/files_imageviewer/img/blank.gif');
+ background: transparent url('%appswebroot%/files_imageviewer/img/blank.gif');
z-index: 1102;
display: none;
}
@@ -163,12 +163,12 @@ }
#fancybox-left-ico {
- background-image: url('%appswebroot%/apps/files_imageviewer/img/fancybox.png');
+ background-image: url('%appswebroot%/files_imageviewer/img/fancybox.png');
background-position: -40px -30px;
}
#fancybox-right-ico {
- background-image: url('%appswebroot%/apps/files_imageviewer/img/fancybox.png');
+ background-image: url('%appswebroot%/files_imageviewer/img/fancybox.png');
background-position: -40px -60px;
}
@@ -199,13 +199,13 @@ top: -20px;
left: 0;
width: 100%;
- background-image: url('%appswebroot%/apps/files_imageviewer/img/fancybox-x.png');
+ background-image: url('%appswebroot%/files_imageviewer/img/fancybox-x.png');
}
#fancybox-bg-ne {
top: -20px;
right: -20px;
- background-image: url('%appswebroot%/apps/files_imageviewer/img/fancybox.png');
+ background-image: url('%appswebroot%/files_imageviewer/img/fancybox.png');
background-position: -40px -162px;
}
@@ -213,14 +213,14 @@ top: 0;
right: -20px;
height: 100%;
- background-image: url('%appswebroot%/apps/files_imageviewer/img/fancybox-y.png');
+ background-image: url('%appswebroot%/files_imageviewer/img/fancybox-y.png');
background-position: -20px 0px;
}
#fancybox-bg-se {
bottom: -20px;
right: -20px;
- background-image: url('%appswebroot%/apps/files_imageviewer/img/fancybox.png');
+ background-image: url('%appswebroot%/files_imageviewer/img/fancybox.png');
background-position: -40px -182px;
}
@@ -228,14 +228,14 @@ bottom: -20px;
left: 0;
width: 100%;
- background-image: url('%appswebroot%/apps/files_imageviewer/img/fancybox-x.png');
+ background-image: url('%appswebroot%/files_imageviewer/img/fancybox-x.png');
background-position: 0px -20px;
}
#fancybox-bg-sw {
bottom: -20px;
left: -20px;
- background-image: url('%appswebroot%/apps/files_imageviewer/img/fancybox.png');
+ background-image: url('%appswebroot%/files_imageviewer/img/fancybox.png');
background-position: -40px -142px;
}
@@ -243,13 +243,13 @@ top: 0;
left: -20px;
height: 100%;
- background-image: url('%appswebroot%/apps/files_imageviewer/img/fancybox-y.png');
+ background-image: url('%appswebroot%/files_imageviewer/img/fancybox-y.png');
}
#fancybox-bg-nw {
top: -20px;
left: -20px;
- background-image: url('%appswebroot%/apps/files_imageviewer/img/fancybox.png');
+ background-image: url('%appswebroot%/files_imageviewer/img/fancybox.png');
background-position: -40px -122px;
}
@@ -282,7 +282,7 @@ #fancybox-title-over {
padding: 10px;
- background-image: url('%appswebroot%/apps/files_imageviewer/img/fancy_title_over.png');
+ background-image: url('%appswebroot%/files_imageviewer/img/fancy_title_over.png');
display: block;
}
@@ -306,7 +306,7 @@ #fancybox-title-float-left {
padding: 0 0 0 15px;
- background: url('%appswebroot%/apps/files_imageviewer/img/fancybox.png') -40px -90px no-repeat;
+ background: url('%appswebroot%/files_imageviewer/img/fancybox.png') -40px -90px no-repeat;
}
#fancybox-title-float-main {
@@ -314,25 +314,25 @@ line-height: 29px;
font-weight: bold;
padding: 0 0 3px 0;
- background: url('%appswebroot%/apps/files_imageviewer/img/fancybox-x.png') 0px -40px;
+ background: url('%appswebroot%/files_imageviewer/img/fancybox-x.png') 0px -40px;
}
#fancybox-title-float-right {
padding: 0 0 0 15px;
- background: url('%appswebroot%/apps/files_imageviewer/img/fancybox.png') -55px -90px no-repeat;
+ background: url('%appswebroot%/files_imageviewer/img/fancybox.png') -55px -90px no-repeat;
}
/* IE6 */
-.fancybox-ie6 #fancybox-close { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_close.png', sizingMethod='scale'); }
+.fancybox-ie6 #fancybox-close { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_close.png', sizingMethod='scale'); }
-.fancybox-ie6 #fancybox-left-ico { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_nav_left.png', sizingMethod='scale'); }
-.fancybox-ie6 #fancybox-right-ico { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_nav_right.png', sizingMethod='scale'); }
+.fancybox-ie6 #fancybox-left-ico { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_nav_left.png', sizingMethod='scale'); }
+.fancybox-ie6 #fancybox-right-ico { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_nav_right.png', sizingMethod='scale'); }
-.fancybox-ie6 #fancybox-title-over { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_title_over.png', sizingMethod='scale'); zoom: 1; }
-.fancybox-ie6 #fancybox-title-float-left { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_title_left.png', sizingMethod='scale'); }
-.fancybox-ie6 #fancybox-title-float-main { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_title_main.png', sizingMethod='scale'); }
-.fancybox-ie6 #fancybox-title-float-right { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_title_right.png', sizingMethod='scale'); }
+.fancybox-ie6 #fancybox-title-over { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_title_over.png', sizingMethod='scale'); zoom: 1; }
+.fancybox-ie6 #fancybox-title-float-left { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_title_left.png', sizingMethod='scale'); }
+.fancybox-ie6 #fancybox-title-float-main { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_title_main.png', sizingMethod='scale'); }
+.fancybox-ie6 #fancybox-title-float-right { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_title_right.png', sizingMethod='scale'); }
.fancybox-ie6 #fancybox-bg-w, .fancybox-ie6 #fancybox-bg-e, .fancybox-ie6 #fancybox-left, .fancybox-ie6 #fancybox-right, #fancybox-hide-sel-frame {
height: expression(this.parentNode.clientHeight + "px");
@@ -343,17 +343,17 @@ top: expression( (-20 + (document.documentElement.clientHeight ? document.documentElement.clientHeight/2 : document.body.clientHeight/2 ) + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop )) + 'px');
}
-#fancybox-loading.fancybox-ie6 div { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_loading.png', sizingMethod='scale'); }
+#fancybox-loading.fancybox-ie6 div { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_loading.png', sizingMethod='scale'); }
/* IE6, IE7, IE8 */
.fancybox-ie .fancybox-bg { background: transparent !important; }
-.fancybox-ie #fancybox-bg-n { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_shadow_n.png', sizingMethod='scale'); }
-.fancybox-ie #fancybox-bg-ne { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_shadow_ne.png', sizingMethod='scale'); }
-.fancybox-ie #fancybox-bg-e { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_shadow_e.png', sizingMethod='scale'); }
-.fancybox-ie #fancybox-bg-se { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_shadow_se.png', sizingMethod='scale'); }
-.fancybox-ie #fancybox-bg-s { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_shadow_s.png', sizingMethod='scale'); }
-.fancybox-ie #fancybox-bg-sw { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_shadow_sw.png', sizingMethod='scale'); }
-.fancybox-ie #fancybox-bg-w { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_shadow_w.png', sizingMethod='scale'); }
-.fancybox-ie #fancybox-bg-nw { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_shadow_nw.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-n { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_shadow_n.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-ne { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_shadow_ne.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-e { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_shadow_e.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-se { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_shadow_se.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-s { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_shadow_s.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-sw { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_shadow_sw.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-w { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_shadow_w.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-nw { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_shadow_nw.png', sizingMethod='scale'); }
diff --git a/apps/files_imageviewer/img/fancybox-y.png b/apps/files_imageviewer/img/fancybox-y.png Binary files differindex 7ef399b9908..62f31761998 100644 --- a/apps/files_imageviewer/img/fancybox-y.png +++ b/apps/files_imageviewer/img/fancybox-y.png diff --git a/apps/files_imageviewer/js/lightbox.js b/apps/files_imageviewer/js/lightbox.js index 31f08456d22..ff12d808bc8 100644 --- a/apps/files_imageviewer/js/lightbox.js +++ b/apps/files_imageviewer/js/lightbox.js @@ -1,6 +1,6 @@ $(document).ready(function() { if(typeof FileActions!=='undefined'){ - FileActions.register('image','View','',function(filename){ + FileActions.register('image','View', FileActions.PERMISSION_READ, '',function(filename){ viewImage($('#dir').val(),filename); }); FileActions.setDefault('image','View'); diff --git a/apps/files_pdfviewer/appinfo/app.php b/apps/files_pdfviewer/appinfo/app.php index 06b15670674..e4771ee517f 100644 --- a/apps/files_pdfviewer/appinfo/app.php +++ b/apps/files_pdfviewer/appinfo/app.php @@ -1,7 +1,8 @@ <?php //load the required files -OCP\Util::addscript( 'files_pdfviewer', 'viewer'); OCP\Util::addStyle( 'files_pdfviewer', 'viewer'); + +OCP\Util::addscript( 'files_pdfviewer', 'pdfjs/compatibility'); +OCP\Util::addscript( 'files_pdfviewer', 'viewer'); OCP\Util::addscript( 'files_pdfviewer', 'pdfjs/build/pdf'); -OCP\Util::addscript( 'files_pdfviewer', 'pdfview'); -?> +OCP\Util::addscript( 'files_pdfviewer', 'pdfjs/viewer'); diff --git a/apps/files_pdfviewer/appinfo/info.xml b/apps/files_pdfviewer/appinfo/info.xml index e3813be1001..074962f57fb 100644 --- a/apps/files_pdfviewer/appinfo/info.xml +++ b/apps/files_pdfviewer/appinfo/info.xml @@ -4,7 +4,7 @@ <name>PDF Viewer</name> <description>Inline PDF viewer (pdfjs-based)</description> <licence>GPL</licence> - <author>Joan Creus</author> + <author>Joan Creus, Thomas Müller</author> <require>4</require> <shipped>true</shipped> <default_enable/> diff --git a/apps/files_pdfviewer/css/viewer.css b/apps/files_pdfviewer/css/viewer.css index b735dbfedfc..f9ce929d8b8 100644 --- a/apps/files_pdfviewer/css/viewer.css +++ b/apps/files_pdfviewer/css/viewer.css @@ -2,7 +2,6 @@ /* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */ #viewer { - background-color: #929292; font-family: 'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif; /*margin: 0px;*/ padding: 0px; diff --git a/apps/files_pdfviewer/js/pdfjs/build/pdf.js b/apps/files_pdfviewer/js/pdfjs/build/pdf.js index a19a9b75fea..b1fc9d9747e 100644 --- a/apps/files_pdfviewer/js/pdfjs/build/pdf.js +++ b/apps/files_pdfviewer/js/pdfjs/build/pdf.js @@ -7,10 +7,9 @@ var PDFJS = {}; // Use strict in our context only - users might not want it 'use strict'; - PDFJS.build = 'd823592'; + PDFJS.build = '2aae4fd'; // Files are inserted below - see Makefile - /* PDFJSSCRIPT_INCLUDE_ALL */ /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -20,7 +19,7 @@ var globalScope = (typeof window === 'undefined') ? this : window; var isWorker = (typeof window == 'undefined'); -var ERRORS = 0, WARNINGS = 1, TODOS = 5; +var ERRORS = 0, WARNINGS = 1, INFOS = 5; var verbosity = WARNINGS; // The global PDFJS object exposes the API @@ -44,7 +43,19 @@ function getPdf(arg, callback) { params = { url: arg }; var xhr = new XMLHttpRequest(); + xhr.open('GET', params.url); + + var headers = params.headers; + if (headers) { + for (var property in headers) { + if (typeof headers[property] === 'undefined') + continue; + + xhr.setRequestHeader(property, params.headers[property]); + } + } + xhr.mozResponseType = xhr.responseType = 'arraybuffer'; var protocol = params.url.indexOf(':') < 0 ? window.location.protocol : params.url.substring(0, params.url.indexOf(':') + 1); @@ -76,8 +87,6 @@ var Page = (function PageClosure() { function Page(xref, pageNumber, pageDict, ref) { this.pageNumber = pageNumber; this.pageDict = pageDict; - this.stats = new StatTimer(); - this.stats.enabled = !!globalScope.PDFJS.enableStats; this.xref = xref; this.ref = ref; @@ -113,18 +122,10 @@ var Page = (function PageClosure() { return shadow(this, 'mediaBox', obj); }, get view() { + var mediaBox = this.mediaBox; var cropBox = this.inheritPageProp('CropBox'); - var view = { - x: 0, - y: 0, - width: this.width, - height: this.height - }; if (!isArray(cropBox) || cropBox.length !== 4) - return shadow(this, 'view', view); - - var mediaBox = this.mediaBox; - var offsetX = mediaBox[0], offsetY = mediaBox[1]; + return shadow(this, 'view', mediaBox); // From the spec, 6th ed., p.963: // "The crop, bleed, trim, and art boxes should not ordinarily @@ -132,42 +133,13 @@ var Page = (function PageClosure() { // effectively reduced to their intersection with the media box." cropBox = Util.intersect(cropBox, mediaBox); if (!cropBox) - return shadow(this, 'view', view); - - var tl = this.rotatePoint(cropBox[0] - offsetX, cropBox[1] - offsetY); - var br = this.rotatePoint(cropBox[2] - offsetX, cropBox[3] - offsetY); - view.x = Math.min(tl.x, br.x); - view.y = Math.min(tl.y, br.y); - view.width = Math.abs(tl.x - br.x); - view.height = Math.abs(tl.y - br.y); + return shadow(this, 'view', mediaBox); - return shadow(this, 'view', view); + return shadow(this, 'view', cropBox); }, get annotations() { return shadow(this, 'annotations', this.inheritPageProp('Annots')); }, - get width() { - var mediaBox = this.mediaBox; - var rotate = this.rotate; - var width; - if (rotate == 0 || rotate == 180) { - width = (mediaBox[2] - mediaBox[0]); - } else { - width = (mediaBox[3] - mediaBox[1]); - } - return shadow(this, 'width', width); - }, - get height() { - var mediaBox = this.mediaBox; - var rotate = this.rotate; - var height; - if (rotate == 0 || rotate == 180) { - height = (mediaBox[3] - mediaBox[1]); - } else { - height = (mediaBox[2] - mediaBox[0]); - } - return shadow(this, 'height', height); - }, get rotate() { var rotate = this.inheritPageProp('Rotate') || 0; // Normalize rotation so it's a multiple of 90 and between 0 and 270 @@ -183,43 +155,20 @@ var Page = (function PageClosure() { return shadow(this, 'rotate', rotate); }, - startRenderingFromOperatorList: - function Page_startRenderingFromOperatorList(operatorList, fonts) { - var self = this; - this.operatorList = operatorList; - - var displayContinuation = function pageDisplayContinuation() { - // Always defer call to display() to work around bug in - // Firefox error reporting from XHR callbacks. - setTimeout(function pageSetTimeout() { - self.displayReadyPromise.resolve(); - }); - }; - - this.ensureFonts(fonts, - function pageStartRenderingFromOperatorListEnsureFonts() { - displayContinuation(); - } - ); - }, - getOperatorList: function Page_getOperatorList(handler, dependency) { - if (this.operatorList) { - // content was compiled - return this.operatorList; - } - - this.stats.time('Build IR Queue'); - var xref = this.xref; var content = this.content; var resources = this.resources; if (isArray(content)) { // fetching items + var streams = []; var i, n = content.length; + var streams = []; for (i = 0; i < n; ++i) - content[i] = xref.fetchIfRef(content[i]); - content = new StreamsSequenceStream(content); + streams.push(xref.fetchIfRef(content[i])); + content = new StreamsSequenceStream(streams); + } else if (isStream(content)) { + content.reset(); } else if (!content) { // replacing non-existent page content with empty one content = new Stream(new Uint8Array(0)); @@ -228,9 +177,31 @@ var Page = (function PageClosure() { var pe = this.pe = new PartialEvaluator( xref, handler, 'p' + this.pageNumber + '_'); - this.operatorList = pe.getOperatorList(content, resources, dependency); - this.stats.timeEnd('Build IR Queue'); - return this.operatorList; + return pe.getOperatorList(content, resources, dependency); + }, + extractTextContent: function Page_extractTextContent() { + var handler = { + on: function nullHandlerOn() {}, + send: function nullHandlerSend() {} + }; + + var xref = this.xref; + var content = xref.fetchIfRef(this.content); + var resources = xref.fetchIfRef(this.resources); + if (isArray(content)) { + // fetching items + var i, n = content.length; + var streams = []; + for (i = 0; i < n; ++i) + streams.push(xref.fetchIfRef(content[i])); + content = new StreamsSequenceStream(streams); + } else if (isStream(content)) { + content.reset(); + } + + var pe = new PartialEvaluator( + xref, handler, 'p' + this.pageNumber + '_'); + return pe.getTextContent(content, resources); }, ensureFonts: function Page_ensureFonts(fonts, callback) { @@ -250,60 +221,6 @@ var Page = (function PageClosure() { }.bind(this) ); }, - - display: function Page_display(gfx, callback) { - var stats = this.stats; - stats.time('Rendering'); - var xref = this.xref; - var resources = this.resources; - var mediaBox = this.mediaBox; - assertWellFormed(isDict(resources), 'invalid page resources'); - - gfx.xref = xref; - gfx.res = resources; - gfx.beginDrawing({ x: mediaBox[0], y: mediaBox[1], - width: this.width, - height: this.height, - rotate: this.rotate }); - - var startIdx = 0; - var length = this.operatorList.fnArray.length; - var operatorList = this.operatorList; - var stepper = null; - if (PDFJS.pdfBug && StepperManager.enabled) { - stepper = StepperManager.create(this.pageNumber); - stepper.init(operatorList); - stepper.nextBreakPoint = stepper.getNextBreakPoint(); - } - - var self = this; - function next() { - startIdx = - gfx.executeOperatorList(operatorList, startIdx, next, stepper); - if (startIdx == length) { - gfx.endDrawing(); - stats.timeEnd('Rendering'); - stats.timeEnd('Overall'); - if (callback) callback(); - } - } - next(); - }, - rotatePoint: function Page_rotatePoint(x, y, reverse) { - var rotate = reverse ? (360 - this.rotate) : this.rotate; - switch (rotate) { - case 180: - return {x: this.width - x, y: y}; - case 90: - return {x: this.width - y, y: this.height - x}; - case 270: - return {x: y, y: x}; - case 360: - case 0: - default: - return {x: x, y: this.height - y}; - } - }, getLinks: function Page_getLinks() { var links = []; var annotations = pageGetAnnotations(); @@ -337,6 +254,7 @@ var Page = (function PageClosure() { case 'http': case 'https': case 'ftp': + case 'mailto': return true; default: return false; @@ -355,15 +273,10 @@ var Page = (function PageClosure() { if (!isName(subtype)) continue; var rect = annotation.get('Rect'); - var topLeftCorner = this.rotatePoint(rect[0], rect[1]); - var bottomRightCorner = this.rotatePoint(rect[2], rect[3]); var item = {}; item.type = subtype.name; - item.x = Math.min(topLeftCorner.x, bottomRightCorner.x); - item.y = Math.min(topLeftCorner.y, bottomRightCorner.y); - item.width = Math.abs(topLeftCorner.x - bottomRightCorner.x); - item.height = Math.abs(topLeftCorner.y - bottomRightCorner.y); + item.rect = rect; switch (subtype.name) { case 'Link': var a = annotation.get('A'); @@ -437,7 +350,8 @@ var Page = (function PageClosure() { var title = annotation.get('T'); item.content = stringToPDFString(content || ''); item.title = stringToPDFString(title || ''); - item.name = annotation.get('Name').name; + item.name = !annotation.has('Name') ? 'Note' : + annotation.get('Name').name; break; default: TODO('unimplemented annotation type: ' + subtype.name); @@ -446,37 +360,6 @@ var Page = (function PageClosure() { items.push(item); } return items; - }, - startRendering: function Page_startRendering(ctx, callback, textLayer) { - var stats = this.stats; - stats.time('Overall'); - // If there is no displayReadyPromise yet, then the operatorList was never - // requested before. Make the request and create the promise. - if (!this.displayReadyPromise) { - this.pdf.startRendering(this); - this.displayReadyPromise = new Promise(); - } - - // Once the operatorList and fonts are loaded, do the actual rendering. - this.displayReadyPromise.then( - function pageDisplayReadyPromise() { - var gfx = new CanvasGraphics(ctx, this.objs, textLayer); - try { - this.display(gfx, callback); - } catch (e) { - if (callback) - callback(e); - else - error(e); - } - }.bind(this), - function pageDisplayReadPromiseError(reason) { - if (callback) - callback(reason); - else - error(reason); - } - ); } }; @@ -484,26 +367,26 @@ var Page = (function PageClosure() { })(); /** - * The `PDFDocModel` holds all the data of the PDF file. Compared to the + * The `PDFDocument` holds all the data of the PDF file. Compared to the * `PDFDoc`, this one doesn't have any job management code. - * Right now there exists one PDFDocModel on the main thread + one object + * Right now there exists one PDFDocument on the main thread + one object * for each worker. If there is no worker support enabled, there are two - * `PDFDocModel` objects on the main thread created. + * `PDFDocument` objects on the main thread created. */ -var PDFDocModel = (function PDFDocModelClosure() { - function PDFDocModel(arg, callback) { +var PDFDocument = (function PDFDocumentClosure() { + function PDFDocument(arg, password) { if (isStream(arg)) - init.call(this, arg); + init.call(this, arg, password); else if (isArrayBuffer(arg)) - init.call(this, new Stream(arg)); + init.call(this, new Stream(arg), password); else - error('PDFDocModel: Unknown argument type'); + error('PDFDocument: Unknown argument type'); } - function init(stream) { + function init(stream, password) { assertWellFormed(stream.length > 0, 'stream must have data'); this.stream = stream; - this.setup(); + this.setup(password); this.acroForm = this.catalog.catDict.get('AcroForm'); } @@ -523,7 +406,7 @@ var PDFDocModel = (function PDFDocModelClosure() { return true; /* found */ } - PDFDocModel.prototype = { + PDFDocument.prototype = { get linearization() { var length = this.stream.length; var linearization = false; @@ -584,7 +467,7 @@ var PDFDocModel = (function PDFDocModelClosure() { }, // Find the header, remove leading garbage and setup the stream // starting from the header. - checkHeader: function PDFDocModel_checkHeader() { + checkHeader: function PDFDocument_checkHeader() { var stream = this.stream; stream.reset(); if (find(stream, '%PDF-', 1024)) { @@ -594,11 +477,12 @@ var PDFDocModel = (function PDFDocModelClosure() { } // May not be a PDF file, continue anyway. }, - setup: function PDFDocModel_setup(ownerPassword, userPassword) { + setup: function PDFDocument_setup(password) { this.checkHeader(); var xref = new XRef(this.stream, this.startXRef, - this.mainXRefEntriesOffset); + this.mainXRefEntriesOffset, + password); this.xref = xref; this.catalog = new Catalog(xref); }, @@ -608,7 +492,7 @@ var PDFDocModel = (function PDFDocModelClosure() { // shadow the prototype getter return shadow(this, 'numPages', num); }, - getDocumentInfo: function PDFDocModel_getDocumentInfo() { + getDocumentInfo: function PDFDocument_getDocumentInfo() { var info; if (this.xref.trailer.has('Info')) { var infoDict = this.xref.trailer.get('Info'); @@ -622,7 +506,7 @@ var PDFDocModel = (function PDFDocModelClosure() { return shadow(this, 'getDocumentInfo', info); }, - getFingerprint: function PDFDocModel_getFingerprint() { + getFingerprint: function PDFDocument_getFingerprint() { var xref = this.xref, fileID; if (xref.trailer.has('ID')) { fileID = ''; @@ -643,259 +527,22 @@ var PDFDocModel = (function PDFDocModelClosure() { return shadow(this, 'getFingerprint', fileID); }, - getPage: function PDFDocModel_getPage(n) { + getPage: function PDFDocument_getPage(n) { return this.catalog.getPage(n); } }; - return PDFDocModel; + return PDFDocument; })(); -var PDFDoc = (function PDFDocClosure() { - function PDFDoc(arg, callback) { - var stream = null; - var data = null; - - if (isStream(arg)) { - stream = arg; - data = arg.bytes; - } else if (isArrayBuffer(arg)) { - stream = new Stream(arg); - data = arg; - } else { - error('PDFDoc: Unknown argument type'); - } - - this.data = data; - this.stream = stream; - this.pdfModel = new PDFDocModel(stream); - this.fingerprint = this.pdfModel.getFingerprint(); - this.info = this.pdfModel.getDocumentInfo(); - this.catalog = this.pdfModel.catalog; - this.objs = new PDFObjects(); - - this.pageCache = []; - this.fontsLoading = {}; - this.workerReadyPromise = new Promise('workerReady'); - - // If worker support isn't disabled explicit and the browser has worker - // support, create a new web worker and test if it/the browser fullfills - // all requirements to run parts of pdf.js in a web worker. - // Right now, the requirement is, that an Uint8Array is still an Uint8Array - // as it arrives on the worker. Chrome added this with version 15. - if (!globalScope.PDFJS.disableWorker && typeof Worker !== 'undefined') { - var workerSrc = PDFJS.workerSrc; - if (typeof workerSrc === 'undefined') { - error('No PDFJS.workerSrc specified'); - } - - try { - var worker; - if (PDFJS.isFirefoxExtension) { - // The firefox extension can't load the worker from the resource:// - // url so we have to inline the script and then use the blob loader. - var bb = new MozBlobBuilder(); - bb.append(document.querySelector('#PDFJS_SCRIPT_TAG').textContent); - var blobUrl = window.URL.createObjectURL(bb.getBlob()); - worker = new Worker(blobUrl); - } else { - // Some versions of FF can't create a worker on localhost, see: - // https://bugzilla.mozilla.org/show_bug.cgi?id=683280 - worker = new Worker(workerSrc); - } - - var messageHandler = new MessageHandler('main', worker); - - messageHandler.on('test', function pdfDocTest(supportTypedArray) { - if (supportTypedArray) { - this.worker = worker; - this.setupMessageHandler(messageHandler); - } else { - globalScope.PDFJS.disableWorker = true; - this.setupFakeWorker(); - } - }.bind(this)); - - var testObj = new Uint8Array(1); - // Some versions of Opera throw a DATA_CLONE_ERR on - // serializing the typed array. - messageHandler.send('test', testObj); - return; - } catch (e) { - warn('The worker has been disabled.'); - } - } - // Either workers are disabled, not supported or have thrown an exception. - // Thus, we fallback to a faked worker. - globalScope.PDFJS.disableWorker = true; - this.setupFakeWorker(); - } - - PDFDoc.prototype = { - setupFakeWorker: function PDFDoc_setupFakeWorker() { - // If we don't use a worker, just post/sendMessage to the main thread. - var fakeWorker = { - postMessage: function PDFDoc_postMessage(obj) { - fakeWorker.onmessage({data: obj}); - }, - terminate: function PDFDoc_terminate() {} - }; - - var messageHandler = new MessageHandler('main', fakeWorker); - this.setupMessageHandler(messageHandler); - - // If the main thread is our worker, setup the handling for the messages - // the main thread sends to it self. - WorkerMessageHandler.setup(messageHandler); - }, - - - setupMessageHandler: function PDFDoc_setupMessageHandler(messageHandler) { - this.messageHandler = messageHandler; - - messageHandler.on('page', function pdfDocPage(data) { - var pageNum = data.pageNum; - var page = this.pageCache[pageNum]; - var depFonts = data.depFonts; - - page.stats.timeEnd('Page Request'); - page.startRenderingFromOperatorList(data.operatorList, depFonts); - }, this); - - messageHandler.on('obj', function pdfDocObj(data) { - var id = data[0]; - var type = data[1]; - - switch (type) { - case 'JpegStream': - var imageData = data[2]; - loadJpegStream(id, imageData, this.objs); - break; - case 'Image': - var imageData = data[2]; - this.objs.resolve(id, imageData); - break; - case 'Font': - var name = data[2]; - var file = data[3]; - var properties = data[4]; - - if (file) { - // Rewrap the ArrayBuffer in a stream. - var fontFileDict = new Dict(); - file = new Stream(file, 0, file.length, fontFileDict); - } - - // At this point, only the font object is created but the font is - // not yet attached to the DOM. This is done in `FontLoader.bind`. - var font = new Font(name, file, properties); - this.objs.resolve(id, font); - break; - default: - error('Got unkown object type ' + type); - } - }, this); - - messageHandler.on('page_error', function pdfDocError(data) { - var page = this.pageCache[data.pageNum]; - if (page.displayReadyPromise) - page.displayReadyPromise.reject(data.error); - else - error(data.error); - }, this); - - messageHandler.on('jpeg_decode', function(data, promise) { - var imageData = data[0]; - var components = data[1]; - if (components != 3 && components != 1) - error('Only 3 component or 1 component can be returned'); - - var img = new Image(); - img.onload = (function messageHandler_onloadClosure() { - var width = img.width; - var height = img.height; - var size = width * height; - var rgbaLength = size * 4; - var buf = new Uint8Array(size * components); - var tmpCanvas = createScratchCanvas(width, height); - var tmpCtx = tmpCanvas.getContext('2d'); - tmpCtx.drawImage(img, 0, 0); - var data = tmpCtx.getImageData(0, 0, width, height).data; - - if (components == 3) { - for (var i = 0, j = 0; i < rgbaLength; i += 4, j += 3) { - buf[j] = data[i]; - buf[j + 1] = data[i + 1]; - buf[j + 2] = data[i + 2]; - } - } else if (components == 1) { - for (var i = 0, j = 0; i < rgbaLength; i += 4, j++) { - buf[j] = data[i]; - } - } - promise.resolve({ data: buf, width: width, height: height}); - }).bind(this); - var src = 'data:image/jpeg;base64,' + window.btoa(imageData); - img.src = src; - }); - - setTimeout(function pdfDocFontReadySetTimeout() { - messageHandler.send('doc', this.data); - this.workerReadyPromise.resolve(true); - }.bind(this)); - }, - - get numPages() { - return this.pdfModel.numPages; - }, - - startRendering: function PDFDoc_startRendering(page) { - // The worker might not be ready to receive the page request yet. - this.workerReadyPromise.then(function pdfDocStartRenderingThen() { - page.stats.time('Page Request'); - this.messageHandler.send('page_request', page.pageNumber + 1); - }.bind(this)); - }, - - getPage: function PDFDoc_getPage(n) { - if (this.pageCache[n]) - return this.pageCache[n]; - - var page = this.pdfModel.getPage(n); - // Add a reference to the objects such that Page can forward the reference - // to the CanvasGraphics and so on. - page.objs = this.objs; - page.pdf = this; - return (this.pageCache[n] = page); - }, - - destroy: function PDFDoc_destroy() { - if (this.worker) - this.worker.terminate(); - - if (this.fontWorker) - this.fontWorker.terminate(); - - for (var n in this.pageCache) - delete this.pageCache[n]; - - delete this.data; - delete this.stream; - delete this.pdf; - delete this.catalog; - } - }; - - return PDFDoc; -})(); - -globalScope.PDFJS.PDFDoc = PDFDoc; /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ 'use strict'; +// Use only for debugging purposes. This should not be used in any code that is +// in mozilla master. function log(msg) { if (console && console.log) console.log(msg); @@ -903,32 +550,44 @@ function log(msg) { print(msg); } -function warn(msg) { - if (verbosity >= WARNINGS) - log('Warning: ' + msg); +// A notice for devs that will not trigger the fallback UI. These are good +// for things that are helpful to devs, such as warning that Workers were +// disabled, which is important to devs but not end users. +function info(msg) { + if (verbosity >= INFOS) { + log('Info: ' + msg); + PDFJS.LogManager.notify('info', msg); + } } -function backtrace() { - try { - throw new Error(); - } catch (e) { - return e.stack ? e.stack.split('\n').slice(2).join('\n') : ''; +// Non-fatal warnings that should trigger the fallback UI. +function warn(msg) { + if (verbosity >= WARNINGS) { + log('Warning: ' + msg); + PDFJS.LogManager.notify('warn', msg); } } +// Fatal errors that should trigger the fallback UI and halt execution by +// throwing an exception. function error(msg) { log('Error: ' + msg); log(backtrace()); + PDFJS.LogManager.notify('error', msg); throw new Error(msg); } +// Missing features that should trigger the fallback UI. function TODO(what) { - if (verbosity >= TODOS) - log('TODO: ' + what); + warn('TODO: ' + what); } -function malformed(msg) { - error('Malformed PDF: ' + msg); +function backtrace() { + try { + throw new Error(); + } catch (e) { + return e.stack ? e.stack.split('\n').slice(2).join('\n') : ''; + } } function assert(cond, msg) { @@ -940,9 +599,25 @@ function assert(cond, msg) { // behavior is undefined. function assertWellFormed(cond, msg) { if (!cond) - malformed(msg); + error(msg); } +var LogManager = PDFJS.LogManager = (function LogManagerClosure() { + var loggers = []; + return { + addLogger: function logManager_addLogger(logger) { + loggers.push(logger); + }, + notify: function(type, message) { + for (var i = 0, ii = loggers.length; i < ii; i++) { + var logger = loggers[i]; + if (logger[type]) + logger[type](message); + } + } + }; +})(); + function shadow(obj, prop, value) { Object.defineProperty(obj, prop, { value: value, enumerable: true, @@ -951,6 +626,19 @@ function shadow(obj, prop, value) { return value; } +var PasswordException = (function PasswordExceptionClosure() { + function PasswordException(msg, code) { + this.name = 'PasswordException'; + this.message = msg; + this.code = code; + } + + PasswordException.prototype = new Error(); + PasswordException.constructor = PasswordException; + + return PasswordException; +})(); + function bytesToString(bytes) { var str = ''; var length = bytes.length; @@ -969,7 +657,7 @@ function stringToBytes(str) { var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; -var Util = (function UtilClosure() { +var Util = PDFJS.Util = (function UtilClosure() { function Util() {} Util.makeCssRgb = function Util_makeCssRgb(r, g, b) { @@ -990,6 +678,19 @@ var Util = (function UtilClosure() { return [xt, yt]; }; + Util.applyInverseTransform = function Util_applyInverseTransform(p, m) { + var d = m[0] * m[3] - m[1] * m[2]; + var xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d; + var yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d; + return [xt, yt]; + }; + + Util.inverseTransform = function Util_inverseTransform(m) { + var d = m[0] * m[3] - m[1] * m[2]; + return [m[3] / d, -m[1] / d, -m[2] / d, m[0] / d, + (m[2] * m[5] - m[4] * m[3]) / d, (m[4] * m[1] - m[5] * m[0]) / d]; + }; + // Apply a generic 3d matrix M on a 3-vector v: // | a b c | | X | // | d e f | x | Y | @@ -1058,7 +759,7 @@ var Util = (function UtilClosure() { } return result; - } + }; Util.sign = function Util_sign(num) { return num < 0 ? -1 : 1; @@ -1067,6 +768,80 @@ var Util = (function UtilClosure() { return Util; })(); +var PageViewport = PDFJS.PageViewport = (function PageViewportClosure() { + function PageViewport(viewBox, scale, rotate, offsetX, offsetY) { + // creating transform to convert pdf coordinate system to the normal + // canvas like coordinates taking in account scale and rotation + var centerX = (viewBox[2] + viewBox[0]) / 2; + var centerY = (viewBox[3] + viewBox[1]) / 2; + var rotateA, rotateB, rotateC, rotateD; + switch (rotate) { + case -180: + case 180: + rotateA = -1; rotateB = 0; rotateC = 0; rotateD = 1; + break; + case -270: + case 90: + rotateA = 0; rotateB = 1; rotateC = 1; rotateD = 0; + break; + case -90: + case 270: + rotateA = 0; rotateB = -1; rotateC = -1; rotateD = 0; + break; + case 360: + case 0: + default: + rotateA = 1; rotateB = 0; rotateC = 0; rotateD = -1; + break; + } + var offsetCanvasX, offsetCanvasY; + var width, height; + if (rotateA == 0) { + offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX; + offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY; + width = Math.abs(viewBox[3] - viewBox[1]) * scale; + height = Math.abs(viewBox[2] - viewBox[0]) * scale; + } else { + offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX; + offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY; + width = Math.abs(viewBox[2] - viewBox[0]) * scale; + height = Math.abs(viewBox[3] - viewBox[1]) * scale; + } + // creating transform for the following operations: + // translate(-centerX, -centerY), rotate and flip vertically, + // scale, and translate(offsetCanvasX, offsetCanvasY) + this.transform = [ + rotateA * scale, + rotateB * scale, + rotateC * scale, + rotateD * scale, + offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY, + offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY + ]; + + this.offsetX = offsetX; + this.offsetY = offsetY; + this.width = width; + this.height = height; + this.fontScale = scale; + } + PageViewport.prototype = { + convertToViewportPoint: function PageViewport_convertToViewportPoint(x, y) { + return Util.applyTransform([x, y], this.transform); + }, + convertToViewportRectangle: + function PageViewport_convertToViewportRectangle(rect) { + var tl = Util.applyTransform([rect[0], rect[1]], this.transform); + var br = Util.applyTransform([rect[2], rect[3]], this.transform); + return [tl[0], tl[1], br[0], br[1]]; + }, + convertToPdfPoint: function PageViewport_convertToPdfPoint(x, y) { + return Util.applyInverseTransform([x, y], this.transform); + } + }; + return PageViewport; +})(); + var PDFStringTranslateTable = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2D8, 0x2C7, 0x2C6, 0x2D9, 0x2DD, 0x2DB, 0x2DA, 0x2DC, 0, 0, 0, 0, 0, 0, 0, @@ -1095,6 +870,10 @@ function stringToPDFString(str) { return str2; } +function stringToUTF8String(str) { + return decodeURIComponent(escape(str)); +} + function isBool(v) { return typeof v == 'boolean'; } @@ -1168,7 +947,7 @@ function isPDFFunction(v) { * can be set. If any of these happens twice or the data is required before * it was set, an exception is throw. */ -var Promise = (function PromiseClosure() { +var Promise = PDFJS.Promise = (function PromiseClosure() { var EMPTY_PROMISE = {}; /** @@ -1190,6 +969,7 @@ var Promise = (function PromiseClosure() { } this.callbacks = []; this.errbacks = []; + this.progressbacks = []; }; /** * Builds a promise that is resolved when all the passed in promises are @@ -1205,7 +985,7 @@ var Promise = (function PromiseClosure() { deferred.resolve(results); return deferred; } - for (var i = 0; i < unresolved; ++i) { + for (var i = 0, ii = promises.length; i < ii; ++i) { var promise = promises[i]; promise.then((function(i) { return function(value) { @@ -1261,7 +1041,7 @@ var Promise = (function PromiseClosure() { } this.isResolved = true; - this.data = data || null; + this.data = (typeof data !== 'undefined') ? data : null; var callbacks = this.callbacks; for (var i = 0, ii = callbacks.length; i < ii; i++) { @@ -1269,7 +1049,14 @@ var Promise = (function PromiseClosure() { } }, - reject: function Promise_reject(reason) { + progress: function Promise_progress(data) { + var callbacks = this.progressbacks; + for (var i = 0, ii = callbacks.length; i < ii; i++) { + callbacks[i].call(null, data); + } + }, + + reject: function Promise_reject(reason, exception) { if (this.isRejected) { error('A Promise can be rejected only once ' + this.name); } @@ -1282,11 +1069,11 @@ var Promise = (function PromiseClosure() { var errbacks = this.errbacks; for (var i = 0, ii = errbacks.length; i < ii; i++) { - errbacks[i].call(null, reason); + errbacks[i].call(null, reason, exception); } }, - then: function Promise_then(callback, errback) { + then: function Promise_then(callback, errback, progressback) { if (!callback) { error('Requiring callback' + this.name); } @@ -1303,6 +1090,9 @@ var Promise = (function PromiseClosure() { if (errback) this.errbacks.push(errback); } + + if (progressback) + this.progressbacks.push(progressback); } }; @@ -1361,6 +1151,659 @@ var StatTimer = (function StatTimerClosure() { }; return StatTimer; })(); + +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ + +/** + * This is the main entry point for loading a PDF and interacting with it. + * NOTE: If a URL is used to fetch the PDF data a standard XMLHttpRequest(XHR) + * is used, which means it must follow the same origin rules that any XHR does + * e.g. No cross domain requests without CORS. + * + * @param {string|TypedAray|object} source Can be an url to where a PDF is + * located, a typed array (Uint8Array) already populated with data or + * and parameter object with the following possible fields: + * - url - The URL of the PDF. + * - data - A typed array with PDF data. + * - httpHeaders - Basic authentication headers. + * - password - For decrypting password-protected PDFs. + * + * @return {Promise} A promise that is resolved with {PDFDocumentProxy} object. + */ +PDFJS.getDocument = function getDocument(source) { + var url, data, headers, password, parameters = {}; + if (typeof source === 'string') { + url = source; + } else if (isArrayBuffer(source)) { + data = source; + } else if (typeof source === 'object') { + url = source.url; + data = source.data; + headers = source.httpHeaders; + password = source.password; + parameters.password = password || null; + + if (!url && !data) + error('Invalid parameter array, need either .data or .url'); + } else { + error('Invalid parameter in getDocument, need either Uint8Array, ' + + 'string or a parameter object'); + } + + var promise = new PDFJS.Promise(); + var transport = new WorkerTransport(promise); + if (data) { + // assuming the data is array, instantiating directly from it + transport.sendData(data, parameters); + } else if (url) { + // fetch url + PDFJS.getPdf( + { + url: url, + progress: function getPDFProgress(evt) { + if (evt.lengthComputable) + promise.progress({ + loaded: evt.loaded, + total: evt.total + }); + }, + error: function getPDFError(e) { + promise.reject('Unexpected server response of ' + + e.target.status + '.'); + }, + headers: headers + }, + function getPDFLoad(data) { + transport.sendData(data, parameters); + }); + } + + return promise; +}; + +/** + * Proxy to a PDFDocument in the worker thread. Also, contains commonly used + * properties that can be read synchronously. + */ +var PDFDocumentProxy = (function PDFDocumentProxyClosure() { + function PDFDocumentProxy(pdfInfo, transport) { + this.pdfInfo = pdfInfo; + this.transport = transport; + } + PDFDocumentProxy.prototype = { + /** + * @return {number} Total number of pages the PDF contains. + */ + get numPages() { + return this.pdfInfo.numPages; + }, + /** + * @return {string} A unique ID to identify a PDF. Not guaranteed to be + * unique. + */ + get fingerprint() { + return this.pdfInfo.fingerprint; + }, + /** + * @param {number} The page number to get. The first page is 1. + * @return {Promise} A promise that is resolved with a {PDFPageProxy} + * object. + */ + getPage: function PDFDocumentProxy_getPage(number) { + return this.transport.getPage(number); + }, + /** + * @return {Promise} A promise that is resolved with a lookup table for + * mapping named destinations to reference numbers. + */ + getDestinations: function PDFDocumentProxy_getDestinations() { + var promise = new PDFJS.Promise(); + var destinations = this.pdfInfo.destinations; + promise.resolve(destinations); + return promise; + }, + /** + * @return {Promise} A promise that is resolved with an {array} that is a + * tree outline (if it has one) of the PDF. The tree is in the format of: + * [ + * { + * title: string, + * bold: boolean, + * italic: boolean, + * color: rgb array, + * dest: dest obj, + * items: array of more items like this + * }, + * ... + * ]. + */ + getOutline: function PDFDocumentProxy_getOutline() { + var promise = new PDFJS.Promise(); + var outline = this.pdfInfo.outline; + promise.resolve(outline); + return promise; + }, + /** + * @return {Promise} A promise that is resolved with an {object} that has + * info and metadata properties. Info is an {object} filled with anything + * available in the information dictionary and similarly metadata is a + * {Metadata} object with information from the metadata section of the PDF. + */ + getMetadata: function PDFDocumentProxy_getMetadata() { + var promise = new PDFJS.Promise(); + var info = this.pdfInfo.info; + var metadata = this.pdfInfo.metadata; + promise.resolve({ + info: info, + metadata: metadata ? new PDFJS.Metadata(metadata) : null + }); + return promise; + }, + isEncrypted: function PDFDocumentProxy_isEncrypted() { + var promise = new PDFJS.Promise(); + promise.resolve(this.pdfInfo.encrypted); + return promise; + }, + /** + * @return {Promise} A promise that is resolved with a TypedArray that has + * the raw data from the PDF. + */ + getData: function PDFDocumentProxy_getData() { + var promise = new PDFJS.Promise(); + this.transport.getData(promise); + return promise; + }, + destroy: function PDFDocumentProxy_destroy() { + this.transport.destroy(); + } + }; + return PDFDocumentProxy; +})(); + +var PDFPageProxy = (function PDFPageProxyClosure() { + function PDFPageProxy(pageInfo, transport) { + this.pageInfo = pageInfo; + this.transport = transport; + this.stats = new StatTimer(); + this.stats.enabled = !!globalScope.PDFJS.enableStats; + this.objs = transport.objs; + this.renderInProgress = false; + } + PDFPageProxy.prototype = { + /** + * @return {number} Page number of the page. First page is 1. + */ + get pageNumber() { + return this.pageInfo.pageIndex + 1; + }, + /** + * @return {number} The number of degrees the page is rotated clockwise. + */ + get rotate() { + return this.pageInfo.rotate; + }, + /** + * @return {object} The reference that points to this page. It has 'num' and + * 'gen' properties. + */ + get ref() { + return this.pageInfo.ref; + }, + /** + * @return {array} An array of the visible portion of the PDF page in the + * user space units - [x1, y1, x2, y2]. + */ + get view() { + return this.pageInfo.view; + }, + /** + * @param {number} scale The desired scale of the viewport. + * @param {number} rotate Degrees to rotate the viewport. If omitted this + * defaults to the page rotation. + * @return {PageViewport} Contains 'width' and 'height' properties along + * with transforms required for rendering. + */ + getViewport: function PDFPageProxy_getViewport(scale, rotate) { + if (arguments.length < 2) + rotate = this.rotate; + return new PDFJS.PageViewport(this.view, scale, rotate, 0, 0); + }, + /** + * @return {Promise} A promise that is resolved with an {array} of the + * annotation objects. + */ + getAnnotations: function PDFPageProxy_getAnnotations() { + if (this.annotationsPromise) + return this.annotationsPromise; + + var promise = new PDFJS.Promise(); + this.annotationsPromise = promise; + this.transport.getAnnotations(this.pageInfo.pageIndex); + return promise; + }, + /** + * Begins the process of rendering a page to the desired context. + * @param {object} params A parameter object that supports: + * { + * canvasContext(required): A 2D context of a DOM Canvas object., + * textLayer(optional): An object that has beginLayout, endLayout, and + * appendText functions. + * }. + * @return {Promise} A promise that is resolved when the page finishes + * rendering. + */ + render: function PDFPageProxy_render(params) { + this.renderInProgress = true; + + var promise = new Promise(); + var stats = this.stats; + stats.time('Overall'); + // If there is no displayReadyPromise yet, then the operatorList was never + // requested before. Make the request and create the promise. + if (!this.displayReadyPromise) { + this.displayReadyPromise = new Promise(); + this.destroyed = false; + + this.stats.time('Page Request'); + this.transport.messageHandler.send('RenderPageRequest', { + pageIndex: this.pageNumber - 1 + }); + } + + var self = this; + function complete(error) { + self.renderInProgress = false; + if (self.destroyed) { + delete self.operatorList; + delete self.displayReadyPromise; + } + + if (error) + promise.reject(error); + else + promise.resolve(); + }; + + // Once the operatorList and fonts are loaded, do the actual rendering. + this.displayReadyPromise.then( + function pageDisplayReadyPromise() { + if (self.destroyed) { + complete(); + return; + } + + var gfx = new CanvasGraphics(params.canvasContext, + this.objs, params.textLayer); + try { + this.display(gfx, params.viewport, complete); + } catch (e) { + complete(e); + } + }.bind(this), + function pageDisplayReadPromiseError(reason) { + complete(reason); + } + ); + + return promise; + }, + /** + * For internal use only. + */ + startRenderingFromOperatorList: + function PDFPageProxy_startRenderingFromOperatorList(operatorList, + fonts) { + var self = this; + this.operatorList = operatorList; + + var displayContinuation = function pageDisplayContinuation() { + // Always defer call to display() to work around bug in + // Firefox error reporting from XHR callbacks. + setTimeout(function pageSetTimeout() { + self.displayReadyPromise.resolve(); + }); + }; + + this.ensureFonts(fonts, + function pageStartRenderingFromOperatorListEnsureFonts() { + displayContinuation(); + } + ); + }, + /** + * For internal use only. + */ + ensureFonts: function PDFPageProxy_ensureFonts(fonts, callback) { + this.stats.time('Font Loading'); + // Convert the font names to the corresponding font obj. + for (var i = 0, ii = fonts.length; i < ii; i++) { + fonts[i] = this.objs.objs[fonts[i]].data; + } + + // Load all the fonts + FontLoader.bind( + fonts, + function pageEnsureFontsFontObjs(fontObjs) { + this.stats.timeEnd('Font Loading'); + + callback.call(this); + }.bind(this) + ); + }, + /** + * For internal use only. + */ + display: function PDFPageProxy_display(gfx, viewport, callback) { + var stats = this.stats; + stats.time('Rendering'); + + gfx.beginDrawing(viewport); + + var startIdx = 0; + var length = this.operatorList.fnArray.length; + var operatorList = this.operatorList; + var stepper = null; + if (PDFJS.pdfBug && StepperManager.enabled) { + stepper = StepperManager.create(this.pageNumber - 1); + stepper.init(operatorList); + stepper.nextBreakPoint = stepper.getNextBreakPoint(); + } + + var self = this; + function next() { + startIdx = + gfx.executeOperatorList(operatorList, startIdx, next, stepper); + if (startIdx == length) { + gfx.endDrawing(); + stats.timeEnd('Rendering'); + stats.timeEnd('Overall'); + if (callback) callback(); + } + } + next(); + }, + /** + * @return {Promise} That is resolved with the a {string} that is the text + * content from the page. + */ + getTextContent: function PDFPageProxy_getTextContent() { + var promise = new PDFJS.Promise(); + this.transport.messageHandler.send('GetTextContent', { + pageIndex: this.pageNumber - 1 + }, + function textContentCallback(textContent) { + promise.resolve(textContent); + } + ); + return promise; + }, + /** + * Stub for future feature. + */ + getOperationList: function PDFPageProxy_getOperationList() { + var promise = new PDFJS.Promise(); + var operationList = { // not implemented + dependencyFontsID: null, + operatorList: null + }; + promise.resolve(operationList); + return promise; + }, + /** + * Destroys resources allocated by the page. + */ + destroy: function PDFPageProxy_destroy() { + this.destroyed = true; + + if (!this.renderInProgress) { + delete this.operatorList; + delete this.displayReadyPromise; + } + } + }; + return PDFPageProxy; +})(); +/** + * For internal use only. + */ +var WorkerTransport = (function WorkerTransportClosure() { + function WorkerTransport(promise) { + this.workerReadyPromise = promise; + this.objs = new PDFObjects(); + + this.pageCache = []; + this.pagePromises = []; + this.fontsLoading = {}; + + // If worker support isn't disabled explicit and the browser has worker + // support, create a new web worker and test if it/the browser fullfills + // all requirements to run parts of pdf.js in a web worker. + // Right now, the requirement is, that an Uint8Array is still an Uint8Array + // as it arrives on the worker. Chrome added this with version 15. + if (!globalScope.PDFJS.disableWorker && typeof Worker !== 'undefined') { + var workerSrc = PDFJS.workerSrc; + if (typeof workerSrc === 'undefined') { + error('No PDFJS.workerSrc specified'); + } + + try { + var worker; + if (PDFJS.isFirefoxExtension) { + // The firefox extension can't load the worker from the resource:// + // url so we have to inline the script and then use the blob loader. + var bb = new MozBlobBuilder(); + bb.append(document.querySelector('#PDFJS_SCRIPT_TAG').textContent); + var blobUrl = window.URL.createObjectURL(bb.getBlob()); + worker = new Worker(blobUrl); + } else { + // Some versions of FF can't create a worker on localhost, see: + // https://bugzilla.mozilla.org/show_bug.cgi?id=683280 + worker = new Worker(workerSrc); + } + + var messageHandler = new MessageHandler('main', worker); + this.messageHandler = messageHandler; + + messageHandler.on('test', function transportTest(supportTypedArray) { + if (supportTypedArray) { + this.worker = worker; + this.setupMessageHandler(messageHandler); + } else { + globalScope.PDFJS.disableWorker = true; + this.setupFakeWorker(); + } + }.bind(this)); + + var testObj = new Uint8Array(1); + // Some versions of Opera throw a DATA_CLONE_ERR on + // serializing the typed array. + messageHandler.send('test', testObj); + return; + } catch (e) { + info('The worker has been disabled.'); + } + } + // Either workers are disabled, not supported or have thrown an exception. + // Thus, we fallback to a faked worker. + globalScope.PDFJS.disableWorker = true; + this.setupFakeWorker(); + } + WorkerTransport.prototype = { + destroy: function WorkerTransport_destroy() { + if (this.worker) + this.worker.terminate(); + + this.pageCache = []; + this.pagePromises = []; + }, + setupFakeWorker: function WorkerTransport_setupFakeWorker() { + // If we don't use a worker, just post/sendMessage to the main thread. + var fakeWorker = { + postMessage: function WorkerTransport_postMessage(obj) { + fakeWorker.onmessage({data: obj}); + }, + terminate: function WorkerTransport_terminate() {} + }; + + var messageHandler = new MessageHandler('main', fakeWorker); + this.setupMessageHandler(messageHandler); + + // If the main thread is our worker, setup the handling for the messages + // the main thread sends to it self. + WorkerMessageHandler.setup(messageHandler); + }, + + setupMessageHandler: + function WorkerTransport_setupMessageHandler(messageHandler) { + this.messageHandler = messageHandler; + + messageHandler.on('GetDoc', function transportDoc(data) { + var pdfInfo = data.pdfInfo; + var pdfDocument = new PDFDocumentProxy(pdfInfo, this); + this.pdfDocument = pdfDocument; + this.workerReadyPromise.resolve(pdfDocument); + }, this); + + messageHandler.on('NeedPassword', function transportPassword(data) { + this.workerReadyPromise.reject(data.exception.message, data.exception); + }, this); + + messageHandler.on('IncorrectPassword', function transportBadPass(data) { + this.workerReadyPromise.reject(data.exception.message, data.exception); + }, this); + + messageHandler.on('GetPage', function transportPage(data) { + var pageInfo = data.pageInfo; + var page = new PDFPageProxy(pageInfo, this); + this.pageCache[pageInfo.pageIndex] = page; + var promise = this.pagePromises[pageInfo.pageIndex]; + promise.resolve(page); + }, this); + + messageHandler.on('GetAnnotations', function transportAnnotations(data) { + var annotations = data.annotations; + var promise = this.pageCache[data.pageIndex].annotationsPromise; + promise.resolve(annotations); + }, this); + + messageHandler.on('RenderPage', function transportRender(data) { + var page = this.pageCache[data.pageIndex]; + var depFonts = data.depFonts; + + page.stats.timeEnd('Page Request'); + page.startRenderingFromOperatorList(data.operatorList, depFonts); + }, this); + + messageHandler.on('obj', function transportObj(data) { + var id = data[0]; + var type = data[1]; + if (this.objs.hasData(id)) + return; + + switch (type) { + case 'JpegStream': + var imageData = data[2]; + loadJpegStream(id, imageData, this.objs); + break; + case 'Image': + var imageData = data[2]; + this.objs.resolve(id, imageData); + break; + case 'Font': + var name = data[2]; + var file = data[3]; + var properties = data[4]; + + if (file) { + // Rewrap the ArrayBuffer in a stream. + var fontFileDict = new Dict(); + file = new Stream(file, 0, file.length, fontFileDict); + } + + // At this point, only the font object is created but the font is + // not yet attached to the DOM. This is done in `FontLoader.bind`. + var font = new Font(name, file, properties); + this.objs.resolve(id, font); + break; + default: + error('Got unkown object type ' + type); + } + }, this); + + messageHandler.on('PageError', function transportError(data) { + var page = this.pageCache[data.pageNum - 1]; + if (page.displayReadyPromise) + page.displayReadyPromise.reject(data.error); + else + error(data.error); + }, this); + + messageHandler.on('JpegDecode', function(data, promise) { + var imageData = data[0]; + var components = data[1]; + if (components != 3 && components != 1) + error('Only 3 component or 1 component can be returned'); + + var img = new Image(); + img.onload = (function messageHandler_onloadClosure() { + var width = img.width; + var height = img.height; + var size = width * height; + var rgbaLength = size * 4; + var buf = new Uint8Array(size * components); + var tmpCanvas = createScratchCanvas(width, height); + var tmpCtx = tmpCanvas.getContext('2d'); + tmpCtx.drawImage(img, 0, 0); + var data = tmpCtx.getImageData(0, 0, width, height).data; + + if (components == 3) { + for (var i = 0, j = 0; i < rgbaLength; i += 4, j += 3) { + buf[j] = data[i]; + buf[j + 1] = data[i + 1]; + buf[j + 2] = data[i + 2]; + } + } else if (components == 1) { + for (var i = 0, j = 0; i < rgbaLength; i += 4, j++) { + buf[j] = data[i]; + } + } + promise.resolve({ data: buf, width: width, height: height}); + }).bind(this); + var src = 'data:image/jpeg;base64,' + window.btoa(imageData); + img.src = src; + }); + }, + + sendData: function WorkerTransport_sendData(data, params) { + this.messageHandler.send('GetDocRequest', {data: data, params: params}); + }, + + getData: function WorkerTransport_sendData(promise) { + this.messageHandler.send('GetData', null, function(data) { + promise.resolve(data); + }); + }, + + getPage: function WorkerTransport_getPage(pageNumber, promise) { + var pageIndex = pageNumber - 1; + if (pageIndex in this.pagePromises) + return this.pagePromises[pageIndex]; + var promise = new PDFJS.Promise('Page ' + pageNumber); + this.pagePromises[pageIndex] = promise; + this.messageHandler.send('GetPageRequest', { pageIndex: pageIndex }); + return promise; + }, + + getAnnotations: function WorkerTransport_getAnnotations(pageIndex) { + this.messageHandler.send('GetAnnotationsRequest', + { pageIndex: pageIndex }); + } + }; + return WorkerTransport; + +})(); + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -1604,27 +2047,10 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { 'shadingFill': true }, - beginDrawing: function CanvasGraphics_beginDrawing(mediaBox) { - var cw = this.ctx.canvas.width, ch = this.ctx.canvas.height; + beginDrawing: function CanvasGraphics_beginDrawing(viewport) { + var transform = viewport.transform; this.ctx.save(); - switch (mediaBox.rotate) { - case 0: - this.ctx.transform(1, 0, 0, -1, 0, ch); - break; - case 90: - this.ctx.transform(0, 1, 1, 0, 0, 0); - break; - case 180: - this.ctx.transform(-1, 0, 0, 1, cw, 0); - break; - case 270: - this.ctx.transform(0, -1, -1, 0, cw, ch); - break; - } - // Scale so that canvas units are the same as PDF user space units - this.ctx.scale(cw / mediaBox.width, ch / mediaBox.height); - // Move the media left-top corner to the (0,0) canvas position - this.ctx.translate(-mediaBox.x, -mediaBox.y); + this.ctx.transform.apply(this.ctx, transform); if (this.textLayer) this.textLayer.beginLayout(); @@ -1723,10 +2149,13 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { this.ctx.webkitLineDashOffset = dashPhase; }, setRenderingIntent: function CanvasGraphics_setRenderingIntent(intent) { - TODO('set rendering intent: ' + intent); + // Maybe if we one day fully support color spaces this will be important + // for now we can ignore. + // TODO set rendering intent? }, setFlatness: function CanvasGraphics_setFlatness(flatness) { - TODO('set flatness: ' + flatness); + // There's no way to control this with canvas, but we can safely ignore. + // TODO set flatness? }, setGState: function CanvasGraphics_setGState(states) { for (var i = 0, ii = states.length; i < ii; i++) { @@ -2221,7 +2650,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { text.length += shownText.length; } } else { - malformed('TJ array element ' + e + ' is not string or num'); + error('TJ array element ' + e + ' is not string or num'); } } @@ -2474,6 +2903,40 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { } } } + function rescaleImage(pixels, widthScale, heightScale) { + var scaledWidth = Math.ceil(width / widthScale); + var scaledHeight = Math.ceil(height / heightScale); + + var itemsSum = new Uint32Array(scaledWidth * scaledHeight * 4); + var itemsCount = new Uint32Array(scaledWidth * scaledHeight); + for (var i = 0, position = 0; i < height; i++) { + var lineOffset = (0 | (i / heightScale)) * scaledWidth; + for (var j = 0; j < width; j++) { + var countOffset = lineOffset + (0 | (j / widthScale)); + var sumOffset = countOffset << 2; + itemsSum[sumOffset] += pixels[position]; + itemsSum[sumOffset + 1] += pixels[position + 1]; + itemsSum[sumOffset + 2] += pixels[position + 2]; + itemsSum[sumOffset + 3] += pixels[position + 3]; + itemsCount[countOffset]++; + position += 4; + } + } + var tmpCanvas = createScratchCanvas(scaledWidth, scaledHeight); + var tmpCtx = tmpCanvas.getContext('2d'); + var imgData = tmpCtx.getImageData(0, 0, scaledWidth, scaledHeight); + pixels = imgData.data; + for (var i = 0, j = 0, ii = scaledWidth * scaledHeight; i < ii; i++) { + var count = itemsCount[i]; + pixels[j] = itemsSum[j] / count; + pixels[j + 1] = itemsSum[j + 1] / count; + pixels[j + 2] = itemsSum[j + 2] / count; + pixels[j + 3] = itemsSum[j + 3] / count; + j += 4; + } + tmpCtx.putImageData(imgData, 0, 0); + return tmpCanvas; + } this.save(); @@ -2496,8 +2959,19 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { applyStencilMask(pixels, inverseDecode); - tmpCtx.putImageData(imgData, 0, 0); - ctx.drawImage(tmpCanvas, 0, -h); + var currentTransform = ctx.mozCurrentTransformInverse; + var widthScale = Math.max(Math.abs(currentTransform[0]), 1); + var heightScale = Math.max(Math.abs(currentTransform[3]), 1); + if (widthScale >= 2 || heightScale >= 2) { + // canvas does not resize well large images to small -- using simple + // algorithm to perform pre-scaling + tmpCanvas = rescaleImage(imgData.data, widthScale, heightScale); + ctx.scale(widthScale, heightScale); + ctx.drawImage(tmpCanvas, 0, -h / heightScale); + } else { + tmpCtx.putImageData(imgData, 0, 0); + ctx.drawImage(tmpCanvas, 0, -h); + } this.restore(); }, @@ -2624,6 +3098,7 @@ if (!isWorker) { }; } } + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -2663,51 +3138,55 @@ var Dict = (function DictClosure() { // xref is optional function Dict(xref) { // Map should only be used internally, use functions below to access. - this.map = Object.create(null); - this.xref = xref; - } + var map = Object.create(null); + + this.assignXref = function Dict_assignXref(newXref) { + xref = newXref; + }; - Dict.prototype = { // automatically dereferences Ref objects - get: function Dict_get(key1, key2, key3) { + this.get = function Dict_get(key1, key2, key3) { var value; - var xref = this.xref; - if (typeof (value = this.map[key1]) != 'undefined' || key1 in this.map || + if (typeof (value = map[key1]) != 'undefined' || key1 in map || typeof key2 == 'undefined') { - return xref ? this.xref.fetchIfRef(value) : value; + return xref ? xref.fetchIfRef(value) : value; } - if (typeof (value = this.map[key2]) != 'undefined' || key2 in this.map || + if (typeof (value = map[key2]) != 'undefined' || key2 in map || typeof key3 == 'undefined') { - return xref ? this.xref.fetchIfRef(value) : value; + return xref ? xref.fetchIfRef(value) : value; } - value = this.map[key3] || null; - return xref ? this.xref.fetchIfRef(value) : value; - }, + value = map[key3] || null; + return xref ? xref.fetchIfRef(value) : value; + }; + // no dereferencing - getRaw: function Dict_getRaw(key) { - return this.map[key]; - }, + this.getRaw = function Dict_getRaw(key) { + return map[key]; + }; + // creates new map and dereferences all Refs - getAll: function Dict_getAll() { + this.getAll = function Dict_getAll() { var all = {}; - for (var key in this.map) - all[key] = this.get(key); + for (var key in map) { + var obj = this.get(key); + all[key] = obj instanceof Dict ? obj.getAll() : obj; + } return all; - }, + }; - set: function Dict_set(key, value) { - this.map[key] = value; - }, + this.set = function Dict_set(key, value) { + map[key] = value; + }; - has: function Dict_has(key) { - return key in this.map; - }, + this.has = function Dict_has(key) { + return key in map; + }; - forEach: function Dict_forEach(callback) { - for (var key in this.map) { + this.forEach = function Dict_forEach(callback) { + for (var key in map) { callback(key, this.get(key)); } - } + }; }; return Dict; @@ -2754,7 +3233,14 @@ var Catalog = (function CatalogClosure() { Catalog.prototype = { get metadata() { - var stream = this.catDict.get('Metadata'); + var streamRef = this.catDict.getRaw('Metadata'); + if (!isRef(streamRef)) + return shadow(this, 'metadata', null); + + var encryptMetadata = !this.xref.encrypt ? false : + this.xref.encrypt.encryptMetadata; + + var stream = this.xref.fetch(streamRef, !encryptMetadata); var metadata; if (stream && isDict(stream.dict)) { var type = stream.dict.get('Type'); @@ -2762,7 +3248,16 @@ var Catalog = (function CatalogClosure() { if (isName(type) && isName(subtype) && type.name === 'Metadata' && subtype.name === 'XML') { - metadata = stringToPDFString(bytesToString(stream.getBytes())); + // XXX: This should examine the charset the XML document defines, + // however since there are currently no real means to decode + // arbitrary charsets, let's just hope that the author of the PDF + // was reasonable enough to stick with the XML default charset, + // which is UTF-8. + try { + metadata = stringToUTF8String(bytesToString(stream.getBytes())); + } catch (e) { + info('Skipping invalid metadata.'); + } } } @@ -2920,12 +3415,12 @@ var Catalog = (function CatalogClosure() { })(); var XRef = (function XRefClosure() { - function XRef(stream, startXRef, mainXRefEntriesOffset) { + function XRef(stream, startXRef, mainXRefEntriesOffset, password) { this.stream = stream; this.entries = []; this.xrefstms = {}; var trailerDict = this.readXRef(startXRef); - trailerDict.xref = this; + trailerDict.assignXref(this); this.trailer = trailerDict; // prepare the XRef cache this.cache = []; @@ -2933,8 +3428,7 @@ var XRef = (function XRefClosure() { var encrypt = trailerDict.get('Encrypt'); if (encrypt) { var fileId = trailerDict.get('ID'); - this.encrypt = new CipherTransformFactory(encrypt, - fileId[0] /*, password */); + this.encrypt = new CipherTransformFactory(encrypt, fileId[0], password); } // get the root dictionary (catalog) object @@ -2986,9 +3480,8 @@ var XRef = (function XRefClosure() { } } - // Sanity check: as per spec, first object must have these properties - if (this.entries[0] && - !(this.entries[0].gen === 65535 && this.entries[0].free)) + // Sanity check: as per spec, first object must be free + if (this.entries[0] && !this.entries[0].free) error('Invalid XRef table: unexpected first object'); // Sanity check @@ -3147,7 +3640,7 @@ var XRef = (function XRefClosure() { } // reading XRef streams for (var i = 0, ii = xrefStms.length; i < ii; ++i) { - this.readXRef(xrefStms[i]); + this.readXRef(xrefStms[i], true); } // finding main trailer var dict; @@ -3170,7 +3663,7 @@ var XRef = (function XRefClosure() { // nothing helps error('Invalid PDF structure'); }, - readXRef: function XRef_readXRef(startXRef) { + readXRef: function XRef_readXRef(startXRef, recoveryMode) { var stream = this.stream; stream.pos = startXRef; @@ -3203,16 +3696,18 @@ var XRef = (function XRefClosure() { error('Invalid XRef stream'); } dict = this.readXRefStream(obj); + if (!dict) + error('Failed to read XRef stream'); } // Recursively get previous dictionary, if any obj = dict.get('Prev'); if (isInt(obj)) - this.readXRef(obj); + this.readXRef(obj, recoveryMode); else if (isRef(obj)) { // The spec says Prev must not be a reference, i.e. "/Prev NNN" // This is a fallback for non-compliant PDFs, i.e. "/Prev NNN 0 R" - this.readXRef(obj.num); + this.readXRef(obj.num, recoveryMode); } return dict; @@ -3220,6 +3715,9 @@ var XRef = (function XRefClosure() { log('(while reading XRef): ' + e); } + if (recoveryMode) + return; + warn('Indexing all PDF objects'); return this.indexObjects(); }, @@ -3432,6 +3930,7 @@ var PDFObjects = (function PDFObjectsClosure() { return PDFObjects; })(); + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -4270,7 +4769,10 @@ var PostScriptLexer = (function PostScriptLexerClosure() { // operator var str = ch.toLowerCase(); while (true) { - ch = stream.lookChar().toLowerCase(); + ch = stream.lookChar(); + if (ch === null) + break; + ch = ch.toLowerCase(); if (ch >= 'a' && ch <= 'z') str += ch; else @@ -4306,6 +4808,7 @@ var PostScriptLexer = (function PostScriptLexerClosure() { return PostScriptLexer; })(); + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -4411,6 +4914,7 @@ var ExpertSubsetCharset = [ 'periodinferior', 'commainferior' ]; + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -11344,6 +11848,7 @@ var CIDToUnicodeMaps = { {f: 7, c: 19887}] }; + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -11797,12 +12302,12 @@ var LabCS = (function LabCSClosure() { error('Invalid WhitePoint components, no fallback available'); if (this.XB < 0 || this.YB < 0 || this.ZB < 0) { - warn('Invalid BlackPoint, falling back to default'); + info('Invalid BlackPoint, falling back to default'); this.XB = this.YB = this.ZB = 0; } if (this.amin > this.amax || this.bmin > this.bmax) { - warn('Invalid Range, falling back to defaults'); + info('Invalid Range, falling back to defaults'); this.amin = -100; this.amax = 100; this.bmin = -100; @@ -11876,6 +12381,7 @@ var LabCS = (function LabCSClosure() { }; return LabCS; })(); + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -12297,13 +12803,14 @@ var CipherTransform = (function CipherTransformClosure() { })(); var CipherTransformFactory = (function CipherTransformFactoryClosure() { + var defaultPasswordBytes = new Uint8Array([ + 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, + 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08, + 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, + 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A]); + function prepareKeyData(fileId, password, ownerPassword, userPassword, flags, revision, keyLength, encryptMetadata) { - var defaultPasswordBytes = new Uint8Array([ - 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, - 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08, - 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, - 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A]); var hashData = new Uint8Array(100), i = 0, j, n; if (password) { n = Math.min(32, password.length); @@ -12340,9 +12847,8 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() { var cipher, checkData; if (revision >= 3) { - // padded password in hashData, we can use this array for user - // password check - i = 32; + for (i = 0; i < 32; ++i) + hashData[i] = defaultPasswordBytes[i]; for (j = 0, n = fileId.length; j < n; ++j) hashData[i++] = fileId[j]; cipher = new ARCFourCipher(encryptionKey); @@ -12355,16 +12861,53 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() { cipher = new ARCFourCipher(derivedKey); checkData = cipher.encryptBlock(checkData); } + for (j = 0, n = checkData.length; j < n; ++j) { + if (userPassword[j] != checkData[j]) + return null; + } } else { cipher = new ARCFourCipher(encryptionKey); - checkData = cipher.encryptBlock(hashData.subarray(0, 32)); - } - for (j = 0, n = checkData.length; j < n; ++j) { - if (userPassword[j] != checkData[j]) - error('incorrect password'); + checkData = cipher.encryptBlock(defaultPasswordBytes); + for (j = 0, n = checkData.length; j < n; ++j) { + if (userPassword[j] != checkData[j]) + return null; + } } return encryptionKey; } + function decodeUserPassword(password, ownerPassword, revision, keyLength) { + var hashData = new Uint8Array(32), i = 0, j, n; + n = Math.min(32, password.length); + for (; i < n; ++i) + hashData[i] = password[i]; + j = 0; + while (i < 32) { + hashData[i++] = defaultPasswordBytes[j++]; + } + var hash = calculateMD5(hashData, 0, i); + var keyLengthInBytes = keyLength >> 3; + if (revision >= 3) { + for (j = 0; j < 50; ++j) { + hash = calculateMD5(hash, 0, hash.length); + } + } + + var cipher, userPassword; + if (revision >= 3) { + userPassword = ownerPassword; + var derivedKey = new Uint8Array(keyLengthInBytes), k; + for (j = 19; j >= 0; j--) { + for (k = 0; k < keyLengthInBytes; ++k) + derivedKey[k] = hash[k] ^ j; + cipher = new ARCFourCipher(derivedKey); + userPassword = cipher.encryptBlock(userPassword); + } + } else { + cipher = new ARCFourCipher(hash.subarray(0, keyLengthInBytes)); + userPassword = cipher.encryptBlock(ownerPassword); + } + return userPassword; + } var identityName = new Name('Identity'); @@ -12387,17 +12930,34 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() { var userPassword = stringToBytes(dict.get('U')); var flags = dict.get('P'); var revision = dict.get('R'); - var encryptMetadata = + var encryptMetadata = algorithm == 4 && // meaningful when V is 4 dict.get('EncryptMetadata') !== false; // makes true as default value + this.encryptMetadata = encryptMetadata; + var fileIdBytes = stringToBytes(fileId); var passwordBytes; if (password) passwordBytes = stringToBytes(password); - this.encryptionKey = prepareKeyData(fileIdBytes, passwordBytes, - ownerPassword, userPassword, - flags, revision, - keyLength, encryptMetadata); + var encryptionKey = prepareKeyData(fileIdBytes, passwordBytes, + ownerPassword, userPassword, flags, + revision, keyLength, encryptMetadata); + if (!encryptionKey && !password) { + throw new PasswordException('No password given', 'needpassword'); + } else if (!encryptionKey && password) { + // Attempting use the password as an owner password + var decodedPassword = decodeUserPassword(passwordBytes, ownerPassword, + revision, keyLength); + encryptionKey = prepareKeyData(fileIdBytes, decodedPassword, + ownerPassword, userPassword, flags, + revision, keyLength, encryptMetadata); + } + + if (!encryptionKey) + throw new PasswordException('Incorrect Password', 'incorrectpassword'); + + this.encryptionKey = encryptionKey; + if (algorithm == 4) { this.cf = dict.get('CF'); this.stmf = dict.get('StmF') || identityName; @@ -12472,6 +13032,7 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() { return CipherTransformFactory; })(); + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -12582,20 +13143,21 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { // Compatibility BX: 'beginCompat', - EX: 'endCompat' + EX: 'endCompat', + + // (reserved partial commands for the lexer) + BM: null, + BD: null, + 'true': null, + fa: null, + fal: null, + fals: null, + 'false': null, + nu: null, + nul: null, + 'null': null }; - function splitCombinedOperations(operations) { - // Two operations can be combined together, trying to find which two - // operations were concatenated. - for (var i = operations.length - 1; i > 0; i--) { - var op1 = operations.substring(0, i), op2 = operations.substring(i); - if (op1 in OP_MAP && op2 in OP_MAP) - return [op1, op2]; // operations found - } - return null; - } - PartialEvaluator.prototype = { getOperatorList: function PartialEvaluator_getOperatorList(stream, resources, @@ -12627,13 +13189,15 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { font = xref.fetchIfRef(font) || fontRes.get(fontName); assertWellFormed(isDict(font)); - if (!font.translated) { + + ++self.objIdCounter; + if (!font.loadedName) { font.translated = self.translateFont(font, xref, resources, dependency); if (font.translated) { // keep track of each font we translated so the caller can // load them asynchronously before calling display on a page - loadedName = 'font_' + uniquePrefix + (++self.objIdCounter); + loadedName = 'font_' + uniquePrefix + self.objIdCounter; font.translated.properties.loadedName = loadedName; font.loadedName = loadedName; @@ -12738,36 +13302,19 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { resources = resources || new Dict(); var xobjs = resources.get('XObject') || new Dict(); var patterns = resources.get('Pattern') || new Dict(); - var parser = new Parser(new Lexer(stream), false, xref); + var parser = new Parser(new Lexer(stream, OP_MAP), false, xref); var res = resources; - var hasNextObj = false, nextObj; var args = [], obj; var TILING_PATTERN = 1, SHADING_PATTERN = 2; while (true) { - if (hasNextObj) { - obj = nextObj; - hasNextObj = false; - } else { - obj = parser.getObj(); - if (isEOF(obj)) - break; - } + obj = parser.getObj(); + if (isEOF(obj)) + break; if (isCmd(obj)) { var cmd = obj.cmd; var fn = OP_MAP[cmd]; - if (!fn) { - // invalid content command, trying to recover - var cmds = splitCombinedOperations(cmd); - if (cmds) { - cmd = cmds[0]; - fn = OP_MAP[cmd]; - // feeding other command on the next interation - hasNextObj = true; - nextObj = Cmd.get(cmds[1]); - } - } assertWellFormed(fn, 'Unknown command "' + cmd + '"'); // TODO figure out how to type-check vararg functions @@ -12907,6 +13454,18 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { value[1] ]); break; + case 'BM': + // We support the default so don't trigger the TODO. + if (!isName(value) || value.name != 'Normal') + TODO('graphic state operator ' + key); + break; + case 'SMask': + // We support the default so don't trigger the TODO. + if (!isName(value) || value.name != 'None') + TODO('graphic state operator ' + key); + break; + // Only generate info log messages for the following since + // they are unlikey to have a big impact on the rendering. case 'OP': case 'op': case 'OPM': @@ -12919,14 +13478,13 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { case 'HT': case 'SM': case 'SA': - case 'BM': - case 'SMask': case 'AIS': case 'TK': - TODO('graphic state operator ' + key); + // TODO implement these operators. + info('graphic state operator ' + key); break; default: - warn('Unknown graphic state operator ' + key); + info('Unknown graphic state operator ' + key); break; } } @@ -12940,13 +13498,88 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { args = []; } else if (obj != null) { assertWellFormed(args.length <= 33, 'Too many arguments'); - args.push(obj); + args.push(obj instanceof Dict ? obj.getAll() : obj); } } return queue; }, + getTextContent: function partialEvaluatorGetIRQueue(stream, resources) { + + var self = this; + var xref = this.xref; + + function handleSetFont(fontName, fontRef) { + var fontRes = resources.get('Font'); + + // TODO: TOASK: Is it possible to get here? If so, what does + // args[0].name should be like??? + assert(fontRes, 'fontRes not available'); + + fontRes = xref.fetchIfRef(fontRes); + fontRef = fontRef || fontRes.get(fontName); + var font = xref.fetchIfRef(fontRef), tra; + assertWellFormed(isDict(font)); + if (!font.translated) { + font.translated = self.translateFont(font, xref, resources); + } + return font; + } + + resources = xref.fetchIfRef(resources) || new Dict(); + + var parser = new Parser(new Lexer(stream), false); + var res = resources; + var args = [], obj; + + var text = ''; + var chunk = ''; + var font = null; + while (!isEOF(obj = parser.getObj())) { + if (isCmd(obj)) { + var cmd = obj.cmd; + switch (cmd) { + case 'Tf': + font = handleSetFont(args[0].name); + break; + case 'TJ': + var items = args[0]; + for (var j = 0, jj = items.length; j < jj; j++) { + if (typeof items[j] === 'string') { + chunk += items[j]; + } else if (items[j] < 0) { + // making all negative offsets a space - better to have + // a space in incorrect place than not have them at all + chunk += ' '; + } + } + break; + case 'Tj': + chunk += args[0]; + break; + case "'": + chunk += args[0] + ' '; + break; + case '"': + chunk += args[2] + ' '; + break; + } // switch + if (chunk !== '') { + text += fontCharsToUnicode(chunk, font.translated.properties); + chunk = ''; + } + + args = []; + } else if (obj != null) { + assertWellFormed(args.length <= 33, 'Too many arguments'); + args.push(obj); + } + } + + return text; + }, + extractDataStructures: function partialEvaluatorExtractDataStructures(dict, baseDict, xref, properties) { @@ -12954,7 +13587,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var toUnicode = dict.get('ToUnicode') || baseDict.get('ToUnicode'); if (toUnicode) - properties.toUnicode = this.readToUnicode(toUnicode, xref); + properties.toUnicode = this.readToUnicode(toUnicode, xref, properties); if (properties.composite) { // CIDSystemInfo helps to match CID to glyphs @@ -13010,7 +13643,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { properties.hasEncoding = hasEncoding; }, - readToUnicode: function PartialEvaluator_readToUnicode(toUnicode, xref) { + readToUnicode: function PartialEvaluator_readToUnicode(toUnicode, xref, + properties) { var cmapObj = toUnicode; var charToUnicode = []; if (isName(cmapObj)) { @@ -13099,6 +13733,11 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { } } else if (octet == 0x3E) { if (token.length) { + // Heuristic: guessing chars size by checking numbers sizes + // in the CMap entries. + if (token.length == 2 && properties.composite) + properties.wideChars = false; + if (token.length <= 4) { // parsing hex number tokens.push(parseInt(token, 16)); @@ -13316,6 +13955,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { length1: length1, length2: length2, composite: composite, + wideChars: composite, fixedPitch: false, fontMatrix: dict.get('FontMatrix') || IDENTITY_MATRIX, firstChar: firstChar || 0, @@ -13336,7 +13976,6 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { properties.coded = true; var charProcs = dict.get('CharProcs').getAll(); var fontResources = dict.get('Resources') || resources; - properties.resources = fontResources; properties.charProcOperatorList = {}; for (var key in charProcs) { var glyphStream = charProcs[key]; @@ -13380,6 +14019,7 @@ var EvalState = (function EvalStateClosure() { return EvalState; })(); + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -14135,6 +14775,736 @@ function isSpecialUnicode(unicode) { unicode < kCmapGlyphOffset + kSizeOfGlyphArea); } +// The normalization table is obtained by filtering the Unicode characters +// database with <compat> entries. +var NormalizedUnicodes = { + '\u00A8': '\u0020\u0308', + '\u00AF': '\u0020\u0304', + '\u00B4': '\u0020\u0301', + '\u00B5': '\u03BC', + '\u00B8': '\u0020\u0327', + '\u0132': '\u0049\u004A', + '\u0133': '\u0069\u006A', + '\u013F': '\u004C\u00B7', + '\u0140': '\u006C\u00B7', + '\u0149': '\u02BC\u006E', + '\u017F': '\u0073', + '\u01C4': '\u0044\u017D', + '\u01C5': '\u0044\u017E', + '\u01C6': '\u0064\u017E', + '\u01C7': '\u004C\u004A', + '\u01C8': '\u004C\u006A', + '\u01C9': '\u006C\u006A', + '\u01CA': '\u004E\u004A', + '\u01CB': '\u004E\u006A', + '\u01CC': '\u006E\u006A', + '\u01F1': '\u0044\u005A', + '\u01F2': '\u0044\u007A', + '\u01F3': '\u0064\u007A', + '\u02D8': '\u0020\u0306', + '\u02D9': '\u0020\u0307', + '\u02DA': '\u0020\u030A', + '\u02DB': '\u0020\u0328', + '\u02DC': '\u0020\u0303', + '\u02DD': '\u0020\u030B', + '\u037A': '\u0020\u0345', + '\u0384': '\u0020\u0301', + '\u03D0': '\u03B2', + '\u03D1': '\u03B8', + '\u03D2': '\u03A5', + '\u03D5': '\u03C6', + '\u03D6': '\u03C0', + '\u03F0': '\u03BA', + '\u03F1': '\u03C1', + '\u03F2': '\u03C2', + '\u03F4': '\u0398', + '\u03F5': '\u03B5', + '\u03F9': '\u03A3', + '\u0587': '\u0565\u0582', + '\u0675': '\u0627\u0674', + '\u0676': '\u0648\u0674', + '\u0677': '\u06C7\u0674', + '\u0678': '\u064A\u0674', + '\u0E33': '\u0E4D\u0E32', + '\u0EB3': '\u0ECD\u0EB2', + '\u0EDC': '\u0EAB\u0E99', + '\u0EDD': '\u0EAB\u0EA1', + '\u0F77': '\u0FB2\u0F81', + '\u0F79': '\u0FB3\u0F81', + '\u1E9A': '\u0061\u02BE', + '\u1FBD': '\u0020\u0313', + '\u1FBF': '\u0020\u0313', + '\u1FC0': '\u0020\u0342', + '\u1FFE': '\u0020\u0314', + '\u2002': '\u0020', + '\u2003': '\u0020', + '\u2004': '\u0020', + '\u2005': '\u0020', + '\u2006': '\u0020', + '\u2008': '\u0020', + '\u2009': '\u0020', + '\u200A': '\u0020', + '\u2017': '\u0020\u0333', + '\u2024': '\u002E', + '\u2025': '\u002E\u002E', + '\u2026': '\u002E\u002E\u002E', + '\u2033': '\u2032\u2032', + '\u2034': '\u2032\u2032\u2032', + '\u2036': '\u2035\u2035', + '\u2037': '\u2035\u2035\u2035', + '\u203C': '\u0021\u0021', + '\u203E': '\u0020\u0305', + '\u2047': '\u003F\u003F', + '\u2048': '\u003F\u0021', + '\u2049': '\u0021\u003F', + '\u2057': '\u2032\u2032\u2032\u2032', + '\u205F': '\u0020', + '\u20A8': '\u0052\u0073', + '\u2100': '\u0061\u002F\u0063', + '\u2101': '\u0061\u002F\u0073', + '\u2103': '\u00B0\u0043', + '\u2105': '\u0063\u002F\u006F', + '\u2106': '\u0063\u002F\u0075', + '\u2107': '\u0190', + '\u2109': '\u00B0\u0046', + '\u2116': '\u004E\u006F', + '\u2121': '\u0054\u0045\u004C', + '\u2135': '\u05D0', + '\u2136': '\u05D1', + '\u2137': '\u05D2', + '\u2138': '\u05D3', + '\u213B': '\u0046\u0041\u0058', + '\u2160': '\u0049', + '\u2161': '\u0049\u0049', + '\u2162': '\u0049\u0049\u0049', + '\u2163': '\u0049\u0056', + '\u2164': '\u0056', + '\u2165': '\u0056\u0049', + '\u2166': '\u0056\u0049\u0049', + '\u2167': '\u0056\u0049\u0049\u0049', + '\u2168': '\u0049\u0058', + '\u2169': '\u0058', + '\u216A': '\u0058\u0049', + '\u216B': '\u0058\u0049\u0049', + '\u216C': '\u004C', + '\u216D': '\u0043', + '\u216E': '\u0044', + '\u216F': '\u004D', + '\u2170': '\u0069', + '\u2171': '\u0069\u0069', + '\u2172': '\u0069\u0069\u0069', + '\u2173': '\u0069\u0076', + '\u2174': '\u0076', + '\u2175': '\u0076\u0069', + '\u2176': '\u0076\u0069\u0069', + '\u2177': '\u0076\u0069\u0069\u0069', + '\u2178': '\u0069\u0078', + '\u2179': '\u0078', + '\u217A': '\u0078\u0069', + '\u217B': '\u0078\u0069\u0069', + '\u217C': '\u006C', + '\u217D': '\u0063', + '\u217E': '\u0064', + '\u217F': '\u006D', + '\u222C': '\u222B\u222B', + '\u222D': '\u222B\u222B\u222B', + '\u222F': '\u222E\u222E', + '\u2230': '\u222E\u222E\u222E', + '\u2474': '\u0028\u0031\u0029', + '\u2475': '\u0028\u0032\u0029', + '\u2476': '\u0028\u0033\u0029', + '\u2477': '\u0028\u0034\u0029', + '\u2478': '\u0028\u0035\u0029', + '\u2479': '\u0028\u0036\u0029', + '\u247A': '\u0028\u0037\u0029', + '\u247B': '\u0028\u0038\u0029', + '\u247C': '\u0028\u0039\u0029', + '\u247D': '\u0028\u0031\u0030\u0029', + '\u247E': '\u0028\u0031\u0031\u0029', + '\u247F': '\u0028\u0031\u0032\u0029', + '\u2480': '\u0028\u0031\u0033\u0029', + '\u2481': '\u0028\u0031\u0034\u0029', + '\u2482': '\u0028\u0031\u0035\u0029', + '\u2483': '\u0028\u0031\u0036\u0029', + '\u2484': '\u0028\u0031\u0037\u0029', + '\u2485': '\u0028\u0031\u0038\u0029', + '\u2486': '\u0028\u0031\u0039\u0029', + '\u2487': '\u0028\u0032\u0030\u0029', + '\u2488': '\u0031\u002E', + '\u2489': '\u0032\u002E', + '\u248A': '\u0033\u002E', + '\u248B': '\u0034\u002E', + '\u248C': '\u0035\u002E', + '\u248D': '\u0036\u002E', + '\u248E': '\u0037\u002E', + '\u248F': '\u0038\u002E', + '\u2490': '\u0039\u002E', + '\u2491': '\u0031\u0030\u002E', + '\u2492': '\u0031\u0031\u002E', + '\u2493': '\u0031\u0032\u002E', + '\u2494': '\u0031\u0033\u002E', + '\u2495': '\u0031\u0034\u002E', + '\u2496': '\u0031\u0035\u002E', + '\u2497': '\u0031\u0036\u002E', + '\u2498': '\u0031\u0037\u002E', + '\u2499': '\u0031\u0038\u002E', + '\u249A': '\u0031\u0039\u002E', + '\u249B': '\u0032\u0030\u002E', + '\u249C': '\u0028\u0061\u0029', + '\u249D': '\u0028\u0062\u0029', + '\u249E': '\u0028\u0063\u0029', + '\u249F': '\u0028\u0064\u0029', + '\u24A0': '\u0028\u0065\u0029', + '\u24A1': '\u0028\u0066\u0029', + '\u24A2': '\u0028\u0067\u0029', + '\u24A3': '\u0028\u0068\u0029', + '\u24A4': '\u0028\u0069\u0029', + '\u24A5': '\u0028\u006A\u0029', + '\u24A6': '\u0028\u006B\u0029', + '\u24A7': '\u0028\u006C\u0029', + '\u24A8': '\u0028\u006D\u0029', + '\u24A9': '\u0028\u006E\u0029', + '\u24AA': '\u0028\u006F\u0029', + '\u24AB': '\u0028\u0070\u0029', + '\u24AC': '\u0028\u0071\u0029', + '\u24AD': '\u0028\u0072\u0029', + '\u24AE': '\u0028\u0073\u0029', + '\u24AF': '\u0028\u0074\u0029', + '\u24B0': '\u0028\u0075\u0029', + '\u24B1': '\u0028\u0076\u0029', + '\u24B2': '\u0028\u0077\u0029', + '\u24B3': '\u0028\u0078\u0029', + '\u24B4': '\u0028\u0079\u0029', + '\u24B5': '\u0028\u007A\u0029', + '\u2A0C': '\u222B\u222B\u222B\u222B', + '\u2A74': '\u003A\u003A\u003D', + '\u2A75': '\u003D\u003D', + '\u2A76': '\u003D\u003D\u003D', + '\u2E9F': '\u6BCD', + '\u2EF3': '\u9F9F', + '\u2F00': '\u4E00', + '\u2F01': '\u4E28', + '\u2F02': '\u4E36', + '\u2F03': '\u4E3F', + '\u2F04': '\u4E59', + '\u2F05': '\u4E85', + '\u2F06': '\u4E8C', + '\u2F07': '\u4EA0', + '\u2F08': '\u4EBA', + '\u2F09': '\u513F', + '\u2F0A': '\u5165', + '\u2F0B': '\u516B', + '\u2F0C': '\u5182', + '\u2F0D': '\u5196', + '\u2F0E': '\u51AB', + '\u2F0F': '\u51E0', + '\u2F10': '\u51F5', + '\u2F11': '\u5200', + '\u2F12': '\u529B', + '\u2F13': '\u52F9', + '\u2F14': '\u5315', + '\u2F15': '\u531A', + '\u2F16': '\u5338', + '\u2F17': '\u5341', + '\u2F18': '\u535C', + '\u2F19': '\u5369', + '\u2F1A': '\u5382', + '\u2F1B': '\u53B6', + '\u2F1C': '\u53C8', + '\u2F1D': '\u53E3', + '\u2F1E': '\u56D7', + '\u2F1F': '\u571F', + '\u2F20': '\u58EB', + '\u2F21': '\u5902', + '\u2F22': '\u590A', + '\u2F23': '\u5915', + '\u2F24': '\u5927', + '\u2F25': '\u5973', + '\u2F26': '\u5B50', + '\u2F27': '\u5B80', + '\u2F28': '\u5BF8', + '\u2F29': '\u5C0F', + '\u2F2A': '\u5C22', + '\u2F2B': '\u5C38', + '\u2F2C': '\u5C6E', + '\u2F2D': '\u5C71', + '\u2F2E': '\u5DDB', + '\u2F2F': '\u5DE5', + '\u2F30': '\u5DF1', + '\u2F31': '\u5DFE', + '\u2F32': '\u5E72', + '\u2F33': '\u5E7A', + '\u2F34': '\u5E7F', + '\u2F35': '\u5EF4', + '\u2F36': '\u5EFE', + '\u2F37': '\u5F0B', + '\u2F38': '\u5F13', + '\u2F39': '\u5F50', + '\u2F3A': '\u5F61', + '\u2F3B': '\u5F73', + '\u2F3C': '\u5FC3', + '\u2F3D': '\u6208', + '\u2F3E': '\u6236', + '\u2F3F': '\u624B', + '\u2F40': '\u652F', + '\u2F41': '\u6534', + '\u2F42': '\u6587', + '\u2F43': '\u6597', + '\u2F44': '\u65A4', + '\u2F45': '\u65B9', + '\u2F46': '\u65E0', + '\u2F47': '\u65E5', + '\u2F48': '\u66F0', + '\u2F49': '\u6708', + '\u2F4A': '\u6728', + '\u2F4B': '\u6B20', + '\u2F4C': '\u6B62', + '\u2F4D': '\u6B79', + '\u2F4E': '\u6BB3', + '\u2F4F': '\u6BCB', + '\u2F50': '\u6BD4', + '\u2F51': '\u6BDB', + '\u2F52': '\u6C0F', + '\u2F53': '\u6C14', + '\u2F54': '\u6C34', + '\u2F55': '\u706B', + '\u2F56': '\u722A', + '\u2F57': '\u7236', + '\u2F58': '\u723B', + '\u2F59': '\u723F', + '\u2F5A': '\u7247', + '\u2F5B': '\u7259', + '\u2F5C': '\u725B', + '\u2F5D': '\u72AC', + '\u2F5E': '\u7384', + '\u2F5F': '\u7389', + '\u2F60': '\u74DC', + '\u2F61': '\u74E6', + '\u2F62': '\u7518', + '\u2F63': '\u751F', + '\u2F64': '\u7528', + '\u2F65': '\u7530', + '\u2F66': '\u758B', + '\u2F67': '\u7592', + '\u2F68': '\u7676', + '\u2F69': '\u767D', + '\u2F6A': '\u76AE', + '\u2F6B': '\u76BF', + '\u2F6C': '\u76EE', + '\u2F6D': '\u77DB', + '\u2F6E': '\u77E2', + '\u2F6F': '\u77F3', + '\u2F70': '\u793A', + '\u2F71': '\u79B8', + '\u2F72': '\u79BE', + '\u2F73': '\u7A74', + '\u2F74': '\u7ACB', + '\u2F75': '\u7AF9', + '\u2F76': '\u7C73', + '\u2F77': '\u7CF8', + '\u2F78': '\u7F36', + '\u2F79': '\u7F51', + '\u2F7A': '\u7F8A', + '\u2F7B': '\u7FBD', + '\u2F7C': '\u8001', + '\u2F7D': '\u800C', + '\u2F7E': '\u8012', + '\u2F7F': '\u8033', + '\u2F80': '\u807F', + '\u2F81': '\u8089', + '\u2F82': '\u81E3', + '\u2F83': '\u81EA', + '\u2F84': '\u81F3', + '\u2F85': '\u81FC', + '\u2F86': '\u820C', + '\u2F87': '\u821B', + '\u2F88': '\u821F', + '\u2F89': '\u826E', + '\u2F8A': '\u8272', + '\u2F8B': '\u8278', + '\u2F8C': '\u864D', + '\u2F8D': '\u866B', + '\u2F8E': '\u8840', + '\u2F8F': '\u884C', + '\u2F90': '\u8863', + '\u2F91': '\u897E', + '\u2F92': '\u898B', + '\u2F93': '\u89D2', + '\u2F94': '\u8A00', + '\u2F95': '\u8C37', + '\u2F96': '\u8C46', + '\u2F97': '\u8C55', + '\u2F98': '\u8C78', + '\u2F99': '\u8C9D', + '\u2F9A': '\u8D64', + '\u2F9B': '\u8D70', + '\u2F9C': '\u8DB3', + '\u2F9D': '\u8EAB', + '\u2F9E': '\u8ECA', + '\u2F9F': '\u8F9B', + '\u2FA0': '\u8FB0', + '\u2FA1': '\u8FB5', + '\u2FA2': '\u9091', + '\u2FA3': '\u9149', + '\u2FA4': '\u91C6', + '\u2FA5': '\u91CC', + '\u2FA6': '\u91D1', + '\u2FA7': '\u9577', + '\u2FA8': '\u9580', + '\u2FA9': '\u961C', + '\u2FAA': '\u96B6', + '\u2FAB': '\u96B9', + '\u2FAC': '\u96E8', + '\u2FAD': '\u9751', + '\u2FAE': '\u975E', + '\u2FAF': '\u9762', + '\u2FB0': '\u9769', + '\u2FB1': '\u97CB', + '\u2FB2': '\u97ED', + '\u2FB3': '\u97F3', + '\u2FB4': '\u9801', + '\u2FB5': '\u98A8', + '\u2FB6': '\u98DB', + '\u2FB7': '\u98DF', + '\u2FB8': '\u9996', + '\u2FB9': '\u9999', + '\u2FBA': '\u99AC', + '\u2FBB': '\u9AA8', + '\u2FBC': '\u9AD8', + '\u2FBD': '\u9ADF', + '\u2FBE': '\u9B25', + '\u2FBF': '\u9B2F', + '\u2FC0': '\u9B32', + '\u2FC1': '\u9B3C', + '\u2FC2': '\u9B5A', + '\u2FC3': '\u9CE5', + '\u2FC4': '\u9E75', + '\u2FC5': '\u9E7F', + '\u2FC6': '\u9EA5', + '\u2FC7': '\u9EBB', + '\u2FC8': '\u9EC3', + '\u2FC9': '\u9ECD', + '\u2FCA': '\u9ED1', + '\u2FCB': '\u9EF9', + '\u2FCC': '\u9EFD', + '\u2FCD': '\u9F0E', + '\u2FCE': '\u9F13', + '\u2FCF': '\u9F20', + '\u2FD0': '\u9F3B', + '\u2FD1': '\u9F4A', + '\u2FD2': '\u9F52', + '\u2FD3': '\u9F8D', + '\u2FD4': '\u9F9C', + '\u2FD5': '\u9FA0', + '\u3036': '\u3012', + '\u3038': '\u5341', + '\u3039': '\u5344', + '\u303A': '\u5345', + '\u309B': '\u0020\u3099', + '\u309C': '\u0020\u309A', + '\u3131': '\u1100', + '\u3132': '\u1101', + '\u3133': '\u11AA', + '\u3134': '\u1102', + '\u3135': '\u11AC', + '\u3136': '\u11AD', + '\u3137': '\u1103', + '\u3138': '\u1104', + '\u3139': '\u1105', + '\u313A': '\u11B0', + '\u313B': '\u11B1', + '\u313C': '\u11B2', + '\u313D': '\u11B3', + '\u313E': '\u11B4', + '\u313F': '\u11B5', + '\u3140': '\u111A', + '\u3141': '\u1106', + '\u3142': '\u1107', + '\u3143': '\u1108', + '\u3144': '\u1121', + '\u3145': '\u1109', + '\u3146': '\u110A', + '\u3147': '\u110B', + '\u3148': '\u110C', + '\u3149': '\u110D', + '\u314A': '\u110E', + '\u314B': '\u110F', + '\u314C': '\u1110', + '\u314D': '\u1111', + '\u314E': '\u1112', + '\u314F': '\u1161', + '\u3150': '\u1162', + '\u3151': '\u1163', + '\u3152': '\u1164', + '\u3153': '\u1165', + '\u3154': '\u1166', + '\u3155': '\u1167', + '\u3156': '\u1168', + '\u3157': '\u1169', + '\u3158': '\u116A', + '\u3159': '\u116B', + '\u315A': '\u116C', + '\u315B': '\u116D', + '\u315C': '\u116E', + '\u315D': '\u116F', + '\u315E': '\u1170', + '\u315F': '\u1171', + '\u3160': '\u1172', + '\u3161': '\u1173', + '\u3162': '\u1174', + '\u3163': '\u1175', + '\u3164': '\u1160', + '\u3165': '\u1114', + '\u3166': '\u1115', + '\u3167': '\u11C7', + '\u3168': '\u11C8', + '\u3169': '\u11CC', + '\u316A': '\u11CE', + '\u316B': '\u11D3', + '\u316C': '\u11D7', + '\u316D': '\u11D9', + '\u316E': '\u111C', + '\u316F': '\u11DD', + '\u3170': '\u11DF', + '\u3171': '\u111D', + '\u3172': '\u111E', + '\u3173': '\u1120', + '\u3174': '\u1122', + '\u3175': '\u1123', + '\u3176': '\u1127', + '\u3177': '\u1129', + '\u3178': '\u112B', + '\u3179': '\u112C', + '\u317A': '\u112D', + '\u317B': '\u112E', + '\u317C': '\u112F', + '\u317D': '\u1132', + '\u317E': '\u1136', + '\u317F': '\u1140', + '\u3180': '\u1147', + '\u3181': '\u114C', + '\u3182': '\u11F1', + '\u3183': '\u11F2', + '\u3184': '\u1157', + '\u3185': '\u1158', + '\u3186': '\u1159', + '\u3187': '\u1184', + '\u3188': '\u1185', + '\u3189': '\u1188', + '\u318A': '\u1191', + '\u318B': '\u1192', + '\u318C': '\u1194', + '\u318D': '\u119E', + '\u318E': '\u11A1', + '\u3200': '\u0028\u1100\u0029', + '\u3201': '\u0028\u1102\u0029', + '\u3202': '\u0028\u1103\u0029', + '\u3203': '\u0028\u1105\u0029', + '\u3204': '\u0028\u1106\u0029', + '\u3205': '\u0028\u1107\u0029', + '\u3206': '\u0028\u1109\u0029', + '\u3207': '\u0028\u110B\u0029', + '\u3208': '\u0028\u110C\u0029', + '\u3209': '\u0028\u110E\u0029', + '\u320A': '\u0028\u110F\u0029', + '\u320B': '\u0028\u1110\u0029', + '\u320C': '\u0028\u1111\u0029', + '\u320D': '\u0028\u1112\u0029', + '\u320E': '\u0028\u1100\u1161\u0029', + '\u320F': '\u0028\u1102\u1161\u0029', + '\u3210': '\u0028\u1103\u1161\u0029', + '\u3211': '\u0028\u1105\u1161\u0029', + '\u3212': '\u0028\u1106\u1161\u0029', + '\u3213': '\u0028\u1107\u1161\u0029', + '\u3214': '\u0028\u1109\u1161\u0029', + '\u3215': '\u0028\u110B\u1161\u0029', + '\u3216': '\u0028\u110C\u1161\u0029', + '\u3217': '\u0028\u110E\u1161\u0029', + '\u3218': '\u0028\u110F\u1161\u0029', + '\u3219': '\u0028\u1110\u1161\u0029', + '\u321A': '\u0028\u1111\u1161\u0029', + '\u321B': '\u0028\u1112\u1161\u0029', + '\u321C': '\u0028\u110C\u116E\u0029', + '\u321D': '\u0028\u110B\u1169\u110C\u1165\u11AB\u0029', + '\u321E': '\u0028\u110B\u1169\u1112\u116E\u0029', + '\u3220': '\u0028\u4E00\u0029', + '\u3221': '\u0028\u4E8C\u0029', + '\u3222': '\u0028\u4E09\u0029', + '\u3223': '\u0028\u56DB\u0029', + '\u3224': '\u0028\u4E94\u0029', + '\u3225': '\u0028\u516D\u0029', + '\u3226': '\u0028\u4E03\u0029', + '\u3227': '\u0028\u516B\u0029', + '\u3228': '\u0028\u4E5D\u0029', + '\u3229': '\u0028\u5341\u0029', + '\u322A': '\u0028\u6708\u0029', + '\u322B': '\u0028\u706B\u0029', + '\u322C': '\u0028\u6C34\u0029', + '\u322D': '\u0028\u6728\u0029', + '\u322E': '\u0028\u91D1\u0029', + '\u322F': '\u0028\u571F\u0029', + '\u3230': '\u0028\u65E5\u0029', + '\u3231': '\u0028\u682A\u0029', + '\u3232': '\u0028\u6709\u0029', + '\u3233': '\u0028\u793E\u0029', + '\u3234': '\u0028\u540D\u0029', + '\u3235': '\u0028\u7279\u0029', + '\u3236': '\u0028\u8CA1\u0029', + '\u3237': '\u0028\u795D\u0029', + '\u3238': '\u0028\u52B4\u0029', + '\u3239': '\u0028\u4EE3\u0029', + '\u323A': '\u0028\u547C\u0029', + '\u323B': '\u0028\u5B66\u0029', + '\u323C': '\u0028\u76E3\u0029', + '\u323D': '\u0028\u4F01\u0029', + '\u323E': '\u0028\u8CC7\u0029', + '\u323F': '\u0028\u5354\u0029', + '\u3240': '\u0028\u796D\u0029', + '\u3241': '\u0028\u4F11\u0029', + '\u3242': '\u0028\u81EA\u0029', + '\u3243': '\u0028\u81F3\u0029', + '\u32C0': '\u0031\u6708', + '\u32C1': '\u0032\u6708', + '\u32C2': '\u0033\u6708', + '\u32C3': '\u0034\u6708', + '\u32C4': '\u0035\u6708', + '\u32C5': '\u0036\u6708', + '\u32C6': '\u0037\u6708', + '\u32C7': '\u0038\u6708', + '\u32C8': '\u0039\u6708', + '\u32C9': '\u0031\u0030\u6708', + '\u32CA': '\u0031\u0031\u6708', + '\u32CB': '\u0031\u0032\u6708', + '\u3358': '\u0030\u70B9', + '\u3359': '\u0031\u70B9', + '\u335A': '\u0032\u70B9', + '\u335B': '\u0033\u70B9', + '\u335C': '\u0034\u70B9', + '\u335D': '\u0035\u70B9', + '\u335E': '\u0036\u70B9', + '\u335F': '\u0037\u70B9', + '\u3360': '\u0038\u70B9', + '\u3361': '\u0039\u70B9', + '\u3362': '\u0031\u0030\u70B9', + '\u3363': '\u0031\u0031\u70B9', + '\u3364': '\u0031\u0032\u70B9', + '\u3365': '\u0031\u0033\u70B9', + '\u3366': '\u0031\u0034\u70B9', + '\u3367': '\u0031\u0035\u70B9', + '\u3368': '\u0031\u0036\u70B9', + '\u3369': '\u0031\u0037\u70B9', + '\u336A': '\u0031\u0038\u70B9', + '\u336B': '\u0031\u0039\u70B9', + '\u336C': '\u0032\u0030\u70B9', + '\u336D': '\u0032\u0031\u70B9', + '\u336E': '\u0032\u0032\u70B9', + '\u336F': '\u0032\u0033\u70B9', + '\u3370': '\u0032\u0034\u70B9', + '\u33E0': '\u0031\u65E5', + '\u33E1': '\u0032\u65E5', + '\u33E2': '\u0033\u65E5', + '\u33E3': '\u0034\u65E5', + '\u33E4': '\u0035\u65E5', + '\u33E5': '\u0036\u65E5', + '\u33E6': '\u0037\u65E5', + '\u33E7': '\u0038\u65E5', + '\u33E8': '\u0039\u65E5', + '\u33E9': '\u0031\u0030\u65E5', + '\u33EA': '\u0031\u0031\u65E5', + '\u33EB': '\u0031\u0032\u65E5', + '\u33EC': '\u0031\u0033\u65E5', + '\u33ED': '\u0031\u0034\u65E5', + '\u33EE': '\u0031\u0035\u65E5', + '\u33EF': '\u0031\u0036\u65E5', + '\u33F0': '\u0031\u0037\u65E5', + '\u33F1': '\u0031\u0038\u65E5', + '\u33F2': '\u0031\u0039\u65E5', + '\u33F3': '\u0032\u0030\u65E5', + '\u33F4': '\u0032\u0031\u65E5', + '\u33F5': '\u0032\u0032\u65E5', + '\u33F6': '\u0032\u0033\u65E5', + '\u33F7': '\u0032\u0034\u65E5', + '\u33F8': '\u0032\u0035\u65E5', + '\u33F9': '\u0032\u0036\u65E5', + '\u33FA': '\u0032\u0037\u65E5', + '\u33FB': '\u0032\u0038\u65E5', + '\u33FC': '\u0032\u0039\u65E5', + '\u33FD': '\u0033\u0030\u65E5', + '\u33FE': '\u0033\u0031\u65E5', + '\uFB00': '\u0066\u0066', + '\uFB01': '\u0066\u0069', + '\uFB02': '\u0066\u006C', + '\uFB03': '\u0066\u0066\u0069', + '\uFB04': '\u0066\u0066\u006C', + '\uFB05': '\u017F\u0074', + '\uFB06': '\u0073\u0074', + '\uFB13': '\u0574\u0576', + '\uFB14': '\u0574\u0565', + '\uFB15': '\u0574\u056B', + '\uFB16': '\u057E\u0576', + '\uFB17': '\u0574\u056D', + '\uFB4F': '\u05D0\u05DC', + '\uFE49': '\u203E', + '\uFE4A': '\u203E', + '\uFE4B': '\u203E', + '\uFE4C': '\u203E', + '\uFE4D': '\u005F', + '\uFE4E': '\u005F', + '\uFE4F': '\u005F' +}; + +function fontCharsToUnicode(charCodes, fontProperties) { + var toUnicode = fontProperties.toUnicode; + var composite = fontProperties.composite; + var encoding, differences, cidToUnicode; + var result = ''; + if (composite) { + cidToUnicode = fontProperties.cidToUnicode; + for (var i = 0, ii = charCodes.length; i < ii; i += 2) { + var charCode = (charCodes.charCodeAt(i) << 8) | + charCodes.charCodeAt(i + 1); + if (toUnicode && charCode in toUnicode) { + var unicode = toUnicode[charCode]; + result += typeof unicode !== 'number' ? unicode : + String.fromCharCode(unicode); + continue; + } + result += String.fromCharCode(!cidToUnicode ? charCode : + cidToUnicode[charCode] || charCode); + } + } else { + differences = fontProperties.differences; + encoding = fontProperties.baseEncoding; + for (var i = 0, ii = charCodes.length; i < ii; i++) { + var charCode = charCodes.charCodeAt(i); + var unicode; + if (toUnicode && charCode in toUnicode) { + var unicode = toUnicode[charCode]; + result += typeof unicode !== 'number' ? unicode : + String.fromCharCode(unicode); + continue; + } + + var glyphName = charCode in differences ? differences[charCode] : + encoding[charCode]; + if (glyphName in GlyphsUnicode) { + result += String.fromCharCode(GlyphsUnicode[glyphName]); + continue; + } + result += String.fromCharCode(charCode); + } + } + // normalizing the unicode characters + for (var i = 0, ii = result.length; i < ii; i++) { + if (!(result[i] in NormalizedUnicodes)) + continue; + result = result.substring(0, i) + NormalizedUnicodes[result[i]] + + result.substring(i + 1); + ii = result.length; + } + return result; +} + /** * 'Font' is the class the outside world should use, it encapsulate all the font * decoding logics whatever type it is (assuming the font type is supported). @@ -14148,7 +15518,6 @@ var Font = (function FontClosure() { this.name = name; this.coded = properties.coded; this.charProcOperatorList = properties.charProcOperatorList; - this.resources = properties.resources; this.sizes = []; var names = name.split('+'); @@ -14172,6 +15541,7 @@ var Font = (function FontClosure() { this.widths = properties.widths; this.defaultWidth = properties.defaultWidth; this.composite = properties.composite; + this.wideChars = properties.wideChars; this.hasEncoding = properties.hasEncoding; this.fontMatrix = properties.fontMatrix; @@ -15109,6 +16479,16 @@ var Font = (function FontClosure() { properties.glyphNames = glyphNames; } + function isOS2Valid(os2Table) { + var data = os2Table.data; + // usWinAscent == 0 makes font unreadable by windows + var usWinAscent = (data[74] << 8) | data[75]; + if (usWinAscent == 0) + return false; + + return true; + } + // Check that required tables are present var requiredTables = ['OS/2', 'cmap', 'head', 'hhea', 'hmtx', 'maxp', 'name', 'post']; @@ -15116,7 +16496,7 @@ var Font = (function FontClosure() { var header = readOpenTypeHeader(font); var numTables = header.numTables; - var cmap, post, maxp, hhea, hmtx, vhea, vmtx, head, loca, glyf; + var cmap, post, maxp, hhea, hmtx, vhea, vmtx, head, loca, glyf, os2; var tables = []; for (var i = 0; i < numTables; i++) { var table = readTableEntry(font); @@ -15134,6 +16514,8 @@ var Font = (function FontClosure() { hmtx = table; else if (table.tag == 'head') head = table; + else if (table.tag == 'OS/2') + os2 = table; requiredTables.splice(index, 1); } else { @@ -15149,7 +16531,7 @@ var Font = (function FontClosure() { tables.push(table); } - var numTables = header.numTables + requiredTables.length; + var numTables = tables.length + requiredTables.length; // header and new offsets. Table entry information is appended to the // end of file. The virtualOffset represents where to put the actual @@ -15163,21 +16545,10 @@ var Font = (function FontClosure() { // of missing tables createOpenTypeHeader(header.version, ttf, numTables); - if (requiredTables.indexOf('OS/2') != -1) { - // extract some more font properties from the OpenType head and - // hhea tables; yMin and descent value are always negative - var override = { - unitsPerEm: int16([head.data[18], head.data[19]]), - yMax: int16([head.data[42], head.data[43]]), - yMin: int16([head.data[38], head.data[39]]) - 0x10000, - ascent: int16([hhea.data[4], hhea.data[5]]), - descent: int16([hhea.data[6], hhea.data[7]]) - 0x10000 - }; - - tables.push({ - tag: 'OS/2', - data: stringToArray(createOS2Table(properties, null, override)) - }); + // Invalid OS/2 can break the font for the Windows + if (os2 && !isOS2Valid(os2)) { + tables.splice(tables.indexOf(os2), 1); + os2 = null; } // Ensure the [h/v]mtx tables contains the advance width and @@ -15357,9 +16728,9 @@ var Font = (function FontClosure() { this.isSymbolicFont = false; } - // heuristics: if removed more than 2 glyphs encoding WinAnsiEncoding - // does not set properly - if (glyphsRemoved > 2) { + // heuristics: if removed more than 10 glyphs encoding WinAnsiEncoding + // does not set properly (broken PDFs have about 100 removed glyphs) + if (glyphsRemoved > 10) { warn('Switching TrueType encoding to MacRomanEncoding for ' + this.name + ' font'); encoding = Encodings.MacRomanEncoding; @@ -15458,6 +16829,23 @@ var Font = (function FontClosure() { } this.unicodeIsEnabled = unicodeIsEnabled; + if (!os2) { + // extract some more font properties from the OpenType head and + // hhea tables; yMin and descent value are always negative + var override = { + unitsPerEm: int16([head.data[18], head.data[19]]), + yMax: int16([head.data[42], head.data[43]]), + yMin: int16([head.data[38], head.data[39]]) - 0x10000, + ascent: int16([hhea.data[4], hhea.data[5]]), + descent: int16([hhea.data[6], hhea.data[7]]) - 0x10000 + }; + + tables.push({ + tag: 'OS/2', + data: stringToArray(createOS2Table(properties, glyphs, override)) + }); + } + // Rewrite the 'post' table if needed if (requiredTables.indexOf('post') != -1) { tables.push({ @@ -15842,6 +17230,7 @@ var Font = (function FontClosure() { } // MacRoman encoding address by re-encoding the cmap table + fontCharCode = glyphName in this.glyphNameMap ? this.glyphNameMap[glyphName] : GlyphsUnicode[glyphName]; break; @@ -15885,7 +17274,7 @@ var Font = (function FontClosure() { glyphs = []; - if (this.composite) { + if (this.wideChars) { // composite fonts have multi-byte strings convert the string from // single-byte to multi-byte // XXX assuming CIDFonts are two-byte - later need to extract the @@ -16841,7 +18230,7 @@ var CFFFont = (function CFFFontClosure() { this.properties = properties; var parser = new CFFParser(file, properties); - var cff = parser.parse(); + var cff = parser.parse(true); var compiler = new CFFCompiler(cff); this.readExtra(cff); try { @@ -16932,7 +18321,7 @@ var CFFParser = (function CFFParserClosure() { this.properties = properties; } CFFParser.prototype = { - parse: function CFFParser_parse() { + parse: function CFFParser_parse(normalizeCIDData) { var properties = this.properties; var cff = new CFF(); this.cff = cff; @@ -16987,6 +18376,21 @@ var CFFParser = (function CFFParserClosure() { cff.charset = charset; cff.encoding = encoding; + if (!cff.isCIDFont || !normalizeCIDData) + return cff; + + // DirectWrite does not like CID fonts data. Trying to convert/flatten + // the font data and remove CID properties. + if (cff.fdArray.length !== 1) + error('Unable to normalize CID font in CFF data'); + + var fontDict = cff.fdArray[0]; + fontDict.setByKey(17, topDict.getByName('CharStrings')); + cff.topDict = fontDict; + cff.isCIDFont = false; + delete cff.fdArray; + delete cff.fdSelect; + return cff; }, parseHeader: function CFFParser_parseHeader() { @@ -16997,7 +18401,7 @@ var CFFParser = (function CFFParserClosure() { ++offset; if (offset != 0) { - warn('cff data is shifted'); + info('cff data is shifted'); bytes = bytes.subarray(offset); this.bytes = bytes; } @@ -17585,9 +18989,9 @@ var CFFPrivateDict = (function CFFPrivateDictClosure() { [[12, 17], 'LanguageGroup', 'num', 0], [[12, 18], 'ExpansionFactor', 'num', 0.06], [[12, 19], 'initialRandomSeed', 'num', 0], - [19, 'Subrs', 'offset', null], [20, 'defaultWidthX', 'num', 0], - [21, 'nominalWidthX', 'num', 0] + [21, 'nominalWidthX', 'num', 0], + [19, 'Subrs', 'offset', null] ]; var tables = null; function CFFPrivateDict(strings) { @@ -18045,6 +19449,7 @@ var CFFCompiler = (function CFFCompilerClosure() { return CFFCompiler; })(); + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -22257,6 +23662,7 @@ var GlyphsUnicode = { '.notdef': 0x0000 }; + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -22274,7 +23680,7 @@ var PDFImage = (function PDFImageClosure() { var colorSpace = dict.get('ColorSpace', 'CS'); colorSpace = ColorSpace.parse(colorSpace, xref, res); var numComps = colorSpace.numComps; - handler.send('jpeg_decode', [image.getIR(), numComps], function(message) { + handler.send('JpegDecode', [image.getIR(), numComps], function(message) { var data = message.data; var stream = new Stream(data, 0, data.length, image.dict); promise.resolve(stream); @@ -22632,6 +24038,7 @@ function loadJpegStream(id, imageData, objs) { img.src = 'data:image/jpeg;base64,' + window.btoa(imageData); } + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -25579,6 +26986,7 @@ var Metrics = { } }; + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -25830,9 +27238,12 @@ var Parser = (function ParserClosure() { if (name == 'CCITTFaxDecode' || name == 'CCF') { return new CCITTFaxStream(stream, params); } - if (name == 'RunLengthDecode') { + if (name == 'RunLengthDecode' || name == 'RL') { return new RunLengthStream(stream); } + if (name == 'JBIG2Decode') { + error('JBIG2 image format is not currently supprted.'); + } warn('filter "' + name + '" not supported yet'); return stream; } @@ -25842,8 +27253,16 @@ var Parser = (function ParserClosure() { })(); var Lexer = (function LexerClosure() { - function Lexer(stream) { + function Lexer(stream, knownCommands) { this.stream = stream; + // The PDFs might have "glued" commands with other commands, operands or + // literals, e.g. "q1". The knownCommands is a dictionary of the valid + // commands and their prefixes. The prefixes are built the following way: + // if there a command that is a prefix of the other valid command or + // literal (e.g. 'f' and 'false') the following prefixes must be included, + // 'fa', 'fal', 'fals'. The prefixes are not needed, if the command has no + // other commands or literals as a prefix. The knowCommands is optional. + this.knownCommands = knownCommands; } Lexer.isSpace = function Lexer_isSpace(ch) { @@ -26107,12 +27526,18 @@ var Lexer = (function LexerClosure() { // command var str = ch; + var knownCommands = this.knownCommands; + var knownCommandFound = knownCommands && (str in knownCommands); while (!!(ch = stream.lookChar()) && !specialChars[ch.charCodeAt(0)]) { + // stop if known command is found and next character does not make + // the str a command + if (knownCommandFound && !((str + ch) in knownCommands)) + break; stream.skip(); if (str.length == 128) error('Command token too long: ' + str.length); - str += ch; + knownCommandFound = knownCommands && (str in knownCommands); } if (str == 'true') return true; @@ -26218,6 +27643,7 @@ var Linearization = (function LinearizationClosure() { return Linearization; })(); + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -26330,7 +27756,7 @@ Shadings.RadialAxial = (function RadialAxialClosure() { var r1 = raw[6]; return { type: 'Pattern', - getPattern: function(ctx) { + getPattern: function RadialAxial_getPattern(ctx) { var curMatrix = ctx.mozCurrentTransform; if (curMatrix) { var userMatrix = ctx.mozCurrentTransformInverse; @@ -26521,6 +27947,7 @@ var TilingPattern = (function TilingPatternClosure() { return TilingPattern; })(); + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -28210,7 +29637,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() { if (a1 > codingLine[codingPos]) { if (a1 > this.columns) { - warn('row is wrong length'); + info('row is wrong length'); this.err = true; a1 = this.columns; } @@ -28230,7 +29657,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() { if (a1 > codingLine[codingPos]) { if (a1 > this.columns) { - warn('row is wrong length'); + info('row is wrong length'); this.err = true; a1 = this.columns; } @@ -28240,7 +29667,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() { codingLine[codingPos] = a1; } else if (a1 < codingLine[codingPos]) { if (a1 < 0) { - warn('invalid code'); + info('invalid code'); this.err = true; a1 = 0; } @@ -28402,7 +29829,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() { this.eof = true; break; default: - warn('bad 2d code'); + info('bad 2d code'); this.addPixels(columns, 0); this.err = true; } @@ -28465,7 +29892,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() { for (var i = 0; i < 4; ++i) { code1 = this.lookBits(12); if (code1 != 1) - warn('bad rtc code: ' + code1); + info('bad rtc code: ' + code1); this.eatBits(12); if (this.encoding > 0) { this.lookBits(1); @@ -28588,7 +30015,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() { if (result[0] && result[2]) return result[1]; } - warn('Bad two dim code'); + info('Bad two dim code'); return EOF; }; @@ -28621,7 +30048,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() { if (result[0]) return result[1]; } - warn('bad white code'); + info('bad white code'); this.eatBits(1); return 1; }; @@ -28658,7 +30085,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() { if (result[0]) return result[1]; } - warn('bad black code'); + info('bad black code'); this.eatBits(1); return 1; }; @@ -28815,6 +30242,7 @@ var LZWStream = (function LZWStreamClosure() { return LZWStream; })(); + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -28828,10 +30256,13 @@ function MessageHandler(name, comObj) { var ah = this.actionHandler = {}; ah['console_log'] = [function ahConsoleLog(data) { - console.log.apply(console, data); + console.log.apply(console, data); }]; ah['console_error'] = [function ahConsoleError(data) { - console.error.apply(console, data); + console.error.apply(console, data); + }]; + ah['_warn'] = [function ah_Warn(data) { + warn(data); }]; comObj.onmessage = function messageHandlerComObjOnMessage(event) { @@ -28902,15 +30333,68 @@ var WorkerMessageHandler = { handler.send('test', data instanceof Uint8Array); }); - handler.on('doc', function wphSetupDoc(data) { + handler.on('GetDocRequest', function wphSetupDoc(data) { // Create only the model of the PDFDoc, which is enough for // processing the content of the pdf. - pdfModel = new PDFDocModel(new Stream(data)); + var pdfData = data.data; + var pdfPassword = data.params.password; + try { + pdfModel = new PDFDocument(new Stream(pdfData), pdfPassword); + } catch (e) { + if (e instanceof PasswordException) { + if (e.code === 'needpassword') { + handler.send('NeedPassword', { + exception: e + }); + } else if (e.code === 'incorrectpassword') { + handler.send('IncorrectPassword', { + exception: e + }); + } + + return; + } else { + throw e; + } + } + var doc = { + numPages: pdfModel.numPages, + fingerprint: pdfModel.getFingerprint(), + destinations: pdfModel.catalog.destinations, + outline: pdfModel.catalog.documentOutline, + info: pdfModel.getDocumentInfo(), + metadata: pdfModel.catalog.metadata, + encrypted: !!pdfModel.xref.encrypt + }; + handler.send('GetDoc', {pdfInfo: doc}); }); - handler.on('page_request', function wphSetupPageRequest(pageNum) { - pageNum = parseInt(pageNum); + handler.on('GetPageRequest', function wphSetupGetPage(data) { + var pageNumber = data.pageIndex + 1; + var pdfPage = pdfModel.getPage(pageNumber); + var page = { + pageIndex: data.pageIndex, + rotate: pdfPage.rotate, + ref: pdfPage.ref, + view: pdfPage.view + }; + handler.send('GetPage', {pageInfo: page}); + }); + + handler.on('GetData', function wphSetupGetData(data, promise) { + promise.resolve(pdfModel.stream.bytes); + }); + + handler.on('GetAnnotationsRequest', function wphSetupGetAnnotations(data) { + var pdfPage = pdfModel.getPage(data.pageIndex + 1); + handler.send('GetAnnotations', { + pageIndex: data.pageIndex, + annotations: pdfPage.getAnnotations() + }); + }); + handler.on('RenderPageRequest', function wphSetupRenderPage(data) { + var pageNum = data.pageIndex + 1; // The following code does quite the same as // Page.prototype.startRendering, but stops at one point and sends the @@ -28947,7 +30431,7 @@ var WorkerMessageHandler = { }; } - handler.send('page_error', { + handler.send('PageError', { pageNum: pageNum, error: e }); @@ -28965,13 +30449,30 @@ var WorkerMessageHandler = { fonts[dep] = true; } } - - handler.send('page', { - pageNum: pageNum, + handler.send('RenderPage', { + pageIndex: data.pageIndex, operatorList: operatorList, depFonts: Object.keys(fonts) }); }, this); + + handler.on('GetTextContent', function wphExtractText(data, promise) { + var pageNum = data.pageIndex + 1; + var start = Date.now(); + + var textContent = ''; + try { + var page = pdfModel.getPage(pageNum); + textContent = page.extractTextContent(); + promise.resolve(textContent); + } catch (e) { + // Skip errored pages + promise.reject(e); + } + + console.log('text indexing: page=%d - time=%dms', + pageNum, Date.now() - start); + }); } }; @@ -29012,10 +30513,22 @@ var workerConsole = { if (typeof window === 'undefined') { globalScope.console = workerConsole; + // Add a logger so we can pass warnings on to the main thread, errors will + // throw an exception which will be forwarded on automatically. + PDFJS.LogManager.addLogger({ + warn: function(msg) { + postMessage({ + action: '_warn', + data: msg + }); + } + }); + var handler = new MessageHandler('worker_processor', this); WorkerMessageHandler.setup(handler); } + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- / /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -29655,7 +31168,10 @@ var JpegImage = (function jpegImage() { tableData[z] = data[offset++]; } } else if ((quantizationTableSpec >> 4) === 1) { //16 bit - tableData[j] = readUint16(); + for (j = 0; j < 64; j++) { + var z = dctZigZag[j]; + tableData[z] = readUint16(); + } } else throw "DQT: invalid table spec"; quantizationTables[quantizationTableSpec & 15] = tableData; @@ -29670,7 +31186,8 @@ var JpegImage = (function jpegImage() { frame.precision = data[offset++]; frame.scanLines = readUint16(); frame.samplesPerLine = readUint16(); - frame.components = []; + frame.components = {}; + frame.componentsOrder = []; var componentsCount = data[offset++], componentId; var maxH = 0, maxV = 0; for (i = 0; i < componentsCount; i++) { @@ -29678,6 +31195,7 @@ var JpegImage = (function jpegImage() { var h = data[offset + 1] >> 4; var v = data[offset + 1] & 15; var qId = data[offset + 2]; + frame.componentsOrder.push(componentId); frame.components[componentId] = { h: h, v: v, @@ -29746,14 +31264,13 @@ var JpegImage = (function jpegImage() { this.jfif = jfif; this.adobe = adobe; this.components = []; - for (var id in frame.components) { - if (frame.components.hasOwnProperty(id)) { - this.components.push({ - lines: buildComponentData(frame, frame.components[id]), - scaleX: frame.components[id].h / frame.maxH, - scaleY: frame.components[id].v / frame.maxV - }); - } + for (var i = 0; i < frame.componentsOrder.length; i++) { + var component = frame.components[frame.componentsOrder[i]]; + this.components.push({ + lines: buildComponentData(frame, component), + scaleX: component.h / frame.maxH, + scaleY: component.v / frame.maxV + }); } }, getData: function getData(width, height) { @@ -29926,7 +31443,8 @@ var JpegImage = (function jpegImage() { }; return constructor; -})();/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +})(); +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ 'use strict'; @@ -31788,6 +33306,7 @@ var JpxImage = (function JpxImageClosure() { return JpxImage; })(); + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -32220,14 +33739,35 @@ var bidi = PDFJS.bidi = (function bidiClosure() { return bidi; })(); + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ 'use strict'; var Metadata = PDFJS.Metadata = (function MetadataClosure() { + function fixMetadata(meta) { + return meta.replace(/>\\376\\377([^<]+)/g, function(all, codes) { + var bytes = codes.replace(/\\([0-3])([0-7])([0-7])/g, + function(code, d1, d2, d3) { + return String.fromCharCode(d1 * 64 + d2 * 8 + d3 * 1); + }); + var chars = ''; + for (var i = 0; i < bytes.length; i += 2) { + var code = bytes.charCodeAt(i) * 256 + bytes.charCodeAt(i + 1); + chars += code >= 32 && code < 127 && code != 60 && code != 62 && + code != 38 && false ? String.fromCharCode(code) : + '&#x' + (0x10000 + code).toString(16).substring(1) + ';'; + } + return '>' + chars; + }); + } + function Metadata(meta) { if (typeof meta === 'string') { + // Ghostscript produces invalid metadata + meta = fixMetadata(meta); + var parser = new DOMParser(); meta = parser.parseFromString(meta, 'application/xml'); } else if (!(meta instanceof Document)) { diff --git a/apps/files_pdfviewer/js/pdfjs/compatibility.js b/apps/files_pdfviewer/js/pdfjs/compatibility.js new file mode 100644 index 00000000000..528841bb614 --- /dev/null +++ b/apps/files_pdfviewer/js/pdfjs/compatibility.js @@ -0,0 +1,340 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ + +'use strict'; + +// Checking if the typed arrays are supported +(function checkTypedArrayCompatibility() { + if (typeof Uint8Array !== 'undefined') { + // some mobile version might not support Float64Array + if (typeof Float64Array === 'undefined') + window.Float64Array = Float32Array; + + return; + } + + function subarray(start, end) { + return new TypedArray(this.slice(start, end)); + } + + function setArrayOffset(array, offset) { + if (arguments.length < 2) + offset = 0; + for (var i = 0, n = array.length; i < n; ++i, ++offset) + this[offset] = array[i] & 0xFF; + } + + function TypedArray(arg1) { + var result; + if (typeof arg1 === 'number') { + result = []; + for (var i = 0; i < arg1; ++i) + result[i] = 0; + } else + result = arg1.slice(0); + + result.subarray = subarray; + result.buffer = result; + result.byteLength = result.length; + result.set = setArrayOffset; + + if (typeof arg1 === 'object' && arg1.buffer) + result.buffer = arg1.buffer; + + return result; + } + + window.Uint8Array = TypedArray; + + // we don't need support for set, byteLength for 32-bit array + // so we can use the TypedArray as well + window.Uint32Array = TypedArray; + window.Int32Array = TypedArray; + window.Uint16Array = TypedArray; + window.Float32Array = TypedArray; + window.Float64Array = TypedArray; +})(); + +// Object.create() ? +(function checkObjectCreateCompatibility() { + if (typeof Object.create !== 'undefined') + return; + + Object.create = function objectCreate(proto) { + var constructor = function objectCreateConstructor() {}; + constructor.prototype = proto; + return new constructor(); + }; +})(); + +// Object.defineProperty() ? +(function checkObjectDefinePropertyCompatibility() { + if (typeof Object.defineProperty !== 'undefined') + return; + + Object.defineProperty = function objectDefineProperty(obj, name, def) { + delete obj[name]; + if ('get' in def) + obj.__defineGetter__(name, def['get']); + if ('set' in def) + obj.__defineSetter__(name, def['set']); + if ('value' in def) { + obj.__defineSetter__(name, function objectDefinePropertySetter(value) { + this.__defineGetter__(name, function objectDefinePropertyGetter() { + return value; + }); + return value; + }); + obj[name] = def.value; + } + }; +})(); + +// Object.keys() ? +(function checkObjectKeysCompatibility() { + if (typeof Object.keys !== 'undefined') + return; + + Object.keys = function objectKeys(obj) { + var result = []; + for (var i in obj) { + if (obj.hasOwnProperty(i)) + result.push(i); + } + return result; + }; +})(); + +// No XMLHttpRequest.response ? +(function checkXMLHttpRequestResponseCompatibility() { + var xhrPrototype = XMLHttpRequest.prototype; + if ('response' in xhrPrototype || + 'mozResponseArrayBuffer' in xhrPrototype || + 'mozResponse' in xhrPrototype || + 'responseArrayBuffer' in xhrPrototype) + return; + // IE ? + if (typeof VBArray !== 'undefined') { + Object.defineProperty(xhrPrototype, 'response', { + get: function xmlHttpRequestResponseGet() { + return new Uint8Array(new VBArray(this.responseBody).toArray()); + } + }); + Object.defineProperty(xhrPrototype, 'overrideMimeType', { + value: function xmlHttpRequestOverrideMimeType(mimeType) {} + }); + return; + } + + // other browsers + function responseTypeSetter() { + // will be only called to set "arraybuffer" + this.overrideMimeType('text/plain; charset=x-user-defined'); + } + if (typeof xhrPrototype.overrideMimeType === 'function') { + Object.defineProperty(xhrPrototype, 'responseType', + { set: responseTypeSetter }); + } + function responseGetter() { + var text = this.responseText; + var i, n = text.length; + var result = new Uint8Array(n); + for (i = 0; i < n; ++i) + result[i] = text.charCodeAt(i) & 0xFF; + return result; + } + Object.defineProperty(xhrPrototype, 'response', { get: responseGetter }); +})(); + +// window.btoa (base64 encode function) ? +(function checkWindowBtoaCompatibility() { + if ('btoa' in window) + return; + + var digits = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; + + window.btoa = function windowBtoa(chars) { + var buffer = ''; + var i, n; + for (i = 0, n = chars.length; i < n; i += 3) { + var b1 = chars.charCodeAt(i) & 0xFF; + var b2 = chars.charCodeAt(i + 1) & 0xFF; + var b3 = chars.charCodeAt(i + 2) & 0xFF; + var d1 = b1 >> 2, d2 = ((b1 & 3) << 4) | (b2 >> 4); + var d3 = i + 1 < n ? ((b2 & 0xF) << 2) | (b3 >> 6) : 64; + var d4 = i + 2 < n ? (b3 & 0x3F) : 64; + buffer += (digits.charAt(d1) + digits.charAt(d2) + + digits.charAt(d3) + digits.charAt(d4)); + } + return buffer; + }; +})(); + +// Function.prototype.bind ? +(function checkFunctionPrototypeBindCompatibility() { + if (typeof Function.prototype.bind !== 'undefined') + return; + + Function.prototype.bind = function functionPrototypeBind(obj) { + var fn = this, headArgs = Array.prototype.slice.call(arguments, 1); + var bound = function functionPrototypeBindBound() { + var args = Array.prototype.concat.apply(headArgs, arguments); + return fn.apply(obj, args); + }; + return bound; + }; +})(); + +// IE9 text/html data URI +(function checkDocumentDocumentModeCompatibility() { + if (!('documentMode' in document) || document.documentMode !== 9) + return; + // overriding the src property + var originalSrcDescriptor = Object.getOwnPropertyDescriptor( + HTMLIFrameElement.prototype, 'src'); + Object.defineProperty(HTMLIFrameElement.prototype, 'src', { + get: function htmlIFrameElementPrototypeSrcGet() { return this.$src; }, + set: function htmlIFrameElementPrototypeSrcSet(src) { + this.$src = src; + if (src.substr(0, 14) != 'data:text/html') { + originalSrcDescriptor.set.call(this, src); + return; + } + // for text/html, using blank document and then + // document's open, write, and close operations + originalSrcDescriptor.set.call(this, 'about:blank'); + setTimeout((function htmlIFrameElementPrototypeSrcOpenWriteClose() { + var doc = this.contentDocument; + doc.open('text/html'); + doc.write(src.substr(src.indexOf(',') + 1)); + doc.close(); + }).bind(this), 0); + }, + enumerable: true + }); +})(); + +// HTMLElement dataset property +(function checkDatasetProperty() { + var div = document.createElement('div'); + if ('dataset' in div) + return; // dataset property exists + + Object.defineProperty(HTMLElement.prototype, 'dataset', { + get: function() { + if (this._dataset) + return this._dataset; + + var dataset = {}; + for (var j = 0, jj = this.attributes.length; j < jj; j++) { + var attribute = this.attributes[j]; + if (attribute.name.substring(0, 5) != 'data-') + continue; + var key = attribute.name.substring(5).replace(/\-([a-z])/g, + function(all, ch) { return ch.toUpperCase(); }); + dataset[key] = attribute.value; + } + + Object.defineProperty(this, '_dataset', { + value: dataset, + writable: false, + enumerable: false + }); + return dataset; + }, + enumerable: true + }); +})(); + +// HTMLElement classList property +(function checkClassListProperty() { + var div = document.createElement('div'); + if ('classList' in div) + return; // classList property exists + + function changeList(element, itemName, add, remove) { + var s = element.className || ''; + var list = s.split(/\s+/g); + if (list[0] == '') list.shift(); + var index = list.indexOf(itemName); + if (index < 0 && add) + list.push(itemName); + if (index >= 0 && remove) + list.splice(index, 1); + element.className = list.join(' '); + } + + var classListPrototype = { + add: function(name) { + changeList(this.element, name, true, false); + }, + remove: function(name) { + changeList(this.element, name, false, true); + }, + toggle: function(name) { + changeList(this.element, name, true, true); + } + }; + + Object.defineProperty(HTMLElement.prototype, 'classList', { + get: function() { + if (this._classList) + return this._classList; + + var classList = Object.create(classListPrototype, { + element: { + value: this, + writable: false, + enumerable: true + } + }); + Object.defineProperty(this, '_classList', { + value: classList, + writable: false, + enumerable: false + }); + return classList; + }, + enumerable: true + }); +})(); + +// Check console compatability +(function checkConsoleCompatibility() { + if (typeof console == 'undefined') { + console = {log: function() {}}; + } +})(); + +// Check onclick compatibility in Opera +(function checkOnClickCompatibility() { + // workaround for reported Opera bug DSK-354448: + // onclick fires on disabled buttons with opaque content + function ignoreIfTargetDisabled(event) { + if (isDisabled(event.target)) { + event.stopPropagation(); + } + } + function isDisabled(node) { + return node.disabled || (node.parentNode && isDisabled(node.parentNode)); + } + if (navigator.userAgent.indexOf('Opera') != -1) { + // use browser detection since we cannot feature-check this bug + document.addEventListener('click', ignoreIfTargetDisabled, true); + } +})(); + +// Checks if navigator.language is supported +(function checkNavigatorLanguage() { + if ('language' in navigator) + return; + Object.defineProperty(navigator, 'language', { + get: function navigatorLanguage() { + var language = navigator.userLanguage || 'en-US'; + return language.substring(0, 2).toLowerCase() + + language.substring(2).toUpperCase(); + }, + enumerable: true + }); +})(); diff --git a/apps/files_pdfviewer/js/pdfjs/update.sh b/apps/files_pdfviewer/js/pdfjs/update.sh index 599f6338602..df100e780ba 100755 --- a/apps/files_pdfviewer/js/pdfjs/update.sh +++ b/apps/files_pdfviewer/js/pdfjs/update.sh @@ -1,3 +1,6 @@ cd build rm pdf.js wget http://mozilla.github.com/pdf.js/build/pdf.js +wget http://mozilla.github.com/pdf.js/web/compatibility.js +wget http://mozilla.github.com/pdf.js/web/viewer.js + diff --git a/apps/files_pdfviewer/js/pdfjs/viewer.js b/apps/files_pdfviewer/js/pdfjs/viewer.js new file mode 100644 index 00000000000..90dd1eef020 --- /dev/null +++ b/apps/files_pdfviewer/js/pdfjs/viewer.js @@ -0,0 +1,1873 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ + +'use strict'; + +var kDefaultURL = 'compressed.tracemonkey-pldi-09.pdf'; +var kDefaultScale = 'auto'; +var kDefaultScaleDelta = 1.1; +var kUnknownScale = 0; +var kCacheSize = 20; +var kCssUnits = 96.0 / 72.0; +var kScrollbarPadding = 40; +var kMinScale = 0.25; +var kMaxScale = 4.0; +var kImageDirectory = './images/'; +var kSettingsMemory = 20; + +var mozL10n = document.mozL10n || document.webL10n; + +function getFileName(url) { + var anchor = url.indexOf('#'); + var query = url.indexOf('?'); + var end = Math.min( + anchor > 0 ? anchor : url.length, + query > 0 ? query : url.length); + return url.substring(url.lastIndexOf('/', end) + 1, end); +} + +var Cache = function cacheCache(size) { + var data = []; + this.push = function cachePush(view) { + var i = data.indexOf(view); + if (i >= 0) + data.splice(i); + data.push(view); + if (data.length > size) + data.shift().destroy(); + }; +}; + +var ProgressBar = (function ProgressBarClosure() { + + function clamp(v, min, max) { + return Math.min(Math.max(v, min), max); + } + + function ProgressBar(id, opts) { + + // Fetch the sub-elements for later + this.div = document.querySelector(id + ' .progress'); + + // Get options, with sensible defaults + this.height = opts.height || 100; + this.width = opts.width || 100; + this.units = opts.units || '%'; + this.percent = opts.percent || 0; + + // Initialize heights + this.div.style.height = this.height + this.units; + } + + ProgressBar.prototype = { + + updateBar: function ProgressBar_updateBar() { + var progressSize = this.width * this._percent / 100; + + if (this._percent > 95) + this.div.classList.add('full'); + + this.div.style.width = progressSize + this.units; + }, + + get percent() { + return this._percent; + }, + + set percent(val) { + this._percent = clamp(val, 0, 100); + this.updateBar(); + } + }; + + return ProgressBar; +})(); + +var RenderingQueue = (function RenderingQueueClosure() { + function RenderingQueue() { + this.items = []; + } + + RenderingQueue.prototype = { + enqueueDraw: function RenderingQueueEnqueueDraw(item) { + if (!item.drawingRequired()) + return; // as no redraw required, no need for queueing. + + this.items.push(item); + if (this.items.length > 1) + return; // not first item + + item.draw(this.continueExecution.bind(this)); + }, + continueExecution: function RenderingQueueContinueExecution() { + var item = this.items.shift(); + + if (this.items.length == 0) + return; // queue is empty + + item = this.items[0]; + item.draw(this.continueExecution.bind(this)); + } + }; + + return RenderingQueue; +})(); + +var FirefoxCom = (function FirefoxComClosure() { + return { + /** + * Creates an event that the extension is listening for and will + * synchronously respond to. + * NOTE: It is reccomended to use request() instead since one day we may not + * be able to synchronously reply. + * @param {String} action The action to trigger. + * @param {String} data Optional data to send. + * @return {*} The response. + */ + requestSync: function(action, data) { + var request = document.createTextNode(''); + request.setUserData('action', action, null); + request.setUserData('data', data, null); + request.setUserData('sync', true, null); + document.documentElement.appendChild(request); + + var sender = document.createEvent('Events'); + sender.initEvent('pdf.js.message', true, false); + request.dispatchEvent(sender); + var response = request.getUserData('response'); + document.documentElement.removeChild(request); + return response; + }, + /** + * Creates an event that the extension is listening for and will + * asynchronously respond by calling the callback. + * @param {String} action The action to trigger. + * @param {String} data Optional data to send. + * @param {Function} callback Optional response callback that will be called + * with one data argument. + */ + request: function(action, data, callback) { + var request = document.createTextNode(''); + request.setUserData('action', action, null); + request.setUserData('data', data, null); + request.setUserData('sync', false, null); + if (callback) { + request.setUserData('callback', callback, null); + + document.addEventListener('pdf.js.response', function listener(event) { + var node = event.target, + callback = node.getUserData('callback'), + response = node.getUserData('response'); + + document.documentElement.removeChild(node); + + document.removeEventListener('pdf.js.response', listener, false); + return callback(response); + }, false); + } + document.documentElement.appendChild(request); + + var sender = document.createEvent('HTMLEvents'); + sender.initEvent('pdf.js.message', true, false); + return request.dispatchEvent(sender); + } + }; +})(); + +// Settings Manager - This is a utility for saving settings +// First we see if localStorage is available +// If not, we use FUEL in FF +var Settings = (function SettingsClosure() { + var isLocalStorageEnabled = (function localStorageEnabledTest() { + // Feature test as per http://diveintohtml5.info/storage.html + // The additional localStorage call is to get around a FF quirk, see + // bug #495747 in bugzilla + try { + return 'localStorage' in window && window['localStorage'] !== null && + localStorage; + } catch (e) { + return false; + } + })(); + + var isFirefoxExtension = PDFJS.isFirefoxExtension; + + function Settings(fingerprint) { + var database = null; + var index; + if (isFirefoxExtension) + database = FirefoxCom.requestSync('getDatabase', null) || '{}'; + else if (isLocalStorageEnabled) + database = localStorage.getItem('database') || '{}'; + else + return false; + + database = JSON.parse(database); + if (!('files' in database)) + database.files = []; + if (database.files.length >= kSettingsMemory) + database.files.shift(); + for (var i = 0, length = database.files.length; i < length; i++) { + var branch = database.files[i]; + if (branch.fingerprint == fingerprint) { + index = i; + break; + } + } + if (typeof index != 'number') + index = database.files.push({fingerprint: fingerprint}) - 1; + this.file = database.files[index]; + this.database = database; + } + + Settings.prototype = { + set: function settingsSet(name, val) { + if (!('file' in this)) + return false; + + var file = this.file; + file[name] = val; + var database = JSON.stringify(this.database); + if (isFirefoxExtension) + FirefoxCom.requestSync('setDatabase', database); + else if (isLocalStorageEnabled) + localStorage.setItem('database', database); + }, + + get: function settingsGet(name, defaultValue) { + if (!('file' in this)) + return defaultValue; + + return this.file[name] || defaultValue; + } + }; + + return Settings; +})(); + +var cache = new Cache(kCacheSize); +var renderingQueue = new RenderingQueue(); +var currentPageNumber = 1; + +var PDFView = { + pages: [], + thumbnails: [], + currentScale: kUnknownScale, + currentScaleValue: null, + initialBookmark: document.location.hash.substring(1), + startedTextExtraction: false, + pageText: [], + container: null, + initialized: false, + fellback: false, + pdfDocument: null, + // called once when the document is loaded + initialize: function pdfViewInitialize() { + this.container = document.getElementById('viewer'); + this.initialized = true; + }, + + setScale: function pdfViewSetScale(val, resetAutoSettings) { + if (val == this.currentScale) + return; + + var pages = this.pages; + for (var i = 0; i < pages.length; i++) + pages[i].update(val * kCssUnits); + + if (this.currentScale != val) + this.pages[this.page - 1].scrollIntoView(); + this.currentScale = val; + + var event = document.createEvent('UIEvents'); + event.initUIEvent('scalechange', false, false, window, 0); + event.scale = val; + event.resetAutoSettings = resetAutoSettings; + window.dispatchEvent(event); + }, + + parseScale: function pdfViewParseScale(value, resetAutoSettings) { + if ('custom' == value) + return; + + var scale = parseFloat(value); + this.currentScaleValue = value; + if (scale) { + this.setScale(scale, true); + return; + } + + var container = this.container; + var currentPage = this.pages[this.page - 1]; + var pageWidthScale = (container.clientWidth - kScrollbarPadding) / + currentPage.width * currentPage.scale / kCssUnits; + var pageHeightScale = (container.clientHeight - kScrollbarPadding) / + currentPage.height * currentPage.scale / kCssUnits; + switch (value) { + case 'page-actual': + this.setScale(1, resetAutoSettings); + break; + case 'page-width': + this.setScale(pageWidthScale, resetAutoSettings); + break; + case 'page-height': + this.setScale(pageHeightScale, resetAutoSettings); + break; + case 'page-fit': + this.setScale( + Math.min(pageWidthScale, pageHeightScale), resetAutoSettings); + break; + case 'auto': + this.setScale(Math.min(1.0, pageWidthScale), resetAutoSettings); + break; + } + + selectScaleOption(value); + }, + + zoomIn: function pdfViewZoomIn() { + var newScale = (this.currentScale * kDefaultScaleDelta).toFixed(2); + newScale = Math.min(kMaxScale, newScale); + this.parseScale(newScale, true); + }, + + zoomOut: function pdfViewZoomOut() { + var newScale = (this.currentScale / kDefaultScaleDelta).toFixed(2); + newScale = Math.max(kMinScale, newScale); + this.parseScale(newScale, true); + }, + + set page(val) { + var pages = this.pages; + var input = document.getElementById('pageNumber'); + if (!(0 < val && val <= pages.length)) { + var event = document.createEvent('UIEvents'); + event.initUIEvent('pagechange', false, false, window, 0); + event.pageNumber = this.page; + window.dispatchEvent(event); + return; + } + + pages[val - 1].updateStats(); + currentPageNumber = val; + var event = document.createEvent('UIEvents'); + event.initUIEvent('pagechange', false, false, window, 0); + event.pageNumber = val; + window.dispatchEvent(event); + + // checking if the this.page was called from the updateViewarea function: + // avoiding the creation of two "set page" method (internal and public) + if (updateViewarea.inProgress) + return; + + // Avoid scrolling the first page during loading + if (this.loading && val == 1) + return; + + pages[val - 1].scrollIntoView(); + }, + + get page() { + return currentPageNumber; + }, + + open: function pdfViewOpen(url, scale, password) { + var parameters = {password: password}; + if (typeof url === 'string') { // URL + this.url = url; + document.title = decodeURIComponent(getFileName(url)) || url; + parameters.url = url; + } else if (url && 'byteLength' in url) { // ArrayBuffer + parameters.data = url; + } + +// if (!PDFView.loadingBar) { +// PDFView.loadingBar = new ProgressBar('#loadingBar', {}); +// } + + this.container = document.getElementById('viewer'); + + this.pdfDocument = null; + var self = this; + self.loading = true; + PDFJS.getDocument(parameters).then( + function getDocumentCallback(pdfDocument) { + self.load(pdfDocument, scale); + self.loading = false; + }, + function getDocumentError(message, exception) { + if (exception.name === 'PasswordException') { + if (exception.code === 'needpassword') { + var promptString = mozL10n.get('request_password', null, + 'PDF is protected by a password:'); + password = prompt(promptString); + if (password && password.length > 0) { + return PDFView.open(url, scale, password); + } + } + } + + var loadingIndicator = document.getElementById('loading'); + loadingIndicator.textContent = mozL10n.get('loading_error_indicator', + null, 'Error'); + var moreInfo = { + message: message + }; + self.error(mozL10n.get('loading_error', null, + 'An error occurred while loading the PDF.'), moreInfo); + self.loading = false; + }, + function getDocumentProgress(progressData) { + self.progress(progressData.loaded / progressData.total); + } + ); + }, + + download: function pdfViewDownload() { + function noData() { + FirefoxCom.request('download', { originalUrl: url }); + } + + var url = this.url.split('#')[0]; + if (PDFJS.isFirefoxExtension) { + // Document isn't ready just try to download with the url. + if (!this.pdfDocument) { + noData(); + return; + } + this.pdfDocument.getData().then( + function getDataSuccess(data) { + var bb = new MozBlobBuilder(); + bb.append(data.buffer); + var blobUrl = window.URL.createObjectURL( + bb.getBlob('application/pdf')); + + FirefoxCom.request('download', { blobUrl: blobUrl, originalUrl: url }, + function response(err) { + if (err) { + // This error won't really be helpful because it's likely the + // fallback won't work either (or is already open). + PDFView.error('PDF failed to download.'); + } + window.URL.revokeObjectURL(blobUrl); + } + ); + }, + noData // Error ocurred try downloading with just the url. + ); + } else { + url += '#pdfjs.action=download', '_parent'; + window.open(url, '_parent'); + } + }, + + fallback: function pdfViewFallback() { + if (!PDFJS.isFirefoxExtension) + return; + // Only trigger the fallback once so we don't spam the user with messages + // for one PDF. + if (this.fellback) + return; + this.fellback = true; + var url = this.url.split('#')[0]; + FirefoxCom.request('fallback', url, function response(download) { + if (!download) + return; + PDFView.download(); + }); + }, + + navigateTo: function pdfViewNavigateTo(dest) { + if (typeof dest === 'string') + dest = this.destinations[dest]; + if (!(dest instanceof Array)) + return; // invalid destination + // dest array looks like that: <page-ref> </XYZ|FitXXX> <args..> + var destRef = dest[0]; + var pageNumber = destRef instanceof Object ? + this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] : (destRef + 1); + if (pageNumber > this.pages.length) + pageNumber = this.pages.length; + if (pageNumber) { + this.page = pageNumber; + var currentPage = this.pages[pageNumber - 1]; + currentPage.scrollIntoView(dest); + } + }, + + getDestinationHash: function pdfViewGetDestinationHash(dest) { + if (typeof dest === 'string') + return PDFView.getAnchorUrl('#' + escape(dest)); + if (dest instanceof Array) { + var destRef = dest[0]; // see navigateTo method for dest format + var pageNumber = destRef instanceof Object ? + this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] : + (destRef + 1); + if (pageNumber) { + var pdfOpenParams = PDFView.getAnchorUrl('#page=' + pageNumber); + var destKind = dest[1]; + if (typeof destKind === 'object' && 'name' in destKind && + destKind.name == 'XYZ') { + var scale = (dest[4] || this.currentScale); + pdfOpenParams += '&zoom=' + (scale * 100); + if (dest[2] || dest[3]) { + pdfOpenParams += ',' + (dest[2] || 0) + ',' + (dest[3] || 0); + } + } + return pdfOpenParams; + } + } + return ''; + }, + + /** + * For the firefox extension we prefix the full url on anchor links so they + * don't come up as resource:// urls and so open in new tab/window works. + * @param {String} anchor The anchor hash include the #. + */ + getAnchorUrl: function getAnchorUrl(anchor) { + if (PDFJS.isFirefoxExtension) + return this.url.split('#')[0] + anchor; + return anchor; + }, + + /** + * Show the error box. + * @param {String} message A message that is human readable. + * @param {Object} moreInfo (optional) Further information about the error + * that is more technical. Should have a 'message' + * and optionally a 'stack' property. + */ + error: function pdfViewError(message, moreInfo) { + var moreInfoText = mozL10n.get('error_build', {build: PDFJS.build}, + 'PDF.JS Build: {{build}}') + '\n'; + if (moreInfo) { + moreInfoText += + mozL10n.get('error_message', {message: moreInfo.message}, + 'Message: {{message}}'); + if (moreInfo.stack) { + moreInfoText += '\n' + + mozL10n.get('error_stack', {stack: moreInfo.stack}, + 'Stack: {{stack}}'); + } else { + if (moreInfo.filename) { + moreInfoText += '\n' + + mozL10n.get('error_file', {file: moreInfo.filename}, + 'File: {{file}}'); + } + if (moreInfo.lineNumber) { + moreInfoText += '\n' + + mozL10n.get('error_line', {line: moreInfo.lineNumber}, + 'Line: {{line}}'); + } + } + } + if (PDFJS.isFirefoxExtension) { + console.error(message + '\n' + moreInfoText); + this.fallback(); + return; + } + var errorWrapper = document.getElementById('errorWrapper'); + errorWrapper.removeAttribute('hidden'); + + var errorMessage = document.getElementById('errorMessage'); + errorMessage.textContent = message; + + var closeButton = document.getElementById('errorClose'); + closeButton.onclick = function() { + errorWrapper.setAttribute('hidden', 'true'); + }; + + var errorMoreInfo = document.getElementById('errorMoreInfo'); + var moreInfoButton = document.getElementById('errorShowMore'); + var lessInfoButton = document.getElementById('errorShowLess'); + moreInfoButton.onclick = function() { + errorMoreInfo.removeAttribute('hidden'); + moreInfoButton.setAttribute('hidden', 'true'); + lessInfoButton.removeAttribute('hidden'); + }; + lessInfoButton.onclick = function() { + errorMoreInfo.setAttribute('hidden', 'true'); + moreInfoButton.removeAttribute('hidden'); + lessInfoButton.setAttribute('hidden', 'true'); + }; + moreInfoButton.removeAttribute('hidden'); + lessInfoButton.setAttribute('hidden', 'true'); + errorMoreInfo.value = moreInfoText; + + errorMoreInfo.rows = moreInfoText.split('\n').length - 1; + }, + + progress: function pdfViewProgress(level) { + var percent = Math.round(level * 100); + var loadingIndicator = document.getElementById('loading'); + loadingIndicator.textContent = mozL10n.get('loading', {percent: percent}, + 'Loading... {{percent}}%'); + +// PDFView.loadingBar.percent = percent; + }, + + load: function pdfViewLoad(pdfDocument, scale) { + function bindOnAfterDraw(pageView, thumbnailView) { + // when page is painted, using the image as thumbnail base + pageView.onAfterDraw = function pdfViewLoadOnAfterDraw() { + thumbnailView.setImage(pageView.canvas); + preDraw(); + }; + } + + this.pdfDocument = pdfDocument; + +// var errorWrapper = document.getElementById('errorWrapper'); +// errorWrapper.setAttribute('hidden', 'true'); + + var loadingBox = document.getElementById('loading'); + loadingBox.setAttribute('hidden', 'true'); + +// var loadingBox = document.getElementById('loadingBox'); +// loadingBox.setAttribute('hidden', 'true'); + +// var thumbsView = document.getElementById('thumbnailView'); +// thumbsView.parentNode.scrollTop = 0; + +// while (thumbsView.hasChildNodes()) +// thumbsView.removeChild(thumbsView.lastChild); + +// if ('_loadingInterval' in thumbsView) +// clearInterval(thumbsView._loadingInterval); + + var container = document.getElementById('viewer'); + while (container.hasChildNodes()) + container.removeChild(container.lastChild); + + var pagesCount = pdfDocument.numPages; + var id = pdfDocument.fingerprint; + var storedHash = null; +// document.getElementById('numPages').textContent = +// mozL10n.get('page_of', {pageCount: pagesCount}, 'of {{pageCount}}'); + document.getElementById('numPages').innerHTML = pagesCount; + document.getElementById('pageNumber').max = pagesCount; + PDFView.documentFingerprint = id; + var store = PDFView.store = new Settings(id); + if (store.get('exists', false)) { + var page = store.get('page', '1'); + var zoom = store.get('zoom', PDFView.currentScale); + var left = store.get('scrollLeft', '0'); + var top = store.get('scrollTop', '0'); + + storedHash = 'page=' + page + '&zoom=' + zoom + ',' + left + ',' + top; + } + + var pages = this.pages = []; + this.pageText = []; + this.startedTextExtraction = false; + var pagesRefMap = {}; + var thumbnails = this.thumbnails = []; + var pagePromises = []; + for (var i = 1; i <= pagesCount; i++) + pagePromises.push(pdfDocument.getPage(i)); + var self = this; + var pagesPromise = PDFJS.Promise.all(pagePromises); + pagesPromise.then(function(promisedPages) { + for (var i = 1; i <= pagesCount; i++) { + var page = promisedPages[i - 1]; + var pageView = new PageView(container, page, i, scale, + page.stats, self.navigateTo.bind(self)); +// var thumbnailView = new ThumbnailView(thumbsView, page, i); +// bindOnAfterDraw(pageView, thumbnailView); + + pages.push(pageView); +// thumbnails.push(thumbnailView); + var pageRef = page.ref; + pagesRefMap[pageRef.num + ' ' + pageRef.gen + ' R'] = i; + } + + self.pagesRefMap = pagesRefMap; + }); + + var destinationsPromise = pdfDocument.getDestinations(); + destinationsPromise.then(function(destinations) { + self.destinations = destinations; + }); + + // outline and initial view depends on destinations and pagesRefMap + PDFJS.Promise.all([pagesPromise, destinationsPromise]).then(function() { + pdfDocument.getOutline().then(function(outline) { + self.outline = new DocumentOutlineView(outline); + }); + + self.setInitialView(storedHash, scale); + }); + + pdfDocument.getMetadata().then(function(data) { + var info = data.info, metadata = data.metadata; + self.documentInfo = info; + self.metadata = metadata; + + var pdfTitle; + if (metadata) { + if (metadata.has('dc:title')) + pdfTitle = metadata.get('dc:title'); + } + + if (!pdfTitle && info && info['Title']) + pdfTitle = info['Title']; + + if (pdfTitle) + document.title = pdfTitle + ' - ' + document.title; + }); + }, + + setInitialView: function pdfViewSetInitialView(storedHash, scale) { + // Reset the current scale, as otherwise the page's scale might not get + // updated if the zoom level stayed the same. + this.currentScale = 0; + this.currentScaleValue = null; + if (this.initialBookmark) { + this.setHash(this.initialBookmark); + this.initialBookmark = null; + } + else if (storedHash) + this.setHash(storedHash); + else if (scale) { + this.parseScale(scale, true); + this.page = 1; + } + + if (PDFView.currentScale === kUnknownScale) { + // Scale was not initialized: invalid bookmark or scale was not specified. + // Setting the default one. + this.parseScale(kDefaultScale, true); + } + }, + + search: function pdfViewStartSearch() { + // Limit this function to run every <SEARCH_TIMEOUT>ms. + var SEARCH_TIMEOUT = 250; + var lastSeach = this.lastSearch; + var now = Date.now(); + if (lastSeach && (now - lastSeach) < SEARCH_TIMEOUT) { + if (!this.searchTimer) { + this.searchTimer = setTimeout(function resumeSearch() { + PDFView.search(); + }, + SEARCH_TIMEOUT - (now - lastSeach) + ); + } + return; + } + this.searchTimer = null; + this.lastSearch = now; + + function bindLink(link, pageNumber) { + link.href = '#' + pageNumber; + link.onclick = function searchBindLink() { + PDFView.page = pageNumber; + return false; + }; + } + + var searchResults = document.getElementById('searchResults'); + + var searchTermsInput = document.getElementById('searchTermsInput'); + searchResults.removeAttribute('hidden'); + searchResults.textContent = ''; + + var terms = searchTermsInput.value; + + if (!terms) + return; + + // simple search: removing spaces and hyphens, then scanning every + terms = terms.replace(/\s-/g, '').toLowerCase(); + var index = PDFView.pageText; + var pageFound = false; + for (var i = 0, ii = index.length; i < ii; i++) { + var pageText = index[i].replace(/\s-/g, '').toLowerCase(); + var j = pageText.indexOf(terms); + if (j < 0) + continue; + + var pageNumber = i + 1; + var textSample = index[i].substr(j, 50); + var link = document.createElement('a'); + bindLink(link, pageNumber); + link.textContent = 'Page ' + pageNumber + ': ' + textSample; + searchResults.appendChild(link); + + pageFound = true; + } + if (!pageFound) { + searchResults.textContent = mozL10n.get('search_terms_not_found', null, + '(Not found)'); + } + }, + + setHash: function pdfViewSetHash(hash) { + if (!hash) + return; + + if (hash.indexOf('=') >= 0) { + var params = PDFView.parseQueryString(hash); + // borrowing syntax from "Parameters for Opening PDF Files" + if ('nameddest' in params) { + PDFView.navigateTo(params.nameddest); + return; + } + if ('page' in params) { + var pageNumber = (params.page | 0) || 1; + this.page = pageNumber; + if ('zoom' in params) { + var zoomArgs = params.zoom.split(','); // scale,left,top + // building destination array + + // If the zoom value, it has to get divided by 100. If it is a string, + // it should stay as it is. + var zoomArg = zoomArgs[0]; + var zoomArgNumber = parseFloat(zoomArg); + if (zoomArgNumber) + zoomArg = zoomArgNumber / 100; + + var dest = [null, {name: 'XYZ'}, (zoomArgs[1] | 0), + (zoomArgs[2] | 0), zoomArg]; + var currentPage = this.pages[pageNumber - 1]; + currentPage.scrollIntoView(dest); + } else + this.page = params.page; // simple page + return; + } + } else if (/^\d+$/.test(hash)) // page number + this.page = hash; + else // named destination + PDFView.navigateTo(unescape(hash)); + }, + + switchSidebarView: function pdfViewSwitchSidebarView(view) { + var thumbsView = document.getElementById('thumbnailView'); + var outlineView = document.getElementById('outlineView'); + var searchView = document.getElementById('searchView'); + + var thumbsButton = document.getElementById('viewThumbnail'); + var outlineButton = document.getElementById('viewOutline'); + var searchButton = document.getElementById('viewSearch'); + + switch (view) { + case 'thumbs': + thumbsButton.classList.add('toggled'); + outlineButton.classList.remove('toggled'); + searchButton.classList.remove('toggled'); + thumbsView.classList.remove('hidden'); + outlineView.classList.add('hidden'); + searchView.classList.add('hidden'); + + updateThumbViewArea(); + break; + + case 'outline': + thumbsButton.classList.remove('toggled'); + outlineButton.classList.add('toggled'); + searchButton.classList.remove('toggled'); + thumbsView.classList.add('hidden'); + outlineView.classList.remove('hidden'); + searchView.classList.add('hidden'); + + if (outlineButton.getAttribute('disabled')) + return; + break; + + case 'search': + thumbsButton.classList.remove('toggled'); + outlineButton.classList.remove('toggled'); + searchButton.classList.add('toggled'); + thumbsView.classList.add('hidden'); + outlineView.classList.add('hidden'); + searchView.classList.remove('hidden'); + + var searchTermsInput = document.getElementById('searchTermsInput'); + searchTermsInput.focus(); + // Start text extraction as soon as the search gets displayed. + this.extractText(); + break; + } + }, + + extractText: function() { + if (this.startedTextExtraction) + return; + this.startedTextExtraction = true; + var self = this; + function extractPageText(pageIndex) { + self.pages[pageIndex].pdfPage.getTextContent().then( + function textContentResolved(textContent) { + self.pageText[pageIndex] = textContent; + self.search(); + if ((pageIndex + 1) < self.pages.length) + extractPageText(pageIndex + 1); + } + ); + }; + extractPageText(0); + }, + + getVisiblePages: function pdfViewGetVisiblePages() { + var pages = this.pages; + var kBottomMargin = 10; + var kTopPadding = 30; + var visiblePages = []; + + var currentHeight = kTopPadding + kBottomMargin; + //var container = this.container; + var container = document.getElementById('viewer'); + + // Add 1px to the scrolltop to give a little wiggle room if the math is off, + // this won't be needed if we calc current page number based off the middle + // of the screen instead of the top. + var containerTop = container.scrollTop + 1; + for (var i = 1; i <= pages.length; ++i) { + var page = pages[i - 1]; + var pageHeight = page.height + kBottomMargin; + if (currentHeight + pageHeight > containerTop) + break; + + currentHeight += pageHeight; + } + + var containerBottom = containerTop + container.clientHeight; + for (; i <= pages.length && currentHeight < containerBottom; ++i) { + var singlePage = pages[i - 1]; + visiblePages.push({ id: singlePage.id, y: currentHeight, + view: singlePage }); + currentHeight += page.height + kBottomMargin; + } + return visiblePages; + }, + + getVisibleThumbs: function pdfViewGetVisibleThumbs() { + var thumbs = this.thumbnails; + var kBottomMargin = 15; + var visibleThumbs = []; + + var view = document.getElementById('thumbnailView'); + var currentHeight = kBottomMargin; + + var top = view.scrollTop; + for (var i = 1; i <= thumbs.length; ++i) { + var thumb = thumbs[i - 1]; + var thumbHeight = thumb.height * thumb.scaleY + kBottomMargin; + if (currentHeight + thumbHeight > top) + break; + + currentHeight += thumbHeight; + } + + var bottom = top + view.clientHeight; + for (; i <= thumbs.length && currentHeight < bottom; ++i) { + var singleThumb = thumbs[i - 1]; + visibleThumbs.push({ id: singleThumb.id, y: currentHeight, + view: singleThumb }); + currentHeight += singleThumb.height * singleThumb.scaleY + kBottomMargin; + } + + return visibleThumbs; + }, + + // Helper function to parse query string (e.g. ?param1=value&parm2=...). + parseQueryString: function pdfViewParseQueryString(query) { + var parts = query.split('&'); + var params = {}; + for (var i = 0, ii = parts.length; i < parts.length; ++i) { + var param = parts[i].split('='); + var key = param[0]; + var value = param.length > 1 ? param[1] : null; + params[unescape(key)] = unescape(value); + } + return params; + } +}; + +var PageView = function pageView(container, pdfPage, id, scale, + stats, navigateTo) { + this.id = id; + this.pdfPage = pdfPage; + + this.scale = scale || 1.0; + this.viewport = this.pdfPage.getViewport(this.scale); + + var anchor = document.createElement('a'); + anchor.name = '' + this.id; + + var div = document.createElement('div'); + div.id = 'pageContainer' + this.id; + div.className = 'page'; + + container.appendChild(anchor); + container.appendChild(div); + + this.destroy = function pageViewDestroy() { + this.update(); + this.pdfPage.destroy(); + }; + + this.update = function pageViewUpdate(scale) { + this.scale = scale || this.scale; + var viewport = this.pdfPage.getViewport(this.scale); + + this.viewport = viewport; + div.style.width = viewport.width + 'px'; + div.style.height = viewport.height + 'px'; + + while (div.hasChildNodes()) + div.removeChild(div.lastChild); + div.removeAttribute('data-loaded'); + + delete this.canvas; + + this.loadingIconDiv = document.createElement('div'); + this.loadingIconDiv.className = 'loadingIcon'; + div.appendChild(this.loadingIconDiv); + }; + + Object.defineProperty(this, 'width', { + get: function PageView_getWidth() { + return this.viewport.width; + }, + enumerable: true + }); + + Object.defineProperty(this, 'height', { + get: function PageView_getHeight() { + return this.viewport.height; + }, + enumerable: true + }); + + function setupAnnotations(pdfPage, viewport) { + function bindLink(link, dest) { + link.href = PDFView.getDestinationHash(dest); + link.onclick = function pageViewSetupLinksOnclick() { + if (dest) + PDFView.navigateTo(dest); + return false; + }; + } + function createElementWithStyle(tagName, item) { + var rect = viewport.convertToViewportRectangle(item.rect); + rect = PDFJS.Util.normalizeRect(rect); + var element = document.createElement(tagName); + element.style.left = Math.floor(rect[0]) + 'px'; + element.style.top = Math.floor(rect[1]) + 'px'; + element.style.width = Math.ceil(rect[2] - rect[0]) + 'px'; + element.style.height = Math.ceil(rect[3] - rect[1]) + 'px'; + return element; + } + function createCommentAnnotation(type, item) { + var container = document.createElement('section'); + container.className = 'annotComment'; + + var image = createElementWithStyle('img', item); + var type = item.type; + var rect = viewport.convertToViewportRectangle(item.rect); + rect = PDFJS.Util.normalizeRect(rect); + image.src = kImageDirectory + 'annotation-' + type.toLowerCase() + '.svg'; + image.alt = mozL10n.get('text_annotation_type', {type: type}, + '[{{type}} Annotation]'); + var content = document.createElement('div'); + content.setAttribute('hidden', true); + var title = document.createElement('h1'); + var text = document.createElement('p'); + content.style.left = Math.floor(rect[2]) + 'px'; + content.style.top = Math.floor(rect[1]) + 'px'; + title.textContent = item.title; + + if (!item.content && !item.title) { + content.setAttribute('hidden', true); + } else { + var e = document.createElement('span'); + var lines = item.content.split('\n'); + for (var i = 0, ii = lines.length; i < ii; ++i) { + var line = lines[i]; + e.appendChild(document.createTextNode(line)); + if (i < (ii - 1)) + e.appendChild(document.createElement('br')); + } + text.appendChild(e); + image.addEventListener('mouseover', function annotationImageOver() { + content.removeAttribute('hidden'); + }, false); + + image.addEventListener('mouseout', function annotationImageOut() { + content.setAttribute('hidden', true); + }, false); + } + + content.appendChild(title); + content.appendChild(text); + container.appendChild(image); + container.appendChild(content); + + return container; + } + + pdfPage.getAnnotations().then(function(items) { + for (var i = 0; i < items.length; i++) { + var item = items[i]; + switch (item.type) { + case 'Link': + var link = createElementWithStyle('a', item); + link.href = item.url || ''; + if (!item.url) + bindLink(link, ('dest' in item) ? item.dest : null); + div.appendChild(link); + break; + case 'Text': + var comment = createCommentAnnotation(item.name, item); + if (comment) + div.appendChild(comment); + break; + case 'Widget': + // TODO: support forms + PDFView.fallback(); + break; + } + } + }); + } + + this.getPagePoint = function pageViewGetPagePoint(x, y) { + return this.viewport.convertToPdfPoint(x, y); + }; + + this.scrollIntoView = function pageViewScrollIntoView(dest) { + if (!dest) { + div.scrollIntoView(true); + return; + } + + var x = 0, y = 0; + var width = 0, height = 0, widthScale, heightScale; + var scale = 0; + switch (dest[1].name) { + case 'XYZ': + x = dest[2]; + y = dest[3]; + scale = dest[4]; + break; + case 'Fit': + case 'FitB': + scale = 'page-fit'; + break; + case 'FitH': + case 'FitBH': + y = dest[2]; + scale = 'page-width'; + break; + case 'FitV': + case 'FitBV': + x = dest[2]; + scale = 'page-height'; + break; + case 'FitR': + x = dest[2]; + y = dest[3]; + width = dest[4] - x; + height = dest[5] - y; + widthScale = (this.container.clientWidth - kScrollbarPadding) / + width / kCssUnits; + heightScale = (this.container.clientHeight - kScrollbarPadding) / + height / kCssUnits; + scale = Math.min(widthScale, heightScale); + break; + default: + return; + } + + if (scale && scale !== PDFView.currentScale) + PDFView.parseScale(scale, true); + else if (PDFView.currentScale === kUnknownScale) + PDFView.parseScale(kDefaultScale, true); + + var boundingRect = [ + this.viewport.convertToViewportPoint(x, y), + this.viewport.convertToViewportPoint(x + width, y + height) + ]; + setTimeout(function pageViewScrollIntoViewRelayout() { + // letting page to re-layout before scrolling + var scale = PDFView.currentScale; + var x = Math.min(boundingRect[0][0], boundingRect[1][0]); + var y = Math.min(boundingRect[0][1], boundingRect[1][1]); + var width = Math.abs(boundingRect[0][0] - boundingRect[1][0]); + var height = Math.abs(boundingRect[0][1] - boundingRect[1][1]); + + // using temporary div to scroll it into view + var tempDiv = document.createElement('div'); + tempDiv.style.position = 'absolute'; + tempDiv.style.left = Math.floor(x) + 'px'; + tempDiv.style.top = Math.floor(y) + 'px'; + tempDiv.style.width = Math.ceil(width) + 'px'; + tempDiv.style.height = Math.ceil(height) + 'px'; + div.appendChild(tempDiv); + tempDiv.scrollIntoView(true); + div.removeChild(tempDiv); + }, 0); + }; + + this.drawingRequired = function() { + return !div.querySelector('canvas'); + }; + + this.draw = function pageviewDraw(callback) { + if (!this.drawingRequired()) { + this.updateStats(); + callback(); + return; + } + + var canvas = document.createElement('canvas'); + canvas.id = 'page' + this.id; + canvas.mozOpaque = true; + div.appendChild(canvas); + this.canvas = canvas; + + var textLayerDiv = null; + if (!PDFJS.disableTextLayer) { + textLayerDiv = document.createElement('div'); + textLayerDiv.className = 'textLayer'; + div.appendChild(textLayerDiv); + } + var textLayer = textLayerDiv ? new TextLayerBuilder(textLayerDiv) : null; + + var scale = this.scale, viewport = this.viewport; + canvas.width = viewport.width; + canvas.height = viewport.height; + + var ctx = canvas.getContext('2d'); + ctx.save(); + ctx.fillStyle = 'rgb(255, 255, 255)'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.restore(); + + // Rendering area + + var self = this; + function pageViewDrawCallback(error) { + if (self.loadingIconDiv) { + div.removeChild(self.loadingIconDiv); + delete self.loadingIconDiv; + } + + if (error) { + PDFView.error(mozL10n.get('rendering_error', null, + 'An error occurred while rendering the page.'), error); + } + + self.stats = pdfPage.stats; + self.updateStats(); + if (self.onAfterDraw) + self.onAfterDraw(); + + cache.push(self); + callback(); + } + + var renderContext = { + canvasContext: ctx, + viewport: this.viewport, + textLayer: textLayer + }; + this.pdfPage.render(renderContext).then( + function pdfPageRenderCallback() { + pageViewDrawCallback(null); + }, + function pdfPageRenderError(error) { + pageViewDrawCallback(error); + } + ); + + setupAnnotations(this.pdfPage, this.viewport); + div.setAttribute('data-loaded', true); + }; + + this.updateStats = function pageViewUpdateStats() { + if (PDFJS.pdfBug && Stats.enabled) { + var stats = this.stats; + Stats.add(this.id, stats); + } + }; +}; + +var ThumbnailView = function thumbnailView(container, pdfPage, id) { + var anchor = document.createElement('a'); + anchor.href = PDFView.getAnchorUrl('#page=' + id); + anchor.title = mozL10n.get('thumb_page_title', {page: id}, 'Page {{page}}'); + anchor.onclick = function stopNavigation() { + PDFView.page = id; + return false; + }; + + var viewport = pdfPage.getViewport(1); + var pageWidth = this.width = viewport.width; + var pageHeight = this.height = viewport.height; + var pageRatio = pageWidth / pageHeight; + this.id = id; + + var canvasWidth = 98; + var canvasHeight = canvasWidth / this.width * this.height; + var scaleX = this.scaleX = (canvasWidth / pageWidth); + var scaleY = this.scaleY = (canvasHeight / pageHeight); + + var div = document.createElement('div'); + div.id = 'thumbnailContainer' + id; + div.className = 'thumbnail'; + + anchor.appendChild(div); + container.appendChild(anchor); + + this.hasImage = false; + + function getPageDrawContext() { + var canvas = document.createElement('canvas'); + canvas.id = 'thumbnail' + id; + canvas.mozOpaque = true; + + canvas.width = canvasWidth; + canvas.height = canvasHeight; + canvas.className = 'thumbnailImage'; + canvas.setAttribute('aria-label', mozL10n.get('thumb_page_canvas', + {page: id}, 'Thumbnail of Page {{page}}')); + + div.setAttribute('data-loaded', true); + + var ring = document.createElement('div'); + ring.className = 'thumbnailSelectionRing'; + ring.appendChild(canvas); + div.appendChild(ring); + + var ctx = canvas.getContext('2d'); + ctx.save(); + ctx.fillStyle = 'rgb(255, 255, 255)'; + ctx.fillRect(0, 0, canvasWidth, canvasHeight); + ctx.restore(); + return ctx; + } + + this.drawingRequired = function thumbnailViewDrawingRequired() { + return !this.hasImage; + }; + + this.draw = function thumbnailViewDraw(callback) { + if (this.hasImage) { + callback(); + return; + } + + var ctx = getPageDrawContext(); + var drawViewport = pdfPage.getViewport(scaleX); + var renderContext = { + canvasContext: ctx, + viewport: drawViewport + }; + pdfPage.render(renderContext).then( + function pdfPageRenderCallback() { + callback(); + }, + function pdfPageRenderError(error) { + callback(); + } + ); + this.hasImage = true; + }; + + this.setImage = function thumbnailViewSetImage(img) { + if (this.hasImage || !img) + return; + + var ctx = getPageDrawContext(); + ctx.drawImage(img, 0, 0, img.width, img.height, + 0, 0, ctx.canvas.width, ctx.canvas.height); + + this.hasImage = true; + }; +}; + +var DocumentOutlineView = function documentOutlineView(outline) { + var outlineView = document.getElementById('outlineView'); +// while (outlineView.firstChild) +// outlineView.removeChild(outlineView.firstChild); + + function bindItemLink(domObj, item) { + domObj.href = PDFView.getDestinationHash(item.dest); + domObj.onclick = function documentOutlineViewOnclick(e) { + PDFView.navigateTo(item.dest); + return false; + }; + } + + if (!outline) { + var noOutline = document.createElement('div'); + noOutline.classList.add('noOutline'); + //noOutline.textContent = mozL10n.get('no_outline', null, + // 'No Outline Available'); + noOutline.textContent = 'No Outline Available'; + //outlineView.appendChild(noOutline); + return; + } + + var queue = [{parent: outlineView, items: outline}]; + while (queue.length > 0) { + var levelData = queue.shift(); + var i, n = levelData.items.length; + for (i = 0; i < n; i++) { + var item = levelData.items[i]; + var div = document.createElement('div'); + div.className = 'outlineItem'; + var a = document.createElement('a'); + bindItemLink(a, item); + a.textContent = item.title; + div.appendChild(a); + + if (item.items.length > 0) { + var itemsDiv = document.createElement('div'); + itemsDiv.className = 'outlineItems'; + div.appendChild(itemsDiv); + queue.push({parent: itemsDiv, items: item.items}); + } + +// levelData.parent.appendChild(div); + } + } +}; + +// optimised CSS custom property getter/setter +var CustomStyle = (function CustomStyleClosure() { + + // As noted on: http://www.zachstronaut.com/posts/2009/02/17/ + // animate-css-transforms-firefox-webkit.html + // in some versions of IE9 it is critical that ms appear in this list + // before Moz + var prefixes = ['ms', 'Moz', 'Webkit', 'O']; + var _cache = { }; + + function CustomStyle() { + } + + CustomStyle.getProp = function get(propName, element) { + // check cache only when no element is given + if (arguments.length == 1 && typeof _cache[propName] == 'string') { + return _cache[propName]; + } + + element = element || document.documentElement; + var style = element.style, prefixed, uPropName; + + // test standard property first + if (typeof style[propName] == 'string') { + return (_cache[propName] = propName); + } + + // capitalize + uPropName = propName.charAt(0).toUpperCase() + propName.slice(1); + + // test vendor specific properties + for (var i = 0, l = prefixes.length; i < l; i++) { + prefixed = prefixes[i] + uPropName; + if (typeof style[prefixed] == 'string') { + return (_cache[propName] = prefixed); + } + } + + //if all fails then set to undefined + return (_cache[propName] = 'undefined'); + } + + CustomStyle.setProp = function set(propName, element, str) { + var prop = this.getProp(propName); + if (prop != 'undefined') + element.style[prop] = str; + } + + return CustomStyle; +})(); + +var TextLayerBuilder = function textLayerBuilder(textLayerDiv) { + this.textLayerDiv = textLayerDiv; + + this.beginLayout = function textLayerBuilderBeginLayout() { + this.textDivs = []; + this.textLayerQueue = []; + }; + + this.endLayout = function textLayerBuilderEndLayout() { + var self = this; + var textDivs = this.textDivs; + var textLayerDiv = this.textLayerDiv; + var renderTimer = null; + var renderingDone = false; + var renderInterval = 0; + var resumeInterval = 500; // in ms + + // Render the text layer, one div at a time + function renderTextLayer() { + if (textDivs.length === 0) { + clearInterval(renderTimer); + renderingDone = true; + return; + } + var textDiv = textDivs.shift(); + if (textDiv.dataset.textLength > 0) { + textLayerDiv.appendChild(textDiv); + + if (textDiv.dataset.textLength > 1) { // avoid div by zero + // Adjust div width to match canvas text + // Due to the .offsetWidth calls, this is slow + // This needs to come after appending to the DOM + var textScale = textDiv.dataset.canvasWidth / textDiv.offsetWidth; + CustomStyle.setProp('transform' , textDiv, + 'scale(' + textScale + ', 1)'); + CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%'); + } + } // textLength > 0 + } + renderTimer = setInterval(renderTextLayer, renderInterval); + + // Stop rendering when user scrolls. Resume after XXX milliseconds + // of no scroll events + var scrollTimer = null; + function textLayerOnScroll() { + if (renderingDone) { + window.removeEventListener('scroll', textLayerOnScroll, false); + return; + } + + // Immediately pause rendering + clearInterval(renderTimer); + + clearTimeout(scrollTimer); + scrollTimer = setTimeout(function textLayerScrollTimer() { + // Resume rendering + renderTimer = setInterval(renderTextLayer, renderInterval); + }, resumeInterval); + }; // textLayerOnScroll + + window.addEventListener('scroll', textLayerOnScroll, false); + }; // endLayout + + this.appendText = function textLayerBuilderAppendText(text, + fontName, fontSize) { + var textDiv = document.createElement('div'); + + // vScale and hScale already contain the scaling to pixel units + var fontHeight = fontSize * text.geom.vScale; + textDiv.dataset.canvasWidth = text.canvasWidth * text.geom.hScale; + textDiv.dataset.fontName = fontName; + + textDiv.style.fontSize = fontHeight + 'px'; + textDiv.style.left = text.geom.x + 'px'; + textDiv.style.top = (text.geom.y - fontHeight) + 'px'; + textDiv.textContent = PDFJS.bidi(text, -1); + textDiv.dir = text.direction; + textDiv.dataset.textLength = text.length; + this.textDivs.push(textDiv); + }; +}; + +window.addEventListener('load', function webViewerLoad(evt) { + PDFView.initialize(); + var params = PDFView.parseQueryString(document.location.search.substring(1)); + + var file = PDFJS.isFirefoxExtension ? + window.location.toString() : params.file || kDefaultURL; + +/* if (PDFJS.isFirefoxExtension || !window.File || !window.FileReader || + !window.FileList || !window.Blob) { + document.getElementById('openFile').setAttribute('hidden', 'true'); + } else { + document.getElementById('fileInput').value = null; + } +*/ + // Special debugging flags in the hash section of the URL. + var hash = document.location.hash.substring(1); + var hashParams = PDFView.parseQueryString(hash); + + if ('disableWorker' in hashParams) + PDFJS.disableWorker = (hashParams['disableWorker'] === 'true'); + +/* if (!PDFJS.isFirefoxExtension) { + var locale = navigator.language; + if ('locale' in hashParams) + locale = hashParams['locale']; + mozL10n.language.code = locale; + } +*/ + if ('disableTextLayer' in hashParams) + PDFJS.disableTextLayer = (hashParams['disableTextLayer'] === 'true'); + + if ('pdfBug' in hashParams && + (!PDFJS.isFirefoxExtension || FirefoxCom.requestSync('pdfBugEnabled'))) { + PDFJS.pdfBug = true; + var pdfBug = hashParams['pdfBug']; + var enabled = pdfBug.split(','); + PDFBug.enable(enabled); + PDFBug.init(); + } + +/* if (!PDFJS.isFirefoxExtension || + (PDFJS.isFirefoxExtension && FirefoxCom.requestSync('searchEnabled'))) { + document.querySelector('#viewSearch').classList.remove('hidden'); + } +*/ + // Listen for warnings to trigger the fallback UI. Errors should be caught + // and call PDFView.error() so we don't need to listen for those. + PDFJS.LogManager.addLogger({ + warn: function() { + PDFView.fallback(); + } + }); + +// var thumbsView = document.getElementById('thumbnailView'); +// thumbsView.addEventListener('scroll', updateThumbViewArea, true); +/* + var mainContainer = document.getElementById('mainContainer'); + var outerContainer = document.getElementById('outerContainer'); + mainContainer.addEventListener('transitionend', function(e) { + if (e.target == mainContainer) { + var event = document.createEvent('UIEvents'); + event.initUIEvent('resize', false, false, window, 0); + window.dispatchEvent(event); + outerContainer.classList.remove('sidebarMoving'); + } + }, true); + + document.getElementById('sidebarToggle').addEventListener('click', + function() { + this.classList.toggle('toggled'); + outerContainer.classList.add('sidebarMoving'); + outerContainer.classList.toggle('sidebarOpen'); + updateThumbViewArea(); + }); + + PDFView.open(file, 0); +*/ +}, true); + +/** + * Render the next not yet visible page already such that it is + * hopefully ready once the user scrolls to it. + */ +function preDraw() { + var pages = PDFView.pages; + var visible = PDFView.getVisiblePages(); + var last = visible[visible.length - 1]; + // PageView.id is the actual page number, which is + 1 compared + // to the index in `pages`. That means, pages[last.id] is the next + // PageView instance. + if (pages[last.id] && pages[last.id].drawingRequired()) { + renderingQueue.enqueueDraw(pages[last.id]); + return; + } + // If there is nothing to draw on the next page, maybe the user + // is scrolling up, so, let's try to render the next page *before* + // the first visible page + if (pages[visible[0].id - 2]) { + renderingQueue.enqueueDraw(pages[visible[0].id - 2]); + } +} + +function updateViewarea() { + if (!PDFView.initialized) + return; + var container = document.getElementById('viewer'); + if (!container) + return; + + var visiblePages = PDFView.getVisiblePages(); + var pageToDraw; + for (var i = 0; i < visiblePages.length; i++) { + var page = visiblePages[i]; + var pageObj = PDFView.pages[page.id - 1]; + + pageToDraw |= pageObj.drawingRequired(); + renderingQueue.enqueueDraw(pageObj); + } + + if (!visiblePages.length) + return; + + // If there is no need to draw a page that is currenlty visible, preDraw the + // next page the user might scroll to. + if (!pageToDraw) { + preDraw(); + } + + updateViewarea.inProgress = true; // used in "set page" + var currentId = PDFView.page; + var firstPage = visiblePages[0]; + PDFView.page = firstPage.id; + updateViewarea.inProgress = false; + + var currentScale = PDFView.currentScale; + var currentScaleValue = PDFView.currentScaleValue; + var normalizedScaleValue = currentScaleValue == currentScale ? + currentScale * 100 : currentScaleValue; + + var pageNumber = firstPage.id; + var pdfOpenParams = '#page=' + pageNumber; + pdfOpenParams += '&zoom=' + normalizedScaleValue; + var currentPage = PDFView.pages[pageNumber - 1]; + var topLeft = currentPage.getPagePoint(PDFView.container.scrollLeft, + (PDFView.container.scrollTop - firstPage.y)); + pdfOpenParams += ',' + Math.round(topLeft[0]) + ',' + Math.round(topLeft[1]); + + var store = PDFView.store; + store.set('exists', true); + store.set('page', pageNumber); + store.set('zoom', normalizedScaleValue); + store.set('scrollLeft', Math.round(topLeft[0])); + store.set('scrollTop', Math.round(topLeft[1])); +// var href = PDFView.getAnchorUrl(pdfOpenParams); +// document.getElementById('viewBookmark').href = href; +} + +window.addEventListener('scroll', function webViewerScroll(evt) { + updateViewarea(); +}, true); + +var thumbnailTimer; + +function updateThumbViewArea() { + // Only render thumbs after pausing scrolling for this amount of time + // (makes UI more responsive) + var delay = 50; // in ms + + if (thumbnailTimer) + clearTimeout(thumbnailTimer); + + thumbnailTimer = setTimeout(function() { + var visibleThumbs = PDFView.getVisibleThumbs(); + for (var i = 0; i < visibleThumbs.length; i++) { + var thumb = visibleThumbs[i]; + renderingQueue.enqueueDraw(PDFView.thumbnails[thumb.id - 1]); + } + }, delay); +} + +window.addEventListener('resize', function webViewerResize(evt) { + if (PDFView.initialized && PDFView.active && + (document.getElementById('pageWidthOption').selected || + document.getElementById('pageFitOption').selected )) + PDFView.parseScale(document.getElementById('scaleSelect').value); + updateViewarea(); +}); + +window.addEventListener('hashchange', function webViewerHashchange(evt) { + PDFView.setHash(document.location.hash.substring(1)); +}); + +window.addEventListener('change', function webViewerChange(evt) { + var files = evt.target.files; + if (!files || files.length == 0) + return; + + // Read the local file into a Uint8Array. + var fileReader = new FileReader(); + fileReader.onload = function webViewerChangeFileReaderOnload(evt) { + var data = evt.target.result; + var buffer = new ArrayBuffer(data.length); + var uint8Array = new Uint8Array(buffer); + + for (var i = 0; i < data.length; i++) + uint8Array[i] = data.charCodeAt(i); + + PDFView.open(uint8Array, 0); + }; + + // Read as a binary string since "readAsArrayBuffer" is not yet + // implemented in Firefox. + var file = files[0]; + fileReader.readAsBinaryString(file); + document.title = file.name; + + // URL does not reflect proper document location - hiding some icons. +// document.getElementById('viewBookmark').setAttribute('hidden', 'true'); +// document.getElementById('download').setAttribute('hidden', 'true'); +}, true); + +function selectScaleOption(value) { + var options = document.getElementById('scaleSelect').options; + var predefinedValueFound = false; + for (var i = 0; i < options.length; i++) { + var option = options[i]; + if (option.value != value) { + option.selected = false; + continue; + } + option.selected = true; + predefinedValueFound = true; + } + return predefinedValueFound; +} + +window.addEventListener('localized', function localized(evt) { + document.getElementsByTagName('html')[0].dir = mozL10n.language.direction; +}, true); + +window.addEventListener('scalechange', function scalechange(evt) { + var customScaleOption = document.getElementById('customScaleOption'); + customScaleOption.selected = false; + + if (!evt.resetAutoSettings && + (document.getElementById('pageWidthOption').selected || + document.getElementById('pageFitOption').selected || + document.getElementById('pageAutoOption').selected)) { + updateViewarea(); + return; + } + + var predefinedValueFound = selectScaleOption('' + evt.scale); + if (!predefinedValueFound) { + customScaleOption.textContent = Math.round(evt.scale * 10000) / 100 + '%'; + customScaleOption.selected = true; + } + + updateViewarea(); +}, true); + +window.addEventListener('pagechange', function pagechange(evt) { + var page = evt.pageNumber; + if (document.getElementById('pageNumber').value != page) { + document.getElementById('pageNumber').value = page; + var selected = document.querySelector('.thumbnail.selected'); + if (selected) + selected.classList.remove('selected'); +/* + var thumbnail = document.getElementById('thumbnailContainer' + page); + thumbnail.classList.add('selected'); + var visibleThumbs = PDFView.getVisibleThumbs(); + var numVisibleThumbs = visibleThumbs.length; + // If the thumbnail isn't currently visible scroll it into view. + if (numVisibleThumbs > 0) { + var first = visibleThumbs[0].id; + // Account for only one thumbnail being visible. + var last = numVisibleThumbs > 1 ? + visibleThumbs[numVisibleThumbs - 1].id : first; + if (page <= first || page >= last) + thumbnail.scrollIntoView(); + } +*/ + } + document.getElementById('previous').disabled = (page <= 1); + document.getElementById('next').disabled = (page >= PDFView.pages.length); +}, true); + +// Firefox specific event, so that we can prevent browser from zooming +window.addEventListener('DOMMouseScroll', function(evt) { + if (evt.ctrlKey) { + evt.preventDefault(); + + var ticks = evt.detail; + var direction = (ticks > 0) ? 'zoomOut' : 'zoomIn'; + for (var i = 0, length = Math.abs(ticks); i < length; i++) + PDFView[direction](); + } +}, false); + + diff --git a/apps/files_pdfviewer/js/viewer.js b/apps/files_pdfviewer/js/viewer.js index 417d3a073f0..29db2ea7f24 100644 --- a/apps/files_pdfviewer/js/viewer.js +++ b/apps/files_pdfviewer/js/viewer.js @@ -24,21 +24,9 @@ function showPDFviewer(dir,filename){ var oldcontent = $("#content").html(); $("#content").html(oldcontent+'<div id="loading">Loading... 0%</div><div id="viewer"></div>'); showPDFviewer.lastTitle = document.title; - if(!showPDFviewer.loaded){ - OC.addScript( 'files_pdfviewer', 'pdfjs/build/pdf',function(){ - OC.addScript( 'files_pdfviewer', 'pdfview',function(){ - showPDFviewer.loaded=true; - PDFJS.workerSrc = OC.filePath('files_pdfviewer','js','pdfjs/build/pdf.js'); - PDFView.Ptitle = filename; - PDFView.open(url,1.00); - PDFView.active=true; - }); - }); - }else{ - PDFView.Ptitle = filename; - PDFView.open(url,1.00); - PDFView.active=true; - } + PDFView.Ptitle = filename; + PDFView.open(url,1.00); + PDFView.active=true; $("#pageWidthOption").attr("selected","selected"); showPDFviewer.shown = true; } @@ -46,14 +34,13 @@ function showPDFviewer(dir,filename){ showPDFviewer.shown=false; showPDFviewer.oldCode=''; showPDFviewer.lastTitle=''; -showPDFviewer.loaded=false; $(document).ready(function(){ if(!$.browser.msie){//doesnt work on IE if(location.href.indexOf("files")!=-1) { PDFJS.workerSrc = OC.filePath('files_pdfviewer','js','pdfjs/build/pdf.js'); if(typeof FileActions!=='undefined'){ - FileActions.register('application/pdf','Edit','',function(filename){ + FileActions.register('application/pdf','Edit', FileActions.PERMISSION_READ, '',function(filename){ showPDFviewer($('#dir').val(),filename); }); FileActions.setDefault('application/pdf','Edit'); diff --git a/apps/files_sharing/ajax/email.php b/apps/files_sharing/ajax/email.php deleted file mode 100644 index 2a81e6f9827..00000000000 --- a/apps/files_sharing/ajax/email.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php -OCP\JSON::checkLoggedIn(); -OCP\JSON::callCheck(); -OCP\JSON::checkAppEnabled('files_sharing'); -$user = OCP\USER::getUser(); -// TODO translations -$type = (strpos($_POST['file'], '.') === false) ? 'folder' : 'file'; -$subject = $user.' shared a '.$type.' with you'; -$link = $_POST['link']; -$text = $user.' shared the '.$type.' '.$_POST['file'].' with you. It is available for download here: '.$link; -$fromaddress = OCP\Config::getUserValue($user, 'settings', 'email', 'sharing-noreply@'.OCP\Util::getServerHost()); -try { - OCP\Util::sendMail($_POST['toaddress'], $_POST['toaddress'], $subject, $text, $fromaddress, $user); - OCP\JSON::success(); -} catch (Exception $exception) { - OCP\JSON::error(array('data' => array('message' => $exception->getMessage()))); -} diff --git a/apps/files_sharing/ajax/getitem.php b/apps/files_sharing/ajax/getitem.php deleted file mode 100644 index 852e5a24571..00000000000 --- a/apps/files_sharing/ajax/getitem.php +++ /dev/null @@ -1,74 +0,0 @@ -<?php -require_once(OC::$APPSROOT . '/apps/files_sharing/lib_share.php'); - -OCP\JSON::checkAppEnabled('files_sharing'); -OCP\JSON::checkLoggedIn(); - -$item = array(); -$userDirectory = '/'.OCP\USER::getUser().'/files'; -$source = $userDirectory.$_GET['item']; -$path = $source; -// Search for item and shared parent folders -while ($path != $userDirectory) { - if ($rows = OC_Share::getMySharedItem($path)) { - for ($i = 0; $i < count($rows); $i++) { - $uid_shared_with = $rows[$i]['uid_shared_with']; - if ($uid_shared_with == OC_Share::PUBLICLINK && !isset($item['privateLink'])) { - $token = OC_Share::getTokenFromSource($path); - if ($path == $source) { - $item['privateLink'] = $token; - } else { - // If in parent folder, include a path parameter to get direct access to file - $item['privateLink'] = $token.'&path='.str_replace('%2F', '/', str_replace('+', '%20', urlencode(substr($source, strlen($path)))));; - } - } else { - // Check if uid_shared_with is a group - $pos = strrpos($uid_shared_with, '@'); - if ($pos !== false) { - $gid = substr($uid_shared_with, $pos + 1); - } else { - $gid = false; - } - if ($gid && OC_Group::groupExists($gid)) { - // Include users in the group so the users can be removed from the list of people to share with - if ($path == $source) { - $group = array(array('gid' => $gid, 'permissions' => $rows[$i]['permissions'], 'users' => OC_Group::usersInGroup($gid), 'parentFolder' => false)); - } else { - $group = array(array('gid' => $gid, 'permissions' => $rows[$i]['permissions'], 'users' => OC_Group::usersInGroup($gid), 'parentFolder' => basename($path))); - } - if (!isset($item['groups'])) { - $item['groups'] = $group; - } else if (is_array($item['groups'])) { - $gidExists = false; - $currentGroups = $item['groups']; - // Check if the group is already included - foreach ($currentGroups as $g) { - if ($g['gid'] == $gid) { - $gidExists = true; - } - } - if (!$gidExists) { - $item['groups'] = array_merge($item['groups'], $group); - } - } - } else { - if ($path == $source) { - $user = array(array('uid' => $uid_shared_with, 'permissions' => $rows[$i]['permissions'], 'parentFolder' => false)); - } else { - $user = array(array('uid' => $uid_shared_with, 'permissions' => $rows[$i]['permissions'], 'parentFolder' => basename($path))); - } - if (!isset($item['users'])) { - $item['users'] = $user; - } else if (is_array($item['users'])) { - $item['users'] = array_merge($item['users'], $user); - } - } - } - } - } - $path = dirname($path); -} - -OCP\JSON::success(array('data' => $item)); - -?> diff --git a/apps/files_sharing/ajax/getstatuses.php b/apps/files_sharing/ajax/getstatuses.php deleted file mode 100644 index 8edcb214758..00000000000 --- a/apps/files_sharing/ajax/getstatuses.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php -require_once(OC::$APPSROOT . '/apps/files_sharing/lib_share.php'); - -OCP\JSON::checkAppEnabled('files_sharing'); -OCP\JSON::checkLoggedIn(); - -$items = array(); -$userDirectory = '/'.OCP\USER::getUser().'/files'; -$dirLength = strlen($userDirectory); -if ($rows = OC_Share::getMySharedItems()) { - for ($i = 0; $i < count($rows); $i++) { - $source = $rows[$i]['source']; - // Strip out user directory - $item = substr($source, $dirLength); - if ($rows[$i]['uid_shared_with'] == OC_Share::PUBLICLINK) { - $items[$item] = true; - } else if (!isset($items[$item])) { - $items[$item] = false; - } - } -} - -OCP\JSON::success(array('data' => $items)); - -?>
\ No newline at end of file diff --git a/apps/files_sharing/ajax/setpermissions.php b/apps/files_sharing/ajax/setpermissions.php deleted file mode 100644 index 13daab738de..00000000000 --- a/apps/files_sharing/ajax/setpermissions.php +++ /dev/null @@ -1,15 +0,0 @@ -<?php -require_once(OC::$APPSROOT . '/apps/files_sharing/lib_share.php'); - -OCP\JSON::checkAppEnabled('files_sharing'); -OCP\JSON::checkLoggedIn(); -OCP\JSON::callCheck(); - -$source = '/'.OCP\USER::getUser().'/files'.$_POST['source']; -$uid_shared_with = $_POST['uid_shared_with']; -$permissions = $_POST['permissions']; -OC_Share::setPermissions($source, $uid_shared_with, $permissions); - -OCP\JSON::success(); - -?> diff --git a/apps/files_sharing/ajax/share.php b/apps/files_sharing/ajax/share.php deleted file mode 100644 index fb28caf7b7e..00000000000 --- a/apps/files_sharing/ajax/share.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php -require_once(OC::$APPSROOT . '/apps/files_sharing/lib_share.php'); - -OCP\JSON::checkAppEnabled('files_sharing'); -OCP\JSON::checkLoggedIn(); -OCP\JSON::callCheck(); - -$userDirectory = '/'.OCP\USER::getUser().'/files'; -$sources = explode(';', $_POST['sources']); -$uid_shared_with = $_POST['uid_shared_with']; -$permissions = $_POST['permissions']; -foreach ($sources as $source) { - $file = OC_FileCache::get($source); - $path = ltrim($source, '/'); - $source = $userDirectory.$source; - // Check if the file exists or if the file is being reshared - if ($source && $file['encrypted'] == false && (OC_FILESYSTEM::file_exists($path) && OC_FILESYSTEM::is_readable($path) || OC_Share::getSource($source))) { - try { - $shared = new OC_Share($source, $uid_shared_with, $permissions); - // If this is a private link, return the token - if ($uid_shared_with == OC_Share::PUBLICLINK) { - OCP\JSON::success(array('data' => $shared->getToken())); - } else { - OCP\JSON::success(); - } - } catch (Exception $exception) { - OCP\Util::writeLog('files_sharing', 'Unexpected Error : '.$exception->getMessage(), OCP\Util::ERROR); - OCP\JSON::error(array('data' => array('message' => $exception->getMessage()))); - } - } else { - if ($file['encrypted'] == true) { - OCP\JSON::error(array('data' => array('message' => 'Encrypted files cannot be shared'))); - } else { - OCP\Util::writeLog('files_sharing', 'File does not exist or is not readable :'.$source, OCP\Util::ERROR); - OCP\JSON::error(array('data' => array('message' => 'File does not exist or is not readable'))); - } - } -} - -?> diff --git a/apps/files_sharing/ajax/toggleresharing.php b/apps/files_sharing/ajax/toggleresharing.php deleted file mode 100644 index ab8e82c8c3f..00000000000 --- a/apps/files_sharing/ajax/toggleresharing.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -OCP\JSON::callCheck(); - -OCP\JSON::checkAppEnabled('files_sharing'); -OCP\JSON::checkAdminUser(); -if ($_POST['resharing'] == true) { - OCP\Config::setAppValue('files_sharing', 'resharing', 'yes'); -} else { - OCP\Config::setAppValue('files_sharing', 'resharing', 'no'); -} - -?> diff --git a/apps/files_sharing/ajax/unshare.php b/apps/files_sharing/ajax/unshare.php deleted file mode 100644 index d291b719e38..00000000000 --- a/apps/files_sharing/ajax/unshare.php +++ /dev/null @@ -1,14 +0,0 @@ -<?php -require_once(OC::$APPSROOT . '/apps/files_sharing/lib_share.php'); - -OCP\JSON::checkAppEnabled('files_sharing'); -OCP\JSON::checkLoggedIn(); -OCP\JSON::callCheck(); - -$source = '/'.OCP\USER::getUser().'/files'.$_POST['source']; -$uid_shared_with = $_POST['uid_shared_with']; -OC_Share::unshare($source, $uid_shared_with); - -OCP\JSON::success(); - -?> diff --git a/apps/files_sharing/ajax/userautocomplete.php b/apps/files_sharing/ajax/userautocomplete.php deleted file mode 100644 index 23865693a1b..00000000000 --- a/apps/files_sharing/ajax/userautocomplete.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -OCP\JSON::checkLoggedIn(); -OCP\JSON::checkAppEnabled('files_sharing'); - -$users = array(); -$groups = array(); -$self = OCP\USER::getUser(); -$users[] = "<optgroup label='Users'>"; -$groups[] = "<optgroup label='Groups'>"; -if(OCP\Config::getAppValue('files_sharing', 'allowSharingWithEveryone', 'no') == 'yes') { - $allGroups = OC_Group::getGroups(); - foreach($allGroups as $group) { - $groups[] = "<option value='".$group."(group)'>".$group." (group) </option>"; - } - $allUsers = OC_User::getUsers(); - foreach($allUsers as $user) { - if($user != $self) { - $users[] = "<option value='".$user."'>".$user."</option>"; - } - } -} else { - $userGroups = OC_Group::getUserGroups($self); - foreach ($userGroups as $group) { - $groupUsers = OC_Group::usersInGroup($group); - $userCount = 0; - foreach ($groupUsers as $user) { - if ($user != $self) { - $users[] = "<option value='".$user."'>".$user."</option>"; - $userCount++; - } - } - // Don't include the group if only the current user is a member of it - if ($userCount > 0) { - $groups[] = "<option value='".$group."(group)'>".$group." (group) </option>"; - } - } - $users = array_unique($users); -} -$users[] = "</optgroup>"; -$groups[] = "</optgroup>"; -$users = array_merge($users, $groups); -OCP\JSON::encodedPrint($users); - -?> diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php index ea3a9da6f7a..109f86b2e87 100644 --- a/apps/files_sharing/appinfo/app.php +++ b/apps/files_sharing/appinfo/app.php @@ -1,19 +1,9 @@ <?php -require_once('apps/files_sharing/sharedstorage.php'); - -OC::$CLASSPATH['OC_Share'] = "apps/files_sharing/lib_share.php"; -OCP\App::registerAdmin('files_sharing', 'settings'); -OCP\Util::connectHook("OC_Filesystem", "post_delete", "OC_Share", "deleteItem"); -OCP\Util::connectHook("OC_Filesystem", "post_rename", "OC_Share", "renameItem"); -OCP\Util::connectHook("OC_Filesystem", "post_write", "OC_Share", "updateItem"); -OCP\Util::connectHook('OC_User', 'post_deleteUser', 'OC_Share', 'removeUser'); -OCP\Util::connectHook('OC_User', 'post_addToGroup', 'OC_Share', 'addToGroupShare'); -OCP\Util::connectHook('OC_User', 'post_removeFromGroup', 'OC_Share', 'removeFromGroupShare'); -$dir = isset($_GET['dir']) ? $_GET['dir'] : '/'; -if ($dir != '/Shared' || OCP\Config::getAppValue('files_sharing', 'resharing', 'yes') == 'yes') { - OCP\Util::addscript("files_sharing", "share"); -} -OCP\Util::addscript("3rdparty", "chosen/chosen.jquery.min"); -OCP\Util::addStyle( 'files_sharing', 'sharing' ); -OCP\Util::addStyle("3rdparty", "chosen/chosen"); +OC::$CLASSPATH['OC_Share_Backend_File'] = "apps/files_sharing/lib/share/file.php"; +OC::$CLASSPATH['OC_Share_Backend_Folder'] = 'apps/files_sharing/lib/share/folder.php'; +OC::$CLASSPATH['OC_Filestorage_Shared'] = "apps/files_sharing/lib/sharedstorage.php"; +OCP\Util::connectHook('OC_Filesystem', 'setup', 'OC_Filestorage_Shared', 'setup'); +OCP\Share::registerBackend('file', 'OC_Share_Backend_File'); +OCP\Share::registerBackend('folder', 'OC_Share_Backend_Folder', 'file'); +OCP\Util::addScript('files_sharing', 'share'); diff --git a/apps/files_sharing/css/sharing.css b/apps/files_sharing/css/sharing.css deleted file mode 100644 index c4b4540e9d1..00000000000 --- a/apps/files_sharing/css/sharing.css +++ /dev/null @@ -1,14 +0,0 @@ -/* Copyright (c) 2011, Jan-Christoph Borchardt, http://jancborchardt.net - This file is licensed under the Affero General Public License version 3 or later. - See the COPYING-README file. */ - -#dropdown { display:block; position:absolute; z-index:100; width:16em; right:0; margin-right:7em; background:#eee; padding:1em; --moz-box-shadow:0 1px 1px #777; -webkit-box-shadow:0 1px 1px #777; box-shadow:0 1px 1px #777; --moz-border-radius-bottomleft:1em; -webkit-border-bottom-left-radius:1em; border-bottom-left-radius:1em; --moz-border-radius-bottomright:1em; -webkit-border-bottom-right-radius:1em; border-bottom-right-radius:1em; } -#sharedWithList { padding:0.5em; list-style-type: none; } -#privateLink { border-top:1px solid #ddd; padding-top:0.5em; } -a.unshare { float:right; display:inline; margin:0 .5em; padding:.3em .3em 0 .3em !important; opacity:.5; } -a.unshare:hover { opacity:1; } -#share_with { width: 16em; } -#privateLink label, .edit { font-weight:normal; } diff --git a/apps/files_sharing/get.php b/apps/files_sharing/get.php index e2fcb82750d..1d219719b2d 100644 --- a/apps/files_sharing/get.php +++ b/apps/files_sharing/get.php @@ -1,6 +1,4 @@ <?php -$RUNTIME_NOSETUPFS=true; //don't setup the fs yet - // only need authentication apps $RUNTIME_APPTYPES=array('authentication'); OC_App::loadApps($RUNTIME_APPTYPES); @@ -61,7 +59,7 @@ if (isset($_GET['token']) && $source = OC_Share::getSource($_GET['token'])) { $list->assign("downloadURL", OCP\Util::linkTo("", "public.php")."?service=files&token=".$token."&path="); $list->assign("readonly", true); $tmpl = new OCP\Template("files", "index", "user"); - $tmpl->assign("fileList", $list->fetchPage()); + $tmpl->assign("fileList", $list->fetchPage(), false); $tmpl->assign("breadcrumb", $breadcrumbNav->fetchPage()); $tmpl->assign("readonly", true); $tmpl->assign("allowZipDownload", false); @@ -77,6 +75,7 @@ if (isset($_GET['token']) && $source = OC_Share::getSource($_GET['token'])) { header("Content-Length: " . OC_Filesystem::filesize($source)); //download the file @ob_clean(); + OCP\Util::emitHook('OC_Share', 'public-download', array('source'=>$source, 'token'=>$token)); OC_Filesystem::readfile($source); } } else { @@ -85,4 +84,3 @@ if (isset($_GET['token']) && $source = OC_Share::getSource($_GET['token'])) { $tmpl->printPage(); die(); } -?> diff --git a/apps/files_sharing/js/list.js b/apps/files_sharing/js/list.js deleted file mode 100644 index 41eabd1f4af..00000000000 --- a/apps/files_sharing/js/list.js +++ /dev/null @@ -1,54 +0,0 @@ -$(document).ready(function() { - $( "#source" ).autocomplete({ - source: "../../files/ajax/autocomplete.php", - minLength: 1 - }); - $( "#uid_shared_with" ).autocomplete({ - source: "ajax/userautocomplete.php", - minLength: 1 - }); - $("button.delete").live('click', function( event ) { - event.preventDefault(); -// var row=$(this); - var source=$(this).attr('data-source'); - var uid_shared_with=$(this).attr('data-uid_shared_with'); - var data='source='+encodeURIComponent(source)+'&uid_shared_with='+encodeURIComponent(uid_shared_with); - $.ajax({ - type: 'GET', - url: 'ajax/unshare.php', - cache: false, - data: data -// success: function(){ -// row.remove(); -// } - }); - }); - $('#share_item').submit(function( event ){ - event.preventDefault(); - var source=$('#source').val(); - var uid_shared_with=$('#uid_shared_with').val(); - var permissions=$('#permissions').val()||0; - var data='source='+source+'&uid_shared_with='+uid_shared_with+'&permissions='+permissions; - $.ajax({ - type: 'GET', - url: 'ajax/share.php', - cache: false, - data: data, -// success: function(token){ -// if(token){ -// var html="<tr class='link' id='"+token+"'>"; -// html+="<td class='path'>"+path+"</td>"; -// var expire=($('#expire').val())?$('#expire').val():'Never' -// html+="<td class='expire'>"+expire+"</td>" -// html+="<td class='link'><a href='get.php?token="+token+"'>"+$('#baseUrl').val()+"?token="+token+"</a></td>" -// html+="<td><button class='delete fancybutton' data-token='"+token+"'>Delete</button></td>" -// html+="</tr>" -// $(html).insertBefore($('#newlink_row')); -// $('#expire').val(''); -// $('#expire_time').val(''); -// $('#path').val(''); -// } -// } - }); - }); -});
\ No newline at end of file diff --git a/apps/files_sharing/js/settings.js b/apps/files_sharing/js/settings.js deleted file mode 100644 index c276521b7b5..00000000000 --- a/apps/files_sharing/js/settings.js +++ /dev/null @@ -1,16 +0,0 @@ -$(document).ready(function() { - $('#allowResharing').bind('change', function() { - var checked = 1; - if (!this.checked) { - checked = 0; - } - $.post(OC.filePath('files_sharing','ajax','toggleresharing.php'), 'resharing='+checked); - }); - $('#allowSharingWithEveryone').bind('change', function() { - var checked = 1; - if (!this.checked) { - checked = 0; - } - $.post(OC.filePath('files_sharing','ajax','togglesharewitheveryone.php'), 'allowSharingWithEveryone='+checked); - }); -});
\ No newline at end of file diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index 1ad51873540..bcfd42ce21e 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -1,375 +1,64 @@ -OC.Share={ - icons:[], - itemUsers:[], - itemGroups:[], - itemPrivateLink:false, - usersAndGroups:[], - loadIcons:function() { - // Cache all icons for shared files - $.getJSON(OC.filePath('files_sharing', 'ajax', 'getstatuses.php'), function(result) { - if (result && result.status === 'success') { - $.each(result.data, function(item, hasPrivateLink) { - if (hasPrivateLink) { - OC.Share.icons[item] = OC.imagePath('core', 'actions/public'); - } else { - OC.Share.icons[item] = OC.imagePath('core', 'actions/shared'); - } - }); - } - }); - }, - loadItem:function(item) { - $.ajax({type: 'GET', url: OC.filePath('files_sharing', 'ajax', 'getitem.php'), data: { item: item }, async: false, success: function(result) { - if (result && result.status === 'success') { - var item = result.data; - OC.Share.itemUsers = item.users; - OC.Share.itemGroups = item.groups; - OC.Share.itemPrivateLink = item.privateLink; - } - }}); - }, - share:function(source, uid_shared_with, permissions, callback) { - $.post(OC.filePath('files_sharing', 'ajax', 'share.php'), { sources: source, uid_shared_with: uid_shared_with, permissions: permissions }, function(result) { - if (result && result.status === 'success') { - if (callback) { - callback(result.data); - } - } else { - OC.dialogs.alert(result.data.message, 'Error while sharing'); - } - }); - }, - unshare:function(source, uid_shared_with, callback) { - $.post(OC.filePath('files_sharing', 'ajax', 'unshare.php'), { source: source, uid_shared_with: uid_shared_with }, function(result) { - if (result && result.status === 'success') { - if (callback) { - callback(); - } - } else { - OC.dialogs.alert('Error', 'Error while unsharing'); - } - }); - }, - changePermissions:function(source, uid_shared_with, permissions) { - $.post(OC.filePath('files_sharing','ajax','setpermissions.php'), { source: source, uid_shared_with: uid_shared_with, permissions: permissions }, function(result) { - if (!result || result.status !== 'success') { - OC.dialogs.alert('Error', 'Error while changing permissions'); - } - }); - }, - showDropDown:function(item, appendTo) { - OC.Share.loadItem(item); - var html = '<div id="dropdown" class="drop" data-item="'+item+'">'; - html += '<select data-placeholder="User or Group" id="share_with" class="chzen-select">'; - html += '<option value=""></option>'; - html += '</select>'; - html += '<div id="sharedWithList">'; - html += '<ul id="userList"></ul>'; - html += '<div id="groups" style="display:none;">'; - html += '<br />'; - html += 'Groups: '; - html += '<ul id="groupList"></ul>'; - html += '</div>'; - html += '</div>'; - html += '<div id="privateLink">'; - html += '<input type="checkbox" name="privateLinkCheckbox" id="privateLinkCheckbox" value="1" /><label for="privateLinkCheckbox">Share with private link</label>'; - html += '<br />'; - html += '<form id="emailPrivateLink">'; - html += '<input id="privateLinkText" style="display:none; width:90%;" />'; - html += '<input id="email" style="display:none; width:65%;" value="" placeholder="Email link to person" />'; - html += '<input id="emailButton" style="display:none;" type="submit" value="Send" />'; - html += '</form>'; - html += '</div>'; - $(html).appendTo(appendTo); - if (OC.Share.usersAndGroups.length < 1) { - $.ajax({type: 'GET', url: OC.filePath('files_sharing', 'ajax', 'userautocomplete.php'), async: false, success: function(users) { - if (users) { - OC.Share.usersAndGroups = users; - $.each(users, function(index, user) { - $(user).appendTo('#share_with'); - }); - $('#share_with').trigger('liszt:updated'); - } - }}); - } else { - $.each(OC.Share.usersAndGroups, function(index, user) { - $(user).appendTo('#share_with'); - }); - $('#share_with').trigger('liszt:updated'); - } - if (OC.Share.itemUsers) { - $.each(OC.Share.itemUsers, function(index, user) { - if (user.parentFolder) { - OC.Share.addSharedWith(user.uid, user.permissions, false, user.parentFolder); - } else { - OC.Share.addSharedWith(user.uid, user.permissions, false, false); - } - }); - } - if (OC.Share.itemGroups) { - $.each(OC.Share.itemGroups, function(index, group) { - if (group.parentFolder) { - OC.Share.addSharedWith(group.gid, group.permissions, group.users, group.parentFolder); - } else { - OC.Share.addSharedWith(group.gid, group.permissions, group.users, false); - } - }); - } - if (OC.Share.itemPrivateLink) { - OC.Share.showPrivateLink(item, OC.Share.itemPrivateLink); - } - $('#dropdown').show('blind'); - $('#share_with').chosen(); - }, - hideDropDown:function(callback) { - $('#dropdown').hide('blind', function() { - $('#dropdown').remove(); - if (callback) { - callback.call(); - } - }); - }, - addSharedWith:function(uid_shared_with, permissions, isGroup, parentFolder) { - if (parentFolder) { - var sharedWith = '<li>Parent folder '+parentFolder+' shared with '+uid_shared_with+'</li>'; - } else { - var checked = ((permissions > 0) ? 'checked="checked"' : 'style="display:none;"'); - var style = ((permissions == 0) ? 'style="display:none;"' : ''); - var sharedWith = '<li data-uid_shared_with="'+uid_shared_with+'">'; - sharedWith += '<a href="" class="unshare" style="display:none;"><img class="svg" alt="Unshare" src="'+OC.imagePath('core','actions/delete')+'"/></a>'; - sharedWith += uid_shared_with; - sharedWith += '<input type="checkbox" name="permissions" id="'+uid_shared_with+'" class="permissions" '+checked+' />'; - sharedWith += '<label class="edit" for="'+uid_shared_with+'" '+style+'>can edit</label>'; - sharedWith += '</li>'; - } - if (isGroup) { - // Groups are added to a different list - $('#groups').show(); - $(sharedWith).appendTo('#groupList'); - // Remove group from select form - $('#share_with option[value="'+uid_shared_with+'(group)"]').remove(); - $('#share_with').trigger('liszt:updated'); - // Remove users in group from select form - $.each(isGroup, function(index, user) { - $('#share_with option[value="'+user+'"]').remove(); - $('#share_with').trigger('liszt:updated'); - }); - } else { - $(sharedWith).appendTo('#userList'); - // Remove user from select form - $('#share_with option[value="'+uid_shared_with+'"]').remove(); - $('#share_with').trigger('liszt:updated'); - } - - }, - removeSharedWith:function(uid_shared_with) { - var option; - if ($('#userList li[data-uid_shared_with="'+uid_shared_with+'"]').length > 0) { - $('#userList li[data-uid_shared_with="'+uid_shared_with+'"]').remove(); - option = '<option value="'+uid_shared_with+'">'+uid_shared_with+'</option>'; - } else if ($('#groupList li[data-uid_shared_with="'+uid_shared_with+'"]').length > 0) { - $('#groupList li[data-uid_shared_with="'+uid_shared_with+'"]').remove(); - if ($('#groupList li').length < 1) { - $('#groups').hide(); - } - option = '<option value="'+uid_shared_with+'(group)">'+uid_shared_with+' (group)</option>'; - } - $(option).appendTo('#share_with'); - $('#share_with').trigger('liszt:updated'); - }, - showPrivateLink:function(item, token) { - $('#privateLinkCheckbox').attr('checked', true); - var link = parent.location.protocol+'//'+location.host+OC.linkTo('', 'public.php')+'?service=files&token='+token; - if (token.indexOf('&path=') == -1) { - link += '&file=' + encodeURIComponent(item).replace(/%2F/g, '/'); - } else { - // Disable checkbox if inside a shared parent folder - $('#privateLinkCheckbox').attr('disabled', 'true'); - } - $('#privateLinkText').val(link); - $('#privateLinkText').show('blind', function() { - $('#privateLinkText').after('<br id="emailBreak" />'); - $('#email').show(); - $('#emailButton').show(); - }); - }, - hidePrivateLink:function() { - $('#privateLinkText').hide('blind'); - $('#emailBreak').remove(); - $('#email').hide(); - $('#emailButton').hide(); - }, - emailPrivateLink:function() { - var link = $('#privateLinkText').val(); - var file = link.substr(link.lastIndexOf('/') + 1).replace(/%20/g, ' '); - var email = $('#email').val(); - if (email != '') { - $.post(OC.filePath('files_sharing', 'ajax', 'email.php'), { toaddress: email, link: link, file: file }, function(result) { - if (result && result.status == 'success') { - $('#email').css('font-weight', 'bold'); - $('#email').animate({ fontWeight: 'normal' }, 2000, function() { - $(this).val(''); - }).val('Email sent'); - } else { - OC.dialogs.alert(result.data.message, 'Error while sharing'); - } - }); - } - }, - dirname:function(path) { - return path.replace(/\\/g,'/').replace(/\/[^\/]*$/, ''); - } -} - $(document).ready(function() { if (typeof FileActions !== 'undefined') { - OC.Share.loadIcons(); - FileActions.register('all', 'Share', function(filename) { + OC.Share.loadIcons('file'); + FileActions.register('all', 'Share', FileActions.PERMISSION_SHARE, function(filename) { // Return the correct sharing icon if (scanFiles.scanning) { return; } // workaround to prevent additional http request block scanning feedback - var item = $('#dir').val() + '/' + filename; - // Check if icon is in cache - if (OC.Share.icons[item]) { - return OC.Share.icons[item]; + if ($('#dir').val() == '/') { + var item = $('#dir').val() + filename; + } else { + var item = $('#dir').val() + '/' + filename; + } + // Check if status is in cache + if (OC.Share.statuses[item] === true) { + return OC.imagePath('core', 'actions/public'); + } else if (OC.Share.statuses[item] === false) { + return OC.imagePath('core', 'actions/shared'); } else { var last = ''; var path = OC.Share.dirname(item); // Search for possible parent folders that are shared while (path != last) { - if (OC.Share.icons[path]) { - OC.Share.icons[item] = OC.Share.icons[path]; - return OC.Share.icons[item]; + if (OC.Share.statuses[path] === true) { + return OC.imagePath('core', 'actions/public'); + } else if (OC.Share.statuses[path] === false) { + return OC.imagePath('core', 'actions/shared'); } last = path; path = OC.Share.dirname(path); } - OC.Share.icons[item] = OC.imagePath('core', 'actions/share'); - return OC.Share.icons[item]; + return OC.imagePath('core', 'actions/share'); } }, function(filename) { - var file = $('#dir').val() + '/' + filename; - var appendTo = $('tr').filterAttr('data-file',filename).find('td.filename'); + if ($('#dir').val() == '/') { + var item = $('#dir').val() + filename; + } else { + var item = $('#dir').val() + '/' + filename; + } + if ($('tr').filterAttr('data-file', filename).data('type') == 'dir') { + var itemType = 'folder'; + var possiblePermissions = OC.Share.PERMISSION_CREATE | OC.Share.PERMISSION_UPDATE | OC.Share.PERMISSION_DELETE | OC.Share.PERMISSION_SHARE; + } else { + var itemType = 'file'; + var possiblePermissions = OC.Share.PERMISSION_UPDATE | OC.Share.PERMISSION_DELETE | OC.Share.PERMISSION_SHARE; + } + var appendTo = $('tr').filterAttr('data-file', filename).find('td.filename'); // Check if drop down is already visible for a different file - if (($('#dropdown').length > 0)) { - if (file != $('#dropdown').data('item')) { + if (OC.Share.droppedDown) { + if (item != $('#dropdown').data('item')) { OC.Share.hideDropDown(function () { - $('tr').removeClass('mouseOver'); - $('tr').filterAttr('data-file',filename).addClass('mouseOver'); - OC.Share.showDropDown(file, appendTo); + $('tr').filterAttr('data-file', filename).addClass('mouseOver'); + OC.Share.showDropDown(itemType, item, appendTo, true, possiblePermissions); }); + } else { + OC.Share.hideDropDown(); } } else { $('tr').filterAttr('data-file',filename).addClass('mouseOver'); - OC.Share.showDropDown(file, appendTo); + OC.Share.showDropDown(itemType, item, appendTo, true, possiblePermissions); } }); - }; - - $(this).click(function(event) { - if (!($(event.target).hasClass('drop')) && $(event.target).parents().index($('#dropdown')) == -1) { - if ($('#dropdown').is(':visible')) { - OC.Share.hideDropDown(function() { - $('tr').removeClass('mouseOver'); - }); - } - } - }); - - $('#sharedWithList li').live('mouseenter', function(event) { - // Show permissions and unshare button - $(':hidden', this).show(); - }); - - $('#sharedWithList li').live('mouseleave', function(event) { - // Hide permissions and unshare button - $('a', this).hide(); - if (!$('input:[type=checkbox]', this).is(':checked')) { - $('input:[type=checkbox]', this).hide(); - $('label', this).hide(); - } - }); - - $('#share_with').live('change', function() { - var item = $('#dropdown').data('item'); - var uid_shared_with = $(this).val(); - var pos = uid_shared_with.indexOf('(group)'); - var isGroup = false; - if (pos != -1) { - // Remove '(group)' from uid_shared_with - uid_shared_with = uid_shared_with.substr(0, pos); - isGroup = true; - } - OC.Share.share(item, uid_shared_with, 0, function() { - if (isGroup) { - // Reload item because we don't know which users are in the group - OC.Share.loadItem(item); - var users; - $.each(OC.Share.itemGroups, function(index, group) { - if (group.gid == uid_shared_with) { - users = group.users; - } - }); - OC.Share.addSharedWith(uid_shared_with, 0, users, false); - } else { - OC.Share.addSharedWith(uid_shared_with, 0, false, false); - } - // Change icon - if (!OC.Share.itemPrivateLink) { - OC.Share.icons[item] = OC.imagePath('core', 'actions/shared'); - } - }); - }); - - $('.unshare').live('click', function() { - var item = $('#dropdown').data('item'); - var uid_shared_with = $(this).parent().data('uid_shared_with'); - OC.Share.unshare(item, uid_shared_with, function() { - OC.Share.removeSharedWith(uid_shared_with); - // Reload item to update cached users and groups for the icon check - OC.Share.loadItem(item); - // Change icon - if (!OC.Share.itemPrivateLink && !OC.Share.itemUsers && !OC.Share.itemGroups) { - OC.Share.icons[item] = OC.imagePath('core', 'actions/share'); - } - }); - }); - - $('.permissions').live('change', function() { - var permissions = (this.checked) ? 1 : 0; - OC.Share.changePermissions($('#dropdown').data('item'), $(this).parent().data('uid_shared_with'), permissions); - }); - - $('#privateLinkCheckbox').live('change', function() { - var item = $('#dropdown').data('item'); - if (this.checked) { - // Create a private link - OC.Share.share(item, 'public', 0, function(token) { - OC.Share.showPrivateLink(item, token); - // Change icon - OC.Share.icons[item] = OC.imagePath('core', 'actions/public'); - }); - } else { - // Delete private link - OC.Share.unshare(item, 'public', function() { - OC.Share.hidePrivateLink(); - // Change icon - if (OC.Share.itemUsers || OC.Share.itemGroups) { - OC.Share.icons[item] = OC.imagePath('core', 'actions/shared'); - } else { - OC.Share.icons[item] = OC.imagePath('core', 'actions/share'); - } - }); - } - }); - - $('#privateLinkText').live('click', function() { - $(this).focus(); - $(this).select(); - }); + } - $('#emailPrivateLink').live('submit', function(event) { - event.preventDefault(); - OC.Share.emailPrivateLink(); - }); });
\ No newline at end of file diff --git a/apps/files_sharing/l10n/.gitkeep b/apps/files_sharing/l10n/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/apps/files_sharing/l10n/.gitkeep diff --git a/apps/files_sharing/l10n/ca.php b/apps/files_sharing/l10n/ca.php new file mode 100644 index 00000000000..6931389b9c9 --- /dev/null +++ b/apps/files_sharing/l10n/ca.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Size" => "Mida", +"Modified" => "Modificat", +"Delete all" => "Esborra-ho tot", +"Delete" => "Elimina" +); diff --git a/apps/files_sharing/l10n/cs_CZ.php b/apps/files_sharing/l10n/cs_CZ.php new file mode 100644 index 00000000000..7c5828a6136 --- /dev/null +++ b/apps/files_sharing/l10n/cs_CZ.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Size" => "Velikost", +"Modified" => "Upraveno", +"Delete all" => "Smazat vÅ¡e", +"Delete" => "Smazat" +); diff --git a/apps/files_sharing/l10n/de.php b/apps/files_sharing/l10n/de.php new file mode 100644 index 00000000000..d42383b43b0 --- /dev/null +++ b/apps/files_sharing/l10n/de.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Size" => "Größe", +"Modified" => "Geändert", +"Delete all" => "Alle löschen", +"Delete" => "Löschen" +); diff --git a/apps/files_sharing/l10n/el.php b/apps/files_sharing/l10n/el.php new file mode 100644 index 00000000000..63f4c61204a --- /dev/null +++ b/apps/files_sharing/l10n/el.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Size" => "ÎœÎγεθος", +"Modified" => "ΤÏοποποιήθηκε", +"Delete all" => "ΔιαγÏαφή όλων", +"Delete" => "ΔιαγÏαφή" +); diff --git a/apps/files_sharing/l10n/eo.php b/apps/files_sharing/l10n/eo.php new file mode 100644 index 00000000000..81a8dcb1525 --- /dev/null +++ b/apps/files_sharing/l10n/eo.php @@ -0,0 +1,3 @@ +<?php $TRANSLATIONS = array( +"Delete" => "Forigi" +); diff --git a/apps/files_sharing/l10n/es.php b/apps/files_sharing/l10n/es.php new file mode 100644 index 00000000000..85f880927d6 --- /dev/null +++ b/apps/files_sharing/l10n/es.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Size" => "Tamaño", +"Modified" => "Modificado", +"Delete all" => "Eliminar todo", +"Delete" => "Eliminar" +); diff --git a/apps/files_sharing/l10n/et_EE.php b/apps/files_sharing/l10n/et_EE.php new file mode 100644 index 00000000000..da299f4ff70 --- /dev/null +++ b/apps/files_sharing/l10n/et_EE.php @@ -0,0 +1,3 @@ +<?php $TRANSLATIONS = array( +"Delete" => "Kustutamine" +); diff --git a/apps/files_sharing/l10n/fa.php b/apps/files_sharing/l10n/fa.php new file mode 100644 index 00000000000..06e1862e8b3 --- /dev/null +++ b/apps/files_sharing/l10n/fa.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Size" => "اندازه", +"Modified" => "تاریخ", +"Delete all" => "Øذ٠همه", +"Delete" => "ØØ°Ù" +); diff --git a/apps/files_sharing/l10n/fi_FI.php b/apps/files_sharing/l10n/fi_FI.php new file mode 100644 index 00000000000..ca7928aeecb --- /dev/null +++ b/apps/files_sharing/l10n/fi_FI.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Size" => "Koko", +"Modified" => "Muokattu", +"Delete all" => "Poista kaikki", +"Delete" => "Poista" +); diff --git a/apps/files_sharing/l10n/fr.php b/apps/files_sharing/l10n/fr.php new file mode 100644 index 00000000000..5a90331e422 --- /dev/null +++ b/apps/files_sharing/l10n/fr.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Size" => "Taille", +"Modified" => "Modifié", +"Delete all" => "Tout effacer", +"Delete" => "Effacement" +); diff --git a/apps/files_sharing/l10n/it.php b/apps/files_sharing/l10n/it.php new file mode 100644 index 00000000000..7f91b856783 --- /dev/null +++ b/apps/files_sharing/l10n/it.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Size" => "Dimensione", +"Modified" => "Modificato", +"Delete all" => "Elimina tutto", +"Delete" => "Eliminazione" +); diff --git a/apps/files_sharing/l10n/ja_JP.php b/apps/files_sharing/l10n/ja_JP.php new file mode 100644 index 00000000000..9fca88d0a33 --- /dev/null +++ b/apps/files_sharing/l10n/ja_JP.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Size" => "サイズ", +"Modified" => "変更", +"Delete all" => "ã™ã¹ã¦å‰Šé™¤", +"Delete" => "削除" +); diff --git a/apps/files_sharing/l10n/lt_LT.php b/apps/files_sharing/l10n/lt_LT.php new file mode 100644 index 00000000000..d21a3c14f40 --- /dev/null +++ b/apps/files_sharing/l10n/lt_LT.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Size" => "Dydis", +"Modified" => "Pakeista", +"Delete all" => "IÅ¡trinti viskÄ…", +"Delete" => "IÅ¡trinti" +); diff --git a/apps/files_sharing/l10n/nb_NO.php b/apps/files_sharing/l10n/nb_NO.php new file mode 100644 index 00000000000..6102b03db74 --- /dev/null +++ b/apps/files_sharing/l10n/nb_NO.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Size" => "Størrelse", +"Modified" => "Endret", +"Delete all" => "Slett alle", +"Delete" => "Slett" +); diff --git a/apps/files_sharing/l10n/pl.php b/apps/files_sharing/l10n/pl.php new file mode 100644 index 00000000000..7f612126b93 --- /dev/null +++ b/apps/files_sharing/l10n/pl.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Size" => "Rozmiar", +"Modified" => "Zmodyfikowane", +"Delete all" => "UsuÅ„ wszystko", +"Delete" => "UsuÅ„" +); diff --git a/apps/files_sharing/l10n/sl.php b/apps/files_sharing/l10n/sl.php new file mode 100644 index 00000000000..485ab7a85ac --- /dev/null +++ b/apps/files_sharing/l10n/sl.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Size" => "Velikost", +"Modified" => "Spremenjeno", +"Delete all" => "IzbriÅ¡i vse", +"Delete" => "Izbris" +); diff --git a/apps/files_sharing/l10n/sv.php b/apps/files_sharing/l10n/sv.php new file mode 100644 index 00000000000..5a2cda43686 --- /dev/null +++ b/apps/files_sharing/l10n/sv.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Size" => "Storlek", +"Modified" => "Ändrad", +"Delete all" => "Radera alla", +"Delete" => "Radera" +); diff --git a/apps/files_sharing/l10n/th_TH.php b/apps/files_sharing/l10n/th_TH.php new file mode 100644 index 00000000000..3f8908a91a3 --- /dev/null +++ b/apps/files_sharing/l10n/th_TH.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Size" => "ขนาด", +"Modified" => "à¹à¸à¹‰à¹„ขà¹à¸¥à¹‰à¸§", +"Delete all" => "ลบทั้งหมด", +"Delete" => "ลบ" +); diff --git a/apps/files_sharing/lib/share/file.php b/apps/files_sharing/lib/share/file.php new file mode 100644 index 00000000000..ae6315600f8 --- /dev/null +++ b/apps/files_sharing/lib/share/file.php @@ -0,0 +1,91 @@ +<?php +/** +* ownCloud +* +* @author Michael Gapczynski +* @copyright 2012 Michael Gapczynski mtgap@owncloud.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { + + const FORMAT_SHARED_STORAGE = 0; + const FORMAT_FILE_APP = 1; + const FORMAT_FILE_APP_ROOT = 2; + const FORMAT_OPENDIR = 3; + + public function isValidSource($item, $uid) { + if (OC_Filesystem::file_exists($item)) { + return true; + } + return false; + } + + public function getFilePath($item, $uid) { + return $item; + } + + public function generateTarget($item, $uid, $exclude = null) { + // TODO Make sure target path doesn't exist already + return $item; + } + + public function formatItems($items, $format, $parameters = null) { + if ($format == self::FORMAT_SHARED_STORAGE) { + // Only 1 item should come through for this format call + return array('path' => $items[key($items)]['file_source'], 'permissions' => $items[key($items)]['permissions']); + } else if ($format == self::FORMAT_FILE_APP) { + $files = array(); + foreach ($items as $item) { + $file = array(); + $file['path'] = $item['file_target']; + $file['name'] = basename($item['file_target']); + $file['ctime'] = $item['ctime']; + $file['mtime'] = $item['mtime']; + $file['mimetype'] = $item['mimetype']; + $file['size'] = $item['size']; + $file['encrypted'] = $item['encrypted']; + $file['versioned'] = $item['versioned']; + $file['directory'] = $parameters['folder']; + $file['type'] = ($item['mimetype'] == 'httpd/unix-directory') ? 'dir' : 'file'; + $file['permissions'] = $item['permissions']; + if ($file['type'] == 'file') { + // Remove Create permission if type is file + $file['permissions'] &= ~OCP\Share::PERMISSION_CREATE; + } + $files[] = $file; + } + return $files; + } else if ($format == self::FORMAT_FILE_APP_ROOT) { + $mtime = 0; + $size = 0; + foreach ($items as $item) { + if ($item['mtime'] > $mtime) { + $mtime = $item['mtime']; + } + $size += $item['size']; + } + return array(0 => array('name' => 'Shared', 'mtime' => $mtime, 'mimetype' => 'httpd/unix-directory', 'size' => $size, 'writable' => false, 'type' => 'dir', 'directory' => '', 'permissions' => OCP\Share::PERMISSION_READ)); + } else if ($format == self::FORMAT_OPENDIR) { + $files = array(); + foreach ($items as $item) { + $files[] = basename($item['file_target']); + } + return $files; + } + return array(); + } + +}
\ No newline at end of file diff --git a/apps/files_sharing/lib/share/folder.php b/apps/files_sharing/lib/share/folder.php new file mode 100644 index 00000000000..b6db96614fd --- /dev/null +++ b/apps/files_sharing/lib/share/folder.php @@ -0,0 +1,61 @@ +<?php +/** +* ownCloud +* +* @author Michael Gapczynski +* @copyright 2012 Michael Gapczynski mtgap@owncloud.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +class OC_Share_Backend_Folder extends OC_Share_Backend_File { + + public function formatItems($items, $format, $parameters = null) { + if ($format == self::FORMAT_SHARED_STORAGE) { + // Only 1 item should come through for this format call + return array('path' => $items[key($items)]['file_source'], 'permissions' => $items[key($items)]['permissions']); + } else if ($format == self::FORMAT_FILE_APP && isset($parameters['folder'])) { + // Only 1 item should come through for this format call + $folder = $items[key($items)]; + if (isset($parameters['mimetype_filter'])) { + $mimetype_filter = $parameters['mimetype_filter']; + } else { + $mimetype_filter = ''; + } + $path = $folder['file_source'].substr($parameters['folder'], 7 + strlen($folder['file_target'])); + $files = OC_FileCache::getFolderContent($path, '', $mimetype_filter); + foreach ($files as &$file) { + $file['directory'] = $parameters['folder']; + $file['type'] = ($file['mimetype'] == 'httpd/unix-directory') ? 'dir' : 'file'; + $file['permissions'] = $folder['permissions']; + if ($file['type'] == 'file') { + // Remove Create permission if type is file + $file['permissions'] &= ~OCP\Share::PERMISSION_CREATE; + } + } + return $files; + } + return array(); + } + + public function getChildren($itemSource) { + $files = OC_FileCache::getFolderContent($itemSource); + $sources = array(); + foreach ($files as $file) { + $sources[] = $file['path']; + } + return $sources; + } + +}
\ No newline at end of file diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php new file mode 100644 index 00000000000..582c9c66172 --- /dev/null +++ b/apps/files_sharing/lib/sharedstorage.php @@ -0,0 +1,427 @@ +<?php +/** + * ownCloud + * + * @author Michael Gapczynski + * @copyright 2011 Michael Gapczynski mtgap@owncloud.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +/** + * Convert target path to source path and pass the function call to the correct storage provider + */ +class OC_Filestorage_Shared extends OC_Filestorage_Common { + + private $sharedFolder; + private $files = array(); + + public function __construct($arguments) { + $this->sharedFolder = $arguments['sharedFolder']; + } + + /** + * @brief Get the source file path and the permissions granted for a shared file + * @param string Shared target file path + * @return Returns array with the keys path and permissions or false if not found + */ + private function getFile($target) { + $target = '/'.$target; + $target = rtrim($target, '/'); + if (isset($this->files[$target])) { + return $this->files[$target]; + } else { + $pos = strpos($target, '/', 1); + // Get shared folder name + if ($pos !== false) { + $folder = substr($target, 0, $pos); + if (isset($this->files[$folder])) { + $file = $this->files[$folder]; + } else { + $file = OCP\Share::getItemSharedWith('folder', $folder, OC_Share_Backend_File::FORMAT_SHARED_STORAGE); + } + if ($file) { + $this->files[$target]['path'] = $file['path'].substr($target, strlen($folder)); + $this->files[$target]['permissions'] = $file['permissions']; + return $this->files[$target]; + } + } else { + $file = OCP\Share::getItemSharedWith('file', $target, OC_Share_Backend_File::FORMAT_SHARED_STORAGE); + if ($file) { + $this->files[$target] = $file; + return $this->files[$target]; + } + } + OCP\Util::writeLog('files_sharing', 'File source not found for: '.$target, OCP\Util::ERROR); + return false; + } + } + + /** + * @brief Get the source file path for a shared file + * @param string Shared target file path + * @return Returns source file path or false if not found + */ + private function getSourcePath($target) { + $file = $this->getFile($target); + if (isset($file['path'])) { + return $file['path']; + } + return false; + } + + /** + * @brief Get the permissions granted for a shared file + * @param string Shared target file path + * @return Returns CRUDS permissions granted or false if not found + */ + private function getPermissions($target) { + $file = $this->getFile($target); + if (isset($file['permissions'])) { + return $file['permissions']; + } + return false; + } + + /** + * @brief Get the internal path to pass to the storage filesystem call + * @param string Source file path + * @return Source file path with mount point stripped out + */ + private function getInternalPath($path) { + $mountPoint = OC_Filesystem::getMountPoint($path); + $internalPath = substr($path, strlen($mountPoint)); + return $internalPath; + } + + public function mkdir($path) { + if ($path == '' || $path == '/' || !$this->isCreatable(dirname($path))) { + return false; + } else if ($source = $this->getSourcePath($path)) { + $storage = OC_Filesystem::getStorage($source); + return $storage->mkdir($this->getInternalPath($source)); + } + return false; + } + + public function rmdir($path) { + if (($source = $this->getSourcePath($path)) && $this->isDeletable($path)) { + $storage = OC_Filesystem::getStorage($source); + return $storage->rmdir($this->getInternalPath($source)); + } + return false; + } + + public function opendir($path) { + if ($path == '' || $path == '/') { + $files = OCP\Share::getItemsSharedWith('file', OC_Share_Backend_Folder::FORMAT_OPENDIR); + OC_FakeDirStream::$dirs['shared'] = $files; + return opendir('fakedir://shared'); + } else if ($source = $this->getSourcePath($path)) { + $storage = OC_Filesystem::getStorage($source); + return $storage->opendir($this->getInternalPath($source)); + } + return false; + } + + public function is_dir($path) { + if ($path == '' || $path == '/') { + return true; + } else if ($source = $this->getSourcePath($path)) { + $storage = OC_Filesystem::getStorage($source); + return $storage->is_dir($this->getInternalPath($source)); + } + return false; + } + + public function is_file($path) { + if ($source = $this->getSourcePath($path)) { + $storage = OC_Filesystem::getStorage($source); + return $storage->is_file($this->getInternalPath($source)); + } + return false; + } + + public function stat($path) { + if ($path == '' || $path == '/') { + $stat['size'] = $this->filesize($path); + $stat['mtime'] = $this->filemtime($path); + $stat['ctime'] = $this->filectime($path); + return $stat; + } else if ($source = $this->getSourcePath($path)) { + $storage = OC_Filesystem::getStorage($source); + return $storage->stat($this->getInternalPath($source)); + } + return false; + } + + public function filetype($path) { + if ($path == '' || $path == '/') { + return 'dir'; + } else if ($source = $this->getSourcePath($path)) { + $storage = OC_Filesystem::getStorage($source); + return $storage->filetype($this->getInternalPath($source)); + } + return false; + } + + public function filesize($path) { + if ($path == '' || $path == '/' || $this->is_dir($path)) { + return 0; + } else if ($source = $this->getSourcePath($path)) { + $storage = OC_Filesystem::getStorage($source); + return $storage->filesize($this->getInternalPath($source)); + } + return false; + } + + public function isCreatable($path) { + if ($path == '') { + return false; + } + return ($this->getPermissions($path) & OCP\Share::PERMISSION_CREATE); + } + + public function isReadable($path) { + return $this->file_exists($path); + } + + public function isUpdatable($path) { + if ($path == '') { + return false; + } + return ($this->getPermissions($path) & OCP\Share::PERMISSION_UPDATE); + } + + public function isDeletable($path) { + if ($path == '') { + return false; + } + return ($this->getPermissions($path) & OCP\Share::PERMISSION_DELETE); + } + + public function isSharable($path) { + if ($path == '') { + return false; + } + return ($this->getPermissions($path) & OCP\Share::PERMISSION_SHARE); + } + + public function file_exists($path) { + if ($path == '' || $path == '/') { + return true; + } else if ($source = $this->getSourcePath($path)) { + $storage = OC_Filesystem::getStorage($source); + return $storage->file_exists($this->getInternalPath($source)); + } + return false; + } + + public function filectime($path) { + if ($path == '' || $path == '/') { + $ctime = 0; + if ($dh = $this->opendir($path)) { + while (($filename = readdir($dh)) !== false) { + $tempctime = $this->filectime($filename); + if ($tempctime < $ctime) { + $ctime = $tempctime; + } + } + } + return $ctime; + } else { + $source = $this->getSourcePath($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->filectime($this->getInternalPath($source)); + } + } + } + + public function filemtime($path) { + if ($path == '' || $path == '/') { + $mtime = 0; + if ($dh = $this->opendir($path)) { + while (($filename = readdir($dh)) !== false) { + $tempmtime = $this->filemtime($filename); + if ($tempmtime > $mtime) { + $mtime = $tempmtime; + } + } + } + return $mtime; + } else { + $source = $this->getSourcePath($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->filemtime($this->getInternalPath($source)); + } + } + } + + public function file_get_contents($path) { + $source = $this->getSourcePath($path); + if ($source) { + $info = array( + 'target' => $this->sharedFolder.$path, + 'source' => $source, + ); + OCP\Util::emitHook('OC_Filestorage_Shared', 'file_get_contents', $info); + $storage = OC_Filesystem::getStorage($source); + return $storage->file_get_contents($this->getInternalPath($source)); + } + } + + public function file_put_contents($path, $data) { + if ($source = $this->getSourcePath($path)) { + // Check if permission is granted + if (($this->file_exists($path) && !$this->isUpdatable($path)) || ($this->is_dir($path) && !$this->isCreatable($path))) { + return false; + } + $info = array( + 'target' => $this->sharedFolder.$path, + 'source' => $source, + ); + OCP\Util::emitHook('OC_Filestorage_Shared', 'file_put_contents', $info); + $storage = OC_Filesystem::getStorage($source); + $result = $storage->file_put_contents($this->getInternalPath($source), $data); + return $result; + } + return false; + } + + public function unlink($path) { + // Delete the file if DELETE permission is granted + if (($source = $this->getSourcePath($path)) && $this->isDeletable($path)) { + $storage = OC_Filesystem::getStorage($source); + return $storage->unlink($this->getInternalPath($source)); + } + return false; + } + + public function rename($path1, $path2) { + if ($oldSource = $this->getSourcePath($path1)) { + $root1 = substr($path1, 0, strpos($path1, '/')); + $root2 = substr($path2, 0, strpos($path2, '/')); + // Moving/renaming is only allowed within the same shared folder + if ($root1 == $root2) { + $storage = OC_Filesystem::getStorage($oldSource); + $newSource = substr($oldSource, 0, strpos($oldSource, $root1)).$path2; + if (dirname($path1) == dirname($path2)) { + // Rename the file if UPDATE permission is granted + if ($this->isUpdatable($path1)) { + return $storage->rename($this->getInternalPath($oldSource), $this->getInternalPath($newSource)); + } + // Move the file if DELETE and CREATE permissions are granted + } else if ($this->isDeletable($path1) && $this->isCreatable(dirname($path2))) { + return $storage->rename($this->getInternalPath($oldSource), $this->getInternalPath($newSource)); + } + } + } + return false; + } + + public function copy($path1, $path2) { + // Copy the file if CREATE permission is granted + if (($source = $this->getSourcePath($path1)) && $this->isCreatable(dirname($path2))) { + $source = $this->fopen($path1, 'r'); + $target = $this->fopen($path2, 'w'); + return OC_Helper::streamCopy($source, $target); + } + return true; + } + + public function fopen($path, $mode) { + if ($source = $this->getSourcePath($path)) { + switch ($mode) { + case 'r+': + case 'rb+': + case 'w+': + case 'wb+': + case 'x+': + case 'xb+': + case 'a+': + case 'ab+': + case 'w': + case 'wb': + case 'x': + case 'xb': + case 'a': + case 'ab': + if (!$this->isUpdatable($path)) { + return false; + } + } + $info = array( + 'target' => $this->sharedFolder.$path, + 'source' => $source, + 'mode' => $mode, + ); + OCP\Util::emitHook('OC_Filestorage_Shared', 'fopen', $info); + $storage = OC_Filesystem::getStorage($source); + return $storage->fopen($this->getInternalPath($source), $mode); + } + return false; + } + + public function getMimeType($path) { + if ($path == '' || $path == '/') { + return 'httpd/unix-directory'; + } + if ($source = $this->getSourcePath($path)) { + $storage = OC_Filesystem::getStorage($source); + return $storage->getMimeType($this->getInternalPath($source)); + } + return false; + } + + public function free_space($path) { + $source = $this->getSourcePath($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->free_space($this->getInternalPath($source)); + } + } + + public function getLocalFile($path) { + if ($source = $this->getSourcePath($path)) { + $storage = OC_Filesystem::getStorage($source); + return $storage->getLocalFile($this->getInternalPath($source)); + } + return false; + } + public function touch($path, $mtime = null) { + if ($source = $this->getSourcePath($path)) { + $storage = OC_Filesystem::getStorage($source); + return $storage->touch($this->getInternalPath($source), $mtime); + } + return false; + } + + public static function setup($options) { + $user_dir = $options['user_dir']; + OC_Filesystem::mount('OC_Filestorage_Shared', array('sharedFolder' => '/Shared'), $user_dir.'/Shared/'); + } + + /** + * check if a file or folder has been updated since $time + * @param int $time + * @return bool + */ + public function hasUpdated($path,$time){ + //TODO + return false; + } +} diff --git a/apps/files_sharing/lib_share.php b/apps/files_sharing/lib_share.php deleted file mode 100644 index fe312ad9ed7..00000000000 --- a/apps/files_sharing/lib_share.php +++ /dev/null @@ -1,523 +0,0 @@ -<?php -/** - * ownCloud - * - * @author Michael Gapczynski - * @copyright 2011 Michael Gapczynski GapczynskiM@gmail.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - */ - -/** - * This class manages shared items within the database. - */ -class OC_Share { - - const WRITE = 1; - const DELETE = 2; - const UNSHARED = -1; - const PUBLICLINK = "public"; - - private $token; - - /** - * Share an item, adds an entry into the database - * @param $source The source location of the item - * @param $uid_shared_with The user or group to share the item with - * @param $permissions The permissions, use the constants WRITE and DELETE - */ - public function __construct($source, $uid_shared_with, $permissions) { - $uid_owner = OCP\USER::getUser(); - $query = OCP\DB::prepare('INSERT INTO `*PREFIX*sharing` VALUES(?,?,?,?,?)'); - // Check if this is a reshare and use the original source - if ($result = OC_Share::getSource($source)) { - $source = $result; - } - if ($uid_shared_with == self::PUBLICLINK) { - $token = sha1("$uid_shared_with-$source"); - $query->execute(array($uid_owner, self::PUBLICLINK, $source, $token, $permissions)); - $this->token = $token; - } else { - if (OC_Group::groupExists($uid_shared_with)) { - $gid = $uid_shared_with; - $uid_shared_with = OC_Group::usersInGroup($gid); - // Remove the owner from the list of users in the group - $uid_shared_with = array_diff($uid_shared_with, array($uid_owner)); - } else if (OCP\User::userExists($uid_shared_with)) { - if(OCP\Config::getAppValue('files_sharing', 'allowSharingWithEveryone', 'no') == 'yes') { - $gid = null; - $uid_shared_with = array($uid_shared_with); - } else { - $userGroups = OC_Group::getUserGroups($uid_owner); - // Check if the user is in one of the owner's groups - foreach ($userGroups as $group) { - if ($inGroup = OC_Group::inGroup($uid_shared_with, $group)) { - $gid = null; - $uid_shared_with = array($uid_shared_with); - break; - } - } - if (!$inGroup) { - throw new Exception("You can't share with ".$uid_shared_with); - } - } - } else { - throw new Exception($uid_shared_with." is not a user"); - } - foreach ($uid_shared_with as $uid) { - // Check if this item is already shared with the user - $checkSource = OCP\DB::prepare('SELECT `source` FROM `*PREFIX*sharing` WHERE `source` = ? AND `uid_shared_with` '.self::getUsersAndGroups($uid, false)); - $resultCheckSource = $checkSource->execute(array($source))->fetchAll(); - // TODO Check if the source is inside a folder - if (count($resultCheckSource) > 0) { - if (!isset($gid)) { - throw new Exception("This item is already shared with ".$uid); - } else { - // Skip this user if sharing with a group - continue; - } - } - // Check if the target already exists for the user, if it does append a number to the name - $sharedFolder = '/'.$uid.'/files/Shared'; - $target = $sharedFolder."/".basename($source); - $checkTarget = OCP\DB::prepare('SELECT `source` FROM `*PREFIX*sharing` WHERE `target` = ? AND `uid_shared_with` '.self::getUsersAndGroups($uid, false),1); - $result = $checkTarget->execute(array($target))->fetchAll(); - if (count($result) > 0) { - if ($pos = strrpos($target, ".")) { - $name = substr($target, 0, $pos); - $ext = substr($target, $pos); - } else { - $name = $target; - $ext = ""; - } - $counter = 1; - while (count($result) > 0) { - $target = $name."_".$counter.$ext; - $result = $checkTarget->execute(array($target))->fetchAll(); - $counter++; - } - } - // Update mtime of shared folder to invoke a file cache rescan - $rootView=new OC_FilesystemView('/'); - if (!$rootView->is_dir($sharedFolder)) { - if (!$rootView->is_dir('/'.$uid.'/files')) { - OC_Util::tearDownFS(); - OC_Util::setupFS($uid); - OC_Util::tearDownFS(); - } - $rootView->mkdir($sharedFolder); - } - $rootView->touch($sharedFolder); - if (isset($gid)) { - $uid = $uid."@".$gid; - } - $query->execute(array($uid_owner, $uid, $source, $target, $permissions)); - } - } - } - - /** - * Remove any duplicate or trailing '/' from the path - * @return A clean path - */ - private static function cleanPath($path) { - $path = rtrim($path, "/"); - return preg_replace('{(/)\1+}', "/", $path); - } - - /** - * Generate a string to be used for searching for uid_shared_with that handles both users and groups - * @param $uid (Optional) The uid to get the user groups for, a gid to get the users in a group, or if not set the current user - * @return An IN operator as a string - */ - private static function getUsersAndGroups($uid = null, $includePrivateLinks = true) { - $in = " IN("; - if (isset($uid) && OC_Group::groupExists($uid)) { - $users = OC_Group::usersInGroup($uid); - foreach ($users as $user) { - // Add a comma only if the the current element isn't the last - if ($user !== end($users)) { - $in .= "'".$user."@".$uid."', "; - } else { - $in .= "'".$user."@".$uid."'"; - } - } - } else if (isset($uid)) { - // TODO Check if this is necessary, only constructor needs it as IN. It would be better for other queries to just return =$uid - $in .= "'".$uid."'"; - $groups = OC_Group::getUserGroups($uid); - foreach ($groups as $group) { - $in .= ", '".$uid."@".$group."'"; - } - } else { - $uid = OCP\USER::getUser(); - $in .= "'".$uid."'"; - $groups = OC_Group::getUserGroups($uid); - foreach ($groups as $group) { - $in .= ", '".$uid."@".$group."'"; - } - } - if ($includePrivateLinks) { - $in .= ", '".self::PUBLICLINK."'"; - } - $in .= ")"; - return $in; - } - - private static function updateFolder($uid_shared_with) { - if ($uid_shared_with != self::PUBLICLINK) { - if (OC_Group::groupExists($uid_shared_with)) { - $uid_shared_with = OC_Group::usersInGroup($uid_shared_with); - // Remove the owner from the list of users in the group - $uid_shared_with = array_diff($uid_shared_with, array(OCP\USER::getUser())); - } else { - $pos = strrpos($uid_shared_with, '@'); - if ($pos !== false && OC_Group::groupExists(substr($uid_shared_with, $pos + 1))) { - $uid_shared_with = array(substr($uid_shared_with, 0, $pos)); - } else { - $uid_shared_with = array($uid_shared_with); - } - } - foreach ($uid_shared_with as $uid) { - $sharedFolder = $uid.'/files/Shared'; - // Update mtime of shared folder to invoke a file cache rescan - $rootView = new OC_FilesystemView('/'); - $rootView->touch($sharedFolder); - } - } - } - - /** - * Create a new entry in the database for a file inside a shared folder - * - * $oldTarget and $newTarget may be the same value. $oldTarget exists in case the file is being moved outside of the folder - * - * @param $oldTarget The current target location - * @param $newTarget The new target location - */ - public static function pullOutOfFolder($oldTarget, $newTarget) { - $folders = self::getParentFolders($oldTarget); - $source = $folders['source'].substr($oldTarget, strlen($folders['target'])); - $item = self::getItem($folders['target']); - $query = OCP\DB::prepare('INSERT INTO `*PREFIX*sharing` VALUES(?,?,?,?,?)'); - $query->execute(array($item[0]['uid_owner'], OCP\USER::getUser(), $source, $newTarget, $item[0]['permissions'])); - } - - /** - * Get the item with the specified target location - * @param $target The target location of the item - * @return An array with the item - */ - public static function getItem($target) { - $target = self::cleanPath($target); - $query = OCP\DB::prepare('SELECT `uid_owner`, `source`, `permissions` FROM `*PREFIX*sharing` WHERE `target` = ? AND `uid_shared_with` = ?',1); - return $query->execute(array($target, OCP\USER::getUser()))->fetchAll(); - } - - /** - * Get the item with the specified source location - * @param $source The source location of the item - * @return An array with the users and permissions the item is shared with - */ - public static function getMySharedItem($source) { - $source = self::cleanPath($source); - $query = OCP\DB::prepare('SELECT `uid_shared_with`, `permissions` FROM `*PREFIX*sharing` WHERE `source` = ? AND `uid_owner` = ?'); - $result = $query->execute(array($source, OCP\USER::getUser()))->fetchAll(); - if (count($result) > 0) { - return $result; - } else if ($originalSource = self::getSource($source)) { - return $query->execute(array($originalSource, OCP\USER::getUser()))->fetchAll(); - } else { - return false; - } - } - - /** - * Get all items the current user is sharing - * @return An array with all items the user is sharing - */ - public static function getMySharedItems() { - $query = OCP\DB::prepare('SELECT `uid_shared_with`, `source`, `permissions` FROM `*PREFIX*sharing` WHERE `uid_owner` = ?'); - return $query->execute(array(OCP\USER::getUser()))->fetchAll(); - } - - /** - * Get the items within a shared folder that have their own entry for the purpose of name, location, or permissions that differ from the folder itself - * - * Works for both target and source folders. Can be used for getting all items shared with you e.g. pass '/MTGap/files' - * - * @param $folder The folder of the items to look for - * @return An array with all items in the database that are in the folder - */ - public static function getItemsInFolder($folder) { - $folder = self::cleanPath($folder); - // Append '/' in order to filter out the folder itself if not already there - if (substr($folder, -1) !== "/") { - $folder .= "/"; - } - $length = strlen($folder); - $query = OCP\DB::prepare('SELECT `uid_owner`, `source`, `target`, `permissions` FROM `*PREFIX*sharing` WHERE SUBSTR(`source`, 1, ?) = ? OR SUBSTR(`target`, 1, ?) = ? AND `uid_shared_with` '.self::getUsersAndGroups()); - return $query->execute(array($length, $folder, $length, $folder))->fetchAll(); - } - - /** - * Get the source and target parent folders of the specified target location - * @param $target The target location of the item - * @return An array with the keys 'source' and 'target' with the values of the source and target parent folders - */ - public static function getParentFolders($target) { - $target = self::cleanPath($target); - $query = OCP\DB::prepare('SELECT `source` FROM `*PREFIX*sharing` WHERE `target` = ? AND `uid_shared_with` '.self::getUsersAndGroups(),1); - // Prevent searching for user directory e.g. '/MTGap/files' - $userDirectory = substr($target, 0, strpos($target, "files") + 5); - $target = dirname($target); - $result = array(); - while ($target != "" && $target != "/" && $target != "." && $target != $userDirectory && $target != "\\") { - // Check if the parent directory of this target location is shared - $result = $query->execute(array($target))->fetchAll(); - if (count($result) > 0) { - break; - } - $target = dirname($target); - } - if (count($result) > 0) { - // Return both the source folder and the target folder - return array("source" => $result[0]['source'], "target" => $target); - } else { - return false; - } - } - - /** - * Get the source location of the item at the specified target location - * @param $target The target location of the item - * @return Source location or false if target location is not valid - */ - public static function getSource($target) { - $target = self::cleanPath($target); - $query = OCP\DB::prepare('SELECT `source` FROM `*PREFIX*sharing` WHERE `target` = ? AND `uid_shared_with` '.self::getUsersAndGroups(),1); - $result = $query->execute(array($target))->fetchAll(); - if (count($result) > 0) { - return $result[0]['source']; - } else { - $folders = self::getParentFolders($target); - if ($folders == true) { - return $folders['source'].substr($target, strlen($folders['target'])); - } else { - return false; - } - } -} - - public static function getTarget($source) { - $source = self::cleanPath($source); - $query = OCP\DB::prepare('SELECT `target` FROM `*PREFIX*sharing` WHERE `source` = ? AND `uid_owner` = ',1); - $result = $query->execute(array($source, OCP\USER::getUser()))->fetchAll(); - if (count($result) > 0) { - return $result[0]['target']; - } else { - // TODO Check in folders - return false; - } - } - - /** - * Get the user's permissions for the item at the specified target location - * @param $target The target location of the item - * @return The permissions, use bitwise operators to check against the constants WRITE and DELETE - */ - public static function getPermissions($target) { - $target = self::cleanPath($target); - $query = OCP\DB::prepare('SELECT `permissions` FROM `*PREFIX*sharing` WHERE `target` = ? AND `uid_shared_with` '.self::getUsersAndGroups(),1); - $result = $query->execute(array($target))->fetchAll(); - if (count($result) > 0) { - return $result[0]['permissions']; - } else { - $folders = self::getParentFolders($target); - if ($folders == true) { - $result = $query->execute(array($folders['target']))->fetchAll(); - if (count($result) > 0) { - return $result[0]['permissions']; - } - } else { - OCP\Util::writeLog('files_sharing',"Not existing parent folder : ".$target,OCP\Util::ERROR); - return false; - } - } - } - - /** - * Get the token for a public link - * @return The token of the public link, a sha1 hash - */ - public function getToken() { - return $this->token; - } - - /** - * Get the token for a public link - * @param $source The source location of the item - * @return The token of the public link, a sha1 hash - */ - public static function getTokenFromSource($source) { - $query = OCP\DB::prepare('SELECT `target` FROM `*PREFIX*sharing` WHERE `source` = ? AND `uid_shared_with` = ? AND `uid_owner` = ?',1); - $result = $query->execute(array($source, self::PUBLICLINK, OCP\USER::getUser()))->fetchAll(); - if (count($result) > 0) { - return $result[0]['target']; - } else { - return false; - } - } - - /** - * Set the target location to a new value - * - * You must use the pullOutOfFolder() function to change the target location of a file inside a shared folder if the target location differs from the folder - * - * @param $oldTarget The current target location - * @param $newTarget The new target location - */ - public static function setTarget($oldTarget, $newTarget) { - $oldTarget = self::cleanPath($oldTarget); - $newTarget = self::cleanPath($newTarget); - $query = OCP\DB::prepare('UPDATE `*PREFIX*sharing` SET `target` = `REPLACE(`target`, ?, ?) WHERE `uid_shared_with` '.self::getUsersAndGroups()); - $query->execute(array($oldTarget, $newTarget)); - } - - /** - * Change the permissions for the specified item and user - * - * You must construct a new shared item to change the permissions of a file inside a shared folder if the permissions differ from the folder - * - * @param $source The source location of the item - * @param $uid_shared_with The user to change the permissions for - * @param $permissions The permissions, use the constants WRITE and DELETE - */ - public static function setPermissions($source, $uid_shared_with, $permissions) { - $source = self::cleanPath($source); - $query = OCP\DB::prepare('UPDATE `*PREFIX*sharing` SET `permissions` = ? WHERE SUBSTR(`source`, 1, ?) = ? AND `uid_owner` = ? AND `uid_shared_with` '.self::getUsersAndGroups($uid_shared_with)); - $query->execute(array($permissions, strlen($source), $source, OCP\USER::getUser())); - } - - /** - * Unshare the item, removes it from all specified users - * - * You must use the pullOutOfFolder() function to unshare a file inside a shared folder and set $newTarget to nothing - * - * @param $source The source location of the item - * @param $uid_shared_with Array of users to unshare the item from - */ - public static function unshare($source, $uid_shared_with) { - $source = self::cleanPath($source); - $uid_owner = OCP\USER::getUser(); - $query = OCP\DB::prepare('DELETE FROM `*PREFIX*sharing` WHERE SUBSTR(`source`, 1, ?) = ? AND `uid_owner` = ? AND `uid_shared_with` '.self::getUsersAndGroups($uid_shared_with, false)); - $query->execute(array(strlen($source), $source, $uid_owner)); - self::updateFolder($uid_shared_with); - } - - /** - * Unshare the item from the current user, removes it only from the database and doesn't touch the source file - * - * You must use the pullOutOfFolder() function before you call unshareFromMySelf() and set the delete parameter to false to unshare from self a file inside a shared folder - * - * @param $target The target location of the item - * @param $delete (Optional) If true delete the entry from the database, if false the permission is set to UNSHARED - */ - public static function unshareFromMySelf($target, $delete = true) { - $target = self::cleanPath($target); - if ($delete) { - $query = OCP\DB::prepare('DELETE FROM `*PREFIX*sharing` WHERE SUBSTR(`target`, 1, ?) = ? AND `uid_shared_with` '.self::getUsersAndGroups()); - $query->execute(array(strlen($target), $target)); - } else { - $query = OCP\DB::prepare('UPDATE `*PREFIX*sharing` SET `permissions` = ? WHERE SUBSTR(`target`, 1, ?) = ? AND `uid_shared_with` '.self::getUsersAndGroups()); - $query->execute(array(self::UNSHARED, strlen($target), $target)); - } - } - - /** - * Remove the item from the database, the owner deleted the file - * @param $arguments Array of arguments passed from OC_Hook - */ - public static function deleteItem($arguments) { - $source = "/".OCP\USER::getUser()."/files".self::cleanPath($arguments['path']); - $result = self::getMySharedItem($source); - if (is_array($result)) { - foreach ($result as $item) { - self::updateFolder($item['uid_shared_with']); - } - } - $query = OCP\DB::prepare('DELETE FROM `*PREFIX*sharing` WHERE SUBSTR(`source`, 1, ?) = ? AND `uid_owner` = ?'); - $query->execute(array(strlen($source), $source, OCP\USER::getUser())); - } - - /** - * Rename the item in the database, the owner renamed the file - * @param $arguments Array of arguments passed from OC_Hook - */ - public static function renameItem($arguments) { - $oldSource = "/".OCP\USER::getUser()."/files".self::cleanPath($arguments['oldpath']); - $newSource = "/".OCP\USER::getUser()."/files".self::cleanPath($arguments['newpath']); - $query = OCP\DB::prepare('UPDATE `*PREFIX*sharing` SET `source` = REPLACE(`source`, ?, ?) WHERE `uid_owner` = ?'); - $query->execute(array($oldSource, $newSource, OCP\USER::getUser())); - } - - public static function updateItem($arguments) { - $source = "/".OCP\USER::getUser()."/files".self::cleanPath($arguments['path']); - $result = self::getMySharedItem($source); - if (is_array($result)) { - foreach ($result as $item) { - self::updateFolder($item['uid_shared_with']); - } - } - } - - public static function removeUser($arguments) { - $query = OCP\DB::prepare('SELECT `uid_shared_with` FROM `*PREFIX*sharing` WHERE `uid_owner` = ?'); - $result = $query->execute(array($arguments['uid']))->fetchAll(); - if (is_array($result)) { - $result = array_unique($result); - foreach ($result as $item) { - self::updateFolder($item['uid_shared_with']); - } - $query = OCP\DB::prepare('DELETE FROM `*PREFIX*sharing` WHERE `uid_owner` = ? OR `uid_shared_with` '.self::getUsersAndGroups($arguments['uid'])); - $query->execute(array($arguments['uid'])); - } - } - - public static function addToGroupShare($arguments) { - $length = -strlen($arguments['gid']) - 1; - $query = OCP\DB::prepare('SELECT `uid_owner`, `source`, `permissions` FROM `*PREFIX*sharing` WHERE SUBSTR(`uid_shared_with`, '.$length.') = ?'); - $gid = '@'.$arguments['gid']; - $result = $query->execute(array($gid))->fetchAll(); - if (count($result) > 0) { - $lastSource = ''; - for ($i = 0; $i < count($result) - 1; $i++) { - if ($result[$i]['source'] != $lastSource) { - new OC_Share($result[$i]['source'], $arguments['gid'], $result[$i]['permissions']); - $lastSource = $result[$i]['source']; - } - } - } - } - - public static function removeFromGroupShare($arguments) { - $query = OCP\DB::prepare('DELETE FROM `*PREFIX*sharing` WHERE `uid_shared_with` = ?'); - $query->execute(array($arguments['uid'].'@'.$arguments['gid'])); - self::updateFolder($arguments['uid']); - } - -} - -?> diff --git a/apps/files_sharing/settings.php b/apps/files_sharing/settings.php deleted file mode 100644 index fcae7e0370f..00000000000 --- a/apps/files_sharing/settings.php +++ /dev/null @@ -1,10 +0,0 @@ -<?php - -OCP\User::checkAdminUser(); -OCP\Util::addscript('files_sharing', 'settings'); -$tmpl = new OCP\Template('files_sharing', 'settings'); -$tmpl->assign('allowResharing', OCP\Config::getAppValue('files_sharing', 'resharing', 'yes')); -$tmpl->assign('allowSharingWithEveryone', OCP\Config::getAppValue('files_sharing', 'allowSharingWithEveryone', 'no')); -return $tmpl->fetchPage(); - -?>
\ No newline at end of file diff --git a/apps/files_sharing/sharedstorage.php b/apps/files_sharing/sharedstorage.php deleted file mode 100644 index a2dfe34402f..00000000000 --- a/apps/files_sharing/sharedstorage.php +++ /dev/null @@ -1,542 +0,0 @@ -<?php -/** - * ownCloud - * - * @author Michael Gapczynski - * @copyright 2011 Michael Gapczynski GapczynskiM@gmail.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - */ - -require_once( 'lib_share.php' ); - -/** - * Convert target path to source path and pass the function call to the correct storage provider - */ -class OC_Filestorage_Shared extends OC_Filestorage { - - private $datadir; - private $sourcePaths = array(); - - public function __construct($arguments) { - $this->datadir = $arguments['datadir']; - $this->datadir .= "/"; - } - - public function getInternalPath($path) { - $mountPoint = OC_Filesystem::getMountPoint($path); - $internalPath = substr($path, strlen($mountPoint)); - return $internalPath; - } - - public function getSource($target) { - $target = $this->datadir.$target; - if (array_key_exists($target, $this->sourcePaths)) { - return $this->sourcePaths[$target]; - } else { - $source = OC_Share::getSource($target); - $this->sourcePaths[$target] = $source; - return $source; - } - } - - public function mkdir($path) { - if ($path == "" || $path == "/" || !$this->is_writable($path)) { - return false; - } else { - $source = $this->getSource($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - return $storage->mkdir($this->getInternalPath($source)); - } - } - } - - public function rmdir($path) { - // The folder will be removed from the database, but won't be deleted from the owner's filesystem - OC_Share::unshareFromMySelf($this->datadir.$path); - $this->clearFolderSizeCache($path); - } - - public function opendir($path) { - if ($path == "" || $path == "/") { - $path = $this->datadir.$path; - $sharedItems = OC_Share::getItemsInFolder($path); - $files = array(); - foreach ($sharedItems as $item) { - // If item is in the root of the shared storage provider and the item exists add it to the fakedirs - if (dirname($item['target'])."/" == $path && $this->file_exists(basename($item['target']))) { - $files[] = basename($item['target']); - } - } - OC_FakeDirStream::$dirs['shared'.$path] = $files; - return opendir('fakedir://shared'.$path); - } else { - $source = $this->getSource($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - $dh = $storage->opendir($this->getInternalPath($source)); - $modifiedItems = OC_Share::getItemsInFolder($source); - if ($modifiedItems && $dh) { - $sources = array(); - $targets = array(); - // Remove any duplicate or trailing '/' - $path = preg_replace('{(/)\1+}', "/", $path); - $targetFolder = rtrim($this->datadir.$path, "/"); - foreach ($modifiedItems as $item) { - // If the item is in the current directory and the item exists add it to the arrays - if (dirname($item['target']) == $targetFolder && $this->file_exists($path."/".basename($item['target']))) { - // If the item was unshared from self, add it it to the arrays - if ($item['permissions'] == OC_Share::UNSHARED) { - $sources[] = basename($item['source']); - $targets[] = ""; - } else { - $sources[] = basename($item['source']); - $targets[] = basename($item['target']); - } - } - } - // Don't waste time if there aren't any modified items in the current directory - if (empty($sources)) { - return $dh; - } else { - global $FAKEDIRS; - $files = array(); - while (($filename = readdir($dh)) !== false) { - if ($filename != "." && $filename != "..") { - // If the file isn't in the sources array it isn't modified and can be added as is - if (!in_array($filename, $sources)) { - $files[] = $filename; - // The file has a different name than the source and is added to the fakedirs - } else { - $target = $targets[array_search($filename, $sources)]; - // Don't add the file if it was unshared from self by the user - if ($target != "") { - $files[] = $target; - } - } - } - } - $FAKEDIRS['shared'] = $files; - return opendir('fakedir://shared'); - } - } else { - return $dh; - } - } - } - } - - public function is_dir($path) { - if ($path == "" || $path == "/") { - return true; - } else { - $source = $this->getSource($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - return $storage->is_dir($this->getInternalPath($source)); - } - } - } - - public function is_file($path) { - $source = $this->getSource($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - return $storage->is_file($this->getInternalPath($source)); - } - } - - // TODO fill in other components of array - public function stat($path) { - if ($path == "" || $path == "/") { - $stat["size"] = $this->filesize($path); - $stat["mtime"] = $this->filemtime($path); - $stat["ctime"] = $this->filectime($path); - return $stat; - } else { - $source = $this->getSource($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - return $storage->stat($this->getInternalPath($source)); - } - } - } - - public function filetype($path) { - if ($path == "" || $path == "/") { - return "dir"; - } else { - $source = $this->getSource($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - return $storage->filetype($this->getInternalPath($source)); - } - } - - } - - public function filesize($path) { - if ($path == "" || $path == "/" || $this->is_dir($path)) { - return $this->getFolderSize($path); - } else { - $source = $this->getSource($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - return $storage->filesize($this->getInternalPath($source)); - } - } - } - - public function getFolderSize($path) { - return 0; //depricated - } - - private function calculateFolderSize($path) { - if ($this->is_file($path)) { - $path = dirname($path); - } - $size = 0; - if ($dh = $this->opendir($path)) { - while (($filename = readdir($dh)) !== false) { - if ($filename != "." && $filename != "..") { - $subFile = $path."/".$filename; - if ($this->is_file($subFile)) { - $size += $this->filesize($subFile); - } else { - $size += $this->getFolderSize($subFile); - } - } - } - if ($size > 0) { - $dbpath = rtrim($this->datadir.$path, "/"); -// $query = OCP\DB::prepare("INSERT INTO `*PREFIX*foldersize` VALUES(?,?)"); -// $result = $query->execute(array($dbpath, $size)); - } - } - return $size; - } - - private function clearFolderSizeCache($path) { - $path = rtrim($path, "/"); - $path = preg_replace('{(/)\1+}', "/", $path); - if ($this->is_file($path)) { - $path = dirname($path); - } - $dbpath = rtrim($this->datadir.$path, "/"); -// $query = OCP\DB::prepare("DELETE FROM `*PREFIX*/*foldersize*/` WHERE `path` = ?"); -// $result = $query->execute(array($dbpath)); - if ($path != "/" && $path != "") { - $parts = explode("/", $path); - $part = array_pop($parts); - if (empty($part)) { - array_pop($parts); - } - $parent = implode("/", $parts); - $this->clearFolderSizeCache($parent); - } - } - - public function is_readable($path) { - return true; - } - - public function is_writable($path) { - if($path == "" || $path == "/"){ - return false; - }elseif (OC_Share::getPermissions($this->datadir.$path) & OC_Share::WRITE) { - return true; - } else { - return false; - } - } - - public function file_exists($path) { - if ($path == "" || $path == "/") { - return true; - } else { - $source = $this->getSource($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - return $storage->file_exists($this->getInternalPath($source)); - } - } - } - - public function filectime($path) { - if ($path == "" || $path == "/") { - $ctime = 0; - if ($dh = $this->opendir($path)) { - while (($filename = readdir($dh)) !== false) { - $tempctime = $this->filectime($filename); - if ($tempctime < $ctime) { - $ctime = $tempctime; - } - } - } - return $ctime; - } else { - $source = $this->getSource($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - return $storage->filectime($this->getInternalPath($source)); - } - } - } - - public function filemtime($path) { - if ($path == "" || $path == "/") { - $mtime = 0; - if ($dh = $this->opendir($path)) { - while (($filename = readdir($dh)) !== false) { - $tempmtime = $this->filemtime($filename); - if ($tempmtime > $mtime) { - $mtime = $tempmtime; - } - } - } - return $mtime; - } else { - $source = $this->getSource($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - return $storage->filemtime($this->getInternalPath($source)); - } - } - } - - public function file_get_contents($path) { - $source = $this->getSource($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - return $storage->file_get_contents($this->getInternalPath($source)); - } - } - - public function file_put_contents($path, $data) { - if ($this->is_writable($path)) { - $source = $this->getSource($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - $result = $storage->file_put_contents($this->getInternalPath($source), $data); - if ($result) { - $this->clearFolderSizeCache($path); - } - return $result; - } - } - } - - public function unlink($path) { - // The item will be removed from the database, but won't be touched on the owner's filesystem - $target = $this->datadir.$path; - // Check if the item is inside a shared folder - if (OC_Share::getParentFolders($target)) { - // If entry for item already exists - if (OC_Share::getItem($target)) { - OC_Share::unshareFromMySelf($target, false); - } else { - OC_Share::pullOutOfFolder($target, $target); - OC_Share::unshareFromMySelf($target, false); - } - // Delete the database entry - } else { - OC_Share::unshareFromMySelf($target); - } - $this->clearFolderSizeCache($this->getInternalPath($target)); - return true; - } - - public function rename($path1, $path2) { - $oldTarget = $this->datadir.$path1; - $newTarget = $this->datadir.$path2; - // Check if the item is inside a shared folder - if ($folders = OC_Share::getParentFolders($oldTarget)) { - $root1 = substr($path1, 0, strpos($path1, "/")); - $root2 = substr($path1, 0, strpos($path2, "/")); - // Prevent items from being moved into different shared folders until versioning (cut and paste) and prevent items going into 'Shared' - if ($root1 !== $root2) { - return false; - // Check if both paths have write permission - } else if ($this->is_writable($path1) && $this->is_writable($path2)) { - $oldSource = $this->getSource($path1); - $newSource = $folders['source'].substr($newTarget, strlen($folders['target'])); - if ($oldSource) { - $storage = OC_Filesystem::getStorage($oldSource); - return $storage->rename($this->getInternalPath($oldSource), $this->getInternalPath($newSource)); - } - // If the user doesn't have write permission, items can only be renamed and not moved - } else if (dirname($path1) !== dirname($path2)) { - return false; - // The item will be renamed in the database, but won't be touched on the owner's filesystem - } else { - OC_Share::pullOutOfFolder($oldTarget, $newTarget); - // If this is a folder being renamed, call setTarget in case there are any database entries inside the folder - if (self::is_dir($path1)) { - OC_Share::setTarget($oldTarget, $newTarget); - } - } - } else { - OC_Share::setTarget($oldTarget, $newTarget); - } - $this->clearFolderSizeCache($this->getInternalPath($oldTarget)); - $this->clearFolderSizeCache($this->getInternalPath($newTarget)); - return true; - } - - public function copy($path1, $path2) { - if ($path2 == "" || $path2 == "/") { - // TODO Construct new shared item or should this not be allowed? - } else { - if ($this->is_writable($path2)) { - $tmpFile = $this->toTmpFile($path1); - $result = $this->fromTmpFile($tmpFile, $path2); - if ($result) { - $this->clearFolderSizeCache($path2); - } - return $result; - } else { - return false; - } - } - } - - public function fopen($path, $mode) { - $source = $this->getSource($path); - if ($source) { - switch ($mode) { - case 'r+': - case 'rb+': - case 'w+': - case 'wb+': - case 'x+': - case 'xb+': - case 'a+': - case 'ab+': - case 'w': - case 'wb': - case 'x': - case 'xb': - case 'a': - case 'ab': - if (!$this->is_writable($path)) { - return false; - } - } - $storage = OC_Filesystem::getStorage($source); - return $storage->fopen($this->getInternalPath($source), $mode); - } - } - - public function toTmpFile($path) { - $source = $this->getSource($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - return $storage->toTmpFile($this->getInternalPath($source)); - } - } - - public function fromTmpFile($tmpFile, $path) { - if ($this->is_writable($path)) { - $source = $this->getSource($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - $result = $storage->fromTmpFile($tmpFile, $this->getInternalPath($source)); - if ($result) { - $this->clearFolderSizeCache($path); - } - return $result; - } - } else { - return false; - } - } - - public function getMimeType($path) { - if ($path == "" || $path == "/") { - return 'httpd/unix-directory'; - } - $source = $this->getSource($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - return $storage->getMimeType($this->getInternalPath($source)); - } - } - - public function hash($type, $path, $raw = false) { - $source = $this->getSource($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - return $storage->hash($type, $this->getInternalPath($source), $raw); - } - } - - public function free_space($path) { - $source = $this->getSource($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - return $storage->free_space($this->getInternalPath($source)); - } - } - - public function search($query) { - return $this->searchInDir($query); - } - - private function searchInDir($query, $path = "") { - $files = array(); - if ($dh = $this->opendir($path)) { - while (($filename = readdir($dh)) !== false) { - if ($filename != "." && $filename != "..") { - if (strstr(strtolower($filename), strtolower($query))) { - $files[] = $path."/".$filename; - } - if ($this->is_dir($path."/".$filename)) { - $files = array_merge($files, $this->searchInDir($query, $path."/".$filename)); - } - } - } - } - return $files; - } - - public function getLocalFile($path) { - $source = $this->getSource($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - return $storage->getLocalFile($this->getInternalPath($source)); - } - } - public function touch($path, $mtime=null){ - $source = $this->getSource($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - return $storage->touch($this->getInternalPath($source),$time); - } - } - - public static function setup() { - OC_Filesystem::mount('OC_Filestorage_Shared', array('datadir' => '/'.OCP\USER::getUser().'/files/Shared'), '/'.OCP\USER::getUser().'/files/Shared/'); - } - -} - -if (OCP\USER::isLoggedIn()) { - OC_Filestorage_Shared::setup(); -} else { - OCP\Util::connectHook('OC_User', 'post_login', 'OC_Filestorage_Shared', 'setup'); -} - -?> diff --git a/apps/files_sharing/templates/get.php b/apps/files_sharing/templates/get.php new file mode 100755 index 00000000000..57275f07a3d --- /dev/null +++ b/apps/files_sharing/templates/get.php @@ -0,0 +1,11 @@ +<table> + <thead> + <tr> + <th id="headerSize"><?php echo $l->t( 'Size' ); ?></th> + <th id="headerDate"><span id="modified"><?php echo $l->t( 'Modified' ); ?></span><span class="selectedActions"><a href="" class="delete"><?php echo $l->t('Delete all')?> <img class="svg" alt="<?php echo $l->t('Delete')?>" src="<?php echo OCP\image_path("core", "actions/delete.svg"); ?>" /></a></span></th> + </tr> + </thead> + <tbody id="fileList" data-readonly="<?php echo $_['readonly'];?>"> + <?php echo($_['fileList']); ?> + </tbody> +</table>
\ No newline at end of file diff --git a/apps/files_sharing/templates/list.php b/apps/files_sharing/templates/list.php deleted file mode 100644 index d46ff818ac1..00000000000 --- a/apps/files_sharing/templates/list.php +++ /dev/null @@ -1,30 +0,0 @@ -<fieldset> - <legend><?php echo $l->t('Your Shared Files');?></legend> - <table id="itemlist"> - <thead> - <tr> - <th><?php echo $l->t('Item');?></th> - <th><?php echo $l->t('Shared With');?></th> - <th><?php echo $l->t('Permissions');?></th> - </tr> - </thead> - <tbody> - <?php foreach($_['shared_items'] as $item):?> - <tr class="item"> - <td class="source"><?php echo substr($item['source'], strlen("/".$_SESSION['user_id']."/files/"));?></td> - <td class="uid_shared_with"><?php echo $item['uid_shared_with'];?></td> - <td class="permissions"><?php echo $l->t('Read'); echo($item['permissions'] & OC_SHARE::WRITE ? ", ".$l->t('Edit') : ""); echo($item['permissions'] & OC_SHARE::DELETE ? ", ".$l->t('Delete') : "");?></td> - <td><button class="delete" data-source="<?php echo $item['source'];?>" data-uid_shared_with="<?php echo $item['uid_shared_with'];?>"><?php echo $l->t('Delete');?></button></td> - </tr> - <?php endforeach;?> - <tr id="share_item_row"> - <form action="#" id="share_item"> - <td class="source"><input placeholder="Item" id="source" /></td> - <td class="uid_shared_with"><input placeholder="Share With" id="uid_shared_with" /></td> - <td class="permissions"><input placeholder="Permissions" id="permissions" /></td> - <td><input type="submit" value="Share" /></td> - </form> - </tr> - </tbody> - </table> -</fieldset> diff --git a/apps/files_sharing/templates/settings.php b/apps/files_sharing/templates/settings.php deleted file mode 100644 index 533a5c0c0c8..00000000000 --- a/apps/files_sharing/templates/settings.php +++ /dev/null @@ -1,8 +0,0 @@ -<form id="resharing"> - <fieldset class="personalblock"> - <p><input type="checkbox" name="allowResharing" id="allowResharing" value="1" <?php if ($_['allowResharing'] == 'yes') echo ' checked="checked"'; ?> /> <label for="allowResharing"><?php echo $l->t('Enable Resharing'); ?></label> <br/> - <em><?php echo $l->t('Allow users to reshare files they don\'t own');?></em></p> - <p><input type="checkbox" name="allowSharingWithEveryone" id="allowSharingWithEveryone" value="1" <?php if ($_['allowSharingWithEveryone'] == 'yes') echo ' checked="checked"'; ?> /> <label for="allowSharingWithEveryone"><?php echo $l->t('Enable sharing with everyone'); ?></label> <br/> - <em><?php echo $l->t('Allow users to share files with everyone');?></em></p> - </fieldset> -</form>
\ No newline at end of file diff --git a/apps/files_sharing_log/appinfo/app.php b/apps/files_sharing_log/appinfo/app.php new file mode 100644 index 00000000000..23cae61fbf4 --- /dev/null +++ b/apps/files_sharing_log/appinfo/app.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +OC::$CLASSPATH['OC_Files_Sharing_Log'] = 'apps/files_sharing_log/log.php'; + +$l=new OC_L10N('files_sharing_log'); +OCP\App::addNavigationEntry( array( + 'id' => 'files_sharing_log_index', + 'order' => 5, + 'href' => OCP\Util::linkTo( 'files_sharing_log', 'index.php' ), + 'icon' => OCP\Util::imagePath( 'files_sharing_log', 'icon.png' ), + 'name' => $l->t('Shared files log')) +); + +OCP\Util::connectHook('OC_Filestorage_Shared', 'fopen', 'OC_Files_Sharing_Log', 'fopen'); +OCP\Util::connectHook('OC_Filestorage_Shared', 'file_get_contents', 'OC_Files_Sharing_Log', 'file_get_contents'); +OCP\Util::connectHook('OC_Filestorage_Shared', 'file_put_contents', 'OC_Files_Sharing_Log', 'file_put_contents'); diff --git a/apps/files_sharing_log/appinfo/database.xml b/apps/files_sharing_log/appinfo/database.xml new file mode 100644 index 00000000000..dae811f87fa --- /dev/null +++ b/apps/files_sharing_log/appinfo/database.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="ISO-8859-1" ?> +<database> + <name>*dbname*</name> + <create>true</create> + <overwrite>false</overwrite> + <charset>utf8</charset> + <table> + <name>*dbprefix*sharing_log</name> + <declaration> + <field> + <name>user_id</name> + <type>text</type> + <notnull>true</notnull> + <length>64</length> + </field> + <field> + <name>source</name> + <type>text</type> + <notnull>true</notnull> + <length>128</length> + </field> + <field> + <name>uid_who</name> + <type>text</type> + <notnull>true</notnull> + <length>64</length> + </field> + <field> + <name>when</name> + <type>integer</type> + <default></default> + <notnull>false</notnull> + <unsigned>true</unsigned> + <length>4</length> + </field> + <field> + <name>mode</name> + <type>text</type> + <notnull>true</notnull> + <length>4</length> + </field> + </declaration> + </table> +</database> diff --git a/apps/files_sharing_log/appinfo/info.xml b/apps/files_sharing_log/appinfo/info.xml new file mode 100644 index 00000000000..d5e3283df3f --- /dev/null +++ b/apps/files_sharing_log/appinfo/info.xml @@ -0,0 +1,10 @@ +<?xml version="1.0"?> +<info> + <id>files_sharing_log</id> + <name>File Shared access logging app</name> + <description>Log access to shared files</description> + <licence>AGPL</licence> + <author>Bart Visscher</author> + <require>4</require> + <shipped>true</shipped> +</info> diff --git a/apps/files_sharing_log/appinfo/version b/apps/files_sharing_log/appinfo/version new file mode 100644 index 00000000000..49d59571fbf --- /dev/null +++ b/apps/files_sharing_log/appinfo/version @@ -0,0 +1 @@ +0.1 diff --git a/apps/files_sharing_log/css/style.css b/apps/files_sharing_log/css/style.css new file mode 100644 index 00000000000..069d3a45e0d --- /dev/null +++ b/apps/files_sharing_log/css/style.css @@ -0,0 +1,7 @@ +#files_sharing_log { +padding: 2em; +} +#files_sharing_log th, +#files_sharing_log td { +padding: 0 1em; +} diff --git a/apps/files_sharing_log/index.php b/apps/files_sharing_log/index.php new file mode 100644 index 00000000000..ffacbdd8604 --- /dev/null +++ b/apps/files_sharing_log/index.php @@ -0,0 +1,21 @@ +<?php +/** + * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +OCP\User::checkLoggedIn(); +OCP\App::checkAppEnabled('files_sharing_log'); + +OCP\App::setActiveNavigationEntry('files_sharing_log_index'); + +OCP\Util::addStyle('files_sharing_log', 'style'); + +$query = OCP\DB::prepare('SELECT * FROM *PREFIX*sharing_log WHERE user_id = ?'); +$log = $query->execute(array(OCP\User::getUser()))->fetchAll(); + +$output = new OCP\Template('files_sharing_log', 'index', 'user'); +$output->assign('log', $log); +$output->printPage(); diff --git a/apps/files_sharing_log/log.php b/apps/files_sharing_log/log.php new file mode 100644 index 00000000000..e6a12b9fb1d --- /dev/null +++ b/apps/files_sharing_log/log.php @@ -0,0 +1,34 @@ +<?php + +class OC_Files_Sharing_Log { + static public function fopen($arguments) { + $target = $arguments['target']; + $source = $arguments['source']; + $mode = $arguments['mode']; + self::log($target, $source, $mode); + } + + static public function file_get_contents($arguments) { + $target = $arguments['target']; + $source = $arguments['source']; + $mode = 'get'; + self::log($target, $source, $mode); + } + + static public function file_put_contents($arguments) { + $target = $arguments['target']; + $source = $arguments['source']; + $mode = 'put'; + self::log($target, $source, $mode); + } + + static public function log($target, $source, $mode) { + $query = OCP\DB::prepare("SELECT * FROM *PREFIX*sharing WHERE source = ? AND target = ?"); + $info = $query->execute(array($source, $target))->fetchAll(); + $info = $info[0]; + //var_dump($info); + $query = OCP\DB::prepare("INSERT INTO *PREFIX*sharing_log VALUES (?,?,?,?,?)"); + $query->execute(array($info['uid_owner'], $source, OCP\User::getUser(), time(), $mode)); + //die; + } +} diff --git a/apps/files_sharing_log/templates/index.php b/apps/files_sharing_log/templates/index.php new file mode 100644 index 00000000000..55bfc1d6a3c --- /dev/null +++ b/apps/files_sharing_log/templates/index.php @@ -0,0 +1,42 @@ +<table id="files_sharing_log"> + <thead> + <tr> + <th><?php echo $l->t('File') ?></th> + <th><?php echo $l->t('Who') ?></th> + <th><?php echo $l->t('When') ?></th> + <th><?php echo $l->t('What') ?></th> + </tr> + </thead> + <tbody> + <?php foreach($_['log'] as $log): ?> + <tr> + <td> + <?php echo $log['source'] ?> + </td> + <td> + <?php echo $log['uid_who'] ?> + </td> + <td> + <?php echo date('Y-m-d H:i:s', $log['when']) ?> + </td> + <td> + <?php switch ($log['mode']): + case 'get': + echo $l->t('Read'); + break; + case 'put': + echo $l->t('Write'); + break; + default: + if (strpos('r', $log['mode']) !== false): + echo $l->t('Read'); + else: + echo $l->t('Write'); + endif; + endswitch; + ?> + </td> + </tr> + <?php endforeach; ?> + </tbody> +</table> diff --git a/apps/files_texteditor/ajax/loadfile.php b/apps/files_texteditor/ajax/loadfile.php index c263306e719..5a5affa46be 100644 --- a/apps/files_texteditor/ajax/loadfile.php +++ b/apps/files_texteditor/ajax/loadfile.php @@ -43,6 +43,7 @@ if(!empty($filename)) { $mtime = OC_Filesystem::filemtime($path); $filecontents = OC_Filesystem::file_get_contents($path); + $filecontents = iconv(mb_detect_encoding($filecontents), "UTF-8", $filecontents); OCP\JSON::success(array('data' => array('filecontents' => $filecontents, 'write' => 'false', 'mtime' => $mtime))); } } else { diff --git a/apps/files_texteditor/ajax/savefile.php b/apps/files_texteditor/ajax/savefile.php index aa24d07eef1..f3ac323e32f 100644 --- a/apps/files_texteditor/ajax/savefile.php +++ b/apps/files_texteditor/ajax/savefile.php @@ -49,6 +49,7 @@ if($path != '' && $mtime != '' && $filecontents) // Save file if(OC_Filesystem::is_writable($path)) { + $filecontents = iconv(mb_detect_encoding($filecontents), "UTF-8", $filecontents); OC_Filesystem::file_put_contents($path, $filecontents); // Clear statcache clearstatcache(); diff --git a/apps/files_texteditor/appinfo/app.php b/apps/files_texteditor/appinfo/app.php index a08077ebb67..1f9773bca3a 100644 --- a/apps/files_texteditor/appinfo/app.php +++ b/apps/files_texteditor/appinfo/app.php @@ -1,6 +1,6 @@ <?php //load the required files +OCP\Util::addStyle( 'files_texteditor', 'DroidSansMono/stylesheet' ); OCP\Util::addStyle( 'files_texteditor', 'style' ); OCP\Util::addscript( 'files_texteditor', 'editor'); OCP\Util::addscript( 'files_texteditor', 'aceeditor/ace'); -?> diff --git a/apps/files_texteditor/css/DroidSansMono/DroidSansMono-webfont.eot b/apps/files_texteditor/css/DroidSansMono/DroidSansMono-webfont.eot Binary files differnew file mode 100644 index 00000000000..bc2d524aa76 --- /dev/null +++ b/apps/files_texteditor/css/DroidSansMono/DroidSansMono-webfont.eot diff --git a/apps/files_texteditor/css/DroidSansMono/DroidSansMono-webfont.svg b/apps/files_texteditor/css/DroidSansMono/DroidSansMono-webfont.svg new file mode 100644 index 00000000000..4ca5fc20dc5 --- /dev/null +++ b/apps/files_texteditor/css/DroidSansMono/DroidSansMono-webfont.svg @@ -0,0 +1,630 @@ +<?xml version="1.0" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" > +<svg xmlns="http://www.w3.org/2000/svg"> +<metadata> +This is a custom SVG webfont generated by Font Squirrel. +Copyright : Digitized data copyright 2006 Google Corporation +Foundry : Ascender Corporation +Foundry URL : httpwwwascendercorpcom +</metadata> +<defs> +<font id="webfontq63XZdUr" horiz-adv-x="1228" > +<font-face units-per-em="2048" ascent="1638" descent="-410" /> +<missing-glyph horiz-adv-x="500" /> +<glyph unicode=" " /> +<glyph unicode="!" d="M487 110.5q0 139.5 127 139.5t127 -139.5t-127 -139.5t-127 139.5zM504 1462h223l-51 -1048h-121z" /> +<glyph unicode=""" d="M285 1462h237l-41 -528h-155zM707 1462h237l-41 -528h-155z" /> +<glyph unicode="#" d="M45 428v137h244l65 328h-233v137h258l82 432h147l-82 -432h293l84 432h144l-84 -432h221v-137h-248l-64 -328h240v-137h-266l-82 -428h-148l84 428h-290l-82 -428h-144l78 428h-217zM436 565h291l64 328h-291z" /> +<glyph unicode="$" d="M182 172v172q197 -92 365 -92v434q-197 66 -271 145q-78 84 -77 220q0 131 92 217q90 84 256 106v180h137v-176q182 -8 336 -78l-66 -145q-143 63 -270 72v-422q199 -68 279 -148q82 -82 81 -211q0 -281 -360 -335v-230h-137v221q-228 0 -365 70zM375 1049q0 -76 41 -121 q41 -43 131 -74v369q-172 -27 -172 -174zM684 262q184 28 184 184q0 127 -184 189v-373z" /> +<glyph unicode="%" d="M0 1133q0 164 80 256q78 90 215 90q131 0 211 -92.5t80 -253.5q0 -166 -78 -256q-80 -92 -217 -93q-131 0 -211 95q-80 96 -80 254zM152 1133q0 -229 141 -230q139 0 139 230q0 225 -139 225q-141 0 -141 -225zM170 0l729 1462h158l-729 -1462h-158zM643 330 q0 164 80 256q78 90 215 90q131 0 211 -92t80 -254q0 -164 -78 -254q-82 -94 -217 -94q-131 0 -211 94q-80 96 -80 254zM795 330q0 -229 141 -230q139 0 139 230q0 225 -139 225q-141 0 -141 -225z" /> +<glyph unicode="&" d="M61 381q0 133 64 229q63 98 235 199q-164 193 -163 356q0 147 96 233.5t274 86.5q168 0 260.5 -86t92.5 -234q0 -203 -318 -389l281 -348q70 119 104 266h184q-53 -236 -178 -403l234 -291h-217l-131 166q-174 -186 -412 -186q-190 0 -297 106q-109 109 -109 295z M252 387q0 -104 67 -176q68 -70 160 -70q162 0 299 146l-323 401q-203 -123 -203 -301zM375 1165q0 -117 133 -268q133 80 184 139q49 57 49 133q0 72 -49 119q-51 47 -131 47q-86 0 -137 -45q-49 -43 -49 -125z" /> +<glyph unicode="'" d="M496 1462h237l-41 -528h-155z" /> +<glyph unicode="(" d="M295 567q0 532 444 895h193q-449 -375 -449 -893q0 -522 447 -893h-191q-444 352 -444 891z" /> +<glyph unicode=")" d="M297 1462h192q444 -362 445 -895q0 -541 -445 -891h-190q446 373 446 893q1 518 -448 893z" /> +<glyph unicode="*" d="M133 1081l29 193l391 -111l-43 393h205l-43 -393l397 111l27 -193l-379 -28l246 -326l-179 -96l-176 358l-157 -358l-185 96l242 326z" /> +<glyph unicode="+" d="M152 647v150h387v389h149v-389h387v-150h-387v-385h-149v385h-387z" /> +<glyph unicode="," d="M440 -289q76 322 111 551h219l16 -24q-59 -229 -194 -527h-152z" /> +<glyph unicode="-" d="M285 465v168h659v-168h-659z" /> +<glyph unicode="." d="M463 135q0 166 151.5 166t151.5 -166t-151.5 -166t-151.5 166z" /> +<glyph unicode="/" d="M211 0l627 1462h178l-627 -1462h-178z" /> +<glyph unicode="0" d="M147 733q0 752 465 752q231 0 350 -192.5t119 -559.5q0 -754 -469 -753q-231 0 -348 194q-117 193 -117 559zM332 733q0 -317 67 -459q68 -139 213 -139q147 0 215 141q70 145 70 457q0 309 -70 455q-68 141 -215 141q-145 0 -213 -139q-67 -138 -67 -457z" /> +<glyph unicode="1" d="M225 1163l383 299h150v-1462h-176v913q0 147 8 361q-43 -47 -121 -113l-147 -121z" /> +<glyph unicode="2" d="M158 0v156l350 381q201 219 258 317q61 104 61 231q0 115 -63 179q-66 66 -172 65q-162 0 -318 -137l-102 119q190 172 422 172q197 0 305 -107q113 -109 113 -284q0 -115 -59.5 -244t-290.5 -375l-281 -299v-8h688v-166h-911z" /> +<glyph unicode="3" d="M131 59v170q186 -96 383 -96q354 0 354 289q0 258 -381 258h-133v151h133q160 0 248 76t88 201q0 104 -67 162q-70 59 -183 59q-176 0 -344 -121l-92 125q186 150 436 150q205 0 322 -99q115 -96 115 -264q0 -141 -82 -231q-86 -94 -234 -119v-6q360 -45 361 -348 q0 -203 -137 -320q-138 -117 -400 -116q-243 -1 -387 79z" /> +<glyph unicode="4" d="M61 328v159l664 983h188v-976h213v-166h-213v-328h-176v328h-676zM240 494h497v356q0 178 13 432h-9q-41 -111 -90 -180z" /> +<glyph unicode="5" d="M172 59v172q145 -96 360 -96q336 0 336 314q0 295 -344 294q-78 0 -231 -26l-90 57l55 688h690v-166h-532l-39 -419q102 20 209 20q209 0 340 -115q129 -114 129 -313q0 -233 -137 -363q-135 -127 -390 -126q-224 -1 -356 79z" /> +<glyph unicode="6" d="M154 625q0 858 639 858q104 0 172 -19v-155q-71 25 -166 24q-221 0 -336 -141t-123 -447h12q96 170 307 170q195 0 306 -118q111 -120 110 -326q0 -227 -121 -360q-119 -131 -323 -131q-219 0 -348 170t-129 475zM336 506q0 -150 82 -262q80 -111 211 -111q129 0 198 88 q72 90 72 250q0 147 -68 223q-68 78 -196 78q-125 0 -213 -82q-86 -80 -86 -184z" /> +<glyph unicode="7" d="M143 1296v166h940v-145l-555 -1317h-194l563 1296h-754z" /> +<glyph unicode="8" d="M156 373q0 258 282 393q-236 150 -235 369q0 160 116 256q115 94 295 94q184 0 297 -94q115 -96 115 -258q0 -229 -262 -359q309 -160 309 -393q0 -180 -127 -291q-126 -111 -332 -110q-217 0 -337.5 104.5t-120.5 288.5zM334 371q0 -240 276 -240q135 0 211 66 q74 66 74 182q0 90 -63.5 159.5t-213.5 143.5l-30 14q-254 -118 -254 -325zM381 1126q0 -92 49 -153q47 -57 186 -125q231 104 232 278q0 102 -64 154q-66 53 -172 53q-104 0 -167.5 -53.5t-63.5 -153.5z" /> +<glyph unicode="9" d="M154 991q0 227 120 361q119 131 324 131q221 0 348 -170q129 -172 129 -475q0 -858 -639 -858q-104 0 -172 18v156q68 -25 166 -25q221 0 336 141.5t123 446.5h-12q-96 -170 -308 -170q-193 0 -305 119q-110 116 -110 325zM330 991q0 -143 69 -223q68 -78 195 -78 q129 0 213 82q86 84 86 184q0 152 -80 262.5t-213 110.5q-127 0 -198.5 -88t-71.5 -250z" /> +<glyph unicode=":" d="M487 110.5q0 139.5 127 139.5t127 -139.5t-127 -139.5t-127 139.5zM487 987q0 139 127 139t127 -139t-127 -139t-127 139z" /> +<glyph unicode=";" d="M410 -264q66 276 100 502h199l14 -23q-55 -209 -176 -479h-137zM494 987q0 139 127 139t127 -139t-127 -139t-127 139z" /> +<glyph unicode="<" d="M152 672v102l923 451v-160l-715 -342l715 -342v-160z" /> +<glyph unicode="=" d="M152 442v150h923v-150h-923zM152 852v149h923v-149h-923z" /> +<glyph unicode=">" d="M152 221v160l714 342l-714 342v160l923 -451v-102z" /> +<glyph unicode="?" d="M168 1386q205 96 426 97q217 0 340 -97q125 -98 125 -262q0 -133 -53 -217q-52 -83 -197 -190q-119 -88 -151.5 -141.5t-32.5 -143.5v-18h-160v37q0 119 47 194.5t160 157.5q131 100 172 155.5t41 157.5q0 92 -74 150q-72 57 -207 57q-172 0 -371 -90zM426 110.5 q0 139.5 127 139.5t127 -139.5t-127 -139.5t-127 139.5z" /> +<glyph unicode="@" d="M31 602q0 395 168 629q166 231 454 231q248 0 396 -196q150 -199 149 -535q0 -236 -63 -371q-66 -139 -179 -139q-131 0 -157 180h-4q-74 -180 -230 -180q-120 0 -190 105q-70 102 -70 280q0 209 96 332q98 127 256 127q133 0 256 -47l-22 -416q-2 -25 -2 -70v-6 q0 -176 72 -176q100 0 100 383q0 279 -110.5 436.5t-299.5 157.5q-225 0 -352 -192.5t-127 -526.5q0 -311 129 -483q127 -170 369 -170q178 0 346 78v-133q-156 -82 -352 -82q-295 0 -465 207q-168 204 -168 577zM465 602q0 -252 127 -252q131 0 145 312l15 253 q-53 20 -103 21q-90 0 -137 -94.5t-47 -239.5z" /> +<glyph unicode="A" d="M33 0l483 1468h195l485 -1468h-192l-144 453h-491l-146 -453h-190zM422 618h385l-133 424q-39 121 -62 226q-20 -88 -47 -183z" /> +<glyph unicode="B" d="M135 0v1462h440q272 0 398 -88q123 -86 123 -282q0 -129 -78 -213t-213 -103v-10q332 -55 332 -342q0 -199 -127 -311.5t-348 -112.5h-527zM322 158h307q311 0 311 274q0 254 -324 254h-294v-528zM322 842h284q157 0 228 55q72 55 71 182q0 121 -75.5 172.5t-243.5 51.5 h-264v-461z" /> +<glyph unicode="C" d="M129 733q0 344 178 547t490 203q219 0 383 -86l-78 -156q-156 78 -305 78q-215 0 -342 -158q-129 -160 -129 -430q0 -287 120 -438q119 -150 351 -150q135 0 327 58v-162q-150 -59 -358 -59q-310 0 -473 196q-164 199 -164 557z" /> +<glyph unicode="D" d="M135 0v1462h342q319 0 494 -188q176 -193 176 -529q0 -365 -182 -552q-186 -193 -529 -193h-301zM322 160h96q532 0 532 579q0 563 -493 564h-135v-1143z" /> +<glyph unicode="E" d="M217 0v1462h842v-164h-656v-452h617v-162h-617v-520h656v-164h-842z" /> +<glyph unicode="F" d="M244 0v1462h841v-164h-655v-516h617v-164h-617v-618h-186z" /> +<glyph unicode="G" d="M117 733q0 352 161.5 551t446.5 199q193 0 346 -86l-72 -162q-150 84 -280 84q-193 0 -299.5 -155.5t-106.5 -432.5q0 -588 394 -588q94 0 202 29v436h-237v164h422v-717q-199 -76 -420 -75q-262 0 -410 200q-147 200 -147 553z" /> +<glyph unicode="H" d="M135 0v1462h187v-616h585v616h187v-1462h-187v682h-585v-682h-187z" /> +<glyph unicode="I" d="M225 0v123l295 20v1176l-295 20v123h776v-123l-294 -20v-1176l294 -20v-123h-776z" /> +<glyph unicode="J" d="M137 39v166q162 -61 309 -62q162 0 254.5 80t92.5 226v1013h186v-1011q0 -215 -141 -345q-139 -127 -377 -126q-215 0 -324 59z" /> +<glyph unicode="K" d="M211 0v1462h186v-731l121 168l453 563h209l-521 -637l539 -825h-211l-450 698l-140 -114v-584h-186z" /> +<glyph unicode="L" d="M233 0v1462h187v-1296h635v-166h-822z" /> +<glyph unicode="M" d="M113 0v1462h247l248 -1192h6l250 1192h252v-1462h-153v887q0 121 14 391h-8l-283 -1278h-154l-278 1280h-8q18 -268 18 -406v-874h-151z" /> +<glyph unicode="N" d="M135 0v1462h213l578 -1204h6q-14 285 -14 404v800h174v-1462h-215l-580 1210h-8q18 -276 18 -417v-793h-172z" /> +<glyph unicode="O" d="M84 735q0 750 534 750q258 0 392 -195q137 -199 137 -557q0 -362 -137 -557q-138 -197 -394 -196q-532 -1 -532 755zM281 733q0 -590 335 -590q174 0 254 145.5t80 444.5q0 305 -82 445q-84 143 -250 143q-337 0 -337 -588z" /> +<glyph unicode="P" d="M176 0v1462h404q514 0 514 -428q0 -219 -137.5 -342t-403.5 -123h-191v-569h-186zM362 727h170q195 0 283 72q86 70 86 225q0 279 -338 279h-201v-576z" /> +<glyph unicode="Q" d="M84 735q0 750 534 750q258 0 392 -195q137 -199 137 -557q0 -526 -285 -694q86 -180 279 -311l-121 -142q-238 172 -328 400q-37 -6 -76 -6q-532 -1 -532 755zM281 733q0 -590 335 -590q174 0 254 145.5t80 444.5q0 305 -82 445q-84 143 -250 143q-337 0 -337 -588z" /> +<glyph unicode="R" d="M186 0v1462h357q520 0 520 -415q0 -287 -289 -392l397 -655h-219l-350 604h-229v-604h-187zM373 762h164q170 0 251.5 65.5t81.5 210.5q0 141 -79.5 203t-258.5 62h-159v-541z" /> +<glyph unicode="S" d="M141 49v178q211 -86 414 -86q350 0 350 240q0 104 -71 160q-74 57 -285 133q-211 74 -299 174q-90 102 -90 264q0 174 129 272.5t352 98.5q225 0 416 -78l-64 -164q-197 78 -360 78q-293 0 -293 -209q0 -102 66 -164q66 -63 270 -133q244 -88 327.5 -182t83.5 -240 q0 -190 -139 -301q-138 -111 -393 -110q-258 -1 -414 69z" /> +<glyph unicode="T" d="M102 1298v164h1022v-164h-417v-1298h-187v1298h-418z" /> +<glyph unicode="U" d="M125 520v942h186v-932q0 -387 307 -387q293 0 300 389v932h186v-948q0 -260 -125 -397q-127 -139 -371 -139q-483 -1 -483 540z" /> +<glyph unicode="V" d="M33 1462h196l295 -927q45 -143 88 -334q33 152 93 340l292 921h199l-489 -1462h-187z" /> +<glyph unicode="W" d="M2 1462h170l88 -663q18 -145 39 -377q18 -203 18 -242q27 162 70 312l141 516h177l145 -521q57 -205 72 -307q6 92 65 619l70 663h170l-187 -1462h-190l-168 580q-43 147 -66 282q-31 -168 -65 -284l-156 -578h-190z" /> +<glyph unicode="X" d="M53 0l453 764l-422 698h199l331 -559l334 559h191l-422 -692l457 -770h-211l-355 635l-366 -635h-189z" /> +<glyph unicode="Y" d="M33 1462h203l376 -739l381 739h201l-487 -893v-569h-187v559z" /> +<glyph unicode="Z" d="M102 0v145l793 1151h-772v166h981v-145l-793 -1151h813v-166h-1022z" /> +<glyph unicode="[" d="M412 -324v1786h528v-149h-346v-1487h346v-150h-528z" /> +<glyph unicode="\" d="M211 1462h178l627 -1462h-178z" /> +<glyph unicode="]" d="M289 -174h346v1487h-346v149h528v-1786h-528v150z" /> +<glyph unicode="^" d="M111 549l424 924h102l481 -924h-162l-368 735l-318 -735h-159z" /> +<glyph unicode="_" d="M-16 -184h1259v-140h-1259v140z" /> +<glyph unicode="`" d="M418 1548v21h219q88 -182 174 -301v-27h-121q-163 139 -272 307z" /> +<glyph unicode="a" d="M135 307q0 332 510 348l203 7v69q0 236 -244 236q-150 0 -328 -82l-63 137q199 96 383 96q223 0 328 -88q102 -86 102 -278v-752h-131l-37 152h-8q-74 -94 -158 -133t-209 -39q-164 0 -256 85.5t-92 241.5zM324 305q0 -178 200 -178q150 0 234 82q88 84 88 229v99 l-162 -7q-195 -8 -278 -61q-82 -51 -82 -164z" /> +<glyph unicode="b" d="M158 0v1556h182v-376q0 -96 -8 -226h8q109 164 322 164q203 0 315 -149q115 -152 115 -418q0 -268 -115 -419.5t-315 -151.5q-207 0 -322 159h-12l-37 -139h-133zM340 551q0 -229 70 -324q72 -96 221 -96q272 0 272 422q0 414 -274 414q-154 0 -221.5 -94.5t-67.5 -321.5 z" /> +<glyph unicode="c" d="M172 543q0 279 145 428q145 147 408 147q176 0 336 -59l-62 -158q-147 57 -268 57q-371 0 -371 -413q0 -406 361 -406q160 0 321 62v-160q-135 -61 -329 -61q-258 0 -400 145q-141 145 -141 418z" /> +<glyph unicode="d" d="M137 547q0 268 115 419.5t315 151.5q205 0 322 -160h12q-12 129 -12 162v436h182v-1556h-147l-27 147h-8q-115 -168 -322 -167q-203 0 -315 149q-115 152 -115 418zM326 545q0 -414 274 -414q152 0 219 88q66 88 70 287v41q0 229 -70 323q-72 96 -221 97 q-272 0 -272 -422z" /> +<glyph unicode="e" d="M133 541q0 266 135 421.5t363 155.5q212 0 338 -135q127 -137 127 -356v-113h-774q9 -375 344 -375q199 0 370 76v-160q-170 -76 -364 -75q-244 0 -391 149q-148 151 -148 412zM326 662h573q0 305 -272 305q-276 0 -301 -305z" /> +<glyph unicode="f" d="M156 961v110l317 33v96q0 195 90 281q88 86 299 86q125 0 252 -35l-41 -143q-109 29 -207 28q-123 0 -168 -51q-43 -49 -43 -164v-104h389v-137h-389v-961h-182v961h-317z" /> +<glyph unicode="g" d="M102 -186q0 212 240 270q-96 47 -96 154q0 113 133 192q-86 35 -139 123q-51 84 -52 186q0 182 106.5 280.5t303.5 98.5q88 0 150 -20h378v-113l-196 -27q66 -88 65 -213q0 -162 -106 -256q-109 -96 -297 -96q-55 0 -86 6q-100 -55 -100 -133q0 -84 161 -84h187 q172 0 266 -78q92 -76 92 -219q0 -377 -565 -377q-218 0 -332 80q-113 81 -113 226zM274 -180q0 -174 271 -174q395 0 395 223q0 88 -49 118.5t-199 30.5h-188q-230 1 -230 -198zM367 745q0 -227 227 -227q223 0 223 230q0 240 -225 239.5t-225 -242.5z" /> +<glyph unicode="h" d="M160 0v1556h182v-462l-8 -144h10q104 168 336 168q389 0 389 -401v-717h-182v707q0 260 -238 260q-307 0 -307 -398v-569h-182z" /> +<glyph unicode="i" d="M197 0v123l344 20v811l-269 21v123h451v-955l352 -20v-123h-878zM526 1435.5q0 114.5 106.5 114.5t106.5 -114q0 -57 -32.5 -86t-73.5 -29q-107 0 -107 114.5z" /> +<glyph unicode="j" d="M135 -303q131 -39 289 -39q119 0 182 57q66 59 66 158v1081l-420 21v123h602v-1215q0 -180 -113 -278q-111 -96 -319 -97q-160 0 -287 35v154zM637 1435.5q0 114.5 106.5 114.5t106.5 -114.5t-106.5 -114.5t-106.5 114.5z" /> +<glyph unicode="k" d="M215 0v1556h180v-714l-16 -289h4l135 152l395 393h222l-494 -475l522 -623h-213l-426 504l-129 -82v-422h-180z" /> +<glyph unicode="l" d="M188 0v123l344 20v1270l-268 21v122h451v-1413l352 -20v-123h-879z" /> +<glyph unicode="m" d="M92 0v1098h127l27 -148h10q68 168 201 168q164 0 213 -182h6q78 182 219 182q129 0 186 -92q57 -90 58 -309v-717h-162v707q0 147 -27 202q-27 57 -88 58q-88 0 -127 -82q-39 -80 -39 -279v-606h-161v707q0 260 -125 260q-82 0 -119 -80t-37 -318v-569h-162z" /> +<glyph unicode="n" d="M160 0v1098h147l27 -148h10q104 168 336 168q389 0 389 -401v-717h-182v707q0 260 -238 260q-307 0 -307 -398v-569h-182z" /> +<glyph unicode="o" d="M115 551q0 264 135 415.5t366 151.5q217 0 356.5 -155.5t139.5 -411.5q0 -266 -137 -418q-139 -154 -365 -153q-219 0 -356 155q-139 158 -139 416zM303 551q0 -420 311 -420q309 0 310 420q0 416 -312 416q-309 0 -309 -416z" /> +<glyph unicode="p" d="M158 -492v1590h147l27 -148h8q111 168 322 168q203 0 315 -149q115 -152 115 -418q0 -268 -115 -419.5t-315 -151.5q-207 0 -322 159h-12q12 -129 12 -162v-469h-182zM340 551q0 -229 70 -324q72 -96 221 -96q272 0 272 422q0 414 -274 414q-152 0 -219.5 -88t-69.5 -287 v-41z" /> +<glyph unicode="q" d="M137 547q0 268 115 419.5t315 151.5q209 0 322 -168h8l27 148h147v-1590h-182v469q0 41 12 170h-12q-115 -168 -322 -167q-203 0 -315 149q-115 152 -115 418zM326 545q0 -414 274 -414q152 0 219 88q66 88 70 287v41q0 229 -70 323q-72 96 -221 97q-272 0 -272 -422z " /> +<glyph unicode="r" d="M264 0v1098h148l22 -201h8q76 117 162 170q84 51 215 51q119 0 240 -45l-49 -166q-121 45 -224 45q-163 0 -251 -92q-88 -90 -89 -268v-592h-182z" /> +<glyph unicode="s" d="M203 49v166q195 -86 370 -86q274 0 275 162q0 55 -49 98t-226 107q-233 86 -294 159q-59 72 -60 172q0 135 111 213q113 78 309.5 78t368.5 -74l-60 -149q-184 72 -319 72q-236 0 -236 -133q0 -57 51.5 -96.5t231.5 -102.5q207 -76 280 -152q70 -72 70 -182 q0 -150 -116.5 -235.5t-331.5 -85.5q-246 -1 -375 69z" /> +<glyph unicode="t" d="M139 961v94l267 49l77 287h105v-293h438v-137h-438v-637q0 -195 192 -195q98 0 240 21v-138q-137 -33 -252 -32q-362 0 -362 344v637h-267z" /> +<glyph unicode="u" d="M160 381v717h182v-707q0 -260 236 -260q162 0 235.5 92t73.5 305v570h182v-1098h-147l-27 147h-10q-106 -168 -334 -167q-391 0 -391 401z" /> +<glyph unicode="v" d="M82 1098h188l240 -652q84 -225 100 -325h6q8 53 101 325l239 652h189l-416 -1098h-231z" /> +<glyph unicode="w" d="M-4 1098h162l98 -543q39 -215 57 -393h6q33 195 68 358l133 578h193l127 -578q43 -188 67 -358h6q29 225 60 393l102 543h158l-225 -1098h-195l-131 596l-68 330h-6l-65 -334l-135 -592h-189z" /> +<glyph unicode="x" d="M96 0l414 563l-393 535h207l290 -410l291 410h207l-395 -535l413 -563h-206l-310 436l-311 -436h-207z" /> +<glyph unicode="y" d="M82 1098h188l262 -654q82 -203 89 -290h6q20 106 90 292l239 652h189l-475 -1241q-70 -178 -156 -263q-89 -86 -246 -86q-94 0 -168 17v145q61 -12 136 -12q96 0 149 41t96 141l58 150z" /> +<glyph unicode="z" d="M182 0v125l660 836h-627v137h811v-146l-647 -815h665v-137h-862z" /> +<glyph unicode="{" d="M225 492v155q338 0 338 189v333q0 287 438 293v-149q-147 -4 -200 -39q-55 -37 -56 -119v-332q0 -209 -233 -248v-12q233 -39 233 -248v-331q0 -82 56 -119q53 -35 200 -39v-150q-438 6 -438 293v334q0 189 -338 189z" /> +<glyph unicode="|" d="M539 -492v2048h149v-2048h-149z" /> +<glyph unicode="}" d="M227 -174q141 0 198.5 39t57.5 119v331q0 209 234 248v12q-233 39 -234 248v332q0 80 -57 119t-199 39v149q438 -6 439 -293v-333q0 -188 338 -189v-155q-338 0 -338 -189v-334q0 -287 -439 -293v150z" /> +<glyph unicode="~" d="M152 586v162q98 109 247 108q102 0 248 -63q129 -55 201 -56q106 0 227 121v-162q-98 -109 -248 -108q-102 0 -247 63q-133 55 -201 56q-106 0 -227 -121z" /> +<glyph unicode=" " /> +<glyph unicode="¡" d="M487 979q0 139 127 139t127 -139t-127 -139t-127 139zM502 -373l51 1049h121l51 -1049h-223z" /> +<glyph unicode="¢" d="M172 743q0 494 434 568v172h137v-164q158 -2 318 -59l-62 -158q-147 57 -268 57q-371 0 -371 -414q0 -406 361 -405q152 0 321 61v-159q-123 -59 -299 -62v-200h-137v206q-434 68 -434 557z" /> +<glyph unicode="£" d="M119 0v154q201 49 200 284v213h-198v137h198v324q0 166 109 268q106 100 289 101q193 0 346 -80l-66 -144q-143 72 -272 72q-223 0 -223 -246v-295h377v-137h-377v-211q0 -199 -140 -274h748v-166h-991z" /> +<glyph unicode="¤" d="M174 1065l98 98l127 -129q101 68 215 68q115 0 213 -68l129 129l99 -96l-129 -129q68 -101 67 -215q0 -120 -67 -215l127 -127l-97 -96l-129 127q-100 -66 -213 -66q-117 0 -215 68l-127 -127l-96 96l127 127q-66 96 -65 213q0 113 65 213zM375 723q0 -98 71 -170 q70 -70 168 -70q102 0 172 70q72 72 72 170q0 100 -71.5 172t-172 72t-170 -70t-69.5 -174z" /> +<glyph unicode="¥" d="M78 1462h192l342 -739l346 739h191l-385 -768h240v-137h-302v-158h302v-137h-302v-262h-178v262h-301v137h301v158h-301v137h234z" /> +<glyph unicode="¦" d="M539 289h149v-781h-149v781zM539 776v780h149v-780h-149z" /> +<glyph unicode="§" d="M244 55v158q170 -82 323 -82q240 0 240 141q0 55 -43 97q-45 41 -195 102q-197 82 -254 160q-55 74 -55 178q0 178 160 258q-160 80 -160 236q0 123 105 192q106 72 276 72q160 0 326 -72l-56 -139q-157 68 -276 67q-201 0 -201 -116q0 -53 49 -93q47 -37 197 -100 q158 -61 231 -139q74 -77 74 -191q0 -180 -145 -270q145 -80 145 -225q0 -139 -110.5 -219t-307.5 -80q-202 -1 -323 65zM414 831q0 -74 57 -126q55 -51 207 -117l35 -15q115 78 114 185q0 90 -73 145q-68 51 -207 103q-133 -50 -133 -175z" /> +<glyph unicode="¨" d="M330 1394.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5zM705 1394.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5z" /> +<glyph unicode="©" d="M6 731q0 342 160 547t448 205q283 0 447 -203q162 -201 162 -549t-162 -549q-164 -203 -447 -202q-285 0 -446.5 204.5t-161.5 546.5zM115 731q0 -301 129 -473q127 -170 370 -170q242 0 369 170q131 174 131 473t-131 473q-127 170 -368.5 170t-370.5 -172t-129 -471z M248 733q0 209 110.5 332t300.5 123q127 0 254 -62l-61 -127q-106 53 -193 54q-123 0 -186 -86q-66 -88 -65 -236q0 -324 251 -323q98 0 215 45v-131q-108 -49 -221 -50q-197 0 -301 123t-104 338z" /> +<glyph unicode="ª" d="M276 989q0 117 84 168t277 57l125 5v10q0 133 -152 133q-117 0 -245 -51l-41 110q135 57 294 58q291 0 291 -238v-444h-110l-33 118q-104 -131 -268 -131q-222 0 -222 205zM428 987q0 -90 104 -90q106 0 168 55.5t62 145.5v20l-100 -2q-111 -2 -175 -29q-59 -24 -59 -100 z" /> +<glyph unicode="«" d="M197 526v27l309 414l117 -78l-238 -348l238 -348l-117 -78zM604 526v27l309 414l117 -78l-237 -348l237 -348l-117 -78z" /> +<glyph unicode="¬" d="M152 647v150h923v-535h-149v385h-774z" /> +<glyph unicode="­" d="M285 465v168h659v-168h-659z" /> +<glyph unicode="®" d="M6 731q0 342 160 547t448 205q283 0 447 -203q162 -201 162 -549t-162 -549q-164 -203 -447 -202q-285 0 -446.5 204.5t-161.5 546.5zM115 731q0 -301 129 -473q127 -170 370 -170q242 0 369 170q131 174 131 473t-131 473q-127 170 -368.5 170t-370.5 -172t-129 -471z M348 285v893h234q326 0 325 -265q0 -163 -159 -233l237 -395h-178l-207 352h-94v-352h-158zM506 768h72q170 0 170 141q0 74 -41 103q-43 31 -132 30h-69v-274z" /> +<glyph unicode="¯" d="M-20 1556v140h1269v-140h-1269z" /> +<glyph unicode="°" d="M299 1167q0 129 92 222q94 94 223 94q127 0 221.5 -94.5t94.5 -221.5q0 -129 -92.5 -221t-223.5 -92t-223 92t-92 221zM422 1167q0 -80 55 -135t137 -55q78 0 135.5 55t57.5 135t-57.5 137.5t-135.5 57.5q-80 0 -135 -57q-57 -62 -57 -138z" /> +<glyph unicode="±" d="M152 0v150h923v-150h-923zM152 647v150h387v389h149v-389h387v-150h-387v-385h-149v385h-387z" /> +<glyph unicode="²" d="M348 672v102l187 199q106 113 141 168q31 49 31 108q0 115 -111 115q-82 0 -164 -76l-78 86q115 102 248 103q121 0 182 -60q66 -61 66 -164q0 -66 -35 -135q-33 -66 -164 -196l-135 -136h363v-114h-531z" /> +<glyph unicode="³" d="M342 705v124q113 -66 203 -65q150 0 149 137q0 125 -147 125h-72v104h70q123 0 123 127q0 111 -97 111q-74 0 -161 -70l-66 86q111 92 238 93q113 0 174 -54q63 -55 63 -149q0 -139 -149 -189q176 -41 176 -186q0 -117 -74 -180q-72 -63 -215 -64q-132 1 -215 50z" /> +<glyph unicode="´" d="M418 1241v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="µ" d="M180 -492v1590h182v-707q0 -260 218 -260q152 0 217 90q70 94 69 307v570h183v-1098h-148l-27 147h-10q-96 -168 -295 -167q-139 0 -213 88q6 -154 6 -240v-320h-182z" /> +<glyph unicode="¶" d="M66 1042q0 256 108 388q106 127 342 126h563v-1816h-121v1657h-206v-1657h-121v819q-61 -18 -146 -18q-419 -1 -419 501z" /> +<glyph unicode="·" d="M487 723q0 139 127 139t127 -139t-127 -139t-127 139z" /> +<glyph unicode="¸" d="M428 -375q34 -6 80 -6q152 0 151 92q0 72 -172 113l91 176h120l-57 -115q160 -37 160 -172q0 -205 -291 -205q-39 0 -82 9v108z" /> +<glyph unicode="¹" d="M367 1309l219 151h135v-788h-146v465q0 84 9 200q-43 -45 -74 -65l-70 -51z" /> +<glyph unicode="º" d="M281 1133q0 160 90 253q88 92 245 93q145 0 238 -93q94 -94 94 -253q0 -164 -92 -256.5t-244 -92.5q-145 0 -237 93q-94 94 -94 256zM432 1133q0 -229 182 -230q180 0 181 230q0 225 -181 225q-182 0 -182 -225z" /> +<glyph unicode="»" d="M197 193l237 348l-237 348l116 78l310 -414v-27l-310 -411zM604 193l238 348l-238 348l117 78l309 -414v-27l-309 -411z" /> +<glyph unicode="¼" d="M23 1309l219 151h135v-788h-146v465q0 84 9 200q-43 -45 -74 -65l-70 -51zM170 0l729 1462h158l-729 -1462h-158zM606 174v98l377 523h141v-508h84v-113h-84v-174h-143v174h-375zM751 287h230v176q0 83 6 172q-37 -70 -80 -131z" /> +<glyph unicode="½" d="M2 1309l219 151h135v-788h-146v465q0 84 9 200q-43 -45 -74 -65l-70 -51zM104 0l729 1462h158l-729 -1462h-158zM694 0v102l187 199q106 113 141 168q31 49 31 108q0 115 -111 115q-82 0 -164 -76l-78 86q115 102 248 103q121 0 182 -60q66 -61 66 -164q0 -66 -35 -135 q-33 -66 -164 -196l-135 -136h363v-114h-531z" /> +<glyph unicode="¾" d="M20 705v124q113 -66 203 -65q150 0 149 137q0 125 -147 125h-72v104h70q123 0 123 127q0 111 -97 111q-74 0 -161 -70l-66 86q111 92 238 93q113 0 174 -54q63 -55 63 -149q0 -139 -149 -189q176 -41 176 -186q0 -117 -74 -180q-72 -63 -215 -64q-132 1 -215 50zM223 0 l729 1462h158l-729 -1462h-158zM627 174v98l377 523h141v-508h84v-113h-84v-174h-143v174h-375zM772 287h230v176q0 83 6 172q-37 -70 -80 -131z" /> +<glyph unicode="¿" d="M168 -35q0 133 53 217q52 83 197 191q113 80 151 141q33 51 33 143v19h160v-37q0 -115 -45 -193q-45 -76 -162 -159q-127 -96 -170 -156q-43 -57 -43 -158q0 -94 74 -149q76 -57 207 -57q178 0 370 90l62 -144q-215 -106 -422 -106q-217 0 -340 98q-125 100 -125 260z M547 979q0 139 127 139t127 -139t-127 -139t-127 139z" /> +<glyph unicode="À" d="M33 0l483 1468h195l485 -1468h-192l-144 453h-491l-146 -453h-190zM422 618h385l-133 424q-39 121 -62 226q-20 -88 -47 -183zM340 1886v21h219q88 -182 174 -301v-27h-121q-163 139 -272 307z" /> +<glyph unicode="Á" d="M33 0l483 1468h195l485 -1468h-192l-144 453h-491l-146 -453h-190zM422 618h385l-133 424q-39 121 -62 226q-20 -88 -47 -183zM520 1579v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="Â" d="M33 0l483 1468h195l485 -1468h-192l-144 453h-491l-146 -453h-190zM422 618h385l-133 424q-39 121 -62 226q-20 -88 -47 -183zM283 1579v27l59 67q141 156 176 234h193q35 -78 176 -234l59 -67v-27h-121q-78 49 -211 186q-133 -137 -211 -186h-120z" /> +<glyph unicode="Ã" d="M33 0l483 1468h195l485 -1468h-192l-144 453h-491l-146 -453h-190zM422 618h385l-133 424q-39 121 -62 226q-20 -88 -47 -183zM254 1579q25 264 211 264q57 0 162 -55q104 -57 135 -57q82 0 106 114h105q-27 -264 -211 -264q-57 0 -158 57q-100 55 -139 56 q-82 0 -107 -115h-104z" /> +<glyph unicode="Ä" d="M33 0l483 1468h195l485 -1468h-192l-144 453h-491l-146 -453h-190zM422 618h385l-133 424q-39 121 -62 226q-20 -88 -47 -183zM330 1732.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5zM705 1732.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5z" /> +<glyph unicode="Å" d="M33 0l483 1468h195l485 -1468h-192l-144 453h-491l-146 -453h-190zM422 618h385l-133 424q-39 121 -62 226q-20 -88 -47 -183zM387 1581q0 100 62 158q59 57 161 57q100 0 166 -59q63 -57 64 -154q0 -100 -64 -158q-66 -59 -166 -59q-102 0 -161 57q-62 58 -62 158z M498 1581q0 -113 112 -113q53 0 82 29q31 31 31 84q0 51 -30.5 82t-82.5 31q-49 0 -80 -31q-32 -33 -32 -82z" /> +<glyph unicode="Æ" d="M0 0l338 1462h872v-164h-477v-452h438v-162h-438v-520h477v-164h-653v453h-289l-96 -453h-172zM305 618h252v680h-106z" /> +<glyph unicode="Ç" d="M129 733q0 344 178 547t490 203q219 0 383 -86l-78 -156q-156 78 -305 78q-215 0 -342 -158q-129 -160 -129 -430q0 -287 120 -438q119 -150 351 -150q135 0 327 58v-162q-150 -59 -358 -59q-310 0 -473 196q-164 199 -164 557zM508 -375q34 -6 80 -6q152 0 151 92 q0 72 -172 113l91 176h120l-57 -115q160 -37 160 -172q0 -205 -291 -205q-39 0 -82 9v108z" /> +<glyph unicode="È" d="M217 0v1462h842v-164h-656v-452h617v-162h-617v-520h656v-164h-842zM344 1886v21h219q88 -182 174 -301v-27h-121q-163 139 -272 307z" /> +<glyph unicode="É" d="M217 0v1462h842v-164h-656v-452h617v-162h-617v-520h656v-164h-842zM481 1579v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="Ê" d="M217 0v1462h842v-164h-656v-452h617v-162h-617v-520h656v-164h-842zM318 1579v27l59 67q141 156 176 234h193q35 -78 176 -234l59 -67v-27h-121q-78 49 -211 186q-133 -137 -211 -186h-120z" /> +<glyph unicode="Ë" d="M217 0v1462h842v-164h-656v-452h617v-162h-617v-520h656v-164h-842zM355 1732.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5zM730 1732.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5z" /> +<glyph unicode="Ì" d="M225 0v123l295 20v1176l-295 20v123h776v-123l-294 -20v-1176l294 -20v-123h-776zM309 1886v21h219q88 -182 174 -301v-27h-121q-163 139 -272 307z" /> +<glyph unicode="Í" d="M225 0v123l295 20v1176l-295 20v123h776v-123l-294 -20v-1176l294 -20v-123h-776zM537 1579v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="Î" d="M225 0v123l295 20v1176l-295 20v123h776v-123l-294 -20v-1176l294 -20v-123h-776zM283 1579v27l59 67q141 156 176 234h193q35 -78 176 -234l59 -67v-27h-121q-78 49 -211 186q-133 -137 -211 -186h-120z" /> +<glyph unicode="Ï" d="M225 0v123l295 20v1176l-295 20v123h776v-123l-294 -20v-1176l294 -20v-123h-776zM332 1732.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5zM707 1732.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5z" /> +<glyph unicode="Ð" d="M0 659v162h135v641h342q319 0 494 -188q176 -193 176 -529q0 -365 -182 -552q-186 -193 -529 -193h-301v659h-135zM322 160h96q532 0 532 579q0 563 -493 564h-135v-482h380v-162h-380v-499z" /> +<glyph unicode="Ñ" d="M135 0v1462h213l578 -1204h6q-14 285 -14 404v800h174v-1462h-215l-580 1210h-8q18 -276 18 -417v-793h-172zM258 1579q25 264 211 264q57 0 162 -55q104 -57 135 -57q82 0 106 114h105q-27 -264 -211 -264q-57 0 -158 57q-100 55 -139 56q-82 0 -107 -115h-104z" /> +<glyph unicode="Ò" d="M84 735q0 750 534 750q258 0 392 -195q137 -199 137 -557q0 -362 -137 -557q-138 -197 -394 -196q-532 -1 -532 755zM281 733q0 -590 335 -590q174 0 254 145.5t80 444.5q0 305 -82 445q-84 143 -250 143q-337 0 -337 -588zM336 1886v21h219q88 -182 174 -301v-27h-121 q-163 139 -272 307z" /> +<glyph unicode="Ó" d="M84 735q0 750 534 750q258 0 392 -195q137 -199 137 -557q0 -362 -137 -557q-138 -197 -394 -196q-532 -1 -532 755zM281 733q0 -590 335 -590q174 0 254 145.5t80 444.5q0 305 -82 445q-84 143 -250 143q-337 0 -337 -588zM508 1579v27q92 127 174 301h219v-21 q-109 -168 -272 -307h-121z" /> +<glyph unicode="Ô" d="M84 735q0 750 534 750q258 0 392 -195q137 -199 137 -557q0 -362 -137 -557q-138 -197 -394 -196q-532 -1 -532 755zM281 733q0 -590 335 -590q174 0 254 145.5t80 444.5q0 305 -82 445q-84 143 -250 143q-337 0 -337 -588zM283 1579v27l59 67q141 156 176 234h193 q35 -78 176 -234l59 -67v-27h-121q-78 49 -211 186q-133 -137 -211 -186h-120z" /> +<glyph unicode="Õ" d="M84 735q0 750 534 750q258 0 392 -195q137 -199 137 -557q0 -362 -137 -557q-138 -197 -394 -196q-532 -1 -532 755zM281 733q0 -590 335 -590q174 0 254 145.5t80 444.5q0 305 -82 445q-84 143 -250 143q-337 0 -337 -588zM262 1579q25 264 211 264q57 0 162 -55 q104 -57 135 -57q82 0 106 114h105q-27 -264 -211 -264q-57 0 -158 57q-100 55 -139 56q-82 0 -107 -115h-104z" /> +<glyph unicode="Ö" d="M84 735q0 750 534 750q258 0 392 -195q137 -199 137 -557q0 -362 -137 -557q-138 -197 -394 -196q-532 -1 -532 755zM281 733q0 -590 335 -590q174 0 254 145.5t80 444.5q0 305 -82 445q-84 143 -250 143q-337 0 -337 -588zM332 1732.5q0 102.5 96 102.5t96 -102.5 t-96 -102.5t-96 102.5zM707 1732.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5z" /> +<glyph unicode="×" d="M190 1042l105 105l317 -318l322 318l104 -103l-321 -321l319 -320l-102 -102l-322 317l-317 -315l-102 103l315 317z" /> +<glyph unicode="Ø" d="M80 2l121 197q-117 184 -117 536q0 750 534 750q186 0 310 -105l92 152l137 -78l-125 -201q115 -188 115 -520q0 -362 -137 -557q-138 -197 -394 -196q-193 0 -307 94l-92 -150zM281 733q0 -205 38 -342l515 836q-80 94 -216 94q-337 0 -337 -588zM403 229 q80 -86 213 -86q174 0 254 145.5t80 444.5q0 205 -35 328z" /> +<glyph unicode="Ù" d="M125 520v942h186v-932q0 -387 307 -387q293 0 300 389v932h186v-948q0 -260 -125 -397q-127 -139 -371 -139q-483 -1 -483 540zM348 1886v21h219q88 -182 174 -301v-27h-121q-163 139 -272 307z" /> +<glyph unicode="Ú" d="M125 520v942h186v-932q0 -387 307 -387q293 0 300 389v932h186v-948q0 -260 -125 -397q-127 -139 -371 -139q-483 -1 -483 540zM494 1579v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="Û" d="M125 520v942h186v-932q0 -387 307 -387q293 0 300 389v932h186v-948q0 -260 -125 -397q-127 -139 -371 -139q-483 -1 -483 540zM283 1579v27l59 67q141 156 176 234h193q35 -78 176 -234l59 -67v-27h-121q-78 49 -211 186q-133 -137 -211 -186h-120z" /> +<glyph unicode="Ü" d="M125 520v942h186v-932q0 -387 307 -387q293 0 300 389v932h186v-948q0 -260 -125 -397q-127 -139 -371 -139q-483 -1 -483 540zM332 1732.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5zM707 1732.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5z" /> +<glyph unicode="Ý" d="M33 1462h203l376 -739l381 739h201l-487 -893v-569h-187v559zM494 1579v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="Þ" d="M176 0v1462h186v-252h218q514 0 514 -428q0 -219 -137.5 -342t-403.5 -123h-191v-317h-186zM362 475h170q195 0 283 72q86 72 86 225q0 279 -338 279h-201v-576z" /> +<glyph unicode="ß" d="M164 0v1200q0 367 424 367q195 0 303 -80t108 -227q0 -131 -133 -248q-80 -72 -108 -107q-25 -31 -25 -63q0 -37 29 -70q27 -29 151 -110q144 -95 191 -173t47 -176q0 -162 -100.5 -247.5t-282.5 -85.5q-178 0 -289 69v166q143 -86 277 -86q213 0 213 174q0 80 -45 131 t-144 113q-125 78 -174 139.5t-49 147.5q0 117 129 223q129 108 129 196q0 164 -227 164q-242 0 -242 -215v-1202h-182z" /> +<glyph unicode="à" d="M135 307q0 332 510 348l203 7v69q0 236 -244 236q-150 0 -328 -82l-63 137q199 96 383 96q223 0 328 -88q102 -86 102 -278v-752h-131l-37 152h-8q-74 -94 -158 -133t-209 -39q-164 0 -256 85.5t-92 241.5zM324 305q0 -178 200 -178q150 0 234 82q88 84 88 229v99 l-162 -7q-195 -8 -278 -61q-82 -51 -82 -164zM332 1548v21h219q88 -182 174 -301v-27h-121q-163 139 -272 307z" /> +<glyph unicode="á" d="M135 307q0 332 510 348l203 7v69q0 236 -244 236q-150 0 -328 -82l-63 137q199 96 383 96q223 0 328 -88q102 -86 102 -278v-752h-131l-37 152h-8q-74 -94 -158 -133t-209 -39q-164 0 -256 85.5t-92 241.5zM324 305q0 -178 200 -178q150 0 234 82q88 84 88 229v99 l-162 -7q-195 -8 -278 -61q-82 -51 -82 -164zM502 1241v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="â" d="M135 307q0 332 510 348l203 7v69q0 236 -244 236q-150 0 -328 -82l-63 137q199 96 383 96q223 0 328 -88q102 -86 102 -278v-752h-131l-37 152h-8q-74 -94 -158 -133t-209 -39q-164 0 -256 85.5t-92 241.5zM324 305q0 -178 200 -178q150 0 234 82q88 84 88 229v99 l-162 -7q-195 -8 -278 -61q-82 -51 -82 -164zM291 1241v27l59 67q141 156 176 234h193q35 -78 176 -234l59 -67v-27h-121q-78 49 -211 186q-133 -137 -211 -186h-120z" /> +<glyph unicode="ã" d="M135 307q0 332 510 348l203 7v69q0 236 -244 236q-150 0 -328 -82l-63 137q199 96 383 96q223 0 328 -88q102 -86 102 -278v-752h-131l-37 152h-8q-74 -94 -158 -133t-209 -39q-164 0 -256 85.5t-92 241.5zM324 305q0 -178 200 -178q150 0 234 82q88 84 88 229v99 l-162 -7q-195 -8 -278 -61q-82 -51 -82 -164zM264 1241q25 264 211 264q57 0 162 -55q104 -57 135 -57q82 0 106 114h105q-27 -264 -211 -264q-57 0 -158 57q-100 55 -139 56q-82 0 -107 -115h-104z" /> +<glyph unicode="ä" d="M135 307q0 332 510 348l203 7v69q0 236 -244 236q-150 0 -328 -82l-63 137q199 96 383 96q223 0 328 -88q102 -86 102 -278v-752h-131l-37 152h-8q-74 -94 -158 -133t-209 -39q-164 0 -256 85.5t-92 241.5zM324 305q0 -178 200 -178q150 0 234 82q88 84 88 229v99 l-162 -7q-195 -8 -278 -61q-82 -51 -82 -164zM342 1394.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5zM717 1394.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5z" /> +<glyph unicode="å" d="M135 307q0 332 510 348l203 7v69q0 236 -244 236q-150 0 -328 -82l-63 137q199 96 383 96q223 0 328 -88q102 -86 102 -278v-752h-131l-37 152h-8q-74 -94 -158 -133t-209 -39q-164 0 -256 85.5t-92 241.5zM324 305q0 -178 200 -178q150 0 234 82q88 84 88 229v99 l-162 -7q-195 -8 -278 -61q-82 -51 -82 -164zM387 1456q0 100 62 158q59 57 161 57q100 0 166 -59q63 -57 64 -154q0 -100 -64 -158q-66 -59 -166 -59q-102 0 -161 57q-62 58 -62 158zM498 1456q0 -113 112 -113q53 0 82 29q31 31 31 84q0 51 -30.5 82t-82.5 31 q-49 0 -80 -31q-32 -33 -32 -82z" /> +<glyph unicode="æ" d="M45 307q0 334 328 348l149 7v69q0 236 -139 236q-109 0 -211 -82l-57 137q133 96 276 96q187 0 246 -178q77 178 240 178q137 0 223 -135t86 -356v-113h-496q3 -190 68 -283q66 -92 168 -92q113 0 233 76v-162q-106 -74 -241 -73q-221 0 -316 229q-104 -229 -301 -229 q-117 0 -186 86q-70 83 -70 241zM215 305q0 -82 33 -131q31 -47 86 -47q84 0 135 82t51 229v99l-88 -7q-217 -18 -217 -225zM694 662h316q0 137 -41 223q-39 82 -111 82q-66 0 -113 -78q-45 -75 -51 -227z" /> +<glyph unicode="ç" d="M172 543q0 279 145 428q145 147 408 147q176 0 336 -59l-62 -158q-147 57 -268 57q-371 0 -371 -413q0 -406 361 -406q160 0 321 62v-160q-135 -61 -329 -61q-258 0 -400 145q-141 145 -141 418zM477 -375q34 -6 80 -6q152 0 151 92q0 72 -172 113l91 176h120l-57 -115 q160 -37 160 -172q0 -205 -291 -205q-39 0 -82 9v108z" /> +<glyph unicode="è" d="M133 541q0 266 135 421.5t363 155.5q212 0 338 -135q127 -137 127 -356v-113h-774q9 -375 344 -375q199 0 370 76v-160q-170 -76 -364 -75q-244 0 -391 149q-148 151 -148 412zM326 662h573q0 305 -272 305q-276 0 -301 -305zM375 1548v21h219q88 -182 174 -301v-27h-121 q-163 139 -272 307z" /> +<glyph unicode="é" d="M133 541q0 266 135 421.5t363 155.5q212 0 338 -135q127 -137 127 -356v-113h-774q9 -375 344 -375q199 0 370 76v-160q-170 -76 -364 -75q-244 0 -391 149q-148 151 -148 412zM326 662h573q0 305 -272 305q-276 0 -301 -305zM500 1241v27q92 127 174 301h219v-21 q-109 -168 -272 -307h-121z" /> +<glyph unicode="ê" d="M133 541q0 266 135 421.5t363 155.5q212 0 338 -135q127 -137 127 -356v-113h-774q9 -375 344 -375q199 0 370 76v-160q-170 -76 -364 -75q-244 0 -391 149q-148 151 -148 412zM326 662h573q0 305 -272 305q-276 0 -301 -305zM299 1241v27l59 67q141 156 176 234h193 q35 -78 176 -234l59 -67v-27h-121q-78 49 -211 186q-133 -137 -211 -186h-120z" /> +<glyph unicode="ë" d="M133 541q0 266 135 421.5t363 155.5q212 0 338 -135q127 -137 127 -356v-113h-774q9 -375 344 -375q199 0 370 76v-160q-170 -76 -364 -75q-244 0 -391 149q-148 151 -148 412zM326 662h573q0 305 -272 305q-276 0 -301 -305zM348 1394.5q0 102.5 96 102.5t96 -102.5 t-96 -102.5t-96 102.5zM723 1394.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5z" /> +<glyph unicode="ì" d="M197 0v123l344 20v811l-269 21v123h451v-955l352 -20v-123h-878zM332 1548v21h219q88 -182 174 -301v-27h-121q-163 139 -272 307z" /> +<glyph unicode="í" d="M197 0v123l344 20v811l-269 21v123h451v-955l352 -20v-123h-878zM531 1241v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="î" d="M197 0v123l344 20v811l-269 21v123h451v-955l352 -20v-123h-878zM283 1241v27l59 67q141 156 176 234h193q35 -78 176 -234l59 -67v-27h-121q-78 49 -211 186q-133 -137 -211 -186h-120z" /> +<glyph unicode="ï" d="M197 0v123l344 20v811l-269 21v123h451v-955l352 -20v-123h-878zM330 1394.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5zM705 1394.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5z" /> +<glyph unicode="ð" d="M135 477.5q0 233.5 121 364.5q121 129 334 129q211 0 299 -119l8 4q-57 225 -242 391l-256 -153l-73 114l217 131q-113 76 -172 109l69 123q135 -66 246 -148l227 138l74 -113l-194 -117q301 -291 301 -758q0 -279 -129 -436t-353 -157q-213 0 -346 133 q-131 131 -131 364.5zM328 471q0 -340 288 -340q152 0 222 100q68 98 67 295q0 129 -77.5 213t-213.5 84q-286 0 -286 -352z" /> +<glyph unicode="ñ" d="M160 0v1098h147l27 -148h10q104 168 336 168q389 0 389 -401v-717h-182v707q0 260 -238 260q-307 0 -307 -398v-569h-182zM260 1241q25 264 211 264q57 0 162 -55q104 -57 135 -57q82 0 106 114h105q-27 -264 -211 -264q-57 0 -158 57q-100 55 -139 56q-82 0 -107 -115 h-104z" /> +<glyph unicode="ò" d="M115 551q0 264 135 415.5t366 151.5q217 0 356.5 -155.5t139.5 -411.5q0 -266 -137 -418q-139 -154 -365 -153q-219 0 -356 155q-139 158 -139 416zM303 551q0 -420 311 -420q309 0 310 420q0 416 -312 416q-309 0 -309 -416zM377 1548v21h219q88 -182 174 -301v-27h-121 q-163 139 -272 307z" /> +<glyph unicode="ó" d="M115 551q0 264 135 415.5t366 151.5q217 0 356.5 -155.5t139.5 -411.5q0 -266 -137 -418q-139 -154 -365 -153q-219 0 -356 155q-139 158 -139 416zM303 551q0 -420 311 -420q309 0 310 420q0 416 -312 416q-309 0 -309 -416zM498 1241v27q92 127 174 301h219v-21 q-109 -168 -272 -307h-121z" /> +<glyph unicode="ô" d="M115 551q0 264 135 415.5t366 151.5q217 0 356.5 -155.5t139.5 -411.5q0 -266 -137 -418q-139 -154 -365 -153q-219 0 -356 155q-139 158 -139 416zM303 551q0 -420 311 -420q309 0 310 420q0 416 -312 416q-309 0 -309 -416zM279 1241v27l59 67q141 156 176 234h193 q35 -78 176 -234l59 -67v-27h-121q-78 49 -211 186q-133 -137 -211 -186h-120z" /> +<glyph unicode="õ" d="M115 551q0 264 135 415.5t366 151.5q217 0 356.5 -155.5t139.5 -411.5q0 -266 -137 -418q-139 -154 -365 -153q-219 0 -356 155q-139 158 -139 416zM303 551q0 -420 311 -420q309 0 310 420q0 416 -312 416q-309 0 -309 -416zM254 1241q25 264 211 264q57 0 162 -55 q104 -57 135 -57q82 0 106 114h105q-27 -264 -211 -264q-57 0 -158 57q-100 55 -139 56q-82 0 -107 -115h-104z" /> +<glyph unicode="ö" d="M115 551q0 264 135 415.5t366 151.5q217 0 356.5 -155.5t139.5 -411.5q0 -266 -137 -418q-139 -154 -365 -153q-219 0 -356 155q-139 158 -139 416zM303 551q0 -420 311 -420q309 0 310 420q0 416 -312 416q-309 0 -309 -416zM324 1394.5q0 102.5 96 102.5t96 -102.5 t-96 -102.5t-96 102.5zM699 1394.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5z" /> +<glyph unicode="÷" d="M152 647v150h923v-150h-923zM498 373q0 125 114.5 125t114.5 -125t-114.5 -125t-114.5 125zM498 1071q0 125 114.5 125t114.5 -125t-114.5 -125t-114.5 125z" /> +<glyph unicode="ø" d="M115 551q0 264 133 415.5t368 151.5q129 0 236 -57l76 119l131 -84l-84 -131q137 -156 137 -414q0 -272 -135 -421.5t-367 -149.5q-127 0 -233 53l-76 -119l-131 84l84 131q-139 158 -139 422zM303 551q0 -178 53 -275l406 656q-66 35 -156 35q-162 0 -231 -103 q-72 -106 -72 -313zM467 164q59 -33 154 -33q162 0 233 105q70 102 70 315q0 166 -52 266z" /> +<glyph unicode="ù" d="M160 381v717h182v-707q0 -260 236 -260q162 0 235.5 92t73.5 305v570h182v-1098h-147l-27 147h-10q-106 -168 -334 -167q-391 0 -391 401zM340 1548v21h219q88 -182 174 -301v-27h-121q-163 139 -272 307z" /> +<glyph unicode="ú" d="M160 381v717h182v-707q0 -260 236 -260q162 0 235.5 92t73.5 305v570h182v-1098h-147l-27 147h-10q-106 -168 -334 -167q-391 0 -391 401zM500 1241v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="û" d="M160 381v717h182v-707q0 -260 236 -260q162 0 235.5 92t73.5 305v570h182v-1098h-147l-27 147h-10q-106 -168 -334 -167q-391 0 -391 401zM291 1241v27l59 67q141 156 176 234h193q35 -78 176 -234l59 -67v-27h-121q-78 49 -211 186q-133 -137 -211 -186h-120z" /> +<glyph unicode="ü" d="M160 381v717h182v-707q0 -260 236 -260q162 0 235.5 92t73.5 305v570h182v-1098h-147l-27 147h-10q-106 -168 -334 -167q-391 0 -391 401zM332 1394.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5zM707 1394.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5z " /> +<glyph unicode="ý" d="M82 1098h188l262 -654q82 -203 89 -290h6q20 106 90 292l239 652h189l-475 -1241q-70 -178 -156 -263q-89 -86 -246 -86q-94 0 -168 17v145q61 -12 136 -12q96 0 149 41t96 141l58 150zM494 1241v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="þ" d="M158 -492v2048h182v-458l-8 -148h8q111 168 322 168q203 0 315 -149q115 -152 115 -418q0 -268 -115 -419.5t-315 -151.5q-207 0 -322 159h-12q12 -129 12 -162v-469h-182zM340 551q0 -229 70 -324q72 -96 221 -96q272 0 272 422q0 414 -274 414q-152 0 -219.5 -88 t-69.5 -287v-41z" /> +<glyph unicode="ÿ" d="M82 1098h188l262 -654q82 -203 89 -290h6q20 106 90 292l239 652h189l-475 -1241q-70 -178 -156 -263q-89 -86 -246 -86q-94 0 -168 17v145q61 -12 136 -12q96 0 149 41t96 141l58 150zM338 1394.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5zM713 1394.5 q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5z" /> +<glyph unicode="Ā" d="M33 0l483 1468h195l485 -1468h-192l-144 453h-491l-146 -453h-190zM422 618h385l-133 424q-39 121 -62 226q-20 -88 -47 -183zM326 1579v150h575v-150h-575z" /> +<glyph unicode="ā" d="M135 307q0 332 510 348l203 7v69q0 236 -244 236q-150 0 -328 -82l-63 137q199 96 383 96q223 0 328 -88q102 -86 102 -278v-752h-131l-37 152h-8q-74 -94 -158 -133t-209 -39q-164 0 -256 85.5t-92 241.5zM324 305q0 -178 200 -178q150 0 234 82q88 84 88 229v99 l-162 -7q-195 -8 -278 -61q-82 -51 -82 -164zM318 1241v150h575v-150h-575z" /> +<glyph unicode="Ă" d="M33 0l483 1468h195l485 -1468h-192l-144 453h-491l-146 -453h-190zM422 618h385l-133 424q-39 121 -62 226q-20 -88 -47 -183zM311 1856h109q6 -76 47 -101q39 -25 143 -24q182 0 191 125h112q-10 -127 -90 -201q-82 -76 -215 -76q-279 1 -297 277z" /> +<glyph unicode="ă" d="M135 307q0 332 510 348l203 7v69q0 236 -244 236q-150 0 -328 -82l-63 137q199 96 383 96q223 0 328 -88q102 -86 102 -278v-752h-131l-37 152h-8q-74 -94 -158 -133t-209 -39q-164 0 -256 85.5t-92 241.5zM324 305q0 -178 200 -178q150 0 234 82q88 84 88 229v99 l-162 -7q-195 -8 -278 -61q-82 -51 -82 -164zM303 1518h109q6 -76 47 -101q39 -25 143 -24q182 0 191 125h112q-10 -127 -90 -201q-82 -76 -215 -76q-279 1 -297 277z" /> +<glyph unicode="Ą" d="M33 0l483 1468h195l485 -1468h-192l-144 453h-491l-146 -453h-190zM422 618h385l-133 424q-39 121 -62 226q-20 -88 -47 -183zM813 -252q0 144 190 252h131q-170 -127 -170 -238q0 -96 127 -96q68 0 123 15v-113q-78 -23 -156 -23q-121 0 -182 54q-63 55 -63 149z" /> +<glyph unicode="ą" d="M135 307q0 332 510 348l203 7v69q0 236 -244 236q-150 0 -328 -82l-63 137q199 96 383 96q223 0 328 -88q102 -86 102 -278v-752h-131l-37 152h-8q-74 -94 -158 -133t-209 -39q-164 0 -256 85.5t-92 241.5zM324 305q0 -178 200 -178q150 0 234 82q88 84 88 229v99 l-162 -7q-195 -8 -278 -61q-82 -51 -82 -164zM705 -252q0 144 190 252h131q-170 -127 -170 -238q0 -96 127 -96q68 0 123 15v-113q-78 -23 -156 -23q-121 0 -182 54q-63 55 -63 149z" /> +<glyph unicode="Ć" d="M129 733q0 344 178 547t490 203q219 0 383 -86l-78 -156q-156 78 -305 78q-215 0 -342 -158q-129 -160 -129 -430q0 -287 120 -438q119 -150 351 -150q135 0 327 58v-162q-150 -59 -358 -59q-310 0 -473 196q-164 199 -164 557zM660 1579v27q92 127 174 301h219v-21 q-109 -168 -272 -307h-121z" /> +<glyph unicode="ć" d="M172 543q0 279 145 428q145 147 408 147q176 0 336 -59l-62 -158q-147 57 -268 57q-371 0 -371 -413q0 -406 361 -406q160 0 321 62v-160q-135 -61 -329 -61q-258 0 -400 145q-141 145 -141 418zM578 1241v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="Ĉ" d="M129 733q0 344 178 547t490 203q219 0 383 -86l-78 -156q-156 78 -305 78q-215 0 -342 -158q-129 -160 -129 -430q0 -287 120 -438q119 -150 351 -150q135 0 327 58v-162q-150 -59 -358 -59q-310 0 -473 196q-164 199 -164 557zM443 1579v27l59 67q141 156 176 234h193 q35 -78 176 -234l59 -67v-27h-121q-78 49 -211 186q-133 -137 -211 -186h-120z" /> +<glyph unicode="ĉ" d="M172 543q0 279 145 428q145 147 408 147q176 0 336 -59l-62 -158q-147 57 -268 57q-371 0 -371 -413q0 -406 361 -406q160 0 321 62v-160q-135 -61 -329 -61q-258 0 -400 145q-141 145 -141 418zM367 1241v27l59 67q141 156 176 234h193q35 -78 176 -234l59 -67v-27h-121 q-78 49 -211 186q-133 -137 -211 -186h-120z" /> +<glyph unicode="Ċ" d="M129 733q0 344 178 547t490 203q219 0 383 -86l-78 -156q-156 78 -305 78q-215 0 -342 -158q-129 -160 -129 -430q0 -287 120 -438q119 -150 351 -150q135 0 327 58v-162q-150 -59 -358 -59q-310 0 -473 196q-164 199 -164 557zM672 1732.5q0 114.5 106.5 114.5 t106.5 -114.5t-106.5 -114.5t-106.5 114.5z" /> +<glyph unicode="ċ" d="M172 543q0 279 145 428q145 147 408 147q176 0 336 -59l-62 -158q-147 57 -268 57q-371 0 -371 -413q0 -406 361 -406q160 0 321 62v-160q-135 -61 -329 -61q-258 0 -400 145q-141 145 -141 418zM592 1394.5q0 114.5 106.5 114.5t106.5 -114.5t-106.5 -114.5 t-106.5 114.5z" /> +<glyph unicode="Č" d="M129 733q0 344 178 547t490 203q219 0 383 -86l-78 -156q-156 78 -305 78q-215 0 -342 -158q-129 -160 -129 -430q0 -287 120 -438q119 -150 351 -150q135 0 327 58v-162q-150 -59 -358 -59q-310 0 -473 196q-164 199 -164 557zM437 1880v27h120q82 -55 211 -187 q123 129 211 187h121v-27l-59 -67q-143 -160 -176 -234h-193q-33 74 -176 234z" /> +<glyph unicode="č" d="M172 543q0 279 145 428q145 147 408 147q176 0 336 -59l-62 -158q-147 57 -268 57q-371 0 -371 -413q0 -406 361 -406q160 0 321 62v-160q-135 -61 -329 -61q-258 0 -400 145q-141 145 -141 418zM375 1542v27h120q82 -55 211 -187q123 129 211 187h121v-27l-59 -67 q-143 -160 -176 -234h-193q-33 74 -176 234z" /> +<glyph unicode="Ď" d="M135 0v1462h342q319 0 494 -188q176 -193 176 -529q0 -365 -182 -552q-186 -193 -529 -193h-301zM322 160h96q532 0 532 579q0 563 -493 564h-135v-1143zM275 1880v27h120q82 -55 211 -187q123 129 211 187h121v-27l-59 -67q-143 -160 -176 -234h-193q-33 74 -176 234z " /> +<glyph unicode="ď" d="M80 547q0 268 115 419.5t315 151.5q205 0 322 -160h12q-12 129 -12 162v436h182v-1556h-147l-27 147h-8q-115 -168 -322 -167q-203 0 -315 149q-115 152 -115 418zM269 545q0 -414 274 -414q152 0 219 88q66 88 70 287v41q0 229 -70 323q-72 96 -221 97q-272 0 -272 -422 zM1074 1229v26l26 107q27 127 35 194h180v-20q0 -14 -43 -113q-49 -111 -102 -194h-96z" /> +<glyph unicode="Đ" d="M0 659v162h135v641h342q319 0 494 -188q176 -193 176 -529q0 -365 -182 -552q-186 -193 -529 -193h-301v659h-135zM322 160h96q532 0 532 579q0 563 -493 564h-135v-482h380v-162h-380v-499z" /> +<glyph unicode="đ" d="M137 526q0 260 115 405.5t315 145.5q207 0 322 -159h12q-12 127 -12 161v158h-379v137h379v182h182v-182h156v-137h-156v-1237h-147l-27 147h-8q-115 -168 -322 -167q-201 0 -315.5 145t-114.5 401zM326 524q0 -393 274 -393q150 0 217 82q70 84 72 274v39q0 217 -70 308 q-71 92 -221 92q-272 -1 -272 -402z" /> +<glyph unicode="Ē" d="M217 0v1462h842v-164h-656v-452h617v-162h-617v-520h656v-164h-842zM336 1579v150h575v-150h-575z" /> +<glyph unicode="ē" d="M133 541q0 266 135 421.5t363 155.5q212 0 338 -135q127 -137 127 -356v-113h-774q9 -375 344 -375q199 0 370 76v-160q-170 -76 -364 -75q-244 0 -391 149q-148 151 -148 412zM326 662h573q0 305 -272 305q-276 0 -301 -305zM346 1241v150h575v-150h-575z" /> +<glyph unicode="Ĕ" d="M217 0v1462h842v-164h-656v-452h617v-162h-617v-520h656v-164h-842zM342 1856h109q6 -76 47 -101q39 -25 143 -24q182 0 191 125h112q-10 -127 -90 -201q-82 -76 -215 -76q-279 1 -297 277z" /> +<glyph unicode="ĕ" d="M133 541q0 266 135 421.5t363 155.5q212 0 338 -135q127 -137 127 -356v-113h-774q9 -375 344 -375q199 0 370 76v-160q-170 -76 -364 -75q-244 0 -391 149q-148 151 -148 412zM326 662h573q0 305 -272 305q-276 0 -301 -305zM329 1518h109q6 -76 47 -101q39 -25 143 -24 q182 0 191 125h112q-10 -127 -90 -201q-82 -76 -215 -76q-279 1 -297 277z" /> +<glyph unicode="Ė" d="M217 0v1462h842v-164h-656v-452h617v-162h-617v-520h656v-164h-842zM543 1703.5q0 114.5 106.5 114.5t106.5 -114.5t-106.5 -114.5t-106.5 114.5z" /> +<glyph unicode="ė" d="M133 541q0 266 135 421.5t363 155.5q212 0 338 -135q127 -137 127 -356v-113h-774q9 -375 344 -375q199 0 370 76v-160q-170 -76 -364 -75q-244 0 -391 149q-148 151 -148 412zM326 662h573q0 305 -272 305q-276 0 -301 -305zM531 1394.5q0 114.5 106.5 114.5 t106.5 -114.5t-106.5 -114.5t-106.5 114.5z" /> +<glyph unicode="Ę" d="M217 0v1462h842v-164h-656v-452h617v-162h-617v-520h656v-164h-842zM619 -252q0 144 190 252h131q-170 -127 -170 -238q0 -96 127 -96q68 0 123 15v-113q-78 -23 -156 -23q-121 0 -182 54q-63 55 -63 149z" /> +<glyph unicode="ę" d="M133 541q0 266 135 421.5t363 155.5q212 0 338 -135q127 -137 127 -356v-113h-774q9 -375 344 -375q199 0 370 76v-160q-170 -76 -364 -75q-244 0 -391 149q-148 151 -148 412zM326 662h573q0 305 -272 305q-276 0 -301 -305zM641 -221q0 144 190 252h131 q-170 -127 -170 -238q0 -96 127 -96q68 0 123 15v-113q-78 -23 -156 -23q-121 0 -182 54q-63 55 -63 149z" /> +<glyph unicode="Ě" d="M217 0v1462h842v-164h-656v-452h617v-162h-617v-520h656v-164h-842zM318 1880v27h120q82 -55 211 -187q123 129 211 187h121v-27l-59 -67q-143 -160 -176 -234h-193q-33 74 -176 234z" /> +<glyph unicode="ě" d="M133 541q0 266 135 421.5t363 155.5q212 0 338 -135q127 -137 127 -356v-113h-774q9 -375 344 -375q199 0 370 76v-160q-170 -76 -364 -75q-244 0 -391 149q-148 151 -148 412zM326 662h573q0 305 -272 305q-276 0 -301 -305zM308 1542v27h120q82 -55 211 -187 q123 129 211 187h121v-27l-59 -67q-143 -160 -176 -234h-193q-33 74 -176 234z" /> +<glyph unicode="Ĝ" d="M117 733q0 352 161.5 551t446.5 199q193 0 346 -86l-72 -162q-150 84 -280 84q-193 0 -299.5 -155.5t-106.5 -432.5q0 -588 394 -588q94 0 202 29v436h-237v164h422v-717q-199 -76 -420 -75q-262 0 -410 200q-147 200 -147 553zM363 1579v27l59 67q141 156 176 234h193 q35 -78 176 -234l59 -67v-27h-121q-78 49 -211 186q-133 -137 -211 -186h-120z" /> +<glyph unicode="ĝ" d="M102 -186q0 212 240 270q-96 47 -96 154q0 113 133 192q-86 35 -139 123q-51 84 -52 186q0 182 106.5 280.5t303.5 98.5q88 0 150 -20h378v-113l-196 -27q66 -88 65 -213q0 -162 -106 -256q-109 -96 -297 -96q-55 0 -86 6q-100 -55 -100 -133q0 -84 161 -84h187 q172 0 266 -78q92 -76 92 -219q0 -377 -565 -377q-218 0 -332 80q-113 81 -113 226zM274 -180q0 -174 271 -174q395 0 395 223q0 88 -49 118.5t-199 30.5h-188q-230 1 -230 -198zM367 745q0 -227 227 -227q223 0 223 230q0 240 -225 239.5t-225 -242.5zM275 1241v27l59 67 q141 156 176 234h193q35 -78 176 -234l59 -67v-27h-121q-78 49 -211 186q-133 -137 -211 -186h-120z" /> +<glyph unicode="Ğ" d="M117 733q0 352 161.5 551t446.5 199q193 0 346 -86l-72 -162q-150 84 -280 84q-193 0 -299.5 -155.5t-106.5 -432.5q0 -588 394 -588q94 0 202 29v436h-237v164h422v-717q-199 -76 -420 -75q-262 0 -410 200q-147 200 -147 553zM411 1856h109q6 -76 47 -101 q39 -25 143 -24q182 0 191 125h112q-10 -127 -90 -201q-82 -76 -215 -76q-279 1 -297 277z" /> +<glyph unicode="ğ" d="M102 -186q0 212 240 270q-96 47 -96 154q0 113 133 192q-86 35 -139 123q-51 84 -52 186q0 182 106.5 280.5t303.5 98.5q88 0 150 -20h378v-113l-196 -27q66 -88 65 -213q0 -162 -106 -256q-109 -96 -297 -96q-55 0 -86 6q-100 -55 -100 -133q0 -84 161 -84h187 q172 0 266 -78q92 -76 92 -219q0 -377 -565 -377q-218 0 -332 80q-113 81 -113 226zM274 -180q0 -174 271 -174q395 0 395 223q0 88 -49 118.5t-199 30.5h-188q-230 1 -230 -198zM367 745q0 -227 227 -227q223 0 223 230q0 240 -225 239.5t-225 -242.5zM301 1518h109 q6 -76 47 -101q39 -25 143 -24q182 0 191 125h112q-10 -127 -90 -201q-82 -76 -215 -76q-279 1 -297 277z" /> +<glyph unicode="Ġ" d="M117 733q0 352 161.5 551t446.5 199q193 0 346 -86l-72 -162q-150 84 -280 84q-193 0 -299.5 -155.5t-106.5 -432.5q0 -588 394 -588q94 0 202 29v436h-237v164h422v-717q-199 -76 -420 -75q-262 0 -410 200q-147 200 -147 553zM617 1732.5q0 114.5 106.5 114.5 t106.5 -114.5t-106.5 -114.5t-106.5 114.5z" /> +<glyph unicode="ġ" d="M102 -186q0 212 240 270q-96 47 -96 154q0 113 133 192q-86 35 -139 123q-51 84 -52 186q0 182 106.5 280.5t303.5 98.5q88 0 150 -20h378v-113l-196 -27q66 -88 65 -213q0 -162 -106 -256q-109 -96 -297 -96q-55 0 -86 6q-100 -55 -100 -133q0 -84 161 -84h187 q172 0 266 -78q92 -76 92 -219q0 -377 -565 -377q-218 0 -332 80q-113 81 -113 226zM274 -180q0 -174 271 -174q395 0 395 223q0 88 -49 118.5t-199 30.5h-188q-230 1 -230 -198zM367 745q0 -227 227 -227q223 0 223 230q0 240 -225 239.5t-225 -242.5zM508 1394.5 q0 114.5 106.5 114.5t106.5 -114.5t-106.5 -114.5t-106.5 114.5z" /> +<glyph unicode="Ģ" d="M117 733q0 352 161.5 551t446.5 199q193 0 346 -86l-72 -162q-150 84 -280 84q-193 0 -299.5 -155.5t-106.5 -432.5q0 -588 394 -588q94 0 202 29v436h-237v164h422v-717q-199 -76 -420 -75q-262 0 -410 200q-147 200 -147 553zM540 -426q63 145 84 301h178v-20 q0 -22 -49 -123q-51 -109 -114 -185h-99v27z" /> +<glyph unicode="ģ" d="M102 -186q0 212 240 270q-96 47 -96 154q0 113 133 192q-86 35 -139 123q-51 84 -52 186q0 182 106.5 280.5t303.5 98.5q88 0 150 -20h378v-113l-196 -27q66 -88 65 -213q0 -162 -106 -256q-109 -96 -297 -96q-55 0 -86 6q-100 -55 -100 -133q0 -84 161 -84h187 q172 0 266 -78q92 -76 92 -219q0 -377 -565 -377q-218 0 -332 80q-113 81 -113 226zM274 -180q0 -174 271 -174q395 0 395 223q0 88 -49 118.5t-199 30.5h-188q-230 1 -230 -198zM367 745q0 -227 227 -227q223 0 223 230q0 240 -225 239.5t-225 -242.5zM528 1241v21 q0 20 45 118q45 100 119 189h98v-27q-61 -145 -83 -301h-179z" /> +<glyph unicode="Ĥ" d="M135 0v1462h187v-616h585v616h187v-1462h-187v682h-585v-682h-187zM283 1579v27l59 67q141 156 176 234h193q35 -78 176 -234l59 -67v-27h-121q-78 49 -211 186q-133 -137 -211 -186h-120z" /> +<glyph unicode="ĥ" d="M160 0v1556h182v-462l-8 -144h10q104 168 336 168q389 0 389 -401v-717h-182v707q0 260 -238 260q-307 0 -307 -398v-569h-182zM297 1634v27l59 67q141 156 176 234h193q35 -78 176 -234l59 -67v-27h-121q-78 49 -211 186q-133 -137 -211 -186h-120z" /> +<glyph unicode="Ħ" d="M0 1069v150h135v243h187v-243h585v243h187v-243h135v-150h-135v-1069h-187v682h-585v-682h-187v1069h-135zM322 846h585v223h-585v-223z" /> +<glyph unicode="ħ" d="M4 1237v137h156v182h184v-182h418v-137h-420v-184l-10 -144h10q106 168 346 168q190 0 287 -96q94 -94 94 -305v-676h-182v666q0 260 -236 260q-160 0 -235 -95q-74 -92 -74 -303v-528h-182v1237h-156z" /> +<glyph unicode="Ĩ" d="M225 0v123l295 20v1176l-295 20v123h776v-123l-294 -20v-1176l294 -20v-123h-776zM254 1579q25 264 211 264q57 0 162 -55q104 -57 135 -57q82 0 106 114h105q-27 -264 -211 -264q-57 0 -158 57q-100 55 -139 56q-82 0 -107 -115h-104z" /> +<glyph unicode="ĩ" d="M197 0v123l344 20v811l-269 21v123h451v-955l352 -20v-123h-878zM252 1241q25 264 211 264q57 0 162 -55q104 -57 135 -57q82 0 106 114h105q-27 -264 -211 -264q-57 0 -158 57q-100 55 -139 56q-82 0 -107 -115h-104z" /> +<glyph unicode="Ī" d="M225 0v123l295 20v1176l-295 20v123h776v-123l-294 -20v-1176l294 -20v-123h-776zM328 1579v150h575v-150h-575z" /> +<glyph unicode="ī" d="M197 0v123l344 20v811l-269 21v123h451v-955l352 -20v-123h-878zM326 1241v150h575v-150h-575z" /> +<glyph unicode="Ĭ" d="M225 0v123l295 20v1176l-295 20v123h776v-123l-294 -20v-1176l294 -20v-123h-776zM313 1856h109q6 -76 47 -101q39 -25 143 -24q182 0 191 125h112q-10 -127 -90 -201q-82 -76 -215 -76q-279 1 -297 277z" /> +<glyph unicode="ĭ" d="M197 0v123l344 20v811l-269 21v123h451v-955l352 -20v-123h-878zM338 1518h109q6 -76 47 -101q39 -25 143 -24q182 0 191 125h112q-10 -127 -90 -201q-82 -76 -215 -76q-279 1 -297 277z" /> +<glyph unicode="Į" d="M225 0v123l295 20v1176l-295 20v123h776v-123l-294 -20v-1176l294 -20v-123h-776zM422 -252q0 144 190 252h131q-170 -127 -170 -238q0 -96 127 -96q68 0 123 15v-113q-78 -23 -156 -23q-121 0 -182 54q-63 55 -63 149z" /> +<glyph unicode="į" d="M197 0v123l344 20v811l-269 21v123h451v-955l352 -20v-123h-878zM526 1435.5q0 114.5 106.5 114.5t106.5 -114q0 -57 -32.5 -86t-73.5 -29q-107 0 -107 114.5zM439 -252q0 144 190 252h131q-170 -127 -170 -238q0 -96 127 -96q68 0 123 15v-113q-78 -23 -156 -23 q-121 0 -182 54q-63 55 -63 149z" /> +<glyph unicode="İ" d="M225 0v123l295 20v1176l-295 20v123h776v-123l-294 -20v-1176l294 -20v-123h-776zM508 1732.5q0 114.5 106.5 114.5t106.5 -114.5t-106.5 -114.5t-106.5 114.5z" /> +<glyph unicode="ı" d="M197 0v123l344 20v811l-269 21v123h451v-955l352 -20v-123h-878z" /> +<glyph unicode="IJ" d="M156 0v1462h186v-1462h-186zM492 23v163q90 -45 186 -45q195 0 194 283v1038h187v-1026q0 -457 -365 -456q-134 0 -202 43z" /> +<glyph unicode="ij" d="M188 1435.5q0 114.5 106.5 114.5t106.5 -114q0 -57 -32.5 -86t-73.5 -29q-107 0 -107 114.5zM203 0v1098h182v-1098h-182zM502 -303q86 -39 190 -39q164 0 164 215v1081l-215 21v123h397v-1215q0 -182 -86 -278.5t-235 -96.5q-121 0 -215 35v154zM821 1435.5 q0 114.5 106.5 114.5t106.5 -114q0 -58 -30 -86q-33 -29 -76 -29q-107 0 -107 114.5z" /> +<glyph unicode="Ĵ" d="M137 39v166q162 -61 309 -62q162 0 254.5 80t92.5 226v1013h186v-1011q0 -215 -141 -345q-139 -127 -377 -126q-215 0 -324 59zM551 1579v27l59 67q141 156 176 234h193q35 -78 176 -234l59 -67v-27h-121q-78 49 -211 186q-133 -137 -211 -186h-120z" /> +<glyph unicode="ĵ" d="M303 1241v27l59 67q141 156 176 234h193q35 -78 176 -234l59 -67v-27h-121q-78 49 -211 186q-133 -137 -211 -186h-120zM135 -303q131 -39 289 -39q119 0 182 57q66 59 66 158v1081l-420 21v123h602v-1215q0 -180 -113 -278q-111 -96 -319 -97q-160 0 -287 35v154z" /> +<glyph unicode="Ķ" d="M211 0v1462h186v-731l121 168l453 563h209l-521 -637l539 -825h-211l-450 698l-140 -114v-584h-186zM510 -426q63 145 84 301h178v-20q0 -22 -49 -123q-51 -109 -114 -185h-99v27z" /> +<glyph unicode="ķ" d="M215 0v1556h180v-714l-16 -289h4l135 152l395 393h222l-494 -475l522 -623h-213l-426 504l-129 -82v-422h-180zM485 -426q63 145 84 301h178v-20q0 -22 -49 -123q-51 -109 -114 -185h-99v27z" /> +<glyph unicode="ĸ" d="M215 0v1098h180v-291l-16 -299h4l137 166l402 424h213l-494 -510l522 -588h-213l-426 469l-129 -82v-387h-180z" /> +<glyph unicode="Ĺ" d="M233 0v1462h187v-1296h635v-166h-822zM262 1579v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="ĺ" d="M188 0v123l344 20v1270l-268 21v122h451v-1413l352 -20v-123h-879zM496 1636v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="Ļ" d="M233 0v1462h187v-1296h635v-166h-822zM491 -426q63 145 84 301h178v-20q0 -22 -49 -123q-51 -109 -114 -185h-99v27z" /> +<glyph unicode="ļ" d="M188 0v123l344 20v1270l-268 21v122h451v-1413l352 -20v-123h-879zM460 -426q63 145 84 301h178v-20q0 -22 -49 -123q-51 -109 -114 -185h-99v27z" /> +<glyph unicode="Ľ" d="M233 0v1462h187v-1296h635v-166h-822zM750 1137v26l26 107q27 127 35 194h180v-20q0 -14 -43 -113q-49 -111 -102 -194h-96z" /> +<glyph unicode="ľ" d="M188 0v123l344 20v1270l-268 21v122h451v-1413l352 -20v-123h-879zM865 1229v26l26 107q27 127 35 194h180v-20q0 -14 -43 -113q-49 -111 -102 -194h-96z" /> +<glyph unicode="Ŀ" d="M233 0v1462h187v-1296h635v-166h-822zM750 773.5q0 114.5 106.5 114.5t106.5 -114.5t-106.5 -114.5t-106.5 114.5z" /> +<glyph unicode="ŀ" d="M188 0v123l344 20v1270l-268 21v122h451v-1413l352 -20v-123h-879zM866 681.5q0 114.5 106.5 114.5t106.5 -114.5t-106.5 -114.5t-106.5 114.5z" /> +<glyph unicode="Ł" d="M72 539l198 125v798h187v-680l299 189l78 -125l-377 -238v-442h635v-166h-822v492l-125 -78z" /> +<glyph unicode="ł" d="M188 0v123l344 20v449l-141 -92l-78 121l219 143v649l-268 21v122h451v-673l164 108l77 -121l-241 -159v-568l352 -20v-123h-879z" /> +<glyph unicode="Ń" d="M135 0v1462h213l578 -1204h6q-14 285 -14 404v800h174v-1462h-215l-580 1210h-8q18 -276 18 -417v-793h-172zM516 1579v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="ń" d="M160 0v1098h147l27 -148h10q104 168 336 168q389 0 389 -401v-717h-182v707q0 260 -238 260q-307 0 -307 -398v-569h-182zM529 1241v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="Ņ" d="M135 0v1462h213l578 -1204h6q-14 285 -14 404v800h174v-1462h-215l-580 1210h-8q18 -276 18 -417v-793h-172zM434 -426q63 145 84 301h178v-20q0 -22 -49 -123q-51 -109 -114 -185h-99v27z" /> +<glyph unicode="ņ" d="M160 0v1098h147l27 -148h10q104 168 336 168q389 0 389 -401v-717h-182v707q0 260 -238 260q-307 0 -307 -398v-569h-182zM454 -426q63 145 84 301h178v-20q0 -22 -49 -123q-51 -109 -114 -185h-99v27z" /> +<glyph unicode="Ň" d="M135 0v1462h213l578 -1204h6q-14 285 -14 404v800h174v-1462h-215l-580 1210h-8q18 -276 18 -417v-793h-172zM281 1880v27h120q82 -55 211 -187q123 129 211 187h121v-27l-59 -67q-143 -160 -176 -234h-193q-33 74 -176 234z" /> +<glyph unicode="ň" d="M160 0v1098h147l27 -148h10q104 168 336 168q389 0 389 -401v-717h-182v707q0 260 -238 260q-307 0 -307 -398v-569h-182zM295 1542v27h120q82 -55 211 -187q123 129 211 187h121v-27l-59 -67q-143 -160 -176 -234h-193q-33 74 -176 234z" /> +<glyph unicode="ʼn" d="M-16 961q59 231 100 501h199l14 -22q-47 -190 -176 -479h-137zM262 0v1098h148l26 -148h10q43 77 129 123q84 45 189 45q367 0 366 -401v-717h-182v707q0 260 -219 260q-285 0 -285 -398v-569h-182z" /> +<glyph unicode="Ŋ" d="M135 0v1462h213l578 -1128h10q-18 272 -18 403v725h174v-1442q0 -201 -101 -303q-102 -106 -286 -106q-94 0 -156 24v160q74 -20 158 -20q211 0 211 225l-621 1210h-8q18 -276 18 -417v-793h-172z" /> +<glyph unicode="ŋ" d="M160 0v1098h147l27 -148h10q104 168 336 168q389 0 389 -401v-875q0 -334 -291 -334q-82 0 -133 25v148q53 -20 115 -21q127 0 127 170v877q0 260 -238 260q-307 0 -307 -398v-569h-182z" /> +<glyph unicode="Ō" d="M84 735q0 750 534 750q258 0 392 -195q137 -199 137 -557q0 -362 -137 -557q-138 -197 -394 -196q-532 -1 -532 755zM281 733q0 -590 335 -590q174 0 254 145.5t80 444.5q0 305 -82 445q-84 143 -250 143q-337 0 -337 -588zM330 1579v150h575v-150h-575z" /> +<glyph unicode="ō" d="M115 551q0 264 135 415.5t366 151.5q217 0 356.5 -155.5t139.5 -411.5q0 -266 -137 -418q-139 -154 -365 -153q-219 0 -356 155q-139 158 -139 416zM303 551q0 -420 311 -420q309 0 310 420q0 416 -312 416q-309 0 -309 -416zM326 1241v150h575v-150h-575z" /> +<glyph unicode="Ŏ" d="M84 735q0 750 534 750q258 0 392 -195q137 -199 137 -557q0 -362 -137 -557q-138 -197 -394 -196q-532 -1 -532 755zM281 733q0 -590 335 -590q174 0 254 145.5t80 444.5q0 305 -82 445q-84 143 -250 143q-337 0 -337 -588zM313 1856h109q6 -76 47 -101q39 -25 143 -24 q182 0 191 125h112q-10 -127 -90 -201q-82 -76 -215 -76q-279 1 -297 277z" /> +<glyph unicode="ŏ" d="M115 551q0 264 135 415.5t366 151.5q217 0 356.5 -155.5t139.5 -411.5q0 -266 -137 -418q-139 -154 -365 -153q-219 0 -356 155q-139 158 -139 416zM303 551q0 -420 311 -420q309 0 310 420q0 416 -312 416q-309 0 -309 -416zM313 1518h109q6 -76 47 -101q39 -25 143 -24 q182 0 191 125h112q-10 -127 -90 -201q-82 -76 -215 -76q-279 1 -297 277z" /> +<glyph unicode="Ő" d="M84 735q0 750 534 750q258 0 392 -195q137 -199 137 -557q0 -362 -137 -557q-138 -197 -394 -196q-532 -1 -532 755zM281 733q0 -590 335 -590q174 0 254 145.5t80 444.5q0 305 -82 445q-84 143 -250 143q-337 0 -337 -588zM326 1579v27q92 127 174 301h198v-21 q-109 -168 -272 -307h-100zM688 1579v27q92 125 174 301h199v-21q-109 -168 -272 -307h-101z" /> +<glyph unicode="ő" d="M115 551q0 264 135 415.5t366 151.5q217 0 356.5 -155.5t139.5 -411.5q0 -266 -137 -418q-139 -154 -365 -153q-219 0 -356 155q-139 158 -139 416zM303 551q0 -420 311 -420q309 0 310 420q0 416 -312 416q-309 0 -309 -416zM328 1241v27q92 127 174 301h198v-21 q-109 -168 -272 -307h-100zM690 1241v27q92 125 174 301h199v-21q-109 -168 -272 -307h-101z" /> +<glyph unicode="Œ" d="M20 735q0 750 512 750q80 0 154 -23h541v-164h-398v-452h359v-162h-359v-520h398v-164h-574l-49 -10q-57 -10 -96 -10q-488 -1 -488 755zM209 733q0 -590 309 -590q70 0 133 33v1112q-57 33 -131 33q-311 0 -311 -588z" /> +<glyph unicode="œ" d="M57 551q0 268 88 417.5t242 149.5q172 0 260 -217q82 217 242 217q131 0 209 -133q76 -131 76 -358v-113h-439q2 -375 189 -375q111 0 204 76v-162q-96 -74 -217 -73q-88 0 -159 59q-70 57 -101 162q-90 -221 -266 -221q-145 0 -238 153q-90 150 -90 418zM227 551 q0 -213 39 -315q41 -104 131 -105q168 0 168 410q0 426 -170 426q-90 0 -129 -102.5t-39 -313.5zM737 662h260q0 305 -123 305q-125 0 -137 -305z" /> +<glyph unicode="Ŕ" d="M186 0v1462h357q520 0 520 -415q0 -287 -289 -392l397 -655h-219l-350 604h-229v-604h-187zM373 762h164q170 0 251.5 65.5t81.5 210.5q0 141 -79.5 203t-258.5 62h-159v-541zM447 1579v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="ŕ" d="M264 0v1098h148l22 -201h8q76 117 162 170q84 51 215 51q119 0 240 -45l-49 -166q-121 45 -224 45q-163 0 -251 -92q-88 -90 -89 -268v-592h-182zM510 1241v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="Ŗ" d="M186 0v1462h357q520 0 520 -415q0 -287 -289 -392l397 -655h-219l-350 604h-229v-604h-187zM373 762h164q170 0 251.5 65.5t81.5 210.5q0 141 -79.5 203t-258.5 62h-159v-541zM481 -426q63 145 84 301h178v-20q0 -22 -49 -123q-51 -109 -114 -185h-99v27z" /> +<glyph unicode="ŗ" d="M264 0v1098h148l22 -201h8q76 117 162 170q84 51 215 51q119 0 240 -45l-49 -166q-121 45 -224 45q-163 0 -251 -92q-88 -90 -89 -268v-592h-182zM186 -426q63 145 84 301h178v-20q0 -22 -49 -123q-51 -109 -114 -185h-99v27z" /> +<glyph unicode="Ř" d="M186 0v1462h357q520 0 520 -415q0 -287 -289 -392l397 -655h-219l-350 604h-229v-604h-187zM373 762h164q170 0 251.5 65.5t81.5 210.5q0 141 -79.5 203t-258.5 62h-159v-541zM252 1880v27h120q82 -55 211 -187q123 129 211 187h121v-27l-59 -67q-143 -160 -176 -234 h-193q-33 74 -176 234z" /> +<glyph unicode="ř" d="M264 0v1098h148l22 -201h8q76 117 162 170q84 51 215 51q119 0 240 -45l-49 -166q-121 45 -224 45q-163 0 -251 -92q-88 -90 -89 -268v-592h-182zM295 1542v27h120q82 -55 211 -187q123 129 211 187h121v-27l-59 -67q-143 -160 -176 -234h-193q-33 74 -176 234z" /> +<glyph unicode="Ś" d="M141 49v178q211 -86 414 -86q350 0 350 240q0 104 -71 160q-74 57 -285 133q-211 74 -299 174q-90 102 -90 264q0 174 129 272.5t352 98.5q225 0 416 -78l-64 -164q-197 78 -360 78q-293 0 -293 -209q0 -102 66 -164q66 -63 270 -133q244 -88 327.5 -182t83.5 -240 q0 -190 -139 -301q-138 -111 -393 -110q-258 -1 -414 69zM514 1579v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="ś" d="M203 49v166q195 -86 370 -86q274 0 275 162q0 55 -49 98t-226 107q-233 86 -294 159q-59 72 -60 172q0 135 111 213q113 78 309.5 78t368.5 -74l-60 -149q-184 72 -319 72q-236 0 -236 -133q0 -57 51.5 -96.5t231.5 -102.5q207 -76 280 -152q70 -72 70 -182 q0 -150 -116.5 -235.5t-331.5 -85.5q-246 -1 -375 69zM512 1241v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="Ŝ" d="M141 49v178q211 -86 414 -86q350 0 350 240q0 104 -71 160q-74 57 -285 133q-211 74 -299 174q-90 102 -90 264q0 174 129 272.5t352 98.5q225 0 416 -78l-64 -164q-197 78 -360 78q-293 0 -293 -209q0 -102 66 -164q66 -63 270 -133q244 -88 327.5 -182t83.5 -240 q0 -190 -139 -301q-138 -111 -393 -110q-258 -1 -414 69zM303 1579v27l59 67q141 156 176 234h193q35 -78 176 -234l59 -67v-27h-121q-78 49 -211 186q-133 -137 -211 -186h-120z" /> +<glyph unicode="ŝ" d="M203 49v166q195 -86 370 -86q274 0 275 162q0 55 -49 98t-226 107q-233 86 -294 159q-59 72 -60 172q0 135 111 213q113 78 309.5 78t368.5 -74l-60 -149q-184 72 -319 72q-236 0 -236 -133q0 -57 51.5 -96.5t231.5 -102.5q207 -76 280 -152q70 -72 70 -182 q0 -150 -116.5 -235.5t-331.5 -85.5q-246 -1 -375 69zM306 1241v27l59 67q141 156 176 234h193q35 -78 176 -234l59 -67v-27h-121q-78 49 -211 186q-133 -137 -211 -186h-120z" /> +<glyph unicode="Ş" d="M141 49v178q211 -86 414 -86q350 0 350 240q0 104 -71 160q-74 57 -285 133q-211 74 -299 174q-90 102 -90 264q0 174 129 272.5t352 98.5q225 0 416 -78l-64 -164q-197 78 -360 78q-293 0 -293 -209q0 -102 66 -164q66 -63 270 -133q244 -88 327.5 -182t83.5 -240 q0 -190 -139 -301q-138 -111 -393 -110q-258 -1 -414 69zM405 -375q34 -6 80 -6q152 0 151 92q0 72 -172 113l91 176h120l-57 -115q160 -37 160 -172q0 -205 -291 -205q-39 0 -82 9v108z" /> +<glyph unicode="ş" d="M203 49v166q195 -86 370 -86q274 0 275 162q0 55 -49 98t-226 107q-233 86 -294 159q-59 72 -60 172q0 135 111 213q113 78 309.5 78t368.5 -74l-60 -149q-184 72 -319 72q-236 0 -236 -133q0 -57 51.5 -96.5t231.5 -102.5q207 -76 280 -152q70 -72 70 -182 q0 -150 -116.5 -235.5t-331.5 -85.5q-246 -1 -375 69zM420 -375q34 -6 80 -6q152 0 151 92q0 72 -172 113l91 176h120l-57 -115q160 -37 160 -172q0 -205 -291 -205q-39 0 -82 9v108z" /> +<glyph unicode="Š" d="M141 49v178q211 -86 414 -86q350 0 350 240q0 104 -71 160q-74 57 -285 133q-211 74 -299 174q-90 102 -90 264q0 174 129 272.5t352 98.5q225 0 416 -78l-64 -164q-197 78 -360 78q-293 0 -293 -209q0 -102 66 -164q66 -63 270 -133q244 -88 327.5 -182t83.5 -240 q0 -190 -139 -301q-138 -111 -393 -110q-258 -1 -414 69zM314 1880v27h120q82 -55 211 -187q123 129 211 187h121v-27l-59 -67q-143 -160 -176 -234h-193q-33 74 -176 234z" /> +<glyph unicode="š" d="M203 49v166q195 -86 370 -86q274 0 275 162q0 55 -49 98t-226 107q-233 86 -294 159q-59 72 -60 172q0 135 111 213q113 78 309.5 78t368.5 -74l-60 -149q-184 72 -319 72q-236 0 -236 -133q0 -57 51.5 -96.5t231.5 -102.5q207 -76 280 -152q70 -72 70 -182 q0 -150 -116.5 -235.5t-331.5 -85.5q-246 -1 -375 69zM293 1542v27h120q82 -55 211 -187q123 129 211 187h121v-27l-59 -67q-143 -160 -176 -234h-193q-33 74 -176 234z" /> +<glyph unicode="Ţ" d="M102 1298v164h1022v-164h-417v-1298h-187v1298h-418zM448 -426q63 145 84 301h178v-20q0 -22 -49 -123q-51 -109 -114 -185h-99v27z" /> +<glyph unicode="ţ" d="M139 961v94l267 49l77 287h105v-293h438v-137h-438v-637q0 -195 192 -195q98 0 240 21v-138q-137 -33 -252 -32q-362 0 -362 344v637h-267zM489 -426q63 145 84 301h178v-20q0 -22 -49 -123q-51 -109 -114 -185h-99v27z" /> +<glyph unicode="Ť" d="M102 1298v164h1022v-164h-417v-1298h-187v1298h-418zM283 1880v27h120q82 -55 211 -187q123 129 211 187h121v-27l-59 -67q-143 -160 -176 -234h-193q-33 74 -176 234z" /> +<glyph unicode="ť" d="M139 961v94l267 49l77 287h105v-293h438v-137h-438v-637q0 -195 192 -195q98 0 240 21v-138q-137 -33 -252 -32q-362 0 -362 344v637h-267zM799 1229v26l26 107q27 127 35 194h180v-20q0 -14 -43 -113q-49 -111 -102 -194h-96z" /> +<glyph unicode="Ŧ" d="M102 1298v164h1022v-164h-417v-479h294v-149h-294v-670h-187v670h-297v149h297v479h-418z" /> +<glyph unicode="ŧ" d="M139 961v94l267 49l77 287h105v-293h438v-137h-438v-248h307v-138h-307v-251q0 -195 192 -195q98 0 240 21v-138q-137 -33 -252 -32q-362 0 -362 344v251h-162v138h162v248h-267z" /> +<glyph unicode="Ũ" d="M125 520v942h186v-932q0 -387 307 -387q293 0 300 389v932h186v-948q0 -260 -125 -397q-127 -139 -371 -139q-483 -1 -483 540zM260 1579q25 264 211 264q57 0 162 -55q104 -57 135 -57q82 0 106 114h105q-27 -264 -211 -264q-57 0 -158 57q-100 55 -139 56 q-82 0 -107 -115h-104z" /> +<glyph unicode="ũ" d="M160 381v717h182v-707q0 -260 236 -260q162 0 235.5 92t73.5 305v570h182v-1098h-147l-27 147h-10q-106 -168 -334 -167q-391 0 -391 401zM256 1241q25 264 211 264q57 0 162 -55q104 -57 135 -57q82 0 106 114h105q-27 -264 -211 -264q-57 0 -158 57q-100 55 -139 56 q-82 0 -107 -115h-104z" /> +<glyph unicode="Ū" d="M125 520v942h186v-932q0 -387 307 -387q293 0 300 389v932h186v-948q0 -260 -125 -397q-127 -139 -371 -139q-483 -1 -483 540zM326 1579v150h575v-150h-575z" /> +<glyph unicode="ū" d="M160 381v717h182v-707q0 -260 236 -260q162 0 235.5 92t73.5 305v570h182v-1098h-147l-27 147h-10q-106 -168 -334 -167q-391 0 -391 401zM328 1241v150h575v-150h-575z" /> +<glyph unicode="Ŭ" d="M125 520v942h186v-932q0 -387 307 -387q293 0 300 389v932h186v-948q0 -260 -125 -397q-127 -139 -371 -139q-483 -1 -483 540zM313 1856h109q6 -76 47 -101q39 -25 143 -24q182 0 191 125h112q-10 -127 -90 -201q-82 -76 -215 -76q-279 1 -297 277z" /> +<glyph unicode="ŭ" d="M160 381v717h182v-707q0 -260 236 -260q162 0 235.5 92t73.5 305v570h182v-1098h-147l-27 147h-10q-106 -168 -334 -167q-391 0 -391 401zM313 1518h109q6 -76 47 -101q39 -25 143 -24q182 0 191 125h112q-10 -127 -90 -201q-82 -76 -215 -76q-279 1 -297 277z" /> +<glyph unicode="Ů" d="M125 520v942h186v-932q0 -387 307 -387q293 0 300 389v932h186v-948q0 -260 -125 -397q-127 -139 -371 -139q-483 -1 -483 540zM389 1794q0 100 62 158q59 57 161 57q100 0 166 -59q63 -57 64 -154q0 -100 -64 -158q-66 -59 -166 -59q-102 0 -161 57q-62 58 -62 158z M500 1794q0 -113 112 -113q53 0 82 29q31 31 31 84q0 51 -30.5 82t-82.5 31q-49 0 -80 -31q-32 -33 -32 -82z" /> +<glyph unicode="ů" d="M160 381v717h182v-707q0 -260 236 -260q162 0 235.5 92t73.5 305v570h182v-1098h-147l-27 147h-10q-106 -168 -334 -167q-391 0 -391 401zM385 1456q0 100 62 158q59 57 161 57q100 0 166 -59q63 -57 64 -154q0 -100 -64 -158q-66 -59 -166 -59q-102 0 -161 57 q-62 58 -62 158zM496 1456q0 -113 112 -113q53 0 82 29q31 31 31 84q0 51 -30.5 82t-82.5 31q-49 0 -80 -31q-32 -33 -32 -82z" /> +<glyph unicode="Ű" d="M125 520v942h186v-932q0 -387 307 -387q293 0 300 389v932h186v-948q0 -260 -125 -397q-127 -139 -371 -139q-483 -1 -483 540zM322 1579v27q92 127 174 301h198v-21q-109 -168 -272 -307h-100zM684 1579v27q92 125 174 301h199v-21q-109 -168 -272 -307h-101z" /> +<glyph unicode="ű" d="M160 381v717h182v-707q0 -260 236 -260q162 0 235.5 92t73.5 305v570h182v-1098h-147l-27 147h-10q-106 -168 -334 -167q-391 0 -391 401zM326 1241v27q92 127 174 301h198v-21q-109 -168 -272 -307h-100zM688 1241v27q92 125 174 301h199v-21q-109 -168 -272 -307h-101z " /> +<glyph unicode="Ų" d="M125 520v942h186v-932q0 -387 307 -387q293 0 300 389v932h186v-948q0 -260 -125 -397q-127 -139 -371 -139q-483 -1 -483 540zM441 -252q0 144 190 252h131q-170 -127 -170 -238q0 -96 127 -96q68 0 123 15v-113q-78 -23 -156 -23q-121 0 -182 54q-63 55 -63 149z" /> +<glyph unicode="ų" d="M160 381v717h182v-707q0 -260 236 -260q162 0 235.5 92t73.5 305v570h182v-1098h-147l-27 147h-10q-106 -168 -334 -167q-391 0 -391 401zM723 -252q0 144 190 252h131q-170 -127 -170 -238q0 -96 127 -96q68 0 123 15v-113q-78 -23 -156 -23q-121 0 -182 54 q-63 55 -63 149z" /> +<glyph unicode="Ŵ" d="M2 1462h170l88 -663q18 -145 39 -377q18 -203 18 -242q27 162 70 312l141 516h177l145 -521q57 -205 72 -307q6 92 65 619l70 663h170l-187 -1462h-190l-168 580q-43 147 -66 282q-31 -168 -65 -284l-156 -578h-190zM285 1579v27l59 67q141 156 176 234h193 q35 -78 176 -234l59 -67v-27h-121q-78 49 -211 186q-133 -137 -211 -186h-120z" /> +<glyph unicode="ŵ" d="M-4 1098h162l98 -543q39 -215 57 -393h6q33 195 68 358l133 578h193l127 -578q43 -188 67 -358h6q29 225 60 393l102 543h158l-225 -1098h-195l-131 596l-68 330h-6l-65 -334l-135 -592h-189zM281 1241v27l59 67q141 156 176 234h193q35 -78 176 -234l59 -67v-27h-121 q-78 49 -211 186q-133 -137 -211 -186h-120z" /> +<glyph unicode="Ŷ" d="M33 1462h203l376 -739l381 739h201l-487 -893v-569h-187v559zM283 1579v27l59 67q141 156 176 234h193q35 -78 176 -234l59 -67v-27h-121q-78 49 -211 186q-133 -137 -211 -186h-120z" /> +<glyph unicode="ŷ" d="M82 1098h188l262 -654q82 -203 89 -290h6q20 106 90 292l239 652h189l-475 -1241q-70 -178 -156 -263q-89 -86 -246 -86q-94 0 -168 17v145q61 -12 136 -12q96 0 149 41t96 141l58 150zM285 1241v27l59 67q141 156 176 234h193q35 -78 176 -234l59 -67v-27h-121 q-78 49 -211 186q-133 -137 -211 -186h-120z" /> +<glyph unicode="Ÿ" d="M33 1462h203l376 -739l381 739h201l-487 -893v-569h-187v559zM332 1732.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5zM707 1732.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5z" /> +<glyph unicode="Ź" d="M102 0v145l793 1151h-772v166h981v-145l-793 -1151h813v-166h-1022zM475 1579v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="ź" d="M182 0v125l660 836h-627v137h811v-146l-647 -815h665v-137h-862zM496 1241v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="Ż" d="M102 0v145l793 1151h-772v166h981v-145l-793 -1151h813v-166h-1022zM524 1732.5q0 114.5 106.5 114.5t106.5 -114.5t-106.5 -114.5t-106.5 114.5z" /> +<glyph unicode="ż" d="M182 0v125l660 836h-627v137h811v-146l-647 -815h665v-137h-862zM524 1394.5q0 114.5 106.5 114.5t106.5 -114.5t-106.5 -114.5t-106.5 114.5z" /> +<glyph unicode="Ž" d="M102 0v145l793 1151h-772v166h981v-145l-793 -1151h813v-166h-1022zM283 1880v27h120q82 -55 211 -187q123 129 211 187h121v-27l-59 -67q-143 -160 -176 -234h-193q-33 74 -176 234z" /> +<glyph unicode="ž" d="M182 0v125l660 836h-627v137h811v-146l-647 -815h665v-137h-862zM295 1542v27h120q82 -55 211 -187q123 129 211 187h121v-27l-59 -67q-143 -160 -176 -234h-193q-33 74 -176 234z" /> +<glyph unicode="ſ" d="M356 0v1200q0 188 90 279q88 88 299 88q121 0 273 -35l-41 -143q-109 29 -227.5 28.5t-163.5 -49.5q-47 -51 -47 -166v-1202h-183z" /> +<glyph unicode="ƒ" d="M215 -324q59 -18 115 -18q92 0 123 51q33 55 32 164v963h-194v75l194 68v137q0 190 76 278.5t256 88.5q96 0 197 -37l-47 -141q-82 29 -144 28q-92 0 -123 -51q-33 -55 -32 -164v-145h245v-137h-245v-961q0 -190 -76 -278.5t-256 -88.5q-55 0 -121 15v153z" /> +<glyph unicode="ǰ" d="M135 -303q131 -39 289 -39q119 0 182 57q66 59 66 158v1081l-420 21v123h602v-1215q0 -180 -113 -278q-111 -96 -319 -97q-160 0 -287 35v154zM275 1542v27h120q82 -55 211 -187q123 129 211 187h121v-27l-59 -67q-143 -160 -176 -234h-193q-33 74 -176 234z" /> +<glyph unicode="Ǻ" d="M33 0l483 1468h195l485 -1468h-192l-144 453h-491l-146 -453h-190zM422 618h385l-133 424q-39 121 -62 226q-20 -88 -47 -183zM389 1581q0 100 62 158q59 57 161 57q100 0 166 -59q63 -57 64 -154q0 -100 -64 -158q-66 -59 -166 -59q-102 0 -161 57q-62 58 -62 158z M500 1581q0 -113 112 -113q53 0 82 29q31 31 31 84q0 51 -30.5 82t-82.5 31q-49 0 -80 -31q-32 -33 -32 -82zM490 1835v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="ǻ" d="M135 307q0 332 510 348l203 7v69q0 236 -244 236q-150 0 -328 -82l-63 137q199 96 383 96q223 0 328 -88q102 -86 102 -278v-752h-131l-37 152h-8q-74 -94 -158 -133t-209 -39q-164 0 -256 85.5t-92 241.5zM324 305q0 -178 200 -178q150 0 234 82q88 84 88 229v99 l-162 -7q-195 -8 -278 -61q-82 -51 -82 -164zM387 1456q0 100 62 158q59 57 161 57q100 0 166 -59q63 -57 64 -154q0 -100 -64 -158q-66 -59 -166 -59q-102 0 -161 57q-62 58 -62 158zM498 1456q0 -113 112 -113q53 0 82 29q31 31 31 84q0 51 -30.5 82t-82.5 31 q-49 0 -80 -31q-32 -33 -32 -82zM512 1720v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="Ǽ" d="M0 0l338 1462h872v-164h-477v-452h438v-162h-438v-520h477v-164h-653v453h-289l-96 -453h-172zM305 618h252v680h-106zM631 1579v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="ǽ" d="M45 307q0 334 328 348l149 7v69q0 236 -139 236q-109 0 -211 -82l-57 137q133 96 276 96q187 0 246 -178q77 178 240 178q137 0 223 -135t86 -356v-113h-496q3 -190 68 -283q66 -92 168 -92q113 0 233 76v-162q-106 -74 -241 -73q-221 0 -316 229q-104 -229 -301 -229 q-117 0 -186 86q-70 83 -70 241zM215 305q0 -82 33 -131q31 -47 86 -47q84 0 135 82t51 229v99l-88 -7q-217 -18 -217 -225zM694 662h316q0 137 -41 223q-39 82 -111 82q-66 0 -113 -78q-45 -75 -51 -227zM531 1241v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z " /> +<glyph unicode="Ǿ" d="M80 2l121 197q-117 184 -117 536q0 750 534 750q186 0 310 -105l92 152l137 -78l-125 -201q115 -188 115 -520q0 -362 -137 -557q-138 -197 -394 -196q-193 0 -307 94l-92 -150zM281 733q0 -205 38 -342l515 836q-80 94 -216 94q-337 0 -337 -588zM403 229 q80 -86 213 -86q174 0 254 145.5t80 444.5q0 205 -35 328zM475 1579v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="ǿ" d="M115 551q0 264 133 415.5t368 151.5q129 0 236 -57l76 119l131 -84l-84 -131q137 -156 137 -414q0 -272 -135 -421.5t-367 -149.5q-127 0 -233 53l-76 -119l-131 84l84 131q-139 158 -139 422zM303 551q0 -178 53 -275l406 656q-66 35 -156 35q-162 0 -231 -103 q-72 -106 -72 -313zM467 164q59 -33 154 -33q162 0 233 105q70 102 70 315q0 166 -52 266zM498 1241v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="Ș" d="M141 49v178q211 -86 414 -86q350 0 350 240q0 104 -71 160q-74 57 -285 133q-211 74 -299 174q-90 102 -90 264q0 174 129 272.5t352 98.5q225 0 416 -78l-64 -164q-197 78 -360 78q-293 0 -293 -209q0 -102 66 -164q66 -63 270 -133q244 -88 327.5 -182t83.5 -240 q0 -190 -139 -301q-138 -111 -393 -110q-258 -1 -414 69zM483 -426q63 145 84 301h178v-20q0 -22 -49 -123q-51 -109 -114 -185h-99v27z" /> +<glyph unicode="ș" d="M203 49v166q195 -86 370 -86q274 0 275 162q0 55 -49 98t-226 107q-233 86 -294 159q-59 72 -60 172q0 135 111 213q113 78 309.5 78t368.5 -74l-60 -149q-184 72 -319 72q-236 0 -236 -133q0 -57 51.5 -96.5t231.5 -102.5q207 -76 280 -152q70 -72 70 -182 q0 -150 -116.5 -235.5t-331.5 -85.5q-246 -1 -375 69zM454 -426q63 145 84 301h178v-20q0 -22 -49 -123q-51 -109 -114 -185h-99v27z" /> +<glyph unicode="ʼ" d="M446 961q61 254 101 501h219l14 -22q-53 -207 -176 -479h-158z" /> +<glyph unicode="ˆ" d="M283 1241v27l59 67q141 156 176 234h193q35 -78 176 -234l59 -67v-27h-121q-78 49 -211 186q-133 -137 -211 -186h-120z" /> +<glyph unicode="ˇ" d="M283 1542v27h120q82 -55 211 -187q123 129 211 187h121v-27l-59 -67q-143 -160 -176 -234h-193q-33 74 -176 234z" /> +<glyph unicode="ˉ" d="M326 1241v150h575v-150h-575z" /> +<glyph unicode="˘" d="M313 1518h109q6 -76 47 -101q39 -25 143 -24q182 0 191 125h112q-10 -127 -90 -201q-82 -76 -215 -76q-279 1 -297 277z" /> +<glyph unicode="˙" d="M508 1394.5q0 114.5 106.5 114.5t106.5 -114.5t-106.5 -114.5t-106.5 114.5z" /> +<glyph unicode="˚" d="M387 1456q0 100 62 158q59 57 161 57q100 0 166 -59q63 -57 64 -154q0 -100 -64 -158q-66 -59 -166 -59q-102 0 -161 57q-62 58 -62 158zM498 1456q0 -113 112 -113q53 0 82 29q31 31 31 84q0 51 -30.5 82t-82.5 31q-49 0 -80 -31q-32 -33 -32 -82z" /> +<glyph unicode="˛" d="M414 -252q0 144 190 252h131q-170 -127 -170 -238q0 -96 127 -96q68 0 123 15v-113q-78 -23 -156 -23q-121 0 -182 54q-63 55 -63 149z" /> +<glyph unicode="˜" d="M254 1241q25 264 211 264q57 0 162 -55q104 -57 135 -57q82 0 106 114h105q-27 -264 -211 -264q-57 0 -158 57q-100 55 -139 56q-82 0 -107 -115h-104z" /> +<glyph unicode="˝" d="M246 1241v27q92 127 174 301h198v-21q-109 -168 -272 -307h-100zM608 1241v27q92 125 174 301h199v-21q-109 -168 -272 -307h-101z" /> +<glyph unicode="˳" d="M387 -340q0 47 16 90q16 41 46 68q31 29 69 43q37 14 92 14q49 0 90 -14q39 -14 72 -43t49 -66q18 -39 19 -90q0 -57 -17 -92q-18 -40 -49 -67.5t-74 -43.5q-41 -14 -90 -14q-55 0 -92 14q-39 14 -69 43q-29 27 -46 68q-16 43 -16 90zM498 -340q0 -51 28 -84 q27 -29 84 -29q53 0 82 29q31 31 31 84t-31 84q-29 29 -82 29q-51 0 -80 -29q-32 -33 -32 -84z" /> +<glyph unicode="΄" d="M479 1241v27l31 106q29 106 39 195h199v-21q0 -16 -50 -121q-47 -102 -110 -186h-109z" /> +<glyph unicode="΅" d="M299 1394.5q0 102.5 96.5 102.5t96.5 -102q0 -49 -27 -76t-70 -27q-96 0 -96 102.5zM532 1389v26q53 147 91 301h206v-20q-92 -163 -215 -307h-82zM735 1394.5q0 102.5 96.5 102.5t96.5 -102.5t-96.5 -102.5t-96.5 102.5z" /> +<glyph unicode="Ά" d="M33 0l483 1468h195l485 -1468h-192l-144 453h-491l-146 -453h-190zM422 618h385l-133 424q-39 121 -62 226q-20 -88 -47 -183zM37 1137v27l31 106q29 106 39 195h199v-21q0 -16 -50 -121q-47 -102 -110 -186h-109z" /> +<glyph unicode="·" d="M487 723q0 139 127 139t127 -139t-127 -139t-127 139z" /> +<glyph unicode="Έ" d="M217 0v1462h842v-164h-656v-452h617v-162h-617v-520h656v-164h-842zM-115 1137v27l31 106q29 106 39 195h199v-21q0 -16 -50 -121q-47 -102 -110 -186h-109z" /> +<glyph unicode="Ή" d="M135 0v1462h187v-616h585v616h187v-1462h-187v682h-585v-682h-187zM-156 1137v27l31 106q29 106 39 195h199v-21q0 -16 -50 -121q-47 -102 -110 -186h-109z" /> +<glyph unicode="Ί" d="M225 0v123l295 20v1176l-295 20v123h776v-123l-294 -20v-1176l294 -20v-123h-776zM-92 1137v27l31 106q29 106 39 195h199v-21q0 -16 -50 -121q-47 -102 -110 -186h-109z" /> +<glyph unicode="Ό" d="M84 735q0 750 534 750q258 0 392 -195q137 -199 137 -557q0 -362 -137 -557q-138 -197 -394 -196q-532 -1 -532 755zM281 733q0 -590 335 -590q174 0 254 145.5t80 444.5q0 305 -82 445q-84 143 -250 143q-337 0 -337 -588zM-131 1137v27l31 106q29 106 39 195h199v-21 q0 -16 -50 -121q-47 -102 -110 -186h-109z" /> +<glyph unicode="Ύ" d="M33 1462h203l376 -739l381 739h201l-487 -893v-569h-187v559zM-238 1137v27l31 106q29 106 39 195h199v-21q0 -16 -50 -121q-47 -102 -110 -186h-109z" /> +<glyph unicode="Ώ" d="M74 0v164h266q-254 233 -254 661q0 313 139 486q141 174 391 174q248 0 390 -174q141 -176 141 -486q0 -428 -256 -661h268v-164h-469v147q260 229 260 674q0 500 -335.5 500t-335.5 -500q0 -442 262 -674v-147h-467zM-133 1137v27l31 106q29 106 39 195h199v-21 q0 -16 -50 -121q-47 -102 -110 -186h-109z" /> +<glyph unicode="ΐ" d="M428 324v774h182v-774q0 -195 193 -195q117 0 213 21v-138q-98 -33 -225 -32q-363 0 -363 344zM242 1394.5q0 102.5 96.5 102.5t96.5 -102q0 -49 -27 -76t-70 -27q-96 0 -96 102.5zM475 1389v26q53 147 91 301h206v-20q-92 -163 -215 -307h-82zM678 1394.5 q0 102.5 96.5 102.5t96.5 -102.5t-96.5 -102.5t-96.5 102.5z" /> +<glyph unicode="Α" d="M33 0l483 1468h195l485 -1468h-192l-144 453h-491l-146 -453h-190zM422 618h385l-133 424q-39 121 -62 226q-20 -88 -47 -183z" /> +<glyph unicode="Β" d="M135 0v1462h440q272 0 398 -88q123 -86 123 -282q0 -129 -78 -213t-213 -103v-10q332 -55 332 -342q0 -199 -127 -311.5t-348 -112.5h-527zM322 158h307q311 0 311 274q0 254 -324 254h-294v-528zM322 842h284q157 0 228 55q72 55 71 182q0 121 -75.5 172.5t-243.5 51.5 h-264v-461z" /> +<glyph unicode="Γ" d="M233 0v1462h826v-166h-639v-1296h-187z" /> +<glyph unicode="Δ" d="M68 0v133l450 1329h187l454 -1337v-125h-1091zM268 166h686l-252 752q-51 150 -92 339q-53 -231 -88 -333z" /> +<glyph unicode="Ε" d="M217 0v1462h842v-164h-656v-452h617v-162h-617v-520h656v-164h-842z" /> +<glyph unicode="Ζ" d="M102 0v145l793 1151h-772v166h981v-145l-793 -1151h813v-166h-1022z" /> +<glyph unicode="Η" d="M135 0v1462h187v-616h585v616h187v-1462h-187v682h-585v-682h-187z" /> +<glyph unicode="Θ" d="M84 735q0 750 534 750q258 0 392 -195q137 -199 137 -557q0 -362 -137 -557q-138 -197 -394 -196q-532 -1 -532 755zM281 733q0 -590 335 -590q174 0 254 145.5t80 444.5q0 305 -82 445q-84 143 -250 143q-337 0 -337 -588zM410 664v161h411v-161h-411z" /> +<glyph unicode="Ι" d="M225 0v123l295 20v1176l-295 20v123h776v-123l-294 -20v-1176l294 -20v-123h-776z" /> +<glyph unicode="Κ" d="M211 0v1462h186v-731l121 168l453 563h209l-521 -637l539 -825h-211l-450 698l-140 -114v-584h-186z" /> +<glyph unicode="Λ" d="M33 0l487 1462h187l489 -1462h-199l-292 922q-55 172 -93 340q-47 -207 -88 -334l-295 -928h-196z" /> +<glyph unicode="Μ" d="M113 0v1462h247l248 -1192h6l250 1192h252v-1462h-153v887q0 121 14 391h-8l-283 -1278h-154l-278 1280h-8q18 -268 18 -406v-874h-151z" /> +<glyph unicode="Ν" d="M135 0v1462h213l578 -1204h6q-14 285 -14 404v800h174v-1462h-215l-580 1210h-8q18 -276 18 -417v-793h-172z" /> +<glyph unicode="Ξ" d="M111 0v164h1005v-164h-1005zM152 1298v164h923v-164h-923zM233 684v162h760v-162h-760z" /> +<glyph unicode="Ο" d="M84 735q0 750 534 750q258 0 392 -195q137 -199 137 -557q0 -362 -137 -557q-138 -197 -394 -196q-532 -1 -532 755zM281 733q0 -590 335 -590q174 0 254 145.5t80 444.5q0 305 -82 445q-84 143 -250 143q-337 0 -337 -588z" /> +<glyph unicode="Π" d="M135 0v1462h959v-1462h-187v1298h-585v-1298h-187z" /> +<glyph unicode="Ρ" d="M176 0v1462h404q514 0 514 -428q0 -219 -137.5 -342t-403.5 -123h-191v-569h-186zM362 727h170q195 0 283 72q86 70 86 225q0 279 -338 279h-201v-576z" /> +<glyph unicode="Σ" d="M131 0v152l414 614l-402 549v147h893v-164h-680l389 -530l-409 -604h760v-164h-965z" /> +<glyph unicode="Τ" d="M102 1298v164h1022v-164h-417v-1298h-187v1298h-418z" /> +<glyph unicode="Υ" d="M33 1462h203l376 -739l381 739h201l-487 -893v-569h-187v559z" /> +<glyph unicode="Φ" d="M27 758q0 238 122.5 391.5t335.5 153.5h47v180h166v-180h47q213 0 336 -154t123 -391q0 -233 -127 -396q-125 -158 -354 -157h-25v-225h-166v225h-24q-230 0 -356 160q-125 157 -125 393zM199 762q0 -184 86 -293q86 -106 241 -107h6v781h-32q-141 0 -221 -104.5 t-80 -276.5zM698 362h7q156 0 241 107q86 109 86 293q0 172 -80 276.5t-221 104.5h-33v-781z" /> +<glyph unicode="Χ" d="M53 0l453 764l-422 698h199l331 -559l334 559h191l-422 -692l457 -770h-211l-355 635l-366 -635h-189z" /> +<glyph unicode="Ψ" d="M57 979v483h172v-479q0 -229 70 -305q68 -74 217 -74h16v858h166v-858h17q150 0 217 72q70 74 69 303v483h173v-479q0 -289 -115 -412q-117 -125 -344 -125h-17v-446h-166v446h-16q-231 0 -344 125q-115 127 -115 408z" /> +<glyph unicode="Ω" d="M74 0v164h266q-254 233 -254 661q0 313 139 486q141 174 391 174q248 0 390 -174q141 -176 141 -486q0 -428 -256 -661h268v-164h-469v147q260 229 260 674q0 500 -335.5 500t-335.5 -500q0 -442 262 -674v-147h-467z" /> +<glyph unicode="Ϊ" d="M225 0v123l295 20v1176l-295 20v123h776v-123l-294 -20v-1176l294 -20v-123h-776zM332 1732.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5zM707 1732.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5z" /> +<glyph unicode="Ϋ" d="M33 1462h203l376 -739l381 739h201l-487 -893v-569h-187v559zM332 1732.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5zM707 1732.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5z" /> +<glyph unicode="ά" d="M121 547q0 270 115 422q113 150 321 149q211 0 305 -168h12q29 100 68 148h143q-51 -162 -51 -439v-419q0 -111 84 -111q25 0 60 10v-133q-57 -27 -115 -26q-158 0 -199 167h-12q-109 -168 -313 -167q-193 0 -305.5 149.5t-112.5 417.5zM309 545q0 -414 264 -414 q147 0 211 92q66 92 68 309v15q0 227 -67.5 323.5t-213.5 96.5q-262 0 -262 -422zM495 1241v27l31 106q29 106 39 195h199v-21q0 -16 -50 -121q-47 -102 -110 -186h-109z" /> +<glyph unicode="έ" d="M201 299q0 195 237 268v11q-203 66 -202 249q0 135 112.5 213t300.5 78q219 0 383 -76l-63 -147q-162 72 -316 72q-244 0 -243 -154q0 -166 276 -166h154v-153h-160q-301 0 -301 -185q0 -180 283 -180q199 0 366 86v-160q-139 -76 -377 -75q-211 0 -332 83 q-118 82 -118 236zM575 1241v27l31 106q29 106 39 195h199v-21q0 -16 -50 -121q-47 -102 -110 -186h-109z" /> +<glyph unicode="ή" d="M160 0v1098h147l27 -148h10q100 168 336 168q389 0 389 -401v-1209h-182v1199q0 260 -238 260q-307 0 -307 -398v-569h-182zM559 1241v27l31 106q29 106 39 195h199v-21q0 -16 -50 -121q-47 -102 -110 -186h-109z" /> +<glyph unicode="ί" d="M428 324v774h182v-774q0 -195 193 -195q117 0 213 21v-138q-98 -33 -225 -32q-363 0 -363 344zM471 1241v27l31 106q29 106 39 195h199v-21q0 -16 -50 -121q-47 -102 -110 -186h-109z" /> +<glyph unicode="ΰ" d="M160 498v600h182v-590q0 -379 274 -379q150 0 217.5 116.5t67.5 362.5q0 242 -63 490h182q63 -242 63 -498q0 -317 -114.5 -468.5t-358.5 -151.5q-450 0 -450 518zM297 1394.5q0 102.5 96.5 102.5t96.5 -102q0 -49 -27 -76t-70 -27q-96 0 -96 102.5zM530 1389v26 q53 147 91 301h206v-20q-92 -163 -215 -307h-82zM733 1394.5q0 102.5 96.5 102.5t96.5 -102.5t-96.5 -102.5t-96.5 102.5z" /> +<glyph unicode="α" d="M121 547q0 270 115 422q113 150 321 149q211 0 305 -168h12q29 100 68 148h143q-51 -162 -51 -439v-419q0 -111 84 -111q25 0 60 10v-133q-57 -27 -115 -26q-158 0 -199 167h-12q-109 -168 -313 -167q-193 0 -305.5 149.5t-112.5 417.5zM309 545q0 -414 264 -414 q147 0 211 92q66 92 68 309v15q0 227 -67.5 323.5t-213.5 96.5q-262 0 -262 -422z" /> +<glyph unicode="β" d="M158 -492v1588q0 227 118 348q121 123 328 123q199 0 316 -105q119 -106 118 -288q0 -135 -80 -228q-76 -88 -235 -117v-6q381 -49 381 -409q0 -206 -123 -320q-125 -115 -338 -114q-188 0 -303 63v-535h-182zM340 209q135 -80 289 -80q293 0 293 299q0 147 -94.5 229 t-254.5 82h-94v152h70q141 0 223 76q80 74 80 200q0 119 -68 185q-70 66 -180 65q-264 0 -264 -330v-878z" /> +<glyph unicode="γ" d="M82 1098h192l242 -592q70 -174 107 -320h6q18 82 98 295l229 617h189l-406 -1035q-111 -281 -110 -555h-191q0 209 101 504z" /> +<glyph unicode="δ" d="M135 438q0 371 422 500q-254 143 -254 348q0 135 90 207q92 74 252 74q199 0 393 -109l-73 -145q-168 106 -328 106q-74 0 -117 -36.5t-43 -100.5q0 -66 41 -115q39 -49 215 -151q360 -205 361 -518q0 -246 -129 -381q-131 -137 -355 -137q-209 0 -344 124 q-131 123 -131 334zM328 434q0 -137 75 -219q78 -84 207 -84q295 0 295 350q0 244 -221 373q-356 -101 -356 -420z" /> +<glyph unicode="ε" d="M201 299q0 195 237 268v11q-203 66 -202 249q0 135 112.5 213t300.5 78q219 0 383 -76l-63 -147q-162 72 -316 72q-244 0 -243 -154q0 -166 276 -166h154v-153h-160q-301 0 -301 -185q0 -180 283 -180q199 0 366 86v-160q-139 -76 -377 -75q-211 0 -332 83 q-118 82 -118 236z" /> +<glyph unicode="ζ" d="M162 469q0 254 162 477q160 223 536 469q-215 -12 -352 -12h-285v153h838v-141q-396 -289 -553 -485q-158 -199 -158 -453q0 -154 82 -217q83 -65 313 -104q168 -31 246 -80q76 -49 76 -162q0 -147 -121 -315h-170q121 164 121 260q0 57 -51 90q-52 34 -246 61 q-227 33 -334 146q-104 110 -104 313z" /> +<glyph unicode="η" d="M160 0v1098h147l27 -148h10q100 168 336 168q389 0 389 -401v-1209h-182v1199q0 260 -238 260q-307 0 -307 -398v-569h-182z" /> +<glyph unicode="θ" d="M147 729q0 381 113 563.5t352 182.5q230 0 351 -191q119 -188 118 -555q0 -750 -469 -749q-229 0 -348 192q-117 190 -117 557zM332 670q6 -276 71.5 -407.5t209 -131.5t208.5 127q68 129 74 412h-563zM332 807h563q-6 258 -73.5 387t-209.5 129q-139 0 -206 -125 q-64 -121 -74 -391z" /> +<glyph unicode="ι" d="M428 324v774h182v-774q0 -195 193 -195q117 0 213 21v-138q-98 -33 -225 -32q-363 0 -363 344z" /> +<glyph unicode="κ" d="M215 0v1098h180v-291l-16 -299h4l137 166l402 424h213l-494 -510l522 -588h-213l-426 469l-129 -82v-387h-180z" /> +<glyph unicode="λ" d="M61 0l504 1083l-53 160q-33 100 -78 139q-43 37 -115 37q-55 0 -104 -12v145q57 16 127 17q141 0 213 -67.5t141 -278.5l326 -983q37 -111 102 -111q25 0 60 10v-133q-53 -27 -115 -26q-80 0 -125 40q-43 41 -72 127l-135 418q-74 238 -94 336h-6q-37 -141 -119 -321 l-268 -580h-189z" /> +<glyph unicode="μ" d="M160 -492v1590h182v-707q0 -260 238 -260q160 0 233.5 92t73.5 305v570h182v-1098h-147l-27 147h-10q-100 -168 -301 -167q-174 0 -248 88q6 -154 6 -240v-320h-182z" /> +<glyph unicode="ν" d="M61 1098h189l223 -586q106 -276 121 -358h6q156 162 235.5 368.5t79.5 575.5h183q0 -367 -100.5 -610.5t-325.5 -487.5h-187z" /> +<glyph unicode="ξ" d="M172 422q0 137 76 240q78 104 207 147v12q-215 59 -215 279q0 113 75 188q74 74 260 127q-174 -12 -280 -12h-62v153h764v-153h-69q-217 0 -364.5 -80t-147.5 -219q0 -114 92 -166q90 -51 287 -51h190v-137h-201q-209 0 -315 -82q-109 -84 -109 -230q0 -90 37 -143 q39 -55 109 -82q76 -31 248 -59q158 -27 237 -78q76 -49 76 -162q0 -147 -121 -315h-170q121 164 121 262q0 52 -49 86q-49 33 -238 61q-438 68 -438 414z" /> +<glyph unicode="ο" d="M115 551q0 264 135 415.5t366 151.5q217 0 356.5 -155.5t139.5 -411.5q0 -266 -137 -418q-139 -154 -365 -153q-219 0 -356 155q-139 158 -139 416zM303 551q0 -420 311 -420q309 0 310 420q0 416 -312 416q-309 0 -309 -416z" /> +<glyph unicode="π" d="M14 944v74l138 80h1062v-154h-210v-690q0 -125 90 -125q49 0 90 21v-144q-57 -27 -137 -26q-225 0 -226 262v702h-403v-944h-182v944h-222z" /> +<glyph unicode="ρ" d="M158 -492v1043q0 264 127 415.5t344 151.5q207 0 329 -153q125 -156 125 -414q0 -268 -122.5 -419.5t-339.5 -151.5q-166 0 -281 90h-6q6 -176 6 -271v-291h-182zM340 231q109 -100 283 -100q144 0 208 105q63 102 64 315q0 207 -66 313q-63 102 -208 103 q-281 0 -281 -396v-340z" /> +<glyph unicode="ς" d="M172 516q0 602 567 602q162 0 322 -59l-62 -158q-154 57 -272 57q-367 0 -367 -440q0 -186 78 -254q78 -70 269 -102q358 -63 358 -252q0 -143 -125 -311h-166q121 164 121 262q0 59 -66 90q-59 29 -229 57q-428 72 -428 508z" /> +<glyph unicode="σ" d="M129 524q0 573 555 574h490v-154h-267q178 -195 178 -440q0 -233 -129 -378.5t-352 -145.5t-348 143q-127 145 -127 401zM317 524q0 -186 76 -288q78 -104 219 -105q137 0 211 96.5t74 268.5q0 270 -154 448h-59q-200 0 -283 -98q-84 -101 -84 -322z" /> +<glyph unicode="τ" d="M106 944v74l134 80h841v-154h-475v-606q0 -215 209 -215q111 0 191 20v-135q-86 -35 -220 -35q-362 0 -362 344v627h-318z" /> +<glyph unicode="υ" d="M160 498v600h182v-590q0 -379 274 -379q150 0 217.5 116.5t67.5 362.5q0 242 -63 490h182q63 -242 63 -498q0 -317 -114.5 -468.5t-358.5 -151.5q-450 0 -450 518z" /> +<glyph unicode="φ" d="M74 547q0 311 207 559l120 -90q-98 -129 -131 -232q-35 -109 -34 -237q0 -383 288 -416v606q0 381 281 381q166 0 258 -147.5t92 -399.5q0 -264 -125 -419.5t-348 -169.5v-474h-158v474q-450 24 -450 565zM682 131q305 29 305 438q0 397 -176 398q-129 0 -129 -228v-608z " /> +<glyph unicode="χ" d="M43 -492l475 830l-180 446q-68 168 -152 168q-27 0 -63 -10v143q47 16 129 17q80 0 135 -55.5t111 -192.5l139 -348l311 592h178l-417 -776l186 -461q47 -115 86 -158q41 -45 104 -45q47 0 80 8v-139q-63 -18 -118 -19q-104 0 -170 46q-68 47 -138 221l-147 377 l-354 -644h-195z" /> +<glyph unicode="ψ" d="M80 555v543h172v-529q0 -424 272 -438v1423h158v-1421q301 28 301 436q0 254 -63 529h172q63 -272 63 -523q0 -569 -473 -593v-474h-158v474q-233 10 -338 143q-106 137 -106 430z" /> +<glyph unicode="ω" d="M90 532q0 301 105 566h165q-104 -261 -104 -566q0 -401 139 -401q68 0 105 72q37 71 37 196v306h157v-306q0 -133 39 -198q41 -70 103 -70q139 0 139 401q0 303 -105 566h166q104 -258 105 -566q0 -553 -289 -552q-182 0 -231 178h-11q-49 -178 -231 -178 q-289 -1 -289 552z" /> +<glyph unicode="ϊ" d="M428 324v774h182v-774q0 -195 193 -195q117 0 213 21v-138q-98 -33 -225 -32q-363 0 -363 344zM258 1394.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5zM633 1394.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5z" /> +<glyph unicode="ϋ" d="M160 498v600h182v-590q0 -379 274 -379q150 0 217.5 116.5t67.5 362.5q0 242 -63 490h182q63 -242 63 -498q0 -317 -114.5 -468.5t-358.5 -151.5q-450 0 -450 518zM330 1394.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5zM705 1394.5q0 102.5 96 102.5t96 -102.5 t-96 -102.5t-96 102.5z" /> +<glyph unicode="ό" d="M115 551q0 264 135 415.5t366 151.5q217 0 356.5 -155.5t139.5 -411.5q0 -266 -137 -418q-139 -154 -365 -153q-219 0 -356 155q-139 158 -139 416zM303 551q0 -420 311 -420q309 0 310 420q0 416 -312 416q-309 0 -309 -416zM551 1241v27l31 106q29 106 39 195h199v-21 q0 -16 -50 -121q-47 -102 -110 -186h-109z" /> +<glyph unicode="ύ" d="M160 498v600h182v-590q0 -379 274 -379q150 0 217.5 116.5t67.5 362.5q0 242 -63 490h182q63 -242 63 -498q0 -317 -114.5 -468.5t-358.5 -151.5q-450 0 -450 518zM518 1241v27l31 106q29 106 39 195h199v-21q0 -16 -50 -121q-47 -102 -110 -186h-109z" /> +<glyph unicode="ώ" d="M90 532q0 301 105 566h165q-104 -261 -104 -566q0 -401 139 -401q68 0 105 72q37 71 37 196v306h157v-306q0 -133 39 -198q41 -70 103 -70q139 0 139 401q0 303 -105 566h166q104 -258 105 -566q0 -553 -289 -552q-182 0 -231 178h-11q-49 -178 -231 -178 q-289 -1 -289 552zM530 1241v27l31 106q29 106 39 195h199v-21q0 -16 -50 -121q-47 -102 -110 -186h-109z" /> +<glyph unicode="ϑ" d="M10 825q29 14 86.5 28.5t104.5 14.5q53 0 84 -14q39 -18 57 -39q20 -25 31 -59q10 -37 10 -74q0 -49 -4 -78q-2 -25 -6 -48.5t-6 -41.5q-2 -16 -5.5 -47t-5.5 -47q-4 -33 -4 -94q0 -37 10 -78q8 -37 35 -66q23 -25 64 -41q43 -16 96 -16q163 0 246 154q82 152 82 456v53 q0 23 -4 52q-178 4 -295 39q-125 39 -197 98q-76 63 -108.5 137t-32.5 158q0 61 20 123q18 59 60 98q43 43 104 63q68 23 152 23q104 0 184 -43q82 -45 139 -119q59 -78 97 -190q37 -111 55 -250h143v-137h-137q2 -14 2 -52q0 -8 1 -28.5t1 -30.5q0 -150 -33 -297 q-29 -133 -96 -237q-63 -98 -162 -158q-96 -57 -229 -57q-109 0 -184 28q-70 27 -115 74q-41 45 -62 111q-16 55 -16 131q0 55 4 86q2 10 4 29.5t4 34.5t4 28l11 82q4 25 4 61q0 47 -15 62q-16 16 -47 16q-14 0 -51 -8q-25 -6 -43 -17zM426 1272q0 -53 23 -109 q23 -53 76 -94t139 -65q94 -27 210 -27q-10 90 -43 192q-29 90 -65 144q-41 59 -84 88q-47 31 -94 31q-76 0 -119 -43t-43 -117z" /> +<glyph unicode="ϒ" d="M31 1462h202l379 -731q29 86 62 160q18 41 78 164q39 80 75 147l32.5 63.5t27.5 49.5q16 31 45 71q22 31 49 50q29 20 59 28q35 10 74 11q27 0 55 -5q20 -2 39 -12v-145q-8 2 -28 6q-10 2 -29 2q-31 0 -59 -23q-35 -29 -66 -84q-6 -8 -9 -15t-10.5 -19.5t-15.5 -28.5 l-21.5 -37t-31.5 -59l-61 -119q-39 -72 -66 -133q-41 -90 -59 -135q-35 -82 -47 -127v-541h-187v559z" /> +<glyph unicode="ϖ" d="M18 944v74l134 80h1052v-154h-155q12 -33 41 -123q18 -61 30 -112q8 -39 17 -113q4 -39 4 -125q0 -121 -14.5 -205t-51.5 -153q-33 -63 -90 -98t-133 -35q-94 0 -150 43q-57 45 -81 135h-11q-25 -90 -82 -135q-55 -43 -149 -43q-76 0 -133.5 34.5t-89.5 98.5 q-34 65 -50 153q-16 94 -16 205q0 86 4 125q8 74 17 113q16 74 28 112q18 58 43 123h-164zM256 471q0 -98 8 -156q10 -74 25 -104q20 -43 44.5 -61.5t61.5 -18.5t64 21q23 18 43 57q18 35 26 84q8 43 9 106v203h157v-203q0 -133 39 -198q41 -70 103 -70q37 0 61.5 18.5 t44.5 61.5q14 31 25 104q8 57 8 156q0 80 -4 119q-8 74 -17 112q-12 59 -30 115l-41 127h-535q-23 -61 -43 -127q-10 -35 -29 -115q-8 -39 -16 -112q-4 -39 -4 -119z" /> +<glyph unicode="Ё" d="M217 0v1462h842v-164h-656v-452h617v-162h-617v-520h656v-164h-842zM355 1732.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5zM730 1732.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5z" /> +<glyph unicode="Ђ" d="M23 1298v164h841v-164h-356v-401h264q184 0 291 -96q102 -94 102 -275v-137q0 -197 -94 -303t-266 -106q-89 0 -141 24v160q63 -20 135 -21q180 0 180 226v133q0 231 -227 231h-244v-733h-186v1298h-299z" /> +<glyph unicode="Ѓ" d="M233 0v1462h826v-166h-639v-1296h-187zM508 1579v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="Є" d="M129 733q0 344 178 547t490 203q219 0 383 -86l-78 -156q-156 78 -305 78q-188 0 -314 -125q-123 -125 -151 -348h629v-162h-635q23 -541 471 -541q135 0 327 58v-162q-150 -59 -358 -59q-310 0 -473 196q-164 199 -164 557z" /> +<glyph unicode="Ѕ" d="M141 49v178q211 -86 414 -86q350 0 350 240q0 104 -71 160q-74 57 -285 133q-211 74 -299 174q-90 102 -90 264q0 174 129 272.5t352 98.5q225 0 416 -78l-64 -164q-197 78 -360 78q-293 0 -293 -209q0 -102 66 -164q66 -63 270 -133q244 -88 327.5 -182t83.5 -240 q0 -190 -139 -301q-138 -111 -393 -110q-258 -1 -414 69z" /> +<glyph unicode="І" d="M225 0v123l295 20v1176l-295 20v123h776v-123l-294 -20v-1176l294 -20v-123h-776z" /> +<glyph unicode="Ї" d="M225 0v123l295 20v1176l-295 20v123h776v-123l-294 -20v-1176l294 -20v-123h-776zM332 1732.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5zM707 1732.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5z" /> +<glyph unicode="Ј" d="M137 39v166q162 -61 309 -62q162 0 254.5 80t92.5 226v1013h186v-1011q0 -215 -141 -345q-139 -127 -377 -126q-215 0 -324 59z" /> +<glyph unicode="Љ" d="M0 -2v145q29 -14 68 -14q33 0 61 35q27 33 39 127q10 80 33 524q23 434 24 647h498v-616h39q446 0 446 -418q0 -207 -114.5 -317.5t-329.5 -110.5h-207v1298h-182l-21 -548q-23 -580 -82 -672q-63 -98 -176 -98q-55 0 -96 18zM723 160h45q268 0 268 268q0 131 -69 195 q-66 59 -224 59h-20v-522z" /> +<glyph unicode="Њ" d="M66 0v1462h165v-616h318v616h166v-616h39q221 0 333.5 -106.5t112.5 -311.5q0 -207 -114.5 -317.5t-329.5 -110.5h-207v682h-318v-682h-165zM715 160h45q268 0 268 268q0 131 -70 195q-66 59 -223 59h-20v-522z" /> +<glyph unicode="Ћ" d="M23 1298v164h841v-164h-356v-401h264q184 0 291 -96q102 -94 102 -275v-526h-186v502q0 231 -227 231h-244v-733h-186v1298h-299z" /> +<glyph unicode="Ќ" d="M211 0v1462h186v-708l566 708h206l-565 -702l594 -760h-219l-582 741v-741h-186zM574 1579v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="Ў" d="M27 1462h204l383 -809q2 -4 21 -47l7 -21.5t9 -23.5h2l39 109l312 792h196l-428 -1030q-80 -190 -135 -268q-61 -86 -150 -135t-215 -49q-104 0 -178 30v185q84 -45 178 -45q111 0 170 51q59 49 115 174zM237 1897h170q10 -104 50 -144q43 -43 161 -43q193 0 211 187h174 q-20 -317 -389 -318q-195 0 -281 74t-96 244z" /> +<glyph unicode="Џ" d="M135 0v1462h187v-1296h585v1296h187v-1462h-394v-385h-176v385h-389z" /> +<glyph unicode="А" d="M33 0l483 1468h195l485 -1468h-192l-144 453h-491l-146 -453h-190zM422 618h385l-133 424q-39 121 -62 226q-20 -88 -47 -183z" /> +<glyph unicode="Б" d="M135 0v1462h912v-164h-725v-452h272q541 0 541 -418q0 -207 -131.5 -317.5t-382.5 -110.5h-486zM322 160h282q338 0 338 268q0 131 -86 192.5t-283 61.5h-251v-522z" /> +<glyph unicode="В" d="M135 0v1462h440q272 0 398 -88q123 -86 123 -282q0 -129 -78 -213t-213 -103v-10q332 -55 332 -342q0 -199 -127 -311.5t-348 -112.5h-527zM322 158h307q311 0 311 274q0 254 -324 254h-294v-528zM322 842h284q157 0 228 55q72 55 71 182q0 121 -75.5 172.5t-243.5 51.5 h-264v-461z" /> +<glyph unicode="Г" d="M233 0v1462h826v-166h-639v-1296h-187z" /> +<glyph unicode="Д" d="M6 166h113q133 246 217 594q86 360 98 702h592v-1296h195v-551h-177v385h-862v-385h-176v551zM311 166h529v1130h-242q-18 -281 -98 -596q-85 -339 -189 -534z" /> +<glyph unicode="Е" d="M217 0v1462h842v-164h-656v-452h617v-162h-617v-520h656v-164h-842z" /> +<glyph unicode="Ж" d="M0 0l373 754l-361 708h178l349 -708v708h155v-708l342 708h178l-356 -708l371 -754h-185l-350 741v-741h-155v741l-357 -741h-182z" /> +<glyph unicode="З" d="M139 59v170q186 -96 383 -96q354 0 355 283q0 264 -394 264h-190v151h176q178 0 281 74q100 74 100 199q0 102 -70 164q-72 61 -188 61q-184 0 -354 -121l-93 125q193 150 455 150q209 0 326 -99q119 -100 118 -264q0 -135 -90 -229t-252 -121v-6q176 -20 273 -115 q96 -92 96 -233q0 -203 -141.5 -319.5t-403.5 -116.5q-243 -1 -387 79z" /> +<glyph unicode="И" d="M137 0v1462h174v-800q0 -106 -8 -254l-6 -150h6l578 1204h213v-1462h-172v793q0 141 18 417h-8l-580 -1210h-215z" /> +<glyph unicode="Й" d="M137 0v1462h174v-800q0 -106 -8 -254l-6 -150h6l578 1204h213v-1462h-172v793q0 141 18 417h-8l-580 -1210h-215zM231 1897h170q10 -104 50 -144q43 -43 161 -43q193 0 211 187h174q-20 -317 -389 -318q-195 0 -281 74t-96 244z" /> +<glyph unicode="К" d="M211 0v1462h186v-708l566 708h206l-565 -702l594 -760h-219l-582 741v-741h-186z" /> +<glyph unicode="Л" d="M4 2v150q57 -25 113 -25q102 0 157 180q53 176 181 1155h639v-1462h-187v1298h-297q-117 -979 -204 -1148q-89 -172 -265 -173q-71 0 -137 25z" /> +<glyph unicode="М" d="M113 0v1462h247l248 -1192h6l250 1192h252v-1462h-153v887q0 121 14 391h-8l-283 -1278h-154l-278 1280h-8q18 -268 18 -406v-874h-151z" /> +<glyph unicode="Н" d="M135 0v1462h187v-616h585v616h187v-1462h-187v682h-585v-682h-187z" /> +<glyph unicode="О" d="M84 735q0 750 534 750q258 0 392 -195q137 -199 137 -557q0 -362 -137 -557q-138 -197 -394 -196q-532 -1 -532 755zM281 733q0 -590 335 -590q174 0 254 145.5t80 444.5q0 305 -82 445q-84 143 -250 143q-337 0 -337 -588z" /> +<glyph unicode="П" d="M135 0v1462h959v-1462h-187v1298h-585v-1298h-187z" /> +<glyph unicode="Р" d="M176 0v1462h404q514 0 514 -428q0 -219 -137.5 -342t-403.5 -123h-191v-569h-186zM362 727h170q195 0 283 72q86 70 86 225q0 279 -338 279h-201v-576z" /> +<glyph unicode="С" d="M129 733q0 344 178 547t490 203q219 0 383 -86l-78 -156q-156 78 -305 78q-215 0 -342 -158q-129 -160 -129 -430q0 -287 120 -438q119 -150 351 -150q135 0 327 58v-162q-150 -59 -358 -59q-310 0 -473 196q-164 199 -164 557z" /> +<glyph unicode="Т" d="M102 1298v164h1022v-164h-417v-1298h-187v1298h-418z" /> +<glyph unicode="У" d="M27 1462h204l383 -809q2 -4 21 -47l7 -21.5t9 -23.5h2l39 109l312 792h196l-428 -1030q-80 -190 -135 -268q-61 -86 -150 -135t-215 -49q-104 0 -178 30v185q84 -45 178 -45q111 0 170 51q59 49 115 174z" /> +<glyph unicode="Ф" d="M27 758q0 238 122.5 391.5t335.5 153.5h47v180h166v-180h47q213 0 336 -154t123 -391q0 -233 -127 -396q-125 -158 -354 -157h-25v-225h-166v225h-24q-230 0 -356 160q-125 157 -125 393zM199 762q0 -184 86 -293q86 -106 241 -107h6v781h-32q-141 0 -221 -104.5 t-80 -276.5zM698 362h7q156 0 241 107q86 109 86 293q0 172 -80 276.5t-221 104.5h-33v-781z" /> +<glyph unicode="Х" d="M53 0l453 764l-422 698h199l331 -559l334 559h191l-422 -692l457 -770h-211l-355 635l-366 -635h-189z" /> +<glyph unicode="Ц" d="M135 0v1462h187v-1296h526v1296h186v-1296h176v-551h-176v385h-899z" /> +<glyph unicode="Ч" d="M135 879v583h187v-559q0 -131 51 -180q55 -51 162 -51q154 0 372 80v710h187v-1462h-187v598q-229 -90 -393 -90q-379 0 -379 371z" /> +<glyph unicode="Ш" d="M121 0v1462h166v-1296h245v1296h166v-1296h246v1296h166v-1462h-989z" /> +<glyph unicode="Щ" d="M61 0v1462h166v-1296h246v1296h166v-1296h246v1296h166v-1296h178v-551h-176v385h-992z" /> +<glyph unicode="Ъ" d="M27 1298v164h485v-616h68q270 0 415.5 -108.5t145.5 -309.5q0 -203 -137.5 -315.5t-397.5 -112.5h-280v1298h-299zM512 160h98q338 0 338 268q0 131 -86 192.5t-282 61.5h-68v-522z" /> +<glyph unicode="Ы" d="M96 0v1462h166v-616h47q541 0 541 -418q0 -207 -131 -317q-133 -111 -383 -111h-240zM262 160h78q338 0 338 268q0 131 -86 192.5t-283 61.5h-47v-522zM967 0v1462h166v-1462h-166z" /> +<glyph unicode="Ь" d="M176 0v1462h186v-616h191q541 0 541 -418q0 -207 -131.5 -317.5t-382.5 -110.5h-404zM362 160h201q338 0 338 268q0 131 -86 192.5t-283 61.5h-170v-522z" /> +<glyph unicode="Э" d="M68 1397q160 86 383 86q305 0 477 -194.5t172 -530.5q0 -375 -170 -576q-172 -203 -488 -202q-209 0 -358 59v162q193 -57 328 -58q473 0 489 541h-629v162h625q-18 223 -137 348t-309 125q-150 0 -306 -78z" /> +<glyph unicode="Ю" d="M98 0v1462h166v-616h144q18 346 106 493q86 145 270 146q190 0 283 -176q92 -174 92 -576q0 -397 -92 -575t-285 -178q-190 0 -280 157q-88 156 -99 545h-139v-682h-166zM580 733q0 -346 47 -469q47 -121 155 -121t154 117q47 119 47 473q0 365 -47 473q-49 115 -154 115 q-111 0 -155 -119q-47 -123 -47 -469z" /> +<glyph unicode="Я" d="M57 0l381 655q-272 98 -272 408q0 399 504 399h372v-1462h-186v604h-246l-334 -604h-219zM358 1047q0 -137 82 -211q84 -74 236 -74h180v541h-188q-310 0 -310 -256z" /> +<glyph unicode="а" d="M135 307q0 332 510 348l203 7v69q0 236 -244 236q-150 0 -328 -82l-63 137q199 96 383 96q223 0 328 -88q102 -86 102 -278v-752h-131l-37 152h-8q-74 -94 -158 -133t-209 -39q-164 0 -256 85.5t-92 241.5zM324 305q0 -178 200 -178q150 0 234 82q88 84 88 229v99 l-162 -7q-195 -8 -278 -61q-82 -51 -82 -164z" /> +<glyph unicode="б" d="M143 659q0 212 31 357q31 143 82 231q49 84 123 142q72 55 170 81q90 25 491 101l33 -160q-408 -70 -500 -100q-102 -35 -165 -140q-61 -104 -72 -319h12q117 170 336 170q186 0 292.5 -131t106.5 -361q0 -262 -125 -405q-127 -145 -346 -145t-344 180t-125 499zM332 655 q0 -524 293 -524q266 0 266 375q0 365 -240 364q-96 0 -184 -63q-90 -66 -135 -152z" /> +<glyph unicode="в" d="M176 0v1098h510q387 0 387 -277q0 -197 -229 -239v-7q264 -34 264 -258q0 -152 -107 -233q-109 -84 -313 -84h-512zM358 150h318q254 0 254 180q0 92 -66 129q-61 35 -192 35h-314v-344zM358 647h293q135 0 191 35q57 37 57 121q0 141 -217 141h-324v-297z" /> +<glyph unicode="г" d="M291 0v1098h747v-154h-565v-944h-182z" /> +<glyph unicode="д" d="M33 154h86q264 393 266 944h592v-944h151v-535h-174v381h-747v-381h-174v535zM311 154h484v804h-250q-37 -481 -234 -804z" /> +<glyph unicode="е" d="M133 541q0 266 135 421.5t363 155.5q212 0 338 -135q127 -137 127 -356v-113h-774q9 -375 344 -375q199 0 370 76v-160q-170 -76 -364 -75q-244 0 -391 149q-148 151 -148 412zM326 662h573q0 305 -272 305q-276 0 -301 -305z" /> +<glyph unicode="ж" d="M0 1098h184l351 -533v533h159v-533l350 533h185l-367 -533l365 -565h-195l-338 557v-557h-159v557l-338 -557h-195l367 565z" /> +<glyph unicode="з" d="M201 1042q158 76 370 76q205 0 315.5 -73.5t110.5 -215.5q0 -184 -192 -243v-11q227 -63 227 -272q0 -152 -121 -237q-120 -86 -344 -86q-233 0 -362 69v166q180 -86 366 -86q283 0 283 180q0 184 -311 185h-150v153h113q172 0 244 39q74 39 73 127q0 154 -250 154 q-131 0 -309 -72z" /> +<glyph unicode="и" d="M160 0v1098h172v-668l-13 -252l517 920h233v-1098h-172v647l10 271l-514 -918h-233z" /> +<glyph unicode="й" d="M160 0v1098h172v-668l-13 -252l517 920h233v-1098h-172v647l10 271l-514 -918h-233zM268 1559h170q10 -104 50 -144q43 -43 161 -43q193 0 211 187h174q-20 -317 -389 -318q-195 0 -281 74t-96 244z" /> +<glyph unicode="к" d="M215 0v1098h180v-533l518 533h222l-527 -527l566 -571h-232l-547 557v-557h-180z" /> +<glyph unicode="л" d="M31 -2v131q22 -6 49 -6q111 0 178 237q68 237 104 738h678v-1098h-182v944h-340q-37 -487 -127 -725q-88 -233 -268 -233q-61 0 -92 12z" /> +<glyph unicode="м" d="M115 0v1098h211l215 -656q23 -72 73 -268q2 4 54 191q10 47 20 73l219 660h207v-1098h-151v897q-8 -23 -33 -127q-18 -74 -35 -123l-213 -647h-135l-213 647q-2 6 -10.5 35t-24.5 91.5l-33 123.5v-897h-151z" /> +<glyph unicode="н" d="M160 0v1098h182v-455h545v455h182v-1098h-182v489h-545v-489h-182z" /> +<glyph unicode="о" d="M115 551q0 264 135 415.5t366 151.5q217 0 356.5 -155.5t139.5 -411.5q0 -266 -137 -418q-139 -154 -365 -153q-219 0 -356 155q-139 158 -139 416zM303 551q0 -420 311 -420q309 0 310 420q0 416 -312 416q-309 0 -309 -416z" /> +<glyph unicode="п" d="M160 0v1098h909v-1098h-182v944h-545v-944h-182z" /> +<glyph unicode="р" d="M158 -492v1590h147l27 -148h8q111 168 322 168q203 0 315 -149q115 -152 115 -418q0 -268 -115 -419.5t-315 -151.5q-207 0 -322 159h-12q12 -129 12 -162v-469h-182zM340 551q0 -229 70 -324q72 -96 221 -96q272 0 272 422q0 414 -274 414q-152 0 -219.5 -88t-69.5 -287 v-41z" /> +<glyph unicode="с" d="M172 543q0 279 145 428q145 147 408 147q176 0 336 -59l-62 -158q-147 57 -268 57q-371 0 -371 -413q0 -406 361 -406q160 0 321 62v-160q-135 -61 -329 -61q-258 0 -400 145q-141 145 -141 418z" /> +<glyph unicode="т" d="M121 944v154h985v-154h-401v-944h-183v944h-401z" /> +<glyph unicode="у" d="M82 1098h188l262 -654q82 -203 89 -290h6q20 106 90 292l239 652h189l-475 -1241q-70 -178 -156 -263q-89 -86 -246 -86q-94 0 -168 17v145q61 -12 136 -12q96 0 149 41t96 141l58 150z" /> +<glyph unicode="ф" d="M74 551q0 524 463 563v442h157v-442q459 -49 459 -563q0 -524 -459 -567v-476h-157v476q-463 45 -463 567zM242 551q0 -385 295 -416v828q-295 -28 -295 -412zM694 135q291 33 291 416q0 377 -291 410v-826z" /> +<glyph unicode="х" d="M96 0l414 563l-393 535h207l290 -410l291 410h207l-395 -535l413 -563h-206l-310 436l-311 -436h-207z" /> +<glyph unicode="ц" d="M152 0v1098h182v-944h504v944h182v-944h160v-535h-183v381h-845z" /> +<glyph unicode="ч" d="M152 676v422h182v-410q0 -174 205 -174q170 0 340 115v469h182v-1098h-182v489q-182 -127 -381 -127q-160 0 -252 87q-94 88 -94 227z" /> +<glyph unicode="ш" d="M125 0v1098h162v-944h248v944h161v-944h248v944h162v-1098h-981z" /> +<glyph unicode="щ" d="M109 0v1098h161v-944h248v944h162v-944h248v944h162v-944h118v-535h-161v381h-938z" /> +<glyph unicode="ъ" d="M43 944v154h543v-451h153q416 0 416 -311q0 -336 -420 -336h-332v944h-360zM586 150h137q254 0 254 172q0 90 -59.5 131t-198.5 41h-133v-344z" /> +<glyph unicode="ы" d="M113 0v1098h161v-451h134q416 0 415 -311q0 -336 -420 -336h-290zM274 150h138q254 0 254 172q0 90 -59.5 131t-198.5 41h-134v-344zM952 0v1098h162v-1098h-162z" /> +<glyph unicode="ь" d="M217 0v1098h182v-451h277q416 0 416 -311q0 -336 -420 -336h-455zM399 150h260q254 0 254 172q0 90 -59 131t-199 41h-256v-344z" /> +<glyph unicode="э" d="M184 1059q156 59 332 59q268 0 406 -145q139 -150 139 -430q0 -270 -143 -416q-143 -147 -408 -147q-187 0 -320 61v160q162 -61 316 -62q350 0 368 348h-538v154h536q-29 317 -364 317q-111 0 -262 -57z" /> +<glyph unicode="ю" d="M123 0v1098h162v-455h151q33 475 367 475q172 0 266 -147q96 -150 96 -420q0 -571 -368 -571q-335 0 -363 509h-149v-489h-162zM602 551q0 -219 47 -319.5t151.5 -100.5t149.5 98q47 100 47 322q0 217 -47 317q-45 98 -151 99q-102 0 -150 -97q-47 -98 -47 -319z" /> +<glyph unicode="я" d="M115 0l291 461q-233 68 -234 315q0 150 102.5 236t274.5 86h485v-1098h-182v432h-285l-254 -432h-198zM346 772q0 -186 238 -186h268v358h-295q-211 0 -211 -172z" /> +<glyph unicode="ё" d="M133 541q0 266 135 421.5t363 155.5q212 0 338 -135q127 -137 127 -356v-113h-774q9 -375 344 -375q199 0 370 76v-160q-170 -76 -364 -75q-244 0 -391 149q-148 151 -148 412zM326 662h573q0 305 -272 305q-276 0 -301 -305zM348 1394.5q0 102.5 96 102.5t96 -102.5 t-96 -102.5t-96 102.5zM723 1394.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5z" /> +<glyph unicode="ђ" d="M4 1237v137h156v182h182v-182h379v-137h-379v-184l-8 -144h10q106 168 334 168q391 0 391 -401v-834q0 -334 -291 -334q-82 0 -133 25v148q53 -20 115 -21q127 0 127 170v836q0 260 -236 260q-160 0 -235 -95q-74 -92 -74 -303v-528h-182v1237h-156z" /> +<glyph unicode="ѓ" d="M291 0v1098h747v-154h-565v-944h-182zM522 1241v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="є" d="M172 543q0 281 145 430q143 145 414 145q176 0 330 -59l-62 -158q-147 57 -262 57q-346 0 -377 -317h537v-154h-539q23 -348 369 -348q154 0 315 62v-160q-132 -61 -319 -61q-264 0 -408 147q-143 146 -143 416z" /> +<glyph unicode="ѕ" d="M203 49v166q195 -86 370 -86q274 0 275 162q0 55 -49 98t-226 107q-233 86 -294 159q-59 72 -60 172q0 135 111 213q113 78 309.5 78t368.5 -74l-60 -149q-184 72 -319 72q-236 0 -236 -133q0 -57 51.5 -96.5t231.5 -102.5q207 -76 280 -152q70 -72 70 -182 q0 -150 -116.5 -235.5t-331.5 -85.5q-246 -1 -375 69z" /> +<glyph unicode="і" d="M197 0v123l344 20v811l-269 21v123h451v-955l352 -20v-123h-878zM526 1435.5q0 114.5 106.5 114.5t106.5 -114q0 -57 -32.5 -86t-73.5 -29q-107 0 -107 114.5z" /> +<glyph unicode="ї" d="M197 0v123l344 20v811l-269 21v123h451v-955l352 -20v-123h-878zM330 1394.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5zM705 1394.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5z" /> +<glyph unicode="ј" d="M135 -303q131 -39 289 -39q119 0 182 57q66 59 66 158v1081l-420 21v123h602v-1215q0 -180 -113 -278q-111 -96 -319 -97q-160 0 -287 35v154zM637 1435.5q0 114.5 106.5 114.5t106.5 -114.5t-106.5 -114.5t-106.5 114.5z" /> +<glyph unicode="љ" d="M0 -2v131q22 -6 49 -6q41 0 66 43q27 45 47 231q27 233 57 701h494v-451h51q416 0 416 -311q0 -336 -420 -336h-209v944h-186q-33 -463 -60 -663q-25 -188 -72 -241.5t-141 -53.5q-61 0 -92 12zM713 150h55q254 0 254 172q0 90 -59.5 131t-198.5 41h-51v-344z" /> +<glyph unicode="њ" d="M106 0v1098h162v-455h299v455h162v-451h51q416 0 416 -311q0 -336 -420 -336h-209v489h-299v-489h-162zM729 150h55q254 0 254 172q0 90 -59 131t-199 41h-51v-344z" /> +<glyph unicode="ћ" d="M4 1237v137h156v182h184v-182h418v-137h-420v-184l-10 -144h10q106 168 346 168q190 0 287 -96q94 -94 94 -305v-676h-182v666q0 260 -236 260q-160 0 -235 -95q-74 -92 -74 -303v-528h-182v1237h-156z" /> +<glyph unicode="ќ" d="M215 0v1098h180v-291l-16 -299h4l137 166l402 424h213l-494 -510l522 -588h-213l-426 469l-129 -82v-387h-180zM508 1241v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="ў" d="M82 1098h188l262 -654q82 -203 89 -290h6q20 106 90 292l239 652h189l-475 -1241q-70 -178 -156 -263q-89 -86 -246 -86q-94 0 -168 17v145q61 -12 136 -12q96 0 149 41t96 141l58 150zM241 1559h170q10 -104 50 -144q43 -43 161 -43q193 0 211 187h174 q-20 -317 -389 -318q-195 0 -281 74t-96 244z" /> +<glyph unicode="џ" d="M160 0v1098h182v-944h545v944h182v-1098h-354v-381h-183v381h-372z" /> +<glyph unicode="Ґ" d="M233 0v1462h637v301h177v-467h-627v-1296h-187z" /> +<glyph unicode="ґ" d="M291 0v1098h545v319h182v-456h-545v-961h-182z" /> +<glyph unicode="Ḁ" d="M33 0l483 1468h195l485 -1468h-192l-144 453h-491l-146 -453h-190zM422 618h385l-133 424q-39 121 -62 226q-20 -88 -47 -183zM389 -340q0 47 16 90q16 41 46 68q31 29 69 43q37 14 92 14q49 0 90 -14q39 -14 72 -43t49 -66q18 -39 19 -90q0 -57 -17 -92 q-18 -40 -49 -67.5t-74 -43.5q-41 -14 -90 -14q-55 0 -92 14q-39 14 -69 43q-29 27 -46 68q-16 43 -16 90zM500 -340q0 -51 28 -84q27 -29 84 -29q53 0 82 29q31 31 31 84t-31 84q-29 29 -82 29q-51 0 -80 -29q-32 -33 -32 -84z" /> +<glyph unicode="ḁ" d="M135 307q0 332 510 348l203 7v69q0 236 -244 236q-150 0 -328 -82l-63 137q199 96 383 96q223 0 328 -88q102 -86 102 -278v-752h-131l-37 152h-8q-74 -94 -158 -133t-209 -39q-164 0 -256 85.5t-92 241.5zM324 305q0 -178 200 -178q150 0 234 82q88 84 88 229v99 l-162 -7q-195 -8 -278 -61q-82 -51 -82 -164zM383 -340q0 47 16 90q16 41 46 68q31 29 69 43q37 14 92 14q49 0 90 -14q39 -14 72 -43t49 -66q18 -39 19 -90q0 -57 -17 -92q-18 -40 -49 -67.5t-74 -43.5q-41 -14 -90 -14q-55 0 -92 14q-39 14 -69 43q-29 27 -46 68 q-16 43 -16 90zM494 -340q0 -51 28 -84q27 -29 84 -29q53 0 82 29q31 31 31 84t-31 84q-29 29 -82 29q-51 0 -80 -29q-32 -33 -32 -84z" /> +<glyph unicode="Ḿ" d="M113 0v1462h247l248 -1192h6l250 1192h252v-1462h-153v887q0 121 14 391h-8l-283 -1278h-154l-278 1280h-8q18 -268 18 -406v-874h-151zM518 1581v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="ḿ" d="M92 0v1098h127l27 -148h10q68 168 201 168q164 0 213 -182h6q78 182 219 182q129 0 186 -92q57 -90 58 -309v-717h-162v707q0 147 -27 202q-27 57 -88 58q-88 0 -127 -82q-39 -80 -39 -279v-606h-161v707q0 260 -125 260q-82 0 -119 -80t-37 -318v-569h-162zM545 1241v27 q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="Ẁ" d="M2 1462h170l88 -663q18 -145 39 -377q18 -203 18 -242q27 162 70 312l141 516h177l145 -521q57 -205 72 -307q6 92 65 619l70 663h170l-187 -1462h-190l-168 580q-43 147 -66 282q-31 -168 -65 -284l-156 -578h-190zM336 1886v21h219q88 -182 174 -301v-27h-121 q-163 139 -272 307z" /> +<glyph unicode="ẁ" d="M-4 1098h162l98 -543q39 -215 57 -393h6q33 195 68 358l133 578h193l127 -578q43 -188 67 -358h6q29 225 60 393l102 543h158l-225 -1098h-195l-131 596l-68 330h-6l-65 -334l-135 -592h-189zM303 1548v21h219q88 -182 174 -301v-27h-121q-163 139 -272 307z" /> +<glyph unicode="Ẃ" d="M2 1462h170l88 -663q18 -145 39 -377q18 -203 18 -242q27 162 70 312l141 516h177l145 -521q57 -205 72 -307q6 92 65 619l70 663h170l-187 -1462h-190l-168 580q-43 147 -66 282q-31 -168 -65 -284l-156 -578h-190zM500 1579v27q92 127 174 301h219v-21 q-109 -168 -272 -307h-121z" /> +<glyph unicode="ẃ" d="M-4 1098h162l98 -543q39 -215 57 -393h6q33 195 68 358l133 578h193l127 -578q43 -188 67 -358h6q29 225 60 393l102 543h158l-225 -1098h-195l-131 596l-68 330h-6l-65 -334l-135 -592h-189zM522 1241v27q92 127 174 301h219v-21q-109 -168 -272 -307h-121z" /> +<glyph unicode="Ẅ" d="M2 1462h170l88 -663q18 -145 39 -377q18 -203 18 -242q27 162 70 312l141 516h177l145 -521q57 -205 72 -307q6 92 65 619l70 663h170l-187 -1462h-190l-168 580q-43 147 -66 282q-31 -168 -65 -284l-156 -578h-190zM334 1732.5q0 102.5 96 102.5t96 -102.5t-96 -102.5 t-96 102.5zM709 1732.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5z" /> +<glyph unicode="ẅ" d="M-4 1098h162l98 -543q39 -215 57 -393h6q33 195 68 358l133 578h193l127 -578q43 -188 67 -358h6q29 225 60 393l102 543h158l-225 -1098h-195l-131 596l-68 330h-6l-65 -334l-135 -592h-189zM330 1394.5q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5zM705 1394.5 q0 102.5 96 102.5t96 -102.5t-96 -102.5t-96 102.5z" /> +<glyph unicode="Ỳ" d="M33 1462h203l376 -739l381 739h201l-487 -893v-569h-187v559zM303 1886v21h219q88 -182 174 -301v-27h-121q-163 139 -272 307z" /> +<glyph unicode="ỳ" d="M82 1098h188l262 -654q82 -203 89 -290h6q20 106 90 292l239 652h189l-475 -1241q-70 -178 -156 -263q-89 -86 -246 -86q-94 0 -168 17v145q61 -12 136 -12q96 0 149 41t96 141l58 150zM314 1548v21h219q88 -182 174 -301v-27h-121q-163 139 -272 307z" /> +<glyph unicode="Ὅ" d="M229 735q0 750 534 750q258 0 392 -195q137 -199 137 -557q0 -362 -137 -557q-138 -197 -394 -196q-532 -1 -532 755zM426 733q0 -590 335 -590q174 0 254 145.5t80 444.5q0 305 -82 445q-84 143 -250 143q-337 0 -337 -588zM-311 1255q0 78 59 136q61 57 183 88v-76 q-55 -18 -88 -45q-29 -23 -29 -47q0 -18 16 -29l33 -16q18 -10 33 -21q16 -12 16 -41q0 -39 -24.5 -57.5t-71.5 -18.5q-57 0 -92 35t-35 92zM-2 1135v26l23 68l24 76l25 81q4 16 10 44t8 32h207v-20l-43 -70l-51 -80q-6 -10 -59 -80q-31 -41 -62 -77h-82z" /> +<glyph unicode=" " /> +<glyph unicode=" " /> +<glyph unicode=" " /> +<glyph unicode=" " /> +<glyph unicode=" " /> +<glyph unicode=" " /> +<glyph unicode=" " /> +<glyph unicode=" " /> +<glyph unicode=" " /> +<glyph unicode=" " /> +<glyph unicode=" " /> +<glyph unicode="​" /> +<glyph unicode="‐" d="M285 465v168h659v-168h-659z" /> +<glyph unicode="‑" d="M285 465v168h659v-168h-659z" /> +<glyph unicode="‒" d="M285 465v168h659v-168h-659z" /> +<glyph unicode="–" d="M184 465v168h860v-168h-860z" /> +<glyph unicode="—" d="M-6 465v168h1241v-168h-1241z" /> +<glyph unicode="―" d="M-6 465v168h1241v-168h-1241z" /> +<glyph unicode="‗" d="M-20 -45h1269v-139h-1269v139zM-20 -324h1269v-139h-1269v139z" /> +<glyph unicode="‘" d="M446 983q57 217 177 479h157q-66 -276 -100 -501h-219z" /> +<glyph unicode="’" d="M446 961q61 254 101 501h219l14 -22q-53 -207 -176 -479h-158z" /> +<glyph unicode="‚" d="M457 -264q76 309 100 502h199l14 -23q-53 -207 -176 -479h-137z" /> +<glyph unicode="‛" d="M446 1440l15 22h219q41 -270 100 -501h-157q-126 278 -177 479z" /> +<glyph unicode="“" d="M233 983q57 217 177 479h157q-66 -276 -100 -501h-219zM659 983q57 217 177 479h157q-66 -276 -100 -501h-219z" /> +<glyph unicode="”" d="M233 961q61 254 101 501h219l14 -22q-53 -207 -176 -479h-158zM659 961q61 254 101 501h219l14 -22q-53 -207 -176 -479h-158z" /> +<glyph unicode="„" d="M244 -264q76 309 100 502h199l14 -23q-53 -207 -176 -479h-137zM670 -264q76 309 100 502h199l14 -23q-55 -209 -176 -479h-137z" /> +<glyph unicode="†" d="M240 989v180l311 -30l-55 417h217l-56 -417l332 30v-180l-332 31l56 -1020h-217l55 1020z" /> +<glyph unicode="‡" d="M229 346v180l332 -30l-47 290l47 275l-332 -31v180l332 -30l-55 376h217l-55 -376l331 30v-180l-331 31l47 -275l-47 -290l331 30v-180l-331 31l55 -377h-217l55 377z" /> +<glyph unicode="•" d="M379 748q0 262 235.5 262t235.5 -262q0 -129 -64 -195q-65 -68 -172 -68q-113 0 -174 68t-61 195z" /> +<glyph unicode="…" d="M78 110.5q0 139.5 127 139.5t127 -139.5t-127 -139.5t-127 139.5zM487 110.5q0 139.5 127 139.5t127 -139.5t-127 -139.5t-127 139.5zM897 110.5q0 139.5 127 139.5t127 -139.5t-127 -139.5t-127 139.5z" /> +<glyph unicode=" " horiz-adv-x="430" /> +<glyph unicode="‰" d="M4 655l698 289l41 -100l-698 -287zM20 1171q0 309 244 310q113 0 176 -80q63 -78 64 -230q0 -311 -244 -311q-113 0 -176.5 82t-63.5 229zM156 1171q0 -102 22.5 -153t84 -51t83.5 51q20 47 21 153q0 119 -23 158q-25 43 -82 43q-55 0 -82 -45q-24 -43 -24 -156zM195 311 q0 309 243 310q111 0 176 -80q63 -78 64 -230q0 -311 -244 -311q-115 0 -176 82q-63 86 -63 229zM330 311q0 -104 22 -155q20 -49 84 -49.5t84 49.5q20 47 21 155q0 119 -23 158q-25 43 -82 43q-55 0 -82 -45q-24 -43 -24 -156zM725 311q0 309 244 310q113 0 176 -80 q63 -78 63 -230q0 -311 -243 -311q-115 0 -177 82q-63 86 -63 229zM860 311q0 -104 23 -155q23 -49 84 -49.5t84 49.5q20 47 20 155q0 113 -22 158q-20 43 -82 43q-59 0 -84 -43q-23 -39 -23 -158z" /> +<glyph unicode="′" d="M496 1462h237l-41 -528h-155z" /> +<glyph unicode="″" d="M285 1462h237l-41 -528h-155zM707 1462h237l-41 -528h-155z" /> +<glyph unicode="‹" d="M401 526v27l310 414l116 -78l-237 -348l237 -348l-116 -78z" /> +<glyph unicode="›" d="M401 193l238 348l-238 348l117 78l309 -414v-27l-309 -411z" /> +<glyph unicode="‼" d="M718 110.5q0 139.5 127 139.5t127 -139.5t-127 -139.5t-127 139.5zM735 1462h223l-51 -1048h-121zM256 110.5q0 139.5 127 139.5t127 -139.5t-127 -139.5t-127 139.5zM273 1462h223l-51 -1048h-121z" /> +<glyph unicode="⁄" d="M170 0l729 1462h158l-729 -1462h-158z" /> +<glyph unicode=" " horiz-adv-x="538" /> +<glyph unicode="ⁿ" d="M317 797v665h125l17 -104h8q68 121 207 121q109 0 174 -66q63 -63 63 -184v-432h-147v413q0 150 -127 150q-98 0 -135 -53q-37 -52 -37 -172v-338h-148z" /> +<glyph unicode="₣" d="M129 262v137h176v1063h752v-164h-574v-516h535v-164h-535v-219h293v-137h-293v-262h-178v262h-176z" /> +<glyph unicode="₤" d="M119 0v154q201 49 200 284v21h-198v137h198v158h-198v137h198v221q0 166 109 268q106 100 289 101q193 0 346 -80l-66 -144q-143 72 -272 72q-223 0 -223 -246v-192h377v-137h-377v-158h377v-137h-377v-19q0 -199 -140 -274h748v-166h-991z" /> +<glyph unicode="₧" d="M82 0v1462h205q451 0 450 -428q0 -219 -114 -344q-113 -121 -342 -121h-41v-569h-158zM240 715h41q293 0 292 309q0 291 -268 291h-65v-600zM688 618v70l115 51l65 215h86v-211h209v-125h-209v-348q0 -154 113 -153q63 0 123 20v-125q-70 -33 -166 -32q-221 0 -221 282 v356h-115z" /> +<glyph unicode="€" d="M96 502v137h148l-2 39l2 119h-148v137h160q41 262 180 405q141 143 359 144q193 0 335 -92l-79 -146q-123 74 -242 74q-133 0 -234 -100q-96 -96 -133 -285h432v-137h-446q0 -6 -1 -14.5t-1 -14.5v-25v-61q0 -29 2 -43h385v-137h-367q74 -358 369 -359q139 0 268 58v-162 q-125 -59 -282 -59q-444 0 -541 522h-164z" /> +<glyph unicode="℅" d="M8 1126q0 168 90 258q88 88 252 89q100 0 182 -37l-32 -107q-80 33 -150 33q-199 0 -198 -234q0 -229 194 -229q104 0 176 33v-107q-74 -37 -182 -37q-156 0 -244 86t-88 252zM156 0l729 1462h157l-729 -1462h-157zM616 334q0 164 82 252q84 90 224 90q129 0 217 -94 q84 -90 84 -248q0 -160 -82 -250q-84 -92 -223 -92q-131 0 -218 92q-84 90 -84 250zM762 334q0 -233 158 -234q156 0 155 234q0 231 -155 231q-158 0 -158 -231z" /> +<glyph unicode="ℓ" d="M227 510v121q66 16 193 59v494q0 137 69 219q68 80 201 80q109 0 172.5 -82t63.5 -219q0 -354 -340 -541v-301q0 -221 145 -221q143 0 154 213h114q-18 -352 -286 -352q-138 0 -215 92q-78 94 -78 264v231q-105 -37 -193 -57zM586 770q207 121 207 406q0 188 -103 188 q-53 0 -78 -41q-27 -43 -26 -147v-406z" /> +<glyph unicode="№" d="M25 0v1462h176l323 -1163h6q-14 285 -14 403v760h133v-1462h-174l-325 1169h-9q18 -252 19 -417v-752h-135zM705 623q0 346 253 346q115 0 182.5 -92.5t67.5 -253.5q0 -168 -65.5 -258.5t-188.5 -90.5q-115 0 -182 92.5t-67 256.5zM745 0v147h414v-147h-414zM844 623 q0 -111 24 -170q25 -57 88 -58q61 0 86 58q25 59 25 170q0 113 -24.5 168t-86.5 55q-63 0 -87.5 -55t-24.5 -168z" /> +<glyph unicode="™" d="M0 1354v108h481v-108h-178v-613h-127v613h-176zM526 741v721h187l139 -534l149 534h179v-721h-127v342q0 74 10 207h-12l-154 -549h-100l-146 549h-12l10 -180v-369h-123z" /> +<glyph unicode="Ω" d="M74 0v164h266q-254 233 -254 661q0 313 139 486q141 174 391 174q248 0 390 -174q141 -176 141 -486q0 -428 -256 -661h268v-164h-469v147q260 229 260 674q0 500 -335.5 500t-335.5 -500q0 -442 262 -674v-147h-467z" /> +<glyph unicode="℮" d="M82 530q0 260 154 414q152 152 376 152q229 0 381 -156q150 -152 150 -405v-5h-828v-356q123 -123 297 -123q254 0 396 234l71 -41q-98 -158 -207 -217q-111 -61 -260 -62q-242 0 -385 162q-145 163 -145 403zM315 616h596v277q-119 119 -301 119q-172 0 -295 -117v-279z " /> +<glyph unicode="⅛" d="M19 1309l219 151h135v-788h-146v465q0 84 9 200q-43 -45 -74 -65l-70 -51zM115 0l729 1462h158l-729 -1462h-158zM647 211q0 131 139 204q-115 80 -114 193q0 90 71 145q70 55 184.5 55.5t182.5 -53.5q70 -55 70 -147q0 -117 -140 -184q166 -86 166 -207 q0 -104 -75.5 -166t-200.5 -62q-133 0 -209 60q-74 60 -74 162zM793 211q0 -117 131 -117q135 0 135 117q0 89 -135 147l-13 6q-118 -65 -118 -153zM819 606q0 -80 109 -133q106 49 106 133q0 96 -108 96q-107 0 -107 -96z" /> +<glyph unicode="⅜" d="M10 705v124q113 -66 203 -65q150 0 149 137q0 125 -147 125h-72v104h70q123 0 123 127q0 111 -97 111q-74 0 -161 -70l-66 86q111 92 238 93q113 0 174 -54q63 -55 63 -149q0 -139 -149 -189q176 -41 176 -186q0 -117 -74 -180q-72 -63 -215 -64q-132 1 -215 50zM154 0 l729 1462h158l-729 -1462h-158zM667 211q0 131 139 204q-115 80 -114 193q0 90 71 145q70 55 184.5 55.5t182.5 -53.5q70 -55 70 -147q0 -117 -140 -184q166 -86 166 -207q0 -104 -75.5 -166t-200.5 -62q-133 0 -209 60q-74 60 -74 162zM813 211q0 -117 131 -117 q135 0 135 117q0 89 -135 147l-13 6q-118 -65 -118 -153zM839 606q0 -80 109 -133q106 49 106 133q0 96 -108 96q-107 0 -107 -96z" /> +<glyph unicode="⅝" d="M25 705v129q102 -68 207 -68q162 0 162 147q0 145 -166 146q-59 0 -125 -17l-62 39l31 381h397v-110h-288l-17 -193q33 6 96 6q115 0 191 -65q74 -63 74 -174q0 -127 -74 -199q-72 -70 -221 -70q-129 1 -205 48zM154 0l729 1462h158l-729 -1462h-158zM667 211 q0 131 139 204q-115 80 -114 193q0 90 71 145q70 55 184.5 55.5t182.5 -53.5q70 -55 70 -147q0 -117 -140 -184q166 -86 166 -207q0 -104 -75.5 -166t-200.5 -62q-133 0 -209 60q-74 60 -74 162zM813 211q0 -117 131 -117q135 0 135 117q0 89 -135 147l-13 6 q-118 -65 -118 -153zM839 606q0 -80 109 -133q106 49 106 133q0 96 -108 96q-107 0 -107 -96z" /> +<glyph unicode="⅞" d="M27 1341v121h548v-100l-309 -690h-155l309 669h-393zM88 0l729 1462h158l-729 -1462h-158zM667 211q0 131 139 204q-115 80 -114 193q0 90 71 145q70 55 184.5 55.5t182.5 -53.5q70 -55 70 -147q0 -117 -140 -184q166 -86 166 -207q0 -104 -75.5 -166t-200.5 -62 q-133 0 -209 60q-74 60 -74 162zM813 211q0 -117 131 -117q135 0 135 117q0 89 -135 147l-13 6q-118 -65 -118 -153zM839 606q0 -80 109 -133q106 49 106 133q0 96 -108 96q-107 0 -107 -96z" /> +<glyph unicode="∂" d="M121 375q0 250 137 442.5t383 192.5q184 0 283 -148l2 39q10 184 -58 303q-68 121 -219 121q-131 0 -258 -84v174q135 61 283 62q432 0 432 -543q0 -391 -168 -672q-169 -283 -455 -282q-362 0 -362 395zM309 369q0 -236 187 -236q137 0 247.5 153.5t147.5 393.5 q-20 84 -86 137t-139 53q-160 0 -258.5 -159.5t-98.5 -341.5z" /> +<glyph unicode="∆" d="M74 0v113l446 1349h187l448 -1351v-111h-1081zM274 164h678l-247 758q-57 176 -93 340q-47 -207 -88 -334z" /> +<glyph unicode="∏" d="M156 -492v1954h956v-1954h-186v1788h-584v-1788h-186z" /> +<glyph unicode="∑" d="M74 -377l583 914l-567 811v114h1014v-164h-754l529 -759l-555 -867h845v-164h-1095v115z" /> +<glyph unicode="−" d="M152 647v150h923v-150h-923z" /> +<glyph unicode="√" d="M23 764v143h296l230 -663l512 1452h145l-594 -1710h-133l-276 778h-180z" /> +<glyph unicode="∞" d="M70 723q0 133 75 225q74 90 193 90q166 0 274 -219q117 213 281 213q117 0 190.5 -86t73.5 -229q0 -133 -76 -223q-78 -92 -188 -93q-172 0 -283 220q-117 -213 -272 -213q-117 0 -192.5 88t-75.5 227zM205 721q0 -80 41 -131t106 -51q102 0 189 184q-83 184 -191 184 q-63 0 -104 -53t-41 -133zM684 717q86 -184 193 -185q66 0 104 54q41 57 41 133q0 82 -41 131q-43 51 -107 51q-104 0 -190 -184z" /> +<glyph unicode="∫" d="M236 -326q61 -25 116 -24q180 0 180 233v1319q0 354 330 354q88 0 131 -18v-147q-59 27 -114 26q-172 0 -172 -233v-1321q0 -176 -89 -264q-90 -90 -249 -91q-78 0 -133 19v147z" /> +<glyph unicode="≈" d="M152 379v162q98 109 247 108q102 0 248 -63q129 -55 201 -56q106 0 227 121v-162q-98 -109 -248 -108q-102 0 -247 63q-133 55 -201 56q-106 0 -227 -121zM152 788v162q98 109 247 109q100 0 248 -64q127 -55 201 -55q111 0 227 121v-162q-98 -109 -248 -108 q-102 0 -247 63q-129 55 -201 55q-112 0 -227 -121z" /> +<glyph unicode="≠" d="M152 442v150h317l121 260h-438v149h507l134 283l137 -59l-105 -224h250v-149h-319l-123 -260h442v-150h-512l-129 -278l-137 57l102 221h-247z" /> +<glyph unicode="≤" d="M152 0v150h923v-150h-923zM152 672v102l923 451v-160l-715 -342l715 -342v-160z" /> +<glyph unicode="≥" d="M152 0v150h923v-150h-923zM152 221v160l714 342l-714 342v160l923 -451v-102z" /> +<glyph unicode="◊" d="M125 735l450 740h76l453 -738l-453 -737h-76zM309 737l305 -514l306 514l-306 512z" /> +<glyph unicode="" horiz-adv-x="1100" d="M0 1100h1100v-1100h-1100v1100z" /> +<glyph unicode="fi" d="M49 961v75l195 68v96q0 190 75.5 278.5t255.5 88.5q96 0 197 -37l-47 -141q-82 29 -143 28q-90 0 -123 -51q-33 -53 -33 -164v-104h246v-137h-246v-961h-182v961h-195zM854 1394.5q0 114.5 106.5 114.5t106.5 -114q0 -58 -31 -86q-33 -29 -75 -29q-107 0 -107 114.5z M868 0v1098h183v-1098h-183z" /> +<glyph unicode="fl" d="M49 961v75l195 68v96q0 190 75.5 278.5t255.5 88.5q96 0 197 -37l-47 -141q-82 29 -143 28q-90 0 -123 -51q-33 -53 -33 -164v-104h246v-137h-246v-961h-182v961h-195zM868 0v1556h183v-1556h-183z" /> +<glyph unicode="ffi" d="M66 971v65l100 62v82q0 109 20 176q23 76 58 112q37 39 96 60q55 18 127 18q57 0 92 -10q47 -14 78 -25q43 33 88 43q53 12 115 13q59 0 98 -11q49 -14 82 -26l-41 -131q-18 8 -64 20q-31 8 -71 8q-37 0 -66 -12q-27 -12 -45 -37q-16 -23 -26 -69q-8 -39 -9 -107v-104 h367v-1098h-164v971h-203v-971h-163v971h-205v-971h-164v971h-100zM330 1098h205v102q0 115 24 193q-29 8 -43 10q-29 4 -45 4q-39 0 -61 -10q-27 -12 -45 -37q-14 -18 -25 -70q-10 -47 -10 -108v-84z" /> +<glyph unicode="ffl" d="M66 971v65l100 62v82q0 109 20 176q23 76 58 112q37 39 96 60q55 18 127 18q57 0 92 -10q47 -14 78 -25q43 33 88 43q53 12 115 13q55 0 94 -11h131v-1556h-164v1421q-20 4 -29 4q-4 0 -14 1t-14 1q-37 0 -66 -12q-27 -12 -45 -37q-16 -23 -26 -69q-8 -39 -9 -107v-104 h142v-127h-142v-971h-163v971h-205v-971h-164v971h-100zM330 1098h205v102q0 115 24 193q-29 8 -43 10q-29 4 -45 4q-39 0 -61 -10q-27 -12 -45 -37q-14 -18 -25 -70q-10 -47 -10 -108v-84z" /> +<glyph unicode="" /> +<glyph unicode="" d="M55 39v270h111v-270h-111zM55 -80h111v-194h192v-109h-303v303zM55 430v272h111v-272h-111zM55 823v271h111v-271h-111zM55 1214v304h303v-111h-192v-193h-111zM254 1083.5q0 55.5 12 98.5q12 45 39 78q23 29 64 47t94 18t92 -18q45 -20 66 -47q27 -33 38 -78 q12 -43 12.5 -98.5t-12 -98.5t-38.5 -76q-25 -31 -66 -49q-39 -18 -92 -18t-94 18q-39 18 -64 49q-27 33 -39 76t-12 98.5zM354 1083.5q0 -75.5 27 -116.5t82 -41q59 0 82 41q27 47 26.5 116.5t-26.5 116.5q-23 41 -82 41q-55 0 -82 -41t-27 -116.5zM479 -274h271v-109h-271 v109zM479 1407v111h271v-111h-271zM535 326v469h137q92 0 133 -27q43 -28 43 -92q0 -40 -19 -68q-18 -27 -53 -33v-4q12 -2 33 -10q10 -4 27 -20q12 -12 18 -31q8 -25 8 -47q0 -63 -43 -100t-119 -37h-165zM633 408h59q37 0 53 18q14 16 15 47q0 27 -15 41q-16 16 -57 16 h-55v-122zM633 610h51q37 0 51.5 12.5t14.5 39t-16.5 39t-53.5 12.5h-47v-103zM709 -111q4 -2 15 -3t17 -1q12 -2 43 -2t47 15q20 16 21 55v244h-94v77h266v-77h-72v-242q0 -80 -45 -119q-43 -37 -112 -37q-37 0 -52 2q-25 4 -34 6v82zM870 -274h195v194h109v-303h-304v109z M870 1407v111h304v-304h-109v193h-195zM1065 39v270h109v-270h-109zM1065 430v272h109v-272h-109zM1065 823v271h109v-271h-109z" /> +<glyph unicode="�" d="M4 618l610 938l613 -938l-613 -937zM342 1014l66 -160q51 29 106 51q49 20 96 21q53 0 78 -27t25 -68q0 -25 -6.5 -47t-16.5 -36q-12 -18 -31 -37q-10 -10 -47 -43q-31 -29 -53 -54q-23 -27 -35 -53q-16 -39 -20 -55q-6 -31 -6 -66v-55h170v43q0 16 4 41q2 16 12 33 q6 10 29 33q27 27 45 40l61 62q25 23 45 59q18 29 27 66q8 33 8 82q0 125 -76 192q-78 68 -211 68q-35 0 -71 -6q-29 -4 -72 -21q-12 -4 -66 -28q-28 -12 -61 -35zM477 139.5q0 -67.5 33 -94.5q35 -29 82 -29q18 0 41 7q20 6 37 22q18 18 26 39q8 23 9 55q0 35 -9 58 q-8 20 -26 39q-14 14 -37 20t-41 6q-47 0 -82 -29q-33 -26 -33 -93.5z" /> +<glyph d="M231 1559h170q10 -104 50 -144q43 -43 161 -43q193 0 211 187h174q-20 -317 -389 -318q-195 0 -281 74t-96 244z" /> +<glyph d="M135 -303q131 -39 289 -39q119 0 182 57q66 59 66 158v1081l-420 21v123h602v-1215q0 -180 -113 -278q-111 -96 -319 -97q-160 0 -287 35v154z" /> +<glyph d="M494 1229v26l26 107q27 127 35 194h180v-20q0 -14 -43 -113q-49 -111 -102 -194h-96z" /> +<glyph d="M483 -426q63 145 84 301h178v-20q0 -22 -49 -123q-51 -109 -114 -185h-99v27z" /> +<glyph d="M483 1241v21q0 20 45 118q45 100 119 189h98v-27q-61 -145 -83 -301h-179z" /> +<glyph d="M313 1026q0 453 299 453q150 0 226 -117q78 -119 77 -336q0 -457 -303 -457q-141 0 -221 117q-78 115 -78 340zM461 1026q0 -326 151 -326q156 0 156 326q0 324 -156 324q-151 0 -151 -324z" /> +<glyph d="M293 854v98l377 523h141v-508h84v-113h-84v-174h-143v174h-375zM438 967h230v176q0 83 6 172q-37 -70 -80 -131z" /> +<glyph d="M336 705v129q102 -68 207 -68q162 0 162 147q0 145 -166 146q-59 0 -125 -17l-62 39l31 381h397v-110h-288l-17 -193q33 6 96 6q115 0 191 -65q74 -63 74 -174q0 -127 -74 -199q-72 -70 -221 -70q-129 1 -205 48z" /> +<glyph d="M313 913q0 408 484 580l45 -109q-329 -111 -381 -325h10q61 92 201 92q115 0 178 -76q66 -78 65 -205q0 -133 -83.5 -217t-215.5 -84q-137 0 -219 93q-84 93 -84 251zM455 889q0 -84 47 -141.5t121 -57.5q68 0 110 45q41 43 41 133q0 172 -147 172q-74 0 -123 -42.5 t-49 -108.5z" /> +<glyph d="M367 1341v121h548v-100l-309 -690h-155l309 669h-393z" /> +<glyph d="M305 877q0 131 139 204q-115 80 -114 193q0 90 71 145q70 55 184.5 55.5t182.5 -53.5q70 -55 70 -147q0 -117 -140 -184q166 -86 166 -207q0 -104 -75.5 -166t-200.5 -62q-133 0 -209 60q-74 60 -74 162zM451 877q0 -117 131 -117q135 0 135 117q0 89 -135 147l-13 6 q-118 -65 -118 -153zM477 1272q0 -80 109 -133q106 49 106 133q0 96 -108 96q-107 0 -107 -96z" /> +<glyph d="M313 1178q0 133 80 219q78 84 211 84q141 0 227 -103q84 -98 84 -288q0 -520 -413 -521q-66 0 -111 15v125q41 -23 101 -23q272 0 286 315h-8q-71 -98 -197 -98q-123 0 -190 74q-70 76 -70 201zM453 1180q0 -164 149 -164q72 0 121 43t49 100q0 78 -45 141 q-43 61 -121 62q-68 0 -110 -47q-43 -46 -43 -135z" /> +<glyph d="M158 547q0 268 112 422q111 150 305 149q203 0 314 -168h8l27 148h147v-1121q0 -225 -119 -346q-121 -123 -336 -123q-195 0 -352 76v170q186 -96 350 -96q274 0 275 309v10q0 41 12 170h-12q-109 -168 -314 -167q-193 0 -305 149.5t-112 417.5zM346 545q0 -414 264 -414 q147 0 211 88q66 90 68 287v41q0 227 -67.5 323.5t-213.5 96.5q-262 0 -262 -422z" /> +<glyph d="M-18 735q0 362 163 557q162 193 473 193q299 0 465 -199q166 -200 166 -553q0 -549 -379 -708l338 -373l-153 -109l-367 441q-41 -4 -72 -4q-309 0 -471 196q-163 199 -163 559zM178 733q0 -590 438 -590q436 0 437 590q0 588 -435 588q-440 0 -440 -588z" /> +<glyph d="M348 1163l383 299h150v-1462h-176v913q0 147 8 361q-43 -47 -121 -113l-148 -121z" /> +<glyph d="M346 946v193q236 111 369 323h166v-1462h-179v823l5 408q-119 -154 -361 -285z" /> +<glyph d="M371 1092v127l301 34l71 209h113v-1462h-176v909q0 111 8 183h-317z" /> +<glyph d="M301 1255q0 78 59 136q61 57 183 88v-76q-55 -18 -88 -45q-29 -23 -29 -47q0 -18 16 -29l33 -16q18 -10 33 -21q16 -12 16 -41q0 -39 -24.5 -57.5t-71.5 -18.5q-57 0 -92 35t-35 92zM610 1135v26l23 68l24 76l25 81q4 16 10 44t8 32h207v-20l-43 -70l-51 -80 q-6 -10 -59 -80q-31 -41 -62 -77h-82z" /> +</font> +</defs></svg>
\ No newline at end of file diff --git a/apps/files_texteditor/css/DroidSansMono/DroidSansMono-webfont.ttf b/apps/files_texteditor/css/DroidSansMono/DroidSansMono-webfont.ttf Binary files differnew file mode 100644 index 00000000000..e125bc1f314 --- /dev/null +++ b/apps/files_texteditor/css/DroidSansMono/DroidSansMono-webfont.ttf diff --git a/apps/files_texteditor/css/DroidSansMono/DroidSansMono-webfont.woff b/apps/files_texteditor/css/DroidSansMono/DroidSansMono-webfont.woff Binary files differnew file mode 100644 index 00000000000..31d51be4a70 --- /dev/null +++ b/apps/files_texteditor/css/DroidSansMono/DroidSansMono-webfont.woff diff --git a/apps/files_texteditor/css/DroidSansMono/Google Android License.txt b/apps/files_texteditor/css/DroidSansMono/Google Android License.txt new file mode 100644 index 00000000000..0a010886a1e --- /dev/null +++ b/apps/files_texteditor/css/DroidSansMono/Google Android License.txt @@ -0,0 +1,18 @@ +Copyright (C) 2008 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +########## + +This directory contains the fonts for the platform. They are licensed +under the Apache 2 license. diff --git a/apps/files_texteditor/css/DroidSansMono/demo.html b/apps/files_texteditor/css/DroidSansMono/demo.html new file mode 100644 index 00000000000..e305598817d --- /dev/null +++ b/apps/files_texteditor/css/DroidSansMono/demo.html @@ -0,0 +1,33 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> + +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> + + <title>Font Face Demo</title> + <link rel="stylesheet" href="stylesheet.css" type="text/css" charset="utf-8"> + <style type="text/css" media="screen"> + h1.fontface {font: 60px/68px 'DroidSansMonoRegular', Arial, sans-serif;letter-spacing: 0;} + + p.style1 {font: 18px/27px 'DroidSansMonoRegular', Arial, sans-serif;} + + #container { + width: 800px; + margin-left: auto; + margin-right: auto; + } + </style> +</head> + +<body> + <div id="container"> + <h1 class="fontface">Font-face Demo for the Droid Sans Mono Font</h1> + + + + <p class="style1">Droid Sans Mono Regular - Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> + + </div> +</body> +</html> diff --git a/apps/files_texteditor/css/DroidSansMono/stylesheet.css b/apps/files_texteditor/css/DroidSansMono/stylesheet.css new file mode 100644 index 00000000000..4d814b262d3 --- /dev/null +++ b/apps/files_texteditor/css/DroidSansMono/stylesheet.css @@ -0,0 +1,15 @@ +/* Generated by Font Squirrel (http://www.fontsquirrel.com) on June 13, 2012 02:50:54 PM America/New_York */ + + + +@font-face { + font-family: 'Droid Sans Mono'; + src: url('%appswebroot%/files_texteditor/css/DroidSansMono/DroidSansMono-webfont.eot'); + src: url('%appswebroot%/files_texteditor/css/DroidSansMono/DroidSansMono-webfont.eot?#iefix') format('embedded-opentype'), + url('%appswebroot%/files_texteditor/css/DroidSansMono/DroidSansMono-webfont.woff') format('woff'), + url('%appswebroot%/files_texteditor/css/DroidSansMono/DroidSansMono-webfont.ttf') format('truetype'), + url('%appswebroot%/files_texteditor/css/DroidSansMono/DroidSansMono-webfont.svg#DroidSansMonoRegular') format('svg'); + font-weight: normal; + font-style: normal; + +} diff --git a/apps/files_texteditor/js/aceeditor/ace.js b/apps/files_texteditor/js/aceeditor/ace.js index 6043589ac1f..679230926e9 100644 --- a/apps/files_texteditor/js/aceeditor/ace.js +++ b/apps/files_texteditor/js/aceeditor/ace.js @@ -1,4 +1,4 @@ -(function(){function g(a){if(typeof requirejs!="undefined"){var e=b.define;b.define=function(a,b,c){return typeof c!="function"?e.apply(this,arguments):e(a,b,function(a,d,e){return b[2]=="module"&&(e.packaged=!0),c.apply(this,arguments)})},b.define.packaged=!0;return}var f=function(a,b){return d("",a,b)};f.packaged=!0;var g=b;a&&(b[a]||(b[a]={}),g=b[a]),g.define&&(c.original=g.define),g.define=c,g.require&&(d.original=g.require),g.require=f}var a="",b=function(){return this}(),c=function(a,b,d){if(typeof a!="string"){c.original?c.original.apply(window,arguments):(console.error("dropping module because define wasn't a string."),console.trace());return}arguments.length==2&&(d=b),c.modules||(c.modules={}),c.modules[a]=d},d=function(a,b,c){if(Object.prototype.toString.call(b)==="[object Array]"){var e=[];for(var g=0,h=b.length;g<h;++g){var i=f(a,b[g]);if(!i&&d.original)return d.original.apply(window,arguments);e.push(i)}c&&c.apply(null,e)}else{if(typeof b=="string"){var j=f(a,b);return!j&&d.original?d.original.apply(window,arguments):(c&&c(),j)}if(d.original)return d.original.apply(window,arguments)}},e=function(a,b){if(b.indexOf("!")!==-1){var c=b.split("!");return e(a,c[0])+"!"+e(a,c[1])}if(b.charAt(0)=="."){var d=a.split("/").slice(0,-1).join("/");b=d+"/"+b;while(b.indexOf(".")!==-1&&f!=b){var f=b;b=b.replace(/\/\.\//,"/").replace(/[^\/]+\/\.\.\//,"")}}return b},f=function(a,b){b=e(a,b);var f=c.modules[b];if(!f)return null;if(typeof f=="function"){var g={},h={id:b,uri:"",exports:g,packaged:!0},i=function(a,c){return d(b,a,c)},j=f(i,g,h);return g=j||h.exports,c.modules[b]=g,g}return f};g(a)})(),define("ace/ace",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/dom","ace/lib/event","ace/editor","ace/edit_session","ace/undomanager","ace/virtual_renderer","ace/multi_select","ace/worker/worker_client","ace/keyboard/hash_handler","ace/keyboard/state_handler","ace/placeholder","ace/config","ace/theme/textmate"],function(a,b,c){"use strict",a("./lib/fixoldbrowsers");var d=a("./lib/dom"),e=a("./lib/event"),f=a("./editor").Editor,g=a("./edit_session").EditSession,h=a("./undomanager").UndoManager,i=a("./virtual_renderer").VirtualRenderer,j=a("./multi_select").MultiSelect;a("./worker/worker_client"),a("./keyboard/hash_handler"),a("./keyboard/state_handler"),a("./placeholder"),a("./config").init(),b.edit=function(b){typeof b=="string"&&(b=document.getElementById(b));var c=new g(d.getInnerText(b));c.setUndoManager(new h),b.innerHTML="";var k=new f(new i(b,a("./theme/textmate")));new j(k),k.setSession(c);var l={};return l.document=c,l.editor=k,k.resize(),e.addListener(window,"resize",function(){k.resize()}),b.env=l,k.env=l,k}}),define("ace/lib/fixoldbrowsers",["require","exports","module","ace/lib/regexp","ace/lib/es5-shim"],function(a,b,c){"use strict",a("./regexp"),a("./es5-shim")}),define("ace/lib/regexp",["require","exports","module"],function(a,b,c){function g(a){return(a.global?"g":"")+(a.ignoreCase?"i":"")+(a.multiline?"m":"")+(a.extended?"x":"")+(a.sticky?"y":"")}function h(a,b,c){if(Array.prototype.indexOf)return a.indexOf(b,c);for(var d=c||0;d<a.length;d++)if(a[d]===b)return d;return-1}"use strict";var d={exec:RegExp.prototype.exec,test:RegExp.prototype.test,match:String.prototype.match,replace:String.prototype.replace,split:String.prototype.split},e=d.exec.call(/()??/,"")[1]===undefined,f=function(){var a=/^/g;return d.test.call(a,""),!a.lastIndex}();RegExp.prototype.exec=function(a){var b=d.exec.apply(this,arguments),c,i;if(typeof a=="string"&&b){!e&&b.length>1&&h(b,"")>-1&&(i=RegExp(this.source,d.replace.call(g(this),"g","")),d.replace.call(a.slice(b.index),i,function(){for(var a=1;a<arguments.length-2;a++)arguments[a]===undefined&&(b[a]=undefined)}));if(this._xregexp&&this._xregexp.captureNames)for(var j=1;j<b.length;j++)c=this._xregexp.captureNames[j-1],c&&(b[c]=b[j]);!f&&this.global&&!b[0].length&&this.lastIndex>b.index&&this.lastIndex--}return b},f||(RegExp.prototype.test=function(a){var b=d.exec.call(this,a);return b&&this.global&&!b[0].length&&this.lastIndex>b.index&&this.lastIndex--,!!b})}),define("ace/lib/es5-shim",["require","exports","module"],function(a,b,c){function p(a){try{return Object.defineProperty(a,"sentinel",{}),"sentinel"in a}catch(b){}}Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=g.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,h=c.apply(f,d.concat(g.call(arguments)));return h!==null&&Object(h)===h?h:f}return c.apply(b,d.concat(g.call(arguments)))};return e});var d=Function.prototype.call,e=Array.prototype,f=Object.prototype,g=e.slice,h=d.bind(f.toString),i=d.bind(f.hasOwnProperty),j,k,l,m,n;if(n=i(f,"__defineGetter__"))j=d.bind(f.__defineGetter__),k=d.bind(f.__defineSetter__),l=d.bind(f.__lookupGetter__),m=d.bind(f.__lookupSetter__);Array.isArray||(Array.isArray=function(b){return h(b)=="[object Array]"}),Array.prototype.forEach||(Array.prototype.forEach=function(b){var c=G(this),d=arguments[1],e=0,f=c.length>>>0;if(h(b)!="[object Function]")throw new TypeError;while(e<f)e in c&&b.call(d,c[e],e,c),e++}),Array.prototype.map||(Array.prototype.map=function(b){var c=G(this),d=c.length>>>0,e=Array(d),f=arguments[1];if(h(b)!="[object Function]")throw new TypeError;for(var g=0;g<d;g++)g in c&&(e[g]=b.call(f,c[g],g,c));return e}),Array.prototype.filter||(Array.prototype.filter=function(b){var c=G(this),d=c.length>>>0,e=[],f=arguments[1];if(h(b)!="[object Function]")throw new TypeError;for(var g=0;g<d;g++)g in c&&b.call(f,c[g],g,c)&&e.push(c[g]);return e}),Array.prototype.every||(Array.prototype.every=function(b){var c=G(this),d=c.length>>>0,e=arguments[1];if(h(b)!="[object Function]")throw new TypeError;for(var f=0;f<d;f++)if(f in c&&!b.call(e,c[f],f,c))return!1;return!0}),Array.prototype.some||(Array.prototype.some=function(b){var c=G(this),d=c.length>>>0,e=arguments[1];if(h(b)!="[object Function]")throw new TypeError;for(var f=0;f<d;f++)if(f in c&&b.call(e,c[f],f,c))return!0;return!1}),Array.prototype.reduce||(Array.prototype.reduce=function(b){var c=G(this),d=c.length>>>0;if(h(b)!="[object Function]")throw new TypeError;if(!d&&arguments.length==1)throw new TypeError;var e=0,f;if(arguments.length>=2)f=arguments[1];else do{if(e in c){f=c[e++];break}if(++e>=d)throw new TypeError}while(!0);for(;e<d;e++)e in c&&(f=b.call(void 0,f,c[e],e,c));return f}),Array.prototype.reduceRight||(Array.prototype.reduceRight=function(b){var c=G(this),d=c.length>>>0;if(h(b)!="[object Function]")throw new TypeError;if(!d&&arguments.length==1)throw new TypeError;var e,f=d-1;if(arguments.length>=2)e=arguments[1];else do{if(f in c){e=c[f--];break}if(--f<0)throw new TypeError}while(!0);do f in this&&(e=b.call(void 0,e,c[f],f,c));while(f--);return e}),Array.prototype.indexOf||(Array.prototype.indexOf=function(b){var c=G(this),d=c.length>>>0;if(!d)return-1;var e=0;arguments.length>1&&(e=E(arguments[1])),e=e>=0?e:Math.max(0,d+e);for(;e<d;e++)if(e in c&&c[e]===b)return e;return-1}),Array.prototype.lastIndexOf||(Array.prototype.lastIndexOf=function(b){var c=G(this),d=c.length>>>0;if(!d)return-1;var e=d-1;arguments.length>1&&(e=Math.min(e,E(arguments[1]))),e=e>=0?e:d-Math.abs(e);for(;e>=0;e--)if(e in c&&b===c[e])return e;return-1}),Object.getPrototypeOf||(Object.getPrototypeOf=function(b){return b.__proto__||(b.constructor?b.constructor.prototype:f)});if(!Object.getOwnPropertyDescriptor){var o="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function(b,c){if(typeof b!="object"&&typeof b!="function"||b===null)throw new TypeError(o+b);if(!i(b,c))return;var d,e,g;d={enumerable:!0,configurable:!0};if(n){var h=b.__proto__;b.__proto__=f;var e=l(b,c),g=m(b,c);b.__proto__=h;if(e||g)return e&&(d.get=e),g&&(d.set=g),d}return d.value=b[c],d}}Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(b){return Object.keys(b)}),Object.create||(Object.create=function(b,c){var d;if(b===null)d={__proto__:null};else{if(typeof b!="object")throw new TypeError("typeof prototype["+typeof b+"] != 'object'");var e=function(){};e.prototype=b,d=new e,d.__proto__=b}return c!==void 0&&Object.defineProperties(d,c),d});if(Object.defineProperty){var q=p({}),r=typeof document=="undefined"||p(document.createElement("div"));if(!q||!r)var s=Object.defineProperty}if(!Object.defineProperty||s){var t="Property description must be an object: ",u="Object.defineProperty called on non-object: ",v="getters & setters can not be defined on this javascript engine";Object.defineProperty=function(b,c,d){if(typeof b!="object"&&typeof b!="function"||b===null)throw new TypeError(u+b);if(typeof d!="object"&&typeof d!="function"||d===null)throw new TypeError(t+d);if(s)try{return s.call(Object,b,c,d)}catch(e){}if(i(d,"value"))if(n&&(l(b,c)||m(b,c))){var g=b.__proto__;b.__proto__=f,delete b[c],b[c]=d.value,b.__proto__=g}else b[c]=d.value;else{if(!n)throw new TypeError(v);i(d,"get")&&j(b,c,d.get),i(d,"set")&&k(b,c,d.set)}return b}}Object.defineProperties||(Object.defineProperties=function(b,c){for(var d in c)i(c,d)&&Object.defineProperty(b,d,c[d]);return b}),Object.seal||(Object.seal=function(b){return b}),Object.freeze||(Object.freeze=function(b){return b});try{Object.freeze(function(){})}catch(w){Object.freeze=function(b){return function(c){return typeof c=="function"?c:b(c)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(b){return b}),Object.isSealed||(Object.isSealed=function(b){return!1}),Object.isFrozen||(Object.isFrozen=function(b){return!1}),Object.isExtensible||(Object.isExtensible=function(b){if(Object(b)===b)throw new TypeError;var c="";while(i(b,c))c+="?";b[c]=!0;var d=i(b,c);return delete b[c],d});if(!Object.keys){var x=!0,y=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],z=y.length;for(var A in{toString:null})x=!1;Object.keys=function H(a){if(typeof a!="object"&&typeof a!="function"||a===null)throw new TypeError("Object.keys called on a non-object");var H=[];for(var b in a)i(a,b)&&H.push(b);if(x)for(var c=0,d=z;c<d;c++){var e=y[c];i(a,e)&&H.push(e)}return H}}if(!Date.prototype.toISOString||(new Date(-621987552e5)).toISOString().indexOf("-000001")===-1)Date.prototype.toISOString=function(){var b,c,d,e;if(!isFinite(this))throw new RangeError;b=[this.getUTCMonth()+1,this.getUTCDate(),this.getUTCHours(),this.getUTCMinutes(),this.getUTCSeconds()],e=this.getUTCFullYear(),e=(e<0?"-":e>9999?"+":"")+("00000"+Math.abs(e)).slice(0<=e&&e<=9999?-4:-6),c=b.length;while(c--)d=b[c],d<10&&(b[c]="0"+d);return e+"-"+b.slice(0,2).join("-")+"T"+b.slice(2).join(":")+"."+("000"+this.getUTCMilliseconds()).slice(-3)+"Z"};Date.now||(Date.now=function(){return(new Date).getTime()}),Date.prototype.toJSON||(Date.prototype.toJSON=function(b){if(typeof this.toISOString!="function")throw new TypeError;return this.toISOString()}),Date.parse("+275760-09-13T00:00:00.000Z")!==864e13&&(Date=function(a){var b=function e(b,c,d,f,g,h,i){var j=arguments.length;if(this instanceof a){var k=j==1&&String(b)===b?new a(e.parse(b)):j>=7?new a(b,c,d,f,g,h,i):j>=6?new a(b,c,d,f,g,h):j>=5?new a(b,c,d,f,g):j>=4?new a(b,c,d,f):j>=3?new a(b,c,d):j>=2?new a(b,c):j>=1?new a(b):new a;return k.constructor=e,k}return a.apply(this,arguments)},c=new RegExp("^(\\d{4}|[+-]\\d{6})(?:-(\\d{2})(?:-(\\d{2})(?:T(\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.(\\d{3}))?)?(?:Z|(?:([-+])(\\d{2}):(\\d{2})))?)?)?)?$");for(var d in a)b[d]=a[d];return b.now=a.now,b.UTC=a.UTC,b.prototype=a.prototype,b.prototype.constructor=b,b.parse=function(d){var e=c.exec(d);if(e){e.shift();for(var f=1;f<7;f++)e[f]=+(e[f]||(f<3?1:0)),f==1&&e[f]--;var g=+e.pop(),h=+e.pop(),i=e.pop(),j=0;if(i){if(h>23||g>59)return NaN;j=(h*60+g)*6e4*(i=="+"?-1:1)}var k=+e[0];return 0<=k&&k<=99?(e[0]=k+400,a.UTC.apply(this,e)+j-126227808e5):a.UTC.apply(this,e)+j}return a.parse.apply(this,arguments)},b}(Date));var B=" \n\f\r Â áš€á Žâ€€â€â€‚         âŸã€€\u2028\u2029";if(!String.prototype.trim||B.trim()){B="["+B+"]";var C=new RegExp("^"+B+B+"*"),D=new RegExp(B+B+"*$");String.prototype.trim=function(){return String(this).replace(C,"").replace(D,"")}}var E=function(a){return a=+a,a!==a?a=0:a!==0&&a!==1/0&&a!==-Infinity&&(a=(a>0||-1)*Math.floor(Math.abs(a))),a},F="a"[0]!="a",G=function(a){if(a==null)throw new TypeError;return F&&typeof a=="string"&&a?a.split(""):Object(a)}}),define("ace/lib/dom",["require","exports","module"],function(a,b,c){"use strict";var d="http://www.w3.org/1999/xhtml";b.createElement=function(a,b){return document.createElementNS?document.createElementNS(b||d,a):document.createElement(a)},b.setText=function(a,b){a.innerText!==undefined&&(a.innerText=b),a.textContent!==undefined&&(a.textContent=b)},b.hasCssClass=function(a,b){var c=a.className.split(/\s+/g);return c.indexOf(b)!==-1},b.addCssClass=function(a,c){b.hasCssClass(a,c)||(a.className+=" "+c)},b.removeCssClass=function(a,b){var c=a.className.split(/\s+/g);for(;;){var d=c.indexOf(b);if(d==-1)break;c.splice(d,1)}a.className=c.join(" ")},b.toggleCssClass=function(a,b){var c=a.className.split(/\s+/g),d=!0;for(;;){var e=c.indexOf(b);if(e==-1)break;d=!1,c.splice(e,1)}return d&&c.push(b),a.className=c.join(" "),d},b.setCssClass=function(a,c,d){d?b.addCssClass(a,c):b.removeCssClass(a,c)},b.hasCssString=function(a,b){var c=0,d;b=b||document;if(b.createStyleSheet&&(d=b.styleSheets)){while(c<d.length)if(d[c++].owningElement.id===a)return!0}else if(d=b.getElementsByTagName("style"))while(c<d.length)if(d[c++].id===a)return!0;return!1},b.importCssString=function(c,e,f){f=f||document;if(e&&b.hasCssString(e,f))return null;var g;if(f.createStyleSheet)g=f.createStyleSheet(),g.cssText=c,e&&(g.owningElement.id=e);else{g=f.createElementNS?f.createElementNS(d,"style"):f.createElement("style"),g.appendChild(f.createTextNode(c)),e&&(g.id=e);var h=f.getElementsByTagName("head")[0]||f.documentElement;h.appendChild(g)}},b.importCssStylsheet=function(a,c){if(c.createStyleSheet)c.createStyleSheet(a);else{var d=b.createElement("link");d.rel="stylesheet",d.href=a;var e=c.getElementsByTagName("head")[0]||c.documentElement;e.appendChild(d)}},b.getInnerWidth=function(a){return parseInt(b.computedStyle(a,"paddingLeft"),10)+parseInt(b.computedStyle(a,"paddingRight"),10)+a.clientWidth},b.getInnerHeight=function(a){return parseInt(b.computedStyle(a,"paddingTop"),10)+parseInt(b.computedStyle(a,"paddingBottom"),10)+a.clientHeight},window.pageYOffset!==undefined?(b.getPageScrollTop=function(){return window.pageYOffset},b.getPageScrollLeft=function(){return window.pageXOffset}):(b.getPageScrollTop=function(){return document.body.scrollTop},b.getPageScrollLeft=function(){return document.body.scrollLeft}),window.getComputedStyle?b.computedStyle=function(a,b){return b?(window.getComputedStyle(a,"")||{})[b]||"":window.getComputedStyle(a,"")||{}}:b.computedStyle=function(a,b){return b?a.currentStyle[b]:a.currentStyle},b.scrollbarWidth=function(a){var c=b.createElement("p");c.style.width="100%",c.style.minWidth="0px",c.style.height="200px";var d=b.createElement("div"),e=d.style;e.position="absolute",e.left="-10000px",e.overflow="hidden",e.width="200px",e.minWidth="0px",e.height="150px",d.appendChild(c);var f=a.body||a.documentElement;f.appendChild(d);var g=c.offsetWidth;e.overflow="scroll";var h=c.offsetWidth;return g==h&&(h=d.clientWidth),f.removeChild(d),g-h},b.setInnerHtml=function(a,b){var c=a.cloneNode(!1);return c.innerHTML=b,a.parentNode.replaceChild(c,a),c},b.setInnerText=function(a,b){var c=a.ownerDocument;c.body&&"textContent"in c.body?a.textContent=b:a.innerText=b},b.getInnerText=function(a){var b=a.ownerDocument;return b.body&&"textContent"in b.body?a.textContent:a.innerText||a.textContent||""},b.getParentWindow=function(a){return a.defaultView||a.parentWindow}}),define("ace/lib/event",["require","exports","module","ace/lib/keys","ace/lib/useragent","ace/lib/dom"],function(a,b,c){function g(a,b,c){var f=0;e.isOpera&&e.isMac?f=0|(b.metaKey?1:0)|(b.altKey?2:0)|(b.shiftKey?4:0)|(b.ctrlKey?8:0):f=0|(b.ctrlKey?1:0)|(b.altKey?2:0)|(b.shiftKey?4:0)|(b.metaKey?8:0);if(c in d.MODIFIER_KEYS){switch(d.MODIFIER_KEYS[c]){case"Alt":f=2;break;case"Shift":f=4;break;case"Ctrl":f=1;break;default:f=8}c=0}return f&8&&(c==91||c==93)&&(c=0),!!f||c in d.FUNCTION_KEYS||c in d.PRINTABLE_KEYS?a(b,f,c):!1}"use strict";var d=a("./keys"),e=a("./useragent"),f=a("./dom");b.addListener=function(a,b,c){if(a.addEventListener)return a.addEventListener(b,c,!1);if(a.attachEvent){var d=function(){c(window.event)};c._wrapper=d,a.attachEvent("on"+b,d)}},b.removeListener=function(a,b,c){if(a.removeEventListener)return a.removeEventListener(b,c,!1);a.detachEvent&&a.detachEvent("on"+b,c._wrapper||c)},b.stopEvent=function(a){return b.stopPropagation(a),b.preventDefault(a),!1},b.stopPropagation=function(a){a.stopPropagation?a.stopPropagation():a.cancelBubble=!0},b.preventDefault=function(a){a.preventDefault?a.preventDefault():a.returnValue=!1},b.getDocumentX=function(a){return a.clientX?a.clientX+f.getPageScrollLeft():a.pageX},b.getDocumentY=function(a){return a.clientY?a.clientY+f.getPageScrollTop():a.pageY},b.getButton=function(a){return a.type=="dblclick"?0:a.type=="contextmenu"?2:a.preventDefault?a.button:{1:0,2:2,4:1}[a.button]},document.documentElement.setCapture?b.capture=function(a,c,d){function e(a){return c(a),b.stopPropagation(a)}function g(e){c(e),f||(f=!0,d(e)),b.removeListener(a,"mousemove",c),b.removeListener(a,"mouseup",g),b.removeListener(a,"losecapture",g),a.releaseCapture()}var f=!1;b.addListener(a,"mousemove",c),b.addListener(a,"mouseup",g),b.addListener(a,"losecapture",g),a.setCapture()}:b.capture=function(a,b,c){function d(a){b(a),a.stopPropagation()}function e(a){b&&b(a),c&&c(a),document.removeEventListener("mousemove",d,!0),document.removeEventListener("mouseup",e,!0),a.stopPropagation()}document.addEventListener("mousemove",d,!0),document.addEventListener("mouseup",e,!0)},b.addMouseWheelListener=function(a,c){var d=8,e=function(a){a.wheelDelta!==undefined?a.wheelDeltaX!==undefined?(a.wheelX=-a.wheelDeltaX/d,a.wheelY=-a.wheelDeltaY/d):(a.wheelX=0,a.wheelY=-a.wheelDelta/d):a.axis&&a.axis==a.HORIZONTAL_AXIS?(a.wheelX=(a.detail||0)*5,a.wheelY=0):(a.wheelX=0,a.wheelY=(a.detail||0)*5),c(a)};b.addListener(a,"DOMMouseScroll",e),b.addListener(a,"mousewheel",e)},b.addMultiMouseDownListener=function(a,c,d,f,g){var h=0,i,j,k=function(a){h+=1,h==1&&(i=a.clientX,j=a.clientY,setTimeout(function(){h=0},f||600));var e=b.getButton(a)==c;if(!e||Math.abs(a.clientX-i)>5||Math.abs(a.clientY-j)>5)h=0;h==d&&(h=0,g(a));if(e)return b.preventDefault(a)};b.addListener(a,"mousedown",k),e.isOldIE&&b.addListener(a,"dblclick",k)},b.addCommandKeyListener=function(a,c){var d=b.addListener;if(e.isOldGecko||e.isOpera){var f=null;d(a,"keydown",function(a){f=a.keyCode}),d(a,"keypress",function(a){return g(c,a,f)})}else{var h=null;d(a,"keydown",function(a){return h=a.keyIdentifier||a.keyCode,g(c,a,a.keyCode)})}};if(window.postMessage){var h=1;b.nextTick=function(a,c){c=c||window;var d="zero-timeout-message-"+h;b.addListener(c,"message",function e(f){f.data==d&&(b.stopPropagation(f),b.removeListener(c,"message",e),a())}),c.postMessage(d,"*")}}else b.nextTick=function(a,b){b=b||window,window.setTimeout(a,0)}}),define("ace/lib/keys",["require","exports","module","ace/lib/oop"],function(a,b,c){"use strict";var d=a("./oop"),e=function(){var a={MODIFIER_KEYS:{16:"Shift",17:"Ctrl",18:"Alt",224:"Meta"},KEY_MODS:{ctrl:1,alt:2,option:2,shift:4,meta:8,command:8},FUNCTION_KEYS:{8:"Backspace",9:"Tab",13:"Return",19:"Pause",27:"Esc",32:"Space",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"Left",38:"Up",39:"Right",40:"Down",44:"Print",45:"Insert",46:"Delete",96:"Numpad0",97:"Numpad1",98:"Numpad2",99:"Numpad3",100:"Numpad4",101:"Numpad5",102:"Numpad6",103:"Numpad7",104:"Numpad8",105:"Numpad9",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"Numlock",145:"Scrolllock"},PRINTABLE_KEYS:{32:" ",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",59:";",61:"=",65:"a",66:"b",67:"c",68:"d",69:"e",70:"f",71:"g",72:"h",73:"i",74:"j",75:"k",76:"l",77:"m",78:"n",79:"o",80:"p",81:"q",82:"r",83:"s",84:"t",85:"u",86:"v",87:"w",88:"x",89:"y",90:"z",107:"+",109:"-",110:".",188:",",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:'"'}};for(var b in a.FUNCTION_KEYS){var c=a.FUNCTION_KEYS[b].toUpperCase();a[c]=parseInt(b,10)}return d.mixin(a,a.MODIFIER_KEYS),d.mixin(a,a.PRINTABLE_KEYS),d.mixin(a,a.FUNCTION_KEYS),a}();d.mixin(b,e),b.keyCodeToString=function(a){return(e[a]||String.fromCharCode(a)).toLowerCase()}}),define("ace/lib/oop",["require","exports","module"],function(a,b,c){"use strict",b.inherits=function(){var a=function(){};return function(b,c){a.prototype=c.prototype,b.super_=c.prototype,b.prototype=new a,b.prototype.constructor=b}}(),b.mixin=function(a,b){for(var c in b)a[c]=b[c]},b.implement=function(a,c){b.mixin(a,c)}}),define("ace/lib/useragent",["require","exports","module"],function(a,b,c){"use strict";var d=(navigator.platform.match(/mac|win|linux/i)||["other"])[0].toLowerCase(),e=navigator.userAgent;b.isWin=d=="win",b.isMac=d=="mac",b.isLinux=d=="linux",b.isIE=navigator.appName=="Microsoft Internet Explorer"&&parseFloat(navigator.userAgent.match(/MSIE ([0-9]+[\.0-9]+)/)[1]),b.isOldIE=b.isIE&&b.isIE<9,b.isGecko=b.isMozilla=window.controllers&&window.navigator.product==="Gecko",b.isOldGecko=b.isGecko&&parseInt((navigator.userAgent.match(/rv\:(\d+)/)||[])[1],10)<4,b.isOpera=window.opera&&Object.prototype.toString.call(window.opera)=="[object Opera]",b.isWebKit=parseFloat(e.split("WebKit/")[1])||undefined,b.isChrome=parseFloat(e.split(" Chrome/")[1])||undefined,b.isAIR=e.indexOf("AdobeAIR")>=0,b.isIPad=e.indexOf("iPad")>=0,b.isTouchPad=e.indexOf("TouchPad")>=0,b.OS={LINUX:"LINUX",MAC:"MAC",WINDOWS:"WINDOWS"},b.getOS=function(){return b.isMac?b.OS.MAC:b.isLinux?b.OS.LINUX:b.OS.WINDOWS}}),define("ace/editor",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/oop","ace/lib/lang","ace/lib/useragent","ace/keyboard/textinput","ace/mouse/mouse_handler","ace/mouse/fold_handler","ace/keyboard/keybinding","ace/edit_session","ace/search","ace/range","ace/lib/event_emitter","ace/commands/command_manager","ace/commands/default_commands"],function(a,b,c){"use strict",a("./lib/fixoldbrowsers");var d=a("./lib/oop"),e=a("./lib/lang"),f=a("./lib/useragent"),g=a("./keyboard/textinput").TextInput,h=a("./mouse/mouse_handler").MouseHandler,i=a("./mouse/fold_handler").FoldHandler,j=a("./keyboard/keybinding").KeyBinding,k=a("./edit_session").EditSession,l=a("./search").Search,m=a("./range").Range,n=a("./lib/event_emitter").EventEmitter,o=a("./commands/command_manager").CommandManager,p=a("./commands/default_commands").commands,q=function(a,b){var c=a.getContainerElement();this.container=c,this.renderer=a,this.textInput=new g(a.getTextAreaContainer(),this),this.keyBinding=new j(this),f.isIPad||(this.$mouseHandler=new h(this),new i(this)),this.$blockScrolling=0,this.$search=(new l).set({wrap:!0}),this.commands=new o(f.isMac?"mac":"win",p),this.setSession(b||new k(""))};(function(){d.implement(this,n),this.setKeyboardHandler=function(a){this.keyBinding.setKeyboardHandler(a)},this.getKeyboardHandler=function(){return this.keyBinding.getKeyboardHandler()},this.setSession=function(a){if(this.session==a)return;if(this.session){var b=this.session;this.session.removeEventListener("change",this.$onDocumentChange),this.session.removeEventListener("changeMode",this.$onChangeMode),this.session.removeEventListener("tokenizerUpdate",this.$onTokenizerUpdate),this.session.removeEventListener("changeTabSize",this.$onChangeTabSize),this.session.removeEventListener("changeWrapLimit",this.$onChangeWrapLimit),this.session.removeEventListener("changeWrapMode",this.$onChangeWrapMode),this.session.removeEventListener("onChangeFold",this.$onChangeFold),this.session.removeEventListener("changeFrontMarker",this.$onChangeFrontMarker),this.session.removeEventListener("changeBackMarker",this.$onChangeBackMarker),this.session.removeEventListener("changeBreakpoint",this.$onChangeBreakpoint),this.session.removeEventListener("changeAnnotation",this.$onChangeAnnotation),this.session.removeEventListener("changeOverwrite",this.$onCursorChange),this.session.removeEventListener("changeScrollTop",this.$onScrollTopChange),this.session.removeEventListener("changeLeftTop",this.$onScrollLeftChange);var c=this.session.getSelection();c.removeEventListener("changeCursor",this.$onCursorChange),c.removeEventListener("changeSelection",this.$onSelectionChange)}this.session=a,this.$onDocumentChange=this.onDocumentChange.bind(this),a.addEventListener("change",this.$onDocumentChange),this.renderer.setSession(a),this.$onChangeMode=this.onChangeMode.bind(this),a.addEventListener("changeMode",this.$onChangeMode),this.$onTokenizerUpdate=this.onTokenizerUpdate.bind(this),a.addEventListener("tokenizerUpdate",this.$onTokenizerUpdate),this.$onChangeTabSize=this.renderer.updateText.bind(this.renderer),a.addEventListener("changeTabSize",this.$onChangeTabSize),this.$onChangeWrapLimit=this.onChangeWrapLimit.bind(this),a.addEventListener("changeWrapLimit",this.$onChangeWrapLimit),this.$onChangeWrapMode=this.onChangeWrapMode.bind(this),a.addEventListener("changeWrapMode",this.$onChangeWrapMode),this.$onChangeFold=this.onChangeFold.bind(this),a.addEventListener("changeFold",this.$onChangeFold),this.$onChangeFrontMarker=this.onChangeFrontMarker.bind(this),this.session.addEventListener("changeFrontMarker",this.$onChangeFrontMarker),this.$onChangeBackMarker=this.onChangeBackMarker.bind(this),this.session.addEventListener("changeBackMarker",this.$onChangeBackMarker),this.$onChangeBreakpoint=this.onChangeBreakpoint.bind(this),this.session.addEventListener("changeBreakpoint",this.$onChangeBreakpoint),this.$onChangeAnnotation=this.onChangeAnnotation.bind(this),this.session.addEventListener("changeAnnotation",this.$onChangeAnnotation),this.$onCursorChange=this.onCursorChange.bind(this),this.session.addEventListener("changeOverwrite",this.$onCursorChange),this.$onScrollTopChange=this.onScrollTopChange.bind(this),this.session.addEventListener("changeScrollTop",this.$onScrollTopChange),this.$onScrollLeftChange=this.onScrollLeftChange.bind(this),this.session.addEventListener("changeScrollLeft",this.$onScrollLeftChange),this.selection=a.getSelection(),this.selection.addEventListener("changeCursor",this.$onCursorChange),this.$onSelectionChange=this.onSelectionChange.bind(this),this.selection.addEventListener("changeSelection",this.$onSelectionChange),this.onChangeMode(),this.$blockScrolling+=1,this.onCursorChange(),this.$blockScrolling-=1,this.onScrollTopChange(),this.onScrollLeftChange(),this.onSelectionChange(),this.onChangeFrontMarker(),this.onChangeBackMarker(),this.onChangeBreakpoint(),this.onChangeAnnotation(),this.session.getUseWrapMode()&&this.renderer.adjustWrapLimit(),this.renderer.updateFull(),this._emit("changeSession",{session:a,oldSession:b})},this.getSession=function(){return this.session},this.getSelection=function(){return this.selection},this.resize=function(){this.renderer.onResize()},this.setTheme=function(a){this.renderer.setTheme(a)},this.getTheme=function(){return this.renderer.getTheme()},this.setStyle=function(a){this.renderer.setStyle(a)},this.unsetStyle=function(a){this.renderer.unsetStyle(a)},this.setFontSize=function(a){this.container.style.fontSize=a,this.renderer.updateFontSize()},this.$highlightBrackets=function(){this.session.$bracketHighlight&&(this.session.removeMarker(this.session.$bracketHighlight),this.session.$bracketHighlight=null);if(this.$highlightPending)return;var a=this;this.$highlightPending=!0,setTimeout(function(){a.$highlightPending=!1;var b=a.session.findMatchingBracket(a.getCursorPosition());if(b){var c=new m(b.row,b.column,b.row,b.column+1);a.session.$bracketHighlight=a.session.addMarker(c,"ace_bracket","text")}},10)},this.focus=function(){var a=this;setTimeout(function(){a.textInput.focus()}),this.textInput.focus()},this.isFocused=function(){return this.textInput.isFocused()},this.blur=function(){this.textInput.blur()},this.onFocus=function(){this.renderer.showCursor(),this.renderer.visualizeFocus(),this._emit("focus")},this.onBlur=function(){this.renderer.hideCursor(),this.renderer.visualizeBlur(),this._emit("blur")},this.onDocumentChange=function(a){var b=a.data,c=b.range,d;c.start.row==c.end.row&&b.action!="insertLines"&&b.action!="removeLines"?d=c.end.row:d=Infinity,this.renderer.updateLines(c.start.row,d),this._emit("change",a),this.onCursorChange()},this.onTokenizerUpdate=function(a){var b=a.data;this.renderer.updateLines(b.first,b.last)},this.onScrollTopChange=function(){this.renderer.scrollToY(this.session.getScrollTop())},this.onScrollLeftChange=function(){this.renderer.scrollToX(this.session.getScrollLeft())},this.onCursorChange=function(){this.renderer.updateCursor(),this.$blockScrolling||this.renderer.scrollCursorIntoView(),this.renderer.moveTextAreaToCursor(this.textInput.getElement()),this.$highlightBrackets(),this.$updateHighlightActiveLine()},this.$updateHighlightActiveLine=function(){var a=this.getSession();a.$highlightLineMarker&&a.removeMarker(a.$highlightLineMarker),typeof this.$lastrow=="number"&&this.renderer.removeGutterDecoration(this.$lastrow,"ace_gutter_active_line"),a.$highlightLineMarker=null,this.$lastrow=null;if(this.getHighlightActiveLine()){var b=this.getCursorPosition(),c=this.session.getFoldLine(b.row);if(this.getSelectionStyle()!="line"||!this.selection.isMultiLine()){var d;c?d=new m(c.start.row,0,c.end.row+1,0):d=new m(b.row,0,b.row+1,0),a.$highlightLineMarker=a.addMarker(d,"ace_active_line","background")}this.renderer.addGutterDecoration(this.$lastrow=b.row,"ace_gutter_active_line")}},this.onSelectionChange=function(a){var b=this.getSession();b.$selectionMarker&&b.removeMarker(b.$selectionMarker),b.$selectionMarker=null;if(!this.selection.isEmpty()){var c=this.selection.getRange(),d=this.getSelectionStyle();b.$selectionMarker=b.addMarker(c,"ace_selection",d)}else this.$updateHighlightActiveLine();this.$highlightSelectedWord&&this.session.getMode().highlightSelection(this)},this.onChangeFrontMarker=function(){this.renderer.updateFrontMarkers()},this.onChangeBackMarker=function(){this.renderer.updateBackMarkers()},this.onChangeBreakpoint=function(){this.renderer.setBreakpoints(this.session.getBreakpoints())},this.onChangeAnnotation=function(){this.renderer.setAnnotations(this.session.getAnnotations())},this.onChangeMode=function(){this.renderer.updateText()},this.onChangeWrapLimit=function(){this.renderer.updateFull()},this.onChangeWrapMode=function(){this.renderer.onResize(!0)},this.onChangeFold=function(){this.$updateHighlightActiveLine(),this.renderer.updateFull()},this.getCopyText=function(){var a="";return this.selection.isEmpty()||(a=this.session.getTextRange(this.getSelectionRange())),this._emit("copy",a),a},this.onCut=function(){this.commands.exec("cut",this)},this.insert=function(a){var b=this.session,c=b.getMode(),d=this.getCursorPosition();if(this.getBehavioursEnabled()){var e=c.transformAction(b.getState(d.row),"insertion",this,b,a);e&&(a=e.text)}a=a.replace(" ",this.session.getTabString());if(!this.selection.isEmpty())d=this.session.remove(this.getSelectionRange()),this.clearSelection();else if(this.session.getOverwrite()){var f=new m.fromPoints(d,d);f.end.column+=a.length,this.session.remove(f)}this.clearSelection();var g=d.column,h=b.getState(d.row),i=c.checkOutdent(h,b.getLine(d.row),a),j=b.getLine(d.row),k=c.getNextLineIndent(h,j.slice(0,d.column),b.getTabString()),l=b.insert(d,a);e&&e.selection&&(e.selection.length==2?this.selection.setSelectionRange(new m(d.row,g+e.selection[0],d.row,g+e.selection[1])):this.selection.setSelectionRange(new m(d.row+e.selection[0],e.selection[1],d.row+e.selection[2],e.selection[3])));var h=b.getState(d.row);if(b.getDocument().isNewLine(a)){this.moveCursorTo(d.row+1,0);var n=b.getTabSize(),o=Number.MAX_VALUE;for(var p=d.row+1;p<=l.row;++p){var q=0;j=b.getLine(p);for(var r=0;r<j.length;++r)if(j.charAt(r)==" ")q+=n;else{if(j.charAt(r)!=" ")break;q+=1}/[^\s]/.test(j)&&(o=Math.min(q,o))}for(var p=d.row+1;p<=l.row;++p){var s=o;j=b.getLine(p);for(var r=0;r<j.length&&s>0;++r)j.charAt(r)==" "?s-=n:j.charAt(r)==" "&&(s-=1);b.remove(new m(p,0,p,r))}b.indentRows(d.row+1,l.row,k)}i&&c.autoOutdent(h,b,d.row)},this.onTextInput=function(a,b){b&&this._emit("paste",a),this.keyBinding.onTextInput(a,b)},this.onCommandKey=function(a,b,c){this.keyBinding.onCommandKey(a,b,c)},this.setOverwrite=function(a){this.session.setOverwrite(a)},this.getOverwrite=function(){return this.session.getOverwrite()},this.toggleOverwrite=function(){this.session.toggleOverwrite()},this.setScrollSpeed=function(a){this.$mouseHandler.setScrollSpeed(a)},this.getScrollSpeed=function(){return this.$mouseHandler.getScrollSpeed()},this.setDragDelay=function(a){this.$mouseHandler.setDragDelay(a)},this.getDragDelay=function(){return this.$mouseHandler.getDragDelay()},this.$selectionStyle="line",this.setSelectionStyle=function(a){if(this.$selectionStyle==a)return;this.$selectionStyle=a,this.onSelectionChange(),this._emit("changeSelectionStyle",{data:a})},this.getSelectionStyle=function(){return this.$selectionStyle},this.$highlightActiveLine=!0,this.setHighlightActiveLine=function(a){if(this.$highlightActiveLine==a)return;this.$highlightActiveLine=a,this.$updateHighlightActiveLine()},this.getHighlightActiveLine=function(){return this.$highlightActiveLine},this.$highlightSelectedWord=!0,this.setHighlightSelectedWord=function(a){if(this.$highlightSelectedWord==a)return;this.$highlightSelectedWord=a,a?this.session.getMode().highlightSelection(this):this.session.getMode().clearSelectionHighlight(this)},this.getHighlightSelectedWord=function(){return this.$highlightSelectedWord},this.setAnimatedScroll=function(a){this.renderer.setAnimatedScroll(a)},this.getAnimatedScroll=function(){return this.renderer.getAnimatedScroll()},this.setShowInvisibles=function(a){if(this.getShowInvisibles()==a)return;this.renderer.setShowInvisibles(a)},this.getShowInvisibles=function(){return this.renderer.getShowInvisibles()},this.setShowPrintMargin=function(a){this.renderer.setShowPrintMargin(a)},this.getShowPrintMargin=function(){return this.renderer.getShowPrintMargin()},this.setPrintMarginColumn=function(a){this.renderer.setPrintMarginColumn(a)},this.getPrintMarginColumn=function(){return this.renderer.getPrintMarginColumn()},this.$readOnly=!1,this.setReadOnly=function(a){this.$readOnly=a},this.getReadOnly=function(){return this.$readOnly},this.$modeBehaviours=!0,this.setBehavioursEnabled=function(a){this.$modeBehaviours=a},this.getBehavioursEnabled=function(){return this.$modeBehaviours},this.setShowFoldWidgets=function(a){var b=this.renderer.$gutterLayer;if(b.getShowFoldWidgets()==a)return;this.renderer.$gutterLayer.setShowFoldWidgets(a),this.$showFoldWidgets=a,this.renderer.updateFull()},this.getShowFoldWidgets=function(){return this.renderer.$gutterLayer.getShowFoldWidgets()},this.remove=function(a){this.selection.isEmpty()&&(a=="left"?this.selection.selectLeft():this.selection.selectRight());var b=this.getSelectionRange();if(this.getBehavioursEnabled()){var c=this.session,d=c.getState(b.start.row),e=c.getMode().transformAction(d,"deletion",this,c,b);e&&(b=e)}this.session.remove(b),this.clearSelection()},this.removeWordRight=function(){this.selection.isEmpty()&&this.selection.selectWordRight(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeWordLeft=function(){this.selection.isEmpty()&&this.selection.selectWordLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineStart=function(){this.selection.isEmpty()&&this.selection.selectLineStart(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineEnd=function(){this.selection.isEmpty()&&this.selection.selectLineEnd();var a=this.getSelectionRange();a.start.column==a.end.column&&a.start.row==a.end.row&&(a.end.column=0,a.end.row++),this.session.remove(a),this.clearSelection()},this.splitLine=function(){this.selection.isEmpty()||(this.session.remove(this.getSelectionRange()),this.clearSelection());var a=this.getCursorPosition();this.insert("\n"),this.moveCursorToPosition(a)},this.transposeLetters=function(){if(!this.selection.isEmpty())return;var a=this.getCursorPosition(),b=a.column;if(b===0)return;var c=this.session.getLine(a.row),d,e;b<c.length?(d=c.charAt(b)+c.charAt(b-1),e=new m(a.row,b-1,a.row,b+1)):(d=c.charAt(b-1)+c.charAt(b-2),e=new m(a.row,b-2,a.row,b)),this.session.replace(e,d)},this.toLowerCase=function(){var a=this.getSelectionRange();this.selection.isEmpty()&&this.selection.selectWord();var b=this.getSelectionRange(),c=this.session.getTextRange(b);this.session.replace(b,c.toLowerCase()),this.selection.setSelectionRange(a)},this.toUpperCase=function(){var a=this.getSelectionRange();this.selection.isEmpty()&&this.selection.selectWord();var b=this.getSelectionRange(),c=this.session.getTextRange(b);this.session.replace(b,c.toUpperCase()),this.selection.setSelectionRange(a)},this.indent=function(){var a=this.session,b=this.getSelectionRange();if(!(b.start.row<b.end.row||b.start.column<b.end.column)){var d;if(this.session.getUseSoftTabs()){var f=a.getTabSize(),g=this.getCursorPosition(),h=a.documentToScreenColumn(g.row,g.column),i=f-h%f;d=e.stringRepeat(" ",i)}else d=" ";return this.insert(d)}var c=this.$getSelectedRows();a.indentRows(c.first,c.last," ")},this.blockOutdent=function(){var a=this.session.getSelection();this.session.outdentRows(a.getRange())},this.toggleCommentLines=function(){var a=this.session.getState(this.getCursorPosition().row),b=this.$getSelectedRows();this.session.getMode().toggleCommentLines(a,this.session,b.first,b.last)},this.removeLines=function(){var a=this.$getSelectedRows(),b;a.first===0||a.last+1<this.session.getLength()?b=new m(a.first,0,a.last+1,0):b=new m(a.first-1,this.session.getLine(a.first-1).length,a.last,this.session.getLine(a.last).length),this.session.remove(b),this.clearSelection()},this.moveLinesDown=function(){this.$moveLines(function(a,b){return this.session.moveLinesDown(a,b)})},this.moveLinesUp=function(){this.$moveLines(function(a,b){return this.session.moveLinesUp(a,b)})},this.moveText=function(a,b){return this.$readOnly?null:this.session.moveText(a,b)},this.copyLinesUp=function(){this.$moveLines(function(a,b){return this.session.duplicateLines(a,b),0})},this.copyLinesDown=function(){this.$moveLines(function(a,b){return this.session.duplicateLines(a,b)})},this.$moveLines=function(a){var b=this.$getSelectedRows(),c=this.selection;if(!c.isMultiLine())var d=c.getRange(),e=c.isBackwards();var f=a.call(this,b.first,b.last);d?(d.start.row+=f,d.end.row+=f,c.setSelectionRange(d,e)):(c.setSelectionAnchor(b.last+f+1,0),c.$moveSelection(function(){c.moveCursorTo(b.first+f,0)}))},this.$getSelectedRows=function(){var a=this.getSelectionRange().collapseRows();return{first:a.start.row,last:a.end.row}},this.onCompositionStart=function(a){this.renderer.showComposition(this.getCursorPosition())},this.onCompositionUpdate=function(a){this.renderer.setCompositionText(a)},this.onCompositionEnd=function(){this.renderer.hideComposition()},this.getFirstVisibleRow=function(){return this.renderer.getFirstVisibleRow()},this.getLastVisibleRow=function(){return this.renderer.getLastVisibleRow()},this.isRowVisible=function(a){return a>=this.getFirstVisibleRow()&&a<=this.getLastVisibleRow()},this.isRowFullyVisible=function(a){return a>=this.renderer.getFirstFullyVisibleRow()&&a<=this.renderer.getLastFullyVisibleRow()},this.$getVisibleRowCount=function(){return this.renderer.getScrollBottomRow()-this.renderer.getScrollTopRow()+1},this.$getPageDownRow=function(){return this.renderer.getScrollBottomRow()},this.$getPageUpRow=function(){var a=this.renderer.getScrollTopRow(),b=this.renderer.getScrollBottomRow();return a-(b-a)},this.selectPageDown=function(){var a=this.$getPageDownRow()+Math.floor(this.$getVisibleRowCount()/2);this.scrollPageDown();var b=this.getSelection(),c=this.session.documentToScreenPosition(b.getSelectionLead()),d=this.session.screenToDocumentPosition(a,c.column);b.selectTo(d.row,d.column)},this.selectPageUp=function(){var a=this.renderer.getScrollTopRow()-this.renderer.getScrollBottomRow(),b=this.$getPageUpRow()+Math.round(a/2);this.scrollPageUp();var c=this.getSelection(),d=this.session.documentToScreenPosition(c.getSelectionLead()),e=this.session.screenToDocumentPosition(b,d.column);c.selectTo(e.row,e.column)},this.gotoPageDown=function(){var a=this.$getPageDownRow(),b=this.getCursorPositionScreen().column;this.scrollToRow(a),this.getSelection().moveCursorToScreen(a,b)},this.gotoPageUp=function(){var a=this.$getPageUpRow(),b=this.getCursorPositionScreen().column;this.scrollToRow(a),this.getSelection().moveCursorToScreen(a,b)},this.scrollPageDown=function(){this.scrollToRow(this.$getPageDownRow())},this.scrollPageUp=function(){this.renderer.scrollToRow(this.$getPageUpRow())},this.scrollToRow=function(a){this.renderer.scrollToRow(a)},this.scrollToLine=function(a,b){this.renderer.scrollToLine(a,b)},this.centerSelection=function(){var a=this.getSelectionRange(),b=Math.floor(a.start.row+(a.end.row-a.start.row)/2);this.renderer.scrollToLine(b,!0)},this.getCursorPosition=function(){return this.selection.getCursor()},this.getCursorPositionScreen=function(){return this.session.documentToScreenPosition(this.getCursorPosition())},this.getSelectionRange=function(){return this.selection.getRange()},this.selectAll=function(){this.$blockScrolling+=1,this.selection.selectAll(),this.$blockScrolling-=1},this.clearSelection=function(){this.selection.clearSelection()},this.moveCursorTo=function(a,b){this.selection.moveCursorTo(a,b)},this.moveCursorToPosition=function(a){this.selection.moveCursorToPosition(a)},this.jumpToMatching=function(){var a=this.getCursorPosition(),b=this.session.findMatchingBracket(a);b||(a.column+=1,b=this.session.findMatchingBracket(a)),b||(a.column-=2,b=this.session.findMatchingBracket(a)),b&&(this.clearSelection(),this.moveCursorTo(b.row,b.column))},this.gotoLine=function(a,b){this.selection.clearSelection(),this.session.unfold({row:a-1,column:b||0}),this.$blockScrolling+=1,this.moveCursorTo(a-1,b||0),this.$blockScrolling-=1,this.isRowFullyVisible(this.getCursorPosition().row)||this.scrollToLine(a,!0)},this.navigateTo=function(a,b){this.clearSelection(),this.moveCursorTo(a,b)},this.navigateUp=function(a){this.selection.clearSelection(),a=a||1,this.selection.moveCursorBy(-a,0)},this.navigateDown=function(a){this.selection.clearSelection(),a=a||1,this.selection.moveCursorBy(a,0)},this.navigateLeft=function(a){if(!this.selection.isEmpty()){var b=this.getSelectionRange().start;this.moveCursorToPosition(b)}else{a=a||1;while(a--)this.selection.moveCursorLeft()}this.clearSelection()},this.navigateRight=function(a){if(!this.selection.isEmpty()){var b=this.getSelectionRange().end;this.moveCursorToPosition(b)}else{a=a||1;while(a--)this.selection.moveCursorRight()}this.clearSelection()},this.navigateLineStart=function(){this.selection.moveCursorLineStart(),this.clearSelection()},this.navigateLineEnd=function(){this.selection.moveCursorLineEnd(),this.clearSelection()},this.navigateFileEnd=function(){this.selection.moveCursorFileEnd(),this.clearSelection()},this.navigateFileStart=function(){this.selection.moveCursorFileStart(),this.clearSelection()},this.navigateWordRight=function(){this.selection.moveCursorWordRight(),this.clearSelection()},this.navigateWordLeft=function(){this.selection.moveCursorWordLeft(),this.clearSelection()},this.replace=function(a,b){b&&this.$search.set(b);var c=this.$search.find(this.session),d=0;return c?(this.$tryReplace(c,a)&&(d=1),c!==null&&(this.selection.setSelectionRange(c),this.renderer.scrollSelectionIntoView(c.start,c.end)),d):d},this.replaceAll=function(a,b){b&&this.$search.set(b);var c=this.$search.findAll(this.session),d=0;if(!c.length)return d;var e=this.getSelectionRange();this.clearSelection(),this.selection.moveCursorTo(0,0),this.$blockScrolling+=1;for(var f=c.length-1;f>=0;--f)this.$tryReplace(c[f],a)&&d++;return this.selection.setSelectionRange(e),this.$blockScrolling-=1,d},this.$tryReplace=function(a,b){var c=this.session.getTextRange(a);return b=this.$search.replace(c,b),b!==null?(a.end=this.session.replace(a,b),a):null},this.getLastSearchOptions=function(){return this.$search.getOptions()},this.find=function(a,b){this.clearSelection(),b=b||{},b.needle=a,this.$search.set(b),this.$find()},this.findNext=function(a){a=a||{},typeof a.backwards=="undefined"&&(a.backwards=!1),this.$search.set(a),this.$find()},this.findPrevious=function(a){a=a||{},typeof a.backwards=="undefined"&&(a.backwards=!0),this.$search.set(a),this.$find()},this.$find=function(a){this.selection.isEmpty()||this.$search.set({needle:this.session.getTextRange(this.getSelectionRange())}),typeof a!="undefined"&&this.$search.set({backwards:a});var b=this.$search.find(this.session);if(b){this.session.unfold(b),this.$blockScrolling+=1,this.selection.setSelectionRange(b),this.$blockScrolling-=1;if(this.getAnimatedScroll()){var c=this.getCursorPosition();this.isRowFullyVisible(c.row)||this.scrollToLine(c.row,!0)}else this.renderer.scrollSelectionIntoView(b.start,b.end)}},this.undo=function(){this.session.getUndoManager().undo()},this.redo=function(){this.session.getUndoManager().redo()},this.destroy=function(){this.renderer.destroy()}}).call(q.prototype),b.Editor=q}),define("ace/lib/lang",["require","exports","module"],function(a,b,c){"use strict",b.stringReverse=function(a){return a.split("").reverse().join("")},b.stringRepeat=function(a,b){return(new Array(b+1)).join(a)};var d=/^\s\s*/,e=/\s\s*$/;b.stringTrimLeft=function(a){return a.replace(d,"")},b.stringTrimRight=function(a){return a.replace(e,"")},b.copyObject=function(a){var b={};for(var c in a)b[c]=a[c];return b},b.copyArray=function(a){var b=[];for(var c=0,d=a.length;c<d;c++)a[c]&&typeof a[c]=="object"?b[c]=this.copyObject(a[c]):b[c]=a[c];return b},b.deepCopy=function(a){if(typeof a!="object")return a;var b=a.constructor();for(var c in a)typeof a[c]=="object"?b[c]=this.deepCopy(a[c]):b[c]=a[c];return b},b.arrayToMap=function(a){var b={};for(var c=0;c<a.length;c++)b[a[c]]=1;return b},b.arrayRemove=function(a,b){for(var c=0;c<=a.length;c++)b===a[c]&&a.splice(c,1)},b.escapeRegExp=function(a){return a.replace(/([.*+?^${}()|[\]\/\\])/g,"\\$1")},b.deferredCall=function(a){var b=null,c=function(){b=null,a()},d=function(a){return d.cancel(),b=setTimeout(c,a||0),d};return d.schedule=d,d.call=function(){return this.cancel(),a(),d},d.cancel=function(){return clearTimeout(b),b=null,d},d}}),define("ace/keyboard/textinput",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/lib/dom"],function(a,b,c){"use strict";var d=a("../lib/event"),e=a("../lib/useragent"),f=a("../lib/dom"),g=function(a,b){function l(){try{c.select()}catch(a){}}function m(a){if(!i){var d=a||c.value;if(d){d.charCodeAt(d.length-1)==g.charCodeAt(0)?(d=d.slice(0,-1),d&&b.onTextInput(d,j)):b.onTextInput(d,j);if(!v())return!1}}i=!1,j=!1,c.value=g,l()}function v(){return document.activeElement===c}var c=f.createElement("textarea");e.isTouchPad&&c.setAttribute("x-palm-disable-auto-cap",!0),c.style.left="-10000px",c.style.position="fixed",a.insertBefore(c,a.firstChild);var g=String.fromCharCode(0);m();var h=!1,i=!1,j=!1,k="",n=function(a){setTimeout(function(){h||m(a.data)},0)},o=function(a){if(e.isOldIE&&c.value.charCodeAt(0)>128)return;setTimeout(function(){h||m()},0)},p=function(a){h=!0,b.onCompositionStart(),e.isGecko||setTimeout(q,0)},q=function(){if(!h)return;b.onCompositionUpdate(c.value)},r=function(a){h=!1,b.onCompositionEnd()},s=function(a){i=!0;var d=b.getCopyText();d?c.value=d:a.preventDefault(),l(),setTimeout(function(){m()},0)},t=function(a){i=!0;var d=b.getCopyText();d?(c.value=d,b.onCut()):a.preventDefault(),l(),setTimeout(function(){m()},0)};d.addCommandKeyListener(c,b.onCommandKey.bind(b));if(e.isOldIE){var u={13:1,27:1};d.addListener(c,"keyup",function(a){h&&(!c.value||u[a.keyCode])&&setTimeout(r,0);if((c.value.charCodeAt(0)|0)<129)return;h?q():p()})}"onpropertychange"in c&&!("oninput"in c)?d.addListener(c,"propertychange",o):d.addListener(c,"input",n),d.addListener(c,"paste",function(a){j=!0,a.clipboardData&&a.clipboardData.getData?(m(a.clipboardData.getData("text/plain")),a.preventDefault()):o()}),"onbeforecopy"in c&&typeof clipboardData!="undefined"?(d.addListener(c,"beforecopy",function(a){var c=b.getCopyText();c?clipboardData.setData("Text",c):a.preventDefault()}),d.addListener(a,"keydown",function(a){if(a.ctrlKey&&a.keyCode==88){var c=b.getCopyText();c&&(clipboardData.setData("Text",c),b.onCut()),d.preventDefault(a)}})):(d.addListener(c,"copy",s),d.addListener(c,"cut",t)),d.addListener(c,"compositionstart",p),e.isGecko&&d.addListener(c,"text",q),e.isWebKit&&d.addListener(c,"keyup",q),d.addListener(c,"compositionend",r),d.addListener(c,"blur",function(){b.onBlur()}),d.addListener(c,"focus",function(){b.onFocus(),l()}),this.focus=function(){b.onFocus(),l(),c.focus()},this.blur=function(){c.blur()},this.isFocused=v,this.getElement=function(){return c},this.onContextMenu=function(a,b){a&&(k||(k=c.style.cssText),c.style.cssText="position:fixed; z-index:1000;left:"+(a.x-2)+"px; top:"+(a.y-2)+"px;"),b&&(c.value="")},this.onContextMenuClose=function(){setTimeout(function(){k&&(c.style.cssText=k,k=""),m()},0)}};b.TextInput=g}),define("ace/mouse/mouse_handler",["require","exports","module","ace/lib/event","ace/mouse/default_handlers","ace/mouse/default_gutter_handler","ace/mouse/mouse_event"],function(a,b,c){"use strict";var d=a("../lib/event"),e=a("./default_handlers").DefaultHandlers,f=a("./default_gutter_handler").GutterHandler,g=a("./mouse_event").MouseEvent,h=function(a){this.editor=a,new e(a),new f(a),d.addListener(a.container,"mousedown",function(b){return a.focus(),d.preventDefault(b)}),d.addListener(a.container,"selectstart",function(a){return d.preventDefault(a)});var b=a.renderer.getMouseEventTarget();d.addListener(b,"mousedown",this.onMouseEvent.bind(this,"mousedown")),d.addListener(b,"click",this.onMouseEvent.bind(this,"click")),d.addListener(b,"mousemove",this.onMouseMove.bind(this,"mousemove")),d.addMultiMouseDownListener(b,0,2,500,this.onMouseEvent.bind(this,"dblclick")),d.addMultiMouseDownListener(b,0,3,600,this.onMouseEvent.bind(this,"tripleclick")),d.addMultiMouseDownListener(b,0,4,600,this.onMouseEvent.bind(this,"quadclick")),d.addMouseWheelListener(a.container,this.onMouseWheel.bind(this,"mousewheel"));var c=a.renderer.$gutter;d.addListener(c,"mousedown",this.onMouseEvent.bind(this,"guttermousedown")),d.addListener(c,"click",this.onMouseEvent.bind(this,"gutterclick")),d.addListener(c,"dblclick",this.onMouseEvent.bind(this,"gutterdblclick")),d.addListener(c,"mousemove",this.onMouseMove.bind(this,"gutter"))};(function(){this.$scrollSpeed=1,this.setScrollSpeed=function(a){this.$scrollSpeed=a},this.getScrollSpeed=function(){return this.$scrollSpeed},this.onMouseEvent=function(a,b){this.editor._emit(a,new g(b,this.editor))},this.$dragDelay=250,this.setDragDelay=function(a){this.$dragDelay=a},this.getDragDelay=function(){return this.$dragDelay},this.onMouseMove=function(a,b){var c=this.editor._eventRegistry&&this.editor._eventRegistry.mousemove;if(!c||!c.length)return;this.editor._emit(a,new g(b,this.editor))},this.onMouseWheel=function(a,b){var c=new g(b,this.editor);c.speed=this.$scrollSpeed*2,c.wheelX=b.wheelX,c.wheelY=b.wheelY,this.editor._emit(a,c)}}).call(h.prototype),b.MouseHandler=h}),define("ace/mouse/default_handlers",["require","exports","module","ace/lib/event","ace/lib/dom","ace/lib/browser_focus"],function(a,b,c){function k(a){this.editor=a,this.$clickSelection=null,this.browserFocus=new f,a.setDefaultHandler("mousedown",this.onMouseDown.bind(this)),a.setDefaultHandler("dblclick",this.onDoubleClick.bind(this)),a.setDefaultHandler("tripleclick",this.onTripleClick.bind(this)),a.setDefaultHandler("quadclick",this.onQuadClick.bind(this)),a.setDefaultHandler("mousewheel",this.onScroll.bind(this))}function l(a,b,c,d){return Math.sqrt(Math.pow(c-a,2)+Math.pow(d-b,2))}"use strict";var d=a("../lib/event"),e=a("../lib/dom"),f=a("../lib/browser_focus").BrowserFocus,g=0,h=1,i=2,j=5;(function(){this.onMouseDown=function(a){function C(b){a.getShiftKey()?m.selection.selectToPosition(b):n.$clickSelection||(m.moveCursorToPosition(b),m.selection.clearSelection()),q=h}var b=a.inSelection(),c=a.pageX,f=a.pageY,k=a.getDocumentPosition(),m=this.editor,n=this,o=m.getSelectionRange(),p=o.isEmpty(),q=g;if(b&&(!this.browserFocus.isFocused()||(new Date).getTime()-this.browserFocus.lastFocus<20||!m.isFocused())){m.focus();return}var r=a.getButton();if(r!==0){p&&m.moveCursorToPosition(k),r==2&&(m.textInput.onContextMenu({x:a.clientX,y:a.clientY},p),d.capture(m.container,function(){},m.textInput.onContextMenuClose));return}b||C(k);var s=c,t=f,u=(new Date).getTime(),v,w,x,y=function(a){s=d.getDocumentX(a),t=d.getDocumentY(a)},z=function(a){clearInterval(F),q==g?C(k):q==i&&A(a),n.$clickSelection=null,q=g},A=function(a){e.removeCssClass(m.container,"ace_dragging"),m.session.removeMarker(x),m.$mouseHandler.$clickSelection||v||(m.moveCursorToPosition(k),m.selection.clearSelection());if(!v)return;if(w.contains(v.row,v.column)){v=null;return}m.clearSelection();if(a&&(a.ctrlKey||a.altKey))var b=m.session,c=b.insert(v,b.getTextRange(w));else var c=m.moveText(w,v);if(!c){v=null;return}m.selection.setSelectionRange(c)},B=function(){if(q==g){var a=l(c,f,s,t),b=(new Date).getTime();if(a>j){q=h;var d=m.renderer.screenToTextCoordinates(s,t);C(d)}else if(b-u>m.getDragDelay()){q=i,w=m.getSelectionRange();var k=m.getSelectionStyle();x=m.session.addMarker(w,"ace_selection",k),m.clearSelection(),e.addCssClass(m.container,"ace_dragging")}}q==i?E():q==h&&D()},D=function(){var a,b=m.renderer.screenToTextCoordinates(s,t);n.$clickSelection?n.$clickSelection.contains(b.row,b.column)?m.selection.setSelectionRange(n.$clickSelection):(n.$clickSelection.compare(b.row,b.column)==-1?a=n.$clickSelection.end:a=n.$clickSelection.start,m.selection.setSelectionAnchor(a.row,a.column),m.selection.selectToPosition(b)):m.selection.selectToPosition(b),m.renderer.scrollCursorIntoView()},E=function(){v=m.renderer.screenToTextCoordinates(s,t),m.moveCursorToPosition(v)};d.capture(m.container,y,z);var F=setInterval(B,20);return a.preventDefault()},this.onDoubleClick=function(a){var b=a.getDocumentPosition(),c=this.editor;c.moveCursorToPosition(b),c.selection.selectWord(),this.$clickSelection=c.getSelectionRange()},this.onTripleClick=function(a){var b=a.getDocumentPosition(),c=this.editor;c.moveCursorToPosition(b),c.selection.selectLine(),this.$clickSelection=c.getSelectionRange()},this.onQuadClick=function(a){var b=this.editor;b.selectAll(),this.$clickSelection=b.getSelectionRange()},this.onScroll=function(a){var b=this.editor;b.renderer.scrollBy(a.wheelX*a.speed,a.wheelY*a.speed);if(b.renderer.isScrollableBy(a.wheelX*a.speed,a.wheelY*a.speed))return a.preventDefault()}}).call(k.prototype),b.DefaultHandlers=k}),define("ace/lib/browser_focus",["require","exports","module","ace/lib/oop","ace/lib/event","ace/lib/event_emitter"],function(a,b,c){"use strict";var d=a("./oop"),e=a("./event"),f=a("./event_emitter").EventEmitter,g=function(a){a=a||window,this.lastFocus=(new Date).getTime(),this._isFocused=!0;var b=this;"onfocusin"in a.document?(e.addListener(a.document,"focusin",function(a){b._setFocused(!0)}),e.addListener(a.document,"focusout",function(a){b._setFocused(!!a.toElement)})):(e.addListener(a,"blur",function(a){b._setFocused(!1)}),e.addListener(a,"focus",function(a){b._setFocused(!0)}))};(function(){d.implement(this,f),this.isFocused=function(){return this._isFocused},this._setFocused=function(a){if(this._isFocused==a)return;a&&(this.lastFocus=(new Date).getTime()),this._isFocused=a,this._emit("changeFocus")}}).call(g.prototype),b.BrowserFocus=g}),define("ace/lib/event_emitter",["require","exports","module"],function(a,b,c){"use strict";var d={};d._emit=d._dispatchEvent=function(a,b){this._eventRegistry=this._eventRegistry||{},this._defaultHandlers=this._defaultHandlers||{};var c=this._eventRegistry[a]||[],d=this._defaultHandlers[a];if(!c.length&&!d)return;b=b||{},b.type=a,b.stopPropagation||(b.stopPropagation=function(){this.propagationStopped=!0}),b.preventDefault||(b.preventDefault=function(){this.defaultPrevented=!0});for(var e=0;e<c.length;e++){c[e](b);if(b.propagationStopped)break}d&&!b.defaultPrevented&&d(b)},d.setDefaultHandler=function(a,b){this._defaultHandlers=this._defaultHandlers||{};if(this._defaultHandlers[a])throw new Error("The default handler for '"+a+"' is already set");this._defaultHandlers[a]=b},d.on=d.addEventListener=function(a,b){this._eventRegistry=this._eventRegistry||{};var c=this._eventRegistry[a];if(!c)var c=this._eventRegistry[a]=[];c.indexOf(b)==-1&&c.push(b)},d.removeListener=d.removeEventListener=function(a,b){this._eventRegistry=this._eventRegistry||{};var c=this._eventRegistry[a];if(!c)return;var d=c.indexOf(b);d!==-1&&c.splice(d,1)},d.removeAllListeners=function(a){this._eventRegistry&&(this._eventRegistry[a]=[])},b.EventEmitter=d}),define("ace/mouse/default_gutter_handler",["require","exports","module"],function(a,b,c){function d(a){a.setDefaultHandler("gutterclick",function(b){var c=b.getDocumentPosition().row,d=a.session.selection;d.moveCursorTo(c,0),d.selectLine()})}"use strict",b.GutterHandler=d}),define("ace/mouse/mouse_event",["require","exports","module","ace/lib/event"],function(a,b,c){"use strict";var d=a("../lib/event"),e=b.MouseEvent=function(a,b){this.domEvent=a,this.editor=b,this.pageX=d.getDocumentX(a),this.pageY=d.getDocumentY(a),this.clientX=a.clientX,this.clientY=a.clientY,this.$pos=null,this.$inSelection=null,this.propagationStopped=!1,this.defaultPrevented=!1};(function(){this.stopPropagation=function(){d.stopPropagation(this.domEvent),this.propagationStopped=!0},this.preventDefault=function(){d.preventDefault(this.domEvent),this.defaultPrevented=!0},this.stop=function(){this.stopPropagation(),this.preventDefault()},this.getDocumentPosition=function(){if(this.$pos)return this.$pos;var a=d.getDocumentX(this.domEvent),b=d.getDocumentY(this.domEvent);return this.$pos=this.editor.renderer.screenToTextCoordinates(a,b),this.$pos},this.inSelection=function(){if(this.$inSelection!==null)return this.$inSelection;var a=this.editor;if(a.getReadOnly())this.$inSelection=!1;else{var b=a.getSelectionRange();if(b.isEmpty())this.$inSelection=!1;else{var c=this.getDocumentPosition();this.$inSelection=b.contains(c.row,c.column)}}return this.$inSelection},this.getButton=function(){return d.getButton(this.domEvent)},this.getShiftKey=function(){return this.domEvent.shiftKey},this.getAccelKey=function(){return this.domEvent.ctrlKey||this.domEvent.metaKey}}).call(e.prototype)}),define("ace/mouse/fold_handler",["require","exports","module"],function(a,b,c){function d(a){a.on("click",function(b){var c=b.getDocumentPosition(),d=a.session,e=d.getFoldAt(c.row,c.column,1);e&&(b.getAccelKey()?d.removeFold(e):d.expandFold(e),b.stop())}),a.on("gutterclick",function(b){if(b.domEvent.target.className.indexOf("ace_fold-widget")!=-1){var c=b.getDocumentPosition().row;a.session.onFoldWidgetClick(c,b.domEvent),b.stop()}})}"use strict",b.FoldHandler=d}),define("ace/keyboard/keybinding",["require","exports","module","ace/lib/keys","ace/lib/event","ace/commands/default_commands"],function(a,b,c){"use strict";var d=a("../lib/keys"),e=a("../lib/event");a("../commands/default_commands");var f=function(a){this.$editor=a,this.$data={},this.$handlers=[this]};(function(){this.setKeyboardHandler=function(a){if(this.$handlers[this.$handlers.length-1]==a)return;this.$data={},this.$handlers=a?[this,a]:[this]},this.addKeyboardHandler=function(a){this.removeKeyboardHandler(a),this.$handlers.push(a)},this.removeKeyboardHandler=function(a){var b=this.$handlers.indexOf(a);return b==-1?!1:(this.$handlers.splice(b,1),!0)},this.getKeyboardHandler=function(){return this.$handlers[this.$handlers.length-1]},this.$callKeyboardHandlers=function(a,b,c,d){var f;for(var g=this.$handlers.length;g--;){f=this.$handlers[g].handleKeyboard(this.$data,a,b,c,d);if(f&&f.command)break}if(!f||!f.command)return!1;var h=!1,i=this.$editor.commands;return f.command!="null"?h=i.exec(f.command,this.$editor,f.args):h=!0,h&&d&&e.stopEvent(d),h},this.handleKeyboard=function(a,b,c){return{command:this.$editor.commands.findKeyCommand(b,c)}},this.onCommandKey=function(a,b,c){var e=d.keyCodeToString(c);this.$callKeyboardHandlers(b,e,c,a)},this.onTextInput=function(a,b){var c=!1;!b&&a.length==1&&(c=this.$callKeyboardHandlers(0,a)),c||this.$editor.commands.exec("insertstring",this.$editor,a)}}).call(f.prototype),b.KeyBinding=f}),define("ace/commands/default_commands",["require","exports","module","ace/lib/lang"],function(a,b,c){function e(a,b){return{win:a,mac:b}}"use strict";var d=a("../lib/lang");b.commands=[{name:"selectall",bindKey:e("Ctrl-A","Command-A"),exec:function(a){a.selectAll()},readOnly:!0},{name:"centerselection",bindKey:e(null,"Ctrl-L"),exec:function(a){a.centerSelection()},readOnly:!0},{name:"gotoline",bindKey:e("Ctrl-L","Command-L"),exec:function(a){var b=parseInt(prompt("Enter line number:"),10);isNaN(b)||a.gotoLine(b)},readOnly:!0},{name:"fold",bindKey:e("Alt-L","Alt-L"),exec:function(a){a.session.toggleFold(!1)},readOnly:!0},{name:"unfold",bindKey:e("Alt-Shift-L","Alt-Shift-L"),exec:function(a){a.session.toggleFold(!0)},readOnly:!0},{name:"foldall",bindKey:e("Alt-0","Alt-0"),exec:function(a){a.session.foldAll()},readOnly:!0},{name:"unfoldall",bindKey:e("Alt-Shift-0","Alt-Shift-0"),exec:function(a){a.session.unfold()},readOnly:!0},{name:"findnext",bindKey:e("Ctrl-K","Command-G"),exec:function(a){a.findNext()},readOnly:!0},{name:"findprevious",bindKey:e("Ctrl-Shift-K","Command-Shift-G"),exec:function(a){a.findPrevious()},readOnly:!0},{name:"find",bindKey:e("Ctrl-F","Command-F"),exec:function(a){var b=prompt("Find:",a.getCopyText());a.find(b)},readOnly:!0},{name:"overwrite",bindKey:e("Insert","Insert"),exec:function(a){a.toggleOverwrite()},readOnly:!0},{name:"selecttostart",bindKey:e("Ctrl-Shift-Home|Alt-Shift-Up","Command-Shift-Up"),exec:function(a){a.getSelection().selectFileStart()},readOnly:!0},{name:"gotostart",bindKey:e("Ctrl-Home|Ctrl-Up","Command-Home|Command-Up"),exec:function(a){a.navigateFileStart()},readOnly:!0},{name:"selectup",bindKey:e("Shift-Up","Shift-Up"),exec:function(a){a.getSelection().selectUp()},multiSelectAction:"forEach",readOnly:!0},{name:"golineup",bindKey:e("Up","Up|Ctrl-P"),exec:function(a,b){a.navigateUp(b.times)},multiSelectAction:"forEach",readOnly:!0},{name:"selecttoend",bindKey:e("Ctrl-Shift-End|Alt-Shift-Down","Command-Shift-Down"),exec:function(a){a.getSelection().selectFileEnd()},multiSelectAction:"forEach",readOnly:!0},{name:"gotoend",bindKey:e("Ctrl-End|Ctrl-Down","Command-End|Command-Down"),exec:function(a){a.navigateFileEnd()},multiSelectAction:"forEach",readOnly:!0},{name:"selectdown",bindKey:e("Shift-Down","Shift-Down"),exec:function(a){a.getSelection().selectDown()},multiSelectAction:"forEach",readOnly:!0},{name:"golinedown",bindKey:e("Down","Down|Ctrl-N"),exec:function(a,b){a.navigateDown(b.times)},multiSelectAction:"forEach",readOnly:!0},{name:"selectwordleft",bindKey:e("Ctrl-Shift-Left","Option-Shift-Left"),exec:function(a){a.getSelection().selectWordLeft()},multiSelectAction:"forEach",readOnly:!0},{name:"gotowordleft",bindKey:e("Ctrl-Left","Option-Left"),exec:function(a){a.navigateWordLeft()},multiSelectAction:"forEach",readOnly:!0},{name:"selecttolinestart",bindKey:e("Alt-Shift-Left","Command-Shift-Left"),exec:function(a){a.getSelection().selectLineStart()},multiSelectAction:"forEach",readOnly:!0},{name:"gotolinestart",bindKey:e("Alt-Left|Home","Command-Left|Home|Ctrl-A"),exec:function(a){a.navigateLineStart()},multiSelectAction:"forEach",readOnly:!0},{name:"selectleft",bindKey:e("Shift-Left","Shift-Left"),exec:function(a){a.getSelection().selectLeft()},multiSelectAction:"forEach",readOnly:!0},{name:"gotoleft",bindKey:e("Left","Left|Ctrl-B"),exec:function(a,b){a.navigateLeft(b.times)},multiSelectAction:"forEach",readOnly:!0},{name:"selectwordright",bindKey:e("Ctrl-Shift-Right","Option-Shift-Right"),exec:function(a){a.getSelection().selectWordRight()},multiSelectAction:"forEach",readOnly:!0},{name:"gotowordright",bindKey:e("Ctrl-Right","Option-Right"),exec:function(a){a.navigateWordRight()},multiSelectAction:"forEach",readOnly:!0},{name:"selecttolineend",bindKey:e("Alt-Shift-Right","Command-Shift-Right"),exec:function(a){a.getSelection().selectLineEnd()},multiSelectAction:"forEach",readOnly:!0},{name:"gotolineend",bindKey:e("Alt-Right|End","Command-Right|End|Ctrl-E"),exec:function(a){a.navigateLineEnd()},multiSelectAction:"forEach",readOnly:!0},{name:"selectright",bindKey:e("Shift-Right","Shift-Right"),exec:function(a){a.getSelection().selectRight()},multiSelectAction:"forEach",readOnly:!0},{name:"gotoright",bindKey:e("Right","Right|Ctrl-F"),exec:function(a,b){a.navigateRight(b.times)},multiSelectAction:"forEach",readOnly:!0},{name:"selectpagedown",bindKey:e("Shift-PageDown","Shift-PageDown"),exec:function(a){a.selectPageDown()},readOnly:!0},{name:"pagedown",bindKey:e(null,"PageDown"),exec:function(a){a.scrollPageDown()},readOnly:!0},{name:"gotopagedown",bindKey:e("PageDown","Option-PageDown|Ctrl-V"),exec:function(a){a.gotoPageDown()},readOnly:!0},{name:"selectpageup",bindKey:e("Shift-PageUp","Shift-PageUp"),exec:function(a){a.selectPageUp()},readOnly:!0},{name:"pageup",bindKey:e(null,"PageUp"),exec:function(a){a.scrollPageUp()},readOnly:!0},{name:"gotopageup",bindKey:e("PageUp","Option-PageUp"),exec:function(a){a.gotoPageUp()},readOnly:!0},{name:"selectlinestart",bindKey:e("Shift-Home","Shift-Home"),exec:function(a){a.getSelection().selectLineStart()},multiSelectAction:"forEach",readOnly:!0},{name:"selectlineend",bindKey:e("Shift-End","Shift-End"),exec:function(a){a.getSelection().selectLineEnd()},multiSelectAction:"forEach",readOnly:!0},{name:"togglerecording",bindKey:e("Ctrl-Alt-E","Command-Option-E"),exec:function(a){a.commands.toggleRecording()},readOnly:!0},{name:"replaymacro",bindKey:e("Ctrl-Shift-E","Command-Shift-E"),exec:function(a){a.commands.replay(a)},readOnly:!0},{name:"jumptomatching",bindKey:e("Ctrl-Shift-P","Ctrl-Shift-P"),exec:function(a){a.jumpToMatching()},multiSelectAction:"forEach",readOnly:!0},{name:"cut",exec:function(a){var b=a.getSelectionRange();a._emit("cut",b),a.selection.isEmpty()||(a.session.remove(b),a.clearSelection())},multiSelectAction:"forEach"},{name:"removeline",bindKey:e("Ctrl-D","Command-D"),exec:function(a){a.removeLines()},multiSelectAction:"forEach"},{name:"togglecomment",bindKey:e("Ctrl-7","Command-7"),exec:function(a){a.toggleCommentLines()},multiSelectAction:"forEach"},{name:"replace",bindKey:e("Ctrl-R","Command-Option-F"),exec:function(a){var b=prompt("Find:",a.getCopyText());if(!b)return;var c=prompt("Replacement:");if(!c)return;a.replace(c,{needle:b})}},{name:"replaceall",bindKey:e("Ctrl-Shift-R","Command-Shift-Option-F"),exec:function(a){var b=prompt("Find:");if(!b)return;var c=prompt("Replacement:");if(!c)return;a.replaceAll(c,{needle:b})}},{name:"undo",bindKey:e("Ctrl-Z","Command-Z"),exec:function(a){a.undo()}},{name:"redo",bindKey:e("Ctrl-Shift-Z|Ctrl-Y","Command-Shift-Z|Command-Y"),exec:function(a){a.redo()}},{name:"copylinesup",bindKey:e("Ctrl-Alt-Up","Command-Option-Up"),exec:function(a){a.copyLinesUp()}},{name:"movelinesup",bindKey:e("Alt-Up","Option-Up"),exec:function(a){a.moveLinesUp()}},{name:"copylinesdown",bindKey:e("Ctrl-Alt-Down","Command-Option-Down"),exec:function(a){a.copyLinesDown()}},{name:"movelinesdown",bindKey:e("Alt-Down","Option-Down"),exec:function(a){a.moveLinesDown()}},{name:"del",bindKey:e("Delete","Delete|Ctrl-D"),exec:function(a){a.remove("right")},multiSelectAction:"forEach"},{name:"backspace",bindKey:e("Command-Backspace|Option-Backspace|Shift-Backspace|Backspace","Ctrl-Backspace|Command-Backspace|Shift-Backspace|Backspace|Ctrl-H"),exec:function(a){a.remove("left")},multiSelectAction:"forEach"},{name:"removetolinestart",bindKey:e("Alt-Backspace","Command-Backspace"),exec:function(a){a.removeToLineStart()},multiSelectAction:"forEach"},{name:"removetolineend",bindKey:e("Alt-Delete","Ctrl-K"),exec:function(a){a.removeToLineEnd()},multiSelectAction:"forEach"},{name:"removewordleft",bindKey:e("Ctrl-Backspace","Alt-Backspace|Ctrl-Alt-Backspace"),exec:function(a){a.removeWordLeft()},multiSelectAction:"forEach"},{name:"removewordright",bindKey:e("Ctrl-Delete","Alt-Delete"),exec:function(a){a.removeWordRight()},multiSelectAction:"forEach"},{name:"outdent",bindKey:e("Shift-Tab","Shift-Tab"),exec:function(a){a.blockOutdent()},multiSelectAction:"forEach"},{name:"indent",bindKey:e("Tab","Tab"),exec:function(a){a.indent()},multiSelectAction:"forEach"},{name:"insertstring",exec:function(a,b){a.insert(b)},multiSelectAction:"forEach"},{name:"inserttext",exec:function(a,b){a.insert(d.stringRepeat(b.text||"",b.times||1))},multiSelectAction:"forEach"},{name:"splitline",bindKey:e(null,"Ctrl-O"),exec:function(a){a.splitLine()},multiSelectAction:"forEach"},{name:"transposeletters",bindKey:e("Ctrl-T","Ctrl-T"),exec:function(a){a.transposeLetters()},multiSelectAction:function(a){a.transposeSelections(1)}},{name:"touppercase",bindKey:e("Ctrl-U","Ctrl-U"),exec:function(a){a.toUpperCase()},multiSelectAction:"forEach"},{name:"tolowercase",bindKey:e("Ctrl-Shift-U","Ctrl-Shift-U"),exec:function(a){a.toLowerCase()},multiSelectAction:"forEach"}]}),define("ace/edit_session",["require","exports","module","ace/config","ace/lib/oop","ace/lib/lang","ace/lib/net","ace/lib/event_emitter","ace/selection","ace/mode/text","ace/range","ace/document","ace/background_tokenizer","ace/edit_session/folding","ace/edit_session/bracket_match"],function(a,b,c){"use strict";var d=a("./config"),e=a("./lib/oop"),f=a("./lib/lang"),g=a("./lib/net"),h=a("./lib/event_emitter").EventEmitter,i=a("./selection").Selection,j=a("./mode/text").Mode,k=a("./range").Range,l=a("./document").Document,m=a("./background_tokenizer").BackgroundTokenizer,n=function(a,b){this.$modified=!0,this.$breakpoints=[],this.$frontMarkers={},this.$backMarkers={},this.$markerId=1,this.$rowCache=[],this.$wrapData=[],this.$foldData=[],this.$undoSelect=!0,this.$foldData.toString=function(){var a="";return this.forEach(function(b){a+="\n"+b.toString()}),a},a instanceof l?this.setDocument(a):this.setDocument(new l(a)),this.selection=new i(this),b?this.setMode(b):this.setMode(new j)};(function(){function q(a){return a<4352?!1:a>=4352&&a<=4447||a>=4515&&a<=4519||a>=4602&&a<=4607||a>=9001&&a<=9002||a>=11904&&a<=11929||a>=11931&&a<=12019||a>=12032&&a<=12245||a>=12272&&a<=12283||a>=12288&&a<=12350||a>=12353&&a<=12438||a>=12441&&a<=12543||a>=12549&&a<=12589||a>=12593&&a<=12686||a>=12688&&a<=12730||a>=12736&&a<=12771||a>=12784&&a<=12830||a>=12832&&a<=12871||a>=12880&&a<=13054||a>=13056&&a<=19903||a>=19968&&a<=42124||a>=42128&&a<=42182||a>=43360&&a<=43388||a>=44032&&a<=55203||a>=55216&&a<=55238||a>=55243&&a<=55291||a>=63744&&a<=64255||a>=65040&&a<=65049||a>=65072&&a<=65106||a>=65108&&a<=65126||a>=65128&&a<=65131||a>=65281&&a<=65376||a>=65504&&a<=65510}e.implement(this,h),this.setDocument=function(a){if(this.doc)throw new Error("Document is already set");this.doc=a,a.on("change",this.onChange.bind(this)),this.on("changeFold",this.onChangeFold.bind(this)),this.bgTokenizer&&(this.bgTokenizer.setDocument(this.getDocument()),this.bgTokenizer.start(0))},this.getDocument=function(){return this.doc},this.$resetRowCache=function(a){if(a==0){this.$rowCache=[];return}var b=this.$rowCache;for(var c=0;c<b.length;c++)if(b[c].docRow>=a){b.splice(c,b.length);return}},this.onChangeFold=function(a){var b=a.data;this.$resetRowCache(b.start.row)},this.onChange=function(a){var b=a.data;this.$modified=!0,this.$resetRowCache(b.range.start.row);var c=this.$updateInternalDataOnChange(a);!this.$fromUndo&&this.$undoManager&&!b.ignore&&(this.$deltasDoc.push(b),c&&c.length!=0&&this.$deltasFold.push({action:"removeFolds",folds:c}),this.$informUndoManager.schedule()),this.bgTokenizer.start(b.range.start.row),this._emit("change",a)},this.setValue=function(a){this.doc.setValue(a),this.selection.moveCursorTo(0,0),this.selection.clearSelection(),this.$resetRowCache(0),this.$deltas=[],this.$deltasDoc=[],this.$deltasFold=[],this.getUndoManager().reset()},this.getValue=this.toString=function(){return this.doc.getValue()},this.getSelection=function(){return this.selection},this.getState=function(a){return this.bgTokenizer.getState(a)},this.getTokens=function(a,b){return this.bgTokenizer.getTokens(a,b)},this.getTokenAt=function(a,b){var c=this.bgTokenizer.getTokens(a,a)[0].tokens,d,e=0;if(b==null)f=c.length-1,e=this.getLine(a).length;else for(var f=0;f<c.length;f++){e+=c[f].value.length;if(e>=b)break}return d=c[f],d?(d.index=f,d.start=e-d.value.length,d):null},this.setUndoManager=function(a){this.$undoManager=a,this.$resetRowCache(0),this.$deltas=[],this.$deltasDoc=[],this.$deltasFold=[],this.$informUndoManager&&this.$informUndoManager.cancel();if(a){var b=this;this.$syncInformUndoManager=function(){b.$informUndoManager.cancel(),b.$deltasFold.length&&(b.$deltas.push({group:"fold",deltas:b.$deltasFold}),b.$deltasFold=[]),b.$deltasDoc.length&&(b.$deltas.push({group:"doc",deltas:b.$deltasDoc}),b.$deltasDoc=[]),b.$deltas.length>0&&a.execute({action:"aceupdate",args:[b.$deltas,b]}),b.$deltas=[]},this.$informUndoManager=f.deferredCall(this.$syncInformUndoManager)}},this.$defaultUndoManager={undo:function(){},redo:function(){},reset:function(){}},this.getUndoManager=function(){return this.$undoManager||this.$defaultUndoManager},this.getTabString=function(){return this.getUseSoftTabs()?f.stringRepeat(" ",this.getTabSize()):" "},this.$useSoftTabs=!0,this.setUseSoftTabs=function(a){if(this.$useSoftTabs===a)return;this.$useSoftTabs=a},this.getUseSoftTabs=function(){return this.$useSoftTabs},this.$tabSize=4,this.setTabSize=function(a){if(isNaN(a)||this.$tabSize===a)return;this.$modified=!0,this.$tabSize=a,this._emit("changeTabSize")},this.getTabSize=function(){return this.$tabSize},this.isTabStop=function(a){return this.$useSoftTabs&&a.column%this.$tabSize==0},this.$overwrite=!1,this.setOverwrite=function(a){if(this.$overwrite==a)return;this.$overwrite=a,this._emit("changeOverwrite")},this.getOverwrite=function(){return this.$overwrite},this.toggleOverwrite=function(){this.setOverwrite(!this.$overwrite)},this.getBreakpoints=function(){return this.$breakpoints},this.setBreakpoints=function(a){this.$breakpoints=[];for(var b=0;b<a.length;b++)this.$breakpoints[a[b]]=!0;this._emit("changeBreakpoint",{})},this.clearBreakpoints=function(){this.$breakpoints=[],this._emit("changeBreakpoint",{})},this.setBreakpoint=function(a){this.$breakpoints[a]=!0,this._emit("changeBreakpoint",{})},this.clearBreakpoint=function(a){delete this.$breakpoints[a],this._emit("changeBreakpoint",{})},this.getBreakpoints=function(){return this.$breakpoints},this.addMarker=function(a,b,c,d){var e=this.$markerId++,f={range:a,type:c||"line",renderer:typeof c=="function"?c:null,clazz:b,inFront:!!d};return d?(this.$frontMarkers[e]=f,this._emit("changeFrontMarker")):(this.$backMarkers[e]=f,this._emit("changeBackMarker")),e},this.removeMarker=function(a){var b=this.$frontMarkers[a]||this.$backMarkers[a];if(!b)return;var c=b.inFront?this.$frontMarkers:this.$backMarkers;b&&(delete c[a],this._emit(b.inFront?"changeFrontMarker":"changeBackMarker"))},this.getMarkers=function(a){return a?this.$frontMarkers:this.$backMarkers},this.setAnnotations=function(a){this.$annotations={};for(var b=0;b<a.length;b++){var c=a[b],d=c.row;this.$annotations[d]?this.$annotations[d].push(c):this.$annotations[d]=[c]}this._emit("changeAnnotation",{})},this.getAnnotations=function(){return this.$annotations||{}},this.clearAnnotations=function(){this.$annotations={},this._emit("changeAnnotation",{})},this.$detectNewLine=function(a){var b=a.match(/^.*?(\r?\n)/m);b?this.$autoNewLine=b[1]:this.$autoNewLine="\n"},this.getWordRange=function(a,b){var c=this.getLine(a),d=!1;b>0&&(d=!!c.charAt(b-1).match(this.tokenRe)),d||(d=!!c.charAt(b).match(this.tokenRe));var e=d?this.tokenRe:this.nonTokenRe,f=b;if(f>0){do f--;while(f>=0&&c.charAt(f).match(e));f++}var g=b;while(g<c.length&&c.charAt(g).match(e))g++;return new k(a,f,a,g)},this.getAWordRange=function(a,b){var c=this.getWordRange(a,b),d=this.getLine(c.end.row);while(d.charAt(c.end.column).match(/[ \t]/))c.end.column+=1;return c},this.setNewLineMode=function(a){this.doc.setNewLineMode(a)},this.getNewLineMode=function(){return this.doc.getNewLineMode()},this.$useWorker=!0,this.setUseWorker=function(a){if(this.$useWorker==a)return;this.$useWorker=a,this.$stopWorker(),a&&this.$startWorker()},this.getUseWorker=function(){return this.$useWorker},this.onReloadTokenizer=function(a){var b=a.data;this.bgTokenizer.start(b.first),this._emit("tokenizerUpdate",a)},this.$modes={},this._loadMode=function(b,c){function i(a){if(e.$modes[b])return c(e.$modes[b]);e.$modes[b]=new a.Mode,e._emit("loadmode",{name:b,mode:e.$modes[b]}),c(e.$modes[b])}function j(a){if(!d.get("packaged"))return a();var c=b.split("/").pop(),e=d.get("modePath")+"/mode-"+c+d.get("suffix");g.loadScript(e,a)}if(this.$modes[b])return c(this.$modes[b]);var e=this,f;try{f=a(b)}catch(h){}if(f)return i(f);j(function(){a([b],i)})},this.$mode=null,this.$origMode=null,this.setMode=function(a){this.$origMode=a;if(typeof a=="string"){var b=this;this._loadMode(a,function(c){if(b.$origMode!==a)return;b.setMode(c)});return}if(this.$mode===a)return;this.$mode=a,this.$stopWorker(),this.$useWorker&&this.$startWorker();var c=a.getTokenizer();if(c.addEventListener!==undefined){var d=this.onReloadTokenizer.bind(this);c.addEventListener("update",d)}if(!this.bgTokenizer){this.bgTokenizer=new m(c);var b=this;this.bgTokenizer.addEventListener("update",function(a){b._emit("tokenizerUpdate",a)})}else this.bgTokenizer.setTokenizer(c);this.bgTokenizer.setDocument(this.getDocument()),this.bgTokenizer.start(0),this.tokenRe=a.tokenRe,this.nonTokenRe=a.nonTokenRe,this.$setFolding(a.foldingRules),this._emit("changeMode")},this.$stopWorker=function(){this.$worker&&this.$worker.terminate(),this.$worker=null},this.$startWorker=function(){if(typeof Worker!="undefined"&&!a.noWorker)try{this.$worker=this.$mode.createWorker(this)}catch(b){console.log("Could not load worker"),console.log(b),this.$worker=null}else this.$worker=null},this.getMode=function(){return this.$mode},this.$scrollTop=0,this.setScrollTop=function(a){a=Math.round(Math.max(0,a));if(this.$scrollTop===a)return;this.$scrollTop=a,this._emit("changeScrollTop",a)},this.getScrollTop=function(){return this.$scrollTop},this.$scrollLeft=0,this.setScrollLeft=function(a){a=Math.round(Math.max(0,a));if(this.$scrollLeft===a)return;this.$scrollLeft=a,this._emit("changeScrollLeft",a)},this.getScrollLeft=function(){return this.$scrollLeft},this.getWidth=function(){return this.$computeWidth(),this.width},this.getScreenWidth=function(){return this.$computeWidth(),this.screenWidth},this.$computeWidth=function(a){if(this.$modified||a){this.$modified=!1;var b=this.doc.getAllLines(),c=0,d=0;for(var e=0;e<b.length;e++){var f=this.getFoldLine(e),g,h;g=b[e];if(f){var i=f.range.end;g=this.getFoldDisplayLine(f),e=i.row}h=g.length,c=Math.max(c,h),this.$useWrapMode||(d=Math.max(d,this.$getStringScreenWidth(g)[0]))}this.width=c,this.$useWrapMode?this.screenWidth=this.$wrapLimit:this.screenWidth=d}},this.getLine=function(a){return this.doc.getLine(a)},this.getLines=function(a,b){return this.doc.getLines(a,b)},this.getLength=function(){return this.doc.getLength()},this.getTextRange=function(a){return this.doc.getTextRange(a)},this.insert=function(a,b){return this.doc.insert(a,b)},this.remove=function(a){return this.doc.remove(a)},this.undoChanges=function(a,b){if(!a.length)return;this.$fromUndo=!0;var c=null;for(var d=a.length-1;d!=-1;d--){var e=a[d];e.group=="doc"?(this.doc.revertDeltas(e.deltas),c=this.$getUndoSelection(e.deltas,!0,c)):e.deltas.forEach(function(a){this.addFolds(a.folds)},this)}return this.$fromUndo=!1,c&&this.$undoSelect&&!b&&this.selection.setSelectionRange(c),c},this.redoChanges=function(a,b){if(!a.length)return;this.$fromUndo=!0;var c=null;for(var d=0;d<a.length;d++){var e=a[d];e.group=="doc"&&(this.doc.applyDeltas(e.deltas),c=this.$getUndoSelection(e.deltas,!1,c))}return this.$fromUndo=!1,c&&this.$undoSelect&&!b&&this.selection.setSelectionRange(c),c},this.setUndoSelect=function(a){this.$undoSelect=a},this.$getUndoSelection=function(a,b,c){function d(a){var c=a.action=="insertText"||a.action=="insertLines";return b?!c:c}var e=a[0],f,g,h=!1;d(e)?(f=e.range.clone(),h=!0):(f=k.fromPoints(e.range.start,e.range.start),h=!1);for(var i=1;i<a.length;i++)e=a[i],d(e)?(g=e.range.start,f.compare(g.row,g.column)==-1&&f.setStart(e.range.start),g=e.range.end,f.compare(g.row,g.column)==1&&f.setEnd(e.range.end),h=!0):(g=e.range.start,f.compare(g.row,g.column)==-1&&(f=k.fromPoints(e.range.start,e.range.start)),h=!1);if(c!=null){var j=c.compareRange(f);j==1?f.setStart(c.start):j==-1&&f.setEnd(c.end)}return f},this.replace=function(a,b){return this.doc.replace(a,b)},this.moveText=function(a,b){var c=this.getTextRange(a);this.remove(a);var d=b.row,e=b.column;!a.isMultiLine()&&a.start.row==d&&a.end.column<e&&(e-=c.length);if(a.isMultiLine()&&a.end.row<d){var f=this.doc.$split(c);d-=f.length-1}var g=d+a.end.row-a.start.row,h=a.isMultiLine()?a.end.column:e+a.end.column-a.start.column,i=new k(d,e,g,h);return this.insert(i.start,c),i},this.indentRows=function(a,b,c){c=c.replace(/\t/g,this.getTabString());for(var d=a;d<=b;d++)this.insert({row:d,column:0},c)},this.outdentRows=function(a){var b=a.collapseRows(),c=new k(0,0,0,0),d=this.getTabSize();for(var e=b.start.row;e<=b.end.row;++e){var f=this.getLine(e);c.start.row=e,c.end.row=e;for(var g=0;g<d;++g)if(f.charAt(g)!=" ")break;g<d&&f.charAt(g)==" "?(c.start.column=g,c.end.column=g+1):(c.start.column=0,c.end.column=g),this.remove(c)}},this.moveLinesUp=function(a,b){if(a<=0)return 0;var c=this.doc.removeLines(a,b);return this.doc.insertLines(a-1,c),-1},this.moveLinesDown=function(a,b){if(b>=this.doc.getLength()-1)return 0;var c=this.doc.removeLines(a,b);return this.doc.insertLines(a+1,c),1},this.duplicateLines=function(a,b){var a=this.$clipRowToDocument(a),b=this.$clipRowToDocument(b),c=this.getLines(a,b);this.doc.insertLines(a,c);var d=b-a+1;return d},this.$clipRowToDocument=function(a){return Math.max(0,Math.min(a,this.doc.getLength()-1))},this.$clipColumnToRow=function(a,b){return b<0?0:Math.min(this.doc.getLine(a).length,b)},this.$clipPositionToDocument=function(a,b){b=Math.max(0,b);if(a<0)a=0,b=0;else{var c=this.doc.getLength();a>=c?(a=c-1,b=this.doc.getLine(c-1).length):b=Math.min(this.doc.getLine(a).length,b)}return{row:a,column:b}},this.$clipRangeToDocument=function(a){a.start.row<0?(a.start.row=0,a.start.column=0):a.start.column=this.$clipColumnToRow(a.start.row,a.start.column);var b=this.doc.getLength()-1;return a.end.row>b?(a.end.row=b,a.end.column=this.doc.getLine(b).length):a.end.column=this.$clipColumnToRow(a.end.row,a.end.column),a},this.$wrapLimit=80,this.$useWrapMode=!1,this.$wrapLimitRange={min:null,max:null},this.setUseWrapMode=function(a){if(a!=this.$useWrapMode){this.$useWrapMode=a,this.$modified=!0,this.$resetRowCache(0);if(a){var b=this.getLength();this.$wrapData=[];for(var c=0;c<b;c++)this.$wrapData.push([]);this.$updateWrapData(0,b-1)}this._emit("changeWrapMode")}},this.getUseWrapMode=function(){return this.$useWrapMode},this.setWrapLimitRange=function(a,b){if(this.$wrapLimitRange.min!==a||this.$wrapLimitRange.max!==b)this.$wrapLimitRange.min=a,this.$wrapLimitRange.max=b,this.$modified=!0,this._emit("changeWrapMode")},this.adjustWrapLimit=function(a){var b=this.$constrainWrapLimit(a);return b!=this.$wrapLimit&&b>0?(this.$wrapLimit=b,this.$modified=!0,this.$useWrapMode&&(this.$updateWrapData(0,this.getLength()-1),this.$resetRowCache(0),this._emit("changeWrapLimit")),!0):!1},this.$constrainWrapLimit=function(a){var b=this.$wrapLimitRange.min;b&&(a=Math.max(b,a));var c=this.$wrapLimitRange.max;return c&&(a=Math.min(c,a)),Math.max(1,a)},this.getWrapLimit=function(){return this.$wrapLimit},this.getWrapLimitRange=function(){return{min:this.$wrapLimitRange.min,max:this.$wrapLimitRange.max}},this.$updateInternalDataOnChange=function(a){var b=this.$useWrapMode,c,d=a.data.action,e=a.data.range.start.row,f=a.data.range.end.row,g=a.data.range.start,h=a.data.range.end,i=null;d.indexOf("Lines")!=-1?(d=="insertLines"?f=e+a.data.lines.length:f=e,c=a.data.lines?a.data.lines.length:f-e):c=f-e;if(c!=0)if(d.indexOf("remove")!=-1){b&&this.$wrapData.splice(e,c);var j=this.$foldData;i=this.getFoldsInRange(a.data.range),this.removeFolds(i);var k=this.getFoldLine(h.row),l=0;if(k){k.addRemoveChars(h.row,h.column,g.column-h.column),k.shiftRow(-c);var m=this.getFoldLine(e);m&&m!==k&&(m.merge(k),k=m),l=j.indexOf(k)+1}for(l;l<j.length;l++){var k=j[l];k.start.row>=h.row&&k.shiftRow(-c)}f=e}else{var n;if(b){n=[e,0];for(var o=0;o<c;o++)n.push([]);this.$wrapData.splice.apply(this.$wrapData,n)}var j=this.$foldData,k=this.getFoldLine(e),l=0;if(k){var p=k.range.compareInside(g.row,g.column);p==0?(k=k.split(g.row,g.column),k.shiftRow(c),k.addRemoveChars(f,0,h.column-g.column)):p==-1&&(k.addRemoveChars(e,0,h.column-g.column),k.shiftRow(c)),l=j.indexOf(k)+1}for(l;l<j.length;l++){var k=j[l];k.start.row>=e&&k.shiftRow(c)}}else{c=Math.abs(a.data.range.start.column-a.data.range.end.column),d.indexOf("remove")!=-1&&(i=this.getFoldsInRange(a.data.range),this.removeFolds(i),c=-c);var k=this.getFoldLine(e);k&&k.addRemoveChars(e,g.column,c)}return b&&this.$wrapData.length!=this.doc.getLength()&&console.error("doc.getLength() and $wrapData.length have to be the same!"),b&&this.$updateWrapData(e,f),i},this.$updateWrapData=function(a,b){var c=this.doc.getAllLines(),d=this.getTabSize(),e=this.$wrapData,g=this.$wrapLimit,h,k,l=a;b=Math.min(b,c.length-1);while(l<=b){k=this.getFoldLine(l,k);if(!k)h=this.$getDisplayTokens(f.stringTrimRight(c[l])),e[l]=this.$computeWrapSplits(h,g,d),l++;else{h=[],k.walk(function(a,b,d,e){var f;if(a){f=this.$getDisplayTokens(a,h.length),f[0]=i;for(var g=1;g<f.length;g++)f[g]=j}else f=this.$getDisplayTokens(c[b].substring(e,d),h.length);h=h.concat(f)}.bind(this),k.end.row,c[k.end.row].length+1);while(h.length!=0&&h[h.length-1]>=n)h.pop();e[k.start.row]=this.$computeWrapSplits(h,g,d),l=k.end.row+1}}};var b=1,c=2,i=3,j=4,l=9,n=10,o=11,p=12;this.$computeWrapSplits=function(a,b){function g(b){var d=a.slice(e,b),g=d.length;d.join("").replace(/12/g,function(){g-=1}).replace(/2/g,function(){g-=1}),f+=g,c.push(f),e=b}if(a.length==0)return[];var c=[],d=a.length,e=0,f=0;while(d-e>b){var h=e+b;if(a[h]>=n){while(a[h]>=n)h++;g(h);continue}if(a[h]==i||a[h]==j){for(h;h!=e-1;h--)if(a[h]==i)break;if(h>e){g(h);continue}h=e+b;for(h;h<a.length;h++)if(a[h]!=j)break;if(h==a.length)break;g(h);continue}var k=Math.max(h-10,e-1);while(h>k&&a[h]<i)h--;while(h>k&&a[h]==l)h--;if(h>k){g(++h);continue}h=e+b,g(h)}return c},this.$getDisplayTokens=function(a,d){var e=[],f;d=d||0;for(var g=0;g<a.length;g++){var h=a.charCodeAt(g);if(h==9){f=this.getScreenTabSize(e.length+d),e.push(o);for(var i=1;i<f;i++)e.push(p)}else h==32?e.push(n):h>39&&h<48||h>57&&h<64?e.push(l):h>=4352&&q(h)?e.push(b,c):e.push(b)}return e},this.$getStringScreenWidth=function(a,b,c){if(b==0)return[0,0];b==null&&(b=c+a.length*Math.max(this.getTabSize(),2)),c=c||0;var d,e;for(e=0;e<a.length;e++){d=a.charCodeAt(e),d==9?c+=this.getScreenTabSize(c):d>=4352&&q(d)?c+=2:c+=1;if(c>b)break}return[c,e]},this.getRowLength=function(a){return!this.$useWrapMode||!this.$wrapData[a]?1:this.$wrapData[a].length+1},this.getRowHeight=function(a,b){return this.getRowLength(b)*a.lineHeight},this.getScreenLastRowColumn=function(a){var b=this.screenToDocumentPosition(a,Number.MAX_VALUE);return this.documentToScreenColumn(b.row,b.column)},this.getDocumentLastRowColumn=function(a,b){var c=this.documentToScreenRow(a,b);return this.getScreenLastRowColumn(c)},this.getDocumentLastRowColumnPosition=function(a,b){var c=this.documentToScreenRow(a,b);return this.screenToDocumentPosition(c,Number.MAX_VALUE/10)},this.getRowSplitData=function(a){return this.$useWrapMode?this.$wrapData[a]:undefined},this.getScreenTabSize=function(a){return this.$tabSize-a%this.$tabSize},this.screenToDocumentRow=function(a,b){return this.screenToDocumentPosition(a,b).row},this.screenToDocumentColumn=function(a,b){return this.screenToDocumentPosition(a,b).column},this.screenToDocumentPosition=function(a,b){if(a<0)return{row:0,column:0};var c,d=0,e=0,f,g=0,h=0,i=this.$rowCache;for(var j=0;j<i.length;j++){if(!(i[j].screenRow<a))break;g=i[j].screenRow,d=i[j].docRow}var k=!i.length||j==i.length,l=this.getLength()-1,m=this.getNextFoldLine(d),n=m?m.start.row:Infinity;while(g<=a){h=this.getRowLength(d);if(g+h-1>=a||d>=l)break;g+=h,d++,d>n&&(d=m.end.row+1,m=this.getNextFoldLine(d,m),n=m?m.start.row:Infinity),k&&i.push({docRow:d,screenRow:g})}if(m&&m.start.row<=d)c=this.getFoldDisplayLine(m),d=m.start.row;else{if(g+h<=a||d>l)return{row:l,column:this.getLine(l).length};c=this.getLine(d),m=null}if(this.$useWrapMode){var o=this.$wrapData[d];o&&(f=o[a-g],a>g&&o.length&&(e=o[a-g-1]||o[o.length-1],c=c.substring(e)))}return e+=this.$getStringScreenWidth(c,b)[1],this.$useWrapMode&&e>=f&&(e=f-1),m?m.idxToPosition(e):{row:d,column:e}},this.documentToScreenPosition=function(a,b){if(typeof b=="undefined")var c=this.$clipPositionToDocument(a.row,a.column);else c=this.$clipPositionToDocument(a,b);a=c.row,b=c.column;var d;if(this.$useWrapMode){d=this.$wrapData;if(a>d.length-1)return{row:this.getScreenLength(),column:d.length==0?0:d[d.length-1].length-1}}var e=0,f=null,g=null;g=this.getFoldAt(a,b,1),g&&(a=g.start.row,b=g.start.column);var h,i=0,j=this.$rowCache;for(var k=0;k<j.length;k++){if(!(j[k].docRow<a))break;e=j[k].screenRow,i=j[k].docRow}var l=!j.length||k==j.length,m=this.getNextFoldLine(i),n=m?m.start.row:Infinity;while(i<a){if(i>=n){h=m.end.row+1;if(h>a)break;m=this.getNextFoldLine(h,m),n=m?m.start.row:Infinity}else h=i+1;e+=this.getRowLength(i),i=h,l&&j.push({docRow:i,screenRow:e})}var o="";m&&i>=n?(o=this.getFoldDisplayLine(m,a,b),f=m.start.row):(o=this.getLine(a).substring(0,b),f=a);if(this.$useWrapMode){var p=d[f],q=0;while(o.length>=p[q])e++,q++;o=o.substring(p[q-1]||0,o.length)}return{row:e,column:this.$getStringScreenWidth(o)[0]}},this.documentToScreenColumn=function(a,b){return this.documentToScreenPosition(a,b).column},this.documentToScreenRow=function(a,b){return this.documentToScreenPosition(a,b).row},this.getScreenLength=function(){var a=0,b=null;if(!this.$useWrapMode){a=this.getLength();var c=this.$foldData;for(var d=0;d<c.length;d++)b=c[d],a-=b.end.row-b.start.row}else{var e=this.$wrapData.length,f=0,d=0,b=this.$foldData[d++],g=b?b.start.row:Infinity;while(f<e)a+=this.$wrapData[f].length+1,f++,f>g&&(f=b.end.row+1,b=this.$foldData[d++],g=b?b.start.row:Infinity)}return a}}).call(n.prototype),a("./edit_session/folding").Folding.call(n.prototype),a("./edit_session/bracket_match").BracketMatch.call(n.prototype),b.EditSession=n}),define("ace/config",["require","exports","module","ace/lib/lang"],function(a,b,c){function g(a){return a.replace(/-(.)/g,function(a,b){return b.toUpperCase()})}"no use strict";var d=a("./lib/lang"),e=function(){return this}(),f={packaged:!1,workerPath:"",modePath:"",themePath:"",suffix:".js"};b.get=function(a){if(!f.hasOwnProperty(a))throw new Error("Unknown confik key: "+a);return f[a]},b.set=function(a,b){if(!f.hasOwnProperty(a))throw new Error("Unknown confik key: "+a);f[a]=b},b.all=function(){return d.copyObject(f)},b.init=function(){f.packaged=a.packaged||c.packaged||e.define&&define.packaged;if(!e.document)return"";var d={},h="",i,j=document.getElementsByTagName("script");for(var k=0;k<j.length;k++){var l=j[k],m=l.src||l.getAttribute("src");if(!m)continue;var n=l.attributes;for(var o=0,p=n.length;o<p;o++){var q=n[o];q.name.indexOf("data-ace-")===0&&(d[g(q.name.replace(/^data-ace-/,""))]=q.value)}var r=m.match(/^(?:(.*\/)ace\.js|(.*\/)ace((-uncompressed)?(-noconflict)?\.js))(?:\?|$)/);r&&(h=r[1]||r[2],i=r[3])}h&&(d.base=d.base||h,d.packaged=!0),d.suffix=d.suffix||i,d.workerPath=d.workerPath||d.base,d.modePath=d.modePath||d.base,d.themePath=d.themePath||d.base,delete d.base;for(var s in d)typeof d[s]!="undefined"&&b.set(s,d[s])}}),define("ace/lib/net",["require","exports","module"],function(a,b,c){"use strict",b.get=function(a,c){var d=b.createXhr();d.open("GET",a,!0),d.onreadystatechange=function(a){d.readyState===4&&c(d.responseText)},d.send(null)};var d=["Msxml2.XMLHTTP","Microsoft.XMLHTTP","Msxml2.XMLHTTP.4.0"];b.createXhr=function(){var a,b,c;if(typeof XMLHttpRequest!="undefined")return new XMLHttpRequest;for(b=0;b<3;b++){c=d[b];try{a=new ActiveXObject(c)}catch(e){}if(a){d=[c];break}}if(!a)throw new Error("createXhr(): XMLHttpRequest not available");return a},b.loadScript=function(a,b){var c=document.getElementsByTagName("head")[0],d=document.createElement("script");d.src=a,c.appendChild(d),d.onload=b}}),define("ace/selection",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter","ace/range"],function(a,b,c){"use strict";var d=a("./lib/oop"),e=a("./lib/lang"),f=a("./lib/event_emitter").EventEmitter,g=a("./range").Range,h=function(a){this.session=a,this.doc=a.getDocument(),this.clearSelection(),this.selectionLead=this.doc.createAnchor(0,0),this.selectionAnchor=this.doc.createAnchor(0,0);var b=this;this.selectionLead.on("change",function(a){b._emit("changeCursor"),b.$isEmpty||b._emit("changeSelection"),!b.$keepDesiredColumnOnChange&&a.old.column!=a.value.column&&(b.$desiredColumn=null)}),this.selectionAnchor.on("change",function(){b.$isEmpty||b._emit("changeSelection")})};(function(){d.implement(this,f),this.isEmpty=function(){return this.$isEmpty||this.selectionAnchor.row==this.selectionLead.row&&this.selectionAnchor.column==this.selectionLead.column},this.isMultiLine=function(){return this.isEmpty()?!1:this.getRange().isMultiLine()},this.getCursor=function(){return this.selectionLead.getPosition()},this.setSelectionAnchor=function(a,b){this.selectionAnchor.setPosition(a,b),this.$isEmpty&&(this.$isEmpty=!1,this._emit("changeSelection"))},this.getSelectionAnchor=function(){return this.$isEmpty?this.getSelectionLead():this.selectionAnchor.getPosition()},this.getSelectionLead=function(){return this.selectionLead.getPosition()},this.shiftSelection=function(a){if(this.$isEmpty){this.moveCursorTo(this.selectionLead.row,this.selectionLead.column+a);return}var b=this.getSelectionAnchor(),c=this.getSelectionLead(),d=this.isBackwards();(!d||b.column!==0)&&this.setSelectionAnchor(b.row,b.column+a),(d||c.column!==0)&&this.$moveSelection(function(){this.moveCursorTo(c.row,c.column+a)})},this.isBackwards=function(){var a=this.selectionAnchor,b=this.selectionLead;return a.row>b.row||a.row==b.row&&a.column>b.column},this.getRange=function(){var a=this.selectionAnchor,b=this.selectionLead;return this.isEmpty()?g.fromPoints(b,b):this.isBackwards()?g.fromPoints(b,a):g.fromPoints(a,b)},this.clearSelection=function(){this.$isEmpty||(this.$isEmpty=!0,this._emit("changeSelection"))},this.selectAll=function(){var a=this.doc.getLength()-1;this.setSelectionAnchor(a,this.doc.getLine(a).length),this.moveCursorTo(0,0)},this.setSelectionRange=function(a,b){b?(this.setSelectionAnchor(a.end.row,a.end.column),this.selectTo(a.start.row,a.start.column)):(this.setSelectionAnchor(a.start.row,a.start.column),this.selectTo(a.end.row,a.end.column)),this.$desiredColumn=null},this.$moveSelection=function(a){var b=this.selectionLead;this.$isEmpty&&this.setSelectionAnchor(b.row,b.column),a.call(this)},this.selectTo=function(a,b){this.$moveSelection(function(){this.moveCursorTo(a,b)})},this.selectToPosition=function(a){this.$moveSelection(function(){this.moveCursorToPosition(a)})},this.selectUp=function(){this.$moveSelection(this.moveCursorUp)},this.selectDown=function(){this.$moveSelection(this.moveCursorDown)},this.selectRight=function(){this.$moveSelection(this.moveCursorRight)},this.selectLeft=function(){this.$moveSelection(this.moveCursorLeft)},this.selectLineStart=function(){this.$moveSelection(this.moveCursorLineStart)},this.selectLineEnd=function(){this.$moveSelection(this.moveCursorLineEnd)},this.selectFileEnd=function(){this.$moveSelection(this.moveCursorFileEnd)},this.selectFileStart=function(){this.$moveSelection(this.moveCursorFileStart)},this.selectWordRight=function(){this.$moveSelection(this.moveCursorWordRight)},this.selectWordLeft=function(){this.$moveSelection(this.moveCursorWordLeft)},this.selectWord=function(){var a=this.getCursor(),b=this.session.getWordRange(a.row,a.column);this.setSelectionRange(b)},this.selectAWord=function(){var a=this.getCursor(),b=this.session.getAWordRange(a.row,a.column);this.setSelectionRange(b)},this.selectLine=function(){var a=this.selectionLead.row,b,c=this.session.getFoldLine(a);c?(a=c.start.row,b=c.end.row):b=a,this.setSelectionAnchor(a,0),this.$moveSelection(function(){this.moveCursorTo(b+1,0)})},this.moveCursorUp=function(){this.moveCursorBy(-1,0)},this.moveCursorDown=function(){this.moveCursorBy(1,0)},this.moveCursorLeft=function(){var a=this.selectionLead.getPosition(),b;if(b=this.session.getFoldAt(a.row,a.column,-1))this.moveCursorTo(b.start.row,b.start.column);else if(a.column==0)a.row>0&&this.moveCursorTo(a.row-1,this.doc.getLine(a.row-1).length);else{var c=this.session.getTabSize();this.session.isTabStop(a)&&this.doc.getLine(a.row).slice(a.column-c,a.column).split(" ").length-1==c?this.moveCursorBy(0,-c):this.moveCursorBy(0,-1)}},this.moveCursorRight=function(){var a=this.selectionLead.getPosition(),b;if(b=this.session.getFoldAt(a.row,a.column,1))this.moveCursorTo(b.end.row,b.end.column);else if(this.selectionLead.column==this.doc.getLine(this.selectionLead.row).length)this.selectionLead.row<this.doc.getLength()-1&&this.moveCursorTo(this.selectionLead.row+1,0);else{var c=this.session.getTabSize(),a=this.selectionLead;this.session.isTabStop(a)&&this.doc.getLine(a.row).slice(a.column,a.column+c).split(" ").length-1==c?this.moveCursorBy(0,c):this.moveCursorBy(0,1)}},this.moveCursorLineStart=function(){var a=this.selectionLead.row,b=this.selectionLead.column,c=this.session.documentToScreenRow(a,b),d=this.session.screenToDocumentPosition(c,0),e=this.session.getDisplayLine(a,null,d.row,d.column),f=e.match(/^\s*/);f[0].length==b?this.moveCursorTo(d.row,d.column):this.moveCursorTo(d.row,d.column+f[0].length)},this.moveCursorLineEnd=function(){var a=this.selectionLead,b=this.session.getDocumentLastRowColumnPosition(a.row,a.column);this.moveCursorTo(b.row,b.column)},this.moveCursorFileEnd=function(){var a=this.doc.getLength()-1,b=this.doc.getLine(a).length;this.moveCursorTo(a,b)},this.moveCursorFileStart=function(){this.moveCursorTo(0,0)},this.moveCursorWordRight=function(){var a=this.selectionLead.row,b=this.selectionLead.column,c=this.doc.getLine(a),d=c.substring(b),e;this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;var f=this.session.getFoldAt(a,b,1);if(f){this.moveCursorTo(f.end.row,f.end.column);return}if(e=this.session.nonTokenRe.exec(d))b+=this.session.nonTokenRe.lastIndex,this.session.nonTokenRe.lastIndex=0,d=c.substring(b);if(b>=c.length){this.moveCursorTo(a,c.length),this.moveCursorRight(),a<this.doc.getLength()-1&&this.moveCursorWordRight();return}if(e=this.session.tokenRe.exec(d))b+=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0;this.moveCursorTo(a,b)},this.moveCursorWordLeft=function(){var a=this.selectionLead.row,b=this.selectionLead.column,c;if(c=this.session.getFoldAt(a,b,-1)){this.moveCursorTo(c.start.row,c.start.column);return}var d=this.session.getFoldStringAt(a,b,-1);d==null&&(d=this.doc.getLine(a).substring(0,b));var f=e.stringReverse(d),g;this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;if(g=this.session.nonTokenRe.exec(f))b-=this.session.nonTokenRe.lastIndex,f=f.slice(this.session.nonTokenRe.lastIndex),this.session.nonTokenRe.lastIndex=0;if(b<=0){this.moveCursorTo(a,0),this.moveCursorLeft(),a>0&&this.moveCursorWordLeft();return}if(g=this.session.tokenRe.exec(f))b-=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0;this.moveCursorTo(a,b)},this.moveCursorBy=function(a,b){var c=this.session.documentToScreenPosition(this.selectionLead.row,this.selectionLead.column);b===0&&(this.$desiredColumn?c.column=this.$desiredColumn:this.$desiredColumn=c.column);var d=this.session.screenToDocumentPosition(c.row+a,c.column);this.moveCursorTo(d.row,d.column+b,b===0)},this.moveCursorToPosition=function(a){this.moveCursorTo(a.row,a.column)},this.moveCursorTo=function(a,b,c){var d=this.session.getFoldAt(a,b,1);d&&(a=d.start.row,b=d.start.column),this.$keepDesiredColumnOnChange=!0,this.selectionLead.setPosition(a,b),this.$keepDesiredColumnOnChange=!1,c||(this.$desiredColumn=null)},this.moveCursorToScreen=function(a,b,c){var d=this.session.screenToDocumentPosition(a,b);this.moveCursorTo(d.row,d.column,c)},this.detach=function(){this.selectionLead.detach(),this.selectionAnchor.detach(),this.session=this.doc=null},this.fromOrientedRange=function(a){this.setSelectionRange(a,a.cursor==a.start),this.$desiredColumn=a.desiredColumn||this.$desiredColumn},this.toOrientedRange=function(a){var b=this.getRange();return a?(a.start.column=b.start.column,a.start.row=b.start.row,a.end.column=b.end.column,a.end.row=b.end.row):a=b,a.cursor=this.isBackwards()?a.start:a.end,a.desiredColumn=this.$desiredColumn,a}}).call(h.prototype),b.Selection=h}),define("ace/range",["require","exports","module"],function(a,b,c){"use strict";var d=function(a,b,c,d){this.start={row:a,column:b},this.end={row:c,column:d}};(function(){this.isEqual=function(a){return this.start.row==a.start.row&&this.end.row==a.end.row&&this.start.column==a.start.column&&this.end.column==a.end.column},this.toString=function(){return"Range: ["+this.start.row+"/"+this.start.column+"] -> ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(a,b){return this.compare(a,b)==0},this.compareRange=function(a){var b,c=a.end,d=a.start;return b=this.compare(c.row,c.column),b==1?(b=this.compare(d.row,d.column),b==1?2:b==0?1:0):b==-1?-2:(b=this.compare(d.row,d.column),b==-1?-1:b==1?42:0)},this.comparePoint=function(a){return this.compare(a.row,a.column)},this.containsRange=function(a){return this.comparePoint(a.start)==0&&this.comparePoint(a.end)==0},this.intersectsRange=function(a){var b=this.compareRange(a);return b==-1||b==0||b==1},this.isEnd=function(a,b){return this.end.row==a&&this.end.column==b},this.isStart=function(a,b){return this.start.row==a&&this.start.column==b},this.setStart=function(a,b){typeof a=="object"?(this.start.column=a.column,this.start.row=a.row):(this.start.row=a,this.start.column=b)},this.setEnd=function(a,b){typeof a=="object"?(this.end.column=a.column,this.end.row=a.row):(this.end.row=a,this.end.column=b)},this.inside=function(a,b){return this.compare(a,b)==0?this.isEnd(a,b)||this.isStart(a,b)?!1:!0:!1},this.insideStart=function(a,b){return this.compare(a,b)==0?this.isEnd(a,b)?!1:!0:!1},this.insideEnd=function(a,b){return this.compare(a,b)==0?this.isStart(a,b)?!1:!0:!1},this.compare=function(a,b){return!this.isMultiLine()&&a===this.start.row?b<this.start.column?-1:b>this.end.column?1:0:a<this.start.row?-1:a>this.end.row?1:this.start.row===a?b>=this.start.column?0:-1:this.end.row===a?b<=this.end.column?0:1:0},this.compareStart=function(a,b){return this.start.row==a&&this.start.column==b?-1:this.compare(a,b)},this.compareEnd=function(a,b){return this.end.row==a&&this.end.column==b?1:this.compare(a,b)},this.compareInside=function(a,b){return this.end.row==a&&this.end.column==b?1:this.start.row==a&&this.start.column==b?-1:this.compare(a,b)},this.clipRows=function(a,b){if(this.end.row>b)var c={row:b+1,column:0};if(this.start.row>b)var e={row:b+1,column:0};if(this.start.row<a)var e={row:a,column:0};if(this.end.row<a)var c={row:a,column:0};return d.fromPoints(e||this.start,c||this.end)},this.extend=function(a,b){var c=this.compare(a,b);if(c==0)return this;if(c==-1)var e={row:a,column:b};else var f={row:a,column:b};return d.fromPoints(e||this.start,f||this.end)},this.fixOrientation=function(){if(this.start.row<this.end.row||this.start.row==this.end.row&&this.start.column<this.end.column)return!1;var a=this.start;return this.end=this.start,this.start=a,!0},this.isEmpty=function(){return this.start.row==this.end.row&&this.start.column==this.end.column},this.isMultiLine=function(){return this.start.row!==this.end.row},this.clone=function(){return d.fromPoints(this.start,this.end)},this.collapseRows=function(){return this.end.column==0?new d(this.start.row,0,Math.max(this.start.row,this.end.row-1),0):new d(this.start.row,0,this.end.row,0)},this.toScreenRange=function(a){var b=a.documentToScreenPosition(this.start),c=a.documentToScreenPosition(this.end);return new d(b.row,b.column,c.row,c.column)}}).call(d.prototype),d.fromPoints=function(a,b){return new d(a.row,a.column,b.row,b.column)},b.Range=d}),define("ace/mode/text",["require","exports","module","ace/tokenizer","ace/mode/text_highlight_rules","ace/mode/behaviour","ace/unicode"],function(a,b,c){"use strict";var d=a("../tokenizer").Tokenizer,e=a("./text_highlight_rules").TextHighlightRules,f=a("./behaviour").Behaviour,g=a("../unicode"),h=function(){this.$tokenizer=new d((new e).getRules()),this.$behaviour=new f};(function(){this.tokenRe=new RegExp("^["+g.packages.L+g.packages.Mn+g.packages.Mc+g.packages.Nd+g.packages.Pc+"\\$_]+","g"),this.nonTokenRe=new RegExp("^(?:[^"+g.packages.L+g.packages.Mn+g.packages.Mc+g.packages.Nd+g.packages.Pc+"\\$_]|s])+","g"),this.getTokenizer=function(){return this.$tokenizer},this.toggleCommentLines=function(a,b,c,d){},this.getNextLineIndent=function(a,b,c){return""},this.checkOutdent=function(a,b,c){return!1},this.autoOutdent=function(a,b,c){},this.$getIndent=function(a){var b=a.match(/^(\s+)/);return b?b[1]:""},this.createWorker=function(a){return null},this.highlightSelection=function(a){var b=a.session;b.$selectionOccurrences||(b.$selectionOccurrences=[]),b.$selectionOccurrences.length&&this.clearSelectionHighlight(a);var c=a.getSelectionRange();if(c.isEmpty()||c.isMultiLine())return;var d=c.start.column-1,e=c.end.column+1,f=b.getLine(c.start.row),g=f.length,h=f.substring(Math.max(d,0),Math.min(e,g));if(d>=0&&/^[\w\d]/.test(h)||e<=g&&/[\w\d]$/.test(h))return;h=f.substring(c.start.column,c.end.column);if(!/^[\w\d]+$/.test(h))return;var i=a.getCursorPosition(),j={wrap:!0,wholeWord:!0,caseSensitive:!0,needle:h},k=a.$search.getOptions();a.$search.set(j);var l=a.$search.findAll(b);l.forEach(function(a){if(!a.contains(i.row,i.column)){var c=b.addMarker(a,"ace_selected_word","text");b.$selectionOccurrences.push(c)}}),a.$search.set(k)},this.clearSelectionHighlight=function(a){if(!a.session.$selectionOccurrences)return;a.session.$selectionOccurrences.forEach(function(b){a.session.removeMarker(b)}),a.session.$selectionOccurrences=[]},this.createModeDelegates=function(a){if(!this.$embeds)return;this.$modes={};for(var b=0;b<this.$embeds.length;b++)a[this.$embeds[b]]&&(this.$modes[this.$embeds[b]]=new a[this.$embeds[b]]);var c=["toggleCommentLines","getNextLineIndent","checkOutdent","autoOutdent","transformAction"];for(var b=0;b<c.length;b++)(function(a){var d=c[b],e=a[d];a[c[b]]=function(){return this.$delegator(d,arguments,e)}})(this)},this.$delegator=function(a,b,c){var d=b[0];for(var e=0;e<this.$embeds.length;e++){if(!this.$modes[this.$embeds[e]])continue;var f=d.split(this.$embeds[e]);if(!f[0]&&f[1]){b[0]=f[1];var g=this.$modes[this.$embeds[e]];return g[a].apply(g,b)}}var h=c.apply(this,b);return c?h:undefined},this.transformAction=function(a,b,c,d,e){if(this.$behaviour){var f=this.$behaviour.getBehaviours();for(var g in f)if(f[g][b]){var h=f[g][b].apply(this,arguments);if(h)return h}}}}).call(h.prototype),b.Mode=h}),define("ace/tokenizer",["require","exports","module"],function(a,b,c){"use strict";var d=function(a,b){b=b?"g"+b:"g",this.rules=a,this.regExps={},this.matchMappings={};for(var c in this.rules){var d=this.rules[c],e=d,f=[],g=0,h=this.matchMappings[c]={};for(var i=0;i<e.length;i++){e[i].regex instanceof RegExp&&(e[i].regex=e[i].regex.toString().slice(1,-1));var j=(new RegExp("(?:("+e[i].regex+")|(.))")).exec("a").length-2,k=e[i].regex.replace(/\\([0-9]+)/g,function(a,b){return"\\"+(parseInt(b,10)+g+1)});if(j>1&&e[i].token.length!==j-1)throw new Error("Matching groups and length of the token array don't match in rule #"+i+" of state "+c);h[g]={rule:i,len:j},g+=j,f.push(k)}this.regExps[c]=new RegExp("(?:("+f.join(")|(")+")|(.))",b)}};(function(){this.getLineTokens=function(a,b){var c=b,d=this.rules[c],e=this.matchMappings[c],f=this.regExps[c];f.lastIndex=0;var g,h=[],i=0,j={type:null,value:""};while(g=f.exec(a)){var k="text",l=null,m=[g[0]];for(var n=0;n<g.length-2;n++){if(g[n+1]===undefined)continue;l=d[e[n].rule],e[n].len>1&&(m=g.slice(n+2,n+1+e[n].len)),typeof l.token=="function"?k=l.token.apply(this,m):k=l.token;var o=l.next;o&&o!==c&&(c=o,d=this.rules[c],e=this.matchMappings[c],i=f.lastIndex,f=this.regExps[c],f.lastIndex=i);break}if(m[0]){typeof k=="string"&&(m=[m.join("")],k=[k]);for(var n=0;n<m.length;n++){if(!m[n])continue;(!l||l.merge||k[n]==="text")&&j.type===k[n]?j.value+=m[n]:(j.type&&h.push(j),j={type:k[n],value:m[n]})}}if(i==a.length)break;i=f.lastIndex}return j.type&&h.push(j),{tokens:h,state:c}}}).call(d.prototype),b.Tokenizer=d}),define("ace/mode/text_highlight_rules",["require","exports","module","ace/lib/lang"],function(a,b,c){"use strict";var d=a("../lib/lang"),e=function(){this.$rules={start:[{token:"empty_line",regex:"^$"},{token:"text",regex:".+"}]}};(function(){this.addRules=function(a,b){for(var c in a){var d=a[c];for(var e=0;e<d.length;e++){var f=d[e];f.next?f.next=b+f.next:f.next=b+c}this.$rules[b+c]=d}},this.getRules=function(){return this.$rules},this.embedRules=function(a,b,c,e){var f=(new a).getRules();if(e)for(var g=0;g<e.length;g++)e[g]=b+e[g];else{e=[];for(var h in f)e.push(b+h)}this.addRules(f,b);for(var g=0;g<e.length;g++)Array.prototype.unshift.apply(this.$rules[e[g]],d.deepCopy(c));this.$embeds||(this.$embeds=[]),this.$embeds.push(b)},this.getEmbeds=function(){return this.$embeds}}).call(e.prototype),b.TextHighlightRules=e}),define("ace/mode/behaviour",["require","exports","module"],function(a,b,c){"use strict";var d=function(){this.$behaviours={}};(function(){this.add=function(a,b,c){switch(undefined){case this.$behaviours:this.$behaviours={};case this.$behaviours[a]:this.$behaviours[a]={}}this.$behaviours[a][b]=c},this.addBehaviours=function(a){for(var b in a)for(var c in a[b])this.add(b,c,a[b][c])},this.remove=function(a){this.$behaviours&&this.$behaviours[a]&&delete this.$behaviours[a]},this.inherit=function(a,b){if(typeof a=="function")var c=(new a).getBehaviours(b);else var c=a.getBehaviours(b);this.addBehaviours(c)},this.getBehaviours=function(a){if(!a)return this.$behaviours;var b={};for(var c=0;c<a.length;c++)this.$behaviours[a[c]]&&(b[a[c]]=this.$behaviours[a[c]]);return b}}).call(d.prototype),b.Behaviour=d}),define("ace/unicode",["require","exports","module"],function(a,b,c){function d(a){var c=/\w{4}/g;for(var d in a)b.packages[d]=a[d].replace(c,"\\u$&")}"use strict",b.packages={},d({L:"0041-005A0061-007A00AA00B500BA00C0-00D600D8-00F600F8-02C102C6-02D102E0-02E402EC02EE0370-037403760377037A-037D03860388-038A038C038E-03A103A3-03F503F7-0481048A-05250531-055605590561-058705D0-05EA05F0-05F20621-064A066E066F0671-06D306D506E506E606EE06EF06FA-06FC06FF07100712-072F074D-07A507B107CA-07EA07F407F507FA0800-0815081A082408280904-0939093D09500958-0961097109720979-097F0985-098C098F09900993-09A809AA-09B009B209B6-09B909BD09CE09DC09DD09DF-09E109F009F10A05-0A0A0A0F0A100A13-0A280A2A-0A300A320A330A350A360A380A390A59-0A5C0A5E0A72-0A740A85-0A8D0A8F-0A910A93-0AA80AAA-0AB00AB20AB30AB5-0AB90ABD0AD00AE00AE10B05-0B0C0B0F0B100B13-0B280B2A-0B300B320B330B35-0B390B3D0B5C0B5D0B5F-0B610B710B830B85-0B8A0B8E-0B900B92-0B950B990B9A0B9C0B9E0B9F0BA30BA40BA8-0BAA0BAE-0BB90BD00C05-0C0C0C0E-0C100C12-0C280C2A-0C330C35-0C390C3D0C580C590C600C610C85-0C8C0C8E-0C900C92-0CA80CAA-0CB30CB5-0CB90CBD0CDE0CE00CE10D05-0D0C0D0E-0D100D12-0D280D2A-0D390D3D0D600D610D7A-0D7F0D85-0D960D9A-0DB10DB3-0DBB0DBD0DC0-0DC60E01-0E300E320E330E40-0E460E810E820E840E870E880E8A0E8D0E94-0E970E99-0E9F0EA1-0EA30EA50EA70EAA0EAB0EAD-0EB00EB20EB30EBD0EC0-0EC40EC60EDC0EDD0F000F40-0F470F49-0F6C0F88-0F8B1000-102A103F1050-1055105A-105D106110651066106E-10701075-1081108E10A0-10C510D0-10FA10FC1100-1248124A-124D1250-12561258125A-125D1260-1288128A-128D1290-12B012B2-12B512B8-12BE12C012C2-12C512C8-12D612D8-13101312-13151318-135A1380-138F13A0-13F41401-166C166F-167F1681-169A16A0-16EA1700-170C170E-17111720-17311740-17511760-176C176E-17701780-17B317D717DC1820-18771880-18A818AA18B0-18F51900-191C1950-196D1970-19741980-19AB19C1-19C71A00-1A161A20-1A541AA71B05-1B331B45-1B4B1B83-1BA01BAE1BAF1C00-1C231C4D-1C4F1C5A-1C7D1CE9-1CEC1CEE-1CF11D00-1DBF1E00-1F151F18-1F1D1F20-1F451F48-1F4D1F50-1F571F591F5B1F5D1F5F-1F7D1F80-1FB41FB6-1FBC1FBE1FC2-1FC41FC6-1FCC1FD0-1FD31FD6-1FDB1FE0-1FEC1FF2-1FF41FF6-1FFC2071207F2090-209421022107210A-211321152119-211D212421262128212A-212D212F-2139213C-213F2145-2149214E218321842C00-2C2E2C30-2C5E2C60-2CE42CEB-2CEE2D00-2D252D30-2D652D6F2D80-2D962DA0-2DA62DA8-2DAE2DB0-2DB62DB8-2DBE2DC0-2DC62DC8-2DCE2DD0-2DD62DD8-2DDE2E2F300530063031-3035303B303C3041-3096309D-309F30A1-30FA30FC-30FF3105-312D3131-318E31A0-31B731F0-31FF3400-4DB54E00-9FCBA000-A48CA4D0-A4FDA500-A60CA610-A61FA62AA62BA640-A65FA662-A66EA67F-A697A6A0-A6E5A717-A71FA722-A788A78BA78CA7FB-A801A803-A805A807-A80AA80C-A822A840-A873A882-A8B3A8F2-A8F7A8FBA90A-A925A930-A946A960-A97CA984-A9B2A9CFAA00-AA28AA40-AA42AA44-AA4BAA60-AA76AA7AAA80-AAAFAAB1AAB5AAB6AAB9-AABDAAC0AAC2AADB-AADDABC0-ABE2AC00-D7A3D7B0-D7C6D7CB-D7FBF900-FA2DFA30-FA6DFA70-FAD9FB00-FB06FB13-FB17FB1DFB1F-FB28FB2A-FB36FB38-FB3CFB3EFB40FB41FB43FB44FB46-FBB1FBD3-FD3DFD50-FD8FFD92-FDC7FDF0-FDFBFE70-FE74FE76-FEFCFF21-FF3AFF41-FF5AFF66-FFBEFFC2-FFC7FFCA-FFCFFFD2-FFD7FFDA-FFDC",Ll:"0061-007A00AA00B500BA00DF-00F600F8-00FF01010103010501070109010B010D010F01110113011501170119011B011D011F01210123012501270129012B012D012F01310133013501370138013A013C013E014001420144014601480149014B014D014F01510153015501570159015B015D015F01610163016501670169016B016D016F0171017301750177017A017C017E-0180018301850188018C018D019201950199-019B019E01A101A301A501A801AA01AB01AD01B001B401B601B901BA01BD-01BF01C601C901CC01CE01D001D201D401D601D801DA01DC01DD01DF01E101E301E501E701E901EB01ED01EF01F001F301F501F901FB01FD01FF02010203020502070209020B020D020F02110213021502170219021B021D021F02210223022502270229022B022D022F02310233-0239023C023F0240024202470249024B024D024F-02930295-02AF037103730377037B-037D039003AC-03CE03D003D103D5-03D703D903DB03DD03DF03E103E303E503E703E903EB03ED03EF-03F303F503F803FB03FC0430-045F04610463046504670469046B046D046F04710473047504770479047B047D047F0481048B048D048F04910493049504970499049B049D049F04A104A304A504A704A904AB04AD04AF04B104B304B504B704B904BB04BD04BF04C204C404C604C804CA04CC04CE04CF04D104D304D504D704D904DB04DD04DF04E104E304E504E704E904EB04ED04EF04F104F304F504F704F904FB04FD04FF05010503050505070509050B050D050F05110513051505170519051B051D051F0521052305250561-05871D00-1D2B1D62-1D771D79-1D9A1E011E031E051E071E091E0B1E0D1E0F1E111E131E151E171E191E1B1E1D1E1F1E211E231E251E271E291E2B1E2D1E2F1E311E331E351E371E391E3B1E3D1E3F1E411E431E451E471E491E4B1E4D1E4F1E511E531E551E571E591E5B1E5D1E5F1E611E631E651E671E691E6B1E6D1E6F1E711E731E751E771E791E7B1E7D1E7F1E811E831E851E871E891E8B1E8D1E8F1E911E931E95-1E9D1E9F1EA11EA31EA51EA71EA91EAB1EAD1EAF1EB11EB31EB51EB71EB91EBB1EBD1EBF1EC11EC31EC51EC71EC91ECB1ECD1ECF1ED11ED31ED51ED71ED91EDB1EDD1EDF1EE11EE31EE51EE71EE91EEB1EED1EEF1EF11EF31EF51EF71EF91EFB1EFD1EFF-1F071F10-1F151F20-1F271F30-1F371F40-1F451F50-1F571F60-1F671F70-1F7D1F80-1F871F90-1F971FA0-1FA71FB0-1FB41FB61FB71FBE1FC2-1FC41FC61FC71FD0-1FD31FD61FD71FE0-1FE71FF2-1FF41FF61FF7210A210E210F2113212F21342139213C213D2146-2149214E21842C30-2C5E2C612C652C662C682C6A2C6C2C712C732C742C76-2C7C2C812C832C852C872C892C8B2C8D2C8F2C912C932C952C972C992C9B2C9D2C9F2CA12CA32CA52CA72CA92CAB2CAD2CAF2CB12CB32CB52CB72CB92CBB2CBD2CBF2CC12CC32CC52CC72CC92CCB2CCD2CCF2CD12CD32CD52CD72CD92CDB2CDD2CDF2CE12CE32CE42CEC2CEE2D00-2D25A641A643A645A647A649A64BA64DA64FA651A653A655A657A659A65BA65DA65FA663A665A667A669A66BA66DA681A683A685A687A689A68BA68DA68FA691A693A695A697A723A725A727A729A72BA72DA72F-A731A733A735A737A739A73BA73DA73FA741A743A745A747A749A74BA74DA74FA751A753A755A757A759A75BA75DA75FA761A763A765A767A769A76BA76DA76FA771-A778A77AA77CA77FA781A783A785A787A78CFB00-FB06FB13-FB17FF41-FF5A",Lu:"0041-005A00C0-00D600D8-00DE01000102010401060108010A010C010E01100112011401160118011A011C011E01200122012401260128012A012C012E01300132013401360139013B013D013F0141014301450147014A014C014E01500152015401560158015A015C015E01600162016401660168016A016C016E017001720174017601780179017B017D018101820184018601870189-018B018E-0191019301940196-0198019C019D019F01A001A201A401A601A701A901AC01AE01AF01B1-01B301B501B701B801BC01C401C701CA01CD01CF01D101D301D501D701D901DB01DE01E001E201E401E601E801EA01EC01EE01F101F401F6-01F801FA01FC01FE02000202020402060208020A020C020E02100212021402160218021A021C021E02200222022402260228022A022C022E02300232023A023B023D023E02410243-02460248024A024C024E03700372037603860388-038A038C038E038F0391-03A103A3-03AB03CF03D2-03D403D803DA03DC03DE03E003E203E403E603E803EA03EC03EE03F403F703F903FA03FD-042F04600462046404660468046A046C046E04700472047404760478047A047C047E0480048A048C048E04900492049404960498049A049C049E04A004A204A404A604A804AA04AC04AE04B004B204B404B604B804BA04BC04BE04C004C104C304C504C704C904CB04CD04D004D204D404D604D804DA04DC04DE04E004E204E404E604E804EA04EC04EE04F004F204F404F604F804FA04FC04FE05000502050405060508050A050C050E05100512051405160518051A051C051E0520052205240531-055610A0-10C51E001E021E041E061E081E0A1E0C1E0E1E101E121E141E161E181E1A1E1C1E1E1E201E221E241E261E281E2A1E2C1E2E1E301E321E341E361E381E3A1E3C1E3E1E401E421E441E461E481E4A1E4C1E4E1E501E521E541E561E581E5A1E5C1E5E1E601E621E641E661E681E6A1E6C1E6E1E701E721E741E761E781E7A1E7C1E7E1E801E821E841E861E881E8A1E8C1E8E1E901E921E941E9E1EA01EA21EA41EA61EA81EAA1EAC1EAE1EB01EB21EB41EB61EB81EBA1EBC1EBE1EC01EC21EC41EC61EC81ECA1ECC1ECE1ED01ED21ED41ED61ED81EDA1EDC1EDE1EE01EE21EE41EE61EE81EEA1EEC1EEE1EF01EF21EF41EF61EF81EFA1EFC1EFE1F08-1F0F1F18-1F1D1F28-1F2F1F38-1F3F1F48-1F4D1F591F5B1F5D1F5F1F68-1F6F1FB8-1FBB1FC8-1FCB1FD8-1FDB1FE8-1FEC1FF8-1FFB21022107210B-210D2110-211221152119-211D212421262128212A-212D2130-2133213E213F214521832C00-2C2E2C602C62-2C642C672C692C6B2C6D-2C702C722C752C7E-2C802C822C842C862C882C8A2C8C2C8E2C902C922C942C962C982C9A2C9C2C9E2CA02CA22CA42CA62CA82CAA2CAC2CAE2CB02CB22CB42CB62CB82CBA2CBC2CBE2CC02CC22CC42CC62CC82CCA2CCC2CCE2CD02CD22CD42CD62CD82CDA2CDC2CDE2CE02CE22CEB2CEDA640A642A644A646A648A64AA64CA64EA650A652A654A656A658A65AA65CA65EA662A664A666A668A66AA66CA680A682A684A686A688A68AA68CA68EA690A692A694A696A722A724A726A728A72AA72CA72EA732A734A736A738A73AA73CA73EA740A742A744A746A748A74AA74CA74EA750A752A754A756A758A75AA75CA75EA760A762A764A766A768A76AA76CA76EA779A77BA77DA77EA780A782A784A786A78BFF21-FF3A",Lt:"01C501C801CB01F21F88-1F8F1F98-1F9F1FA8-1FAF1FBC1FCC1FFC",Lm:"02B0-02C102C6-02D102E0-02E402EC02EE0374037A0559064006E506E607F407F507FA081A0824082809710E460EC610FC17D718431AA71C78-1C7D1D2C-1D611D781D9B-1DBF2071207F2090-20942C7D2D6F2E2F30053031-3035303B309D309E30FC-30FEA015A4F8-A4FDA60CA67FA717-A71FA770A788A9CFAA70AADDFF70FF9EFF9F",Lo:"01BB01C0-01C3029405D0-05EA05F0-05F20621-063F0641-064A066E066F0671-06D306D506EE06EF06FA-06FC06FF07100712-072F074D-07A507B107CA-07EA0800-08150904-0939093D09500958-096109720979-097F0985-098C098F09900993-09A809AA-09B009B209B6-09B909BD09CE09DC09DD09DF-09E109F009F10A05-0A0A0A0F0A100A13-0A280A2A-0A300A320A330A350A360A380A390A59-0A5C0A5E0A72-0A740A85-0A8D0A8F-0A910A93-0AA80AAA-0AB00AB20AB30AB5-0AB90ABD0AD00AE00AE10B05-0B0C0B0F0B100B13-0B280B2A-0B300B320B330B35-0B390B3D0B5C0B5D0B5F-0B610B710B830B85-0B8A0B8E-0B900B92-0B950B990B9A0B9C0B9E0B9F0BA30BA40BA8-0BAA0BAE-0BB90BD00C05-0C0C0C0E-0C100C12-0C280C2A-0C330C35-0C390C3D0C580C590C600C610C85-0C8C0C8E-0C900C92-0CA80CAA-0CB30CB5-0CB90CBD0CDE0CE00CE10D05-0D0C0D0E-0D100D12-0D280D2A-0D390D3D0D600D610D7A-0D7F0D85-0D960D9A-0DB10DB3-0DBB0DBD0DC0-0DC60E01-0E300E320E330E40-0E450E810E820E840E870E880E8A0E8D0E94-0E970E99-0E9F0EA1-0EA30EA50EA70EAA0EAB0EAD-0EB00EB20EB30EBD0EC0-0EC40EDC0EDD0F000F40-0F470F49-0F6C0F88-0F8B1000-102A103F1050-1055105A-105D106110651066106E-10701075-1081108E10D0-10FA1100-1248124A-124D1250-12561258125A-125D1260-1288128A-128D1290-12B012B2-12B512B8-12BE12C012C2-12C512C8-12D612D8-13101312-13151318-135A1380-138F13A0-13F41401-166C166F-167F1681-169A16A0-16EA1700-170C170E-17111720-17311740-17511760-176C176E-17701780-17B317DC1820-18421844-18771880-18A818AA18B0-18F51900-191C1950-196D1970-19741980-19AB19C1-19C71A00-1A161A20-1A541B05-1B331B45-1B4B1B83-1BA01BAE1BAF1C00-1C231C4D-1C4F1C5A-1C771CE9-1CEC1CEE-1CF12135-21382D30-2D652D80-2D962DA0-2DA62DA8-2DAE2DB0-2DB62DB8-2DBE2DC0-2DC62DC8-2DCE2DD0-2DD62DD8-2DDE3006303C3041-3096309F30A1-30FA30FF3105-312D3131-318E31A0-31B731F0-31FF3400-4DB54E00-9FCBA000-A014A016-A48CA4D0-A4F7A500-A60BA610-A61FA62AA62BA66EA6A0-A6E5A7FB-A801A803-A805A807-A80AA80C-A822A840-A873A882-A8B3A8F2-A8F7A8FBA90A-A925A930-A946A960-A97CA984-A9B2AA00-AA28AA40-AA42AA44-AA4BAA60-AA6FAA71-AA76AA7AAA80-AAAFAAB1AAB5AAB6AAB9-AABDAAC0AAC2AADBAADCABC0-ABE2AC00-D7A3D7B0-D7C6D7CB-D7FBF900-FA2DFA30-FA6DFA70-FAD9FB1DFB1F-FB28FB2A-FB36FB38-FB3CFB3EFB40FB41FB43FB44FB46-FBB1FBD3-FD3DFD50-FD8FFD92-FDC7FDF0-FDFBFE70-FE74FE76-FEFCFF66-FF6FFF71-FF9DFFA0-FFBEFFC2-FFC7FFCA-FFCFFFD2-FFD7FFDA-FFDC",M:"0300-036F0483-04890591-05BD05BF05C105C205C405C505C70610-061A064B-065E067006D6-06DC06DE-06E406E706E806EA-06ED07110730-074A07A6-07B007EB-07F30816-0819081B-08230825-08270829-082D0900-0903093C093E-094E0951-0955096209630981-098309BC09BE-09C409C709C809CB-09CD09D709E209E30A01-0A030A3C0A3E-0A420A470A480A4B-0A4D0A510A700A710A750A81-0A830ABC0ABE-0AC50AC7-0AC90ACB-0ACD0AE20AE30B01-0B030B3C0B3E-0B440B470B480B4B-0B4D0B560B570B620B630B820BBE-0BC20BC6-0BC80BCA-0BCD0BD70C01-0C030C3E-0C440C46-0C480C4A-0C4D0C550C560C620C630C820C830CBC0CBE-0CC40CC6-0CC80CCA-0CCD0CD50CD60CE20CE30D020D030D3E-0D440D46-0D480D4A-0D4D0D570D620D630D820D830DCA0DCF-0DD40DD60DD8-0DDF0DF20DF30E310E34-0E3A0E47-0E4E0EB10EB4-0EB90EBB0EBC0EC8-0ECD0F180F190F350F370F390F3E0F3F0F71-0F840F860F870F90-0F970F99-0FBC0FC6102B-103E1056-1059105E-10601062-10641067-106D1071-10741082-108D108F109A-109D135F1712-17141732-1734175217531772177317B6-17D317DD180B-180D18A91920-192B1930-193B19B0-19C019C819C91A17-1A1B1A55-1A5E1A60-1A7C1A7F1B00-1B041B34-1B441B6B-1B731B80-1B821BA1-1BAA1C24-1C371CD0-1CD21CD4-1CE81CED1CF21DC0-1DE61DFD-1DFF20D0-20F02CEF-2CF12DE0-2DFF302A-302F3099309AA66F-A672A67CA67DA6F0A6F1A802A806A80BA823-A827A880A881A8B4-A8C4A8E0-A8F1A926-A92DA947-A953A980-A983A9B3-A9C0AA29-AA36AA43AA4CAA4DAA7BAAB0AAB2-AAB4AAB7AAB8AABEAABFAAC1ABE3-ABEAABECABEDFB1EFE00-FE0FFE20-FE26",Mn:"0300-036F0483-04870591-05BD05BF05C105C205C405C505C70610-061A064B-065E067006D6-06DC06DF-06E406E706E806EA-06ED07110730-074A07A6-07B007EB-07F30816-0819081B-08230825-08270829-082D0900-0902093C0941-0948094D0951-095509620963098109BC09C1-09C409CD09E209E30A010A020A3C0A410A420A470A480A4B-0A4D0A510A700A710A750A810A820ABC0AC1-0AC50AC70AC80ACD0AE20AE30B010B3C0B3F0B41-0B440B4D0B560B620B630B820BC00BCD0C3E-0C400C46-0C480C4A-0C4D0C550C560C620C630CBC0CBF0CC60CCC0CCD0CE20CE30D41-0D440D4D0D620D630DCA0DD2-0DD40DD60E310E34-0E3A0E47-0E4E0EB10EB4-0EB90EBB0EBC0EC8-0ECD0F180F190F350F370F390F71-0F7E0F80-0F840F860F870F90-0F970F99-0FBC0FC6102D-10301032-10371039103A103D103E10581059105E-10601071-1074108210851086108D109D135F1712-17141732-1734175217531772177317B7-17BD17C617C9-17D317DD180B-180D18A91920-19221927192819321939-193B1A171A181A561A58-1A5E1A601A621A65-1A6C1A73-1A7C1A7F1B00-1B031B341B36-1B3A1B3C1B421B6B-1B731B801B811BA2-1BA51BA81BA91C2C-1C331C361C371CD0-1CD21CD4-1CE01CE2-1CE81CED1DC0-1DE61DFD-1DFF20D0-20DC20E120E5-20F02CEF-2CF12DE0-2DFF302A-302F3099309AA66FA67CA67DA6F0A6F1A802A806A80BA825A826A8C4A8E0-A8F1A926-A92DA947-A951A980-A982A9B3A9B6-A9B9A9BCAA29-AA2EAA31AA32AA35AA36AA43AA4CAAB0AAB2-AAB4AAB7AAB8AABEAABFAAC1ABE5ABE8ABEDFB1EFE00-FE0FFE20-FE26",Mc:"0903093E-09400949-094C094E0982098309BE-09C009C709C809CB09CC09D70A030A3E-0A400A830ABE-0AC00AC90ACB0ACC0B020B030B3E0B400B470B480B4B0B4C0B570BBE0BBF0BC10BC20BC6-0BC80BCA-0BCC0BD70C01-0C030C41-0C440C820C830CBE0CC0-0CC40CC70CC80CCA0CCB0CD50CD60D020D030D3E-0D400D46-0D480D4A-0D4C0D570D820D830DCF-0DD10DD8-0DDF0DF20DF30F3E0F3F0F7F102B102C10311038103B103C105610571062-10641067-106D108310841087-108C108F109A-109C17B617BE-17C517C717C81923-19261929-192B193019311933-193819B0-19C019C819C91A19-1A1B1A551A571A611A631A641A6D-1A721B041B351B3B1B3D-1B411B431B441B821BA11BA61BA71BAA1C24-1C2B1C341C351CE11CF2A823A824A827A880A881A8B4-A8C3A952A953A983A9B4A9B5A9BAA9BBA9BD-A9C0AA2FAA30AA33AA34AA4DAA7BABE3ABE4ABE6ABE7ABE9ABEAABEC",Me:"0488048906DE20DD-20E020E2-20E4A670-A672",N:"0030-003900B200B300B900BC-00BE0660-066906F0-06F907C0-07C90966-096F09E6-09EF09F4-09F90A66-0A6F0AE6-0AEF0B66-0B6F0BE6-0BF20C66-0C6F0C78-0C7E0CE6-0CEF0D66-0D750E50-0E590ED0-0ED90F20-0F331040-10491090-10991369-137C16EE-16F017E0-17E917F0-17F91810-18191946-194F19D0-19DA1A80-1A891A90-1A991B50-1B591BB0-1BB91C40-1C491C50-1C5920702074-20792080-20892150-21822185-21892460-249B24EA-24FF2776-27932CFD30073021-30293038-303A3192-31953220-32293251-325F3280-328932B1-32BFA620-A629A6E6-A6EFA830-A835A8D0-A8D9A900-A909A9D0-A9D9AA50-AA59ABF0-ABF9FF10-FF19",Nd:"0030-00390660-066906F0-06F907C0-07C90966-096F09E6-09EF0A66-0A6F0AE6-0AEF0B66-0B6F0BE6-0BEF0C66-0C6F0CE6-0CEF0D66-0D6F0E50-0E590ED0-0ED90F20-0F291040-10491090-109917E0-17E91810-18191946-194F19D0-19DA1A80-1A891A90-1A991B50-1B591BB0-1BB91C40-1C491C50-1C59A620-A629A8D0-A8D9A900-A909A9D0-A9D9AA50-AA59ABF0-ABF9FF10-FF19",Nl:"16EE-16F02160-21822185-218830073021-30293038-303AA6E6-A6EF",No:"00B200B300B900BC-00BE09F4-09F90BF0-0BF20C78-0C7E0D70-0D750F2A-0F331369-137C17F0-17F920702074-20792080-20892150-215F21892460-249B24EA-24FF2776-27932CFD3192-31953220-32293251-325F3280-328932B1-32BFA830-A835",P:"0021-00230025-002A002C-002F003A003B003F0040005B-005D005F007B007D00A100AB00B700BB00BF037E0387055A-055F0589058A05BE05C005C305C605F305F40609060A060C060D061B061E061F066A-066D06D40700-070D07F7-07F90830-083E0964096509700DF40E4F0E5A0E5B0F04-0F120F3A-0F3D0F850FD0-0FD4104A-104F10FB1361-13681400166D166E169B169C16EB-16ED1735173617D4-17D617D8-17DA1800-180A1944194519DE19DF1A1E1A1F1AA0-1AA61AA8-1AAD1B5A-1B601C3B-1C3F1C7E1C7F1CD32010-20272030-20432045-20512053-205E207D207E208D208E2329232A2768-277527C527C627E6-27EF2983-299829D8-29DB29FC29FD2CF9-2CFC2CFE2CFF2E00-2E2E2E302E313001-30033008-30113014-301F3030303D30A030FBA4FEA4FFA60D-A60FA673A67EA6F2-A6F7A874-A877A8CEA8CFA8F8-A8FAA92EA92FA95FA9C1-A9CDA9DEA9DFAA5C-AA5FAADEAADFABEBFD3EFD3FFE10-FE19FE30-FE52FE54-FE61FE63FE68FE6AFE6BFF01-FF03FF05-FF0AFF0C-FF0FFF1AFF1BFF1FFF20FF3B-FF3DFF3FFF5BFF5DFF5F-FF65",Pd:"002D058A05BE140018062010-20152E172E1A301C303030A0FE31FE32FE58FE63FF0D",Ps:"0028005B007B0F3A0F3C169B201A201E2045207D208D23292768276A276C276E27702772277427C527E627E827EA27EC27EE2983298529872989298B298D298F299129932995299729D829DA29FC2E222E242E262E283008300A300C300E3010301430163018301A301DFD3EFE17FE35FE37FE39FE3BFE3DFE3FFE41FE43FE47FE59FE5BFE5DFF08FF3BFF5BFF5FFF62",Pe:"0029005D007D0F3B0F3D169C2046207E208E232A2769276B276D276F27712773277527C627E727E927EB27ED27EF298429862988298A298C298E2990299229942996299829D929DB29FD2E232E252E272E293009300B300D300F3011301530173019301B301E301FFD3FFE18FE36FE38FE3AFE3CFE3EFE40FE42FE44FE48FE5AFE5CFE5EFF09FF3DFF5DFF60FF63",Pi:"00AB2018201B201C201F20392E022E042E092E0C2E1C2E20",Pf:"00BB2019201D203A2E032E052E0A2E0D2E1D2E21",Pc:"005F203F20402054FE33FE34FE4D-FE4FFF3F",Po:"0021-00230025-0027002A002C002E002F003A003B003F0040005C00A100B700BF037E0387055A-055F058905C005C305C605F305F40609060A060C060D061B061E061F066A-066D06D40700-070D07F7-07F90830-083E0964096509700DF40E4F0E5A0E5B0F04-0F120F850FD0-0FD4104A-104F10FB1361-1368166D166E16EB-16ED1735173617D4-17D617D8-17DA1800-18051807-180A1944194519DE19DF1A1E1A1F1AA0-1AA61AA8-1AAD1B5A-1B601C3B-1C3F1C7E1C7F1CD3201620172020-20272030-2038203B-203E2041-20432047-205120532055-205E2CF9-2CFC2CFE2CFF2E002E012E06-2E082E0B2E0E-2E162E182E192E1B2E1E2E1F2E2A-2E2E2E302E313001-3003303D30FBA4FEA4FFA60D-A60FA673A67EA6F2-A6F7A874-A877A8CEA8CFA8F8-A8FAA92EA92FA95FA9C1-A9CDA9DEA9DFAA5C-AA5FAADEAADFABEBFE10-FE16FE19FE30FE45FE46FE49-FE4CFE50-FE52FE54-FE57FE5F-FE61FE68FE6AFE6BFF01-FF03FF05-FF07FF0AFF0CFF0EFF0FFF1AFF1BFF1FFF20FF3CFF61FF64FF65",S:"0024002B003C-003E005E0060007C007E00A2-00A900AC00AE-00B100B400B600B800D700F702C2-02C502D2-02DF02E5-02EB02ED02EF-02FF03750384038503F604820606-0608060B060E060F06E906FD06FE07F609F209F309FA09FB0AF10B700BF3-0BFA0C7F0CF10CF20D790E3F0F01-0F030F13-0F170F1A-0F1F0F340F360F380FBE-0FC50FC7-0FCC0FCE0FCF0FD5-0FD8109E109F13601390-139917DB194019E0-19FF1B61-1B6A1B74-1B7C1FBD1FBF-1FC11FCD-1FCF1FDD-1FDF1FED-1FEF1FFD1FFE20442052207A-207C208A-208C20A0-20B8210021012103-21062108210921142116-2118211E-2123212521272129212E213A213B2140-2144214A-214D214F2190-2328232B-23E82400-24262440-244A249C-24E92500-26CD26CF-26E126E326E8-26FF2701-27042706-2709270C-27272729-274B274D274F-27522756-275E2761-276727942798-27AF27B1-27BE27C0-27C427C7-27CA27CC27D0-27E527F0-29822999-29D729DC-29FB29FE-2B4C2B50-2B592CE5-2CEA2E80-2E992E9B-2EF32F00-2FD52FF0-2FFB300430123013302030363037303E303F309B309C319031913196-319F31C0-31E33200-321E322A-32503260-327F328A-32B032C0-32FE3300-33FF4DC0-4DFFA490-A4C6A700-A716A720A721A789A78AA828-A82BA836-A839AA77-AA79FB29FDFCFDFDFE62FE64-FE66FE69FF04FF0BFF1C-FF1EFF3EFF40FF5CFF5EFFE0-FFE6FFE8-FFEEFFFCFFFD",Sm:"002B003C-003E007C007E00AC00B100D700F703F60606-060820442052207A-207C208A-208C2140-2144214B2190-2194219A219B21A021A321A621AE21CE21CF21D221D421F4-22FF2308-230B23202321237C239B-23B323DC-23E125B725C125F8-25FF266F27C0-27C427C7-27CA27CC27D0-27E527F0-27FF2900-29822999-29D729DC-29FB29FE-2AFF2B30-2B442B47-2B4CFB29FE62FE64-FE66FF0BFF1C-FF1EFF5CFF5EFFE2FFE9-FFEC",Sc:"002400A2-00A5060B09F209F309FB0AF10BF90E3F17DB20A0-20B8A838FDFCFE69FF04FFE0FFE1FFE5FFE6",Sk:"005E006000A800AF00B400B802C2-02C502D2-02DF02E5-02EB02ED02EF-02FF0375038403851FBD1FBF-1FC11FCD-1FCF1FDD-1FDF1FED-1FEF1FFD1FFE309B309CA700-A716A720A721A789A78AFF3EFF40FFE3",So:"00A600A700A900AE00B000B60482060E060F06E906FD06FE07F609FA0B700BF3-0BF80BFA0C7F0CF10CF20D790F01-0F030F13-0F170F1A-0F1F0F340F360F380FBE-0FC50FC7-0FCC0FCE0FCF0FD5-0FD8109E109F13601390-1399194019E0-19FF1B61-1B6A1B74-1B7C210021012103-21062108210921142116-2118211E-2123212521272129212E213A213B214A214C214D214F2195-2199219C-219F21A121A221A421A521A7-21AD21AF-21CD21D021D121D321D5-21F32300-2307230C-231F2322-2328232B-237B237D-239A23B4-23DB23E2-23E82400-24262440-244A249C-24E92500-25B625B8-25C025C2-25F72600-266E2670-26CD26CF-26E126E326E8-26FF2701-27042706-2709270C-27272729-274B274D274F-27522756-275E2761-276727942798-27AF27B1-27BE2800-28FF2B00-2B2F2B452B462B50-2B592CE5-2CEA2E80-2E992E9B-2EF32F00-2FD52FF0-2FFB300430123013302030363037303E303F319031913196-319F31C0-31E33200-321E322A-32503260-327F328A-32B032C0-32FE3300-33FF4DC0-4DFFA490-A4C6A828-A82BA836A837A839AA77-AA79FDFDFFE4FFE8FFEDFFEEFFFCFFFD",Z:"002000A01680180E2000-200A20282029202F205F3000",Zs:"002000A01680180E2000-200A202F205F3000",Zl:"2028",Zp:"2029",C:"0000-001F007F-009F00AD03780379037F-0383038B038D03A20526-05300557055805600588058B-059005C8-05CF05EB-05EF05F5-0605061C061D0620065F06DD070E070F074B074C07B2-07BF07FB-07FF082E082F083F-08FF093A093B094F095609570973-097809800984098D098E0991099209A909B109B3-09B509BA09BB09C509C609C909CA09CF-09D609D8-09DB09DE09E409E509FC-0A000A040A0B-0A0E0A110A120A290A310A340A370A3A0A3B0A3D0A43-0A460A490A4A0A4E-0A500A52-0A580A5D0A5F-0A650A76-0A800A840A8E0A920AA90AB10AB40ABA0ABB0AC60ACA0ACE0ACF0AD1-0ADF0AE40AE50AF00AF2-0B000B040B0D0B0E0B110B120B290B310B340B3A0B3B0B450B460B490B4A0B4E-0B550B58-0B5B0B5E0B640B650B72-0B810B840B8B-0B8D0B910B96-0B980B9B0B9D0BA0-0BA20BA5-0BA70BAB-0BAD0BBA-0BBD0BC3-0BC50BC90BCE0BCF0BD1-0BD60BD8-0BE50BFB-0C000C040C0D0C110C290C340C3A-0C3C0C450C490C4E-0C540C570C5A-0C5F0C640C650C70-0C770C800C810C840C8D0C910CA90CB40CBA0CBB0CC50CC90CCE-0CD40CD7-0CDD0CDF0CE40CE50CF00CF3-0D010D040D0D0D110D290D3A-0D3C0D450D490D4E-0D560D58-0D5F0D640D650D76-0D780D800D810D840D97-0D990DB20DBC0DBE0DBF0DC7-0DC90DCB-0DCE0DD50DD70DE0-0DF10DF5-0E000E3B-0E3E0E5C-0E800E830E850E860E890E8B0E8C0E8E-0E930E980EA00EA40EA60EA80EA90EAC0EBA0EBE0EBF0EC50EC70ECE0ECF0EDA0EDB0EDE-0EFF0F480F6D-0F700F8C-0F8F0F980FBD0FCD0FD9-0FFF10C6-10CF10FD-10FF1249124E124F12571259125E125F1289128E128F12B112B612B712BF12C112C612C712D7131113161317135B-135E137D-137F139A-139F13F5-13FF169D-169F16F1-16FF170D1715-171F1737-173F1754-175F176D17711774-177F17B417B517DE17DF17EA-17EF17FA-17FF180F181A-181F1878-187F18AB-18AF18F6-18FF191D-191F192C-192F193C-193F1941-1943196E196F1975-197F19AC-19AF19CA-19CF19DB-19DD1A1C1A1D1A5F1A7D1A7E1A8A-1A8F1A9A-1A9F1AAE-1AFF1B4C-1B4F1B7D-1B7F1BAB-1BAD1BBA-1BFF1C38-1C3A1C4A-1C4C1C80-1CCF1CF3-1CFF1DE7-1DFC1F161F171F1E1F1F1F461F471F4E1F4F1F581F5A1F5C1F5E1F7E1F7F1FB51FC51FD41FD51FDC1FF01FF11FF51FFF200B-200F202A-202E2060-206F20722073208F2095-209F20B9-20CF20F1-20FF218A-218F23E9-23FF2427-243F244B-245F26CE26E226E4-26E727002705270A270B2728274C274E2753-2755275F27602795-279727B027BF27CB27CD-27CF2B4D-2B4F2B5A-2BFF2C2F2C5F2CF2-2CF82D26-2D2F2D66-2D6E2D70-2D7F2D97-2D9F2DA72DAF2DB72DBF2DC72DCF2DD72DDF2E32-2E7F2E9A2EF4-2EFF2FD6-2FEF2FFC-2FFF3040309730983100-3104312E-3130318F31B8-31BF31E4-31EF321F32FF4DB6-4DBF9FCC-9FFFA48D-A48FA4C7-A4CFA62C-A63FA660A661A674-A67BA698-A69FA6F8-A6FFA78D-A7FAA82C-A82FA83A-A83FA878-A87FA8C5-A8CDA8DA-A8DFA8FC-A8FFA954-A95EA97D-A97FA9CEA9DA-A9DDA9E0-A9FFAA37-AA3FAA4EAA4FAA5AAA5BAA7C-AA7FAAC3-AADAAAE0-ABBFABEEABEFABFA-ABFFD7A4-D7AFD7C7-D7CAD7FC-F8FFFA2EFA2FFA6EFA6FFADA-FAFFFB07-FB12FB18-FB1CFB37FB3DFB3FFB42FB45FBB2-FBD2FD40-FD4FFD90FD91FDC8-FDEFFDFEFDFFFE1A-FE1FFE27-FE2FFE53FE67FE6C-FE6FFE75FEFD-FF00FFBF-FFC1FFC8FFC9FFD0FFD1FFD8FFD9FFDD-FFDFFFE7FFEF-FFFBFFFEFFFF",Cc:"0000-001F007F-009F",Cf:"00AD0600-060306DD070F17B417B5200B-200F202A-202E2060-2064206A-206FFEFFFFF9-FFFB",Co:"E000-F8FF",Cs:"D800-DFFF",Cn:"03780379037F-0383038B038D03A20526-05300557055805600588058B-059005C8-05CF05EB-05EF05F5-05FF06040605061C061D0620065F070E074B074C07B2-07BF07FB-07FF082E082F083F-08FF093A093B094F095609570973-097809800984098D098E0991099209A909B109B3-09B509BA09BB09C509C609C909CA09CF-09D609D8-09DB09DE09E409E509FC-0A000A040A0B-0A0E0A110A120A290A310A340A370A3A0A3B0A3D0A43-0A460A490A4A0A4E-0A500A52-0A580A5D0A5F-0A650A76-0A800A840A8E0A920AA90AB10AB40ABA0ABB0AC60ACA0ACE0ACF0AD1-0ADF0AE40AE50AF00AF2-0B000B040B0D0B0E0B110B120B290B310B340B3A0B3B0B450B460B490B4A0B4E-0B550B58-0B5B0B5E0B640B650B72-0B810B840B8B-0B8D0B910B96-0B980B9B0B9D0BA0-0BA20BA5-0BA70BAB-0BAD0BBA-0BBD0BC3-0BC50BC90BCE0BCF0BD1-0BD60BD8-0BE50BFB-0C000C040C0D0C110C290C340C3A-0C3C0C450C490C4E-0C540C570C5A-0C5F0C640C650C70-0C770C800C810C840C8D0C910CA90CB40CBA0CBB0CC50CC90CCE-0CD40CD7-0CDD0CDF0CE40CE50CF00CF3-0D010D040D0D0D110D290D3A-0D3C0D450D490D4E-0D560D58-0D5F0D640D650D76-0D780D800D810D840D97-0D990DB20DBC0DBE0DBF0DC7-0DC90DCB-0DCE0DD50DD70DE0-0DF10DF5-0E000E3B-0E3E0E5C-0E800E830E850E860E890E8B0E8C0E8E-0E930E980EA00EA40EA60EA80EA90EAC0EBA0EBE0EBF0EC50EC70ECE0ECF0EDA0EDB0EDE-0EFF0F480F6D-0F700F8C-0F8F0F980FBD0FCD0FD9-0FFF10C6-10CF10FD-10FF1249124E124F12571259125E125F1289128E128F12B112B612B712BF12C112C612C712D7131113161317135B-135E137D-137F139A-139F13F5-13FF169D-169F16F1-16FF170D1715-171F1737-173F1754-175F176D17711774-177F17DE17DF17EA-17EF17FA-17FF180F181A-181F1878-187F18AB-18AF18F6-18FF191D-191F192C-192F193C-193F1941-1943196E196F1975-197F19AC-19AF19CA-19CF19DB-19DD1A1C1A1D1A5F1A7D1A7E1A8A-1A8F1A9A-1A9F1AAE-1AFF1B4C-1B4F1B7D-1B7F1BAB-1BAD1BBA-1BFF1C38-1C3A1C4A-1C4C1C80-1CCF1CF3-1CFF1DE7-1DFC1F161F171F1E1F1F1F461F471F4E1F4F1F581F5A1F5C1F5E1F7E1F7F1FB51FC51FD41FD51FDC1FF01FF11FF51FFF2065-206920722073208F2095-209F20B9-20CF20F1-20FF218A-218F23E9-23FF2427-243F244B-245F26CE26E226E4-26E727002705270A270B2728274C274E2753-2755275F27602795-279727B027BF27CB27CD-27CF2B4D-2B4F2B5A-2BFF2C2F2C5F2CF2-2CF82D26-2D2F2D66-2D6E2D70-2D7F2D97-2D9F2DA72DAF2DB72DBF2DC72DCF2DD72DDF2E32-2E7F2E9A2EF4-2EFF2FD6-2FEF2FFC-2FFF3040309730983100-3104312E-3130318F31B8-31BF31E4-31EF321F32FF4DB6-4DBF9FCC-9FFFA48D-A48FA4C7-A4CFA62C-A63FA660A661A674-A67BA698-A69FA6F8-A6FFA78D-A7FAA82C-A82FA83A-A83FA878-A87FA8C5-A8CDA8DA-A8DFA8FC-A8FFA954-A95EA97D-A97FA9CEA9DA-A9DDA9E0-A9FFAA37-AA3FAA4EAA4FAA5AAA5BAA7C-AA7FAAC3-AADAAAE0-ABBFABEEABEFABFA-ABFFD7A4-D7AFD7C7-D7CAD7FC-D7FFFA2EFA2FFA6EFA6FFADA-FAFFFB07-FB12FB18-FB1CFB37FB3DFB3FFB42FB45FBB2-FBD2FD40-FD4FFD90FD91FDC8-FDEFFDFEFDFFFE1A-FE1FFE27-FE2FFE53FE67FE6C-FE6FFE75FEFDFEFEFF00FFBF-FFC1FFC8FFC9FFD0FFD1FFD8FFD9FFDD-FFDFFFE7FFEF-FFF8FFFEFFFF"})}),define("ace/document",["require","exports","module","ace/lib/oop","ace/lib/event_emitter","ace/range","ace/anchor"],function(a,b,c){"use strict";var d=a("./lib/oop"),e=a("./lib/event_emitter").EventEmitter,f=a("./range").Range,g=a("./anchor").Anchor,h=function(a){this.$lines=[],Array.isArray(a)?this.insertLines(0,a):a.length==0?this.$lines=[""]:this.insert({row:0,column:0},a)};(function(){d.implement(this,e),this.setValue=function(a){var b=this.getLength();this.remove(new f(0,0,b,this.getLine(b-1).length)),this.insert({row:0,column:0},a)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(a,b){return new g(this,a,b)},"aaa".split(/a/).length==0?this.$split=function(a){return a.replace(/\r\n|\r/g,"\n").split("\n")}:this.$split=function(a){return a.split(/\r\n|\r|\n/)},this.$detectNewLine=function(a){var b=a.match(/^.*?(\r\n|\r|\n)/m);b?this.$autoNewLine=b[1]:this.$autoNewLine="\n"},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";case"auto":return this.$autoNewLine}},this.$autoNewLine="\n",this.$newLineMode="auto",this.setNewLineMode=function(a){if(this.$newLineMode===a)return;this.$newLineMode=a},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(a){return a=="\r\n"||a=="\r"||a=="\n"},this.getLine=function(a){return this.$lines[a]||""},this.getLines=function(a,b){return this.$lines.slice(a,b+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(a){if(a.start.row==a.end.row)return this.$lines[a.start.row].substring(a.start.column,a.end.column);var b=[];return b.push(this.$lines[a.start.row].substring(a.start.column)),b.push.apply(b,this.getLines(a.start.row+1,a.end.row-1)),b.push(this.$lines[a.end.row].substring(0,a.end.column)),b.join(this.getNewLineCharacter())},this.$clipPosition=function(a){var b=this.getLength();return a.row>=b&&(a.row=Math.max(0,b-1),a.column=this.getLine(b-1).length),a},this.insert=function(a,b){if(!b||b.length===0)return a;a=this.$clipPosition(a),this.getLength()<=1&&this.$detectNewLine(b);var c=this.$split(b),d=c.splice(0,1)[0],e=c.length==0?null:c.splice(c.length-1,1)[0];return a=this.insertInLine(a,d),e!==null&&(a=this.insertNewLine(a),a=this.insertLines(a.row,c),a=this.insertInLine(a,e||"")),a},this.insertLines=function(a,b){if(b.length==0)return{row:a,column:0};var c=[a,0];c.push.apply(c,b),this.$lines.splice.apply(this.$lines,c);var d=new f(a,0,a+b.length,0),e={action:"insertLines",range:d,lines:b};return this._emit("change",{data:e}),d.end},this.insertNewLine=function(a){a=this.$clipPosition(a);var b=this.$lines[a.row]||"";this.$lines[a.row]=b.substring(0,a.column),this.$lines.splice(a.row+1,0,b.substring(a.column,b.length));var c={row:a.row+1,column:0},d={action:"insertText",range:f.fromPoints(a,c),text:this.getNewLineCharacter()};return this._emit("change",{data:d}),c},this.insertInLine=function(a,b){if(b.length==0)return a;var c=this.$lines[a.row]||"";this.$lines[a.row]=c.substring(0,a.column)+b+c.substring(a.column);var d={row:a.row,column:a.column+b.length},e={action:"insertText",range:f.fromPoints(a,d),text:b};return this._emit("change",{data:e}),d},this.remove=function(a){a.start=this.$clipPosition(a.start),a.end=this.$clipPosition(a.end);if(a.isEmpty())return a.start;var b=a.start.row,c=a.end.row;if(a.isMultiLine()){var d=a.start.column==0?b:b+1,e=c-1;a.end.column>0&&this.removeInLine(c,0,a.end.column),e>=d&&this.removeLines(d,e),d!=b&&(this.removeInLine(b,a.start.column,this.getLine(b).length),this.removeNewLine(a.start.row))}else this.removeInLine(b,a.start.column,a.end.column);return a.start},this.removeInLine=function(a,b,c){if(b==c)return;var d=new f(a,b,a,c),e=this.getLine(a),g=e.substring(b,c),h=e.substring(0,b)+e.substring(c,e.length);this.$lines.splice(a,1,h);var i={action:"removeText",range:d,text:g};return this._emit("change",{data:i}),d.start},this.removeLines=function(a,b){var c=new f(a,0,b+1,0),d=this.$lines.splice(a,b-a+1),e={action:"removeLines",range:c,nl:this.getNewLineCharacter(),lines:d};return this._emit("change",{data:e}),d},this.removeNewLine=function(a){var b=this.getLine(a),c=this.getLine(a+1),d=new f(a,b.length,a+1,0),e=b+c;this.$lines.splice(a,2,e);var g={action:"removeText",range:d,text:this.getNewLineCharacter()};this._emit("change",{data:g})},this.replace=function(a,b){if(b.length==0&&a.isEmpty())return a.start;if(b==this.getTextRange(a))return a.end;this.remove(a);if(b)var c=this.insert(a.start,b);else c=a.start;return c},this.applyDeltas=function(a){for(var b=0;b<a.length;b++){var c=a[b],d=f.fromPoints(c.range.start,c.range.end);c.action=="insertLines"?this.insertLines(d.start.row,c.lines):c.action=="insertText"?this.insert(d.start,c.text):c.action=="removeLines"?this.removeLines(d.start.row,d.end.row-1):c.action=="removeText"&&this.remove(d)}},this.revertDeltas=function(a){for(var b=a.length-1;b>=0;b--){var c=a[b],d=f.fromPoints(c.range.start,c.range.end);c.action=="insertLines"?this.removeLines(d.start.row,d.end.row-1):c.action=="insertText"?this.remove(d):c.action=="removeLines"?this.insertLines(d.start.row,c.lines):c.action=="removeText"&&this.insert(d.start,c.text)}}}).call(h.prototype),b.Document=h}),define("ace/anchor",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(a,b,c){"use strict";var d=a("./lib/oop"),e=a("./lib/event_emitter").EventEmitter,f=b.Anchor=function(a,b,c){this.document=a,typeof c=="undefined"?this.setPosition(b.row,b.column):this.setPosition(b,c),this.$onChange=this.onChange.bind(this),a.on("change",this.$onChange)};(function(){d.implement(this,e),this.getPosition=function(){return this.$clipPositionToDocument(this.row,this.column)},this.getDocument=function(){return this.document},this.onChange=function(a){var b=a.data,c=b.range;if(c.start.row==c.end.row&&c.start.row!=this.row)return;if(c.start.row>this.row)return;if(c.start.row==this.row&&c.start.column>this.column)return;var d=this.row,e=this.column;b.action==="insertText"?c.start.row===d&&c.start.column<=e?c.start.row===c.end.row?e+=c.end.column-c.start.column:(e-=c.start.column,d+=c.end.row-c.start.row):c.start.row!==c.end.row&&c.start.row<d&&(d+=c.end.row-c.start.row):b.action==="insertLines"?c.start.row<=d&&(d+=c.end.row-c.start.row):b.action=="removeText"?c.start.row==d&&c.start.column<e?c.end.column>=e?e=c.start.column:e=Math.max(0,e-(c.end.column-c.start.column)):c.start.row!==c.end.row&&c.start.row<d?(c.end.row==d&&(e=Math.max(0,e-c.end.column)+c.start.column),d-=c.end.row-c.start.row):c.end.row==d&&(d-=c.end.row-c.start.row,e=Math.max(0,e-c.end.column)+c.start.column):b.action=="removeLines"&&c.start.row<=d&&(c.end.row<=d?d-=c.end.row-c.start.row:(d=c.start.row,e=0)),this.setPosition(d,e,!0)},this.setPosition=function(a,b,c){var d;c?d={row:a,column:b}:d=this.$clipPositionToDocument(a,b);if(this.row==d.row&&this.column==d.column)return;var e={row:this.row,column:this.column};this.row=d.row,this.column=d.column,this._emit("change",{old:e,value:d})},this.detach=function(){this.document.removeEventListener("change",this.$onChange)},this.$clipPositionToDocument=function(a,b){var c={};return a>=this.document.getLength()?(c.row=Math.max(0,this.document.getLength()-1),c.column=this.document.getLine(c.row).length):a<0?(c.row=0,c.column=0):(c.row=a,c.column=Math.min(this.document.getLine(c.row).length,Math.max(0,b))),b<0&&(c.column=0),c}}).call(f.prototype)}),define("ace/background_tokenizer",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(a,b,c){"use strict";var d=a("./lib/oop"),e=a("./lib/event_emitter").EventEmitter,f=function(a,b){this.running=!1,this.lines=[],this.currentLine=0,this.tokenizer=a;var c=this;this.$worker=function(){if(!c.running)return;var a=new Date,b=c.currentLine,d=c.doc,e=0,f=d.getLength();while(c.currentLine<f){c.lines[c.currentLine]=c.$tokenizeRows(c.currentLine,c.currentLine)[0],c.currentLine++,e+=1;if(e%5==0&&new Date-a>20){c.fireUpdateEvent(b,c.currentLine-1),c.running=setTimeout(c.$worker,20);return}}c.running=!1,c.fireUpdateEvent(b,f-1)}};(function(){d.implement(this,e),this.setTokenizer=function(a){this.tokenizer=a,this.lines=[],this.start(0)},this.setDocument=function(a){this.doc=a,this.lines=[],this.stop()},this.fireUpdateEvent=function(a,b){var c={first:a,last:b};this._emit("update",{data:c})},this.start=function(a){this.currentLine=Math.min(a||0,this.currentLine,this.doc.getLength()),this.lines.splice(this.currentLine,this.lines.length),this.stop(),this.running=setTimeout(this.$worker,700)},this.stop=function(){this.running&&clearTimeout(this.running),this.running=!1},this.getTokens=function(a,b){return this.$tokenizeRows(a,b)},this.getState=function(a){return this.$tokenizeRows(a,a)[0].state},this.$tokenizeRows=function(a,b){if(!this.doc||isNaN(a)||isNaN(b))return[{state:"start",tokens:[]}];var c=[],d="start",e=!1;a>0&&this.lines[a-1]?(d=this.lines[a-1].state,e=!0):a==0?(d="start",e=!0):this.lines.length>0&&(d=this.lines[this.lines.length-1].state);var f=this.doc.getLines(a,b);for(var g=a;g<=b;g++)if(!this.lines[g]){var h=this.tokenizer.getLineTokens(f[g-a]||"",d),d=h.state;c.push(h),e&&(this.lines[g]=h)}else{var h=this.lines[g];d=h.state,c.push(h)}return c}}).call(f.prototype),b.BackgroundTokenizer=f}),define("ace/edit_session/folding",["require","exports","module","ace/range","ace/edit_session/fold_line","ace/edit_session/fold","ace/token_iterator"],function(a,b,c){function h(){this.getFoldAt=function(a,b,c){var d=this.getFoldLine(a);if(!d)return null;var e=d.folds;for(var f=0;f<e.length;f++){var g=e[f];if(g.range.contains(a,b)){if(c==1&&g.range.isEnd(a,b))continue;if(c==-1&&g.range.isStart(a,b))continue;return g}}},this.getFoldsInRange=function(a){a=a.clone();var b=a.start,c=a.end,d=this.$foldData,e=[];b.column+=1,c.column-=1;for(var f=0;f<d.length;f++){var g=d[f].range.compareRange(a);if(g==2)continue;if(g==-2)break;var h=d[f].folds;for(var i=0;i<h.length;i++){var j=h[i];g=j.range.compareRange(a);if(g==-2)break;if(g==2)continue;if(g==42)break;e.push(j)}}return e},this.getAllFolds=function(){function c(b){a.push(b);if(!b.subFolds)return;for(var d=0;d<b.subFolds.length;d++)c(b.subFolds[d])}var a=[],b=this.$foldData;for(var d=0;d<b.length;d++)for(var e=0;e<b[d].folds.length;e++)c(b[d].folds[e]);return a},this.getFoldStringAt=function(a,b,c,d){d=d||this.getFoldLine(a);if(!d)return null;var e={end:{column:0}},f,g;for(var h=0;h<d.folds.length;h++){g=d.folds[h];var i=g.range.compareEnd(a,b);if(i==-1){f=this.getLine(g.start.row).substring(e.end.column,g.start.column);break}if(i===0)return null;e=g}return f||(f=this.getLine(g.start.row).substring(e.end.column)),c==-1?f.substring(0,b-e.end.column):c==1?f.substring(b-e.end.column):f},this.getFoldLine=function(a,b){var c=this.$foldData,d=0;b&&(d=c.indexOf(b)),d==-1&&(d=0);for(d;d<c.length;d++){var e=c[d];if(e.start.row<=a&&e.end.row>=a)return e;if(e.end.row>a)return null}return null},this.getNextFoldLine=function(a,b){var c=this.$foldData,d=0;b&&(d=c.indexOf(b)),d==-1&&(d=0);for(d;d<c.length;d++){var e=c[d];if(e.end.row>=a)return e}return null},this.getFoldedRowCount=function(a,b){var c=this.$foldData,d=b-a+1;for(var e=0;e<c.length;e++){var f=c[e],g=f.end.row,h=f.start.row;if(g>=b){h<b&&(h>=a?d-=b-h:d=0);break}g>=a&&(h>=a?d-=g-h:d-=g-a+1)}return d},this.$addFoldLine=function(a){return this.$foldData.push(a),this.$foldData.sort(function(a,b){return a.start.row-b.start.row}),a},this.addFold=function(a,b){var c=this.$foldData,d=!1,g;a instanceof f?g=a:g=new f(b,a),this.$clipRangeToDocument(g.range);var h=g.start.row,i=g.start.column,j=g.end.row,k=g.end.column;if(g.placeholder.length<2)throw"Placeholder has to be at least 2 characters";if(h==j&&k-i<2)throw"The range has to be at least 2 characters width";var l=this.getFoldAt(h,i,1),m=this.getFoldAt(j,k,-1);if(l&&m==l)return l.addSubFold(g);if(l&&!l.range.isStart(h,i)||m&&!m.range.isEnd(j,k))throw"A fold can't intersect already existing fold"+g.range+l.range;var n=this.getFoldsInRange(g.range);n.length>0&&(this.removeFolds(n),g.subFolds=n);for(var o=0;o<c.length;o++){var p=c[o];if(j==p.start.row){p.addFold(g),d=!0;break}if(h==p.end.row){p.addFold(g),d=!0;if(!g.sameRow){var q=c[o+1];if(q&&q.start.row==j){p.merge(q);break}}break}if(j<=p.start.row)break}return d||(p=this.$addFoldLine(new e(this.$foldData,g))),this.$useWrapMode&&this.$updateWrapData(p.start.row,p.start.row),this.$modified=!0,this._emit("changeFold",{data:g}),g},this.addFolds=function(a){a.forEach(function(a){this.addFold(a)},this)},this.removeFold=function(a){var b=a.foldLine,c=b.start.row,d=b.end.row,e=this.$foldData,f=b.folds;if(f.length==1)e.splice(e.indexOf(b),1);else if(b.range.isEnd(a.end.row,a.end.column))f.pop(),b.end.row=f[f.length-1].end.row,b.end.column=f[f.length-1].end.column;else if(b.range.isStart(a.start.row,a.start.column))f.shift(),b.start.row=f[0].start.row,b.start.column=f[0].start.column;else if(a.sameRow)f.splice(f.indexOf(a),1);else{var g=b.split(a.start.row,a.start.column);f=g.folds,f.shift(),g.start.row=f[0].start.row,g.start.column=f[0].start.column}this.$useWrapMode&&this.$updateWrapData(c,d),this.$modified=!0,this._emit("changeFold",{data:a})},this.removeFolds=function(a){var b=[];for(var c=0;c<a.length;c++)b.push(a[c]);b.forEach(function(a){this.removeFold(a)},this),this.$modified=!0},this.expandFold=function(a){this.removeFold(a),a.subFolds.forEach(function(a){this.addFold(a)},this),a.subFolds=[]},this.expandFolds=function(a){a.forEach(function(a){this.expandFold(a)},this)},this.unfold=function(a,b){var c,e;a==null?c=new d(0,0,this.getLength(),0):typeof a=="number"?c=new d(a,0,a,this.getLine(a).length):"row"in a?c=d.fromPoints(a,a):c=a,e=this.getFoldsInRange(c);if(b)this.removeFolds(e);else while(e.length)this.expandFolds(e),e=this.getFoldsInRange(c)},this.isRowFolded=function(a,b){return!!this.getFoldLine(a,b)},this.getRowFoldEnd=function(a,b){var c=this.getFoldLine(a,b);return c?c.end.row:a},this.getFoldDisplayLine=function(a,b,c,d,e){d==null&&(d=a.start.row,e=0),b==null&&(b=a.end.row,c=this.getLine(b).length);var f=this.doc,g="";return a.walk(function(a,b,c,h){if(b<d)return;if(b==d){if(c<e)return;h=Math.max(e,h)}a?g+=a:g+=f.getLine(b).substring(h,c)}.bind(this),b,c),g},this.getDisplayLine=function(a,b,c,d){var e=this.getFoldLine(a);if(!e){var f;return f=this.doc.getLine(a),f.substring(d||0,b||f.length)}return this.getFoldDisplayLine(e,a,b,c,d)},this.$cloneFoldData=function(){var a=[];return a=this.$foldData.map(function(b){var c=b.folds.map(function(a){return a.clone()});return new e(a,c)}),a},this.toggleFold=function(a){var b=this.selection,c=b.getRange(),d,e;if(c.isEmpty()){var f=c.start;d=this.getFoldAt(f.row,f.column);if(d){this.expandFold(d);return}(e=this.findMatchingBracket(f))?c.comparePoint(e)==1?c.end=e:(c.start=e,c.start.column++,c.end.column--):(e=this.findMatchingBracket({row:f.row,column:f.column+1}))?(c.comparePoint(e)==1?c.end=e:c.start=e,c.start.column++):c=this.getCommentFoldRange(f.row,f.column)||c}else{var g=this.getFoldsInRange(c);if(a&&g.length){this.expandFolds(g);return}g.length==1&&(d=g[0])}d||(d=this.getFoldAt(c.start.row,c.start.column));if(d&&d.range.toString()==c.toString()){this.expandFold(d);return}var h="...";if(!c.isMultiLine()){h=this.getTextRange(c);if(h.length<4)return;h=h.trim().substring(0,2)+".."}this.addFold(h,c)},this.getCommentFoldRange=function(a,b){var c=new g(this,a,b),e=c.getCurrentToken();if(e&&/^comment|string/.test(e.type)){var f=new d,h=new RegExp(e.type.replace(/\..*/,"\\."));do e=c.stepBackward();while(e&&h.test(e.type));c.stepForward(),f.start.row=c.getCurrentTokenRow(),f.start.column=c.getCurrentTokenColumn()+2,c=new g(this,a,b);do e=c.stepForward();while(e&&h.test(e.type));return e=c.stepBackward(),f.end.row=c.getCurrentTokenRow(),f.end.column=c.getCurrentTokenColumn()+e.value.length,f}},this.foldAll=function(a,b){var c=this.foldWidgets;b=b||this.getLength();for(var d=a||0;d<b;d++){c[d]==null&&(c[d]=this.getFoldWidget(d));if(c[d]!="start")continue;var e=this.getFoldWidgetRange(d);if(e&&e.end.row<b)try{this.addFold("...",e)}catch(f){}}},this.$foldStyles={manual:1,markbegin:1,markbeginend:1},this.$foldStyle="markbegin",this.setFoldStyle=function(a){if(!this.$foldStyles[a])throw new Error("invalid fold style: "+a+"["+Object.keys(this.$foldStyles).join(", ")+"]");if(this.$foldStyle==a)return;this.$foldStyle=a,a=="manual"&&this.unfold();var b=this.$foldMode;this.$setFolding(null),this.$setFolding(b)},this.$setFolding=function(a){if(this.$foldMode==a)return;this.$foldMode=a,this.removeListener("change",this.$updateFoldWidgets),this._emit("changeAnnotation");if(!a||this.$foldStyle=="manual"){this.foldWidgets=null;return}this.foldWidgets=[],this.getFoldWidget=a.getFoldWidget.bind(a,this,this.$foldStyle),this.getFoldWidgetRange=a.getFoldWidgetRange.bind(a,this,this.$foldStyle),this.$updateFoldWidgets=this.updateFoldWidgets.bind(this),this.on("change",this.$updateFoldWidgets)},this.onFoldWidgetClick=function(a,b){var c=this.getFoldWidget(a),d=this.getLine(a),e=b.shiftKey,f=e||b.ctrlKey||b.altKey||b.metaKey,g;c=="end"?g=this.getFoldAt(a,0,-1):g=this.getFoldAt(a,d.length,1);if(g){f?this.removeFold(g):this.expandFold(g);return}var h=this.getFoldWidgetRange(a);if(h){if(!h.isMultiLine()){g=this.getFoldAt(h.start.row,h.start.column,1);if(g&&h.isEqual(g.range)){this.removeFold(g);return}}e||this.addFold("...",h),f&&this.foldAll(h.start.row+1,h.end.row)}else f&&this.foldAll(a+1,this.getLength()),b.target.className+=" invalid"},this.updateFoldWidgets=function(a){var b=a.data,c=b.range,d=c.start.row,e=c.end.row-d;if(e===0)this.foldWidgets[d]=null;else if(b.action=="removeText"||b.action=="removeLines")this.foldWidgets.splice(d,e+1,null);else{var f=Array(e+1);f.unshift(d,1),this.foldWidgets.splice.apply(this.foldWidgets,f)}}}"use strict";var d=a("../range").Range,e=a("./fold_line").FoldLine,f=a("./fold").Fold,g=a("../token_iterator").TokenIterator;b.Folding=h}),define("ace/edit_session/fold_line",["require","exports","module","ace/range"],function(a,b,c){function e(a,b){this.foldData=a,Array.isArray(b)?this.folds=b:b=this.folds=[b];var c=b[b.length-1];this.range=new d(b[0].start.row,b[0].start.column,c.end.row,c.end.column),this.start=this.range.start,this.end=this.range.end,this.folds.forEach(function(a){a.setFoldLine(this)},this)}"use strict";var d=a("../range").Range;(function(){this.shiftRow=function(a){this.start.row+=a,this.end.row+=a,this.folds.forEach(function(b){b.start.row+=a,b.end.row+=a})},this.addFold=function(a){if(a.sameRow){if(a.start.row<this.startRow||a.endRow>this.endRow)throw"Can't add a fold to this FoldLine as it has no connection";this.folds.push(a),this.folds.sort(function(a,b){return-a.range.compareEnd(b.start.row,b.start.column)}),this.range.compareEnd(a.start.row,a.start.column)>0?(this.end.row=a.end.row,this.end.column=a.end.column):this.range.compareStart(a.end.row,a.end.column)<0&&(this.start.row=a.start.row,this.start.column=a.start.column)}else if(a.start.row==this.end.row)this.folds.push(a),this.end.row=a.end.row,this.end.column=a.end.column;else{if(a.end.row!=this.start.row)throw"Trying to add fold to FoldRow that doesn't have a matching row";this.folds.unshift(a),this.start.row=a.start.row,this.start.column=a.start.column}a.foldLine=this},this.containsRow=function(a){return a>=this.start.row&&a<=this.end.row},this.walk=function(a,b,c){var d=0,e=this.folds,f,g,h,i=!0;b==null&&(b=this.end.row,c=this.end.column);for(var j=0;j<e.length;j++){f=e[j],g=f.range.compareStart(b,c);if(g==-1){a(null,b,c,d,i);return}h=a(null,f.start.row,f.start.column,d,i),h=!h&&a(f.placeholder,f.start.row,f.start.column,d);if(h||g==0)return;i=!f.sameRow,d=f.end.column}a(null,b,c,d,i)},this.getNextFoldTo=function(a,b){var c,d;for(var e=0;e<this.folds.length;e++){c=this.folds[e],d=c.range.compareEnd(a,b);if(d==-1)return{fold:c,kind:"after"};if(d==0)return{fold:c,kind:"inside"}}return null},this.addRemoveChars=function(a,b,c){var d=this.getNextFoldTo(a,b),e,f;if(d){e=d.fold;if(d.kind=="inside"&&e.start.column!=b&&e.start.row!=a)throw"Moving characters inside of a fold should never be reached";if(e.start.row==a){f=this.folds;var g=f.indexOf(e);g==0&&(this.start.column+=c);for(g;g<f.length;g++){e=f[g],e.start.column+=c;if(!e.sameRow)return;e.end.column+=c}this.end.column+=c}}},this.split=function(a,b){var c=this.getNextFoldTo(a,b).fold,d=this.folds,f=this.foldData;if(!c)return null;var g=d.indexOf(c),h=d[g-1];this.end.row=h.end.row,this.end.column=h.end.column,d=d.splice(g,d.length-g);var i=new e(f,d);return f.splice(f.indexOf(this)+1,0,i),i},this.merge=function(a){var b=a.folds;for(var c=0;c<b.length;c++)this.addFold(b[c]);var d=this.foldData;d.splice(d.indexOf(a),1)},this.toString=function(){var a=[this.range.toString()+": ["];return this.folds.forEach(function(b){a.push(" "+b.toString())}),a.push("]"),a.join("\n")},this.idxToPosition=function(a){var b=0,c;for(var d=0;d<this.folds.length;d++){var c=this.folds[d];a-=c.start.column-b;if(a<0)return{row:c.start.row,column:c.start.column+a};a-=c.placeholder.length;if(a<0)return c.start;b=c.end.column}return{row:this.end.row,column:this.end.column+a}}}).call(e.prototype),b.FoldLine=e}),define("ace/edit_session/fold",["require","exports","module"],function(a,b,c){"use strict";var d=b.Fold=function(a,b){this.foldLine=null,this.placeholder=b,this.range=a,this.start=a.start,this.end=a.end,this.sameRow=a.start.row==a.end.row,this.subFolds=[]};(function(){this.toString=function(){return'"'+this.placeholder+'" '+this.range.toString()},this.setFoldLine=function(a){this.foldLine=a,this.subFolds.forEach(function(b){b.setFoldLine(a)})},this.clone=function(){var a=this.range.clone(),b=new d(a,this.placeholder);return this.subFolds.forEach(function(a){b.subFolds.push(a.clone())}),b},this.addSubFold=function(a){if(this.range.isEqual(a))return this;if(!this.range.containsRange(a))throw"A fold can't intersect already existing fold"+a.range+this.range;var b=a.range.start.row,c=a.range.start.column;for(var d=0,e=-1;d<this.subFolds.length;d++){e=this.subFolds[d].range.compare(b,c);if(e!=1)break}var f=this.subFolds[d];if(e==0)return f.addSubFold(a);var b=a.range.end.row,c=a.range.end.column;for(var g=d,e=-1;g<this.subFolds.length;g++){e=this.subFolds[g].range.compare(b,c);if(e!=1)break}var h=this.subFolds[g];if(e==0)throw"A fold can't intersect already existing fold"+a.range+this.range;var i=this.subFolds.splice(d,g-d,a);return a.setFoldLine(this.foldLine),a}}).call(d.prototype)}),define("ace/token_iterator",["require","exports","module"],function(a,b,c){"use strict";var d=function(a,b,c){this.$session=a,this.$row=b,this.$rowTokens=a.getTokens(b,b)[0].tokens;var d=a.getTokenAt(b,c);this.$tokenIndex=d?d.index:-1};(function(){this.stepBackward=function(){this.$tokenIndex-=1;while(this.$tokenIndex<0){this.$row-=1;if(this.$row<0)return this.$row=0,null;this.$rowTokens=this.$session.getTokens(this.$row,this.$row)[0].tokens,this.$tokenIndex=this.$rowTokens.length-1}return this.$rowTokens[this.$tokenIndex]},this.stepForward=function(){var a=this.$session.getLength();this.$tokenIndex+=1;while(this.$tokenIndex>=this.$rowTokens.length){this.$row+=1;if(this.$row>=a)return this.$row=a-1,null;this.$rowTokens=this.$session.getTokens(this.$row,this.$row)[0].tokens,this.$tokenIndex=0}return this.$rowTokens[this.$tokenIndex]},this.getCurrentToken=function(){return this.$rowTokens[this.$tokenIndex]},this.getCurrentTokenRow=function(){return this.$row},this.getCurrentTokenColumn=function(){var a=this.$rowTokens,b=this.$tokenIndex,c=a[b].start;if(c!==undefined)return c;c=0;while(b>0)b-=1,c+=a[b].value.length;return c}}).call(d.prototype),b.TokenIterator=d}),define("ace/edit_session/bracket_match",["require","exports","module","ace/token_iterator"],function(a,b,c){function e(){this.findMatchingBracket=function(a){if(a.column==0)return null;var b=this.getLine(a.row).charAt(a.column-1);if(b=="")return null;var c=b.match(/([\(\[\{])|([\)\]\}])/);return c?c[1]?this.$findClosingBracket(c[1],a):this.$findOpeningBracket(c[2],a):null},this.$brackets={")":"(","(":")","]":"[","[":"]","{":"}","}":"{"},this.$findOpeningBracket=function(a,b){var c=this.$brackets[a],e=1,f=new d(this,b.row,b.column),g=f.getCurrentToken();if(!g)return null;var h=new RegExp("(\\.?"+g.type.replace(".","|").replace("rparen","lparen|rparen")+")+"),i=b.column-f.getCurrentTokenColumn()-2,j=g.value;for(;;){while(i>=0){var k=j.charAt(i);if(k==c){e-=1;if(e==0)return{row:f.getCurrentTokenRow(),column:i+f.getCurrentTokenColumn()}}else k==a&&(e+=1);i-=1}do g=f.stepBackward();while(g&&!h.test(g.type));if(g==null)break;j=g.value,i=j.length-1}return null},this.$findClosingBracket=function(a,b){var c=this.$brackets[a],e=1,f=new d(this,b.row,b.column),g=f.getCurrentToken();if(!g)return null;var h=new RegExp("(\\.?"+g.type.replace(".","|").replace("lparen","lparen|rparen")+")+"),i=b.column-f.getCurrentTokenColumn();for(;;){var j=g.value,k=j.length;while(i<k){var l=j.charAt(i);if(l==c){e-=1;if(e==0)return{row:f.getCurrentTokenRow(),column:i+f.getCurrentTokenColumn()}}else l==a&&(e+=1);i+=1}do g=f.stepForward();while(g&&!h.test(g.type));if(g==null)break;i=0}return null}}"use strict";var d=a("../token_iterator").TokenIterator;b.BracketMatch=e}),define("ace/search",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(a,b,c){"use strict";var d=a("./lib/lang"),e=a("./lib/oop"),f=a("./range").Range,g=function(){this.$options={needle:"",backwards:!1,wrap:!1,caseSensitive:!1,wholeWord:!1,scope:g.ALL,regExp:!1}};g.ALL=1,g.SELECTION=2,function(){this.set=function(a){return e.mixin(this.$options,a),this},this.getOptions=function(){return d.copyObject(this.$options)},this.find=function(a){if(!this.$options.needle)return null;if(this.$options.backwards)var b=this.$backwardMatchIterator(a);else b=this.$forwardMatchIterator(a);var c=null;return b.forEach(function(a){return c=a,!0}),c},this.findAll=function(a){var b=this.$options;if(!b.needle)return[];if(b.backwards)var c=this.$backwardMatchIterator(a);else c=this.$forwardMatchIterator(a);var d=!b.start&&b.wrap&&b.scope==g.ALL;d&&(b.start={row:0,column:0});var e=[];return c.forEach(function(a){e.push(a)}),d&&(b.start=null),e},this.replace=function(a,b){var c=this.$assembleRegExp(),d=c.exec(a);return d&&d[0].length==a.length?this.$options.regExp?a.replace(c,b):b:null},this.$forwardMatchIterator=function(a){var b=this.$assembleRegExp(),c=this;return{forEach:function(d){c.$forwardLineIterator(a).forEach(function(a,e,f){e&&(a=a.substring(e));var g=[];a.replace(b,function(a){var b=arguments[arguments.length-2];return g.push({str:a,offset:e+b}),a});for(var h=0;h<g.length;h++){var i=g[h],j=c.$rangeFromMatch(f,i.offset,i.str.length);if(d(j))return!0}})}}},this.$backwardMatchIterator=function(a){var b=this.$assembleRegExp(),c=this;return{forEach:function(d){c.$backwardLineIterator(a).forEach(function(a,e,f){e&&(a=a.substring(e));var g=[];a.replace(b,function(a,b){return g.push({str:a,offset:e+b}),a});for(var h=g.length-1;h>=0;h--){var i=g[h],j=c.$rangeFromMatch(f,i.offset,i.str.length);if(d(j))return!0}})}}},this.$rangeFromMatch=function(a,b,c){return new f(a,b,a,b+c)},this.$assembleRegExp=function(){if(this.$options.regExp)var a=this.$options.needle;else a=d.escapeRegExp(this.$options.needle);this.$options.wholeWord&&(a="\\b"+a+"\\b");var b="g";this.$options.caseSensitive||(b+="i");var c=new RegExp(a,b);return c},this.$forwardLineIterator=function(a){function k(e){var f=a.getLine(e);return b&&e==c.end.row&&(f=f.substring(0,c.end.column)),j&&e==d.row&&(f=f.substring(0,d.column)),f}var b=this.$options.scope==g.SELECTION,c=this.$options.range||a.getSelection().getRange(),d=this.$options.start||c[b?"start":"end"],e=b?c.start.row:0,f=b?c.start.column:0,h=b?c.end.row:a.getLength()-1,i=this.$options.wrap,j=!1;return{forEach:function(a){var b=d.row,c=k(b),g=d.column,l=!1;j=!1;while(!a(c,g,b)){if(l)return;b++,g=0;if(b>h){if(!i)return;b=e,g=f,j=!0}b==d.row&&(l=!0),c=k(b)}}}},this.$backwardLineIterator=function(a){var b=this.$options.scope==g.SELECTION,c=this.$options.range||a.getSelection().getRange(),d=this.$options.start||c[b?"end":"start"],e=b?c.start.row:0,f=b?c.start.column:0,h=b?c.end.row:a.getLength()-1,i=this.$options.wrap;return{forEach:function(g){var j=d.row,k=a.getLine(j).substring(0,d.column),l=0,m=!1,n=!1;while(!g(k,l,j)){if(m)return;j--,l=0;if(j<e){if(!i)return;j=h,n=!0}j==d.row&&(m=!0),k=a.getLine(j),b&&(j==e?l=f:j==h&&(k=k.substring(0,c.end.column))),n&&j==d.row&&(l=d.column)}}}}}.call(g.prototype),b.Search=g}),define("ace/commands/command_manager",["require","exports","module","ace/lib/oop","ace/keyboard/hash_handler","ace/lib/event_emitter"],function(a,b,c){"use strict";var d=a("../lib/oop"),e=a("../keyboard/hash_handler").HashHandler,f=a("../lib/event_emitter").EventEmitter,g=function(a,b){this.platform=a,this.commands={},this.commmandKeyBinding={},this.addCommands(b),this.setDefaultHandler("exec",function(a){a.command.exec(a.editor,a.args||{})})};d.inherits(g,e),function(){d.implement(this,f),this.exec=function(a,b,c){return typeof a=="string"&&(a=this.commands[a]),a?b&&b.$readOnly&&!a.readOnly?!1:(this._emit("exec",{editor:b,command:a,args:c}),!0):!1},this.toggleRecording=function(){if(this.$inReplay)return;return this.recording?(this.macro.pop(),this.removeEventListener("exec",this.$addCommandToMacro),this.macro.length||(this.macro=this.oldMacro),this.recording=!1):(this.$addCommandToMacro||(this.$addCommandToMacro=function(a){this.macro.push([a.command,a.args])}.bind(this)),this.oldMacro=this.macro,this.macro=[],this.on("exec",this.$addCommandToMacro),this.recording=!0)},this.replay=function(a){if(this.$inReplay||!this.macro)return;if(this.recording)return this.toggleRecording();try{this.$inReplay=!0,this.macro.forEach(function(b){typeof b=="string"?this.exec(b,a):this.exec(b[0],a,b[1])},this)}finally{this.$inReplay=!1}},this.trimMacro=function(a){return a.map(function(a){return typeof a[0]!="string"&&(a[0]=a[0].name),a[1]||(a=a[0]),a})}}.call(g.prototype),b.CommandManager=g}),define("ace/keyboard/hash_handler",["require","exports","module","ace/lib/keys"],function(a,b,c){function e(a,b){this.platform=b,this.commands={},this.commmandKeyBinding={},this.addCommands(a)}"use strict";var d=a("../lib/keys");(function(){function a(a,c,e){var f,g=0,h=b(a.toLowerCase());for(var i=0,j=h.length;i<j;i++)d.KEY_MODS[h[i]]?g|=d.KEY_MODS[h[i]]:f=h[i]||"-";return{key:f,hashId:g}}function b(a){return a.trim().split(new RegExp("[\\s ]*\\-[\\s ]*","g"),999)}this.addCommand=function(a){this.commands[a.name]&&this.removeCommand(a),this.commands[a.name]=a,a.bindKey&&this._buildKeyHash(a)},this.removeCommand=function(a){var b=typeof a=="string"?a:a.name;a=this.commands[b],delete this.commands[b];var c=this.commmandKeyBinding;for(var d in c)for(var e in c[d])c[d][e]==a&&delete c[d][e]},this.addCommands=function(a){a&&Object.keys(a).forEach(function(b){var c=a[b];if(typeof c=="string")return this.bindKey(c,b);typeof c=="function"&&(c={exec:c}),c.name||(c.name=b),this.addCommand(c)},this)},this.removeCommands=function(a){Object.keys(a).forEach(function(b){this.removeCommand(a[b])},this)},this.bindKey=function(b,c){if(!b)return;var d=this.commmandKeyBinding;b.split("|").forEach(function(b){var e=a(b,c),f=e.hashId;(d[f]||(d[f]={}))[e.key]=c})},this.bindKeys=function(a){Object.keys(a).forEach(function(b){this.bindKey(b,a[b])},this)},this._buildKeyHash=function(a){var b=a.bindKey;if(!b)return;var c=typeof b=="string"?b:b[this.platform];this.bindKey(c,a)},this.findKeyCommand=function(b,c){var d=this.commmandKeyBinding;return d[b]&&d[b][c.toLowerCase()]},this.handleKeyboard=function(a,b,c,d){return{command:this.findKeyCommand(b,c)}}}).call(e.prototype),b.HashHandler=e}),define("ace/undomanager",["require","exports","module"],function(a,b,c){"use strict";var d=function(){this.reset()};(function(){this.execute=function(a){var b=a.args[0];this.$doc=a.args[1],this.$undoStack.push(b),this.$redoStack=[]},this.undo=function(a){var b=this.$undoStack.pop(),c=null;return b&&(c=this.$doc.undoChanges(b,a),this.$redoStack.push(b)),c},this.redo=function(a){var b=this.$redoStack.pop(),c=null;return b&&(c=this.$doc.redoChanges(b,a),this.$undoStack.push(b)),c},this.reset=function(){this.$undoStack=[],this.$redoStack=[]},this.hasUndo=function(){return this.$undoStack.length>0},this.hasRedo=function(){return this.$redoStack.length>0}}).call(d.prototype),b.UndoManager=d}),define("ace/virtual_renderer",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/useragent","ace/config","ace/lib/net","ace/layer/gutter","ace/layer/marker","ace/layer/text","ace/layer/cursor","ace/scrollbar","ace/renderloop","ace/lib/event_emitter","text!ace/css/editor.css"],function(a,b,c){"use strict";var d=a("./lib/oop"),e=a("./lib/dom"),f=a("./lib/event"),g=a("./lib/useragent"),h=a("./config"),i=a("./lib/net"),j=a("./layer/gutter").Gutter,k=a("./layer/marker").Marker,l=a("./layer/text").Text,m=a("./layer/cursor").Cursor,n=a("./scrollbar").ScrollBar,o=a("./renderloop").RenderLoop,p=a("./lib/event_emitter").EventEmitter,q=a("text!./css/editor.css");e.importCssString(q,"ace_editor");var r=function(a,b){var c=this;this.container=a,e.addCssClass(a,"ace_editor"),this.setTheme(b),this.$gutter=e.createElement("div"),this.$gutter.className="ace_gutter",this.container.appendChild(this.$gutter),this.scroller=e.createElement("div"),this.scroller.className="ace_scroller",this.container.appendChild(this.scroller),this.content=e.createElement("div"),this.content.className="ace_content",this.scroller.appendChild(this.content),this.$gutterLayer=new j(this.$gutter),this.$gutterLayer.on("changeGutterWidth",this.onResize.bind(this,!0)),this.$markerBack=new k(this.content);var d=this.$textLayer=new l(this.content);this.canvas=d.element,this.$markerFront=new k(this.content),this.characterWidth=d.getCharacterWidth(),this.lineHeight=d.getLineHeight(),this.$cursorLayer=new m(this.content),this.$cursorPadding=8,this.$horizScroll=!0,this.$horizScrollAlwaysVisible=!0,this.$animatedScroll=!1,this.scrollBar=new n(a),this.scrollBar.addEventListener("scroll",function(a){c.session.setScrollTop(a.data)}),this.scrollTop=0,this.scrollLeft=0,f.addListener(this.scroller,"scroll",function(){var a=c.scroller.scrollLeft;c.scrollLeft=a,c.session.setScrollLeft(a),a==0?c.$gutter.className="ace_gutter":c.$gutter.className="ace_gutter horscroll"}),this.cursorPos={row:0,column:0},this.$textLayer.addEventListener("changeCharacterSize",function(){c.characterWidth=d.getCharacterWidth(),c.lineHeight=d.getLineHeight(),c.$updatePrintMargin(),c.onResize(!0),c.$loop.schedule(c.CHANGE_FULL)}),this.$size={width:0,height:0,scrollerHeight:0,scrollerWidth:0},this.layerConfig={width:1,padding:0,firstRow:0,firstRowScreen:0,lastRow:0,lineHeight:1,characterWidth:1,minHeight:1,maxHeight:1,offset:0,height:1},this.$loop=new o(this.$renderChanges.bind(this),this.container.ownerDocument.defaultView),this.$loop.schedule(this.CHANGE_FULL),this.setPadding(4),this.$updatePrintMargin()};(function(){this.showGutter=!0,this.CHANGE_CURSOR=1,this.CHANGE_MARKER=2,this.CHANGE_GUTTER=4,this.CHANGE_SCROLL=8,this.CHANGE_LINES=16,this.CHANGE_TEXT=32,this.CHANGE_SIZE=64,this.CHANGE_MARKER_BACK=128,this.CHANGE_MARKER_FRONT=256,this.CHANGE_FULL=512,this.CHANGE_H_SCROLL=1024,d.implement(this,p),this.setSession=function(a){this.session=a,this.$cursorLayer.setSession(a),this.$markerBack.setSession(a),this.$markerFront.setSession(a),this.$gutterLayer.setSession(a),this.$textLayer.setSession(a),this.$loop.schedule(this.CHANGE_FULL)},this.updateLines=function(a,b){b===undefined&&(b=Infinity),this.$changedLines?(this.$changedLines.firstRow>a&&(this.$changedLines.firstRow=a),this.$changedLines.lastRow<b&&(this.$changedLines.lastRow=b)):this.$changedLines={firstRow:a,lastRow:b},this.$loop.schedule(this.CHANGE_LINES)},this.updateText=function(){this.$loop.schedule(this.CHANGE_TEXT)},this.updateFull=function(){this.$loop.schedule(this.CHANGE_FULL)},this.updateFontSize=function(){this.$textLayer.checkForSizeChanges()},this.onResize=function(a){var b=this.CHANGE_SIZE,c=this.$size,d=e.getInnerHeight(this.container);if(a||c.height!=d)c.height=d,this.scroller.style.height=d+"px",c.scrollerHeight=this.scroller.clientHeight,this.scrollBar.setHeight(c.scrollerHeight),this.session&&(this.session.setScrollTop(this.getScrollTop()),b|=this.CHANGE_FULL);var f=e.getInnerWidth(this.container);if(a||c.width!=f){c.width=f;var g=this.showGutter?this.$gutter.offsetWidth:0;this.scroller.style.left=g+"px",c.scrollerWidth=Math.max(0,f-g-this.scrollBar.getWidth()),this.scroller.style.width=c.scrollerWidth+"px";if(this.session.getUseWrapMode()&&this.adjustWrapLimit()||a)b|=this.CHANGE_FULL}this.$loop.schedule(b)},this.adjustWrapLimit=function(){var a=this.$size.scrollerWidth-this.$padding*2,b=Math.floor(a/this.characterWidth);return this.session.adjustWrapLimit(b)},this.setAnimatedScroll=function(a){this.$animatedScroll=a},this.getAnimatedScroll=function(){return this.$animatedScroll},this.setShowInvisibles=function(a){this.$textLayer.setShowInvisibles(a)&&this.$loop.schedule(this.CHANGE_TEXT)},this.getShowInvisibles=function(){return this.$textLayer.showInvisibles},this.$showPrintMargin=!0,this.setShowPrintMargin=function(a){this.$showPrintMargin=a,this.$updatePrintMargin()},this.getShowPrintMargin=function(){return this.$showPrintMargin},this.$printMarginColumn=80,this.setPrintMarginColumn=function(a){this.$printMarginColumn=a,this.$updatePrintMargin()},this.getPrintMarginColumn=function(){return this.$printMarginColumn},this.getShowGutter=function(){return this.showGutter},this.setShowGutter=function(a){if(this.showGutter===a)return;this.$gutter.style.display=a?"block":"none",this.showGutter=a,this.onResize(!0)},this.$updatePrintMargin=function(){var a;if(!this.$showPrintMargin&&!this.$printMarginEl)return;this.$printMarginEl||(a=e.createElement("div"),a.className="ace_print_margin_layer",this.$printMarginEl=e.createElement("div"),this.$printMarginEl.className="ace_print_margin",a.appendChild(this.$printMarginEl),this.content.insertBefore(a,this.$textLayer.element));var b=this.$printMarginEl.style;b.left=this.characterWidth*this.$printMarginColumn+this.$padding+"px",b.visibility=this.$showPrintMargin?"visible":"hidden"},this.getContainerElement=function(){return this.container},this.getMouseEventTarget=function(){return this.content},this.getTextAreaContainer=function(){return this.container},this.moveTextAreaToCursor=function(a){if(g.isIE)return;if(this.layerConfig.lastRow===0)return;var b=this.$cursorLayer.getPixelPosition();if(!b)return;var c=this.content.getBoundingClientRect(),d=this.layerConfig.offset;a.style.left=c.left+b.left+"px",a.style.top=c.top+b.top-this.scrollTop+d+"px"},this.getFirstVisibleRow=function(){return this.layerConfig.firstRow},this.getFirstFullyVisibleRow=function(){return this.layerConfig.firstRow+(this.layerConfig.offset===0?0:1)},this.getLastFullyVisibleRow=function(){var a=Math.floor((this.layerConfig.height+this.layerConfig.offset)/this.layerConfig.lineHeight);return this.layerConfig.firstRow-1+a},this.getLastVisibleRow=function(){return this.layerConfig.lastRow},this.$padding=null,this.setPadding=function(a){this.$padding=a,this.$textLayer.setPadding(a),this.$cursorLayer.setPadding(a),this.$markerFront.setPadding(a),this.$markerBack.setPadding(a),this.$loop.schedule(this.CHANGE_FULL),this.$updatePrintMargin()},this.getHScrollBarAlwaysVisible=function(){return this.$horizScrollAlwaysVisible},this.setHScrollBarAlwaysVisible=function(a){this.$horizScrollAlwaysVisible!=a&&(this.$horizScrollAlwaysVisible=a,(!this.$horizScrollAlwaysVisible||!this.$horizScroll)&&this.$loop.schedule(this.CHANGE_SCROLL))},this.$updateScrollBar=function(){this.scrollBar.setInnerHeight(this.layerConfig.maxHeight),this.scrollBar.setScrollTop(this.scrollTop)},this.$renderChanges=function(a){if(!a||!this.session||!this.container.offsetWidth)return;(a&this.CHANGE_FULL||a&this.CHANGE_SIZE||a&this.CHANGE_TEXT||a&this.CHANGE_LINES||a&this.CHANGE_SCROLL)&&this.$computeLayerConfig();if(a&this.CHANGE_H_SCROLL){this.scroller.scrollLeft=this.scrollLeft;var b=this.scroller.scrollLeft;this.scrollLeft=b,this.session.setScrollLeft(b)}if(a&this.CHANGE_FULL){this.$textLayer.checkForSizeChanges(),this.$updateScrollBar(),this.$textLayer.update(this.layerConfig),this.showGutter&&this.$gutterLayer.update(this.layerConfig),this.$markerBack.update(this.layerConfig),this.$markerFront.update(this.layerConfig),this.$cursorLayer.update(this.layerConfig);return}if(a&this.CHANGE_SCROLL){this.$updateScrollBar(),a&this.CHANGE_TEXT||a&this.CHANGE_LINES?this.$textLayer.update(this.layerConfig):this.$textLayer.scrollLines(this.layerConfig),this.showGutter&&this.$gutterLayer.update(this.layerConfig),this.$markerBack.update(this.layerConfig),this.$markerFront.update(this.layerConfig),this.$cursorLayer.update(this.layerConfig);return}a&this.CHANGE_TEXT?(this.$textLayer.update(this.layerConfig),this.showGutter&&this.$gutterLayer.update(this.layerConfig)):a&this.CHANGE_LINES?this.$updateLines()&&(this.$updateScrollBar(),this.showGutter&&this.$gutterLayer.update(this.layerConfig)):a&this.CHANGE_GUTTER&&this.showGutter&&this.$gutterLayer.update(this.layerConfig),a&this.CHANGE_CURSOR&&this.$cursorLayer.update(this.layerConfig),a&(this.CHANGE_MARKER|this.CHANGE_MARKER_FRONT)&&this.$markerFront.update(this.layerConfig),a&(this.CHANGE_MARKER|this.CHANGE_MARKER_BACK)&&this.$markerBack.update(this.layerConfig),a&this.CHANGE_SIZE&&this.$updateScrollBar()},this.$computeLayerConfig=function(){var a=this.session,b=this.scrollTop%this.lineHeight,c=this.$size.scrollerHeight+this.lineHeight,d=this.$getLongestLine(),e=this.$horizScrollAlwaysVisible||this.$size.scrollerWidth-d<0,f=this.$horizScroll!==e;this.$horizScroll=e,f&&(this.scroller.style.overflowX=e?"scroll":"hidden",e||this.session.setScrollLeft(0));var g=this.session.getScreenLength()*this.lineHeight;this.session.setScrollTop(Math.max(0,Math.min(this.scrollTop,g-this.$size.scrollerHeight)));var h=Math.ceil(c/this.lineHeight)-1,i=Math.max(0,Math.round((this.scrollTop-b)/this.lineHeight)),j=i+h,k,l,m={lineHeight:this.lineHeight};i=a.screenToDocumentRow(i,0);var n=a.getFoldLine(i);n&&(i=n.start.row),k=a.documentToScreenRow(i,0),l=a.getRowHeight(m,i),j=Math.min(a.screenToDocumentRow(j,0),a.getLength()-1),c=this.$size.scrollerHeight+a.getRowHeight(m,j)+l,b=this.scrollTop-k*this.lineHeight,this.layerConfig={width:d,padding:this.$padding,firstRow:i,firstRowScreen:k,lastRow:j,lineHeight:this.lineHeight,characterWidth:this.characterWidth,minHeight:c,maxHeight:g,offset:b,height:this.$size.scrollerHeight},this.$gutterLayer.element.style.marginTop=-b+"px",this.content.style.marginTop=-b+"px",this.content.style.width=d+2*this.$padding+"px",this.content.style.height=c+"px",f&&this.onResize(!0)},this.$updateLines=function(){var a=this.$changedLines.firstRow,b=this.$changedLines.lastRow;this.$changedLines=null;var c=this.layerConfig;if(c.width!=this.$getLongestLine())return this.$textLayer.update(c);if(a>c.lastRow+1)return;if(b<c.firstRow)return;if(b===Infinity){this.showGutter&&this.$gutterLayer.update(c),this.$textLayer.update(c);return}return this.$textLayer.updateLines(c,a,b),!0},this.$getLongestLine=function(){var a=this.session.getScreenWidth();return this.$textLayer.showInvisibles&&(a+=1),Math.max(this.$size.scrollerWidth-2*this.$padding,Math.round(a*this.characterWidth))},this.updateFrontMarkers=function(){this.$markerFront.setMarkers(this.session.getMarkers(!0)),this.$loop.schedule(this.CHANGE_MARKER_FRONT)},this.updateBackMarkers=function(){this.$markerBack.setMarkers(this.session.getMarkers()),this.$loop.schedule(this.CHANGE_MARKER_BACK)},this.addGutterDecoration=function(a,b){this.$gutterLayer.addGutterDecoration(a,b),this.$loop.schedule(this.CHANGE_GUTTER)},this.removeGutterDecoration=function(a,b){this.$gutterLayer.removeGutterDecoration(a,b),this.$loop.schedule(this.CHANGE_GUTTER)},this.setBreakpoints=function(a){this.$gutterLayer.setBreakpoints(a),this.$loop.schedule(this.CHANGE_GUTTER)},this.setAnnotations=function(a){this.$gutterLayer.setAnnotations(a),this.$loop.schedule(this.CHANGE_GUTTER)},this.updateCursor=function(){this.$loop.schedule(this.CHANGE_CURSOR)},this.hideCursor=function(){this.$cursorLayer.hideCursor()},this.showCursor=function(){this.$cursorLayer.showCursor()},this.scrollSelectionIntoView=function(a,b){this.scrollCursorIntoView(a),this.scrollCursorIntoView(b)},this.scrollCursorIntoView=function(a){if(this.$size.scrollerHeight===0)return;var b=this.$cursorLayer.getPixelPosition(a),c=b.left,d=b.top;this.scrollTop>d&&this.session.setScrollTop(d),this.scrollTop+this.$size.scrollerHeight<d+this.lineHeight&&this.session.setScrollTop(d+this.lineHeight-this.$size.scrollerHeight);var e=this.scrollLeft;e>c&&(c<this.$padding+2*this.layerConfig.characterWidth&&(c=0),this.session.setScrollLeft(c)),e+this.$size.scrollerWidth<c+this.characterWidth&&this.session.setScrollLeft(Math.round(c+this.characterWidth-this.$size.scrollerWidth))},this.getScrollTop=function(){return this.session.getScrollTop()},this.getScrollLeft=function(){return this.session.getScrollLeft()},this.getScrollTopRow=function(){return this.scrollTop/this.lineHeight},this.getScrollBottomRow=function(){return Math.max(0,Math.floor((this.scrollTop+this.$size.scrollerHeight)/this.lineHeight)-1)},this.scrollToRow=function(a){this.session.setScrollTop(a*this.lineHeight)},this.STEPS=10,this.$calcSteps=function(a,b){var c=0,d=this.STEPS,e=[],f=function(a,b,c){return(a/=.5)<1?c/2*Math.pow(a,3)+b:c/2*(Math.pow(a-2,3)+2)+b};for(c=0;c<d;++c)e.push(f(c/this.STEPS,a,b-a));return e.push(b),e},this.scrollToLine=function(a,b){var c=this.$cursorLayer.getPixelPosition({row:a,column:0}),d=c.top;b&&(d-=this.$size.scrollerHeight/2);if(this.$animatedScroll&&Math.abs(d-this.scrollTop)<1e4){var e=this,f=e.$calcSteps(this.scrollTop,d);clearInterval(this.$timer),this.$timer=setInterval(function(){e.session.setScrollTop(f.shift()),f.length||clearInterval(e.$timer)},10)}else this.session.setScrollTop(d)},this.scrollToY=function(a){this.scrollTop!==a&&(this.$loop.schedule(this.CHANGE_SCROLL),this.scrollTop=a)},this.scrollToX=function(a){a<=this.$padding&&(a=0),this.scrollLeft!==a&&(this.scrollLeft=a),this.$loop.schedule(this.CHANGE_H_SCROLL)},this.scrollBy=function(a,b){b&&this.session.setScrollTop(this.session.getScrollTop()+b),a&&this.session.setScrollLeft(this.session.getScrollLeft()+a)},this.isScrollableBy=function(a,b){if(b<0&&this.session.getScrollTop()>0)return!0;if(b>0&&this.session.getScrollTop()+this.$size.scrollerHeight<this.layerConfig.maxHeight)return!0},this.pixelToScreenCoordinates=function(a,b){var c=this.scroller.getBoundingClientRect(),d=Math.round((a+this.scrollLeft-c.left-this.$padding-e.getPageScrollLeft())/this.characterWidth),f=Math.floor((b+this.scrollTop-c.top-e.getPageScrollTop())/this.lineHeight);return{row:f,column:d}},this.screenToTextCoordinates=function(a,b){var c=this.scroller.getBoundingClientRect(),d=Math.round((a+this.scrollLeft-c.left-this.$padding-e.getPageScrollLeft())/this.characterWidth),f=Math.floor((b+this.scrollTop-c.top-e.getPageScrollTop())/this.lineHeight);return this.session.screenToDocumentPosition(f,Math.max(d,0))},this.textToScreenCoordinates=function(a,b){var c=this.scroller.getBoundingClientRect(),d=this.session.documentToScreenPosition(a,b),e=this.$padding+Math.round(d.column*this.characterWidth),f=d.row*this.lineHeight;return{pageX:c.left+e-this.scrollLeft,pageY:c.top+f-this.scrollTop}},this.visualizeFocus=function(){e.addCssClass(this.container,"ace_focus")},this.visualizeBlur=function(){e.removeCssClass(this.container,"ace_focus")},this.showComposition=function(a){this.$composition||(this.$composition=e.createElement("div"),this.$composition.className="ace_composition",this.content.appendChild(this.$composition)),this.$composition.innerHTML=" ";var b=this.$cursorLayer.getPixelPosition(),c=this.$composition.style;c.top=b.top+"px",c.left=b.left+this.$padding+"px",c.height=this.lineHeight+"px",this.hideCursor()},this.setCompositionText=function(a){e.setInnerText(this.$composition,a)},this.hideComposition=function(){this.showCursor();if(!this.$composition)return;var a=this.$composition.style;a.top="-10000px",a.left="-10000px"},this._loadTheme=function(a,b){if(!h.get("packaged"))return b();var c=a.split("/").pop(),d=h.get("themePath")+"/theme-"+c+h.get("suffix");i.loadScript(d,b)},this.setTheme=function(b){function h(a){e.importCssString(a.cssText,a.cssClass,c.container.ownerDocument),c.$theme&&e.removeCssClass(c.container,c.$theme),c.$theme=a?a.cssClass:null,c.$theme&&e.addCssClass(c.container,c.$theme),a&&a.isDark?e.addCssClass(c.container,"ace_dark"):e.removeCssClass(c.container,"ace_dark"),c.$size&&(c.$size.width=0,c.onResize())}var c=this;this.$themeValue=b;if(!b||typeof b=="string"){var d=b||"ace/theme/textmate",f;try{f=a(d)}catch(g){}if(f)return h(f);c._loadTheme(d,function(){a([d],function(a){if(c.$themeValue!==b)return;h(a)})})}else h(b)},this.getTheme=function(){return this.$themeValue},this.setStyle=function(b){e.addCssClass(this.container,b)},this.unsetStyle=function(b){e.removeCssClass(this.container,b)},this.destroy=function(){this.$textLayer.destroy(),this.$cursorLayer.destroy()}}).call(r.prototype),b.VirtualRenderer=r}),define("ace/layer/gutter",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/event_emitter"],function(a,b,c){"use strict";var d=a("../lib/dom"),e=a("../lib/oop"),f=a("../lib/event_emitter").EventEmitter,g=function(a){this.element=d.createElement("div"),this.element.className="ace_layer ace_gutter-layer",a.appendChild(this.element),this.setShowFoldWidgets(this.$showFoldWidgets),this.gutterWidth=0,this.$breakpoints=[],this.$annotations=[],this.$decorations=[]};(function(){e.implement(this,f),this.setSession=function(a){this.session=a},this.addGutterDecoration=function(a,b){this.$decorations[a]||(this.$decorations[a]=""),this.$decorations[a]+=" "+b},this.removeGutterDecoration=function(a,b){this.$decorations[a]=this.$decorations[a].replace(" "+b,"")},this.setBreakpoints=function(a){this.$breakpoints=a.concat()},this.setAnnotations=function(a){this.$annotations=[];for(var b in a)if(a.hasOwnProperty(b)){var c=a[b];if(!c)continue;var d=this.$annotations[b]={text:[]};for(var e=0;e<c.length;e++){var f=c[e],g=f.text.replace(/"/g,""").replace(/'/g,"’").replace(/</,"<");d.text.indexOf(g)===-1&&d.text.push(g);var h=f.type;h=="error"?d.className="ace_error":h=="warning"&&d.className!="ace_error"?d.className="ace_warning":h=="info"&&!d.className&&(d.className="ace_info")}}},this.update=function(a){this.$config=a;var b={className:"",text:[]},c=[],e=a.firstRow,f=a.lastRow,g=this.session.getNextFoldLine(e),h=g?g.start.row:Infinity,i=this.$showFoldWidgets&&this.session.foldWidgets;for(;;){e>h&&(e=g.end.row+1,g=this.session.getNextFoldLine(e,g),h=g?g.start.row:Infinity);if(e>f)break;var j=this.$annotations[e]||b;c.push("<div class='ace_gutter-cell",this.$decorations[e]||"",this.$breakpoints[e]?" ace_breakpoint ":" ",j.className,"' title='",j.text.join("\n"),"' style='height:",a.lineHeight,"px;'>",e+1);if(i){var k=i[e];k==null&&(k=i[e]=this.session.getFoldWidget(e)),k&&c.push("<span class='ace_fold-widget ",k,k=="start"&&e==h&&e<g.end.row?" closed":" open","'></span>")}var l=this.session.getRowLength(e)-1;while(l--)c.push("</div><div class='ace_gutter-cell' style='height:",a.lineHeight,"px'>¦");c.push("</div>"),e++}this.element=d.setInnerHtml(this.element,c.join("")),this.element.style.height=a.minHeight+"px";var m=this.element.offsetWidth;m!==this.gutterWidth&&(this.gutterWidth=m,this._emit("changeGutterWidth",m))},this.$showFoldWidgets=!0,this.setShowFoldWidgets=function(a){a?d.addCssClass(this.element,"ace_folding-enabled"):d.removeCssClass(this.element,"ace_folding-enabled"),this.$showFoldWidgets=a},this.getShowFoldWidgets=function(){return this.$showFoldWidgets}}).call(g.prototype),b.Gutter=g}),define("ace/layer/marker",["require","exports","module","ace/range","ace/lib/dom"],function(a,b,c){"use strict";var d=a("../range").Range,e=a("../lib/dom"),f=function(a){this.element=e.createElement("div"),this.element.className="ace_layer ace_marker-layer",a.appendChild(this.element)};(function(){this.$padding=0,this.setPadding=function(a){this.$padding=a},this.setSession=function(a){this.session=a},this.setMarkers=function(a){this.markers=a},this.update=function(a){var a=a||this.config;if(!a)return;this.config=a;var b=[];for(var c in this.markers){var d=this.markers[c],f=d.range.clipRows(a.firstRow,a.lastRow);if(f.isEmpty())continue;f=f.toScreenRange(this.session);if(d.renderer){var g=this.$getTop(f.start.row,a),h=Math.round(this.$padding+f.start.column*a.characterWidth);d.renderer(b,f,h,g,a)}else f.isMultiLine()?d.type=="text"?this.drawTextMarker(b,f,d.clazz,a):this.drawMultiLineMarker(b,f,d.clazz,a,d.type):this.drawSingleLineMarker(b,f,d.clazz,a,null,d.type)}this.element=e.setInnerHtml(this.element,b.join(""))},this.$getTop=function(a,b){return(a-b.firstRowScreen)*b.lineHeight},this.drawTextMarker=function(a,b,c,e){var f=b.start.row,g=new d(f,b.start.column,f,this.session.getScreenLastRowColumn(f));this.drawSingleLineMarker(a,g,c,e,1,"text"),f=b.end.row,g=new d(f,0,f,b.end.column),this.drawSingleLineMarker(a,g,c,e,0,"text");for(f=b.start.row+1;f<b.end.row;f++)g.start.row=f,g.end.row=f,g.end.column=this.session.getScreenLastRowColumn(f),this.drawSingleLineMarker(a,g,c,e,1,"text")},this.drawMultiLineMarker=function(a,b,c,d,e){var f=e==="background"?0:this.$padding,g=d.width+2*this.$padding-f,h=d.lineHeight,i=Math.round(g-b.start.column*d.characterWidth),j=this.$getTop(b.start.row,d),k=Math.round(f+b.start.column*d.characterWidth);a.push("<div class='",c,"' style='","height:",h,"px;","width:",i,"px;","top:",j,"px;","left:",k,"px;'></div>"),j=this.$getTop(b.end.row,d),i=Math.round(b.end.column*d.characterWidth),a.push("<div class='",c,"' style='","height:",h,"px;","width:",i,"px;","top:",j,"px;","left:",f,"px;'></div>"),h=(b.end.row-b.start.row-1)*d.lineHeight;if(h<0)return;j=this.$getTop(b.start.row+1,d),a.push("<div class='",c,"' style='","height:",h,"px;","width:",g,"px;","top:",j,"px;","left:",f,"px;'></div>")},this.drawSingleLineMarker=function(a,b,c,d,e,f){var g=f==="background"?0:this.$padding,h=d.lineHeight;if(f==="background")var i=d.width;else i=Math.round((b.end.column+(e||0)-b.start.column)*d.characterWidth);var j=this.$getTop(b.start.row,d),k=Math.round(g+b.start.column*d.characterWidth);a.push("<div class='",c,"' style='","height:",h,"px;","width:",i,"px;","top:",j,"px;","left:",k,"px;'></div>")}}).call(f.prototype),b.Marker=f}),define("ace/layer/text",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/lib/event_emitter"],function(a,b,c){"use strict";var d=a("../lib/oop"),e=a("../lib/dom"),f=a("../lib/lang"),g=a("../lib/useragent"),h=a("../lib/event_emitter").EventEmitter,i=function(a){this.element=e.createElement("div"),this.element.className="ace_layer ace_text-layer",a.appendChild(this.element),this.$characterSize=this.$measureSizes()||{width:0,height:0},this.$pollSizeChanges()};(function(){d.implement(this,h),this.EOF_CHAR="¶",this.EOL_CHAR="¬",this.TAB_CHAR="→",this.SPACE_CHAR="·",this.$padding=0,this.setPadding=function(a){this.$padding=a,this.element.style.padding="0 "+a+"px"},this.getLineHeight=function(){return this.$characterSize.height||1},this.getCharacterWidth=function(){return this.$characterSize.width||1},this.checkForSizeChanges=function(){var a=this.$measureSizes();a&&(this.$characterSize.width!==a.width||this.$characterSize.height!==a.height)&&(this.$characterSize=a,this._emit("changeCharacterSize",{data:a}))},this.$pollSizeChanges=function(){var a=this;this.$pollSizeChangesTimer=setInterval(function(){a.checkForSizeChanges()},500)},this.$fontStyles={fontFamily:1,fontSize:1,fontWeight:1,fontStyle:1,lineHeight:1},this.$measureSizes=g.isIE||g.isOldGecko?function(){var a=1e3;if(!this.$measureNode){var b=this.$measureNode=e.createElement("div"),c=b.style;c.width=c.height="auto",c.left=c.top=-a*40+"px",c.visibility="hidden",c.position="fixed",c.overflow="visible",c.whiteSpace="nowrap",b.innerHTML=f.stringRepeat("Xy",a);if(this.element.ownerDocument.body)this.element.ownerDocument.body.appendChild(b);else{var d=this.element.parentNode;while(!e.hasCssClass(d,"ace_editor"))d=d.parentNode;d.appendChild(b)}}if(!this.element.offsetWidth)return null;var c=this.$measureNode.style,g=e.computedStyle(this.element);for(var h in this.$fontStyles)c[h]=g[h];var i={height:this.$measureNode.offsetHeight,width:this.$measureNode.offsetWidth/(a*2)};return i.width==0||i.height==0?null:i}:function(){if(!this.$measureNode){var a=this.$measureNode=e.createElement("div"),b=a.style;b.width=b.height="auto",b.left=b.top="-100px",b.visibility="hidden",b.position="fixed",b.overflow="visible",b.whiteSpace="nowrap",a.innerHTML="X";var c=this.element.parentNode;while(c&&!e.hasCssClass(c,"ace_editor"))c=c.parentNode;if(!c)return this.$measureNode=null;c.appendChild(a)}var d=this.$measureNode.getBoundingClientRect(),f={height:d.height,width:d.width};return f.width==0||f.height==0?null:f},this.setSession=function(a){this.session=a},this.showInvisibles=!1,this.setShowInvisibles=function(a){return this.showInvisibles==a?!1:(this.showInvisibles=a,!0)},this.$tabStrings=[],this.$computeTabString=function(){var a=this.session.getTabSize(),b=this.$tabStrings=[0];for(var c=1;c<a+1;c++)this.showInvisibles?b.push("<span class='ace_invisible'>"+this.TAB_CHAR+(new Array(c)).join(" ")+"</span>"):b.push((new Array(c+1)).join(" "))},this.updateLines=function(a,b,c){this.$computeTabString(),(this.config.lastRow!=a.lastRow||this.config.firstRow!=a.firstRow)&&this.scrollLines(a),this.config=a;var d=Math.max(b,a.firstRow),f=Math.min(c,a.lastRow),g=this.element.childNodes,h=0;for(var i=a.firstRow;i<d;i++){var j=this.session.getFoldLine(i);if(j){if(j.containsRow(d)){d=j.start.row;break}i=j.end.row}h++}for(var k=d;k<=f;k++){var l=g[h++];if(!l)continue;var m=[],n=this.session.getTokens(k,k);this.$renderLine(m,k,n[0].tokens,!this.$useLineGroups()),l=e.setInnerHtml(l,m.join("")),k=this.session.getRowFoldEnd(k)}},this.scrollLines=function(a){this.$computeTabString();var b=this.config;this.config=a;if(!b||b.lastRow<a.firstRow)return this.update(a);if(a.lastRow<b.firstRow)return this.update(a);var c=this.element;if(b.firstRow<a.firstRow)for(var d=this.session.getFoldedRowCount(b.firstRow,a.firstRow-1);d>0;d--)c.removeChild(c.firstChild);if(b.lastRow>a.lastRow)for(var d=this.session.getFoldedRowCount(a.lastRow+1,b.lastRow);d>0;d--)c.removeChild(c.lastChild);if(a.firstRow<b.firstRow){var e=this.$renderLinesFragment(a,a.firstRow,b.firstRow-1);c.firstChild?c.insertBefore(e,c.firstChild):c.appendChild(e)}if(a.lastRow>b.lastRow){var e=this.$renderLinesFragment(a,b.lastRow+1,a.lastRow);c.appendChild(e)}},this.$renderLinesFragment=function(a,b,c){var d=this.element.ownerDocument.createDocumentFragment(),f=b,g=this.session.getNextFoldLine(f),h=g?g.start.row:Infinity;for(;;){f>h&&(f=g.end.row+1,g=this.session.getNextFoldLine(f,g),h=g?g.start.row:Infinity);if(f>c)break;var i=e.createElement("div"),j=[],k=this.session.getTokens(f,f);k.length==1&&this.$renderLine(j,f,k[0].tokens,!1),i.innerHTML=j.join("");if(this.$useLineGroups())i.className="ace_line_group",d.appendChild(i);else{var l=i.childNodes;while(l.length)d.appendChild(l[0])}f++}return d},this.update=function(a){this.$computeTabString(),this.config=a;var b=[],c=a.firstRow,d=a.lastRow,f=c,g=this.session.getNextFoldLine(f),h=g?g.start.row:Infinity;for(;;){f>h&&(f=g.end.row+1,g=this.session.getNextFoldLine(f,g),h=g?g.start.row:Infinity);if(f>d)break;this.$useLineGroups()&&b.push("<div class='ace_line_group'>");var i=this.session.getTokens(f,f);i.length==1&&this.$renderLine(b,f,i[0].tokens,!1),this.$useLineGroups()&&b.push("</div>"),f++}this.element=e.setInnerHtml(this.element,b.join(""))},this.$textToken={text:!0,rparen:!0,lparen:!0},this.$renderToken=function(a,b,c,d){var e=this,f=/\t|&|<|( +)|([\v\f \u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000])|[\u1100-\u115F]|[\u11A3-\u11A7]|[\u11FA-\u11FF]|[\u2329-\u232A]|[\u2E80-\u2E99]|[\u2E9B-\u2EF3]|[\u2F00-\u2FD5]|[\u2FF0-\u2FFB]|[\u3000-\u303E]|[\u3041-\u3096]|[\u3099-\u30FF]|[\u3105-\u312D]|[\u3131-\u318E]|[\u3190-\u31BA]|[\u31C0-\u31E3]|[\u31F0-\u321E]|[\u3220-\u3247]|[\u3250-\u32FE]|[\u3300-\u4DBF]|[\u4E00-\uA48C]|[\uA490-\uA4C6]|[\uA960-\uA97C]|[\uAC00-\uD7A3]|[\uD7B0-\uD7C6]|[\uD7CB-\uD7FB]|[\uF900-\uFAFF]|[\uFE10-\uFE19]|[\uFE30-\uFE52]|[\uFE54-\uFE66]|[\uFE68-\uFE6B]|[\uFF01-\uFF60]|[\uFFE0-\uFFE6]/g,h=function(a,c,d,f,h){if(a.charCodeAt(0)==32)return(new Array(a.length+1)).join(" ");if(a==" "){var i=e.session.getScreenTabSize(b+f);return b+=i-1,e.$tabStrings[i]}if(a=="&")return g.isOldGecko?"&":"&";if(a=="<")return"<";if(a==" "){var j=e.showInvisibles?"ace_cjk ace_invisible":"ace_cjk",k=e.showInvisibles?e.SPACE_CHAR:"";return b+=1,"<span class='"+j+"' style='width:"+e.config.characterWidth*2+"px'>"+k+"</span>"}if(a.match(/[\v\f \u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000]/)){if(e.showInvisibles){var k=(new Array(a.length+1)).join(e.SPACE_CHAR);return"<span class='ace_invisible'>"+k+"</span>"}return" "}return b+=1,"<span class='ace_cjk' style='width:"+e.config.characterWidth*2+"px'>"+a+"</span>"},i=d.replace(f,h);if(!this.$textToken[c.type]){var j="ace_"+c.type.replace(/\./g," ace_"),k="";c.type=="fold"&&(k=" style='width:"+c.value.length*this.config.characterWidth+"px;' "),a.push("<span class='",j,"'",k,">",i,"</span>")}else a.push(i);return b+d.length},this.$renderLineCore=function(a,b,c,d,e){var f=0,g=0,h,i=0,j=this;!d||d.length==0?h=Number.MAX_VALUE:h=d[0],e||a.push("<div class='ace_line' style='height:",this.config.lineHeight,"px","'>");for(var k=0;k<c.length;k++){var l=c[k],m=l.value;if(f+m.length<h)i=j.$renderToken(a,i,l,m),f+=m.length;else{while(f+m.length>=h)i=j.$renderToken(a,i,l,m.substring(0,h-f)),m=m.substring(h-f),f=h,e||a.push("</div>","<div class='ace_line' style='height:",this.config.lineHeight,"px","'>"),g++,i=0,h=d[g]||Number.MAX_VALUE;m.length!=0&&(f+=m.length,i=j.$renderToken(a,i,l,m))}}this.showInvisibles&&(b!==this.session.getLength()-1?a.push("<span class='ace_invisible'>"+this.EOL_CHAR+"</span>"):a.push("<span class='ace_invisible'>"+this.EOF_CHAR+"</span>")),e||a.push("</div>")},this.$renderLine=function(a,b,c,d){if(!this.session.isRowFolded(b)){var e=this.session.getRowSplitData(b);this.$renderLineCore(a,b,c,e,d)}else this.$renderFoldLine(a,b,c,d)},this.$renderFoldLine=function(a,b,c,d){function h(a,b,c){var d=0,e=0;while(e+a[d].value.length<b){e+=a[d].value.length,d++;if(d==a.length)return}if(e!=b){var f=a[d].value.substring(b-e);f.length>c-b&&(f=f.substring(0,c-b)),g.push({type:a[d].type,value:f}),e=b+f.length,d+=1}while(e<c){var f=a[d].value;f.length+e>c&&(f=f.substring(0,c-e)),g.push({type:a[d].type,value:f}),e+=f.length,d+=1}}var e=this.session,f=e.getFoldLine(b),g=[];f.walk(function(a,b,d,e,f){a?g.push({type:"fold",value:a}):(f&&(c=this.session.getTokens(b,b)[0].tokens),c.length!=0&&h(c,e,d))}.bind(this),f.end.row,this.session.getLine(f.end.row).length);var i=this.session.$useWrapMode?this.session.$wrapData[b]:null;this.$renderLineCore(a,b,g,i,d)},this.$useLineGroups=function(){return this.session.getUseWrapMode()},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.$measureNode&&this.$measureNode.parentNode.removeChild(this.$measureNode),delete this.$measureNode}}).call(i.prototype),b.Text=i}),define("ace/layer/cursor",["require","exports","module","ace/lib/dom"],function(a,b,c){"use strict";var d=a("../lib/dom"),e=function(a){this.element=d.createElement("div"),this.element.className="ace_layer ace_cursor-layer",a.appendChild(this.element),this.isVisible=!1,this.cursors=[],this.cursor=this.addCursor()};(function(){this.$padding=0,this.setPadding=function(a){this.$padding=a},this.setSession=function(a){this.session=a},this.addCursor=function(){var a=d.createElement("div"),b="ace_cursor";return this.isVisible||(b+=" ace_hidden"),this.overwrite&&(b+=" ace_overwrite"),a.className=b,this.element.appendChild(a),this.cursors.push(a),a},this.removeCursor=function(){if(this.cursors.length>1){var a=this.cursors.pop();return a.parentNode.removeChild(a),a}},this.hideCursor=function(){this.isVisible=!1;for(var a=this.cursors.length;a--;)d.addCssClass(this.cursors[a],"ace_hidden");clearInterval(this.blinkId)},this.showCursor=function(){this.isVisible=!0;for(var a=this.cursors.length;a--;)d.removeCssClass(this.cursors[a],"ace_hidden");this.element.style.visibility="",this.restartTimer()},this.restartTimer=function(){clearInterval(this.blinkId);if(!this.isVisible)return;var a=this.element;this.blinkId=setInterval(function(){a.style.visibility="hidden",setTimeout(function(){a.style.visibility="visible"},400)},1e3)},this.getPixelPosition=function(a,b){if(!this.config||!this.session)return{left:0,top:0};a||(a=this.session.selection.getCursor());var c=this.session.documentToScreenPosition(a),d=Math.round(this.$padding+c.column*this.config.characterWidth),e=(c.row-(b?this.config.firstRowScreen:0))*this.config.lineHeight;return{left:d,top:e}},this.update=function(a){this.config=a;if(this.session.selectionMarkerCount>1){var b=this.session.$selectionMarkers,c=0,d,e=0;for(var c=b.length;c--;){d=b[c];var f=this.getPixelPosition(d.cursor,!0),g=(this.cursors[e++]||this.addCursor()).style;g.left=f.left+"px",g.top=f.top+"px",g.width=a.characterWidth+"px",g.height=a.lineHeight+"px"}if(e>1)while(this.cursors.length>e)this.removeCursor()}else{var f=this.getPixelPosition(null,!0),g=this.cursor.style;g.left=f.left+"px",g.top=f.top+"px",g.width=a.characterWidth+"px",g.height=a.lineHeight+"px";while(this.cursors.length>1)this.removeCursor()}var h=this.session.getOverwrite();h!=this.overwrite&&this.$setOverite(h),this.restartTimer()},this.$setOverite=function(a){this.overwrite=a;for(var b=this.cursors.length;b--;)a?d.addCssClass(this.cursors[b],"ace_overwrite"):d.removeCssClass(this.cursors[b],"ace_overwrite")},this.destroy=function(){clearInterval(this.blinkId)}}).call(e.prototype),b.Cursor=e}),define("ace/scrollbar",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"],function(a,b,c){"use strict";var d=a("./lib/oop"),e=a("./lib/dom"),f=a("./lib/event"),g=a("./lib/event_emitter").EventEmitter,h=function(a){this.element=e.createElement("div"),this.element.className="ace_sb",this.inner=e.createElement("div"),this.element.appendChild(this.inner),a.appendChild(this.element),this.width=e.scrollbarWidth(a.ownerDocument),this.element.style.width=(this.width||15)+5+"px",f.addListener(this.element,"scroll",this.onScroll.bind(this))};(function(){d.implement(this,g),this.onScroll=function(){this._emit("scroll",{data:this.element.scrollTop})},this.getWidth=function(){return this.width},this.setHeight=function(a){this.element.style.height=a+"px"},this.setInnerHeight=function(a){this.inner.style.height=a+"px"},this.setScrollTop=function(a){this.element.scrollTop=a}}).call(h.prototype),b.ScrollBar=h}),define("ace/renderloop",["require","exports","module","ace/lib/event"],function(a,b,c){"use strict";var d=a("./lib/event"),e=function(a,b){this.onRender=a,this.pending=!1,this.changes=0,this.window=b||window};(function(){this.schedule=function(a){this.changes=this.changes|a;if(!this.pending){this.pending=!0;var b=this;d.nextTick(function(){b.pending=!1;var a;while(a=b.changes)b.changes=0,b.onRender(a)},this.window)}}}).call(e.prototype),b.RenderLoop=e}),define("text!ace/css/editor.css",[],"@import url(//fonts.googleapis.com/css?family=Droid+Sans+Mono);\n\n.ace_editor {\n position: absolute;\n overflow: hidden;\n font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Droid Sans Mono', 'Consolas', monospace;\n font-size: 12px;\n}\n\n.ace_scroller {\n position: absolute;\n overflow-x: scroll;\n overflow-y: hidden;\n}\n\n.ace_content {\n position: absolute;\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n -webkit-box-sizing: border-box;\n cursor: text;\n}\n\n.ace_composition {\n position: absolute;\n background: #555;\n color: #DDD;\n z-index: 4;\n}\n\n.ace_gutter {\n position: absolute;\n overflow : hidden;\n height: 100%;\n width: auto;\n cursor: default;\n z-index: 1000;\n}\n\n.ace_gutter.horscroll {\n box-shadow: 0px 0px 20px rgba(0,0,0,0.4);\n}\n\n.ace_gutter-cell {\n padding-left: 19px;\n padding-right: 6px;\n}\n\n.ace_gutter-cell.ace_error {\n background-image: url(\"data:image/gif,GIF89a%10%00%10%00%D5%00%00%F5or%F5%87%88%F5nr%F4ns%EBmq%F5z%7F%DDJT%DEKS%DFOW%F1Yc%F2ah%CE(7%CE)8%D18E%DD%40M%F2KZ%EBU%60%F4%60m%DCir%C8%16(%C8%19*%CE%255%F1%3FR%F1%3FS%E6%AB%B5%CA%5DI%CEn%5E%F7%A2%9A%C9G%3E%E0a%5B%F7%89%85%F5yy%F6%82%80%ED%82%80%FF%BF%BF%E3%C4%C4%FF%FF%FF%FF%FF%FF%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00!%F9%04%01%00%00%25%00%2C%00%00%00%00%10%00%10%00%00%06p%C0%92pH%2C%1A%8F%C8%D2H%93%E1d4%23%E4%88%D3%09mB%1DN%B48%F5%90%40%60%92G%5B%94%20%3E%22%D2%87%24%FA%20%24%C5%06A%00%20%B1%07%02B%A38%89X.v%17%82%11%13q%10%0Fi%24%0F%8B%10%7BD%12%0Ei%09%92%09%0EpD%18%15%24%0A%9Ci%05%0C%18F%18%0B%07%04%01%04%06%A0H%18%12%0D%14%0D%12%A1I%B3%B4%B5IA%00%3B\");\n background-repeat: no-repeat;\n background-position: 2px center;\n}\n\n.ace_gutter-cell.ace_warning {\n background-image: url(\"data:image/gif,GIF89a%10%00%10%00%D5%00%00%FF%DBr%FF%DE%81%FF%E2%8D%FF%E2%8F%FF%E4%96%FF%E3%97%FF%E5%9D%FF%E6%9E%FF%EE%C1%FF%C8Z%FF%CDk%FF%D0s%FF%D4%81%FF%D5%82%FF%D5%83%FF%DC%97%FF%DE%9D%FF%E7%B8%FF%CCl%7BQ%13%80U%15%82W%16%81U%16%89%5B%18%87%5B%18%8C%5E%1A%94d%1D%C5%83-%C9%87%2F%C6%84.%C6%85.%CD%8B2%C9%871%CB%8A3%CD%8B5%DC%98%3F%DF%9BB%E0%9CC%E1%A5U%CB%871%CF%8B5%D1%8D6%DB%97%40%DF%9AB%DD%99B%E3%B0p%E7%CC%AE%FF%FF%FF%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00!%F9%04%01%00%00%2F%00%2C%00%00%00%00%10%00%10%00%00%06a%C0%97pH%2C%1A%8FH%A1%ABTr%25%87%2B%04%82%F4%7C%B9X%91%08%CB%99%1C!%26%13%84*iJ9(%15G%CA%84%14%01%1A%97%0C%03%80%3A%9A%3E%81%84%3E%11%08%B1%8B%20%02%12%0F%18%1A%0F%0A%03'F%1C%04%0B%10%16%18%10%0B%05%1CF%1D-%06%07%9A%9A-%1EG%1B%A0%A1%A0U%A4%A5%A6BA%00%3B\");\n background-repeat: no-repeat;\n background-position: 2px center;\n}\n\n.ace_gutter-cell.ace_info {\n background-image: url(\"data:image/gif;base64,R0lGODlhEAAQAMQAAAAAAEFBQVJSUl5eXmRkZGtra39/f4WFhYmJiZGRkaampry8vMPDw8zMzNXV1dzc3OTk5Orq6vDw8P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABQALAAAAAAQABAAAAUuICWOZGmeaBml5XGwFCQSBGyXRSAwtqQIiRuiwIM5BoYVbEFIyGCQoeJGrVptIQA7\");\n background-repeat: no-repeat;\n background-position: 2px center;\n}\n\n.ace_editor .ace_sb {\n position: absolute;\n overflow-x: hidden;\n overflow-y: scroll;\n right: 0;\n}\n\n.ace_editor .ace_sb div {\n position: absolute;\n width: 1px;\n left: 0;\n}\n\n.ace_editor .ace_print_margin_layer {\n z-index: 0;\n position: absolute;\n overflow: hidden;\n margin: 0;\n left: 0;\n height: 100%;\n width: 100%;\n}\n\n.ace_editor .ace_print_margin {\n position: absolute;\n height: 100%;\n}\n\n.ace_editor textarea {\n position: fixed;\n z-index: 0;\n width: 10px;\n height: 30px;\n opacity: 0;\n background: transparent;\n appearance: none;\n -moz-appearance: none;\n border: none;\n resize: none;\n outline: none;\n overflow: hidden;\n}\n\n.ace_layer {\n z-index: 1;\n position: absolute;\n overflow: hidden;\n white-space: nowrap;\n height: 100%;\n width: 100%;\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n -webkit-box-sizing: border-box;\n /* setting pointer-events: auto; on node under the mouse, which changes\n during scroll, will break mouse wheel scrolling in Safari */\n pointer-events: none;\n}\n\n.ace_gutter .ace_layer {\n position: relative;\n min-width: 40px;\n text-align: right;\n pointer-events: auto;\n}\n\n.ace_text-layer {\n color: black;\n}\n\n.ace_cjk {\n display: inline-block;\n text-align: center;\n}\n\n.ace_cursor-layer {\n z-index: 4;\n}\n\n.ace_cursor {\n z-index: 4;\n position: absolute;\n}\n\n.ace_cursor.ace_hidden {\n opacity: 0.2;\n}\n\n.ace_line {\n white-space: nowrap;\n}\n\n.ace_marker-layer .ace_step {\n position: absolute;\n z-index: 3;\n}\n\n.ace_marker-layer .ace_selection {\n position: absolute;\n z-index: 5;\n}\n\n.ace_marker-layer .ace_bracket {\n position: absolute;\n z-index: 6;\n}\n\n.ace_marker-layer .ace_active_line {\n position: absolute;\n z-index: 2;\n}\n\n.ace_gutter .ace_gutter_active_line{\n background-color : #dcdcdc;\n}\n\n.ace_marker-layer .ace_selected_word {\n position: absolute;\n z-index: 4;\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n -webkit-box-sizing: border-box;\n}\n\n.ace_line .ace_fold {\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n -webkit-box-sizing: border-box;\n \n display: inline-block;\n height: 11px;\n margin-top: -2px;\n vertical-align: middle;\n\n background-image: \n url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%11%00%00%00%09%08%06%00%00%00%D4%E8%C7%0C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%B5IDAT(%15%A5%91%3D%0E%02!%10%85ac%E1%05%D6%CE%D6%C6%CE%D2%E8%ED%CD%DE%C0%C6%D6N.%E0V%F8%3D%9Ca%891XH%C2%BE%D9y%3F%90!%E6%9C%C3%BFk%E5%011%C6-%F5%C8N%04%DF%BD%FF%89%DFt%83DN%60%3E%F3%AB%A0%DE%1A%5Dg%BE%10Q%97%1B%40%9C%A8o%10%8F%5E%828%B4%1B%60%87%F6%02%26%85%1Ch%1E%C1%2B%5Bk%FF%86%EE%B7j%09%9A%DA%9B%ACe%A3%F9%EC%DA!9%B4%D5%A6%81%86%86%98%CC%3C%5B%40%FA%81%B3%E9%CB%23%94%C16Azo%05%D4%E1%C1%95a%3B%8A'%A0%E8%CC%17%22%85%1D%BA%00%A2%FA%DC%0A%94%D1%D1%8D%8B%3A%84%17B%C7%60%1A%25Z%FC%8D%00%00%00%00IEND%AEB%60%82\"),\n url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%007%08%06%00%00%00%C4%DD%80C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%3AIDAT8%11c%FC%FF%FF%7F%18%03%1A%60%01%F2%3F%A0%891%80%04%FF%11-%F8%17%9BJ%E2%05%B1ZD%81v%26t%E7%80%F8%A3%82h%A12%1A%20%A3%01%02%0F%01%BA%25%06%00%19%C0%0D%AEF%D5%3ES%00%00%00%00IEND%AEB%60%82\");\n background-repeat: no-repeat, repeat-x;\n background-position: center center, top left;\n color: transparent;\n\n border: 1px solid black;\n -moz-border-radius: 2px;\n -webkit-border-radius: 2px;\n border-radius: 2px;\n \n cursor: pointer;\n pointer-events: auto;\n}\n\n.ace_dark .ace_fold {\n}\n\n.ace_fold:hover{\n background-image: \n url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%11%00%00%00%09%08%06%00%00%00%D4%E8%C7%0C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%B5IDAT(%15%A5%91%3D%0E%02!%10%85ac%E1%05%D6%CE%D6%C6%CE%D2%E8%ED%CD%DE%C0%C6%D6N.%E0V%F8%3D%9Ca%891XH%C2%BE%D9y%3F%90!%E6%9C%C3%BFk%E5%011%C6-%F5%C8N%04%DF%BD%FF%89%DFt%83DN%60%3E%F3%AB%A0%DE%1A%5Dg%BE%10Q%97%1B%40%9C%A8o%10%8F%5E%828%B4%1B%60%87%F6%02%26%85%1Ch%1E%C1%2B%5Bk%FF%86%EE%B7j%09%9A%DA%9B%ACe%A3%F9%EC%DA!9%B4%D5%A6%81%86%86%98%CC%3C%5B%40%FA%81%B3%E9%CB%23%94%C16Azo%05%D4%E1%C1%95a%3B%8A'%A0%E8%CC%17%22%85%1D%BA%00%A2%FA%DC%0A%94%D1%D1%8D%8B%3A%84%17B%C7%60%1A%25Z%FC%8D%00%00%00%00IEND%AEB%60%82\"),\n url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%007%08%06%00%00%00%C4%DD%80C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%003IDAT8%11c%FC%FF%FF%7F%3E%03%1A%60%01%F2%3F%A3%891%80%04%FFQ%26%F8w%C0%B43%A1%DB%0C%E2%8F%0A%A2%85%CAh%80%8C%06%08%3C%04%E8%96%18%00%A3S%0D%CD%CF%D8%C1%9D%00%00%00%00IEND%AEB%60%82\");\n background-repeat: no-repeat, repeat-x;\n background-position: center center, top left;\n}\n\n.ace_dragging .ace_content {\n cursor: move;\n}\n\n.ace_folding-enabled > .ace_gutter-cell {\n padding-right: 13px;\n}\n\n.ace_fold-widget {\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n -webkit-box-sizing: border-box;\n\n margin: 0 -12px 1px 1px;\n display: inline-block;\n height: 14px;\n width: 11px;\n vertical-align: text-bottom;\n \n background-image: url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%00%05%08%06%00%00%00%8Do%26%E5%00%00%004IDATx%DAe%8A%B1%0D%000%0C%C2%F2%2CK%96%BC%D0%8F9%81%88H%E9%D0%0E%96%C0%10%92%3E%02%80%5E%82%E4%A9*-%EEsw%C8%CC%11%EE%96w%D8%DC%E9*Eh%0C%151(%00%00%00%00IEND%AEB%60%82\");\n background-repeat: no-repeat;\n background-position: center 5px;\n\n border-radius: 3px;\n}\n\n.ace_fold-widget.end {\n background-image: url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%00%05%08%06%00%00%00%8Do%26%E5%00%00%004IDATx%DAm%C7%C1%09%000%08C%D1%8C%ECE%C8E(%8E%EC%02)%1EZJ%F1%C1'%04%07I%E1%E5%EE%CAL%F5%A2%99%99%22%E2%D6%1FU%B5%FE0%D9x%A7%26Wz5%0E%D5%00%00%00%00IEND%AEB%60%82\");\n}\n\n.ace_fold-widget.closed {\n background-image: url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%03%00%00%00%06%08%06%00%00%00%06%E5%24%0C%00%00%009IDATx%DA5%CA%C1%09%000%08%03%C0%AC*(%3E%04%C1%0D%BA%B1%23%A4Uh%E0%20%81%C0%CC%F8%82%81%AA%A2%AArGfr%88%08%11%11%1C%DD%7D%E0%EE%5B%F6%F6%CB%B8%05Q%2F%E9tai%D9%00%00%00%00IEND%AEB%60%82\");\n}\n\n.ace_fold-widget:hover {\n border: 1px solid rgba(0, 0, 0, 0.3);\n background-color: rgba(255, 255, 255, 0.2);\n -moz-box-shadow:inset 0 1px 1px rgba(255, 255, 255, 0.7);\n -moz-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);\n -webkit-box-shadow:inset 0 1px 1px rgba(255, 255, 255, 0.7);\n -webkit-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);\n box-shadow:inset 0 1px 1px rgba(255, 255, 255, 0.7);\n box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);\n background-position: center 4px;\n}\n\n.ace_fold-widget:active {\n border: 1px solid rgba(0, 0, 0, 0.4);\n background-color: rgba(0, 0, 0, 0.05);\n -moz-box-shadow:inset 0 1px 1px rgba(255, 255, 255);\n -moz-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);\n -webkit-box-shadow:inset 0 1px 1px rgba(255, 255, 255);\n -webkit-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);\n box-shadow:inset 0 1px 1px rgba(255, 255, 255);\n box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);\n}\n\n.ace_fold-widget.invalid {\n background-color: #FFB4B4;\n border-color: #DE5555;\n}\n"),define("ace/multi_select",["require","exports","module","ace/range_list","ace/range","ace/selection","ace/mouse/multi_select_handler","ace/commands/multi_select_commands","ace/search","ace/edit_session","ace/editor"],function(a,b,c){function j(a,b,c){return i.$options.wrap=!0,i.$options.needle=b,i.$options.backwards=c==-1,i.find(a)}function m(a,b){return a.row==b.row&&a.column==b.column}function n(a){a.$onAddRange=a.$onAddRange.bind(a),a.$onRemoveRange=a.$onRemoveRange.bind(a),a.$onMultiSelect=a.$onMultiSelect.bind(a),a.$onSingleSelect=a.$onSingleSelect.bind(a),b.onSessionChange.call(a,a),a.on("changeSession",b.onSessionChange.bind(a)),a.on("mousedown",g),a.commands.addCommands(b.commands.defaultCommands)}var d=a("./range_list").RangeList,e=a("./range").Range,f=a("./selection").Selection,g=a("./mouse/multi_select_handler").onMouseDown;b.commands=a("./commands/multi_select_commands");var h=a("./search").Search,i=new h,k=a("./edit_session").EditSession;(function(){this.getSelectionMarkers=function(){return this.$selectionMarkers}}).call(k.prototype),function(){this.ranges=null,this.rangeList=null,this.addRange=function(a){if(!this.inMultiSelectMode&&this.rangeCount==0){var b=this.toOrientedRange();if(!a||!a.isEqual(b))this.rangeList.add(b),this.$onAddRange(b)}if(!a)return;a.cursor||(a.cursor=a.end);var c=this.rangeList.add(a);this.$onAddRange(a),c.length&&this.$onRemoveRange(c),this.rangeCount>0&&!this.inMultiSelectMode&&(this._emit("multiSelect"),this.inMultiSelectMode=!0,this.session.$undoSelect=!1,this.rangeList.attach(this.session))},this.toSingleRange=function(a){a=a||this.ranges[0];var b=this.rangeList.removeAll();b.length&&this.$onRemoveRange(b),a&&this.fromOrientedRange(a)},this.substractPoint=function(a){var b=this.rangeList.substractPoint(a);if(b)return this.$onRemoveRange(b),b[0]},this.mergeOverlappingRanges=function(){var a=this.rangeList.merge();a.length?this.$onRemoveRange(a):this.ranges[0]&&this.fromOrientedRange(this.ranges[0])},this.$onAddRange=function(a){this.rangeCount=this.rangeList.ranges.length,this.ranges.unshift(a),this.fromOrientedRange(a),this._emit("addRange",{range:a})},this.$onRemoveRange=function(a){this.rangeCount=this.rangeList.ranges.length;if(this.rangeCount==1&&this.inMultiSelectMode){var b=this.rangeList.ranges.pop();a.push(b),this.rangeCount=0}for(var c=a.length;c--;){var d=this.ranges.indexOf(a[c]);this.ranges.splice(d,1)}this._emit("removeRange",{ranges:a}),this.rangeCount==0&&this.inMultiSelectMode&&(this.inMultiSelectMode=!1,this._emit("singleSelect"),this.session.$undoSelect=!0,this.rangeList.detach(this.session)),b=b||this.ranges[0],b&&!b.isEqual(this.getRange())&&this.fromOrientedRange(b)},this.$initRangeList=function(){if(this.rangeList)return;this.rangeList=new d,this.ranges=[],this.rangeCount=0},this.getAllRanges=function(){return this.rangeList.ranges.concat()},this.splitIntoLines=function(){if(this.rangeCount>1){var a=this.rangeList.ranges,b=a[a.length-1],c=e.fromPoints(a[0].start,b.end);this.toSingleRange(),this.setSelectionRange(c,b.cursor==b.start)}else{var d=this.session.documentToScreenPosition(this.selectionLead),f=this.session.documentToScreenPosition(this.selectionAnchor),g=this.rectangularRangeBlock(d,f);g.forEach(this.addRange,this)}},this.rectangularRangeBlock=function(a,b,c){var d=[],f=a.column<b.column;if(f)var g=a.column,h=b.column;else var g=b.column,h=a.column;var i=a.row<b.row;if(i)var j=a.row,k=b.row;else var j=b.row,k=a.row;g<0&&(g=0),j<0&&(j=0),j==k&&(c=!0);for(var l=j;l<=k;l++){var n=e.fromPoints(this.session.screenToDocumentPosition(l,g),this.session.screenToDocumentPosition(l,h));if(n.isEmpty()){if(o&&m(n.end,o))break;var o=n.end}n.cursor=f?n.start:n.end,d.push(n)}i&&d.reverse();if(!c){var p=d.length-1;while(d[p].isEmpty()&&p>0)p--;if(p>0){var q=0;while(d[q].isEmpty())q++}for(var r=p;r>=q;r--)d[r].isEmpty()&&d.splice(r,1)}return d}}.call(f.prototype);var l=a("./editor").Editor;(function(){this.updateSelectionMarkers=function(){this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.addSelectionMarker=function(a){a.cursor||(a.cursor=a.end);var b=this.getSelectionStyle();return a.marker=this.session.addMarker(a,"ace_selection",b),this.session.$selectionMarkers.push(a),this.session.selectionMarkerCount=this.session.$selectionMarkers.length,a},this.removeSelectionMarkers=function(a){for(var b=a.length;b--;){var c=a[b];if(!c.marker)continue;this.session.removeMarker(c.marker);var d=this.session.$selectionMarkers.indexOf(c);d!=-1&&this.session.$selectionMarkers.splice(d,1)}this.session.selectionMarkerCount=this.session.$selectionMarkers.length},this.$onAddRange=function(a){this.addSelectionMarker(a.range),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onRemoveRange=function(a){this.removeSelectionMarkers(a.ranges),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelect=function(a){if(this.inMultiSelectMode)return;this.inMultiSelectMode=!0,this.setStyle("multiselect"),this.keyBinding.addKeyboardHandler(b.commands.keyboardHandler),this.commands.on("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onSingleSelect=function(a){if(this.session.multiSelect.inVirtualMode)return;this.inMultiSelectMode=!1,this.unsetStyle("multiselect"),this.keyBinding.removeKeyboardHandler(b.commands.keyboardHandler),this.commands.removeEventListener("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelectExec=function(a){var b=a.command,c=a.editor;b.multiSelectAction?b.multiSelectAction=="forEach"?c.forEachSelection(b,a.args):b.multiSelectAction=="single"?(c.exitMultiSelectMode(),b.exec(c,a.args||{})):b.multiSelectAction(c,a.args||{}):(b.exec(c,a.args||{}),c.multiSelect.addRange(c.multiSelect.toOrientedRange()),c.multiSelect.mergeOverlappingRanges()),a.preventDefault()},this.forEachSelection=function(a,b){if(this.inVirtualSelectionMode)return;var c=this.session,d=this.selection,e=d.rangeList,g=d._eventRegistry;d._eventRegistry={};var h=new f(c);this.inVirtualSelectionMode=!0;for(var i=e.ranges.length;i--;)h.fromOrientedRange(e.ranges[i]),this.selection=c.selection=h,a.exec(this,b||{}),h.toOrientedRange(e.ranges[i]);h.detach(),this.selection=c.selection=d,this.inVirtualSelectionMode=!1,d._eventRegistry=g,d.mergeOverlappingRanges(),this.onCursorChange(),this.onSelectionChange()},this.exitMultiSelectMode=function(){if(this.inVirtualSelectionMode)return;this.multiSelect.toSingleRange()},this.getCopyText=function(){var a="";if(this.inMultiSelectMode){var b=this.multiSelect.rangeList.ranges;a=[];for(var c=0;c<b.length;c++)a.push(this.session.getTextRange(b[c]));a=a.join(this.session.getDocument().getNewLineCharacter())}else this.selection.isEmpty()||(a=this.session.getTextRange(this.getSelectionRange()));return a},this.selectMoreLines=function(a,b){var c=this.selection.toOrientedRange(),d=c.cursor==c.end,f=this.session.documentToScreenPosition(c.cursor);this.selection.$desiredColumn&&(f.column=this.selection.$desiredColumn);var g=this.session.screenToDocumentPosition(f.row+a,f.column);if(!c.isEmpty())var h=this.session.documentToScreenPosition(d?c.end:c.start),i=this.session.screenToDocumentPosition(h.row+a,h.column);else var i=g;if(d){var j=e.fromPoints(g,i);j.cursor=j.start}else{var j=e.fromPoints(i,g);j.cursor=j.end}j.desiredColumn=f.column;if(!this.selection.inMultiSelectMode)this.selection.addRange(c);else if(b)var k=c.cursor;this.selection.addRange(j),k&&this.selection.substractPoint(k)},this.transposeSelections=function(a){var b=this.session,c=b.multiSelect,d=c.ranges;for(var e=d.length;e--;){var f=d[e];if(f.isEmpty()){var g=b.getWordRange(f.start.row,f.start.column);f.start.row=g.start.row,f.start.column=g.start.column,f.end.row=g.end.row,f.end.column=g.end.column}}c.mergeOverlappingRanges();var h=[];for(var e=d.length;e--;){var f=d[e];h.unshift(b.getTextRange(f))}a<0?h.unshift(h.pop()):h.push(h.shift());for(var e=d.length;e--;){var f=d[e],g=f.clone();b.replace(f,h[e]),f.start.row=g.start.row,f.start.column=g.start.column}},this.selectMore=function(a,b){var c=this.session,d=c.multiSelect,e=d.toOrientedRange();if(e.isEmpty()){var e=c.getWordRange(e.start.row,e.start.column);e.cursor=e.end,this.multiSelect.addRange(e)}var f=c.getTextRange(e),g=j(c,f,a);g&&(g.cursor=a==-1?g.start:g.end,this.multiSelect.addRange(g)),b&&this.multiSelect.substractPoint(e.cursor)}}).call(l.prototype),b.onSessionChange=function(a){var b=a.session;b.multiSelect||(b.$selectionMarkers=[],b.selection.$initRangeList(),b.multiSelect=b.selection),this.multiSelect=b.multiSelect;var c=a.oldSession;c&&(c.multiSelect&&c.multiSelect.editor==this&&(c.multiSelect.editor=null),b.multiSelect.removeEventListener("addRange",this.$onAddRange),b.multiSelect.removeEventListener("removeRange",this.$onRemoveRange),b.multiSelect.removeEventListener("multiSelect",this.$onMultiSelect),b.multiSelect.removeEventListener("singleSelect",this.$onSingleSelect)),b.multiSelect.on("addRange",this.$onAddRange),b.multiSelect.on("removeRange",this.$onRemoveRange),b.multiSelect.on("multiSelect",this.$onMultiSelect),b.multiSelect.on("singleSelect",this.$onSingleSelect),this.inMultiSelectMode!=b.selection.inMultiSelectMode&&(b.selection.inMultiSelectMode?this.$onMultiSelect():this.$onSingleSelect())},b.MultiSelect=n}),define("ace/range_list",["require","exports","module"],function(a,b,c){"use strict";var d=function(){this.ranges=[]};(function(){this.comparePoints=function(a,b){return a.row-b.row||a.column-b.column},this.pointIndex=function(a,b){var c=this.ranges;for(var d=b||0;d<c.length;d++){var e=c[d],f=this.comparePoints(a,e.end);if(f>0)continue;return f==0?d:(f=this.comparePoints(a,e.start),f>=0?d:-d-1)}return-d-1},this.add=function(a){var b=this.pointIndex(a.start);b<0&&(b=-b-1);var c=this.pointIndex(a.end,b);return c<0?c=-c-1:c++,this.ranges.splice(b,c-b,a)},this.addList=function(a){var b=[];for(var c=a.length;c--;)b.push.call(b,this.add(a[c]));return b},this.substractPoint=function(a){var b=this.pointIndex(a);if(b>=0)return this.ranges.splice(b,1)},this.merge=function(){var a=[],b=this.ranges,c=b[0],d;for(var e=1;e<b.length;e++){d=c,c=b[e];var f=this.comparePoints(d.end,c.start);if(f<0)continue;if(f==0&&!d.isEmpty()&&!c.isEmpty())continue;this.comparePoints(d.end,c.end)<0&&(d.end.row=c.end.row,d.end.column=c.end.column),b.splice(e,1),a.push(c),c=d,e--}return a},this.contains=function(a,b){return this.pointIndex({row:a,column:b})>=0},this.containsPoint=function(a){return this.pointIndex(a)>=0},this.rangeAtPoint=function(a){var b=this.pointIndex(a);if(b>=0)return this.ranges[b]},this.clipRows=function(a,b){var c=this.ranges;if(c[0].start.row>b||c[c.length-1].start.row<a)return[];var d=this.pointIndex({row:a,column:0});d<0&&(d=-d-1);var e=this.pointIndex({row:b,column:0},d);e<0&&(e=-e-1);var f=[];for(var g=d;g<e;g++)f.push(c[g]);return f},this.removeAll=function(){return this.ranges.splice(0,this.ranges.length)},this.attach=function(a){this.session&&this.detach(),this.session=a,this.onChange=this.$onChange.bind(this),this.session.on("change",this.onChange)},this.detach=function(){if(!this.session)return;this.session.removeListener("change",this.onChange),this.session=null},this.$onChange=function(a){var b=a.data.range;if(a.data.action[0]=="i")var c=b.start,d=b.end;else var d=b.start,c=b.end;var e=c.row,f=d.row,g=f-e,h=-c.column+d.column,i=this.ranges;for(var j=0,k=i.length;j<k;j++){var l=i[j];if(l.end.row<e)continue;if(l.start.row>e)break;l.start.row==e&&l.start.column>=c.column&&(l.start.column+=h,l.start.row+=g),l.end.row==e&&l.end.column>=c.column&&(l.end.column+=h,l.end.row+=g)}if(g!=0&&j<k)for(;j<k;j++){var l=i[j];l.start.row+=g,l.end.row+=g}}}).call(d.prototype),b.RangeList=d}),define("ace/mouse/multi_select_handler",["require","exports","module","ace/lib/event"],function(a,b,c){function e(a,b){return a.row==b.row&&a.column==b.column}function f(a){var b=a.domEvent,c=b.altKey,f=b.shiftKey,g=a.getAccelKey(),h=a.getButton();if(!g&&!c){if(a.editor.inMultiSelectMode)if(h==0)a.editor.exitMultiSelectMode();else if(h==2){var i=a.editor,j=i.selection.isEmpty();i.textInput.onContextMenu({x:a.clientX,y:a.clientY},j),d.capture(i.container,function(){},i.textInput.onContextMenuClose),a.stop()}return}var i=a.editor,k=i.selection,l=i.inMultiSelectMode,m=a.getDocumentPosition(),n=k.getCursor(),o=a.inSelection()||k.isEmpty()&&e(m,n),p=a.pageX,q=a.pageY,r=function(a){p=d.getDocumentX(a),q=d.getDocumentY(a)},s=function(){var a=i.renderer.pixelToScreenCoordinates(p,q),b=t.screenToDocumentPosition(a.row,a.column);if(e(v,a)&&e(b,k.selectionLead))return;v=a,i.selection.moveCursorToPosition(b),i.selection.clearSelection(),i.renderer.scrollCursorIntoView(),i.removeSelectionMarkers(x),x=k.rectangularRangeBlock(v,u),x.forEach(i.addSelectionMarker,i),i.updateSelectionMarkers()},t=i.session,u=i.renderer.pixelToScreenCoordinates(p,q),v=u;if(g&&!f&&!c&&h==0){if(!l&&o)return;l||k.addRange(k.toOrientedRange());var w=k.rangeList.rangeAtPoint(m);d.capture(i.container,function(){},function(){var a=k.toOrientedRange();w&&a.isEmpty()&&e(w.cursor,a.cursor)?k.substractPoint(a.cursor):k.addRange(a)})}else if(!f&&c&&h==0){a.stop(),l&&!g?k.toSingleRange():!l&&g&&k.addRange(),k.moveCursorToPosition(m),k.clearSelection();var x=[],y=function(a){clearInterval(A),i.removeSelectionMarkers(x);for(var b=0;b<x.length;b++)k.addRange(x[b])},z=s;d.capture(i.container,r,y);var A=setInterval(function(){z()},20);return a.preventDefault()}}var d=a("../lib/event");b.onMouseDown=f}),define("ace/commands/multi_select_commands",["require","exports","module","ace/keyboard/hash_handler"],function(a,b,c){b.defaultCommands=[{name:"addCursorAbove",exec:function(a){a.selectMoreLines(-1)},bindKey:{win:"Ctrl-Alt-Up",mac:"Ctrl-Alt-Up"},readonly:!0},{name:"addCursorBelow",exec:function(a){a.selectMoreLines(1)},bindKey:{win:"Ctrl-Alt-Down",mac:"Ctrl-Alt-Down"},readonly:!0},{name:"addCursorAboveSkipCurrent",exec:function(a){a.selectMoreLines(-1,!0)},bindKey:{win:"Ctrl-Alt-Shift-Up",mac:"Ctrl-Alt-Shift-Up"},readonly:!0},{name:"addCursorBelowSkipCurrent",exec:function(a){a.selectMoreLines(1,!0)},bindKey:{win:"Ctrl-Alt-Shift-Down",mac:"Ctrl-Alt-Shift-Down"},readonly:!0},{name:"selectMoreBefore",exec:function(a){a.selectMore(-1)},bindKey:{win:"Ctrl-Alt-Left",mac:"Ctrl-Alt-Left"},readonly:!0},{name:"selectMoreAfter",exec:function(a){a.selectMore(1)},bindKey:{win:"Ctrl-Alt-Right",mac:"Ctrl-Alt-Right"},readonly:!0},{name:"selectNextBefore",exec:function(a){a.selectMore(-1,!0)},bindKey:{win:"Ctrl-Alt-Shift-Left",mac:"Ctrl-Alt-Shift-Left"},readonly:!0},{name:"selectNextAfter",exec:function(a){a.selectMore(1,!0)},bindKey:{win:"Ctrl-Alt-Shift-Right",mac:"Ctrl-Alt-Shift-Right"},readonly:!0},{name:"splitIntoLines",exec:function(a){a.multiSelect.splitIntoLines()},bindKey:{win:"Ctrl-Shift-L",mac:"Ctrl-Shift-L"},readonly:!0}],b.multiEditCommands=[{name:"singleSelection",bindKey:"esc",exec:function(a){a.exitMultiSelectMode()},readonly:!0}];var d=a("../keyboard/hash_handler").HashHandler;b.keyboardHandler=new d(b.multiEditCommands)}),define("ace/worker/worker_client",["require","exports","module","ace/lib/oop","ace/lib/event_emitter","ace/config"],function(a,b,c){"use strict";var d=a("../lib/oop"),e=a("../lib/event_emitter").EventEmitter,f=a("../config"),g=function(b,c,d,e){this.changeListener=this.changeListener.bind(this);if(f.get("packaged"))this.$worker=new Worker(f.get("workerPath")+"/"+c);else{var g=this.$normalizePath(a.nameToUrl("ace/worker/worker",null,"_"));this.$worker=new Worker(g);var h={};for(var i=0;i<b.length;i++){var j=b[i],k=this.$normalizePath(a.nameToUrl(j,null,"_").replace(/.js$/,""));h[j]=k}}this.$worker.postMessage({init:!0,tlns:h,module:d,classname:e}),this.callbackId=1,this.callbacks={};var l=this;this.$worker.onerror=function(a){throw window.console&&console.log&&console.log(a),a},this.$worker.onmessage=function(a){var b=a.data;switch(b.type){case"log":window.console&&console.log&&console.log(b.data);break;case"event":l._emit(b.name,{data:b.data});break;case"call":var c=l.callbacks[b.id];c&&(c(b.data),delete l.callbacks[b.id])}}};(function(){d.implement(this,e),this.$normalizePath=function(a){return a=a.replace(/^[a-z]+:\/\/[^\/]+/,""),a=location.protocol+"//"+location.host+(a.charAt(0)=="/"?"":location.pathname.replace(/\/[^\/]*$/,""))+"/"+a.replace(/^[\/]+/,""),a},this.terminate=function(){this._emit("terminate",{}),this.$worker.terminate(),this.$worker=null,this.$doc.removeEventListener("change",this.changeListener),this.$doc=null},this.send=function(a,b){this.$worker.postMessage({command:a,args:b})},this.call=function(a,b,c){if(c){var d=this.callbackId++;this.callbacks[d]=c,b.push(d)}this.send(a,b)},this.emit=function(a,b){try{this.$worker.postMessage({event:a,data:{data:b.data}})}catch(c){}},this.attachToDocument=function(a){this.$doc&&this.terminate(),this.$doc=a,this.call("setValue",[a.getValue()]),a.on("change",this.changeListener)},this.changeListener=function(a){a.range={start:a.data.range.start,end:a.data.range.end},this.emit("change",a)}}).call(g.prototype),b.WorkerClient=g}),define("ace/keyboard/state_handler",["require","exports","module"],function(a,b,c){function e(a){this.keymapping=this.$buildKeymappingRegex(a)}"use strict";var d=!1;e.prototype={$buildKeymappingRegex:function(a){for(var b in a)this.$buildBindingsRegex(a[b]);return a},$buildBindingsRegex:function(a){a.forEach(function(a){a.key?a.key=new RegExp("^"+a.key+"$"):Array.isArray(a.regex)?("key"in a||(a.key=new RegExp("^"+a.regex[1]+"$")),a.regex=new RegExp(a.regex.join("")+"$")):a.regex&&(a.regex=new RegExp(a.regex+"$"))})},$composeBuffer:function(a,b,c,d){if(a.state==null||a.buffer==null)a.state="start",a.buffer="";var e=[];b&1&&e.push("ctrl"),b&8&&e.push("command"),b&2&&e.push("option"),b&4&&e.push("shift"),c&&e.push(c);var f=e.join("-"),g=a.buffer+f;b!=2&&(a.buffer=g);var h={bufferToUse:g,symbolicName:f};return d&&(h.keyIdentifier=d.keyIdentifier),h},$find:function(a,b,c,e,f,g){var h={};return this.keymapping[a.state].some(function(i){var j;if(i.key&&!i.key.test(c))return!1;if(i.regex&&!(j=i.regex.exec(b)))return!1;if(i.match&&!i.match(b,e,f,c,g))return!1;if(i.disallowMatches)for(var k=0;k<i.disallowMatches.length;k++)if(!!j[i.disallowMatches[k]])return!1;if(i.exec){h.command=i.exec;if(i.params){var l;h.args={},i.params.forEach(function(a){a.match!=null&&j!=null?l=j[a.match]||a.defaultValue:l=a.defaultValue,a.type==="number"&&(l=parseInt(l)),h.args[a.name]=l})}a.buffer=""}return i.then&&(a.state=i.then,a.buffer=""),h.command==null&&(h.command="null"),d&&console.log("KeyboardStateMapper#find",i),!0}),h.command?h:(a.buffer="",!1)},handleKeyboard:function(a,b,c,e,f){if(b==0||c!=""&&c!=String.fromCharCode(0)){var g=this.$composeBuffer(a,b,c,f),h=g.bufferToUse,i=g.symbolicName,j=g.keyIdentifier;return g=this.$find(a,h,i,b,c,j),d&&console.log("KeyboardStateMapper#match",h,i,g),g}return null}},b.matchCharacterOnly=function(a,b,c,d){return b==0?!0:b==4&&c.length==1?!0:!1},b.StateHandler=e}),define("ace/placeholder",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/oop"],function(a,b,c){"use strict";var d=a("./range").Range,e=a("./lib/event_emitter").EventEmitter,f=a("./lib/oop"),g=function(a,b,c,d,e,f){var g=this;this.length=b,this.session=a,this.doc=a.getDocument(),this.mainClass=e,this.othersClass=f,this.$onUpdate=this.onUpdate.bind(this),this.doc.on("change",this.$onUpdate),this.$others=d,this.$onCursorChange=function(){setTimeout(function(){g.onCursorChange()})},this.$pos=c;var h=a.getUndoManager().$undoStack||a.getUndoManager().$undostack||{length:-1};this.$undoStackDepth=h.length,this.setup(),a.selection.on("changeCursor",this.$onCursorChange)};(function(){f.implement(this,e),this.setup=function(){var a=this,b=this.doc,c=this.session,e=this.$pos;this.pos=b.createAnchor(e.row,e.column),this.markerId=c.addMarker(new d(e.row,e.column,e.row,e.column+this.length),this.mainClass,null,!1),this.pos.on("change",function(b){c.removeMarker(a.markerId),a.markerId=c.addMarker(new d(b.value.row,b.value.column,b.value.row,b.value.column+a.length),a.mainClass,null,!1)}),this.others=[],this.$others.forEach(function(c){var d=b.createAnchor(c.row,c.column);a.others.push(d)}),c.setUndoSelect(!1)},this.showOtherMarkers=function(){if(this.othersActive)return;var a=this.session,b=this;this.othersActive=!0,this.others.forEach(function(c){c.markerId=a.addMarker(new d(c.row,c.column,c.row,c.column+b.length),b.othersClass,null,!1),c.on("change",function(e){a.removeMarker(c.markerId),c.markerId=a.addMarker(new d(e.value.row,e.value.column,e.value.row,e.value.column+b.length),b.othersClass,null,!1)})})},this.hideOtherMarkers=function(){if(!this.othersActive)return;this.othersActive=!1;for(var a=0;a<this.others.length;a++)this.session.removeMarker(this.others[a].markerId)},this.onUpdate=function(a){var b=a.data,c=b.range;if(c.start.row!==c.end.row)return;if(c.start.row!==this.pos.row)return;if(this.$updating)return;this.$updating=!0;var e=b.action==="insertText"?c.end.column-c.start.column:c.start.column-c.end.column;if(c.start.column>=this.pos.column&&c.start.column<=this.pos.column+this.length+1){var f=c.start.column-this.pos.column;this.length+=e;if(!this.session.$fromUndo){if(b.action==="insertText")for(var g=this.others.length-1;g>=0;g--){var h=this.others[g],i={row:h.row,column:h.column+f};h.row===c.start.row&&c.start.column<h.column&&(i.column+=e),this.doc.insert(i,b.text)}else if(b.action==="removeText")for(var g=this.others.length-1;g>=0;g--){var h=this.others[g],i={row:h.row,column:h.column+f};h.row===c.start.row&&c.start.column<h.column&&(i.column+=e),this.doc.remove(new d(i.row,i.column,i.row,i.column-e))}c.start.column===this.pos.column&&b.action==="insertText"?setTimeout(function(){this.pos.setPosition(this.pos.row,this.pos.column-e);for(var a=0;a<this.others.length;a++){var b=this.others[a],d={row:b.row,column:b.column-e};b.row===c.start.row&&c.start.column<b.column&&(d.column+=e),b.setPosition(d.row,d.column)}}.bind(this),0):c.start.column===this.pos.column&&b.action==="removeText"&&setTimeout(function(){for(var a=0;a<this.others.length;a++){var b=this.others[a];b.row===c.start.row&&c.start.column<b.column&&b.setPosition(b.row,b.column-e)}}.bind(this),0)}this.pos._emit("change",{value:this.pos});for(var g=0;g<this.others.length;g++)this.others[g]._emit("change",{value:this.others[g]})}this.$updating=!1},this.onCursorChange=function(a){if(this.$updating)return;var b=this.session.selection.getCursor();b.row===this.pos.row&&b.column>=this.pos.column&&b.column<=this.pos.column+this.length?(this.showOtherMarkers(),this._emit("cursorEnter",a)):(this.hideOtherMarkers(),this._emit("cursorLeave",a))},this.detach=function(){this.session.removeMarker(this.markerId),this.hideOtherMarkers(),this.doc.removeEventListener("change",this.$onUpdate),this.session.selection.removeEventListener("changeCursor",this.$onCursorChange),this.pos.detach();for(var a=0;a<this.others.length;a++)this.others[a].detach();this.session.setUndoSelect(!0)},this.cancel=function(){if(this.$undoStackDepth===-1)throw Error("Canceling placeholders only supported with undo manager attached to session.");var a=this.session.getUndoManager(),b=(a.$undoStack||a.$undostack).length-this.$undoStackDepth;for(var c=0;c<b;c++)a.undo(!0)}}).call(g.prototype),b.PlaceHolder=g}),define("ace/theme/textmate",["require","exports","module","ace/lib/dom"],function(a,b,c){"use strict",b.isDark=!1,b.cssClass="ace-tm",b.cssText=".ace-tm .ace_editor { border: 2px solid rgb(159, 159, 159);}.ace-tm .ace_editor.ace_focus { border: 2px solid #327fbd;}.ace-tm .ace_gutter { background: #e8e8e8; color: #333;}.ace-tm .ace_print_margin { width: 1px; background: #e8e8e8;}.ace-tm .ace_fold { background-color: #6B72E6;}.ace-tm .ace_text-layer { cursor: text;}.ace-tm .ace_cursor { border-left: 1px solid black;}.ace-tm .ace_cursor.ace_overwrite { border-left: 0px; border-bottom: 1px solid black;} .ace-tm .ace_line .ace_invisible { color: rgb(191, 191, 191);}.ace-tm .ace_line .ace_storage,.ace-tm .ace_line .ace_keyword { color: blue;}.ace-tm .ace_line .ace_constant { color: rgb(197, 6, 11);}.ace-tm .ace_line .ace_constant.ace_buildin { color: rgb(88, 72, 246);}.ace-tm .ace_line .ace_constant.ace_language { color: rgb(88, 92, 246);}.ace-tm .ace_line .ace_constant.ace_library { color: rgb(6, 150, 14);}.ace-tm .ace_line .ace_invalid { background-color: rgb(153, 0, 0); color: white;}.ace-tm .ace_line .ace_support.ace_function { color: rgb(60, 76, 114);}.ace-tm .ace_line .ace_support.ace_constant { color: rgb(6, 150, 14);}.ace-tm .ace_line .ace_support.ace_type,.ace-tm .ace_line .ace_support.ace_class { color: rgb(109, 121, 222);}.ace-tm .ace_line .ace_keyword.ace_operator { color: rgb(104, 118, 135);}.ace-tm .ace_line .ace_string { color: rgb(3, 106, 7);}.ace-tm .ace_line .ace_comment { color: rgb(76, 136, 107);}.ace-tm .ace_line .ace_comment.ace_doc { color: rgb(0, 102, 255);}.ace-tm .ace_line .ace_comment.ace_doc.ace_tag { color: rgb(128, 159, 191);}.ace-tm .ace_line .ace_constant.ace_numeric { color: rgb(0, 0, 205);}.ace-tm .ace_line .ace_variable { color: rgb(49, 132, 149);}.ace-tm .ace_line .ace_xml_pe { color: rgb(104, 104, 91);}.ace-tm .ace_entity.ace_name.ace_function { color: #0000A2;}.ace-tm .ace_markup.ace_markupine { text-decoration:underline;}.ace-tm .ace_markup.ace_heading { color: rgb(12, 7, 255);}.ace-tm .ace_markup.ace_list { color:rgb(185, 6, 144);}.ace-tm .ace_marker-layer .ace_selection { background: rgb(181, 213, 255);}.ace-tm .ace_marker-layer .ace_step { background: rgb(252, 255, 0);}.ace-tm .ace_marker-layer .ace_stack { background: rgb(164, 229, 101);}.ace-tm .ace_marker-layer .ace_bracket { margin: -1px 0 0 -1px; border: 1px solid rgb(192, 192, 192);}.ace-tm .ace_marker-layer .ace_active_line { background: rgba(0, 0, 0, 0.07);}.ace-tm .ace_marker-layer .ace_selected_word { background: rgb(250, 250, 255); border: 1px solid rgb(200, 200, 250);}.ace-tm .ace_meta.ace_tag { color:rgb(28, 2, 255);}.ace-tm .ace_string.ace_regex { color: rgb(255, 0, 0)}";var d=a("../lib/dom");d.importCssString(b.cssText,b.cssClass)}); +(function(){function g(a){if(typeof requirejs!="undefined"){var e=b.define;b.define=function(a,b,c){return typeof c!="function"?e.apply(this,arguments):e(a,b,function(a,d,e){return b[2]=="module"&&(e.packaged=!0),c.apply(this,arguments)})},b.define.packaged=!0;return}var f=function(a,b){return d("",a,b)};f.packaged=!0;var g=b;a&&(b[a]||(b[a]={}),g=b[a]),g.define&&(c.original=g.define),g.define=c,g.require&&(d.original=g.require),g.require=f}var a="",b=function(){return this}(),c=function(a,b,d){if(typeof a!="string"){c.original?c.original.apply(window,arguments):(console.error("dropping module because define wasn't a string."),console.trace());return}arguments.length==2&&(d=b),c.modules||(c.modules={}),c.modules[a]=d},d=function(a,b,c){if(Object.prototype.toString.call(b)==="[object Array]"){var e=[];for(var g=0,h=b.length;g<h;++g){var i=f(a,b[g]);if(!i&&d.original)return d.original.apply(window,arguments);e.push(i)}c&&c.apply(null,e)}else{if(typeof b=="string"){var j=f(a,b);return!j&&d.original?d.original.apply(window,arguments):(c&&c(),j)}if(d.original)return d.original.apply(window,arguments)}},e=function(a,b){if(b.indexOf("!")!==-1){var c=b.split("!");return e(a,c[0])+"!"+e(a,c[1])}if(b.charAt(0)=="."){var d=a.split("/").slice(0,-1).join("/");b=d+"/"+b;while(b.indexOf(".")!==-1&&f!=b){var f=b;b=b.replace(/\/\.\//,"/").replace(/[^\/]+\/\.\.\//,"")}}return b},f=function(a,b){b=e(a,b);var f=c.modules[b];if(!f)return null;if(typeof f=="function"){var g={},h={id:b,uri:"",exports:g,packaged:!0},i=function(a,c){return d(b,a,c)},j=f(i,g,h);return g=j||h.exports,c.modules[b]=g,g}return f};g(a)})(),define("ace/ace",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/dom","ace/lib/event","ace/editor","ace/edit_session","ace/undomanager","ace/virtual_renderer","ace/multi_select","ace/worker/worker_client","ace/keyboard/hash_handler","ace/keyboard/state_handler","ace/placeholder","ace/config","ace/theme/textmate"],function(a,b,c){"use strict",a("./lib/fixoldbrowsers");var d=a("./lib/dom"),e=a("./lib/event"),f=a("./editor").Editor,g=a("./edit_session").EditSession,h=a("./undomanager").UndoManager,i=a("./virtual_renderer").VirtualRenderer,j=a("./multi_select").MultiSelect;a("./worker/worker_client"),a("./keyboard/hash_handler"),a("./keyboard/state_handler"),a("./placeholder"),a("./config").init(),b.edit=function(b){typeof b=="string"&&(b=document.getElementById(b));var c=new g(d.getInnerText(b));c.setUndoManager(new h),b.innerHTML="";var k=new f(new i(b,a("./theme/textmate")));new j(k),k.setSession(c);var l={};return l.document=c,l.editor=k,k.resize(),e.addListener(window,"resize",function(){k.resize()}),b.env=l,k.env=l,k}}),define("ace/lib/fixoldbrowsers",["require","exports","module","ace/lib/regexp","ace/lib/es5-shim"],function(a,b,c){"use strict",a("./regexp"),a("./es5-shim")}),define("ace/lib/regexp",["require","exports","module"],function(a,b,c){function g(a){return(a.global?"g":"")+(a.ignoreCase?"i":"")+(a.multiline?"m":"")+(a.extended?"x":"")+(a.sticky?"y":"")}function h(a,b,c){if(Array.prototype.indexOf)return a.indexOf(b,c);for(var d=c||0;d<a.length;d++)if(a[d]===b)return d;return-1}"use strict";var d={exec:RegExp.prototype.exec,test:RegExp.prototype.test,match:String.prototype.match,replace:String.prototype.replace,split:String.prototype.split},e=d.exec.call(/()??/,"")[1]===undefined,f=function(){var a=/^/g;return d.test.call(a,""),!a.lastIndex}();RegExp.prototype.exec=function(a){var b=d.exec.apply(this,arguments),c,i;if(typeof a=="string"&&b){!e&&b.length>1&&h(b,"")>-1&&(i=RegExp(this.source,d.replace.call(g(this),"g","")),d.replace.call(a.slice(b.index),i,function(){for(var a=1;a<arguments.length-2;a++)arguments[a]===undefined&&(b[a]=undefined)}));if(this._xregexp&&this._xregexp.captureNames)for(var j=1;j<b.length;j++)c=this._xregexp.captureNames[j-1],c&&(b[c]=b[j]);!f&&this.global&&!b[0].length&&this.lastIndex>b.index&&this.lastIndex--}return b},f||(RegExp.prototype.test=function(a){var b=d.exec.call(this,a);return b&&this.global&&!b[0].length&&this.lastIndex>b.index&&this.lastIndex--,!!b})}),define("ace/lib/es5-shim",["require","exports","module"],function(a,b,c){function p(a){try{return Object.defineProperty(a,"sentinel",{}),"sentinel"in a}catch(b){}}Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=g.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,h=c.apply(f,d.concat(g.call(arguments)));return h!==null&&Object(h)===h?h:f}return c.apply(b,d.concat(g.call(arguments)))};return e});var d=Function.prototype.call,e=Array.prototype,f=Object.prototype,g=e.slice,h=d.bind(f.toString),i=d.bind(f.hasOwnProperty),j,k,l,m,n;if(n=i(f,"__defineGetter__"))j=d.bind(f.__defineGetter__),k=d.bind(f.__defineSetter__),l=d.bind(f.__lookupGetter__),m=d.bind(f.__lookupSetter__);Array.isArray||(Array.isArray=function(b){return h(b)=="[object Array]"}),Array.prototype.forEach||(Array.prototype.forEach=function(b){var c=G(this),d=arguments[1],e=0,f=c.length>>>0;if(h(b)!="[object Function]")throw new TypeError;while(e<f)e in c&&b.call(d,c[e],e,c),e++}),Array.prototype.map||(Array.prototype.map=function(b){var c=G(this),d=c.length>>>0,e=Array(d),f=arguments[1];if(h(b)!="[object Function]")throw new TypeError;for(var g=0;g<d;g++)g in c&&(e[g]=b.call(f,c[g],g,c));return e}),Array.prototype.filter||(Array.prototype.filter=function(b){var c=G(this),d=c.length>>>0,e=[],f=arguments[1];if(h(b)!="[object Function]")throw new TypeError;for(var g=0;g<d;g++)g in c&&b.call(f,c[g],g,c)&&e.push(c[g]);return e}),Array.prototype.every||(Array.prototype.every=function(b){var c=G(this),d=c.length>>>0,e=arguments[1];if(h(b)!="[object Function]")throw new TypeError;for(var f=0;f<d;f++)if(f in c&&!b.call(e,c[f],f,c))return!1;return!0}),Array.prototype.some||(Array.prototype.some=function(b){var c=G(this),d=c.length>>>0,e=arguments[1];if(h(b)!="[object Function]")throw new TypeError;for(var f=0;f<d;f++)if(f in c&&b.call(e,c[f],f,c))return!0;return!1}),Array.prototype.reduce||(Array.prototype.reduce=function(b){var c=G(this),d=c.length>>>0;if(h(b)!="[object Function]")throw new TypeError;if(!d&&arguments.length==1)throw new TypeError;var e=0,f;if(arguments.length>=2)f=arguments[1];else do{if(e in c){f=c[e++];break}if(++e>=d)throw new TypeError}while(!0);for(;e<d;e++)e in c&&(f=b.call(void 0,f,c[e],e,c));return f}),Array.prototype.reduceRight||(Array.prototype.reduceRight=function(b){var c=G(this),d=c.length>>>0;if(h(b)!="[object Function]")throw new TypeError;if(!d&&arguments.length==1)throw new TypeError;var e,f=d-1;if(arguments.length>=2)e=arguments[1];else do{if(f in c){e=c[f--];break}if(--f<0)throw new TypeError}while(!0);do f in this&&(e=b.call(void 0,e,c[f],f,c));while(f--);return e}),Array.prototype.indexOf||(Array.prototype.indexOf=function(b){var c=G(this),d=c.length>>>0;if(!d)return-1;var e=0;arguments.length>1&&(e=E(arguments[1])),e=e>=0?e:Math.max(0,d+e);for(;e<d;e++)if(e in c&&c[e]===b)return e;return-1}),Array.prototype.lastIndexOf||(Array.prototype.lastIndexOf=function(b){var c=G(this),d=c.length>>>0;if(!d)return-1;var e=d-1;arguments.length>1&&(e=Math.min(e,E(arguments[1]))),e=e>=0?e:d-Math.abs(e);for(;e>=0;e--)if(e in c&&b===c[e])return e;return-1}),Object.getPrototypeOf||(Object.getPrototypeOf=function(b){return b.__proto__||(b.constructor?b.constructor.prototype:f)});if(!Object.getOwnPropertyDescriptor){var o="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function(b,c){if(typeof b!="object"&&typeof b!="function"||b===null)throw new TypeError(o+b);if(!i(b,c))return;var d,e,g;d={enumerable:!0,configurable:!0};if(n){var h=b.__proto__;b.__proto__=f;var e=l(b,c),g=m(b,c);b.__proto__=h;if(e||g)return e&&(d.get=e),g&&(d.set=g),d}return d.value=b[c],d}}Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(b){return Object.keys(b)}),Object.create||(Object.create=function(b,c){var d;if(b===null)d={__proto__:null};else{if(typeof b!="object")throw new TypeError("typeof prototype["+typeof b+"] != 'object'");var e=function(){};e.prototype=b,d=new e,d.__proto__=b}return c!==void 0&&Object.defineProperties(d,c),d});if(Object.defineProperty){var q=p({}),r=typeof document=="undefined"||p(document.createElement("div"));if(!q||!r)var s=Object.defineProperty}if(!Object.defineProperty||s){var t="Property description must be an object: ",u="Object.defineProperty called on non-object: ",v="getters & setters can not be defined on this javascript engine";Object.defineProperty=function(b,c,d){if(typeof b!="object"&&typeof b!="function"||b===null)throw new TypeError(u+b);if(typeof d!="object"&&typeof d!="function"||d===null)throw new TypeError(t+d);if(s)try{return s.call(Object,b,c,d)}catch(e){}if(i(d,"value"))if(n&&(l(b,c)||m(b,c))){var g=b.__proto__;b.__proto__=f,delete b[c],b[c]=d.value,b.__proto__=g}else b[c]=d.value;else{if(!n)throw new TypeError(v);i(d,"get")&&j(b,c,d.get),i(d,"set")&&k(b,c,d.set)}return b}}Object.defineProperties||(Object.defineProperties=function(b,c){for(var d in c)i(c,d)&&Object.defineProperty(b,d,c[d]);return b}),Object.seal||(Object.seal=function(b){return b}),Object.freeze||(Object.freeze=function(b){return b});try{Object.freeze(function(){})}catch(w){Object.freeze=function(b){return function(c){return typeof c=="function"?c:b(c)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(b){return b}),Object.isSealed||(Object.isSealed=function(b){return!1}),Object.isFrozen||(Object.isFrozen=function(b){return!1}),Object.isExtensible||(Object.isExtensible=function(b){if(Object(b)===b)throw new TypeError;var c="";while(i(b,c))c+="?";b[c]=!0;var d=i(b,c);return delete b[c],d});if(!Object.keys){var x=!0,y=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],z=y.length;for(var A in{toString:null})x=!1;Object.keys=function H(a){if(typeof a!="object"&&typeof a!="function"||a===null)throw new TypeError("Object.keys called on a non-object");var H=[];for(var b in a)i(a,b)&&H.push(b);if(x)for(var c=0,d=z;c<d;c++){var e=y[c];i(a,e)&&H.push(e)}return H}}if(!Date.prototype.toISOString||(new Date(-621987552e5)).toISOString().indexOf("-000001")===-1)Date.prototype.toISOString=function(){var b,c,d,e;if(!isFinite(this))throw new RangeError;b=[this.getUTCMonth()+1,this.getUTCDate(),this.getUTCHours(),this.getUTCMinutes(),this.getUTCSeconds()],e=this.getUTCFullYear(),e=(e<0?"-":e>9999?"+":"")+("00000"+Math.abs(e)).slice(0<=e&&e<=9999?-4:-6),c=b.length;while(c--)d=b[c],d<10&&(b[c]="0"+d);return e+"-"+b.slice(0,2).join("-")+"T"+b.slice(2).join(":")+"."+("000"+this.getUTCMilliseconds()).slice(-3)+"Z"};Date.now||(Date.now=function(){return(new Date).getTime()}),Date.prototype.toJSON||(Date.prototype.toJSON=function(b){if(typeof this.toISOString!="function")throw new TypeError;return this.toISOString()}),Date.parse("+275760-09-13T00:00:00.000Z")!==864e13&&(Date=function(a){var b=function e(b,c,d,f,g,h,i){var j=arguments.length;if(this instanceof a){var k=j==1&&String(b)===b?new a(e.parse(b)):j>=7?new a(b,c,d,f,g,h,i):j>=6?new a(b,c,d,f,g,h):j>=5?new a(b,c,d,f,g):j>=4?new a(b,c,d,f):j>=3?new a(b,c,d):j>=2?new a(b,c):j>=1?new a(b):new a;return k.constructor=e,k}return a.apply(this,arguments)},c=new RegExp("^(\\d{4}|[+-]\\d{6})(?:-(\\d{2})(?:-(\\d{2})(?:T(\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.(\\d{3}))?)?(?:Z|(?:([-+])(\\d{2}):(\\d{2})))?)?)?)?$");for(var d in a)b[d]=a[d];return b.now=a.now,b.UTC=a.UTC,b.prototype=a.prototype,b.prototype.constructor=b,b.parse=function(d){var e=c.exec(d);if(e){e.shift();for(var f=1;f<7;f++)e[f]=+(e[f]||(f<3?1:0)),f==1&&e[f]--;var g=+e.pop(),h=+e.pop(),i=e.pop(),j=0;if(i){if(h>23||g>59)return NaN;j=(h*60+g)*6e4*(i=="+"?-1:1)}var k=+e[0];return 0<=k&&k<=99?(e[0]=k+400,a.UTC.apply(this,e)+j-126227808e5):a.UTC.apply(this,e)+j}return a.parse.apply(this,arguments)},b}(Date));var B=" \n\f\r Â áš€á Žâ€€â€â€‚         âŸã€€\u2028\u2029";if(!String.prototype.trim||B.trim()){B="["+B+"]";var C=new RegExp("^"+B+B+"*"),D=new RegExp(B+B+"*$");String.prototype.trim=function(){return String(this).replace(C,"").replace(D,"")}}var E=function(a){return a=+a,a!==a?a=0:a!==0&&a!==1/0&&a!==-Infinity&&(a=(a>0||-1)*Math.floor(Math.abs(a))),a},F="a"[0]!="a",G=function(a){if(a==null)throw new TypeError;return F&&typeof a=="string"&&a?a.split(""):Object(a)}}),define("ace/lib/dom",["require","exports","module"],function(a,b,c){"use strict";var d="http://www.w3.org/1999/xhtml";b.createElement=function(a,b){return document.createElementNS?document.createElementNS(b||d,a):document.createElement(a)},b.setText=function(a,b){a.innerText!==undefined&&(a.innerText=b),a.textContent!==undefined&&(a.textContent=b)},b.hasCssClass=function(a,b){var c=a.className.split(/\s+/g);return c.indexOf(b)!==-1},b.addCssClass=function(a,c){b.hasCssClass(a,c)||(a.className+=" "+c)},b.removeCssClass=function(a,b){var c=a.className.split(/\s+/g);for(;;){var d=c.indexOf(b);if(d==-1)break;c.splice(d,1)}a.className=c.join(" ")},b.toggleCssClass=function(a,b){var c=a.className.split(/\s+/g),d=!0;for(;;){var e=c.indexOf(b);if(e==-1)break;d=!1,c.splice(e,1)}return d&&c.push(b),a.className=c.join(" "),d},b.setCssClass=function(a,c,d){d?b.addCssClass(a,c):b.removeCssClass(a,c)},b.hasCssString=function(a,b){var c=0,d;b=b||document;if(b.createStyleSheet&&(d=b.styleSheets)){while(c<d.length)if(d[c++].owningElement.id===a)return!0}else if(d=b.getElementsByTagName("style"))while(c<d.length)if(d[c++].id===a)return!0;return!1},b.importCssString=function(c,e,f){f=f||document;if(e&&b.hasCssString(e,f))return null;var g;if(f.createStyleSheet)g=f.createStyleSheet(),g.cssText=c,e&&(g.owningElement.id=e);else{g=f.createElementNS?f.createElementNS(d,"style"):f.createElement("style"),g.appendChild(f.createTextNode(c)),e&&(g.id=e);var h=f.getElementsByTagName("head")[0]||f.documentElement;h.appendChild(g)}},b.importCssStylsheet=function(a,c){if(c.createStyleSheet)c.createStyleSheet(a);else{var d=b.createElement("link");d.rel="stylesheet",d.href=a;var e=c.getElementsByTagName("head")[0]||c.documentElement;e.appendChild(d)}},b.getInnerWidth=function(a){return parseInt(b.computedStyle(a,"paddingLeft"),10)+parseInt(b.computedStyle(a,"paddingRight"),10)+a.clientWidth},b.getInnerHeight=function(a){return parseInt(b.computedStyle(a,"paddingTop"),10)+parseInt(b.computedStyle(a,"paddingBottom"),10)+a.clientHeight},window.pageYOffset!==undefined?(b.getPageScrollTop=function(){return window.pageYOffset},b.getPageScrollLeft=function(){return window.pageXOffset}):(b.getPageScrollTop=function(){return document.body.scrollTop},b.getPageScrollLeft=function(){return document.body.scrollLeft}),window.getComputedStyle?b.computedStyle=function(a,b){return b?(window.getComputedStyle(a,"")||{})[b]||"":window.getComputedStyle(a,"")||{}}:b.computedStyle=function(a,b){return b?a.currentStyle[b]:a.currentStyle},b.scrollbarWidth=function(a){var c=b.createElement("p");c.style.width="100%",c.style.minWidth="0px",c.style.height="200px";var d=b.createElement("div"),e=d.style;e.position="absolute",e.left="-10000px",e.overflow="hidden",e.width="200px",e.minWidth="0px",e.height="150px",d.appendChild(c);var f=a.body||a.documentElement;f.appendChild(d);var g=c.offsetWidth;e.overflow="scroll";var h=c.offsetWidth;return g==h&&(h=d.clientWidth),f.removeChild(d),g-h},b.setInnerHtml=function(a,b){var c=a.cloneNode(!1);return c.innerHTML=b,a.parentNode.replaceChild(c,a),c},b.setInnerText=function(a,b){var c=a.ownerDocument;c.body&&"textContent"in c.body?a.textContent=b:a.innerText=b},b.getInnerText=function(a){var b=a.ownerDocument;return b.body&&"textContent"in b.body?a.textContent:a.innerText||a.textContent||""},b.getParentWindow=function(a){return a.defaultView||a.parentWindow}}),define("ace/lib/event",["require","exports","module","ace/lib/keys","ace/lib/useragent","ace/lib/dom"],function(a,b,c){function g(a,b,c){var f=0;e.isOpera&&e.isMac?f=0|(b.metaKey?1:0)|(b.altKey?2:0)|(b.shiftKey?4:0)|(b.ctrlKey?8:0):f=0|(b.ctrlKey?1:0)|(b.altKey?2:0)|(b.shiftKey?4:0)|(b.metaKey?8:0);if(c in d.MODIFIER_KEYS){switch(d.MODIFIER_KEYS[c]){case"Alt":f=2;break;case"Shift":f=4;break;case"Ctrl":f=1;break;default:f=8}c=0}return f&8&&(c==91||c==93)&&(c=0),!!f||c in d.FUNCTION_KEYS||c in d.PRINTABLE_KEYS?a(b,f,c):!1}"use strict";var d=a("./keys"),e=a("./useragent"),f=a("./dom");b.addListener=function(a,b,c){if(a.addEventListener)return a.addEventListener(b,c,!1);if(a.attachEvent){var d=function(){c(window.event)};c._wrapper=d,a.attachEvent("on"+b,d)}},b.removeListener=function(a,b,c){if(a.removeEventListener)return a.removeEventListener(b,c,!1);a.detachEvent&&a.detachEvent("on"+b,c._wrapper||c)},b.stopEvent=function(a){return b.stopPropagation(a),b.preventDefault(a),!1},b.stopPropagation=function(a){a.stopPropagation?a.stopPropagation():a.cancelBubble=!0},b.preventDefault=function(a){a.preventDefault?a.preventDefault():a.returnValue=!1},b.getDocumentX=function(a){return a.clientX?a.clientX+f.getPageScrollLeft():a.pageX},b.getDocumentY=function(a){return a.clientY?a.clientY+f.getPageScrollTop():a.pageY},b.getButton=function(a){return a.type=="dblclick"?0:a.type=="contextmenu"?2:a.preventDefault?a.button:{1:0,2:2,4:1}[a.button]},document.documentElement.setCapture?b.capture=function(a,c,d){function e(a){return c(a),b.stopPropagation(a)}function g(e){c(e),f||(f=!0,d(e)),b.removeListener(a,"mousemove",c),b.removeListener(a,"mouseup",g),b.removeListener(a,"losecapture",g),a.releaseCapture()}var f=!1;b.addListener(a,"mousemove",c),b.addListener(a,"mouseup",g),b.addListener(a,"losecapture",g),a.setCapture()}:b.capture=function(a,b,c){function d(a){b(a),a.stopPropagation()}function e(a){b&&b(a),c&&c(a),document.removeEventListener("mousemove",d,!0),document.removeEventListener("mouseup",e,!0),a.stopPropagation()}document.addEventListener("mousemove",d,!0),document.addEventListener("mouseup",e,!0)},b.addMouseWheelListener=function(a,c){var d=8,e=function(a){a.wheelDelta!==undefined?a.wheelDeltaX!==undefined?(a.wheelX=-a.wheelDeltaX/d,a.wheelY=-a.wheelDeltaY/d):(a.wheelX=0,a.wheelY=-a.wheelDelta/d):a.axis&&a.axis==a.HORIZONTAL_AXIS?(a.wheelX=(a.detail||0)*5,a.wheelY=0):(a.wheelX=0,a.wheelY=(a.detail||0)*5),c(a)};b.addListener(a,"DOMMouseScroll",e),b.addListener(a,"mousewheel",e)},b.addMultiMouseDownListener=function(a,c,d,f,g){var h=0,i,j,k=function(a){h+=1,h==1&&(i=a.clientX,j=a.clientY,setTimeout(function(){h=0},f||600));var e=b.getButton(a)==c;if(!e||Math.abs(a.clientX-i)>5||Math.abs(a.clientY-j)>5)h=0;h==d&&(h=0,g(a));if(e)return b.preventDefault(a)};b.addListener(a,"mousedown",k),e.isOldIE&&b.addListener(a,"dblclick",k)},b.addCommandKeyListener=function(a,c){var d=b.addListener;if(e.isOldGecko||e.isOpera){var f=null;d(a,"keydown",function(a){f=a.keyCode}),d(a,"keypress",function(a){return g(c,a,f)})}else{var h=null;d(a,"keydown",function(a){return h=a.keyIdentifier||a.keyCode,g(c,a,a.keyCode)})}};if(window.postMessage){var h=1;b.nextTick=function(a,c){c=c||window;var d="zero-timeout-message-"+h;b.addListener(c,"message",function e(f){f.data==d&&(b.stopPropagation(f),b.removeListener(c,"message",e),a())}),c.postMessage(d,"*")}}else b.nextTick=function(a,b){b=b||window,window.setTimeout(a,0)}}),define("ace/lib/keys",["require","exports","module","ace/lib/oop"],function(a,b,c){"use strict";var d=a("./oop"),e=function(){var a={MODIFIER_KEYS:{16:"Shift",17:"Ctrl",18:"Alt",224:"Meta"},KEY_MODS:{ctrl:1,alt:2,option:2,shift:4,meta:8,command:8},FUNCTION_KEYS:{8:"Backspace",9:"Tab",13:"Return",19:"Pause",27:"Esc",32:"Space",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"Left",38:"Up",39:"Right",40:"Down",44:"Print",45:"Insert",46:"Delete",96:"Numpad0",97:"Numpad1",98:"Numpad2",99:"Numpad3",100:"Numpad4",101:"Numpad5",102:"Numpad6",103:"Numpad7",104:"Numpad8",105:"Numpad9",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"Numlock",145:"Scrolllock"},PRINTABLE_KEYS:{32:" ",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",59:";",61:"=",65:"a",66:"b",67:"c",68:"d",69:"e",70:"f",71:"g",72:"h",73:"i",74:"j",75:"k",76:"l",77:"m",78:"n",79:"o",80:"p",81:"q",82:"r",83:"s",84:"t",85:"u",86:"v",87:"w",88:"x",89:"y",90:"z",107:"+",109:"-",110:".",188:",",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:'"'}};for(var b in a.FUNCTION_KEYS){var c=a.FUNCTION_KEYS[b].toUpperCase();a[c]=parseInt(b,10)}return d.mixin(a,a.MODIFIER_KEYS),d.mixin(a,a.PRINTABLE_KEYS),d.mixin(a,a.FUNCTION_KEYS),a}();d.mixin(b,e),b.keyCodeToString=function(a){return(e[a]||String.fromCharCode(a)).toLowerCase()}}),define("ace/lib/oop",["require","exports","module"],function(a,b,c){"use strict",b.inherits=function(){var a=function(){};return function(b,c){a.prototype=c.prototype,b.super_=c.prototype,b.prototype=new a,b.prototype.constructor=b}}(),b.mixin=function(a,b){for(var c in b)a[c]=b[c]},b.implement=function(a,c){b.mixin(a,c)}}),define("ace/lib/useragent",["require","exports","module"],function(a,b,c){"use strict";var d=(navigator.platform.match(/mac|win|linux/i)||["other"])[0].toLowerCase(),e=navigator.userAgent;b.isWin=d=="win",b.isMac=d=="mac",b.isLinux=d=="linux",b.isIE=navigator.appName=="Microsoft Internet Explorer"&&parseFloat(navigator.userAgent.match(/MSIE ([0-9]+[\.0-9]+)/)[1]),b.isOldIE=b.isIE&&b.isIE<9,b.isGecko=b.isMozilla=window.controllers&&window.navigator.product==="Gecko",b.isOldGecko=b.isGecko&&parseInt((navigator.userAgent.match(/rv\:(\d+)/)||[])[1],10)<4,b.isOpera=window.opera&&Object.prototype.toString.call(window.opera)=="[object Opera]",b.isWebKit=parseFloat(e.split("WebKit/")[1])||undefined,b.isChrome=parseFloat(e.split(" Chrome/")[1])||undefined,b.isAIR=e.indexOf("AdobeAIR")>=0,b.isIPad=e.indexOf("iPad")>=0,b.isTouchPad=e.indexOf("TouchPad")>=0,b.OS={LINUX:"LINUX",MAC:"MAC",WINDOWS:"WINDOWS"},b.getOS=function(){return b.isMac?b.OS.MAC:b.isLinux?b.OS.LINUX:b.OS.WINDOWS}}),define("ace/editor",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/oop","ace/lib/lang","ace/lib/useragent","ace/keyboard/textinput","ace/mouse/mouse_handler","ace/mouse/fold_handler","ace/keyboard/keybinding","ace/edit_session","ace/search","ace/range","ace/lib/event_emitter","ace/commands/command_manager","ace/commands/default_commands"],function(a,b,c){"use strict",a("./lib/fixoldbrowsers");var d=a("./lib/oop"),e=a("./lib/lang"),f=a("./lib/useragent"),g=a("./keyboard/textinput").TextInput,h=a("./mouse/mouse_handler").MouseHandler,i=a("./mouse/fold_handler").FoldHandler,j=a("./keyboard/keybinding").KeyBinding,k=a("./edit_session").EditSession,l=a("./search").Search,m=a("./range").Range,n=a("./lib/event_emitter").EventEmitter,o=a("./commands/command_manager").CommandManager,p=a("./commands/default_commands").commands,q=function(a,b){var c=a.getContainerElement();this.container=c,this.renderer=a,this.textInput=new g(a.getTextAreaContainer(),this),this.keyBinding=new j(this),f.isIPad||(this.$mouseHandler=new h(this),new i(this)),this.$blockScrolling=0,this.$search=(new l).set({wrap:!0}),this.commands=new o(f.isMac?"mac":"win",p),this.setSession(b||new k(""))};(function(){d.implement(this,n),this.setKeyboardHandler=function(a){this.keyBinding.setKeyboardHandler(a)},this.getKeyboardHandler=function(){return this.keyBinding.getKeyboardHandler()},this.setSession=function(a){if(this.session==a)return;if(this.session){var b=this.session;this.session.removeEventListener("change",this.$onDocumentChange),this.session.removeEventListener("changeMode",this.$onChangeMode),this.session.removeEventListener("tokenizerUpdate",this.$onTokenizerUpdate),this.session.removeEventListener("changeTabSize",this.$onChangeTabSize),this.session.removeEventListener("changeWrapLimit",this.$onChangeWrapLimit),this.session.removeEventListener("changeWrapMode",this.$onChangeWrapMode),this.session.removeEventListener("onChangeFold",this.$onChangeFold),this.session.removeEventListener("changeFrontMarker",this.$onChangeFrontMarker),this.session.removeEventListener("changeBackMarker",this.$onChangeBackMarker),this.session.removeEventListener("changeBreakpoint",this.$onChangeBreakpoint),this.session.removeEventListener("changeAnnotation",this.$onChangeAnnotation),this.session.removeEventListener("changeOverwrite",this.$onCursorChange),this.session.removeEventListener("changeScrollTop",this.$onScrollTopChange),this.session.removeEventListener("changeLeftTop",this.$onScrollLeftChange);var c=this.session.getSelection();c.removeEventListener("changeCursor",this.$onCursorChange),c.removeEventListener("changeSelection",this.$onSelectionChange)}this.session=a,this.$onDocumentChange=this.onDocumentChange.bind(this),a.addEventListener("change",this.$onDocumentChange),this.renderer.setSession(a),this.$onChangeMode=this.onChangeMode.bind(this),a.addEventListener("changeMode",this.$onChangeMode),this.$onTokenizerUpdate=this.onTokenizerUpdate.bind(this),a.addEventListener("tokenizerUpdate",this.$onTokenizerUpdate),this.$onChangeTabSize=this.renderer.updateText.bind(this.renderer),a.addEventListener("changeTabSize",this.$onChangeTabSize),this.$onChangeWrapLimit=this.onChangeWrapLimit.bind(this),a.addEventListener("changeWrapLimit",this.$onChangeWrapLimit),this.$onChangeWrapMode=this.onChangeWrapMode.bind(this),a.addEventListener("changeWrapMode",this.$onChangeWrapMode),this.$onChangeFold=this.onChangeFold.bind(this),a.addEventListener("changeFold",this.$onChangeFold),this.$onChangeFrontMarker=this.onChangeFrontMarker.bind(this),this.session.addEventListener("changeFrontMarker",this.$onChangeFrontMarker),this.$onChangeBackMarker=this.onChangeBackMarker.bind(this),this.session.addEventListener("changeBackMarker",this.$onChangeBackMarker),this.$onChangeBreakpoint=this.onChangeBreakpoint.bind(this),this.session.addEventListener("changeBreakpoint",this.$onChangeBreakpoint),this.$onChangeAnnotation=this.onChangeAnnotation.bind(this),this.session.addEventListener("changeAnnotation",this.$onChangeAnnotation),this.$onCursorChange=this.onCursorChange.bind(this),this.session.addEventListener("changeOverwrite",this.$onCursorChange),this.$onScrollTopChange=this.onScrollTopChange.bind(this),this.session.addEventListener("changeScrollTop",this.$onScrollTopChange),this.$onScrollLeftChange=this.onScrollLeftChange.bind(this),this.session.addEventListener("changeScrollLeft",this.$onScrollLeftChange),this.selection=a.getSelection(),this.selection.addEventListener("changeCursor",this.$onCursorChange),this.$onSelectionChange=this.onSelectionChange.bind(this),this.selection.addEventListener("changeSelection",this.$onSelectionChange),this.onChangeMode(),this.$blockScrolling+=1,this.onCursorChange(),this.$blockScrolling-=1,this.onScrollTopChange(),this.onScrollLeftChange(),this.onSelectionChange(),this.onChangeFrontMarker(),this.onChangeBackMarker(),this.onChangeBreakpoint(),this.onChangeAnnotation(),this.session.getUseWrapMode()&&this.renderer.adjustWrapLimit(),this.renderer.updateFull(),this._emit("changeSession",{session:a,oldSession:b})},this.getSession=function(){return this.session},this.getSelection=function(){return this.selection},this.resize=function(){this.renderer.onResize()},this.setTheme=function(a){this.renderer.setTheme(a)},this.getTheme=function(){return this.renderer.getTheme()},this.setStyle=function(a){this.renderer.setStyle(a)},this.unsetStyle=function(a){this.renderer.unsetStyle(a)},this.setFontSize=function(a){this.container.style.fontSize=a,this.renderer.updateFontSize()},this.$highlightBrackets=function(){this.session.$bracketHighlight&&(this.session.removeMarker(this.session.$bracketHighlight),this.session.$bracketHighlight=null);if(this.$highlightPending)return;var a=this;this.$highlightPending=!0,setTimeout(function(){a.$highlightPending=!1;var b=a.session.findMatchingBracket(a.getCursorPosition());if(b){var c=new m(b.row,b.column,b.row,b.column+1);a.session.$bracketHighlight=a.session.addMarker(c,"ace_bracket","text")}},10)},this.focus=function(){var a=this;setTimeout(function(){a.textInput.focus()}),this.textInput.focus()},this.isFocused=function(){return this.textInput.isFocused()},this.blur=function(){this.textInput.blur()},this.onFocus=function(){this.renderer.showCursor(),this.renderer.visualizeFocus(),this._emit("focus")},this.onBlur=function(){this.renderer.hideCursor(),this.renderer.visualizeBlur(),this._emit("blur")},this.onDocumentChange=function(a){var b=a.data,c=b.range,d;c.start.row==c.end.row&&b.action!="insertLines"&&b.action!="removeLines"?d=c.end.row:d=Infinity,this.renderer.updateLines(c.start.row,d),this._emit("change",a),this.onCursorChange()},this.onTokenizerUpdate=function(a){var b=a.data;this.renderer.updateLines(b.first,b.last)},this.onScrollTopChange=function(){this.renderer.scrollToY(this.session.getScrollTop())},this.onScrollLeftChange=function(){this.renderer.scrollToX(this.session.getScrollLeft())},this.onCursorChange=function(){this.renderer.updateCursor(),this.$blockScrolling||this.renderer.scrollCursorIntoView(),this.renderer.moveTextAreaToCursor(this.textInput.getElement()),this.$highlightBrackets(),this.$updateHighlightActiveLine()},this.$updateHighlightActiveLine=function(){var a=this.getSession();a.$highlightLineMarker&&a.removeMarker(a.$highlightLineMarker),typeof this.$lastrow=="number"&&this.renderer.removeGutterDecoration(this.$lastrow,"ace_gutter_active_line"),a.$highlightLineMarker=null,this.$lastrow=null;if(this.getHighlightActiveLine()){var b=this.getCursorPosition(),c=this.session.getFoldLine(b.row);if(this.getSelectionStyle()!="line"||!this.selection.isMultiLine()){var d;c?d=new m(c.start.row,0,c.end.row+1,0):d=new m(b.row,0,b.row+1,0),a.$highlightLineMarker=a.addMarker(d,"ace_active_line","background")}this.renderer.addGutterDecoration(this.$lastrow=b.row,"ace_gutter_active_line")}},this.onSelectionChange=function(a){var b=this.getSession();b.$selectionMarker&&b.removeMarker(b.$selectionMarker),b.$selectionMarker=null;if(!this.selection.isEmpty()){var c=this.selection.getRange(),d=this.getSelectionStyle();b.$selectionMarker=b.addMarker(c,"ace_selection",d)}else this.$updateHighlightActiveLine();this.$highlightSelectedWord&&this.session.getMode().highlightSelection(this)},this.onChangeFrontMarker=function(){this.renderer.updateFrontMarkers()},this.onChangeBackMarker=function(){this.renderer.updateBackMarkers()},this.onChangeBreakpoint=function(){this.renderer.setBreakpoints(this.session.getBreakpoints())},this.onChangeAnnotation=function(){this.renderer.setAnnotations(this.session.getAnnotations())},this.onChangeMode=function(){this.renderer.updateText()},this.onChangeWrapLimit=function(){this.renderer.updateFull()},this.onChangeWrapMode=function(){this.renderer.onResize(!0)},this.onChangeFold=function(){this.$updateHighlightActiveLine(),this.renderer.updateFull()},this.getCopyText=function(){var a="";return this.selection.isEmpty()||(a=this.session.getTextRange(this.getSelectionRange())),this._emit("copy",a),a},this.onCut=function(){this.commands.exec("cut",this)},this.insert=function(a){var b=this.session,c=b.getMode(),d=this.getCursorPosition();if(this.getBehavioursEnabled()){var e=c.transformAction(b.getState(d.row),"insertion",this,b,a);e&&(a=e.text)}a=a.replace(" ",this.session.getTabString());if(!this.selection.isEmpty())d=this.session.remove(this.getSelectionRange()),this.clearSelection();else if(this.session.getOverwrite()){var f=new m.fromPoints(d,d);f.end.column+=a.length,this.session.remove(f)}this.clearSelection();var g=d.column,h=b.getState(d.row),i=c.checkOutdent(h,b.getLine(d.row),a),j=b.getLine(d.row),k=c.getNextLineIndent(h,j.slice(0,d.column),b.getTabString()),l=b.insert(d,a);e&&e.selection&&(e.selection.length==2?this.selection.setSelectionRange(new m(d.row,g+e.selection[0],d.row,g+e.selection[1])):this.selection.setSelectionRange(new m(d.row+e.selection[0],e.selection[1],d.row+e.selection[2],e.selection[3])));var h=b.getState(d.row);if(b.getDocument().isNewLine(a)){this.moveCursorTo(d.row+1,0);var n=b.getTabSize(),o=Number.MAX_VALUE;for(var p=d.row+1;p<=l.row;++p){var q=0;j=b.getLine(p);for(var r=0;r<j.length;++r)if(j.charAt(r)==" ")q+=n;else{if(j.charAt(r)!=" ")break;q+=1}/[^\s]/.test(j)&&(o=Math.min(q,o))}for(var p=d.row+1;p<=l.row;++p){var s=o;j=b.getLine(p);for(var r=0;r<j.length&&s>0;++r)j.charAt(r)==" "?s-=n:j.charAt(r)==" "&&(s-=1);b.remove(new m(p,0,p,r))}b.indentRows(d.row+1,l.row,k)}i&&c.autoOutdent(h,b,d.row)},this.onTextInput=function(a,b){b&&this._emit("paste",a),this.keyBinding.onTextInput(a,b)},this.onCommandKey=function(a,b,c){this.keyBinding.onCommandKey(a,b,c)},this.setOverwrite=function(a){this.session.setOverwrite(a)},this.getOverwrite=function(){return this.session.getOverwrite()},this.toggleOverwrite=function(){this.session.toggleOverwrite()},this.setScrollSpeed=function(a){this.$mouseHandler.setScrollSpeed(a)},this.getScrollSpeed=function(){return this.$mouseHandler.getScrollSpeed()},this.setDragDelay=function(a){this.$mouseHandler.setDragDelay(a)},this.getDragDelay=function(){return this.$mouseHandler.getDragDelay()},this.$selectionStyle="line",this.setSelectionStyle=function(a){if(this.$selectionStyle==a)return;this.$selectionStyle=a,this.onSelectionChange(),this._emit("changeSelectionStyle",{data:a})},this.getSelectionStyle=function(){return this.$selectionStyle},this.$highlightActiveLine=!0,this.setHighlightActiveLine=function(a){if(this.$highlightActiveLine==a)return;this.$highlightActiveLine=a,this.$updateHighlightActiveLine()},this.getHighlightActiveLine=function(){return this.$highlightActiveLine},this.$highlightSelectedWord=!0,this.setHighlightSelectedWord=function(a){if(this.$highlightSelectedWord==a)return;this.$highlightSelectedWord=a,a?this.session.getMode().highlightSelection(this):this.session.getMode().clearSelectionHighlight(this)},this.getHighlightSelectedWord=function(){return this.$highlightSelectedWord},this.setAnimatedScroll=function(a){this.renderer.setAnimatedScroll(a)},this.getAnimatedScroll=function(){return this.renderer.getAnimatedScroll()},this.setShowInvisibles=function(a){if(this.getShowInvisibles()==a)return;this.renderer.setShowInvisibles(a)},this.getShowInvisibles=function(){return this.renderer.getShowInvisibles()},this.setShowPrintMargin=function(a){this.renderer.setShowPrintMargin(a)},this.getShowPrintMargin=function(){return this.renderer.getShowPrintMargin()},this.setPrintMarginColumn=function(a){this.renderer.setPrintMarginColumn(a)},this.getPrintMarginColumn=function(){return this.renderer.getPrintMarginColumn()},this.$readOnly=!1,this.setReadOnly=function(a){this.$readOnly=a},this.getReadOnly=function(){return this.$readOnly},this.$modeBehaviours=!0,this.setBehavioursEnabled=function(a){this.$modeBehaviours=a},this.getBehavioursEnabled=function(){return this.$modeBehaviours},this.setShowFoldWidgets=function(a){var b=this.renderer.$gutterLayer;if(b.getShowFoldWidgets()==a)return;this.renderer.$gutterLayer.setShowFoldWidgets(a),this.$showFoldWidgets=a,this.renderer.updateFull()},this.getShowFoldWidgets=function(){return this.renderer.$gutterLayer.getShowFoldWidgets()},this.remove=function(a){this.selection.isEmpty()&&(a=="left"?this.selection.selectLeft():this.selection.selectRight());var b=this.getSelectionRange();if(this.getBehavioursEnabled()){var c=this.session,d=c.getState(b.start.row),e=c.getMode().transformAction(d,"deletion",this,c,b);e&&(b=e)}this.session.remove(b),this.clearSelection()},this.removeWordRight=function(){this.selection.isEmpty()&&this.selection.selectWordRight(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeWordLeft=function(){this.selection.isEmpty()&&this.selection.selectWordLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineStart=function(){this.selection.isEmpty()&&this.selection.selectLineStart(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineEnd=function(){this.selection.isEmpty()&&this.selection.selectLineEnd();var a=this.getSelectionRange();a.start.column==a.end.column&&a.start.row==a.end.row&&(a.end.column=0,a.end.row++),this.session.remove(a),this.clearSelection()},this.splitLine=function(){this.selection.isEmpty()||(this.session.remove(this.getSelectionRange()),this.clearSelection());var a=this.getCursorPosition();this.insert("\n"),this.moveCursorToPosition(a)},this.transposeLetters=function(){if(!this.selection.isEmpty())return;var a=this.getCursorPosition(),b=a.column;if(b===0)return;var c=this.session.getLine(a.row),d,e;b<c.length?(d=c.charAt(b)+c.charAt(b-1),e=new m(a.row,b-1,a.row,b+1)):(d=c.charAt(b-1)+c.charAt(b-2),e=new m(a.row,b-2,a.row,b)),this.session.replace(e,d)},this.toLowerCase=function(){var a=this.getSelectionRange();this.selection.isEmpty()&&this.selection.selectWord();var b=this.getSelectionRange(),c=this.session.getTextRange(b);this.session.replace(b,c.toLowerCase()),this.selection.setSelectionRange(a)},this.toUpperCase=function(){var a=this.getSelectionRange();this.selection.isEmpty()&&this.selection.selectWord();var b=this.getSelectionRange(),c=this.session.getTextRange(b);this.session.replace(b,c.toUpperCase()),this.selection.setSelectionRange(a)},this.indent=function(){var a=this.session,b=this.getSelectionRange();if(!(b.start.row<b.end.row||b.start.column<b.end.column)){var d;if(this.session.getUseSoftTabs()){var f=a.getTabSize(),g=this.getCursorPosition(),h=a.documentToScreenColumn(g.row,g.column),i=f-h%f;d=e.stringRepeat(" ",i)}else d=" ";return this.insert(d)}var c=this.$getSelectedRows();a.indentRows(c.first,c.last," ")},this.blockOutdent=function(){var a=this.session.getSelection();this.session.outdentRows(a.getRange())},this.toggleCommentLines=function(){var a=this.session.getState(this.getCursorPosition().row),b=this.$getSelectedRows();this.session.getMode().toggleCommentLines(a,this.session,b.first,b.last)},this.removeLines=function(){var a=this.$getSelectedRows(),b;a.first===0||a.last+1<this.session.getLength()?b=new m(a.first,0,a.last+1,0):b=new m(a.first-1,this.session.getLine(a.first-1).length,a.last,this.session.getLine(a.last).length),this.session.remove(b),this.clearSelection()},this.moveLinesDown=function(){this.$moveLines(function(a,b){return this.session.moveLinesDown(a,b)})},this.moveLinesUp=function(){this.$moveLines(function(a,b){return this.session.moveLinesUp(a,b)})},this.moveText=function(a,b){return this.$readOnly?null:this.session.moveText(a,b)},this.copyLinesUp=function(){this.$moveLines(function(a,b){return this.session.duplicateLines(a,b),0})},this.copyLinesDown=function(){this.$moveLines(function(a,b){return this.session.duplicateLines(a,b)})},this.$moveLines=function(a){var b=this.$getSelectedRows(),c=this.selection;if(!c.isMultiLine())var d=c.getRange(),e=c.isBackwards();var f=a.call(this,b.first,b.last);d?(d.start.row+=f,d.end.row+=f,c.setSelectionRange(d,e)):(c.setSelectionAnchor(b.last+f+1,0),c.$moveSelection(function(){c.moveCursorTo(b.first+f,0)}))},this.$getSelectedRows=function(){var a=this.getSelectionRange().collapseRows();return{first:a.start.row,last:a.end.row}},this.onCompositionStart=function(a){this.renderer.showComposition(this.getCursorPosition())},this.onCompositionUpdate=function(a){this.renderer.setCompositionText(a)},this.onCompositionEnd=function(){this.renderer.hideComposition()},this.getFirstVisibleRow=function(){return this.renderer.getFirstVisibleRow()},this.getLastVisibleRow=function(){return this.renderer.getLastVisibleRow()},this.isRowVisible=function(a){return a>=this.getFirstVisibleRow()&&a<=this.getLastVisibleRow()},this.isRowFullyVisible=function(a){return a>=this.renderer.getFirstFullyVisibleRow()&&a<=this.renderer.getLastFullyVisibleRow()},this.$getVisibleRowCount=function(){return this.renderer.getScrollBottomRow()-this.renderer.getScrollTopRow()+1},this.$getPageDownRow=function(){return this.renderer.getScrollBottomRow()},this.$getPageUpRow=function(){var a=this.renderer.getScrollTopRow(),b=this.renderer.getScrollBottomRow();return a-(b-a)},this.selectPageDown=function(){var a=this.$getPageDownRow()+Math.floor(this.$getVisibleRowCount()/2);this.scrollPageDown();var b=this.getSelection(),c=this.session.documentToScreenPosition(b.getSelectionLead()),d=this.session.screenToDocumentPosition(a,c.column);b.selectTo(d.row,d.column)},this.selectPageUp=function(){var a=this.renderer.getScrollTopRow()-this.renderer.getScrollBottomRow(),b=this.$getPageUpRow()+Math.round(a/2);this.scrollPageUp();var c=this.getSelection(),d=this.session.documentToScreenPosition(c.getSelectionLead()),e=this.session.screenToDocumentPosition(b,d.column);c.selectTo(e.row,e.column)},this.gotoPageDown=function(){var a=this.$getPageDownRow(),b=this.getCursorPositionScreen().column;this.scrollToRow(a),this.getSelection().moveCursorToScreen(a,b)},this.gotoPageUp=function(){var a=this.$getPageUpRow(),b=this.getCursorPositionScreen().column;this.scrollToRow(a),this.getSelection().moveCursorToScreen(a,b)},this.scrollPageDown=function(){this.scrollToRow(this.$getPageDownRow())},this.scrollPageUp=function(){this.renderer.scrollToRow(this.$getPageUpRow())},this.scrollToRow=function(a){this.renderer.scrollToRow(a)},this.scrollToLine=function(a,b){this.renderer.scrollToLine(a,b)},this.centerSelection=function(){var a=this.getSelectionRange(),b=Math.floor(a.start.row+(a.end.row-a.start.row)/2);this.renderer.scrollToLine(b,!0)},this.getCursorPosition=function(){return this.selection.getCursor()},this.getCursorPositionScreen=function(){return this.session.documentToScreenPosition(this.getCursorPosition())},this.getSelectionRange=function(){return this.selection.getRange()},this.selectAll=function(){this.$blockScrolling+=1,this.selection.selectAll(),this.$blockScrolling-=1},this.clearSelection=function(){this.selection.clearSelection()},this.moveCursorTo=function(a,b){this.selection.moveCursorTo(a,b)},this.moveCursorToPosition=function(a){this.selection.moveCursorToPosition(a)},this.jumpToMatching=function(){var a=this.getCursorPosition(),b=this.session.findMatchingBracket(a);b||(a.column+=1,b=this.session.findMatchingBracket(a)),b||(a.column-=2,b=this.session.findMatchingBracket(a)),b&&(this.clearSelection(),this.moveCursorTo(b.row,b.column))},this.gotoLine=function(a,b){this.selection.clearSelection(),this.session.unfold({row:a-1,column:b||0}),this.$blockScrolling+=1,this.moveCursorTo(a-1,b||0),this.$blockScrolling-=1,this.isRowFullyVisible(this.getCursorPosition().row)||this.scrollToLine(a,!0)},this.navigateTo=function(a,b){this.clearSelection(),this.moveCursorTo(a,b)},this.navigateUp=function(a){this.selection.clearSelection(),a=a||1,this.selection.moveCursorBy(-a,0)},this.navigateDown=function(a){this.selection.clearSelection(),a=a||1,this.selection.moveCursorBy(a,0)},this.navigateLeft=function(a){if(!this.selection.isEmpty()){var b=this.getSelectionRange().start;this.moveCursorToPosition(b)}else{a=a||1;while(a--)this.selection.moveCursorLeft()}this.clearSelection()},this.navigateRight=function(a){if(!this.selection.isEmpty()){var b=this.getSelectionRange().end;this.moveCursorToPosition(b)}else{a=a||1;while(a--)this.selection.moveCursorRight()}this.clearSelection()},this.navigateLineStart=function(){this.selection.moveCursorLineStart(),this.clearSelection()},this.navigateLineEnd=function(){this.selection.moveCursorLineEnd(),this.clearSelection()},this.navigateFileEnd=function(){this.selection.moveCursorFileEnd(),this.clearSelection()},this.navigateFileStart=function(){this.selection.moveCursorFileStart(),this.clearSelection()},this.navigateWordRight=function(){this.selection.moveCursorWordRight(),this.clearSelection()},this.navigateWordLeft=function(){this.selection.moveCursorWordLeft(),this.clearSelection()},this.replace=function(a,b){b&&this.$search.set(b);var c=this.$search.find(this.session),d=0;return c?(this.$tryReplace(c,a)&&(d=1),c!==null&&(this.selection.setSelectionRange(c),this.renderer.scrollSelectionIntoView(c.start,c.end)),d):d},this.replaceAll=function(a,b){b&&this.$search.set(b);var c=this.$search.findAll(this.session),d=0;if(!c.length)return d;var e=this.getSelectionRange();this.clearSelection(),this.selection.moveCursorTo(0,0),this.$blockScrolling+=1;for(var f=c.length-1;f>=0;--f)this.$tryReplace(c[f],a)&&d++;return this.selection.setSelectionRange(e),this.$blockScrolling-=1,d},this.$tryReplace=function(a,b){var c=this.session.getTextRange(a);return b=this.$search.replace(c,b),b!==null?(a.end=this.session.replace(a,b),a):null},this.getLastSearchOptions=function(){return this.$search.getOptions()},this.find=function(a,b){this.clearSelection(),b=b||{},b.needle=a,this.$search.set(b),this.$find()},this.findNext=function(a){a=a||{},typeof a.backwards=="undefined"&&(a.backwards=!1),this.$search.set(a),this.$find()},this.findPrevious=function(a){a=a||{},typeof a.backwards=="undefined"&&(a.backwards=!0),this.$search.set(a),this.$find()},this.$find=function(a){this.selection.isEmpty()||this.$search.set({needle:this.session.getTextRange(this.getSelectionRange())}),typeof a!="undefined"&&this.$search.set({backwards:a});var b=this.$search.find(this.session);if(b){this.session.unfold(b),this.$blockScrolling+=1,this.selection.setSelectionRange(b),this.$blockScrolling-=1;if(this.getAnimatedScroll()){var c=this.getCursorPosition();this.isRowFullyVisible(c.row)||this.scrollToLine(c.row,!0)}else this.renderer.scrollSelectionIntoView(b.start,b.end)}},this.undo=function(){this.session.getUndoManager().undo()},this.redo=function(){this.session.getUndoManager().redo()},this.destroy=function(){this.renderer.destroy()}}).call(q.prototype),b.Editor=q}),define("ace/lib/lang",["require","exports","module"],function(a,b,c){"use strict",b.stringReverse=function(a){return a.split("").reverse().join("")},b.stringRepeat=function(a,b){return(new Array(b+1)).join(a)};var d=/^\s\s*/,e=/\s\s*$/;b.stringTrimLeft=function(a){return a.replace(d,"")},b.stringTrimRight=function(a){return a.replace(e,"")},b.copyObject=function(a){var b={};for(var c in a)b[c]=a[c];return b},b.copyArray=function(a){var b=[];for(var c=0,d=a.length;c<d;c++)a[c]&&typeof a[c]=="object"?b[c]=this.copyObject(a[c]):b[c]=a[c];return b},b.deepCopy=function(a){if(typeof a!="object")return a;var b=a.constructor();for(var c in a)typeof a[c]=="object"?b[c]=this.deepCopy(a[c]):b[c]=a[c];return b},b.arrayToMap=function(a){var b={};for(var c=0;c<a.length;c++)b[a[c]]=1;return b},b.arrayRemove=function(a,b){for(var c=0;c<=a.length;c++)b===a[c]&&a.splice(c,1)},b.escapeRegExp=function(a){return a.replace(/([.*+?^${}()|[\]\/\\])/g,"\\$1")},b.deferredCall=function(a){var b=null,c=function(){b=null,a()},d=function(a){return d.cancel(),b=setTimeout(c,a||0),d};return d.schedule=d,d.call=function(){return this.cancel(),a(),d},d.cancel=function(){return clearTimeout(b),b=null,d},d}}),define("ace/keyboard/textinput",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/lib/dom"],function(a,b,c){"use strict";var d=a("../lib/event"),e=a("../lib/useragent"),f=a("../lib/dom"),g=function(a,b){function l(){try{c.select()}catch(a){}}function m(a){if(!i){var d=a||c.value;if(d){d.charCodeAt(d.length-1)==g.charCodeAt(0)?(d=d.slice(0,-1),d&&b.onTextInput(d,j)):b.onTextInput(d,j);if(!v())return!1}}i=!1,j=!1,c.value=g,l()}function v(){return document.activeElement===c}var c=f.createElement("textarea");e.isTouchPad&&c.setAttribute("x-palm-disable-auto-cap",!0),c.style.left="-10000px",c.style.position="fixed",a.insertBefore(c,a.firstChild);var g=String.fromCharCode(0);m();var h=!1,i=!1,j=!1,k="",n=function(a){setTimeout(function(){h||m(a.data)},0)},o=function(a){if(e.isOldIE&&c.value.charCodeAt(0)>128)return;setTimeout(function(){h||m()},0)},p=function(a){h=!0,b.onCompositionStart(),e.isGecko||setTimeout(q,0)},q=function(){if(!h)return;b.onCompositionUpdate(c.value)},r=function(a){h=!1,b.onCompositionEnd()},s=function(a){i=!0;var d=b.getCopyText();d?c.value=d:a.preventDefault(),l(),setTimeout(function(){m()},0)},t=function(a){i=!0;var d=b.getCopyText();d?(c.value=d,b.onCut()):a.preventDefault(),l(),setTimeout(function(){m()},0)};d.addCommandKeyListener(c,b.onCommandKey.bind(b));if(e.isOldIE){var u={13:1,27:1};d.addListener(c,"keyup",function(a){h&&(!c.value||u[a.keyCode])&&setTimeout(r,0);if((c.value.charCodeAt(0)|0)<129)return;h?q():p()})}"onpropertychange"in c&&!("oninput"in c)?d.addListener(c,"propertychange",o):d.addListener(c,"input",n),d.addListener(c,"paste",function(a){j=!0,a.clipboardData&&a.clipboardData.getData?(m(a.clipboardData.getData("text/plain")),a.preventDefault()):o()}),"onbeforecopy"in c&&typeof clipboardData!="undefined"?(d.addListener(c,"beforecopy",function(a){var c=b.getCopyText();c?clipboardData.setData("Text",c):a.preventDefault()}),d.addListener(a,"keydown",function(a){if(a.ctrlKey&&a.keyCode==88){var c=b.getCopyText();c&&(clipboardData.setData("Text",c),b.onCut()),d.preventDefault(a)}})):(d.addListener(c,"copy",s),d.addListener(c,"cut",t)),d.addListener(c,"compositionstart",p),e.isGecko&&d.addListener(c,"text",q),e.isWebKit&&d.addListener(c,"keyup",q),d.addListener(c,"compositionend",r),d.addListener(c,"blur",function(){b.onBlur()}),d.addListener(c,"focus",function(){b.onFocus(),l()}),this.focus=function(){b.onFocus(),l(),c.focus()},this.blur=function(){c.blur()},this.isFocused=v,this.getElement=function(){return c},this.onContextMenu=function(a,b){a&&(k||(k=c.style.cssText),c.style.cssText="position:fixed; z-index:1000;left:"+(a.x-2)+"px; top:"+(a.y-2)+"px;"),b&&(c.value="")},this.onContextMenuClose=function(){setTimeout(function(){k&&(c.style.cssText=k,k=""),m()},0)}};b.TextInput=g}),define("ace/mouse/mouse_handler",["require","exports","module","ace/lib/event","ace/mouse/default_handlers","ace/mouse/default_gutter_handler","ace/mouse/mouse_event"],function(a,b,c){"use strict";var d=a("../lib/event"),e=a("./default_handlers").DefaultHandlers,f=a("./default_gutter_handler").GutterHandler,g=a("./mouse_event").MouseEvent,h=function(a){this.editor=a,new e(a),new f(a),d.addListener(a.container,"mousedown",function(b){return a.focus(),d.preventDefault(b)}),d.addListener(a.container,"selectstart",function(a){return d.preventDefault(a)});var b=a.renderer.getMouseEventTarget();d.addListener(b,"mousedown",this.onMouseEvent.bind(this,"mousedown")),d.addListener(b,"click",this.onMouseEvent.bind(this,"click")),d.addListener(b,"mousemove",this.onMouseMove.bind(this,"mousemove")),d.addMultiMouseDownListener(b,0,2,500,this.onMouseEvent.bind(this,"dblclick")),d.addMultiMouseDownListener(b,0,3,600,this.onMouseEvent.bind(this,"tripleclick")),d.addMultiMouseDownListener(b,0,4,600,this.onMouseEvent.bind(this,"quadclick")),d.addMouseWheelListener(a.container,this.onMouseWheel.bind(this,"mousewheel"));var c=a.renderer.$gutter;d.addListener(c,"mousedown",this.onMouseEvent.bind(this,"guttermousedown")),d.addListener(c,"click",this.onMouseEvent.bind(this,"gutterclick")),d.addListener(c,"dblclick",this.onMouseEvent.bind(this,"gutterdblclick")),d.addListener(c,"mousemove",this.onMouseMove.bind(this,"gutter"))};(function(){this.$scrollSpeed=1,this.setScrollSpeed=function(a){this.$scrollSpeed=a},this.getScrollSpeed=function(){return this.$scrollSpeed},this.onMouseEvent=function(a,b){this.editor._emit(a,new g(b,this.editor))},this.$dragDelay=250,this.setDragDelay=function(a){this.$dragDelay=a},this.getDragDelay=function(){return this.$dragDelay},this.onMouseMove=function(a,b){var c=this.editor._eventRegistry&&this.editor._eventRegistry.mousemove;if(!c||!c.length)return;this.editor._emit(a,new g(b,this.editor))},this.onMouseWheel=function(a,b){var c=new g(b,this.editor);c.speed=this.$scrollSpeed*2,c.wheelX=b.wheelX,c.wheelY=b.wheelY,this.editor._emit(a,c)}}).call(h.prototype),b.MouseHandler=h}),define("ace/mouse/default_handlers",["require","exports","module","ace/lib/event","ace/lib/dom","ace/lib/browser_focus"],function(a,b,c){function k(a){this.editor=a,this.$clickSelection=null,this.browserFocus=new f,a.setDefaultHandler("mousedown",this.onMouseDown.bind(this)),a.setDefaultHandler("dblclick",this.onDoubleClick.bind(this)),a.setDefaultHandler("tripleclick",this.onTripleClick.bind(this)),a.setDefaultHandler("quadclick",this.onQuadClick.bind(this)),a.setDefaultHandler("mousewheel",this.onScroll.bind(this))}function l(a,b,c,d){return Math.sqrt(Math.pow(c-a,2)+Math.pow(d-b,2))}"use strict";var d=a("../lib/event"),e=a("../lib/dom"),f=a("../lib/browser_focus").BrowserFocus,g=0,h=1,i=2,j=5;(function(){this.onMouseDown=function(a){function C(b){a.getShiftKey()?m.selection.selectToPosition(b):n.$clickSelection||(m.moveCursorToPosition(b),m.selection.clearSelection()),q=h}var b=a.inSelection(),c=a.pageX,f=a.pageY,k=a.getDocumentPosition(),m=this.editor,n=this,o=m.getSelectionRange(),p=o.isEmpty(),q=g;if(b&&(!this.browserFocus.isFocused()||(new Date).getTime()-this.browserFocus.lastFocus<20||!m.isFocused())){m.focus();return}var r=a.getButton();if(r!==0){p&&m.moveCursorToPosition(k),r==2&&(m.textInput.onContextMenu({x:a.clientX,y:a.clientY},p),d.capture(m.container,function(){},m.textInput.onContextMenuClose));return}b||C(k);var s=c,t=f,u=(new Date).getTime(),v,w,x,y=function(a){s=d.getDocumentX(a),t=d.getDocumentY(a)},z=function(a){clearInterval(F),q==g?C(k):q==i&&A(a),n.$clickSelection=null,q=g},A=function(a){e.removeCssClass(m.container,"ace_dragging"),m.session.removeMarker(x),m.$mouseHandler.$clickSelection||v||(m.moveCursorToPosition(k),m.selection.clearSelection());if(!v)return;if(w.contains(v.row,v.column)){v=null;return}m.clearSelection();if(a&&(a.ctrlKey||a.altKey))var b=m.session,c=b.insert(v,b.getTextRange(w));else var c=m.moveText(w,v);if(!c){v=null;return}m.selection.setSelectionRange(c)},B=function(){if(q==g){var a=l(c,f,s,t),b=(new Date).getTime();if(a>j){q=h;var d=m.renderer.screenToTextCoordinates(s,t);C(d)}else if(b-u>m.getDragDelay()){q=i,w=m.getSelectionRange();var k=m.getSelectionStyle();x=m.session.addMarker(w,"ace_selection",k),m.clearSelection(),e.addCssClass(m.container,"ace_dragging")}}q==i?E():q==h&&D()},D=function(){var a,b=m.renderer.screenToTextCoordinates(s,t);n.$clickSelection?n.$clickSelection.contains(b.row,b.column)?m.selection.setSelectionRange(n.$clickSelection):(n.$clickSelection.compare(b.row,b.column)==-1?a=n.$clickSelection.end:a=n.$clickSelection.start,m.selection.setSelectionAnchor(a.row,a.column),m.selection.selectToPosition(b)):m.selection.selectToPosition(b),m.renderer.scrollCursorIntoView()},E=function(){v=m.renderer.screenToTextCoordinates(s,t),m.moveCursorToPosition(v)};d.capture(m.container,y,z);var F=setInterval(B,20);return a.preventDefault()},this.onDoubleClick=function(a){var b=a.getDocumentPosition(),c=this.editor;c.moveCursorToPosition(b),c.selection.selectWord(),this.$clickSelection=c.getSelectionRange()},this.onTripleClick=function(a){var b=a.getDocumentPosition(),c=this.editor;c.moveCursorToPosition(b),c.selection.selectLine(),this.$clickSelection=c.getSelectionRange()},this.onQuadClick=function(a){var b=this.editor;b.selectAll(),this.$clickSelection=b.getSelectionRange()},this.onScroll=function(a){var b=this.editor;b.renderer.scrollBy(a.wheelX*a.speed,a.wheelY*a.speed);if(b.renderer.isScrollableBy(a.wheelX*a.speed,a.wheelY*a.speed))return a.preventDefault()}}).call(k.prototype),b.DefaultHandlers=k}),define("ace/lib/browser_focus",["require","exports","module","ace/lib/oop","ace/lib/event","ace/lib/event_emitter"],function(a,b,c){"use strict";var d=a("./oop"),e=a("./event"),f=a("./event_emitter").EventEmitter,g=function(a){a=a||window,this.lastFocus=(new Date).getTime(),this._isFocused=!0;var b=this;"onfocusin"in a.document?(e.addListener(a.document,"focusin",function(a){b._setFocused(!0)}),e.addListener(a.document,"focusout",function(a){b._setFocused(!!a.toElement)})):(e.addListener(a,"blur",function(a){b._setFocused(!1)}),e.addListener(a,"focus",function(a){b._setFocused(!0)}))};(function(){d.implement(this,f),this.isFocused=function(){return this._isFocused},this._setFocused=function(a){if(this._isFocused==a)return;a&&(this.lastFocus=(new Date).getTime()),this._isFocused=a,this._emit("changeFocus")}}).call(g.prototype),b.BrowserFocus=g}),define("ace/lib/event_emitter",["require","exports","module"],function(a,b,c){"use strict";var d={};d._emit=d._dispatchEvent=function(a,b){this._eventRegistry=this._eventRegistry||{},this._defaultHandlers=this._defaultHandlers||{};var c=this._eventRegistry[a]||[],d=this._defaultHandlers[a];if(!c.length&&!d)return;b=b||{},b.type=a,b.stopPropagation||(b.stopPropagation=function(){this.propagationStopped=!0}),b.preventDefault||(b.preventDefault=function(){this.defaultPrevented=!0});for(var e=0;e<c.length;e++){c[e](b);if(b.propagationStopped)break}d&&!b.defaultPrevented&&d(b)},d.setDefaultHandler=function(a,b){this._defaultHandlers=this._defaultHandlers||{};if(this._defaultHandlers[a])throw new Error("The default handler for '"+a+"' is already set");this._defaultHandlers[a]=b},d.on=d.addEventListener=function(a,b){this._eventRegistry=this._eventRegistry||{};var c=this._eventRegistry[a];if(!c)var c=this._eventRegistry[a]=[];c.indexOf(b)==-1&&c.push(b)},d.removeListener=d.removeEventListener=function(a,b){this._eventRegistry=this._eventRegistry||{};var c=this._eventRegistry[a];if(!c)return;var d=c.indexOf(b);d!==-1&&c.splice(d,1)},d.removeAllListeners=function(a){this._eventRegistry&&(this._eventRegistry[a]=[])},b.EventEmitter=d}),define("ace/mouse/default_gutter_handler",["require","exports","module"],function(a,b,c){function d(a){a.setDefaultHandler("gutterclick",function(b){var c=b.getDocumentPosition().row,d=a.session.selection;d.moveCursorTo(c,0),d.selectLine()})}"use strict",b.GutterHandler=d}),define("ace/mouse/mouse_event",["require","exports","module","ace/lib/event"],function(a,b,c){"use strict";var d=a("../lib/event"),e=b.MouseEvent=function(a,b){this.domEvent=a,this.editor=b,this.pageX=d.getDocumentX(a),this.pageY=d.getDocumentY(a),this.clientX=a.clientX,this.clientY=a.clientY,this.$pos=null,this.$inSelection=null,this.propagationStopped=!1,this.defaultPrevented=!1};(function(){this.stopPropagation=function(){d.stopPropagation(this.domEvent),this.propagationStopped=!0},this.preventDefault=function(){d.preventDefault(this.domEvent),this.defaultPrevented=!0},this.stop=function(){this.stopPropagation(),this.preventDefault()},this.getDocumentPosition=function(){if(this.$pos)return this.$pos;var a=d.getDocumentX(this.domEvent),b=d.getDocumentY(this.domEvent);return this.$pos=this.editor.renderer.screenToTextCoordinates(a,b),this.$pos},this.inSelection=function(){if(this.$inSelection!==null)return this.$inSelection;var a=this.editor;if(a.getReadOnly())this.$inSelection=!1;else{var b=a.getSelectionRange();if(b.isEmpty())this.$inSelection=!1;else{var c=this.getDocumentPosition();this.$inSelection=b.contains(c.row,c.column)}}return this.$inSelection},this.getButton=function(){return d.getButton(this.domEvent)},this.getShiftKey=function(){return this.domEvent.shiftKey},this.getAccelKey=function(){return this.domEvent.ctrlKey||this.domEvent.metaKey}}).call(e.prototype)}),define("ace/mouse/fold_handler",["require","exports","module"],function(a,b,c){function d(a){a.on("click",function(b){var c=b.getDocumentPosition(),d=a.session,e=d.getFoldAt(c.row,c.column,1);e&&(b.getAccelKey()?d.removeFold(e):d.expandFold(e),b.stop())}),a.on("gutterclick",function(b){if(b.domEvent.target.className.indexOf("ace_fold-widget")!=-1){var c=b.getDocumentPosition().row;a.session.onFoldWidgetClick(c,b.domEvent),b.stop()}})}"use strict",b.FoldHandler=d}),define("ace/keyboard/keybinding",["require","exports","module","ace/lib/keys","ace/lib/event","ace/commands/default_commands"],function(a,b,c){"use strict";var d=a("../lib/keys"),e=a("../lib/event");a("../commands/default_commands");var f=function(a){this.$editor=a,this.$data={},this.$handlers=[this]};(function(){this.setKeyboardHandler=function(a){if(this.$handlers[this.$handlers.length-1]==a)return;this.$data={},this.$handlers=a?[this,a]:[this]},this.addKeyboardHandler=function(a){this.removeKeyboardHandler(a),this.$handlers.push(a)},this.removeKeyboardHandler=function(a){var b=this.$handlers.indexOf(a);return b==-1?!1:(this.$handlers.splice(b,1),!0)},this.getKeyboardHandler=function(){return this.$handlers[this.$handlers.length-1]},this.$callKeyboardHandlers=function(a,b,c,d){var f;for(var g=this.$handlers.length;g--;){f=this.$handlers[g].handleKeyboard(this.$data,a,b,c,d);if(f&&f.command)break}if(!f||!f.command)return!1;var h=!1,i=this.$editor.commands;return f.command!="null"?h=i.exec(f.command,this.$editor,f.args):h=!0,h&&d&&e.stopEvent(d),h},this.handleKeyboard=function(a,b,c){return{command:this.$editor.commands.findKeyCommand(b,c)}},this.onCommandKey=function(a,b,c){var e=d.keyCodeToString(c);this.$callKeyboardHandlers(b,e,c,a)},this.onTextInput=function(a,b){var c=!1;!b&&a.length==1&&(c=this.$callKeyboardHandlers(0,a)),c||this.$editor.commands.exec("insertstring",this.$editor,a)}}).call(f.prototype),b.KeyBinding=f}),define("ace/commands/default_commands",["require","exports","module","ace/lib/lang"],function(a,b,c){function e(a,b){return{win:a,mac:b}}"use strict";var d=a("../lib/lang");b.commands=[{name:"selectall",bindKey:e("Ctrl-A","Command-A"),exec:function(a){a.selectAll()},readOnly:!0},{name:"centerselection",bindKey:e(null,"Ctrl-L"),exec:function(a){a.centerSelection()},readOnly:!0},{name:"gotoline",bindKey:e("Ctrl-L","Command-L"),exec:function(a){var b=parseInt(prompt("Enter line number:"),10);isNaN(b)||a.gotoLine(b)},readOnly:!0},{name:"fold",bindKey:e("Alt-L","Alt-L"),exec:function(a){a.session.toggleFold(!1)},readOnly:!0},{name:"unfold",bindKey:e("Alt-Shift-L","Alt-Shift-L"),exec:function(a){a.session.toggleFold(!0)},readOnly:!0},{name:"foldall",bindKey:e("Alt-0","Alt-0"),exec:function(a){a.session.foldAll()},readOnly:!0},{name:"unfoldall",bindKey:e("Alt-Shift-0","Alt-Shift-0"),exec:function(a){a.session.unfold()},readOnly:!0},{name:"findnext",bindKey:e("Ctrl-K","Command-G"),exec:function(a){a.findNext()},readOnly:!0},{name:"findprevious",bindKey:e("Ctrl-Shift-K","Command-Shift-G"),exec:function(a){a.findPrevious()},readOnly:!0},{name:"find",bindKey:e("Ctrl-F","Command-F"),exec:function(a){var b=prompt("Find:",a.getCopyText());a.find(b)},readOnly:!0},{name:"overwrite",bindKey:e("Insert","Insert"),exec:function(a){a.toggleOverwrite()},readOnly:!0},{name:"selecttostart",bindKey:e("Ctrl-Shift-Home|Alt-Shift-Up","Command-Shift-Up"),exec:function(a){a.getSelection().selectFileStart()},readOnly:!0},{name:"gotostart",bindKey:e("Ctrl-Home|Ctrl-Up","Command-Home|Command-Up"),exec:function(a){a.navigateFileStart()},readOnly:!0},{name:"selectup",bindKey:e("Shift-Up","Shift-Up"),exec:function(a){a.getSelection().selectUp()},multiSelectAction:"forEach",readOnly:!0},{name:"golineup",bindKey:e("Up","Up|Ctrl-P"),exec:function(a,b){a.navigateUp(b.times)},multiSelectAction:"forEach",readOnly:!0},{name:"selecttoend",bindKey:e("Ctrl-Shift-End|Alt-Shift-Down","Command-Shift-Down"),exec:function(a){a.getSelection().selectFileEnd()},multiSelectAction:"forEach",readOnly:!0},{name:"gotoend",bindKey:e("Ctrl-End|Ctrl-Down","Command-End|Command-Down"),exec:function(a){a.navigateFileEnd()},multiSelectAction:"forEach",readOnly:!0},{name:"selectdown",bindKey:e("Shift-Down","Shift-Down"),exec:function(a){a.getSelection().selectDown()},multiSelectAction:"forEach",readOnly:!0},{name:"golinedown",bindKey:e("Down","Down|Ctrl-N"),exec:function(a,b){a.navigateDown(b.times)},multiSelectAction:"forEach",readOnly:!0},{name:"selectwordleft",bindKey:e("Ctrl-Shift-Left","Option-Shift-Left"),exec:function(a){a.getSelection().selectWordLeft()},multiSelectAction:"forEach",readOnly:!0},{name:"gotowordleft",bindKey:e("Ctrl-Left","Option-Left"),exec:function(a){a.navigateWordLeft()},multiSelectAction:"forEach",readOnly:!0},{name:"selecttolinestart",bindKey:e("Alt-Shift-Left","Command-Shift-Left"),exec:function(a){a.getSelection().selectLineStart()},multiSelectAction:"forEach",readOnly:!0},{name:"gotolinestart",bindKey:e("Alt-Left|Home","Command-Left|Home|Ctrl-A"),exec:function(a){a.navigateLineStart()},multiSelectAction:"forEach",readOnly:!0},{name:"selectleft",bindKey:e("Shift-Left","Shift-Left"),exec:function(a){a.getSelection().selectLeft()},multiSelectAction:"forEach",readOnly:!0},{name:"gotoleft",bindKey:e("Left","Left|Ctrl-B"),exec:function(a,b){a.navigateLeft(b.times)},multiSelectAction:"forEach",readOnly:!0},{name:"selectwordright",bindKey:e("Ctrl-Shift-Right","Option-Shift-Right"),exec:function(a){a.getSelection().selectWordRight()},multiSelectAction:"forEach",readOnly:!0},{name:"gotowordright",bindKey:e("Ctrl-Right","Option-Right"),exec:function(a){a.navigateWordRight()},multiSelectAction:"forEach",readOnly:!0},{name:"selecttolineend",bindKey:e("Alt-Shift-Right","Command-Shift-Right"),exec:function(a){a.getSelection().selectLineEnd()},multiSelectAction:"forEach",readOnly:!0},{name:"gotolineend",bindKey:e("Alt-Right|End","Command-Right|End|Ctrl-E"),exec:function(a){a.navigateLineEnd()},multiSelectAction:"forEach",readOnly:!0},{name:"selectright",bindKey:e("Shift-Right","Shift-Right"),exec:function(a){a.getSelection().selectRight()},multiSelectAction:"forEach",readOnly:!0},{name:"gotoright",bindKey:e("Right","Right|Ctrl-F"),exec:function(a,b){a.navigateRight(b.times)},multiSelectAction:"forEach",readOnly:!0},{name:"selectpagedown",bindKey:e("Shift-PageDown","Shift-PageDown"),exec:function(a){a.selectPageDown()},readOnly:!0},{name:"pagedown",bindKey:e(null,"PageDown"),exec:function(a){a.scrollPageDown()},readOnly:!0},{name:"gotopagedown",bindKey:e("PageDown","Option-PageDown|Ctrl-V"),exec:function(a){a.gotoPageDown()},readOnly:!0},{name:"selectpageup",bindKey:e("Shift-PageUp","Shift-PageUp"),exec:function(a){a.selectPageUp()},readOnly:!0},{name:"pageup",bindKey:e(null,"PageUp"),exec:function(a){a.scrollPageUp()},readOnly:!0},{name:"gotopageup",bindKey:e("PageUp","Option-PageUp"),exec:function(a){a.gotoPageUp()},readOnly:!0},{name:"selectlinestart",bindKey:e("Shift-Home","Shift-Home"),exec:function(a){a.getSelection().selectLineStart()},multiSelectAction:"forEach",readOnly:!0},{name:"selectlineend",bindKey:e("Shift-End","Shift-End"),exec:function(a){a.getSelection().selectLineEnd()},multiSelectAction:"forEach",readOnly:!0},{name:"togglerecording",bindKey:e("Ctrl-Alt-E","Command-Option-E"),exec:function(a){a.commands.toggleRecording()},readOnly:!0},{name:"replaymacro",bindKey:e("Ctrl-Shift-E","Command-Shift-E"),exec:function(a){a.commands.replay(a)},readOnly:!0},{name:"jumptomatching",bindKey:e("Ctrl-Shift-P","Ctrl-Shift-P"),exec:function(a){a.jumpToMatching()},multiSelectAction:"forEach",readOnly:!0},{name:"cut",exec:function(a){var b=a.getSelectionRange();a._emit("cut",b),a.selection.isEmpty()||(a.session.remove(b),a.clearSelection())},multiSelectAction:"forEach"},{name:"removeline",bindKey:e("Ctrl-D","Command-D"),exec:function(a){a.removeLines()},multiSelectAction:"forEach"},{name:"togglecomment",bindKey:e("Ctrl-7","Command-7"),exec:function(a){a.toggleCommentLines()},multiSelectAction:"forEach"},{name:"replace",bindKey:e("Ctrl-R","Command-Option-F"),exec:function(a){var b=prompt("Find:",a.getCopyText());if(!b)return;var c=prompt("Replacement:");if(!c)return;a.replace(c,{needle:b})}},{name:"replaceall",bindKey:e("Ctrl-Shift-R","Command-Shift-Option-F"),exec:function(a){var b=prompt("Find:");if(!b)return;var c=prompt("Replacement:");if(!c)return;a.replaceAll(c,{needle:b})}},{name:"undo",bindKey:e("Ctrl-Z","Command-Z"),exec:function(a){a.undo()}},{name:"redo",bindKey:e("Ctrl-Shift-Z|Ctrl-Y","Command-Shift-Z|Command-Y"),exec:function(a){a.redo()}},{name:"copylinesup",bindKey:e("Ctrl-Alt-Up","Command-Option-Up"),exec:function(a){a.copyLinesUp()}},{name:"movelinesup",bindKey:e("Alt-Up","Option-Up"),exec:function(a){a.moveLinesUp()}},{name:"copylinesdown",bindKey:e("Ctrl-Alt-Down","Command-Option-Down"),exec:function(a){a.copyLinesDown()}},{name:"movelinesdown",bindKey:e("Alt-Down","Option-Down"),exec:function(a){a.moveLinesDown()}},{name:"del",bindKey:e("Delete","Delete|Ctrl-D"),exec:function(a){a.remove("right")},multiSelectAction:"forEach"},{name:"backspace",bindKey:e("Command-Backspace|Option-Backspace|Shift-Backspace|Backspace","Ctrl-Backspace|Command-Backspace|Shift-Backspace|Backspace|Ctrl-H"),exec:function(a){a.remove("left")},multiSelectAction:"forEach"},{name:"removetolinestart",bindKey:e("Alt-Backspace","Command-Backspace"),exec:function(a){a.removeToLineStart()},multiSelectAction:"forEach"},{name:"removetolineend",bindKey:e("Alt-Delete","Ctrl-K"),exec:function(a){a.removeToLineEnd()},multiSelectAction:"forEach"},{name:"removewordleft",bindKey:e("Ctrl-Backspace","Alt-Backspace|Ctrl-Alt-Backspace"),exec:function(a){a.removeWordLeft()},multiSelectAction:"forEach"},{name:"removewordright",bindKey:e("Ctrl-Delete","Alt-Delete"),exec:function(a){a.removeWordRight()},multiSelectAction:"forEach"},{name:"outdent",bindKey:e("Shift-Tab","Shift-Tab"),exec:function(a){a.blockOutdent()},multiSelectAction:"forEach"},{name:"indent",bindKey:e("Tab","Tab"),exec:function(a){a.indent()},multiSelectAction:"forEach"},{name:"insertstring",exec:function(a,b){a.insert(b)},multiSelectAction:"forEach"},{name:"inserttext",exec:function(a,b){a.insert(d.stringRepeat(b.text||"",b.times||1))},multiSelectAction:"forEach"},{name:"splitline",bindKey:e(null,"Ctrl-O"),exec:function(a){a.splitLine()},multiSelectAction:"forEach"},{name:"transposeletters",bindKey:e("Ctrl-T","Ctrl-T"),exec:function(a){a.transposeLetters()},multiSelectAction:function(a){a.transposeSelections(1)}},{name:"touppercase",bindKey:e("Ctrl-U","Ctrl-U"),exec:function(a){a.toUpperCase()},multiSelectAction:"forEach"},{name:"tolowercase",bindKey:e("Ctrl-Shift-U","Ctrl-Shift-U"),exec:function(a){a.toLowerCase()},multiSelectAction:"forEach"}]}),define("ace/edit_session",["require","exports","module","ace/config","ace/lib/oop","ace/lib/lang","ace/lib/net","ace/lib/event_emitter","ace/selection","ace/mode/text","ace/range","ace/document","ace/background_tokenizer","ace/edit_session/folding","ace/edit_session/bracket_match"],function(a,b,c){"use strict";var d=a("./config"),e=a("./lib/oop"),f=a("./lib/lang"),g=a("./lib/net"),h=a("./lib/event_emitter").EventEmitter,i=a("./selection").Selection,j=a("./mode/text").Mode,k=a("./range").Range,l=a("./document").Document,m=a("./background_tokenizer").BackgroundTokenizer,n=function(a,b){this.$modified=!0,this.$breakpoints=[],this.$frontMarkers={},this.$backMarkers={},this.$markerId=1,this.$rowCache=[],this.$wrapData=[],this.$foldData=[],this.$undoSelect=!0,this.$foldData.toString=function(){var a="";return this.forEach(function(b){a+="\n"+b.toString()}),a},a instanceof l?this.setDocument(a):this.setDocument(new l(a)),this.selection=new i(this),b?this.setMode(b):this.setMode(new j)};(function(){function q(a){return a<4352?!1:a>=4352&&a<=4447||a>=4515&&a<=4519||a>=4602&&a<=4607||a>=9001&&a<=9002||a>=11904&&a<=11929||a>=11931&&a<=12019||a>=12032&&a<=12245||a>=12272&&a<=12283||a>=12288&&a<=12350||a>=12353&&a<=12438||a>=12441&&a<=12543||a>=12549&&a<=12589||a>=12593&&a<=12686||a>=12688&&a<=12730||a>=12736&&a<=12771||a>=12784&&a<=12830||a>=12832&&a<=12871||a>=12880&&a<=13054||a>=13056&&a<=19903||a>=19968&&a<=42124||a>=42128&&a<=42182||a>=43360&&a<=43388||a>=44032&&a<=55203||a>=55216&&a<=55238||a>=55243&&a<=55291||a>=63744&&a<=64255||a>=65040&&a<=65049||a>=65072&&a<=65106||a>=65108&&a<=65126||a>=65128&&a<=65131||a>=65281&&a<=65376||a>=65504&&a<=65510}e.implement(this,h),this.setDocument=function(a){if(this.doc)throw new Error("Document is already set");this.doc=a,a.on("change",this.onChange.bind(this)),this.on("changeFold",this.onChangeFold.bind(this)),this.bgTokenizer&&(this.bgTokenizer.setDocument(this.getDocument()),this.bgTokenizer.start(0))},this.getDocument=function(){return this.doc},this.$resetRowCache=function(a){if(a==0){this.$rowCache=[];return}var b=this.$rowCache;for(var c=0;c<b.length;c++)if(b[c].docRow>=a){b.splice(c,b.length);return}},this.onChangeFold=function(a){var b=a.data;this.$resetRowCache(b.start.row)},this.onChange=function(a){var b=a.data;this.$modified=!0,this.$resetRowCache(b.range.start.row);var c=this.$updateInternalDataOnChange(a);!this.$fromUndo&&this.$undoManager&&!b.ignore&&(this.$deltasDoc.push(b),c&&c.length!=0&&this.$deltasFold.push({action:"removeFolds",folds:c}),this.$informUndoManager.schedule()),this.bgTokenizer.start(b.range.start.row),this._emit("change",a)},this.setValue=function(a){this.doc.setValue(a),this.selection.moveCursorTo(0,0),this.selection.clearSelection(),this.$resetRowCache(0),this.$deltas=[],this.$deltasDoc=[],this.$deltasFold=[],this.getUndoManager().reset()},this.getValue=this.toString=function(){return this.doc.getValue()},this.getSelection=function(){return this.selection},this.getState=function(a){return this.bgTokenizer.getState(a)},this.getTokens=function(a,b){return this.bgTokenizer.getTokens(a,b)},this.getTokenAt=function(a,b){var c=this.bgTokenizer.getTokens(a,a)[0].tokens,d,e=0;if(b==null)f=c.length-1,e=this.getLine(a).length;else for(var f=0;f<c.length;f++){e+=c[f].value.length;if(e>=b)break}return d=c[f],d?(d.index=f,d.start=e-d.value.length,d):null},this.setUndoManager=function(a){this.$undoManager=a,this.$resetRowCache(0),this.$deltas=[],this.$deltasDoc=[],this.$deltasFold=[],this.$informUndoManager&&this.$informUndoManager.cancel();if(a){var b=this;this.$syncInformUndoManager=function(){b.$informUndoManager.cancel(),b.$deltasFold.length&&(b.$deltas.push({group:"fold",deltas:b.$deltasFold}),b.$deltasFold=[]),b.$deltasDoc.length&&(b.$deltas.push({group:"doc",deltas:b.$deltasDoc}),b.$deltasDoc=[]),b.$deltas.length>0&&a.execute({action:"aceupdate",args:[b.$deltas,b]}),b.$deltas=[]},this.$informUndoManager=f.deferredCall(this.$syncInformUndoManager)}},this.$defaultUndoManager={undo:function(){},redo:function(){},reset:function(){}},this.getUndoManager=function(){return this.$undoManager||this.$defaultUndoManager},this.getTabString=function(){return this.getUseSoftTabs()?f.stringRepeat(" ",this.getTabSize()):" "},this.$useSoftTabs=!0,this.setUseSoftTabs=function(a){if(this.$useSoftTabs===a)return;this.$useSoftTabs=a},this.getUseSoftTabs=function(){return this.$useSoftTabs},this.$tabSize=4,this.setTabSize=function(a){if(isNaN(a)||this.$tabSize===a)return;this.$modified=!0,this.$tabSize=a,this._emit("changeTabSize")},this.getTabSize=function(){return this.$tabSize},this.isTabStop=function(a){return this.$useSoftTabs&&a.column%this.$tabSize==0},this.$overwrite=!1,this.setOverwrite=function(a){if(this.$overwrite==a)return;this.$overwrite=a,this._emit("changeOverwrite")},this.getOverwrite=function(){return this.$overwrite},this.toggleOverwrite=function(){this.setOverwrite(!this.$overwrite)},this.getBreakpoints=function(){return this.$breakpoints},this.setBreakpoints=function(a){this.$breakpoints=[];for(var b=0;b<a.length;b++)this.$breakpoints[a[b]]=!0;this._emit("changeBreakpoint",{})},this.clearBreakpoints=function(){this.$breakpoints=[],this._emit("changeBreakpoint",{})},this.setBreakpoint=function(a){this.$breakpoints[a]=!0,this._emit("changeBreakpoint",{})},this.clearBreakpoint=function(a){delete this.$breakpoints[a],this._emit("changeBreakpoint",{})},this.getBreakpoints=function(){return this.$breakpoints},this.addMarker=function(a,b,c,d){var e=this.$markerId++,f={range:a,type:c||"line",renderer:typeof c=="function"?c:null,clazz:b,inFront:!!d};return d?(this.$frontMarkers[e]=f,this._emit("changeFrontMarker")):(this.$backMarkers[e]=f,this._emit("changeBackMarker")),e},this.removeMarker=function(a){var b=this.$frontMarkers[a]||this.$backMarkers[a];if(!b)return;var c=b.inFront?this.$frontMarkers:this.$backMarkers;b&&(delete c[a],this._emit(b.inFront?"changeFrontMarker":"changeBackMarker"))},this.getMarkers=function(a){return a?this.$frontMarkers:this.$backMarkers},this.setAnnotations=function(a){this.$annotations={};for(var b=0;b<a.length;b++){var c=a[b],d=c.row;this.$annotations[d]?this.$annotations[d].push(c):this.$annotations[d]=[c]}this._emit("changeAnnotation",{})},this.getAnnotations=function(){return this.$annotations||{}},this.clearAnnotations=function(){this.$annotations={},this._emit("changeAnnotation",{})},this.$detectNewLine=function(a){var b=a.match(/^.*?(\r?\n)/m);b?this.$autoNewLine=b[1]:this.$autoNewLine="\n"},this.getWordRange=function(a,b){var c=this.getLine(a),d=!1;b>0&&(d=!!c.charAt(b-1).match(this.tokenRe)),d||(d=!!c.charAt(b).match(this.tokenRe));var e=d?this.tokenRe:this.nonTokenRe,f=b;if(f>0){do f--;while(f>=0&&c.charAt(f).match(e));f++}var g=b;while(g<c.length&&c.charAt(g).match(e))g++;return new k(a,f,a,g)},this.getAWordRange=function(a,b){var c=this.getWordRange(a,b),d=this.getLine(c.end.row);while(d.charAt(c.end.column).match(/[ \t]/))c.end.column+=1;return c},this.setNewLineMode=function(a){this.doc.setNewLineMode(a)},this.getNewLineMode=function(){return this.doc.getNewLineMode()},this.$useWorker=!0,this.setUseWorker=function(a){if(this.$useWorker==a)return;this.$useWorker=a,this.$stopWorker(),a&&this.$startWorker()},this.getUseWorker=function(){return this.$useWorker},this.onReloadTokenizer=function(a){var b=a.data;this.bgTokenizer.start(b.first),this._emit("tokenizerUpdate",a)},this.$modes={},this._loadMode=function(b,c){function i(a){if(e.$modes[b])return c(e.$modes[b]);e.$modes[b]=new a.Mode,e._emit("loadmode",{name:b,mode:e.$modes[b]}),c(e.$modes[b])}function j(a){if(!d.get("packaged"))return a();var c=b.split("/").pop(),e=d.get("modePath")+"/mode-"+c+d.get("suffix");g.loadScript(e,a)}if(this.$modes[b])return c(this.$modes[b]);var e=this,f;try{f=a(b)}catch(h){}if(f)return i(f);j(function(){a([b],i)})},this.$mode=null,this.$origMode=null,this.setMode=function(a){this.$origMode=a;if(typeof a=="string"){var b=this;this._loadMode(a,function(c){if(b.$origMode!==a)return;b.setMode(c)});return}if(this.$mode===a)return;this.$mode=a,this.$stopWorker(),this.$useWorker&&this.$startWorker();var c=a.getTokenizer();if(c.addEventListener!==undefined){var d=this.onReloadTokenizer.bind(this);c.addEventListener("update",d)}if(!this.bgTokenizer){this.bgTokenizer=new m(c);var b=this;this.bgTokenizer.addEventListener("update",function(a){b._emit("tokenizerUpdate",a)})}else this.bgTokenizer.setTokenizer(c);this.bgTokenizer.setDocument(this.getDocument()),this.bgTokenizer.start(0),this.tokenRe=a.tokenRe,this.nonTokenRe=a.nonTokenRe,this.$setFolding(a.foldingRules),this._emit("changeMode")},this.$stopWorker=function(){this.$worker&&this.$worker.terminate(),this.$worker=null},this.$startWorker=function(){if(typeof Worker!="undefined"&&!a.noWorker)try{this.$worker=this.$mode.createWorker(this)}catch(b){console.log("Could not load worker"),console.log(b),this.$worker=null}else this.$worker=null},this.getMode=function(){return this.$mode},this.$scrollTop=0,this.setScrollTop=function(a){a=Math.round(Math.max(0,a));if(this.$scrollTop===a)return;this.$scrollTop=a,this._emit("changeScrollTop",a)},this.getScrollTop=function(){return this.$scrollTop},this.$scrollLeft=0,this.setScrollLeft=function(a){a=Math.round(Math.max(0,a));if(this.$scrollLeft===a)return;this.$scrollLeft=a,this._emit("changeScrollLeft",a)},this.getScrollLeft=function(){return this.$scrollLeft},this.getWidth=function(){return this.$computeWidth(),this.width},this.getScreenWidth=function(){return this.$computeWidth(),this.screenWidth},this.$computeWidth=function(a){if(this.$modified||a){this.$modified=!1;var b=this.doc.getAllLines(),c=0,d=0;for(var e=0;e<b.length;e++){var f=this.getFoldLine(e),g,h;g=b[e];if(f){var i=f.range.end;g=this.getFoldDisplayLine(f),e=i.row}h=g.length,c=Math.max(c,h),this.$useWrapMode||(d=Math.max(d,this.$getStringScreenWidth(g)[0]))}this.width=c,this.$useWrapMode?this.screenWidth=this.$wrapLimit:this.screenWidth=d}},this.getLine=function(a){return this.doc.getLine(a)},this.getLines=function(a,b){return this.doc.getLines(a,b)},this.getLength=function(){return this.doc.getLength()},this.getTextRange=function(a){return this.doc.getTextRange(a)},this.insert=function(a,b){return this.doc.insert(a,b)},this.remove=function(a){return this.doc.remove(a)},this.undoChanges=function(a,b){if(!a.length)return;this.$fromUndo=!0;var c=null;for(var d=a.length-1;d!=-1;d--){var e=a[d];e.group=="doc"?(this.doc.revertDeltas(e.deltas),c=this.$getUndoSelection(e.deltas,!0,c)):e.deltas.forEach(function(a){this.addFolds(a.folds)},this)}return this.$fromUndo=!1,c&&this.$undoSelect&&!b&&this.selection.setSelectionRange(c),c},this.redoChanges=function(a,b){if(!a.length)return;this.$fromUndo=!0;var c=null;for(var d=0;d<a.length;d++){var e=a[d];e.group=="doc"&&(this.doc.applyDeltas(e.deltas),c=this.$getUndoSelection(e.deltas,!1,c))}return this.$fromUndo=!1,c&&this.$undoSelect&&!b&&this.selection.setSelectionRange(c),c},this.setUndoSelect=function(a){this.$undoSelect=a},this.$getUndoSelection=function(a,b,c){function d(a){var c=a.action=="insertText"||a.action=="insertLines";return b?!c:c}var e=a[0],f,g,h=!1;d(e)?(f=e.range.clone(),h=!0):(f=k.fromPoints(e.range.start,e.range.start),h=!1);for(var i=1;i<a.length;i++)e=a[i],d(e)?(g=e.range.start,f.compare(g.row,g.column)==-1&&f.setStart(e.range.start),g=e.range.end,f.compare(g.row,g.column)==1&&f.setEnd(e.range.end),h=!0):(g=e.range.start,f.compare(g.row,g.column)==-1&&(f=k.fromPoints(e.range.start,e.range.start)),h=!1);if(c!=null){var j=c.compareRange(f);j==1?f.setStart(c.start):j==-1&&f.setEnd(c.end)}return f},this.replace=function(a,b){return this.doc.replace(a,b)},this.moveText=function(a,b){var c=this.getTextRange(a);this.remove(a);var d=b.row,e=b.column;!a.isMultiLine()&&a.start.row==d&&a.end.column<e&&(e-=c.length);if(a.isMultiLine()&&a.end.row<d){var f=this.doc.$split(c);d-=f.length-1}var g=d+a.end.row-a.start.row,h=a.isMultiLine()?a.end.column:e+a.end.column-a.start.column,i=new k(d,e,g,h);return this.insert(i.start,c),i},this.indentRows=function(a,b,c){c=c.replace(/\t/g,this.getTabString());for(var d=a;d<=b;d++)this.insert({row:d,column:0},c)},this.outdentRows=function(a){var b=a.collapseRows(),c=new k(0,0,0,0),d=this.getTabSize();for(var e=b.start.row;e<=b.end.row;++e){var f=this.getLine(e);c.start.row=e,c.end.row=e;for(var g=0;g<d;++g)if(f.charAt(g)!=" ")break;g<d&&f.charAt(g)==" "?(c.start.column=g,c.end.column=g+1):(c.start.column=0,c.end.column=g),this.remove(c)}},this.moveLinesUp=function(a,b){if(a<=0)return 0;var c=this.doc.removeLines(a,b);return this.doc.insertLines(a-1,c),-1},this.moveLinesDown=function(a,b){if(b>=this.doc.getLength()-1)return 0;var c=this.doc.removeLines(a,b);return this.doc.insertLines(a+1,c),1},this.duplicateLines=function(a,b){var a=this.$clipRowToDocument(a),b=this.$clipRowToDocument(b),c=this.getLines(a,b);this.doc.insertLines(a,c);var d=b-a+1;return d},this.$clipRowToDocument=function(a){return Math.max(0,Math.min(a,this.doc.getLength()-1))},this.$clipColumnToRow=function(a,b){return b<0?0:Math.min(this.doc.getLine(a).length,b)},this.$clipPositionToDocument=function(a,b){b=Math.max(0,b);if(a<0)a=0,b=0;else{var c=this.doc.getLength();a>=c?(a=c-1,b=this.doc.getLine(c-1).length):b=Math.min(this.doc.getLine(a).length,b)}return{row:a,column:b}},this.$clipRangeToDocument=function(a){a.start.row<0?(a.start.row=0,a.start.column=0):a.start.column=this.$clipColumnToRow(a.start.row,a.start.column);var b=this.doc.getLength()-1;return a.end.row>b?(a.end.row=b,a.end.column=this.doc.getLine(b).length):a.end.column=this.$clipColumnToRow(a.end.row,a.end.column),a},this.$wrapLimit=80,this.$useWrapMode=!1,this.$wrapLimitRange={min:null,max:null},this.setUseWrapMode=function(a){if(a!=this.$useWrapMode){this.$useWrapMode=a,this.$modified=!0,this.$resetRowCache(0);if(a){var b=this.getLength();this.$wrapData=[];for(var c=0;c<b;c++)this.$wrapData.push([]);this.$updateWrapData(0,b-1)}this._emit("changeWrapMode")}},this.getUseWrapMode=function(){return this.$useWrapMode},this.setWrapLimitRange=function(a,b){if(this.$wrapLimitRange.min!==a||this.$wrapLimitRange.max!==b)this.$wrapLimitRange.min=a,this.$wrapLimitRange.max=b,this.$modified=!0,this._emit("changeWrapMode")},this.adjustWrapLimit=function(a){var b=this.$constrainWrapLimit(a);return b!=this.$wrapLimit&&b>0?(this.$wrapLimit=b,this.$modified=!0,this.$useWrapMode&&(this.$updateWrapData(0,this.getLength()-1),this.$resetRowCache(0),this._emit("changeWrapLimit")),!0):!1},this.$constrainWrapLimit=function(a){var b=this.$wrapLimitRange.min;b&&(a=Math.max(b,a));var c=this.$wrapLimitRange.max;return c&&(a=Math.min(c,a)),Math.max(1,a)},this.getWrapLimit=function(){return this.$wrapLimit},this.getWrapLimitRange=function(){return{min:this.$wrapLimitRange.min,max:this.$wrapLimitRange.max}},this.$updateInternalDataOnChange=function(a){var b=this.$useWrapMode,c,d=a.data.action,e=a.data.range.start.row,f=a.data.range.end.row,g=a.data.range.start,h=a.data.range.end,i=null;d.indexOf("Lines")!=-1?(d=="insertLines"?f=e+a.data.lines.length:f=e,c=a.data.lines?a.data.lines.length:f-e):c=f-e;if(c!=0)if(d.indexOf("remove")!=-1){b&&this.$wrapData.splice(e,c);var j=this.$foldData;i=this.getFoldsInRange(a.data.range),this.removeFolds(i);var k=this.getFoldLine(h.row),l=0;if(k){k.addRemoveChars(h.row,h.column,g.column-h.column),k.shiftRow(-c);var m=this.getFoldLine(e);m&&m!==k&&(m.merge(k),k=m),l=j.indexOf(k)+1}for(l;l<j.length;l++){var k=j[l];k.start.row>=h.row&&k.shiftRow(-c)}f=e}else{var n;if(b){n=[e,0];for(var o=0;o<c;o++)n.push([]);this.$wrapData.splice.apply(this.$wrapData,n)}var j=this.$foldData,k=this.getFoldLine(e),l=0;if(k){var p=k.range.compareInside(g.row,g.column);p==0?(k=k.split(g.row,g.column),k.shiftRow(c),k.addRemoveChars(f,0,h.column-g.column)):p==-1&&(k.addRemoveChars(e,0,h.column-g.column),k.shiftRow(c)),l=j.indexOf(k)+1}for(l;l<j.length;l++){var k=j[l];k.start.row>=e&&k.shiftRow(c)}}else{c=Math.abs(a.data.range.start.column-a.data.range.end.column),d.indexOf("remove")!=-1&&(i=this.getFoldsInRange(a.data.range),this.removeFolds(i),c=-c);var k=this.getFoldLine(e);k&&k.addRemoveChars(e,g.column,c)}return b&&this.$wrapData.length!=this.doc.getLength()&&console.error("doc.getLength() and $wrapData.length have to be the same!"),b&&this.$updateWrapData(e,f),i},this.$updateWrapData=function(a,b){var c=this.doc.getAllLines(),d=this.getTabSize(),e=this.$wrapData,g=this.$wrapLimit,h,k,l=a;b=Math.min(b,c.length-1);while(l<=b){k=this.getFoldLine(l,k);if(!k)h=this.$getDisplayTokens(f.stringTrimRight(c[l])),e[l]=this.$computeWrapSplits(h,g,d),l++;else{h=[],k.walk(function(a,b,d,e){var f;if(a){f=this.$getDisplayTokens(a,h.length),f[0]=i;for(var g=1;g<f.length;g++)f[g]=j}else f=this.$getDisplayTokens(c[b].substring(e,d),h.length);h=h.concat(f)}.bind(this),k.end.row,c[k.end.row].length+1);while(h.length!=0&&h[h.length-1]>=n)h.pop();e[k.start.row]=this.$computeWrapSplits(h,g,d),l=k.end.row+1}}};var b=1,c=2,i=3,j=4,l=9,n=10,o=11,p=12;this.$computeWrapSplits=function(a,b){function g(b){var d=a.slice(e,b),g=d.length;d.join("").replace(/12/g,function(){g-=1}).replace(/2/g,function(){g-=1}),f+=g,c.push(f),e=b}if(a.length==0)return[];var c=[],d=a.length,e=0,f=0;while(d-e>b){var h=e+b;if(a[h]>=n){while(a[h]>=n)h++;g(h);continue}if(a[h]==i||a[h]==j){for(h;h!=e-1;h--)if(a[h]==i)break;if(h>e){g(h);continue}h=e+b;for(h;h<a.length;h++)if(a[h]!=j)break;if(h==a.length)break;g(h);continue}var k=Math.max(h-10,e-1);while(h>k&&a[h]<i)h--;while(h>k&&a[h]==l)h--;if(h>k){g(++h);continue}h=e+b,g(h)}return c},this.$getDisplayTokens=function(a,d){var e=[],f;d=d||0;for(var g=0;g<a.length;g++){var h=a.charCodeAt(g);if(h==9){f=this.getScreenTabSize(e.length+d),e.push(o);for(var i=1;i<f;i++)e.push(p)}else h==32?e.push(n):h>39&&h<48||h>57&&h<64?e.push(l):h>=4352&&q(h)?e.push(b,c):e.push(b)}return e},this.$getStringScreenWidth=function(a,b,c){if(b==0)return[0,0];b==null&&(b=c+a.length*Math.max(this.getTabSize(),2)),c=c||0;var d,e;for(e=0;e<a.length;e++){d=a.charCodeAt(e),d==9?c+=this.getScreenTabSize(c):d>=4352&&q(d)?c+=2:c+=1;if(c>b)break}return[c,e]},this.getRowLength=function(a){return!this.$useWrapMode||!this.$wrapData[a]?1:this.$wrapData[a].length+1},this.getRowHeight=function(a,b){return this.getRowLength(b)*a.lineHeight},this.getScreenLastRowColumn=function(a){var b=this.screenToDocumentPosition(a,Number.MAX_VALUE);return this.documentToScreenColumn(b.row,b.column)},this.getDocumentLastRowColumn=function(a,b){var c=this.documentToScreenRow(a,b);return this.getScreenLastRowColumn(c)},this.getDocumentLastRowColumnPosition=function(a,b){var c=this.documentToScreenRow(a,b);return this.screenToDocumentPosition(c,Number.MAX_VALUE/10)},this.getRowSplitData=function(a){return this.$useWrapMode?this.$wrapData[a]:undefined},this.getScreenTabSize=function(a){return this.$tabSize-a%this.$tabSize},this.screenToDocumentRow=function(a,b){return this.screenToDocumentPosition(a,b).row},this.screenToDocumentColumn=function(a,b){return this.screenToDocumentPosition(a,b).column},this.screenToDocumentPosition=function(a,b){if(a<0)return{row:0,column:0};var c,d=0,e=0,f,g=0,h=0,i=this.$rowCache;for(var j=0;j<i.length;j++){if(!(i[j].screenRow<a))break;g=i[j].screenRow,d=i[j].docRow}var k=!i.length||j==i.length,l=this.getLength()-1,m=this.getNextFoldLine(d),n=m?m.start.row:Infinity;while(g<=a){h=this.getRowLength(d);if(g+h-1>=a||d>=l)break;g+=h,d++,d>n&&(d=m.end.row+1,m=this.getNextFoldLine(d,m),n=m?m.start.row:Infinity),k&&i.push({docRow:d,screenRow:g})}if(m&&m.start.row<=d)c=this.getFoldDisplayLine(m),d=m.start.row;else{if(g+h<=a||d>l)return{row:l,column:this.getLine(l).length};c=this.getLine(d),m=null}if(this.$useWrapMode){var o=this.$wrapData[d];o&&(f=o[a-g],a>g&&o.length&&(e=o[a-g-1]||o[o.length-1],c=c.substring(e)))}return e+=this.$getStringScreenWidth(c,b)[1],this.$useWrapMode&&e>=f&&(e=f-1),m?m.idxToPosition(e):{row:d,column:e}},this.documentToScreenPosition=function(a,b){if(typeof b=="undefined")var c=this.$clipPositionToDocument(a.row,a.column);else c=this.$clipPositionToDocument(a,b);a=c.row,b=c.column;var d;if(this.$useWrapMode){d=this.$wrapData;if(a>d.length-1)return{row:this.getScreenLength(),column:d.length==0?0:d[d.length-1].length-1}}var e=0,f=null,g=null;g=this.getFoldAt(a,b,1),g&&(a=g.start.row,b=g.start.column);var h,i=0,j=this.$rowCache;for(var k=0;k<j.length;k++){if(!(j[k].docRow<a))break;e=j[k].screenRow,i=j[k].docRow}var l=!j.length||k==j.length,m=this.getNextFoldLine(i),n=m?m.start.row:Infinity;while(i<a){if(i>=n){h=m.end.row+1;if(h>a)break;m=this.getNextFoldLine(h,m),n=m?m.start.row:Infinity}else h=i+1;e+=this.getRowLength(i),i=h,l&&j.push({docRow:i,screenRow:e})}var o="";m&&i>=n?(o=this.getFoldDisplayLine(m,a,b),f=m.start.row):(o=this.getLine(a).substring(0,b),f=a);if(this.$useWrapMode){var p=d[f],q=0;while(o.length>=p[q])e++,q++;o=o.substring(p[q-1]||0,o.length)}return{row:e,column:this.$getStringScreenWidth(o)[0]}},this.documentToScreenColumn=function(a,b){return this.documentToScreenPosition(a,b).column},this.documentToScreenRow=function(a,b){return this.documentToScreenPosition(a,b).row},this.getScreenLength=function(){var a=0,b=null;if(!this.$useWrapMode){a=this.getLength();var c=this.$foldData;for(var d=0;d<c.length;d++)b=c[d],a-=b.end.row-b.start.row}else{var e=this.$wrapData.length,f=0,d=0,b=this.$foldData[d++],g=b?b.start.row:Infinity;while(f<e)a+=this.$wrapData[f].length+1,f++,f>g&&(f=b.end.row+1,b=this.$foldData[d++],g=b?b.start.row:Infinity)}return a}}).call(n.prototype),a("./edit_session/folding").Folding.call(n.prototype),a("./edit_session/bracket_match").BracketMatch.call(n.prototype),b.EditSession=n}),define("ace/config",["require","exports","module","ace/lib/lang"],function(a,b,c){function g(a){return a.replace(/-(.)/g,function(a,b){return b.toUpperCase()})}"no use strict";var d=a("./lib/lang"),e=function(){return this}(),f={packaged:!1,workerPath:"",modePath:"",themePath:"",suffix:".js"};b.get=function(a){if(!f.hasOwnProperty(a))throw new Error("Unknown confik key: "+a);return f[a]},b.set=function(a,b){if(!f.hasOwnProperty(a))throw new Error("Unknown confik key: "+a);f[a]=b},b.all=function(){return d.copyObject(f)},b.init=function(){f.packaged=a.packaged||c.packaged||e.define&&define.packaged;if(!e.document)return"";var d={},h="",i,j=document.getElementsByTagName("script");for(var k=0;k<j.length;k++){var l=j[k],m=l.src||l.getAttribute("src");if(!m)continue;var n=l.attributes;for(var o=0,p=n.length;o<p;o++){var q=n[o];q.name.indexOf("data-ace-")===0&&(d[g(q.name.replace(/^data-ace-/,""))]=q.value)}var r=m.match(/^(?:(.*\/)ace\.js|(.*\/)ace((-uncompressed)?(-noconflict)?\.js))(?:\?|$)/);r&&(h=r[1]||r[2],i=r[3])}h&&(d.base=d.base||h,d.packaged=!0),d.suffix=d.suffix||i,d.workerPath=d.workerPath||d.base,d.modePath=d.modePath||d.base,d.themePath=d.themePath||d.base,delete d.base;for(var s in d)typeof d[s]!="undefined"&&b.set(s,d[s])}}),define("ace/lib/net",["require","exports","module"],function(a,b,c){"use strict",b.get=function(a,c){var d=b.createXhr();d.open("GET",a,!0),d.onreadystatechange=function(a){d.readyState===4&&c(d.responseText)},d.send(null)};var d=["Msxml2.XMLHTTP","Microsoft.XMLHTTP","Msxml2.XMLHTTP.4.0"];b.createXhr=function(){var a,b,c;if(typeof XMLHttpRequest!="undefined")return new XMLHttpRequest;for(b=0;b<3;b++){c=d[b];try{a=new ActiveXObject(c)}catch(e){}if(a){d=[c];break}}if(!a)throw new Error("createXhr(): XMLHttpRequest not available");return a},b.loadScript=function(a,b){var c=document.getElementsByTagName("head")[0],d=document.createElement("script");d.src=a,c.appendChild(d),d.onload=b}}),define("ace/selection",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter","ace/range"],function(a,b,c){"use strict";var d=a("./lib/oop"),e=a("./lib/lang"),f=a("./lib/event_emitter").EventEmitter,g=a("./range").Range,h=function(a){this.session=a,this.doc=a.getDocument(),this.clearSelection(),this.selectionLead=this.doc.createAnchor(0,0),this.selectionAnchor=this.doc.createAnchor(0,0);var b=this;this.selectionLead.on("change",function(a){b._emit("changeCursor"),b.$isEmpty||b._emit("changeSelection"),!b.$keepDesiredColumnOnChange&&a.old.column!=a.value.column&&(b.$desiredColumn=null)}),this.selectionAnchor.on("change",function(){b.$isEmpty||b._emit("changeSelection")})};(function(){d.implement(this,f),this.isEmpty=function(){return this.$isEmpty||this.selectionAnchor.row==this.selectionLead.row&&this.selectionAnchor.column==this.selectionLead.column},this.isMultiLine=function(){return this.isEmpty()?!1:this.getRange().isMultiLine()},this.getCursor=function(){return this.selectionLead.getPosition()},this.setSelectionAnchor=function(a,b){this.selectionAnchor.setPosition(a,b),this.$isEmpty&&(this.$isEmpty=!1,this._emit("changeSelection"))},this.getSelectionAnchor=function(){return this.$isEmpty?this.getSelectionLead():this.selectionAnchor.getPosition()},this.getSelectionLead=function(){return this.selectionLead.getPosition()},this.shiftSelection=function(a){if(this.$isEmpty){this.moveCursorTo(this.selectionLead.row,this.selectionLead.column+a);return}var b=this.getSelectionAnchor(),c=this.getSelectionLead(),d=this.isBackwards();(!d||b.column!==0)&&this.setSelectionAnchor(b.row,b.column+a),(d||c.column!==0)&&this.$moveSelection(function(){this.moveCursorTo(c.row,c.column+a)})},this.isBackwards=function(){var a=this.selectionAnchor,b=this.selectionLead;return a.row>b.row||a.row==b.row&&a.column>b.column},this.getRange=function(){var a=this.selectionAnchor,b=this.selectionLead;return this.isEmpty()?g.fromPoints(b,b):this.isBackwards()?g.fromPoints(b,a):g.fromPoints(a,b)},this.clearSelection=function(){this.$isEmpty||(this.$isEmpty=!0,this._emit("changeSelection"))},this.selectAll=function(){var a=this.doc.getLength()-1;this.setSelectionAnchor(a,this.doc.getLine(a).length),this.moveCursorTo(0,0)},this.setSelectionRange=function(a,b){b?(this.setSelectionAnchor(a.end.row,a.end.column),this.selectTo(a.start.row,a.start.column)):(this.setSelectionAnchor(a.start.row,a.start.column),this.selectTo(a.end.row,a.end.column)),this.$desiredColumn=null},this.$moveSelection=function(a){var b=this.selectionLead;this.$isEmpty&&this.setSelectionAnchor(b.row,b.column),a.call(this)},this.selectTo=function(a,b){this.$moveSelection(function(){this.moveCursorTo(a,b)})},this.selectToPosition=function(a){this.$moveSelection(function(){this.moveCursorToPosition(a)})},this.selectUp=function(){this.$moveSelection(this.moveCursorUp)},this.selectDown=function(){this.$moveSelection(this.moveCursorDown)},this.selectRight=function(){this.$moveSelection(this.moveCursorRight)},this.selectLeft=function(){this.$moveSelection(this.moveCursorLeft)},this.selectLineStart=function(){this.$moveSelection(this.moveCursorLineStart)},this.selectLineEnd=function(){this.$moveSelection(this.moveCursorLineEnd)},this.selectFileEnd=function(){this.$moveSelection(this.moveCursorFileEnd)},this.selectFileStart=function(){this.$moveSelection(this.moveCursorFileStart)},this.selectWordRight=function(){this.$moveSelection(this.moveCursorWordRight)},this.selectWordLeft=function(){this.$moveSelection(this.moveCursorWordLeft)},this.selectWord=function(){var a=this.getCursor(),b=this.session.getWordRange(a.row,a.column);this.setSelectionRange(b)},this.selectAWord=function(){var a=this.getCursor(),b=this.session.getAWordRange(a.row,a.column);this.setSelectionRange(b)},this.selectLine=function(){var a=this.selectionLead.row,b,c=this.session.getFoldLine(a);c?(a=c.start.row,b=c.end.row):b=a,this.setSelectionAnchor(a,0),this.$moveSelection(function(){this.moveCursorTo(b+1,0)})},this.moveCursorUp=function(){this.moveCursorBy(-1,0)},this.moveCursorDown=function(){this.moveCursorBy(1,0)},this.moveCursorLeft=function(){var a=this.selectionLead.getPosition(),b;if(b=this.session.getFoldAt(a.row,a.column,-1))this.moveCursorTo(b.start.row,b.start.column);else if(a.column==0)a.row>0&&this.moveCursorTo(a.row-1,this.doc.getLine(a.row-1).length);else{var c=this.session.getTabSize();this.session.isTabStop(a)&&this.doc.getLine(a.row).slice(a.column-c,a.column).split(" ").length-1==c?this.moveCursorBy(0,-c):this.moveCursorBy(0,-1)}},this.moveCursorRight=function(){var a=this.selectionLead.getPosition(),b;if(b=this.session.getFoldAt(a.row,a.column,1))this.moveCursorTo(b.end.row,b.end.column);else if(this.selectionLead.column==this.doc.getLine(this.selectionLead.row).length)this.selectionLead.row<this.doc.getLength()-1&&this.moveCursorTo(this.selectionLead.row+1,0);else{var c=this.session.getTabSize(),a=this.selectionLead;this.session.isTabStop(a)&&this.doc.getLine(a.row).slice(a.column,a.column+c).split(" ").length-1==c?this.moveCursorBy(0,c):this.moveCursorBy(0,1)}},this.moveCursorLineStart=function(){var a=this.selectionLead.row,b=this.selectionLead.column,c=this.session.documentToScreenRow(a,b),d=this.session.screenToDocumentPosition(c,0),e=this.session.getDisplayLine(a,null,d.row,d.column),f=e.match(/^\s*/);f[0].length==b?this.moveCursorTo(d.row,d.column):this.moveCursorTo(d.row,d.column+f[0].length)},this.moveCursorLineEnd=function(){var a=this.selectionLead,b=this.session.getDocumentLastRowColumnPosition(a.row,a.column);this.moveCursorTo(b.row,b.column)},this.moveCursorFileEnd=function(){var a=this.doc.getLength()-1,b=this.doc.getLine(a).length;this.moveCursorTo(a,b)},this.moveCursorFileStart=function(){this.moveCursorTo(0,0)},this.moveCursorWordRight=function(){var a=this.selectionLead.row,b=this.selectionLead.column,c=this.doc.getLine(a),d=c.substring(b),e;this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;var f=this.session.getFoldAt(a,b,1);if(f){this.moveCursorTo(f.end.row,f.end.column);return}if(e=this.session.nonTokenRe.exec(d))b+=this.session.nonTokenRe.lastIndex,this.session.nonTokenRe.lastIndex=0,d=c.substring(b);if(b>=c.length){this.moveCursorTo(a,c.length),this.moveCursorRight(),a<this.doc.getLength()-1&&this.moveCursorWordRight();return}if(e=this.session.tokenRe.exec(d))b+=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0;this.moveCursorTo(a,b)},this.moveCursorWordLeft=function(){var a=this.selectionLead.row,b=this.selectionLead.column,c;if(c=this.session.getFoldAt(a,b,-1)){this.moveCursorTo(c.start.row,c.start.column);return}var d=this.session.getFoldStringAt(a,b,-1);d==null&&(d=this.doc.getLine(a).substring(0,b));var f=e.stringReverse(d),g;this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;if(g=this.session.nonTokenRe.exec(f))b-=this.session.nonTokenRe.lastIndex,f=f.slice(this.session.nonTokenRe.lastIndex),this.session.nonTokenRe.lastIndex=0;if(b<=0){this.moveCursorTo(a,0),this.moveCursorLeft(),a>0&&this.moveCursorWordLeft();return}if(g=this.session.tokenRe.exec(f))b-=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0;this.moveCursorTo(a,b)},this.moveCursorBy=function(a,b){var c=this.session.documentToScreenPosition(this.selectionLead.row,this.selectionLead.column);b===0&&(this.$desiredColumn?c.column=this.$desiredColumn:this.$desiredColumn=c.column);var d=this.session.screenToDocumentPosition(c.row+a,c.column);this.moveCursorTo(d.row,d.column+b,b===0)},this.moveCursorToPosition=function(a){this.moveCursorTo(a.row,a.column)},this.moveCursorTo=function(a,b,c){var d=this.session.getFoldAt(a,b,1);d&&(a=d.start.row,b=d.start.column),this.$keepDesiredColumnOnChange=!0,this.selectionLead.setPosition(a,b),this.$keepDesiredColumnOnChange=!1,c||(this.$desiredColumn=null)},this.moveCursorToScreen=function(a,b,c){var d=this.session.screenToDocumentPosition(a,b);this.moveCursorTo(d.row,d.column,c)},this.detach=function(){this.selectionLead.detach(),this.selectionAnchor.detach(),this.session=this.doc=null},this.fromOrientedRange=function(a){this.setSelectionRange(a,a.cursor==a.start),this.$desiredColumn=a.desiredColumn||this.$desiredColumn},this.toOrientedRange=function(a){var b=this.getRange();return a?(a.start.column=b.start.column,a.start.row=b.start.row,a.end.column=b.end.column,a.end.row=b.end.row):a=b,a.cursor=this.isBackwards()?a.start:a.end,a.desiredColumn=this.$desiredColumn,a}}).call(h.prototype),b.Selection=h}),define("ace/range",["require","exports","module"],function(a,b,c){"use strict";var d=function(a,b,c,d){this.start={row:a,column:b},this.end={row:c,column:d}};(function(){this.isEqual=function(a){return this.start.row==a.start.row&&this.end.row==a.end.row&&this.start.column==a.start.column&&this.end.column==a.end.column},this.toString=function(){return"Range: ["+this.start.row+"/"+this.start.column+"] -> ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(a,b){return this.compare(a,b)==0},this.compareRange=function(a){var b,c=a.end,d=a.start;return b=this.compare(c.row,c.column),b==1?(b=this.compare(d.row,d.column),b==1?2:b==0?1:0):b==-1?-2:(b=this.compare(d.row,d.column),b==-1?-1:b==1?42:0)},this.comparePoint=function(a){return this.compare(a.row,a.column)},this.containsRange=function(a){return this.comparePoint(a.start)==0&&this.comparePoint(a.end)==0},this.intersectsRange=function(a){var b=this.compareRange(a);return b==-1||b==0||b==1},this.isEnd=function(a,b){return this.end.row==a&&this.end.column==b},this.isStart=function(a,b){return this.start.row==a&&this.start.column==b},this.setStart=function(a,b){typeof a=="object"?(this.start.column=a.column,this.start.row=a.row):(this.start.row=a,this.start.column=b)},this.setEnd=function(a,b){typeof a=="object"?(this.end.column=a.column,this.end.row=a.row):(this.end.row=a,this.end.column=b)},this.inside=function(a,b){return this.compare(a,b)==0?this.isEnd(a,b)||this.isStart(a,b)?!1:!0:!1},this.insideStart=function(a,b){return this.compare(a,b)==0?this.isEnd(a,b)?!1:!0:!1},this.insideEnd=function(a,b){return this.compare(a,b)==0?this.isStart(a,b)?!1:!0:!1},this.compare=function(a,b){return!this.isMultiLine()&&a===this.start.row?b<this.start.column?-1:b>this.end.column?1:0:a<this.start.row?-1:a>this.end.row?1:this.start.row===a?b>=this.start.column?0:-1:this.end.row===a?b<=this.end.column?0:1:0},this.compareStart=function(a,b){return this.start.row==a&&this.start.column==b?-1:this.compare(a,b)},this.compareEnd=function(a,b){return this.end.row==a&&this.end.column==b?1:this.compare(a,b)},this.compareInside=function(a,b){return this.end.row==a&&this.end.column==b?1:this.start.row==a&&this.start.column==b?-1:this.compare(a,b)},this.clipRows=function(a,b){if(this.end.row>b)var c={row:b+1,column:0};if(this.start.row>b)var e={row:b+1,column:0};if(this.start.row<a)var e={row:a,column:0};if(this.end.row<a)var c={row:a,column:0};return d.fromPoints(e||this.start,c||this.end)},this.extend=function(a,b){var c=this.compare(a,b);if(c==0)return this;if(c==-1)var e={row:a,column:b};else var f={row:a,column:b};return d.fromPoints(e||this.start,f||this.end)},this.fixOrientation=function(){if(this.start.row<this.end.row||this.start.row==this.end.row&&this.start.column<this.end.column)return!1;var a=this.start;return this.end=this.start,this.start=a,!0},this.isEmpty=function(){return this.start.row==this.end.row&&this.start.column==this.end.column},this.isMultiLine=function(){return this.start.row!==this.end.row},this.clone=function(){return d.fromPoints(this.start,this.end)},this.collapseRows=function(){return this.end.column==0?new d(this.start.row,0,Math.max(this.start.row,this.end.row-1),0):new d(this.start.row,0,this.end.row,0)},this.toScreenRange=function(a){var b=a.documentToScreenPosition(this.start),c=a.documentToScreenPosition(this.end);return new d(b.row,b.column,c.row,c.column)}}).call(d.prototype),d.fromPoints=function(a,b){return new d(a.row,a.column,b.row,b.column)},b.Range=d}),define("ace/mode/text",["require","exports","module","ace/tokenizer","ace/mode/text_highlight_rules","ace/mode/behaviour","ace/unicode"],function(a,b,c){"use strict";var d=a("../tokenizer").Tokenizer,e=a("./text_highlight_rules").TextHighlightRules,f=a("./behaviour").Behaviour,g=a("../unicode"),h=function(){this.$tokenizer=new d((new e).getRules()),this.$behaviour=new f};(function(){this.tokenRe=new RegExp("^["+g.packages.L+g.packages.Mn+g.packages.Mc+g.packages.Nd+g.packages.Pc+"\\$_]+","g"),this.nonTokenRe=new RegExp("^(?:[^"+g.packages.L+g.packages.Mn+g.packages.Mc+g.packages.Nd+g.packages.Pc+"\\$_]|s])+","g"),this.getTokenizer=function(){return this.$tokenizer},this.toggleCommentLines=function(a,b,c,d){},this.getNextLineIndent=function(a,b,c){return""},this.checkOutdent=function(a,b,c){return!1},this.autoOutdent=function(a,b,c){},this.$getIndent=function(a){var b=a.match(/^(\s+)/);return b?b[1]:""},this.createWorker=function(a){return null},this.highlightSelection=function(a){var b=a.session;b.$selectionOccurrences||(b.$selectionOccurrences=[]),b.$selectionOccurrences.length&&this.clearSelectionHighlight(a);var c=a.getSelectionRange();if(c.isEmpty()||c.isMultiLine())return;var d=c.start.column-1,e=c.end.column+1,f=b.getLine(c.start.row),g=f.length,h=f.substring(Math.max(d,0),Math.min(e,g));if(d>=0&&/^[\w\d]/.test(h)||e<=g&&/[\w\d]$/.test(h))return;h=f.substring(c.start.column,c.end.column);if(!/^[\w\d]+$/.test(h))return;var i=a.getCursorPosition(),j={wrap:!0,wholeWord:!0,caseSensitive:!0,needle:h},k=a.$search.getOptions();a.$search.set(j);var l=a.$search.findAll(b);l.forEach(function(a){if(!a.contains(i.row,i.column)){var c=b.addMarker(a,"ace_selected_word","text");b.$selectionOccurrences.push(c)}}),a.$search.set(k)},this.clearSelectionHighlight=function(a){if(!a.session.$selectionOccurrences)return;a.session.$selectionOccurrences.forEach(function(b){a.session.removeMarker(b)}),a.session.$selectionOccurrences=[]},this.createModeDelegates=function(a){if(!this.$embeds)return;this.$modes={};for(var b=0;b<this.$embeds.length;b++)a[this.$embeds[b]]&&(this.$modes[this.$embeds[b]]=new a[this.$embeds[b]]);var c=["toggleCommentLines","getNextLineIndent","checkOutdent","autoOutdent","transformAction"];for(var b=0;b<c.length;b++)(function(a){var d=c[b],e=a[d];a[c[b]]=function(){return this.$delegator(d,arguments,e)}})(this)},this.$delegator=function(a,b,c){var d=b[0];for(var e=0;e<this.$embeds.length;e++){if(!this.$modes[this.$embeds[e]])continue;var f=d.split(this.$embeds[e]);if(!f[0]&&f[1]){b[0]=f[1];var g=this.$modes[this.$embeds[e]];return g[a].apply(g,b)}}var h=c.apply(this,b);return c?h:undefined},this.transformAction=function(a,b,c,d,e){if(this.$behaviour){var f=this.$behaviour.getBehaviours();for(var g in f)if(f[g][b]){var h=f[g][b].apply(this,arguments);if(h)return h}}}}).call(h.prototype),b.Mode=h}),define("ace/tokenizer",["require","exports","module"],function(a,b,c){"use strict";var d=function(a,b){b=b?"g"+b:"g",this.rules=a,this.regExps={},this.matchMappings={};for(var c in this.rules){var d=this.rules[c],e=d,f=[],g=0,h=this.matchMappings[c]={};for(var i=0;i<e.length;i++){e[i].regex instanceof RegExp&&(e[i].regex=e[i].regex.toString().slice(1,-1));var j=(new RegExp("(?:("+e[i].regex+")|(.))")).exec("a").length-2,k=e[i].regex.replace(/\\([0-9]+)/g,function(a,b){return"\\"+(parseInt(b,10)+g+1)});if(j>1&&e[i].token.length!==j-1)throw new Error("Matching groups and length of the token array don't match in rule #"+i+" of state "+c);h[g]={rule:i,len:j},g+=j,f.push(k)}this.regExps[c]=new RegExp("(?:("+f.join(")|(")+")|(.))",b)}};(function(){this.getLineTokens=function(a,b){var c=b,d=this.rules[c],e=this.matchMappings[c],f=this.regExps[c];f.lastIndex=0;var g,h=[],i=0,j={type:null,value:""};while(g=f.exec(a)){var k="text",l=null,m=[g[0]];for(var n=0;n<g.length-2;n++){if(g[n+1]===undefined)continue;l=d[e[n].rule],e[n].len>1&&(m=g.slice(n+2,n+1+e[n].len)),typeof l.token=="function"?k=l.token.apply(this,m):k=l.token;var o=l.next;o&&o!==c&&(c=o,d=this.rules[c],e=this.matchMappings[c],i=f.lastIndex,f=this.regExps[c],f.lastIndex=i);break}if(m[0]){typeof k=="string"&&(m=[m.join("")],k=[k]);for(var n=0;n<m.length;n++){if(!m[n])continue;(!l||l.merge||k[n]==="text")&&j.type===k[n]?j.value+=m[n]:(j.type&&h.push(j),j={type:k[n],value:m[n]})}}if(i==a.length)break;i=f.lastIndex}return j.type&&h.push(j),{tokens:h,state:c}}}).call(d.prototype),b.Tokenizer=d}),define("ace/mode/text_highlight_rules",["require","exports","module","ace/lib/lang"],function(a,b,c){"use strict";var d=a("../lib/lang"),e=function(){this.$rules={start:[{token:"empty_line",regex:"^$"},{token:"text",regex:".+"}]}};(function(){this.addRules=function(a,b){for(var c in a){var d=a[c];for(var e=0;e<d.length;e++){var f=d[e];f.next?f.next=b+f.next:f.next=b+c}this.$rules[b+c]=d}},this.getRules=function(){return this.$rules},this.embedRules=function(a,b,c,e){var f=(new a).getRules();if(e)for(var g=0;g<e.length;g++)e[g]=b+e[g];else{e=[];for(var h in f)e.push(b+h)}this.addRules(f,b);for(var g=0;g<e.length;g++)Array.prototype.unshift.apply(this.$rules[e[g]],d.deepCopy(c));this.$embeds||(this.$embeds=[]),this.$embeds.push(b)},this.getEmbeds=function(){return this.$embeds}}).call(e.prototype),b.TextHighlightRules=e}),define("ace/mode/behaviour",["require","exports","module"],function(a,b,c){"use strict";var d=function(){this.$behaviours={}};(function(){this.add=function(a,b,c){switch(undefined){case this.$behaviours:this.$behaviours={};case this.$behaviours[a]:this.$behaviours[a]={}}this.$behaviours[a][b]=c},this.addBehaviours=function(a){for(var b in a)for(var c in a[b])this.add(b,c,a[b][c])},this.remove=function(a){this.$behaviours&&this.$behaviours[a]&&delete this.$behaviours[a]},this.inherit=function(a,b){if(typeof a=="function")var c=(new a).getBehaviours(b);else var c=a.getBehaviours(b);this.addBehaviours(c)},this.getBehaviours=function(a){if(!a)return this.$behaviours;var b={};for(var c=0;c<a.length;c++)this.$behaviours[a[c]]&&(b[a[c]]=this.$behaviours[a[c]]);return b}}).call(d.prototype),b.Behaviour=d}),define("ace/unicode",["require","exports","module"],function(a,b,c){function d(a){var c=/\w{4}/g;for(var d in a)b.packages[d]=a[d].replace(c,"\\u$&")}"use strict",b.packages={},d({L:"0041-005A0061-007A00AA00B500BA00C0-00D600D8-00F600F8-02C102C6-02D102E0-02E402EC02EE0370-037403760377037A-037D03860388-038A038C038E-03A103A3-03F503F7-0481048A-05250531-055605590561-058705D0-05EA05F0-05F20621-064A066E066F0671-06D306D506E506E606EE06EF06FA-06FC06FF07100712-072F074D-07A507B107CA-07EA07F407F507FA0800-0815081A082408280904-0939093D09500958-0961097109720979-097F0985-098C098F09900993-09A809AA-09B009B209B6-09B909BD09CE09DC09DD09DF-09E109F009F10A05-0A0A0A0F0A100A13-0A280A2A-0A300A320A330A350A360A380A390A59-0A5C0A5E0A72-0A740A85-0A8D0A8F-0A910A93-0AA80AAA-0AB00AB20AB30AB5-0AB90ABD0AD00AE00AE10B05-0B0C0B0F0B100B13-0B280B2A-0B300B320B330B35-0B390B3D0B5C0B5D0B5F-0B610B710B830B85-0B8A0B8E-0B900B92-0B950B990B9A0B9C0B9E0B9F0BA30BA40BA8-0BAA0BAE-0BB90BD00C05-0C0C0C0E-0C100C12-0C280C2A-0C330C35-0C390C3D0C580C590C600C610C85-0C8C0C8E-0C900C92-0CA80CAA-0CB30CB5-0CB90CBD0CDE0CE00CE10D05-0D0C0D0E-0D100D12-0D280D2A-0D390D3D0D600D610D7A-0D7F0D85-0D960D9A-0DB10DB3-0DBB0DBD0DC0-0DC60E01-0E300E320E330E40-0E460E810E820E840E870E880E8A0E8D0E94-0E970E99-0E9F0EA1-0EA30EA50EA70EAA0EAB0EAD-0EB00EB20EB30EBD0EC0-0EC40EC60EDC0EDD0F000F40-0F470F49-0F6C0F88-0F8B1000-102A103F1050-1055105A-105D106110651066106E-10701075-1081108E10A0-10C510D0-10FA10FC1100-1248124A-124D1250-12561258125A-125D1260-1288128A-128D1290-12B012B2-12B512B8-12BE12C012C2-12C512C8-12D612D8-13101312-13151318-135A1380-138F13A0-13F41401-166C166F-167F1681-169A16A0-16EA1700-170C170E-17111720-17311740-17511760-176C176E-17701780-17B317D717DC1820-18771880-18A818AA18B0-18F51900-191C1950-196D1970-19741980-19AB19C1-19C71A00-1A161A20-1A541AA71B05-1B331B45-1B4B1B83-1BA01BAE1BAF1C00-1C231C4D-1C4F1C5A-1C7D1CE9-1CEC1CEE-1CF11D00-1DBF1E00-1F151F18-1F1D1F20-1F451F48-1F4D1F50-1F571F591F5B1F5D1F5F-1F7D1F80-1FB41FB6-1FBC1FBE1FC2-1FC41FC6-1FCC1FD0-1FD31FD6-1FDB1FE0-1FEC1FF2-1FF41FF6-1FFC2071207F2090-209421022107210A-211321152119-211D212421262128212A-212D212F-2139213C-213F2145-2149214E218321842C00-2C2E2C30-2C5E2C60-2CE42CEB-2CEE2D00-2D252D30-2D652D6F2D80-2D962DA0-2DA62DA8-2DAE2DB0-2DB62DB8-2DBE2DC0-2DC62DC8-2DCE2DD0-2DD62DD8-2DDE2E2F300530063031-3035303B303C3041-3096309D-309F30A1-30FA30FC-30FF3105-312D3131-318E31A0-31B731F0-31FF3400-4DB54E00-9FCBA000-A48CA4D0-A4FDA500-A60CA610-A61FA62AA62BA640-A65FA662-A66EA67F-A697A6A0-A6E5A717-A71FA722-A788A78BA78CA7FB-A801A803-A805A807-A80AA80C-A822A840-A873A882-A8B3A8F2-A8F7A8FBA90A-A925A930-A946A960-A97CA984-A9B2A9CFAA00-AA28AA40-AA42AA44-AA4BAA60-AA76AA7AAA80-AAAFAAB1AAB5AAB6AAB9-AABDAAC0AAC2AADB-AADDABC0-ABE2AC00-D7A3D7B0-D7C6D7CB-D7FBF900-FA2DFA30-FA6DFA70-FAD9FB00-FB06FB13-FB17FB1DFB1F-FB28FB2A-FB36FB38-FB3CFB3EFB40FB41FB43FB44FB46-FBB1FBD3-FD3DFD50-FD8FFD92-FDC7FDF0-FDFBFE70-FE74FE76-FEFCFF21-FF3AFF41-FF5AFF66-FFBEFFC2-FFC7FFCA-FFCFFFD2-FFD7FFDA-FFDC",Ll:"0061-007A00AA00B500BA00DF-00F600F8-00FF01010103010501070109010B010D010F01110113011501170119011B011D011F01210123012501270129012B012D012F01310133013501370138013A013C013E014001420144014601480149014B014D014F01510153015501570159015B015D015F01610163016501670169016B016D016F0171017301750177017A017C017E-0180018301850188018C018D019201950199-019B019E01A101A301A501A801AA01AB01AD01B001B401B601B901BA01BD-01BF01C601C901CC01CE01D001D201D401D601D801DA01DC01DD01DF01E101E301E501E701E901EB01ED01EF01F001F301F501F901FB01FD01FF02010203020502070209020B020D020F02110213021502170219021B021D021F02210223022502270229022B022D022F02310233-0239023C023F0240024202470249024B024D024F-02930295-02AF037103730377037B-037D039003AC-03CE03D003D103D5-03D703D903DB03DD03DF03E103E303E503E703E903EB03ED03EF-03F303F503F803FB03FC0430-045F04610463046504670469046B046D046F04710473047504770479047B047D047F0481048B048D048F04910493049504970499049B049D049F04A104A304A504A704A904AB04AD04AF04B104B304B504B704B904BB04BD04BF04C204C404C604C804CA04CC04CE04CF04D104D304D504D704D904DB04DD04DF04E104E304E504E704E904EB04ED04EF04F104F304F504F704F904FB04FD04FF05010503050505070509050B050D050F05110513051505170519051B051D051F0521052305250561-05871D00-1D2B1D62-1D771D79-1D9A1E011E031E051E071E091E0B1E0D1E0F1E111E131E151E171E191E1B1E1D1E1F1E211E231E251E271E291E2B1E2D1E2F1E311E331E351E371E391E3B1E3D1E3F1E411E431E451E471E491E4B1E4D1E4F1E511E531E551E571E591E5B1E5D1E5F1E611E631E651E671E691E6B1E6D1E6F1E711E731E751E771E791E7B1E7D1E7F1E811E831E851E871E891E8B1E8D1E8F1E911E931E95-1E9D1E9F1EA11EA31EA51EA71EA91EAB1EAD1EAF1EB11EB31EB51EB71EB91EBB1EBD1EBF1EC11EC31EC51EC71EC91ECB1ECD1ECF1ED11ED31ED51ED71ED91EDB1EDD1EDF1EE11EE31EE51EE71EE91EEB1EED1EEF1EF11EF31EF51EF71EF91EFB1EFD1EFF-1F071F10-1F151F20-1F271F30-1F371F40-1F451F50-1F571F60-1F671F70-1F7D1F80-1F871F90-1F971FA0-1FA71FB0-1FB41FB61FB71FBE1FC2-1FC41FC61FC71FD0-1FD31FD61FD71FE0-1FE71FF2-1FF41FF61FF7210A210E210F2113212F21342139213C213D2146-2149214E21842C30-2C5E2C612C652C662C682C6A2C6C2C712C732C742C76-2C7C2C812C832C852C872C892C8B2C8D2C8F2C912C932C952C972C992C9B2C9D2C9F2CA12CA32CA52CA72CA92CAB2CAD2CAF2CB12CB32CB52CB72CB92CBB2CBD2CBF2CC12CC32CC52CC72CC92CCB2CCD2CCF2CD12CD32CD52CD72CD92CDB2CDD2CDF2CE12CE32CE42CEC2CEE2D00-2D25A641A643A645A647A649A64BA64DA64FA651A653A655A657A659A65BA65DA65FA663A665A667A669A66BA66DA681A683A685A687A689A68BA68DA68FA691A693A695A697A723A725A727A729A72BA72DA72F-A731A733A735A737A739A73BA73DA73FA741A743A745A747A749A74BA74DA74FA751A753A755A757A759A75BA75DA75FA761A763A765A767A769A76BA76DA76FA771-A778A77AA77CA77FA781A783A785A787A78CFB00-FB06FB13-FB17FF41-FF5A",Lu:"0041-005A00C0-00D600D8-00DE01000102010401060108010A010C010E01100112011401160118011A011C011E01200122012401260128012A012C012E01300132013401360139013B013D013F0141014301450147014A014C014E01500152015401560158015A015C015E01600162016401660168016A016C016E017001720174017601780179017B017D018101820184018601870189-018B018E-0191019301940196-0198019C019D019F01A001A201A401A601A701A901AC01AE01AF01B1-01B301B501B701B801BC01C401C701CA01CD01CF01D101D301D501D701D901DB01DE01E001E201E401E601E801EA01EC01EE01F101F401F6-01F801FA01FC01FE02000202020402060208020A020C020E02100212021402160218021A021C021E02200222022402260228022A022C022E02300232023A023B023D023E02410243-02460248024A024C024E03700372037603860388-038A038C038E038F0391-03A103A3-03AB03CF03D2-03D403D803DA03DC03DE03E003E203E403E603E803EA03EC03EE03F403F703F903FA03FD-042F04600462046404660468046A046C046E04700472047404760478047A047C047E0480048A048C048E04900492049404960498049A049C049E04A004A204A404A604A804AA04AC04AE04B004B204B404B604B804BA04BC04BE04C004C104C304C504C704C904CB04CD04D004D204D404D604D804DA04DC04DE04E004E204E404E604E804EA04EC04EE04F004F204F404F604F804FA04FC04FE05000502050405060508050A050C050E05100512051405160518051A051C051E0520052205240531-055610A0-10C51E001E021E041E061E081E0A1E0C1E0E1E101E121E141E161E181E1A1E1C1E1E1E201E221E241E261E281E2A1E2C1E2E1E301E321E341E361E381E3A1E3C1E3E1E401E421E441E461E481E4A1E4C1E4E1E501E521E541E561E581E5A1E5C1E5E1E601E621E641E661E681E6A1E6C1E6E1E701E721E741E761E781E7A1E7C1E7E1E801E821E841E861E881E8A1E8C1E8E1E901E921E941E9E1EA01EA21EA41EA61EA81EAA1EAC1EAE1EB01EB21EB41EB61EB81EBA1EBC1EBE1EC01EC21EC41EC61EC81ECA1ECC1ECE1ED01ED21ED41ED61ED81EDA1EDC1EDE1EE01EE21EE41EE61EE81EEA1EEC1EEE1EF01EF21EF41EF61EF81EFA1EFC1EFE1F08-1F0F1F18-1F1D1F28-1F2F1F38-1F3F1F48-1F4D1F591F5B1F5D1F5F1F68-1F6F1FB8-1FBB1FC8-1FCB1FD8-1FDB1FE8-1FEC1FF8-1FFB21022107210B-210D2110-211221152119-211D212421262128212A-212D2130-2133213E213F214521832C00-2C2E2C602C62-2C642C672C692C6B2C6D-2C702C722C752C7E-2C802C822C842C862C882C8A2C8C2C8E2C902C922C942C962C982C9A2C9C2C9E2CA02CA22CA42CA62CA82CAA2CAC2CAE2CB02CB22CB42CB62CB82CBA2CBC2CBE2CC02CC22CC42CC62CC82CCA2CCC2CCE2CD02CD22CD42CD62CD82CDA2CDC2CDE2CE02CE22CEB2CEDA640A642A644A646A648A64AA64CA64EA650A652A654A656A658A65AA65CA65EA662A664A666A668A66AA66CA680A682A684A686A688A68AA68CA68EA690A692A694A696A722A724A726A728A72AA72CA72EA732A734A736A738A73AA73CA73EA740A742A744A746A748A74AA74CA74EA750A752A754A756A758A75AA75CA75EA760A762A764A766A768A76AA76CA76EA779A77BA77DA77EA780A782A784A786A78BFF21-FF3A",Lt:"01C501C801CB01F21F88-1F8F1F98-1F9F1FA8-1FAF1FBC1FCC1FFC",Lm:"02B0-02C102C6-02D102E0-02E402EC02EE0374037A0559064006E506E607F407F507FA081A0824082809710E460EC610FC17D718431AA71C78-1C7D1D2C-1D611D781D9B-1DBF2071207F2090-20942C7D2D6F2E2F30053031-3035303B309D309E30FC-30FEA015A4F8-A4FDA60CA67FA717-A71FA770A788A9CFAA70AADDFF70FF9EFF9F",Lo:"01BB01C0-01C3029405D0-05EA05F0-05F20621-063F0641-064A066E066F0671-06D306D506EE06EF06FA-06FC06FF07100712-072F074D-07A507B107CA-07EA0800-08150904-0939093D09500958-096109720979-097F0985-098C098F09900993-09A809AA-09B009B209B6-09B909BD09CE09DC09DD09DF-09E109F009F10A05-0A0A0A0F0A100A13-0A280A2A-0A300A320A330A350A360A380A390A59-0A5C0A5E0A72-0A740A85-0A8D0A8F-0A910A93-0AA80AAA-0AB00AB20AB30AB5-0AB90ABD0AD00AE00AE10B05-0B0C0B0F0B100B13-0B280B2A-0B300B320B330B35-0B390B3D0B5C0B5D0B5F-0B610B710B830B85-0B8A0B8E-0B900B92-0B950B990B9A0B9C0B9E0B9F0BA30BA40BA8-0BAA0BAE-0BB90BD00C05-0C0C0C0E-0C100C12-0C280C2A-0C330C35-0C390C3D0C580C590C600C610C85-0C8C0C8E-0C900C92-0CA80CAA-0CB30CB5-0CB90CBD0CDE0CE00CE10D05-0D0C0D0E-0D100D12-0D280D2A-0D390D3D0D600D610D7A-0D7F0D85-0D960D9A-0DB10DB3-0DBB0DBD0DC0-0DC60E01-0E300E320E330E40-0E450E810E820E840E870E880E8A0E8D0E94-0E970E99-0E9F0EA1-0EA30EA50EA70EAA0EAB0EAD-0EB00EB20EB30EBD0EC0-0EC40EDC0EDD0F000F40-0F470F49-0F6C0F88-0F8B1000-102A103F1050-1055105A-105D106110651066106E-10701075-1081108E10D0-10FA1100-1248124A-124D1250-12561258125A-125D1260-1288128A-128D1290-12B012B2-12B512B8-12BE12C012C2-12C512C8-12D612D8-13101312-13151318-135A1380-138F13A0-13F41401-166C166F-167F1681-169A16A0-16EA1700-170C170E-17111720-17311740-17511760-176C176E-17701780-17B317DC1820-18421844-18771880-18A818AA18B0-18F51900-191C1950-196D1970-19741980-19AB19C1-19C71A00-1A161A20-1A541B05-1B331B45-1B4B1B83-1BA01BAE1BAF1C00-1C231C4D-1C4F1C5A-1C771CE9-1CEC1CEE-1CF12135-21382D30-2D652D80-2D962DA0-2DA62DA8-2DAE2DB0-2DB62DB8-2DBE2DC0-2DC62DC8-2DCE2DD0-2DD62DD8-2DDE3006303C3041-3096309F30A1-30FA30FF3105-312D3131-318E31A0-31B731F0-31FF3400-4DB54E00-9FCBA000-A014A016-A48CA4D0-A4F7A500-A60BA610-A61FA62AA62BA66EA6A0-A6E5A7FB-A801A803-A805A807-A80AA80C-A822A840-A873A882-A8B3A8F2-A8F7A8FBA90A-A925A930-A946A960-A97CA984-A9B2AA00-AA28AA40-AA42AA44-AA4BAA60-AA6FAA71-AA76AA7AAA80-AAAFAAB1AAB5AAB6AAB9-AABDAAC0AAC2AADBAADCABC0-ABE2AC00-D7A3D7B0-D7C6D7CB-D7FBF900-FA2DFA30-FA6DFA70-FAD9FB1DFB1F-FB28FB2A-FB36FB38-FB3CFB3EFB40FB41FB43FB44FB46-FBB1FBD3-FD3DFD50-FD8FFD92-FDC7FDF0-FDFBFE70-FE74FE76-FEFCFF66-FF6FFF71-FF9DFFA0-FFBEFFC2-FFC7FFCA-FFCFFFD2-FFD7FFDA-FFDC",M:"0300-036F0483-04890591-05BD05BF05C105C205C405C505C70610-061A064B-065E067006D6-06DC06DE-06E406E706E806EA-06ED07110730-074A07A6-07B007EB-07F30816-0819081B-08230825-08270829-082D0900-0903093C093E-094E0951-0955096209630981-098309BC09BE-09C409C709C809CB-09CD09D709E209E30A01-0A030A3C0A3E-0A420A470A480A4B-0A4D0A510A700A710A750A81-0A830ABC0ABE-0AC50AC7-0AC90ACB-0ACD0AE20AE30B01-0B030B3C0B3E-0B440B470B480B4B-0B4D0B560B570B620B630B820BBE-0BC20BC6-0BC80BCA-0BCD0BD70C01-0C030C3E-0C440C46-0C480C4A-0C4D0C550C560C620C630C820C830CBC0CBE-0CC40CC6-0CC80CCA-0CCD0CD50CD60CE20CE30D020D030D3E-0D440D46-0D480D4A-0D4D0D570D620D630D820D830DCA0DCF-0DD40DD60DD8-0DDF0DF20DF30E310E34-0E3A0E47-0E4E0EB10EB4-0EB90EBB0EBC0EC8-0ECD0F180F190F350F370F390F3E0F3F0F71-0F840F860F870F90-0F970F99-0FBC0FC6102B-103E1056-1059105E-10601062-10641067-106D1071-10741082-108D108F109A-109D135F1712-17141732-1734175217531772177317B6-17D317DD180B-180D18A91920-192B1930-193B19B0-19C019C819C91A17-1A1B1A55-1A5E1A60-1A7C1A7F1B00-1B041B34-1B441B6B-1B731B80-1B821BA1-1BAA1C24-1C371CD0-1CD21CD4-1CE81CED1CF21DC0-1DE61DFD-1DFF20D0-20F02CEF-2CF12DE0-2DFF302A-302F3099309AA66F-A672A67CA67DA6F0A6F1A802A806A80BA823-A827A880A881A8B4-A8C4A8E0-A8F1A926-A92DA947-A953A980-A983A9B3-A9C0AA29-AA36AA43AA4CAA4DAA7BAAB0AAB2-AAB4AAB7AAB8AABEAABFAAC1ABE3-ABEAABECABEDFB1EFE00-FE0FFE20-FE26",Mn:"0300-036F0483-04870591-05BD05BF05C105C205C405C505C70610-061A064B-065E067006D6-06DC06DF-06E406E706E806EA-06ED07110730-074A07A6-07B007EB-07F30816-0819081B-08230825-08270829-082D0900-0902093C0941-0948094D0951-095509620963098109BC09C1-09C409CD09E209E30A010A020A3C0A410A420A470A480A4B-0A4D0A510A700A710A750A810A820ABC0AC1-0AC50AC70AC80ACD0AE20AE30B010B3C0B3F0B41-0B440B4D0B560B620B630B820BC00BCD0C3E-0C400C46-0C480C4A-0C4D0C550C560C620C630CBC0CBF0CC60CCC0CCD0CE20CE30D41-0D440D4D0D620D630DCA0DD2-0DD40DD60E310E34-0E3A0E47-0E4E0EB10EB4-0EB90EBB0EBC0EC8-0ECD0F180F190F350F370F390F71-0F7E0F80-0F840F860F870F90-0F970F99-0FBC0FC6102D-10301032-10371039103A103D103E10581059105E-10601071-1074108210851086108D109D135F1712-17141732-1734175217531772177317B7-17BD17C617C9-17D317DD180B-180D18A91920-19221927192819321939-193B1A171A181A561A58-1A5E1A601A621A65-1A6C1A73-1A7C1A7F1B00-1B031B341B36-1B3A1B3C1B421B6B-1B731B801B811BA2-1BA51BA81BA91C2C-1C331C361C371CD0-1CD21CD4-1CE01CE2-1CE81CED1DC0-1DE61DFD-1DFF20D0-20DC20E120E5-20F02CEF-2CF12DE0-2DFF302A-302F3099309AA66FA67CA67DA6F0A6F1A802A806A80BA825A826A8C4A8E0-A8F1A926-A92DA947-A951A980-A982A9B3A9B6-A9B9A9BCAA29-AA2EAA31AA32AA35AA36AA43AA4CAAB0AAB2-AAB4AAB7AAB8AABEAABFAAC1ABE5ABE8ABEDFB1EFE00-FE0FFE20-FE26",Mc:"0903093E-09400949-094C094E0982098309BE-09C009C709C809CB09CC09D70A030A3E-0A400A830ABE-0AC00AC90ACB0ACC0B020B030B3E0B400B470B480B4B0B4C0B570BBE0BBF0BC10BC20BC6-0BC80BCA-0BCC0BD70C01-0C030C41-0C440C820C830CBE0CC0-0CC40CC70CC80CCA0CCB0CD50CD60D020D030D3E-0D400D46-0D480D4A-0D4C0D570D820D830DCF-0DD10DD8-0DDF0DF20DF30F3E0F3F0F7F102B102C10311038103B103C105610571062-10641067-106D108310841087-108C108F109A-109C17B617BE-17C517C717C81923-19261929-192B193019311933-193819B0-19C019C819C91A19-1A1B1A551A571A611A631A641A6D-1A721B041B351B3B1B3D-1B411B431B441B821BA11BA61BA71BAA1C24-1C2B1C341C351CE11CF2A823A824A827A880A881A8B4-A8C3A952A953A983A9B4A9B5A9BAA9BBA9BD-A9C0AA2FAA30AA33AA34AA4DAA7BABE3ABE4ABE6ABE7ABE9ABEAABEC",Me:"0488048906DE20DD-20E020E2-20E4A670-A672",N:"0030-003900B200B300B900BC-00BE0660-066906F0-06F907C0-07C90966-096F09E6-09EF09F4-09F90A66-0A6F0AE6-0AEF0B66-0B6F0BE6-0BF20C66-0C6F0C78-0C7E0CE6-0CEF0D66-0D750E50-0E590ED0-0ED90F20-0F331040-10491090-10991369-137C16EE-16F017E0-17E917F0-17F91810-18191946-194F19D0-19DA1A80-1A891A90-1A991B50-1B591BB0-1BB91C40-1C491C50-1C5920702074-20792080-20892150-21822185-21892460-249B24EA-24FF2776-27932CFD30073021-30293038-303A3192-31953220-32293251-325F3280-328932B1-32BFA620-A629A6E6-A6EFA830-A835A8D0-A8D9A900-A909A9D0-A9D9AA50-AA59ABF0-ABF9FF10-FF19",Nd:"0030-00390660-066906F0-06F907C0-07C90966-096F09E6-09EF0A66-0A6F0AE6-0AEF0B66-0B6F0BE6-0BEF0C66-0C6F0CE6-0CEF0D66-0D6F0E50-0E590ED0-0ED90F20-0F291040-10491090-109917E0-17E91810-18191946-194F19D0-19DA1A80-1A891A90-1A991B50-1B591BB0-1BB91C40-1C491C50-1C59A620-A629A8D0-A8D9A900-A909A9D0-A9D9AA50-AA59ABF0-ABF9FF10-FF19",Nl:"16EE-16F02160-21822185-218830073021-30293038-303AA6E6-A6EF",No:"00B200B300B900BC-00BE09F4-09F90BF0-0BF20C78-0C7E0D70-0D750F2A-0F331369-137C17F0-17F920702074-20792080-20892150-215F21892460-249B24EA-24FF2776-27932CFD3192-31953220-32293251-325F3280-328932B1-32BFA830-A835",P:"0021-00230025-002A002C-002F003A003B003F0040005B-005D005F007B007D00A100AB00B700BB00BF037E0387055A-055F0589058A05BE05C005C305C605F305F40609060A060C060D061B061E061F066A-066D06D40700-070D07F7-07F90830-083E0964096509700DF40E4F0E5A0E5B0F04-0F120F3A-0F3D0F850FD0-0FD4104A-104F10FB1361-13681400166D166E169B169C16EB-16ED1735173617D4-17D617D8-17DA1800-180A1944194519DE19DF1A1E1A1F1AA0-1AA61AA8-1AAD1B5A-1B601C3B-1C3F1C7E1C7F1CD32010-20272030-20432045-20512053-205E207D207E208D208E2329232A2768-277527C527C627E6-27EF2983-299829D8-29DB29FC29FD2CF9-2CFC2CFE2CFF2E00-2E2E2E302E313001-30033008-30113014-301F3030303D30A030FBA4FEA4FFA60D-A60FA673A67EA6F2-A6F7A874-A877A8CEA8CFA8F8-A8FAA92EA92FA95FA9C1-A9CDA9DEA9DFAA5C-AA5FAADEAADFABEBFD3EFD3FFE10-FE19FE30-FE52FE54-FE61FE63FE68FE6AFE6BFF01-FF03FF05-FF0AFF0C-FF0FFF1AFF1BFF1FFF20FF3B-FF3DFF3FFF5BFF5DFF5F-FF65",Pd:"002D058A05BE140018062010-20152E172E1A301C303030A0FE31FE32FE58FE63FF0D",Ps:"0028005B007B0F3A0F3C169B201A201E2045207D208D23292768276A276C276E27702772277427C527E627E827EA27EC27EE2983298529872989298B298D298F299129932995299729D829DA29FC2E222E242E262E283008300A300C300E3010301430163018301A301DFD3EFE17FE35FE37FE39FE3BFE3DFE3FFE41FE43FE47FE59FE5BFE5DFF08FF3BFF5BFF5FFF62",Pe:"0029005D007D0F3B0F3D169C2046207E208E232A2769276B276D276F27712773277527C627E727E927EB27ED27EF298429862988298A298C298E2990299229942996299829D929DB29FD2E232E252E272E293009300B300D300F3011301530173019301B301E301FFD3FFE18FE36FE38FE3AFE3CFE3EFE40FE42FE44FE48FE5AFE5CFE5EFF09FF3DFF5DFF60FF63",Pi:"00AB2018201B201C201F20392E022E042E092E0C2E1C2E20",Pf:"00BB2019201D203A2E032E052E0A2E0D2E1D2E21",Pc:"005F203F20402054FE33FE34FE4D-FE4FFF3F",Po:"0021-00230025-0027002A002C002E002F003A003B003F0040005C00A100B700BF037E0387055A-055F058905C005C305C605F305F40609060A060C060D061B061E061F066A-066D06D40700-070D07F7-07F90830-083E0964096509700DF40E4F0E5A0E5B0F04-0F120F850FD0-0FD4104A-104F10FB1361-1368166D166E16EB-16ED1735173617D4-17D617D8-17DA1800-18051807-180A1944194519DE19DF1A1E1A1F1AA0-1AA61AA8-1AAD1B5A-1B601C3B-1C3F1C7E1C7F1CD3201620172020-20272030-2038203B-203E2041-20432047-205120532055-205E2CF9-2CFC2CFE2CFF2E002E012E06-2E082E0B2E0E-2E162E182E192E1B2E1E2E1F2E2A-2E2E2E302E313001-3003303D30FBA4FEA4FFA60D-A60FA673A67EA6F2-A6F7A874-A877A8CEA8CFA8F8-A8FAA92EA92FA95FA9C1-A9CDA9DEA9DFAA5C-AA5FAADEAADFABEBFE10-FE16FE19FE30FE45FE46FE49-FE4CFE50-FE52FE54-FE57FE5F-FE61FE68FE6AFE6BFF01-FF03FF05-FF07FF0AFF0CFF0EFF0FFF1AFF1BFF1FFF20FF3CFF61FF64FF65",S:"0024002B003C-003E005E0060007C007E00A2-00A900AC00AE-00B100B400B600B800D700F702C2-02C502D2-02DF02E5-02EB02ED02EF-02FF03750384038503F604820606-0608060B060E060F06E906FD06FE07F609F209F309FA09FB0AF10B700BF3-0BFA0C7F0CF10CF20D790E3F0F01-0F030F13-0F170F1A-0F1F0F340F360F380FBE-0FC50FC7-0FCC0FCE0FCF0FD5-0FD8109E109F13601390-139917DB194019E0-19FF1B61-1B6A1B74-1B7C1FBD1FBF-1FC11FCD-1FCF1FDD-1FDF1FED-1FEF1FFD1FFE20442052207A-207C208A-208C20A0-20B8210021012103-21062108210921142116-2118211E-2123212521272129212E213A213B2140-2144214A-214D214F2190-2328232B-23E82400-24262440-244A249C-24E92500-26CD26CF-26E126E326E8-26FF2701-27042706-2709270C-27272729-274B274D274F-27522756-275E2761-276727942798-27AF27B1-27BE27C0-27C427C7-27CA27CC27D0-27E527F0-29822999-29D729DC-29FB29FE-2B4C2B50-2B592CE5-2CEA2E80-2E992E9B-2EF32F00-2FD52FF0-2FFB300430123013302030363037303E303F309B309C319031913196-319F31C0-31E33200-321E322A-32503260-327F328A-32B032C0-32FE3300-33FF4DC0-4DFFA490-A4C6A700-A716A720A721A789A78AA828-A82BA836-A839AA77-AA79FB29FDFCFDFDFE62FE64-FE66FE69FF04FF0BFF1C-FF1EFF3EFF40FF5CFF5EFFE0-FFE6FFE8-FFEEFFFCFFFD",Sm:"002B003C-003E007C007E00AC00B100D700F703F60606-060820442052207A-207C208A-208C2140-2144214B2190-2194219A219B21A021A321A621AE21CE21CF21D221D421F4-22FF2308-230B23202321237C239B-23B323DC-23E125B725C125F8-25FF266F27C0-27C427C7-27CA27CC27D0-27E527F0-27FF2900-29822999-29D729DC-29FB29FE-2AFF2B30-2B442B47-2B4CFB29FE62FE64-FE66FF0BFF1C-FF1EFF5CFF5EFFE2FFE9-FFEC",Sc:"002400A2-00A5060B09F209F309FB0AF10BF90E3F17DB20A0-20B8A838FDFCFE69FF04FFE0FFE1FFE5FFE6",Sk:"005E006000A800AF00B400B802C2-02C502D2-02DF02E5-02EB02ED02EF-02FF0375038403851FBD1FBF-1FC11FCD-1FCF1FDD-1FDF1FED-1FEF1FFD1FFE309B309CA700-A716A720A721A789A78AFF3EFF40FFE3",So:"00A600A700A900AE00B000B60482060E060F06E906FD06FE07F609FA0B700BF3-0BF80BFA0C7F0CF10CF20D790F01-0F030F13-0F170F1A-0F1F0F340F360F380FBE-0FC50FC7-0FCC0FCE0FCF0FD5-0FD8109E109F13601390-1399194019E0-19FF1B61-1B6A1B74-1B7C210021012103-21062108210921142116-2118211E-2123212521272129212E213A213B214A214C214D214F2195-2199219C-219F21A121A221A421A521A7-21AD21AF-21CD21D021D121D321D5-21F32300-2307230C-231F2322-2328232B-237B237D-239A23B4-23DB23E2-23E82400-24262440-244A249C-24E92500-25B625B8-25C025C2-25F72600-266E2670-26CD26CF-26E126E326E8-26FF2701-27042706-2709270C-27272729-274B274D274F-27522756-275E2761-276727942798-27AF27B1-27BE2800-28FF2B00-2B2F2B452B462B50-2B592CE5-2CEA2E80-2E992E9B-2EF32F00-2FD52FF0-2FFB300430123013302030363037303E303F319031913196-319F31C0-31E33200-321E322A-32503260-327F328A-32B032C0-32FE3300-33FF4DC0-4DFFA490-A4C6A828-A82BA836A837A839AA77-AA79FDFDFFE4FFE8FFEDFFEEFFFCFFFD",Z:"002000A01680180E2000-200A20282029202F205F3000",Zs:"002000A01680180E2000-200A202F205F3000",Zl:"2028",Zp:"2029",C:"0000-001F007F-009F00AD03780379037F-0383038B038D03A20526-05300557055805600588058B-059005C8-05CF05EB-05EF05F5-0605061C061D0620065F06DD070E070F074B074C07B2-07BF07FB-07FF082E082F083F-08FF093A093B094F095609570973-097809800984098D098E0991099209A909B109B3-09B509BA09BB09C509C609C909CA09CF-09D609D8-09DB09DE09E409E509FC-0A000A040A0B-0A0E0A110A120A290A310A340A370A3A0A3B0A3D0A43-0A460A490A4A0A4E-0A500A52-0A580A5D0A5F-0A650A76-0A800A840A8E0A920AA90AB10AB40ABA0ABB0AC60ACA0ACE0ACF0AD1-0ADF0AE40AE50AF00AF2-0B000B040B0D0B0E0B110B120B290B310B340B3A0B3B0B450B460B490B4A0B4E-0B550B58-0B5B0B5E0B640B650B72-0B810B840B8B-0B8D0B910B96-0B980B9B0B9D0BA0-0BA20BA5-0BA70BAB-0BAD0BBA-0BBD0BC3-0BC50BC90BCE0BCF0BD1-0BD60BD8-0BE50BFB-0C000C040C0D0C110C290C340C3A-0C3C0C450C490C4E-0C540C570C5A-0C5F0C640C650C70-0C770C800C810C840C8D0C910CA90CB40CBA0CBB0CC50CC90CCE-0CD40CD7-0CDD0CDF0CE40CE50CF00CF3-0D010D040D0D0D110D290D3A-0D3C0D450D490D4E-0D560D58-0D5F0D640D650D76-0D780D800D810D840D97-0D990DB20DBC0DBE0DBF0DC7-0DC90DCB-0DCE0DD50DD70DE0-0DF10DF5-0E000E3B-0E3E0E5C-0E800E830E850E860E890E8B0E8C0E8E-0E930E980EA00EA40EA60EA80EA90EAC0EBA0EBE0EBF0EC50EC70ECE0ECF0EDA0EDB0EDE-0EFF0F480F6D-0F700F8C-0F8F0F980FBD0FCD0FD9-0FFF10C6-10CF10FD-10FF1249124E124F12571259125E125F1289128E128F12B112B612B712BF12C112C612C712D7131113161317135B-135E137D-137F139A-139F13F5-13FF169D-169F16F1-16FF170D1715-171F1737-173F1754-175F176D17711774-177F17B417B517DE17DF17EA-17EF17FA-17FF180F181A-181F1878-187F18AB-18AF18F6-18FF191D-191F192C-192F193C-193F1941-1943196E196F1975-197F19AC-19AF19CA-19CF19DB-19DD1A1C1A1D1A5F1A7D1A7E1A8A-1A8F1A9A-1A9F1AAE-1AFF1B4C-1B4F1B7D-1B7F1BAB-1BAD1BBA-1BFF1C38-1C3A1C4A-1C4C1C80-1CCF1CF3-1CFF1DE7-1DFC1F161F171F1E1F1F1F461F471F4E1F4F1F581F5A1F5C1F5E1F7E1F7F1FB51FC51FD41FD51FDC1FF01FF11FF51FFF200B-200F202A-202E2060-206F20722073208F2095-209F20B9-20CF20F1-20FF218A-218F23E9-23FF2427-243F244B-245F26CE26E226E4-26E727002705270A270B2728274C274E2753-2755275F27602795-279727B027BF27CB27CD-27CF2B4D-2B4F2B5A-2BFF2C2F2C5F2CF2-2CF82D26-2D2F2D66-2D6E2D70-2D7F2D97-2D9F2DA72DAF2DB72DBF2DC72DCF2DD72DDF2E32-2E7F2E9A2EF4-2EFF2FD6-2FEF2FFC-2FFF3040309730983100-3104312E-3130318F31B8-31BF31E4-31EF321F32FF4DB6-4DBF9FCC-9FFFA48D-A48FA4C7-A4CFA62C-A63FA660A661A674-A67BA698-A69FA6F8-A6FFA78D-A7FAA82C-A82FA83A-A83FA878-A87FA8C5-A8CDA8DA-A8DFA8FC-A8FFA954-A95EA97D-A97FA9CEA9DA-A9DDA9E0-A9FFAA37-AA3FAA4EAA4FAA5AAA5BAA7C-AA7FAAC3-AADAAAE0-ABBFABEEABEFABFA-ABFFD7A4-D7AFD7C7-D7CAD7FC-F8FFFA2EFA2FFA6EFA6FFADA-FAFFFB07-FB12FB18-FB1CFB37FB3DFB3FFB42FB45FBB2-FBD2FD40-FD4FFD90FD91FDC8-FDEFFDFEFDFFFE1A-FE1FFE27-FE2FFE53FE67FE6C-FE6FFE75FEFD-FF00FFBF-FFC1FFC8FFC9FFD0FFD1FFD8FFD9FFDD-FFDFFFE7FFEF-FFFBFFFEFFFF",Cc:"0000-001F007F-009F",Cf:"00AD0600-060306DD070F17B417B5200B-200F202A-202E2060-2064206A-206FFEFFFFF9-FFFB",Co:"E000-F8FF",Cs:"D800-DFFF",Cn:"03780379037F-0383038B038D03A20526-05300557055805600588058B-059005C8-05CF05EB-05EF05F5-05FF06040605061C061D0620065F070E074B074C07B2-07BF07FB-07FF082E082F083F-08FF093A093B094F095609570973-097809800984098D098E0991099209A909B109B3-09B509BA09BB09C509C609C909CA09CF-09D609D8-09DB09DE09E409E509FC-0A000A040A0B-0A0E0A110A120A290A310A340A370A3A0A3B0A3D0A43-0A460A490A4A0A4E-0A500A52-0A580A5D0A5F-0A650A76-0A800A840A8E0A920AA90AB10AB40ABA0ABB0AC60ACA0ACE0ACF0AD1-0ADF0AE40AE50AF00AF2-0B000B040B0D0B0E0B110B120B290B310B340B3A0B3B0B450B460B490B4A0B4E-0B550B58-0B5B0B5E0B640B650B72-0B810B840B8B-0B8D0B910B96-0B980B9B0B9D0BA0-0BA20BA5-0BA70BAB-0BAD0BBA-0BBD0BC3-0BC50BC90BCE0BCF0BD1-0BD60BD8-0BE50BFB-0C000C040C0D0C110C290C340C3A-0C3C0C450C490C4E-0C540C570C5A-0C5F0C640C650C70-0C770C800C810C840C8D0C910CA90CB40CBA0CBB0CC50CC90CCE-0CD40CD7-0CDD0CDF0CE40CE50CF00CF3-0D010D040D0D0D110D290D3A-0D3C0D450D490D4E-0D560D58-0D5F0D640D650D76-0D780D800D810D840D97-0D990DB20DBC0DBE0DBF0DC7-0DC90DCB-0DCE0DD50DD70DE0-0DF10DF5-0E000E3B-0E3E0E5C-0E800E830E850E860E890E8B0E8C0E8E-0E930E980EA00EA40EA60EA80EA90EAC0EBA0EBE0EBF0EC50EC70ECE0ECF0EDA0EDB0EDE-0EFF0F480F6D-0F700F8C-0F8F0F980FBD0FCD0FD9-0FFF10C6-10CF10FD-10FF1249124E124F12571259125E125F1289128E128F12B112B612B712BF12C112C612C712D7131113161317135B-135E137D-137F139A-139F13F5-13FF169D-169F16F1-16FF170D1715-171F1737-173F1754-175F176D17711774-177F17DE17DF17EA-17EF17FA-17FF180F181A-181F1878-187F18AB-18AF18F6-18FF191D-191F192C-192F193C-193F1941-1943196E196F1975-197F19AC-19AF19CA-19CF19DB-19DD1A1C1A1D1A5F1A7D1A7E1A8A-1A8F1A9A-1A9F1AAE-1AFF1B4C-1B4F1B7D-1B7F1BAB-1BAD1BBA-1BFF1C38-1C3A1C4A-1C4C1C80-1CCF1CF3-1CFF1DE7-1DFC1F161F171F1E1F1F1F461F471F4E1F4F1F581F5A1F5C1F5E1F7E1F7F1FB51FC51FD41FD51FDC1FF01FF11FF51FFF2065-206920722073208F2095-209F20B9-20CF20F1-20FF218A-218F23E9-23FF2427-243F244B-245F26CE26E226E4-26E727002705270A270B2728274C274E2753-2755275F27602795-279727B027BF27CB27CD-27CF2B4D-2B4F2B5A-2BFF2C2F2C5F2CF2-2CF82D26-2D2F2D66-2D6E2D70-2D7F2D97-2D9F2DA72DAF2DB72DBF2DC72DCF2DD72DDF2E32-2E7F2E9A2EF4-2EFF2FD6-2FEF2FFC-2FFF3040309730983100-3104312E-3130318F31B8-31BF31E4-31EF321F32FF4DB6-4DBF9FCC-9FFFA48D-A48FA4C7-A4CFA62C-A63FA660A661A674-A67BA698-A69FA6F8-A6FFA78D-A7FAA82C-A82FA83A-A83FA878-A87FA8C5-A8CDA8DA-A8DFA8FC-A8FFA954-A95EA97D-A97FA9CEA9DA-A9DDA9E0-A9FFAA37-AA3FAA4EAA4FAA5AAA5BAA7C-AA7FAAC3-AADAAAE0-ABBFABEEABEFABFA-ABFFD7A4-D7AFD7C7-D7CAD7FC-D7FFFA2EFA2FFA6EFA6FFADA-FAFFFB07-FB12FB18-FB1CFB37FB3DFB3FFB42FB45FBB2-FBD2FD40-FD4FFD90FD91FDC8-FDEFFDFEFDFFFE1A-FE1FFE27-FE2FFE53FE67FE6C-FE6FFE75FEFDFEFEFF00FFBF-FFC1FFC8FFC9FFD0FFD1FFD8FFD9FFDD-FFDFFFE7FFEF-FFF8FFFEFFFF"})}),define("ace/document",["require","exports","module","ace/lib/oop","ace/lib/event_emitter","ace/range","ace/anchor"],function(a,b,c){"use strict";var d=a("./lib/oop"),e=a("./lib/event_emitter").EventEmitter,f=a("./range").Range,g=a("./anchor").Anchor,h=function(a){this.$lines=[],Array.isArray(a)?this.insertLines(0,a):a.length==0?this.$lines=[""]:this.insert({row:0,column:0},a)};(function(){d.implement(this,e),this.setValue=function(a){var b=this.getLength();this.remove(new f(0,0,b,this.getLine(b-1).length)),this.insert({row:0,column:0},a)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(a,b){return new g(this,a,b)},"aaa".split(/a/).length==0?this.$split=function(a){return a.replace(/\r\n|\r/g,"\n").split("\n")}:this.$split=function(a){return a.split(/\r\n|\r|\n/)},this.$detectNewLine=function(a){var b=a.match(/^.*?(\r\n|\r|\n)/m);b?this.$autoNewLine=b[1]:this.$autoNewLine="\n"},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";case"auto":return this.$autoNewLine}},this.$autoNewLine="\n",this.$newLineMode="auto",this.setNewLineMode=function(a){if(this.$newLineMode===a)return;this.$newLineMode=a},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(a){return a=="\r\n"||a=="\r"||a=="\n"},this.getLine=function(a){return this.$lines[a]||""},this.getLines=function(a,b){return this.$lines.slice(a,b+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(a){if(a.start.row==a.end.row)return this.$lines[a.start.row].substring(a.start.column,a.end.column);var b=[];return b.push(this.$lines[a.start.row].substring(a.start.column)),b.push.apply(b,this.getLines(a.start.row+1,a.end.row-1)),b.push(this.$lines[a.end.row].substring(0,a.end.column)),b.join(this.getNewLineCharacter())},this.$clipPosition=function(a){var b=this.getLength();return a.row>=b&&(a.row=Math.max(0,b-1),a.column=this.getLine(b-1).length),a},this.insert=function(a,b){if(!b||b.length===0)return a;a=this.$clipPosition(a),this.getLength()<=1&&this.$detectNewLine(b);var c=this.$split(b),d=c.splice(0,1)[0],e=c.length==0?null:c.splice(c.length-1,1)[0];return a=this.insertInLine(a,d),e!==null&&(a=this.insertNewLine(a),a=this.insertLines(a.row,c),a=this.insertInLine(a,e||"")),a},this.insertLines=function(a,b){if(b.length==0)return{row:a,column:0};var c=[a,0];c.push.apply(c,b),this.$lines.splice.apply(this.$lines,c);var d=new f(a,0,a+b.length,0),e={action:"insertLines",range:d,lines:b};return this._emit("change",{data:e}),d.end},this.insertNewLine=function(a){a=this.$clipPosition(a);var b=this.$lines[a.row]||"";this.$lines[a.row]=b.substring(0,a.column),this.$lines.splice(a.row+1,0,b.substring(a.column,b.length));var c={row:a.row+1,column:0},d={action:"insertText",range:f.fromPoints(a,c),text:this.getNewLineCharacter()};return this._emit("change",{data:d}),c},this.insertInLine=function(a,b){if(b.length==0)return a;var c=this.$lines[a.row]||"";this.$lines[a.row]=c.substring(0,a.column)+b+c.substring(a.column);var d={row:a.row,column:a.column+b.length},e={action:"insertText",range:f.fromPoints(a,d),text:b};return this._emit("change",{data:e}),d},this.remove=function(a){a.start=this.$clipPosition(a.start),a.end=this.$clipPosition(a.end);if(a.isEmpty())return a.start;var b=a.start.row,c=a.end.row;if(a.isMultiLine()){var d=a.start.column==0?b:b+1,e=c-1;a.end.column>0&&this.removeInLine(c,0,a.end.column),e>=d&&this.removeLines(d,e),d!=b&&(this.removeInLine(b,a.start.column,this.getLine(b).length),this.removeNewLine(a.start.row))}else this.removeInLine(b,a.start.column,a.end.column);return a.start},this.removeInLine=function(a,b,c){if(b==c)return;var d=new f(a,b,a,c),e=this.getLine(a),g=e.substring(b,c),h=e.substring(0,b)+e.substring(c,e.length);this.$lines.splice(a,1,h);var i={action:"removeText",range:d,text:g};return this._emit("change",{data:i}),d.start},this.removeLines=function(a,b){var c=new f(a,0,b+1,0),d=this.$lines.splice(a,b-a+1),e={action:"removeLines",range:c,nl:this.getNewLineCharacter(),lines:d};return this._emit("change",{data:e}),d},this.removeNewLine=function(a){var b=this.getLine(a),c=this.getLine(a+1),d=new f(a,b.length,a+1,0),e=b+c;this.$lines.splice(a,2,e);var g={action:"removeText",range:d,text:this.getNewLineCharacter()};this._emit("change",{data:g})},this.replace=function(a,b){if(b.length==0&&a.isEmpty())return a.start;if(b==this.getTextRange(a))return a.end;this.remove(a);if(b)var c=this.insert(a.start,b);else c=a.start;return c},this.applyDeltas=function(a){for(var b=0;b<a.length;b++){var c=a[b],d=f.fromPoints(c.range.start,c.range.end);c.action=="insertLines"?this.insertLines(d.start.row,c.lines):c.action=="insertText"?this.insert(d.start,c.text):c.action=="removeLines"?this.removeLines(d.start.row,d.end.row-1):c.action=="removeText"&&this.remove(d)}},this.revertDeltas=function(a){for(var b=a.length-1;b>=0;b--){var c=a[b],d=f.fromPoints(c.range.start,c.range.end);c.action=="insertLines"?this.removeLines(d.start.row,d.end.row-1):c.action=="insertText"?this.remove(d):c.action=="removeLines"?this.insertLines(d.start.row,c.lines):c.action=="removeText"&&this.insert(d.start,c.text)}}}).call(h.prototype),b.Document=h}),define("ace/anchor",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(a,b,c){"use strict";var d=a("./lib/oop"),e=a("./lib/event_emitter").EventEmitter,f=b.Anchor=function(a,b,c){this.document=a,typeof c=="undefined"?this.setPosition(b.row,b.column):this.setPosition(b,c),this.$onChange=this.onChange.bind(this),a.on("change",this.$onChange)};(function(){d.implement(this,e),this.getPosition=function(){return this.$clipPositionToDocument(this.row,this.column)},this.getDocument=function(){return this.document},this.onChange=function(a){var b=a.data,c=b.range;if(c.start.row==c.end.row&&c.start.row!=this.row)return;if(c.start.row>this.row)return;if(c.start.row==this.row&&c.start.column>this.column)return;var d=this.row,e=this.column;b.action==="insertText"?c.start.row===d&&c.start.column<=e?c.start.row===c.end.row?e+=c.end.column-c.start.column:(e-=c.start.column,d+=c.end.row-c.start.row):c.start.row!==c.end.row&&c.start.row<d&&(d+=c.end.row-c.start.row):b.action==="insertLines"?c.start.row<=d&&(d+=c.end.row-c.start.row):b.action=="removeText"?c.start.row==d&&c.start.column<e?c.end.column>=e?e=c.start.column:e=Math.max(0,e-(c.end.column-c.start.column)):c.start.row!==c.end.row&&c.start.row<d?(c.end.row==d&&(e=Math.max(0,e-c.end.column)+c.start.column),d-=c.end.row-c.start.row):c.end.row==d&&(d-=c.end.row-c.start.row,e=Math.max(0,e-c.end.column)+c.start.column):b.action=="removeLines"&&c.start.row<=d&&(c.end.row<=d?d-=c.end.row-c.start.row:(d=c.start.row,e=0)),this.setPosition(d,e,!0)},this.setPosition=function(a,b,c){var d;c?d={row:a,column:b}:d=this.$clipPositionToDocument(a,b);if(this.row==d.row&&this.column==d.column)return;var e={row:this.row,column:this.column};this.row=d.row,this.column=d.column,this._emit("change",{old:e,value:d})},this.detach=function(){this.document.removeEventListener("change",this.$onChange)},this.$clipPositionToDocument=function(a,b){var c={};return a>=this.document.getLength()?(c.row=Math.max(0,this.document.getLength()-1),c.column=this.document.getLine(c.row).length):a<0?(c.row=0,c.column=0):(c.row=a,c.column=Math.min(this.document.getLine(c.row).length,Math.max(0,b))),b<0&&(c.column=0),c}}).call(f.prototype)}),define("ace/background_tokenizer",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(a,b,c){"use strict";var d=a("./lib/oop"),e=a("./lib/event_emitter").EventEmitter,f=function(a,b){this.running=!1,this.lines=[],this.currentLine=0,this.tokenizer=a;var c=this;this.$worker=function(){if(!c.running)return;var a=new Date,b=c.currentLine,d=c.doc,e=0,f=d.getLength();while(c.currentLine<f){c.lines[c.currentLine]=c.$tokenizeRows(c.currentLine,c.currentLine)[0],c.currentLine++,e+=1;if(e%5==0&&new Date-a>20){c.fireUpdateEvent(b,c.currentLine-1),c.running=setTimeout(c.$worker,20);return}}c.running=!1,c.fireUpdateEvent(b,f-1)}};(function(){d.implement(this,e),this.setTokenizer=function(a){this.tokenizer=a,this.lines=[],this.start(0)},this.setDocument=function(a){this.doc=a,this.lines=[],this.stop()},this.fireUpdateEvent=function(a,b){var c={first:a,last:b};this._emit("update",{data:c})},this.start=function(a){this.currentLine=Math.min(a||0,this.currentLine,this.doc.getLength()),this.lines.splice(this.currentLine,this.lines.length),this.stop(),this.running=setTimeout(this.$worker,700)},this.stop=function(){this.running&&clearTimeout(this.running),this.running=!1},this.getTokens=function(a,b){return this.$tokenizeRows(a,b)},this.getState=function(a){return this.$tokenizeRows(a,a)[0].state},this.$tokenizeRows=function(a,b){if(!this.doc||isNaN(a)||isNaN(b))return[{state:"start",tokens:[]}];var c=[],d="start",e=!1;a>0&&this.lines[a-1]?(d=this.lines[a-1].state,e=!0):a==0?(d="start",e=!0):this.lines.length>0&&(d=this.lines[this.lines.length-1].state);var f=this.doc.getLines(a,b);for(var g=a;g<=b;g++)if(!this.lines[g]){var h=this.tokenizer.getLineTokens(f[g-a]||"",d),d=h.state;c.push(h),e&&(this.lines[g]=h)}else{var h=this.lines[g];d=h.state,c.push(h)}return c}}).call(f.prototype),b.BackgroundTokenizer=f}),define("ace/edit_session/folding",["require","exports","module","ace/range","ace/edit_session/fold_line","ace/edit_session/fold","ace/token_iterator"],function(a,b,c){function h(){this.getFoldAt=function(a,b,c){var d=this.getFoldLine(a);if(!d)return null;var e=d.folds;for(var f=0;f<e.length;f++){var g=e[f];if(g.range.contains(a,b)){if(c==1&&g.range.isEnd(a,b))continue;if(c==-1&&g.range.isStart(a,b))continue;return g}}},this.getFoldsInRange=function(a){a=a.clone();var b=a.start,c=a.end,d=this.$foldData,e=[];b.column+=1,c.column-=1;for(var f=0;f<d.length;f++){var g=d[f].range.compareRange(a);if(g==2)continue;if(g==-2)break;var h=d[f].folds;for(var i=0;i<h.length;i++){var j=h[i];g=j.range.compareRange(a);if(g==-2)break;if(g==2)continue;if(g==42)break;e.push(j)}}return e},this.getAllFolds=function(){function c(b){a.push(b);if(!b.subFolds)return;for(var d=0;d<b.subFolds.length;d++)c(b.subFolds[d])}var a=[],b=this.$foldData;for(var d=0;d<b.length;d++)for(var e=0;e<b[d].folds.length;e++)c(b[d].folds[e]);return a},this.getFoldStringAt=function(a,b,c,d){d=d||this.getFoldLine(a);if(!d)return null;var e={end:{column:0}},f,g;for(var h=0;h<d.folds.length;h++){g=d.folds[h];var i=g.range.compareEnd(a,b);if(i==-1){f=this.getLine(g.start.row).substring(e.end.column,g.start.column);break}if(i===0)return null;e=g}return f||(f=this.getLine(g.start.row).substring(e.end.column)),c==-1?f.substring(0,b-e.end.column):c==1?f.substring(b-e.end.column):f},this.getFoldLine=function(a,b){var c=this.$foldData,d=0;b&&(d=c.indexOf(b)),d==-1&&(d=0);for(d;d<c.length;d++){var e=c[d];if(e.start.row<=a&&e.end.row>=a)return e;if(e.end.row>a)return null}return null},this.getNextFoldLine=function(a,b){var c=this.$foldData,d=0;b&&(d=c.indexOf(b)),d==-1&&(d=0);for(d;d<c.length;d++){var e=c[d];if(e.end.row>=a)return e}return null},this.getFoldedRowCount=function(a,b){var c=this.$foldData,d=b-a+1;for(var e=0;e<c.length;e++){var f=c[e],g=f.end.row,h=f.start.row;if(g>=b){h<b&&(h>=a?d-=b-h:d=0);break}g>=a&&(h>=a?d-=g-h:d-=g-a+1)}return d},this.$addFoldLine=function(a){return this.$foldData.push(a),this.$foldData.sort(function(a,b){return a.start.row-b.start.row}),a},this.addFold=function(a,b){var c=this.$foldData,d=!1,g;a instanceof f?g=a:g=new f(b,a),this.$clipRangeToDocument(g.range);var h=g.start.row,i=g.start.column,j=g.end.row,k=g.end.column;if(g.placeholder.length<2)throw"Placeholder has to be at least 2 characters";if(h==j&&k-i<2)throw"The range has to be at least 2 characters width";var l=this.getFoldAt(h,i,1),m=this.getFoldAt(j,k,-1);if(l&&m==l)return l.addSubFold(g);if(l&&!l.range.isStart(h,i)||m&&!m.range.isEnd(j,k))throw"A fold can't intersect already existing fold"+g.range+l.range;var n=this.getFoldsInRange(g.range);n.length>0&&(this.removeFolds(n),g.subFolds=n);for(var o=0;o<c.length;o++){var p=c[o];if(j==p.start.row){p.addFold(g),d=!0;break}if(h==p.end.row){p.addFold(g),d=!0;if(!g.sameRow){var q=c[o+1];if(q&&q.start.row==j){p.merge(q);break}}break}if(j<=p.start.row)break}return d||(p=this.$addFoldLine(new e(this.$foldData,g))),this.$useWrapMode&&this.$updateWrapData(p.start.row,p.start.row),this.$modified=!0,this._emit("changeFold",{data:g}),g},this.addFolds=function(a){a.forEach(function(a){this.addFold(a)},this)},this.removeFold=function(a){var b=a.foldLine,c=b.start.row,d=b.end.row,e=this.$foldData,f=b.folds;if(f.length==1)e.splice(e.indexOf(b),1);else if(b.range.isEnd(a.end.row,a.end.column))f.pop(),b.end.row=f[f.length-1].end.row,b.end.column=f[f.length-1].end.column;else if(b.range.isStart(a.start.row,a.start.column))f.shift(),b.start.row=f[0].start.row,b.start.column=f[0].start.column;else if(a.sameRow)f.splice(f.indexOf(a),1);else{var g=b.split(a.start.row,a.start.column);f=g.folds,f.shift(),g.start.row=f[0].start.row,g.start.column=f[0].start.column}this.$useWrapMode&&this.$updateWrapData(c,d),this.$modified=!0,this._emit("changeFold",{data:a})},this.removeFolds=function(a){var b=[];for(var c=0;c<a.length;c++)b.push(a[c]);b.forEach(function(a){this.removeFold(a)},this),this.$modified=!0},this.expandFold=function(a){this.removeFold(a),a.subFolds.forEach(function(a){this.addFold(a)},this),a.subFolds=[]},this.expandFolds=function(a){a.forEach(function(a){this.expandFold(a)},this)},this.unfold=function(a,b){var c,e;a==null?c=new d(0,0,this.getLength(),0):typeof a=="number"?c=new d(a,0,a,this.getLine(a).length):"row"in a?c=d.fromPoints(a,a):c=a,e=this.getFoldsInRange(c);if(b)this.removeFolds(e);else while(e.length)this.expandFolds(e),e=this.getFoldsInRange(c)},this.isRowFolded=function(a,b){return!!this.getFoldLine(a,b)},this.getRowFoldEnd=function(a,b){var c=this.getFoldLine(a,b);return c?c.end.row:a},this.getFoldDisplayLine=function(a,b,c,d,e){d==null&&(d=a.start.row,e=0),b==null&&(b=a.end.row,c=this.getLine(b).length);var f=this.doc,g="";return a.walk(function(a,b,c,h){if(b<d)return;if(b==d){if(c<e)return;h=Math.max(e,h)}a?g+=a:g+=f.getLine(b).substring(h,c)}.bind(this),b,c),g},this.getDisplayLine=function(a,b,c,d){var e=this.getFoldLine(a);if(!e){var f;return f=this.doc.getLine(a),f.substring(d||0,b||f.length)}return this.getFoldDisplayLine(e,a,b,c,d)},this.$cloneFoldData=function(){var a=[];return a=this.$foldData.map(function(b){var c=b.folds.map(function(a){return a.clone()});return new e(a,c)}),a},this.toggleFold=function(a){var b=this.selection,c=b.getRange(),d,e;if(c.isEmpty()){var f=c.start;d=this.getFoldAt(f.row,f.column);if(d){this.expandFold(d);return}(e=this.findMatchingBracket(f))?c.comparePoint(e)==1?c.end=e:(c.start=e,c.start.column++,c.end.column--):(e=this.findMatchingBracket({row:f.row,column:f.column+1}))?(c.comparePoint(e)==1?c.end=e:c.start=e,c.start.column++):c=this.getCommentFoldRange(f.row,f.column)||c}else{var g=this.getFoldsInRange(c);if(a&&g.length){this.expandFolds(g);return}g.length==1&&(d=g[0])}d||(d=this.getFoldAt(c.start.row,c.start.column));if(d&&d.range.toString()==c.toString()){this.expandFold(d);return}var h="...";if(!c.isMultiLine()){h=this.getTextRange(c);if(h.length<4)return;h=h.trim().substring(0,2)+".."}this.addFold(h,c)},this.getCommentFoldRange=function(a,b){var c=new g(this,a,b),e=c.getCurrentToken();if(e&&/^comment|string/.test(e.type)){var f=new d,h=new RegExp(e.type.replace(/\..*/,"\\."));do e=c.stepBackward();while(e&&h.test(e.type));c.stepForward(),f.start.row=c.getCurrentTokenRow(),f.start.column=c.getCurrentTokenColumn()+2,c=new g(this,a,b);do e=c.stepForward();while(e&&h.test(e.type));return e=c.stepBackward(),f.end.row=c.getCurrentTokenRow(),f.end.column=c.getCurrentTokenColumn()+e.value.length,f}},this.foldAll=function(a,b){var c=this.foldWidgets;b=b||this.getLength();for(var d=a||0;d<b;d++){c[d]==null&&(c[d]=this.getFoldWidget(d));if(c[d]!="start")continue;var e=this.getFoldWidgetRange(d);if(e&&e.end.row<b)try{this.addFold("...",e)}catch(f){}}},this.$foldStyles={manual:1,markbegin:1,markbeginend:1},this.$foldStyle="markbegin",this.setFoldStyle=function(a){if(!this.$foldStyles[a])throw new Error("invalid fold style: "+a+"["+Object.keys(this.$foldStyles).join(", ")+"]");if(this.$foldStyle==a)return;this.$foldStyle=a,a=="manual"&&this.unfold();var b=this.$foldMode;this.$setFolding(null),this.$setFolding(b)},this.$setFolding=function(a){if(this.$foldMode==a)return;this.$foldMode=a,this.removeListener("change",this.$updateFoldWidgets),this._emit("changeAnnotation");if(!a||this.$foldStyle=="manual"){this.foldWidgets=null;return}this.foldWidgets=[],this.getFoldWidget=a.getFoldWidget.bind(a,this,this.$foldStyle),this.getFoldWidgetRange=a.getFoldWidgetRange.bind(a,this,this.$foldStyle),this.$updateFoldWidgets=this.updateFoldWidgets.bind(this),this.on("change",this.$updateFoldWidgets)},this.onFoldWidgetClick=function(a,b){var c=this.getFoldWidget(a),d=this.getLine(a),e=b.shiftKey,f=e||b.ctrlKey||b.altKey||b.metaKey,g;c=="end"?g=this.getFoldAt(a,0,-1):g=this.getFoldAt(a,d.length,1);if(g){f?this.removeFold(g):this.expandFold(g);return}var h=this.getFoldWidgetRange(a);if(h){if(!h.isMultiLine()){g=this.getFoldAt(h.start.row,h.start.column,1);if(g&&h.isEqual(g.range)){this.removeFold(g);return}}e||this.addFold("...",h),f&&this.foldAll(h.start.row+1,h.end.row)}else f&&this.foldAll(a+1,this.getLength()),b.target.className+=" invalid"},this.updateFoldWidgets=function(a){var b=a.data,c=b.range,d=c.start.row,e=c.end.row-d;if(e===0)this.foldWidgets[d]=null;else if(b.action=="removeText"||b.action=="removeLines")this.foldWidgets.splice(d,e+1,null);else{var f=Array(e+1);f.unshift(d,1),this.foldWidgets.splice.apply(this.foldWidgets,f)}}}"use strict";var d=a("../range").Range,e=a("./fold_line").FoldLine,f=a("./fold").Fold,g=a("../token_iterator").TokenIterator;b.Folding=h}),define("ace/edit_session/fold_line",["require","exports","module","ace/range"],function(a,b,c){function e(a,b){this.foldData=a,Array.isArray(b)?this.folds=b:b=this.folds=[b];var c=b[b.length-1];this.range=new d(b[0].start.row,b[0].start.column,c.end.row,c.end.column),this.start=this.range.start,this.end=this.range.end,this.folds.forEach(function(a){a.setFoldLine(this)},this)}"use strict";var d=a("../range").Range;(function(){this.shiftRow=function(a){this.start.row+=a,this.end.row+=a,this.folds.forEach(function(b){b.start.row+=a,b.end.row+=a})},this.addFold=function(a){if(a.sameRow){if(a.start.row<this.startRow||a.endRow>this.endRow)throw"Can't add a fold to this FoldLine as it has no connection";this.folds.push(a),this.folds.sort(function(a,b){return-a.range.compareEnd(b.start.row,b.start.column)}),this.range.compareEnd(a.start.row,a.start.column)>0?(this.end.row=a.end.row,this.end.column=a.end.column):this.range.compareStart(a.end.row,a.end.column)<0&&(this.start.row=a.start.row,this.start.column=a.start.column)}else if(a.start.row==this.end.row)this.folds.push(a),this.end.row=a.end.row,this.end.column=a.end.column;else{if(a.end.row!=this.start.row)throw"Trying to add fold to FoldRow that doesn't have a matching row";this.folds.unshift(a),this.start.row=a.start.row,this.start.column=a.start.column}a.foldLine=this},this.containsRow=function(a){return a>=this.start.row&&a<=this.end.row},this.walk=function(a,b,c){var d=0,e=this.folds,f,g,h,i=!0;b==null&&(b=this.end.row,c=this.end.column);for(var j=0;j<e.length;j++){f=e[j],g=f.range.compareStart(b,c);if(g==-1){a(null,b,c,d,i);return}h=a(null,f.start.row,f.start.column,d,i),h=!h&&a(f.placeholder,f.start.row,f.start.column,d);if(h||g==0)return;i=!f.sameRow,d=f.end.column}a(null,b,c,d,i)},this.getNextFoldTo=function(a,b){var c,d;for(var e=0;e<this.folds.length;e++){c=this.folds[e],d=c.range.compareEnd(a,b);if(d==-1)return{fold:c,kind:"after"};if(d==0)return{fold:c,kind:"inside"}}return null},this.addRemoveChars=function(a,b,c){var d=this.getNextFoldTo(a,b),e,f;if(d){e=d.fold;if(d.kind=="inside"&&e.start.column!=b&&e.start.row!=a)throw"Moving characters inside of a fold should never be reached";if(e.start.row==a){f=this.folds;var g=f.indexOf(e);g==0&&(this.start.column+=c);for(g;g<f.length;g++){e=f[g],e.start.column+=c;if(!e.sameRow)return;e.end.column+=c}this.end.column+=c}}},this.split=function(a,b){var c=this.getNextFoldTo(a,b).fold,d=this.folds,f=this.foldData;if(!c)return null;var g=d.indexOf(c),h=d[g-1];this.end.row=h.end.row,this.end.column=h.end.column,d=d.splice(g,d.length-g);var i=new e(f,d);return f.splice(f.indexOf(this)+1,0,i),i},this.merge=function(a){var b=a.folds;for(var c=0;c<b.length;c++)this.addFold(b[c]);var d=this.foldData;d.splice(d.indexOf(a),1)},this.toString=function(){var a=[this.range.toString()+": ["];return this.folds.forEach(function(b){a.push(" "+b.toString())}),a.push("]"),a.join("\n")},this.idxToPosition=function(a){var b=0,c;for(var d=0;d<this.folds.length;d++){var c=this.folds[d];a-=c.start.column-b;if(a<0)return{row:c.start.row,column:c.start.column+a};a-=c.placeholder.length;if(a<0)return c.start;b=c.end.column}return{row:this.end.row,column:this.end.column+a}}}).call(e.prototype),b.FoldLine=e}),define("ace/edit_session/fold",["require","exports","module"],function(a,b,c){"use strict";var d=b.Fold=function(a,b){this.foldLine=null,this.placeholder=b,this.range=a,this.start=a.start,this.end=a.end,this.sameRow=a.start.row==a.end.row,this.subFolds=[]};(function(){this.toString=function(){return'"'+this.placeholder+'" '+this.range.toString()},this.setFoldLine=function(a){this.foldLine=a,this.subFolds.forEach(function(b){b.setFoldLine(a)})},this.clone=function(){var a=this.range.clone(),b=new d(a,this.placeholder);return this.subFolds.forEach(function(a){b.subFolds.push(a.clone())}),b},this.addSubFold=function(a){if(this.range.isEqual(a))return this;if(!this.range.containsRange(a))throw"A fold can't intersect already existing fold"+a.range+this.range;var b=a.range.start.row,c=a.range.start.column;for(var d=0,e=-1;d<this.subFolds.length;d++){e=this.subFolds[d].range.compare(b,c);if(e!=1)break}var f=this.subFolds[d];if(e==0)return f.addSubFold(a);var b=a.range.end.row,c=a.range.end.column;for(var g=d,e=-1;g<this.subFolds.length;g++){e=this.subFolds[g].range.compare(b,c);if(e!=1)break}var h=this.subFolds[g];if(e==0)throw"A fold can't intersect already existing fold"+a.range+this.range;var i=this.subFolds.splice(d,g-d,a);return a.setFoldLine(this.foldLine),a}}).call(d.prototype)}),define("ace/token_iterator",["require","exports","module"],function(a,b,c){"use strict";var d=function(a,b,c){this.$session=a,this.$row=b,this.$rowTokens=a.getTokens(b,b)[0].tokens;var d=a.getTokenAt(b,c);this.$tokenIndex=d?d.index:-1};(function(){this.stepBackward=function(){this.$tokenIndex-=1;while(this.$tokenIndex<0){this.$row-=1;if(this.$row<0)return this.$row=0,null;this.$rowTokens=this.$session.getTokens(this.$row,this.$row)[0].tokens,this.$tokenIndex=this.$rowTokens.length-1}return this.$rowTokens[this.$tokenIndex]},this.stepForward=function(){var a=this.$session.getLength();this.$tokenIndex+=1;while(this.$tokenIndex>=this.$rowTokens.length){this.$row+=1;if(this.$row>=a)return this.$row=a-1,null;this.$rowTokens=this.$session.getTokens(this.$row,this.$row)[0].tokens,this.$tokenIndex=0}return this.$rowTokens[this.$tokenIndex]},this.getCurrentToken=function(){return this.$rowTokens[this.$tokenIndex]},this.getCurrentTokenRow=function(){return this.$row},this.getCurrentTokenColumn=function(){var a=this.$rowTokens,b=this.$tokenIndex,c=a[b].start;if(c!==undefined)return c;c=0;while(b>0)b-=1,c+=a[b].value.length;return c}}).call(d.prototype),b.TokenIterator=d}),define("ace/edit_session/bracket_match",["require","exports","module","ace/token_iterator"],function(a,b,c){function e(){this.findMatchingBracket=function(a){if(a.column==0)return null;var b=this.getLine(a.row).charAt(a.column-1);if(b=="")return null;var c=b.match(/([\(\[\{])|([\)\]\}])/);return c?c[1]?this.$findClosingBracket(c[1],a):this.$findOpeningBracket(c[2],a):null},this.$brackets={")":"(","(":")","]":"[","[":"]","{":"}","}":"{"},this.$findOpeningBracket=function(a,b){var c=this.$brackets[a],e=1,f=new d(this,b.row,b.column),g=f.getCurrentToken();if(!g)return null;var h=new RegExp("(\\.?"+g.type.replace(".","|").replace("rparen","lparen|rparen")+")+"),i=b.column-f.getCurrentTokenColumn()-2,j=g.value;for(;;){while(i>=0){var k=j.charAt(i);if(k==c){e-=1;if(e==0)return{row:f.getCurrentTokenRow(),column:i+f.getCurrentTokenColumn()}}else k==a&&(e+=1);i-=1}do g=f.stepBackward();while(g&&!h.test(g.type));if(g==null)break;j=g.value,i=j.length-1}return null},this.$findClosingBracket=function(a,b){var c=this.$brackets[a],e=1,f=new d(this,b.row,b.column),g=f.getCurrentToken();if(!g)return null;var h=new RegExp("(\\.?"+g.type.replace(".","|").replace("lparen","lparen|rparen")+")+"),i=b.column-f.getCurrentTokenColumn();for(;;){var j=g.value,k=j.length;while(i<k){var l=j.charAt(i);if(l==c){e-=1;if(e==0)return{row:f.getCurrentTokenRow(),column:i+f.getCurrentTokenColumn()}}else l==a&&(e+=1);i+=1}do g=f.stepForward();while(g&&!h.test(g.type));if(g==null)break;i=0}return null}}"use strict";var d=a("../token_iterator").TokenIterator;b.BracketMatch=e}),define("ace/search",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(a,b,c){"use strict";var d=a("./lib/lang"),e=a("./lib/oop"),f=a("./range").Range,g=function(){this.$options={needle:"",backwards:!1,wrap:!1,caseSensitive:!1,wholeWord:!1,scope:g.ALL,regExp:!1}};g.ALL=1,g.SELECTION=2,function(){this.set=function(a){return e.mixin(this.$options,a),this},this.getOptions=function(){return d.copyObject(this.$options)},this.find=function(a){if(!this.$options.needle)return null;if(this.$options.backwards)var b=this.$backwardMatchIterator(a);else b=this.$forwardMatchIterator(a);var c=null;return b.forEach(function(a){return c=a,!0}),c},this.findAll=function(a){var b=this.$options;if(!b.needle)return[];if(b.backwards)var c=this.$backwardMatchIterator(a);else c=this.$forwardMatchIterator(a);var d=!b.start&&b.wrap&&b.scope==g.ALL;d&&(b.start={row:0,column:0});var e=[];return c.forEach(function(a){e.push(a)}),d&&(b.start=null),e},this.replace=function(a,b){var c=this.$assembleRegExp(),d=c.exec(a);return d&&d[0].length==a.length?this.$options.regExp?a.replace(c,b):b:null},this.$forwardMatchIterator=function(a){var b=this.$assembleRegExp(),c=this;return{forEach:function(d){c.$forwardLineIterator(a).forEach(function(a,e,f){e&&(a=a.substring(e));var g=[];a.replace(b,function(a){var b=arguments[arguments.length-2];return g.push({str:a,offset:e+b}),a});for(var h=0;h<g.length;h++){var i=g[h],j=c.$rangeFromMatch(f,i.offset,i.str.length);if(d(j))return!0}})}}},this.$backwardMatchIterator=function(a){var b=this.$assembleRegExp(),c=this;return{forEach:function(d){c.$backwardLineIterator(a).forEach(function(a,e,f){e&&(a=a.substring(e));var g=[];a.replace(b,function(a,b){return g.push({str:a,offset:e+b}),a});for(var h=g.length-1;h>=0;h--){var i=g[h],j=c.$rangeFromMatch(f,i.offset,i.str.length);if(d(j))return!0}})}}},this.$rangeFromMatch=function(a,b,c){return new f(a,b,a,b+c)},this.$assembleRegExp=function(){if(this.$options.regExp)var a=this.$options.needle;else a=d.escapeRegExp(this.$options.needle);this.$options.wholeWord&&(a="\\b"+a+"\\b");var b="g";this.$options.caseSensitive||(b+="i");var c=new RegExp(a,b);return c},this.$forwardLineIterator=function(a){function k(e){var f=a.getLine(e);return b&&e==c.end.row&&(f=f.substring(0,c.end.column)),j&&e==d.row&&(f=f.substring(0,d.column)),f}var b=this.$options.scope==g.SELECTION,c=this.$options.range||a.getSelection().getRange(),d=this.$options.start||c[b?"start":"end"],e=b?c.start.row:0,f=b?c.start.column:0,h=b?c.end.row:a.getLength()-1,i=this.$options.wrap,j=!1;return{forEach:function(a){var b=d.row,c=k(b),g=d.column,l=!1;j=!1;while(!a(c,g,b)){if(l)return;b++,g=0;if(b>h){if(!i)return;b=e,g=f,j=!0}b==d.row&&(l=!0),c=k(b)}}}},this.$backwardLineIterator=function(a){var b=this.$options.scope==g.SELECTION,c=this.$options.range||a.getSelection().getRange(),d=this.$options.start||c[b?"end":"start"],e=b?c.start.row:0,f=b?c.start.column:0,h=b?c.end.row:a.getLength()-1,i=this.$options.wrap;return{forEach:function(g){var j=d.row,k=a.getLine(j).substring(0,d.column),l=0,m=!1,n=!1;while(!g(k,l,j)){if(m)return;j--,l=0;if(j<e){if(!i)return;j=h,n=!0}j==d.row&&(m=!0),k=a.getLine(j),b&&(j==e?l=f:j==h&&(k=k.substring(0,c.end.column))),n&&j==d.row&&(l=d.column)}}}}}.call(g.prototype),b.Search=g}),define("ace/commands/command_manager",["require","exports","module","ace/lib/oop","ace/keyboard/hash_handler","ace/lib/event_emitter"],function(a,b,c){"use strict";var d=a("../lib/oop"),e=a("../keyboard/hash_handler").HashHandler,f=a("../lib/event_emitter").EventEmitter,g=function(a,b){this.platform=a,this.commands={},this.commmandKeyBinding={},this.addCommands(b),this.setDefaultHandler("exec",function(a){a.command.exec(a.editor,a.args||{})})};d.inherits(g,e),function(){d.implement(this,f),this.exec=function(a,b,c){return typeof a=="string"&&(a=this.commands[a]),a?b&&b.$readOnly&&!a.readOnly?!1:(this._emit("exec",{editor:b,command:a,args:c}),!0):!1},this.toggleRecording=function(){if(this.$inReplay)return;return this.recording?(this.macro.pop(),this.removeEventListener("exec",this.$addCommandToMacro),this.macro.length||(this.macro=this.oldMacro),this.recording=!1):(this.$addCommandToMacro||(this.$addCommandToMacro=function(a){this.macro.push([a.command,a.args])}.bind(this)),this.oldMacro=this.macro,this.macro=[],this.on("exec",this.$addCommandToMacro),this.recording=!0)},this.replay=function(a){if(this.$inReplay||!this.macro)return;if(this.recording)return this.toggleRecording();try{this.$inReplay=!0,this.macro.forEach(function(b){typeof b=="string"?this.exec(b,a):this.exec(b[0],a,b[1])},this)}finally{this.$inReplay=!1}},this.trimMacro=function(a){return a.map(function(a){return typeof a[0]!="string"&&(a[0]=a[0].name),a[1]||(a=a[0]),a})}}.call(g.prototype),b.CommandManager=g}),define("ace/keyboard/hash_handler",["require","exports","module","ace/lib/keys"],function(a,b,c){function e(a,b){this.platform=b,this.commands={},this.commmandKeyBinding={},this.addCommands(a)}"use strict";var d=a("../lib/keys");(function(){function a(a,c,e){var f,g=0,h=b(a.toLowerCase());for(var i=0,j=h.length;i<j;i++)d.KEY_MODS[h[i]]?g|=d.KEY_MODS[h[i]]:f=h[i]||"-";return{key:f,hashId:g}}function b(a){return a.trim().split(new RegExp("[\\s ]*\\-[\\s ]*","g"),999)}this.addCommand=function(a){this.commands[a.name]&&this.removeCommand(a),this.commands[a.name]=a,a.bindKey&&this._buildKeyHash(a)},this.removeCommand=function(a){var b=typeof a=="string"?a:a.name;a=this.commands[b],delete this.commands[b];var c=this.commmandKeyBinding;for(var d in c)for(var e in c[d])c[d][e]==a&&delete c[d][e]},this.addCommands=function(a){a&&Object.keys(a).forEach(function(b){var c=a[b];if(typeof c=="string")return this.bindKey(c,b);typeof c=="function"&&(c={exec:c}),c.name||(c.name=b),this.addCommand(c)},this)},this.removeCommands=function(a){Object.keys(a).forEach(function(b){this.removeCommand(a[b])},this)},this.bindKey=function(b,c){if(!b)return;var d=this.commmandKeyBinding;b.split("|").forEach(function(b){var e=a(b,c),f=e.hashId;(d[f]||(d[f]={}))[e.key]=c})},this.bindKeys=function(a){Object.keys(a).forEach(function(b){this.bindKey(b,a[b])},this)},this._buildKeyHash=function(a){var b=a.bindKey;if(!b)return;var c=typeof b=="string"?b:b[this.platform];this.bindKey(c,a)},this.findKeyCommand=function(b,c){var d=this.commmandKeyBinding;return d[b]&&d[b][c.toLowerCase()]},this.handleKeyboard=function(a,b,c,d){return{command:this.findKeyCommand(b,c)}}}).call(e.prototype),b.HashHandler=e}),define("ace/undomanager",["require","exports","module"],function(a,b,c){"use strict";var d=function(){this.reset()};(function(){this.execute=function(a){var b=a.args[0];this.$doc=a.args[1],this.$undoStack.push(b),this.$redoStack=[]},this.undo=function(a){var b=this.$undoStack.pop(),c=null;return b&&(c=this.$doc.undoChanges(b,a),this.$redoStack.push(b)),c},this.redo=function(a){var b=this.$redoStack.pop(),c=null;return b&&(c=this.$doc.redoChanges(b,a),this.$undoStack.push(b)),c},this.reset=function(){this.$undoStack=[],this.$redoStack=[]},this.hasUndo=function(){return this.$undoStack.length>0},this.hasRedo=function(){return this.$redoStack.length>0}}).call(d.prototype),b.UndoManager=d}),define("ace/virtual_renderer",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/useragent","ace/config","ace/lib/net","ace/layer/gutter","ace/layer/marker","ace/layer/text","ace/layer/cursor","ace/scrollbar","ace/renderloop","ace/lib/event_emitter","text!ace/css/editor.css"],function(a,b,c){"use strict";var d=a("./lib/oop"),e=a("./lib/dom"),f=a("./lib/event"),g=a("./lib/useragent"),h=a("./config"),i=a("./lib/net"),j=a("./layer/gutter").Gutter,k=a("./layer/marker").Marker,l=a("./layer/text").Text,m=a("./layer/cursor").Cursor,n=a("./scrollbar").ScrollBar,o=a("./renderloop").RenderLoop,p=a("./lib/event_emitter").EventEmitter,q=a("text!./css/editor.css");e.importCssString(q,"ace_editor");var r=function(a,b){var c=this;this.container=a,e.addCssClass(a,"ace_editor"),this.setTheme(b),this.$gutter=e.createElement("div"),this.$gutter.className="ace_gutter",this.container.appendChild(this.$gutter),this.scroller=e.createElement("div"),this.scroller.className="ace_scroller",this.container.appendChild(this.scroller),this.content=e.createElement("div"),this.content.className="ace_content",this.scroller.appendChild(this.content),this.$gutterLayer=new j(this.$gutter),this.$gutterLayer.on("changeGutterWidth",this.onResize.bind(this,!0)),this.$markerBack=new k(this.content);var d=this.$textLayer=new l(this.content);this.canvas=d.element,this.$markerFront=new k(this.content),this.characterWidth=d.getCharacterWidth(),this.lineHeight=d.getLineHeight(),this.$cursorLayer=new m(this.content),this.$cursorPadding=8,this.$horizScroll=!0,this.$horizScrollAlwaysVisible=!0,this.$animatedScroll=!1,this.scrollBar=new n(a),this.scrollBar.addEventListener("scroll",function(a){c.session.setScrollTop(a.data)}),this.scrollTop=0,this.scrollLeft=0,f.addListener(this.scroller,"scroll",function(){var a=c.scroller.scrollLeft;c.scrollLeft=a,c.session.setScrollLeft(a),a==0?c.$gutter.className="ace_gutter":c.$gutter.className="ace_gutter horscroll"}),this.cursorPos={row:0,column:0},this.$textLayer.addEventListener("changeCharacterSize",function(){c.characterWidth=d.getCharacterWidth(),c.lineHeight=d.getLineHeight(),c.$updatePrintMargin(),c.onResize(!0),c.$loop.schedule(c.CHANGE_FULL)}),this.$size={width:0,height:0,scrollerHeight:0,scrollerWidth:0},this.layerConfig={width:1,padding:0,firstRow:0,firstRowScreen:0,lastRow:0,lineHeight:1,characterWidth:1,minHeight:1,maxHeight:1,offset:0,height:1},this.$loop=new o(this.$renderChanges.bind(this),this.container.ownerDocument.defaultView),this.$loop.schedule(this.CHANGE_FULL),this.setPadding(4),this.$updatePrintMargin()};(function(){this.showGutter=!0,this.CHANGE_CURSOR=1,this.CHANGE_MARKER=2,this.CHANGE_GUTTER=4,this.CHANGE_SCROLL=8,this.CHANGE_LINES=16,this.CHANGE_TEXT=32,this.CHANGE_SIZE=64,this.CHANGE_MARKER_BACK=128,this.CHANGE_MARKER_FRONT=256,this.CHANGE_FULL=512,this.CHANGE_H_SCROLL=1024,d.implement(this,p),this.setSession=function(a){this.session=a,this.$cursorLayer.setSession(a),this.$markerBack.setSession(a),this.$markerFront.setSession(a),this.$gutterLayer.setSession(a),this.$textLayer.setSession(a),this.$loop.schedule(this.CHANGE_FULL)},this.updateLines=function(a,b){b===undefined&&(b=Infinity),this.$changedLines?(this.$changedLines.firstRow>a&&(this.$changedLines.firstRow=a),this.$changedLines.lastRow<b&&(this.$changedLines.lastRow=b)):this.$changedLines={firstRow:a,lastRow:b},this.$loop.schedule(this.CHANGE_LINES)},this.updateText=function(){this.$loop.schedule(this.CHANGE_TEXT)},this.updateFull=function(){this.$loop.schedule(this.CHANGE_FULL)},this.updateFontSize=function(){this.$textLayer.checkForSizeChanges()},this.onResize=function(a){var b=this.CHANGE_SIZE,c=this.$size,d=e.getInnerHeight(this.container);if(a||c.height!=d)c.height=d,this.scroller.style.height=d+"px",c.scrollerHeight=this.scroller.clientHeight,this.scrollBar.setHeight(c.scrollerHeight),this.session&&(this.session.setScrollTop(this.getScrollTop()),b|=this.CHANGE_FULL);var f=e.getInnerWidth(this.container);if(a||c.width!=f){c.width=f;var g=this.showGutter?this.$gutter.offsetWidth:0;this.scroller.style.left=g+"px",c.scrollerWidth=Math.max(0,f-g-this.scrollBar.getWidth()),this.scroller.style.width=c.scrollerWidth+"px";if(this.session.getUseWrapMode()&&this.adjustWrapLimit()||a)b|=this.CHANGE_FULL}this.$loop.schedule(b)},this.adjustWrapLimit=function(){var a=this.$size.scrollerWidth-this.$padding*2,b=Math.floor(a/this.characterWidth);return this.session.adjustWrapLimit(b)},this.setAnimatedScroll=function(a){this.$animatedScroll=a},this.getAnimatedScroll=function(){return this.$animatedScroll},this.setShowInvisibles=function(a){this.$textLayer.setShowInvisibles(a)&&this.$loop.schedule(this.CHANGE_TEXT)},this.getShowInvisibles=function(){return this.$textLayer.showInvisibles},this.$showPrintMargin=!0,this.setShowPrintMargin=function(a){this.$showPrintMargin=a,this.$updatePrintMargin()},this.getShowPrintMargin=function(){return this.$showPrintMargin},this.$printMarginColumn=80,this.setPrintMarginColumn=function(a){this.$printMarginColumn=a,this.$updatePrintMargin()},this.getPrintMarginColumn=function(){return this.$printMarginColumn},this.getShowGutter=function(){return this.showGutter},this.setShowGutter=function(a){if(this.showGutter===a)return;this.$gutter.style.display=a?"block":"none",this.showGutter=a,this.onResize(!0)},this.$updatePrintMargin=function(){var a;if(!this.$showPrintMargin&&!this.$printMarginEl)return;this.$printMarginEl||(a=e.createElement("div"),a.className="ace_print_margin_layer",this.$printMarginEl=e.createElement("div"),this.$printMarginEl.className="ace_print_margin",a.appendChild(this.$printMarginEl),this.content.insertBefore(a,this.$textLayer.element));var b=this.$printMarginEl.style;b.left=this.characterWidth*this.$printMarginColumn+this.$padding+"px",b.visibility=this.$showPrintMargin?"visible":"hidden"},this.getContainerElement=function(){return this.container},this.getMouseEventTarget=function(){return this.content},this.getTextAreaContainer=function(){return this.container},this.moveTextAreaToCursor=function(a){if(g.isIE)return;if(this.layerConfig.lastRow===0)return;var b=this.$cursorLayer.getPixelPosition();if(!b)return;var c=this.content.getBoundingClientRect(),d=this.layerConfig.offset;a.style.left=c.left+b.left+"px",a.style.top=c.top+b.top-this.scrollTop+d+"px"},this.getFirstVisibleRow=function(){return this.layerConfig.firstRow},this.getFirstFullyVisibleRow=function(){return this.layerConfig.firstRow+(this.layerConfig.offset===0?0:1)},this.getLastFullyVisibleRow=function(){var a=Math.floor((this.layerConfig.height+this.layerConfig.offset)/this.layerConfig.lineHeight);return this.layerConfig.firstRow-1+a},this.getLastVisibleRow=function(){return this.layerConfig.lastRow},this.$padding=null,this.setPadding=function(a){this.$padding=a,this.$textLayer.setPadding(a),this.$cursorLayer.setPadding(a),this.$markerFront.setPadding(a),this.$markerBack.setPadding(a),this.$loop.schedule(this.CHANGE_FULL),this.$updatePrintMargin()},this.getHScrollBarAlwaysVisible=function(){return this.$horizScrollAlwaysVisible},this.setHScrollBarAlwaysVisible=function(a){this.$horizScrollAlwaysVisible!=a&&(this.$horizScrollAlwaysVisible=a,(!this.$horizScrollAlwaysVisible||!this.$horizScroll)&&this.$loop.schedule(this.CHANGE_SCROLL))},this.$updateScrollBar=function(){this.scrollBar.setInnerHeight(this.layerConfig.maxHeight),this.scrollBar.setScrollTop(this.scrollTop)},this.$renderChanges=function(a){if(!a||!this.session||!this.container.offsetWidth)return;(a&this.CHANGE_FULL||a&this.CHANGE_SIZE||a&this.CHANGE_TEXT||a&this.CHANGE_LINES||a&this.CHANGE_SCROLL)&&this.$computeLayerConfig();if(a&this.CHANGE_H_SCROLL){this.scroller.scrollLeft=this.scrollLeft;var b=this.scroller.scrollLeft;this.scrollLeft=b,this.session.setScrollLeft(b)}if(a&this.CHANGE_FULL){this.$textLayer.checkForSizeChanges(),this.$updateScrollBar(),this.$textLayer.update(this.layerConfig),this.showGutter&&this.$gutterLayer.update(this.layerConfig),this.$markerBack.update(this.layerConfig),this.$markerFront.update(this.layerConfig),this.$cursorLayer.update(this.layerConfig);return}if(a&this.CHANGE_SCROLL){this.$updateScrollBar(),a&this.CHANGE_TEXT||a&this.CHANGE_LINES?this.$textLayer.update(this.layerConfig):this.$textLayer.scrollLines(this.layerConfig),this.showGutter&&this.$gutterLayer.update(this.layerConfig),this.$markerBack.update(this.layerConfig),this.$markerFront.update(this.layerConfig),this.$cursorLayer.update(this.layerConfig);return}a&this.CHANGE_TEXT?(this.$textLayer.update(this.layerConfig),this.showGutter&&this.$gutterLayer.update(this.layerConfig)):a&this.CHANGE_LINES?this.$updateLines()&&(this.$updateScrollBar(),this.showGutter&&this.$gutterLayer.update(this.layerConfig)):a&this.CHANGE_GUTTER&&this.showGutter&&this.$gutterLayer.update(this.layerConfig),a&this.CHANGE_CURSOR&&this.$cursorLayer.update(this.layerConfig),a&(this.CHANGE_MARKER|this.CHANGE_MARKER_FRONT)&&this.$markerFront.update(this.layerConfig),a&(this.CHANGE_MARKER|this.CHANGE_MARKER_BACK)&&this.$markerBack.update(this.layerConfig),a&this.CHANGE_SIZE&&this.$updateScrollBar()},this.$computeLayerConfig=function(){var a=this.session,b=this.scrollTop%this.lineHeight,c=this.$size.scrollerHeight+this.lineHeight,d=this.$getLongestLine(),e=this.$horizScrollAlwaysVisible||this.$size.scrollerWidth-d<0,f=this.$horizScroll!==e;this.$horizScroll=e,f&&(this.scroller.style.overflowX=e?"scroll":"hidden",e||this.session.setScrollLeft(0));var g=this.session.getScreenLength()*this.lineHeight;this.session.setScrollTop(Math.max(0,Math.min(this.scrollTop,g-this.$size.scrollerHeight)));var h=Math.ceil(c/this.lineHeight)-1,i=Math.max(0,Math.round((this.scrollTop-b)/this.lineHeight)),j=i+h,k,l,m={lineHeight:this.lineHeight};i=a.screenToDocumentRow(i,0);var n=a.getFoldLine(i);n&&(i=n.start.row),k=a.documentToScreenRow(i,0),l=a.getRowHeight(m,i),j=Math.min(a.screenToDocumentRow(j,0),a.getLength()-1),c=this.$size.scrollerHeight+a.getRowHeight(m,j)+l,b=this.scrollTop-k*this.lineHeight,this.layerConfig={width:d,padding:this.$padding,firstRow:i,firstRowScreen:k,lastRow:j,lineHeight:this.lineHeight,characterWidth:this.characterWidth,minHeight:c,maxHeight:g,offset:b,height:this.$size.scrollerHeight},this.$gutterLayer.element.style.marginTop=-b+"px",this.content.style.marginTop=-b+"px",this.content.style.width=d+2*this.$padding+"px",this.content.style.height=c+"px",f&&this.onResize(!0)},this.$updateLines=function(){var a=this.$changedLines.firstRow,b=this.$changedLines.lastRow;this.$changedLines=null;var c=this.layerConfig;if(c.width!=this.$getLongestLine())return this.$textLayer.update(c);if(a>c.lastRow+1)return;if(b<c.firstRow)return;if(b===Infinity){this.showGutter&&this.$gutterLayer.update(c),this.$textLayer.update(c);return}return this.$textLayer.updateLines(c,a,b),!0},this.$getLongestLine=function(){var a=this.session.getScreenWidth();return this.$textLayer.showInvisibles&&(a+=1),Math.max(this.$size.scrollerWidth-2*this.$padding,Math.round(a*this.characterWidth))},this.updateFrontMarkers=function(){this.$markerFront.setMarkers(this.session.getMarkers(!0)),this.$loop.schedule(this.CHANGE_MARKER_FRONT)},this.updateBackMarkers=function(){this.$markerBack.setMarkers(this.session.getMarkers()),this.$loop.schedule(this.CHANGE_MARKER_BACK)},this.addGutterDecoration=function(a,b){this.$gutterLayer.addGutterDecoration(a,b),this.$loop.schedule(this.CHANGE_GUTTER)},this.removeGutterDecoration=function(a,b){this.$gutterLayer.removeGutterDecoration(a,b),this.$loop.schedule(this.CHANGE_GUTTER)},this.setBreakpoints=function(a){this.$gutterLayer.setBreakpoints(a),this.$loop.schedule(this.CHANGE_GUTTER)},this.setAnnotations=function(a){this.$gutterLayer.setAnnotations(a),this.$loop.schedule(this.CHANGE_GUTTER)},this.updateCursor=function(){this.$loop.schedule(this.CHANGE_CURSOR)},this.hideCursor=function(){this.$cursorLayer.hideCursor()},this.showCursor=function(){this.$cursorLayer.showCursor()},this.scrollSelectionIntoView=function(a,b){this.scrollCursorIntoView(a),this.scrollCursorIntoView(b)},this.scrollCursorIntoView=function(a){if(this.$size.scrollerHeight===0)return;var b=this.$cursorLayer.getPixelPosition(a),c=b.left,d=b.top;this.scrollTop>d&&this.session.setScrollTop(d),this.scrollTop+this.$size.scrollerHeight<d+this.lineHeight&&this.session.setScrollTop(d+this.lineHeight-this.$size.scrollerHeight);var e=this.scrollLeft;e>c&&(c<this.$padding+2*this.layerConfig.characterWidth&&(c=0),this.session.setScrollLeft(c)),e+this.$size.scrollerWidth<c+this.characterWidth&&this.session.setScrollLeft(Math.round(c+this.characterWidth-this.$size.scrollerWidth))},this.getScrollTop=function(){return this.session.getScrollTop()},this.getScrollLeft=function(){return this.session.getScrollLeft()},this.getScrollTopRow=function(){return this.scrollTop/this.lineHeight},this.getScrollBottomRow=function(){return Math.max(0,Math.floor((this.scrollTop+this.$size.scrollerHeight)/this.lineHeight)-1)},this.scrollToRow=function(a){this.session.setScrollTop(a*this.lineHeight)},this.STEPS=10,this.$calcSteps=function(a,b){var c=0,d=this.STEPS,e=[],f=function(a,b,c){return(a/=.5)<1?c/2*Math.pow(a,3)+b:c/2*(Math.pow(a-2,3)+2)+b};for(c=0;c<d;++c)e.push(f(c/this.STEPS,a,b-a));return e.push(b),e},this.scrollToLine=function(a,b){var c=this.$cursorLayer.getPixelPosition({row:a,column:0}),d=c.top;b&&(d-=this.$size.scrollerHeight/2);if(this.$animatedScroll&&Math.abs(d-this.scrollTop)<1e4){var e=this,f=e.$calcSteps(this.scrollTop,d);clearInterval(this.$timer),this.$timer=setInterval(function(){e.session.setScrollTop(f.shift()),f.length||clearInterval(e.$timer)},10)}else this.session.setScrollTop(d)},this.scrollToY=function(a){this.scrollTop!==a&&(this.$loop.schedule(this.CHANGE_SCROLL),this.scrollTop=a)},this.scrollToX=function(a){a<=this.$padding&&(a=0),this.scrollLeft!==a&&(this.scrollLeft=a),this.$loop.schedule(this.CHANGE_H_SCROLL)},this.scrollBy=function(a,b){b&&this.session.setScrollTop(this.session.getScrollTop()+b),a&&this.session.setScrollLeft(this.session.getScrollLeft()+a)},this.isScrollableBy=function(a,b){if(b<0&&this.session.getScrollTop()>0)return!0;if(b>0&&this.session.getScrollTop()+this.$size.scrollerHeight<this.layerConfig.maxHeight)return!0},this.pixelToScreenCoordinates=function(a,b){var c=this.scroller.getBoundingClientRect(),d=Math.round((a+this.scrollLeft-c.left-this.$padding-e.getPageScrollLeft())/this.characterWidth),f=Math.floor((b+this.scrollTop-c.top-e.getPageScrollTop())/this.lineHeight);return{row:f,column:d}},this.screenToTextCoordinates=function(a,b){var c=this.scroller.getBoundingClientRect(),d=Math.round((a+this.scrollLeft-c.left-this.$padding-e.getPageScrollLeft())/this.characterWidth),f=Math.floor((b+this.scrollTop-c.top-e.getPageScrollTop())/this.lineHeight);return this.session.screenToDocumentPosition(f,Math.max(d,0))},this.textToScreenCoordinates=function(a,b){var c=this.scroller.getBoundingClientRect(),d=this.session.documentToScreenPosition(a,b),e=this.$padding+Math.round(d.column*this.characterWidth),f=d.row*this.lineHeight;return{pageX:c.left+e-this.scrollLeft,pageY:c.top+f-this.scrollTop}},this.visualizeFocus=function(){e.addCssClass(this.container,"ace_focus")},this.visualizeBlur=function(){e.removeCssClass(this.container,"ace_focus")},this.showComposition=function(a){this.$composition||(this.$composition=e.createElement("div"),this.$composition.className="ace_composition",this.content.appendChild(this.$composition)),this.$composition.innerHTML=" ";var b=this.$cursorLayer.getPixelPosition(),c=this.$composition.style;c.top=b.top+"px",c.left=b.left+this.$padding+"px",c.height=this.lineHeight+"px",this.hideCursor()},this.setCompositionText=function(a){e.setInnerText(this.$composition,a)},this.hideComposition=function(){this.showCursor();if(!this.$composition)return;var a=this.$composition.style;a.top="-10000px",a.left="-10000px"},this._loadTheme=function(a,b){if(!h.get("packaged"))return b();var c=a.split("/").pop(),d=h.get("themePath")+"/theme-"+c+h.get("suffix");i.loadScript(d,b)},this.setTheme=function(b){function h(a){e.importCssString(a.cssText,a.cssClass,c.container.ownerDocument),c.$theme&&e.removeCssClass(c.container,c.$theme),c.$theme=a?a.cssClass:null,c.$theme&&e.addCssClass(c.container,c.$theme),a&&a.isDark?e.addCssClass(c.container,"ace_dark"):e.removeCssClass(c.container,"ace_dark"),c.$size&&(c.$size.width=0,c.onResize())}var c=this;this.$themeValue=b;if(!b||typeof b=="string"){var d=b||"ace/theme/textmate",f;try{f=a(d)}catch(g){}if(f)return h(f);c._loadTheme(d,function(){a([d],function(a){if(c.$themeValue!==b)return;h(a)})})}else h(b)},this.getTheme=function(){return this.$themeValue},this.setStyle=function(b){e.addCssClass(this.container,b)},this.unsetStyle=function(b){e.removeCssClass(this.container,b)},this.destroy=function(){this.$textLayer.destroy(),this.$cursorLayer.destroy()}}).call(r.prototype),b.VirtualRenderer=r}),define("ace/layer/gutter",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/event_emitter"],function(a,b,c){"use strict";var d=a("../lib/dom"),e=a("../lib/oop"),f=a("../lib/event_emitter").EventEmitter,g=function(a){this.element=d.createElement("div"),this.element.className="ace_layer ace_gutter-layer",a.appendChild(this.element),this.setShowFoldWidgets(this.$showFoldWidgets),this.gutterWidth=0,this.$breakpoints=[],this.$annotations=[],this.$decorations=[]};(function(){e.implement(this,f),this.setSession=function(a){this.session=a},this.addGutterDecoration=function(a,b){this.$decorations[a]||(this.$decorations[a]=""),this.$decorations[a]+=" "+b},this.removeGutterDecoration=function(a,b){this.$decorations[a]=this.$decorations[a].replace(" "+b,"")},this.setBreakpoints=function(a){this.$breakpoints=a.concat()},this.setAnnotations=function(a){this.$annotations=[];for(var b in a)if(a.hasOwnProperty(b)){var c=a[b];if(!c)continue;var d=this.$annotations[b]={text:[]};for(var e=0;e<c.length;e++){var f=c[e],g=f.text.replace(/"/g,""").replace(/'/g,"’").replace(/</,"<");d.text.indexOf(g)===-1&&d.text.push(g);var h=f.type;h=="error"?d.className="ace_error":h=="warning"&&d.className!="ace_error"?d.className="ace_warning":h=="info"&&!d.className&&(d.className="ace_info")}}},this.update=function(a){this.$config=a;var b={className:"",text:[]},c=[],e=a.firstRow,f=a.lastRow,g=this.session.getNextFoldLine(e),h=g?g.start.row:Infinity,i=this.$showFoldWidgets&&this.session.foldWidgets;for(;;){e>h&&(e=g.end.row+1,g=this.session.getNextFoldLine(e,g),h=g?g.start.row:Infinity);if(e>f)break;var j=this.$annotations[e]||b;c.push("<div class='ace_gutter-cell",this.$decorations[e]||"",this.$breakpoints[e]?" ace_breakpoint ":" ",j.className,"' title='",j.text.join("\n"),"' style='height:",a.lineHeight,"px;'>",e+1);if(i){var k=i[e];k==null&&(k=i[e]=this.session.getFoldWidget(e)),k&&c.push("<span class='ace_fold-widget ",k,k=="start"&&e==h&&e<g.end.row?" closed":" open","'></span>")}var l=this.session.getRowLength(e)-1;while(l--)c.push("</div><div class='ace_gutter-cell' style='height:",a.lineHeight,"px'>¦");c.push("</div>"),e++}this.element=d.setInnerHtml(this.element,c.join("")),this.element.style.height=a.minHeight+"px";var m=this.element.offsetWidth;m!==this.gutterWidth&&(this.gutterWidth=m,this._emit("changeGutterWidth",m))},this.$showFoldWidgets=!0,this.setShowFoldWidgets=function(a){a?d.addCssClass(this.element,"ace_folding-enabled"):d.removeCssClass(this.element,"ace_folding-enabled"),this.$showFoldWidgets=a},this.getShowFoldWidgets=function(){return this.$showFoldWidgets}}).call(g.prototype),b.Gutter=g}),define("ace/layer/marker",["require","exports","module","ace/range","ace/lib/dom"],function(a,b,c){"use strict";var d=a("../range").Range,e=a("../lib/dom"),f=function(a){this.element=e.createElement("div"),this.element.className="ace_layer ace_marker-layer",a.appendChild(this.element)};(function(){this.$padding=0,this.setPadding=function(a){this.$padding=a},this.setSession=function(a){this.session=a},this.setMarkers=function(a){this.markers=a},this.update=function(a){var a=a||this.config;if(!a)return;this.config=a;var b=[];for(var c in this.markers){var d=this.markers[c],f=d.range.clipRows(a.firstRow,a.lastRow);if(f.isEmpty())continue;f=f.toScreenRange(this.session);if(d.renderer){var g=this.$getTop(f.start.row,a),h=Math.round(this.$padding+f.start.column*a.characterWidth);d.renderer(b,f,h,g,a)}else f.isMultiLine()?d.type=="text"?this.drawTextMarker(b,f,d.clazz,a):this.drawMultiLineMarker(b,f,d.clazz,a,d.type):this.drawSingleLineMarker(b,f,d.clazz,a,null,d.type)}this.element=e.setInnerHtml(this.element,b.join(""))},this.$getTop=function(a,b){return(a-b.firstRowScreen)*b.lineHeight},this.drawTextMarker=function(a,b,c,e){var f=b.start.row,g=new d(f,b.start.column,f,this.session.getScreenLastRowColumn(f));this.drawSingleLineMarker(a,g,c,e,1,"text"),f=b.end.row,g=new d(f,0,f,b.end.column),this.drawSingleLineMarker(a,g,c,e,0,"text");for(f=b.start.row+1;f<b.end.row;f++)g.start.row=f,g.end.row=f,g.end.column=this.session.getScreenLastRowColumn(f),this.drawSingleLineMarker(a,g,c,e,1,"text")},this.drawMultiLineMarker=function(a,b,c,d,e){var f=e==="background"?0:this.$padding,g=d.width+2*this.$padding-f,h=d.lineHeight,i=Math.round(g-b.start.column*d.characterWidth),j=this.$getTop(b.start.row,d),k=Math.round(f+b.start.column*d.characterWidth);a.push("<div class='",c,"' style='","height:",h,"px;","width:",i,"px;","top:",j,"px;","left:",k,"px;'></div>"),j=this.$getTop(b.end.row,d),i=Math.round(b.end.column*d.characterWidth),a.push("<div class='",c,"' style='","height:",h,"px;","width:",i,"px;","top:",j,"px;","left:",f,"px;'></div>"),h=(b.end.row-b.start.row-1)*d.lineHeight;if(h<0)return;j=this.$getTop(b.start.row+1,d),a.push("<div class='",c,"' style='","height:",h,"px;","width:",g,"px;","top:",j,"px;","left:",f,"px;'></div>")},this.drawSingleLineMarker=function(a,b,c,d,e,f){var g=f==="background"?0:this.$padding,h=d.lineHeight;if(f==="background")var i=d.width;else i=Math.round((b.end.column+(e||0)-b.start.column)*d.characterWidth);var j=this.$getTop(b.start.row,d),k=Math.round(g+b.start.column*d.characterWidth);a.push("<div class='",c,"' style='","height:",h,"px;","width:",i,"px;","top:",j,"px;","left:",k,"px;'></div>")}}).call(f.prototype),b.Marker=f}),define("ace/layer/text",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/lib/event_emitter"],function(a,b,c){"use strict";var d=a("../lib/oop"),e=a("../lib/dom"),f=a("../lib/lang"),g=a("../lib/useragent"),h=a("../lib/event_emitter").EventEmitter,i=function(a){this.element=e.createElement("div"),this.element.className="ace_layer ace_text-layer",a.appendChild(this.element),this.$characterSize=this.$measureSizes()||{width:0,height:0},this.$pollSizeChanges()};(function(){d.implement(this,h),this.EOF_CHAR="¶",this.EOL_CHAR="¬",this.TAB_CHAR="→",this.SPACE_CHAR="·",this.$padding=0,this.setPadding=function(a){this.$padding=a,this.element.style.padding="0 "+a+"px"},this.getLineHeight=function(){return this.$characterSize.height||1},this.getCharacterWidth=function(){return this.$characterSize.width||1},this.checkForSizeChanges=function(){var a=this.$measureSizes();a&&(this.$characterSize.width!==a.width||this.$characterSize.height!==a.height)&&(this.$characterSize=a,this._emit("changeCharacterSize",{data:a}))},this.$pollSizeChanges=function(){var a=this;this.$pollSizeChangesTimer=setInterval(function(){a.checkForSizeChanges()},500)},this.$fontStyles={fontFamily:1,fontSize:1,fontWeight:1,fontStyle:1,lineHeight:1},this.$measureSizes=g.isIE||g.isOldGecko?function(){var a=1e3;if(!this.$measureNode){var b=this.$measureNode=e.createElement("div"),c=b.style;c.width=c.height="auto",c.left=c.top=-a*40+"px",c.visibility="hidden",c.position="fixed",c.overflow="visible",c.whiteSpace="nowrap",b.innerHTML=f.stringRepeat("Xy",a);if(this.element.ownerDocument.body)this.element.ownerDocument.body.appendChild(b);else{var d=this.element.parentNode;while(!e.hasCssClass(d,"ace_editor"))d=d.parentNode;d.appendChild(b)}}if(!this.element.offsetWidth)return null;var c=this.$measureNode.style,g=e.computedStyle(this.element);for(var h in this.$fontStyles)c[h]=g[h];var i={height:this.$measureNode.offsetHeight,width:this.$measureNode.offsetWidth/(a*2)};return i.width==0||i.height==0?null:i}:function(){if(!this.$measureNode){var a=this.$measureNode=e.createElement("div"),b=a.style;b.width=b.height="auto",b.left=b.top="-100px",b.visibility="hidden",b.position="fixed",b.overflow="visible",b.whiteSpace="nowrap",a.innerHTML="X";var c=this.element.parentNode;while(c&&!e.hasCssClass(c,"ace_editor"))c=c.parentNode;if(!c)return this.$measureNode=null;c.appendChild(a)}var d=this.$measureNode.getBoundingClientRect(),f={height:d.height,width:d.width};return f.width==0||f.height==0?null:f},this.setSession=function(a){this.session=a},this.showInvisibles=!1,this.setShowInvisibles=function(a){return this.showInvisibles==a?!1:(this.showInvisibles=a,!0)},this.$tabStrings=[],this.$computeTabString=function(){var a=this.session.getTabSize(),b=this.$tabStrings=[0];for(var c=1;c<a+1;c++)this.showInvisibles?b.push("<span class='ace_invisible'>"+this.TAB_CHAR+(new Array(c)).join(" ")+"</span>"):b.push((new Array(c+1)).join(" "))},this.updateLines=function(a,b,c){this.$computeTabString(),(this.config.lastRow!=a.lastRow||this.config.firstRow!=a.firstRow)&&this.scrollLines(a),this.config=a;var d=Math.max(b,a.firstRow),f=Math.min(c,a.lastRow),g=this.element.childNodes,h=0;for(var i=a.firstRow;i<d;i++){var j=this.session.getFoldLine(i);if(j){if(j.containsRow(d)){d=j.start.row;break}i=j.end.row}h++}for(var k=d;k<=f;k++){var l=g[h++];if(!l)continue;var m=[],n=this.session.getTokens(k,k);this.$renderLine(m,k,n[0].tokens,!this.$useLineGroups()),l=e.setInnerHtml(l,m.join("")),k=this.session.getRowFoldEnd(k)}},this.scrollLines=function(a){this.$computeTabString();var b=this.config;this.config=a;if(!b||b.lastRow<a.firstRow)return this.update(a);if(a.lastRow<b.firstRow)return this.update(a);var c=this.element;if(b.firstRow<a.firstRow)for(var d=this.session.getFoldedRowCount(b.firstRow,a.firstRow-1);d>0;d--)c.removeChild(c.firstChild);if(b.lastRow>a.lastRow)for(var d=this.session.getFoldedRowCount(a.lastRow+1,b.lastRow);d>0;d--)c.removeChild(c.lastChild);if(a.firstRow<b.firstRow){var e=this.$renderLinesFragment(a,a.firstRow,b.firstRow-1);c.firstChild?c.insertBefore(e,c.firstChild):c.appendChild(e)}if(a.lastRow>b.lastRow){var e=this.$renderLinesFragment(a,b.lastRow+1,a.lastRow);c.appendChild(e)}},this.$renderLinesFragment=function(a,b,c){var d=this.element.ownerDocument.createDocumentFragment(),f=b,g=this.session.getNextFoldLine(f),h=g?g.start.row:Infinity;for(;;){f>h&&(f=g.end.row+1,g=this.session.getNextFoldLine(f,g),h=g?g.start.row:Infinity);if(f>c)break;var i=e.createElement("div"),j=[],k=this.session.getTokens(f,f);k.length==1&&this.$renderLine(j,f,k[0].tokens,!1),i.innerHTML=j.join("");if(this.$useLineGroups())i.className="ace_line_group",d.appendChild(i);else{var l=i.childNodes;while(l.length)d.appendChild(l[0])}f++}return d},this.update=function(a){this.$computeTabString(),this.config=a;var b=[],c=a.firstRow,d=a.lastRow,f=c,g=this.session.getNextFoldLine(f),h=g?g.start.row:Infinity;for(;;){f>h&&(f=g.end.row+1,g=this.session.getNextFoldLine(f,g),h=g?g.start.row:Infinity);if(f>d)break;this.$useLineGroups()&&b.push("<div class='ace_line_group'>");var i=this.session.getTokens(f,f);i.length==1&&this.$renderLine(b,f,i[0].tokens,!1),this.$useLineGroups()&&b.push("</div>"),f++}this.element=e.setInnerHtml(this.element,b.join(""))},this.$textToken={text:!0,rparen:!0,lparen:!0},this.$renderToken=function(a,b,c,d){var e=this,f=/\t|&|<|( +)|([\v\f \u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000])|[\u1100-\u115F]|[\u11A3-\u11A7]|[\u11FA-\u11FF]|[\u2329-\u232A]|[\u2E80-\u2E99]|[\u2E9B-\u2EF3]|[\u2F00-\u2FD5]|[\u2FF0-\u2FFB]|[\u3000-\u303E]|[\u3041-\u3096]|[\u3099-\u30FF]|[\u3105-\u312D]|[\u3131-\u318E]|[\u3190-\u31BA]|[\u31C0-\u31E3]|[\u31F0-\u321E]|[\u3220-\u3247]|[\u3250-\u32FE]|[\u3300-\u4DBF]|[\u4E00-\uA48C]|[\uA490-\uA4C6]|[\uA960-\uA97C]|[\uAC00-\uD7A3]|[\uD7B0-\uD7C6]|[\uD7CB-\uD7FB]|[\uF900-\uFAFF]|[\uFE10-\uFE19]|[\uFE30-\uFE52]|[\uFE54-\uFE66]|[\uFE68-\uFE6B]|[\uFF01-\uFF60]|[\uFFE0-\uFFE6]/g,h=function(a,c,d,f,h){if(a.charCodeAt(0)==32)return(new Array(a.length+1)).join(" ");if(a==" "){var i=e.session.getScreenTabSize(b+f);return b+=i-1,e.$tabStrings[i]}if(a=="&")return g.isOldGecko?"&":"&";if(a=="<")return"<";if(a==" "){var j=e.showInvisibles?"ace_cjk ace_invisible":"ace_cjk",k=e.showInvisibles?e.SPACE_CHAR:"";return b+=1,"<span class='"+j+"' style='width:"+e.config.characterWidth*2+"px'>"+k+"</span>"}if(a.match(/[\v\f \u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000]/)){if(e.showInvisibles){var k=(new Array(a.length+1)).join(e.SPACE_CHAR);return"<span class='ace_invisible'>"+k+"</span>"}return" "}return b+=1,"<span class='ace_cjk' style='width:"+e.config.characterWidth*2+"px'>"+a+"</span>"},i=d.replace(f,h);if(!this.$textToken[c.type]){var j="ace_"+c.type.replace(/\./g," ace_"),k="";c.type=="fold"&&(k=" style='width:"+c.value.length*this.config.characterWidth+"px;' "),a.push("<span class='",j,"'",k,">",i,"</span>")}else a.push(i);return b+d.length},this.$renderLineCore=function(a,b,c,d,e){var f=0,g=0,h,i=0,j=this;!d||d.length==0?h=Number.MAX_VALUE:h=d[0],e||a.push("<div class='ace_line' style='height:",this.config.lineHeight,"px","'>");for(var k=0;k<c.length;k++){var l=c[k],m=l.value;if(f+m.length<h)i=j.$renderToken(a,i,l,m),f+=m.length;else{while(f+m.length>=h)i=j.$renderToken(a,i,l,m.substring(0,h-f)),m=m.substring(h-f),f=h,e||a.push("</div>","<div class='ace_line' style='height:",this.config.lineHeight,"px","'>"),g++,i=0,h=d[g]||Number.MAX_VALUE;m.length!=0&&(f+=m.length,i=j.$renderToken(a,i,l,m))}}this.showInvisibles&&(b!==this.session.getLength()-1?a.push("<span class='ace_invisible'>"+this.EOL_CHAR+"</span>"):a.push("<span class='ace_invisible'>"+this.EOF_CHAR+"</span>")),e||a.push("</div>")},this.$renderLine=function(a,b,c,d){if(!this.session.isRowFolded(b)){var e=this.session.getRowSplitData(b);this.$renderLineCore(a,b,c,e,d)}else this.$renderFoldLine(a,b,c,d)},this.$renderFoldLine=function(a,b,c,d){function h(a,b,c){var d=0,e=0;while(e+a[d].value.length<b){e+=a[d].value.length,d++;if(d==a.length)return}if(e!=b){var f=a[d].value.substring(b-e);f.length>c-b&&(f=f.substring(0,c-b)),g.push({type:a[d].type,value:f}),e=b+f.length,d+=1}while(e<c){var f=a[d].value;f.length+e>c&&(f=f.substring(0,c-e)),g.push({type:a[d].type,value:f}),e+=f.length,d+=1}}var e=this.session,f=e.getFoldLine(b),g=[];f.walk(function(a,b,d,e,f){a?g.push({type:"fold",value:a}):(f&&(c=this.session.getTokens(b,b)[0].tokens),c.length!=0&&h(c,e,d))}.bind(this),f.end.row,this.session.getLine(f.end.row).length);var i=this.session.$useWrapMode?this.session.$wrapData[b]:null;this.$renderLineCore(a,b,g,i,d)},this.$useLineGroups=function(){return this.session.getUseWrapMode()},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.$measureNode&&this.$measureNode.parentNode.removeChild(this.$measureNode),delete this.$measureNode}}).call(i.prototype),b.Text=i}),define("ace/layer/cursor",["require","exports","module","ace/lib/dom"],function(a,b,c){"use strict";var d=a("../lib/dom"),e=function(a){this.element=d.createElement("div"),this.element.className="ace_layer ace_cursor-layer",a.appendChild(this.element),this.isVisible=!1,this.cursors=[],this.cursor=this.addCursor()};(function(){this.$padding=0,this.setPadding=function(a){this.$padding=a},this.setSession=function(a){this.session=a},this.addCursor=function(){var a=d.createElement("div"),b="ace_cursor";return this.isVisible||(b+=" ace_hidden"),this.overwrite&&(b+=" ace_overwrite"),a.className=b,this.element.appendChild(a),this.cursors.push(a),a},this.removeCursor=function(){if(this.cursors.length>1){var a=this.cursors.pop();return a.parentNode.removeChild(a),a}},this.hideCursor=function(){this.isVisible=!1;for(var a=this.cursors.length;a--;)d.addCssClass(this.cursors[a],"ace_hidden");clearInterval(this.blinkId)},this.showCursor=function(){this.isVisible=!0;for(var a=this.cursors.length;a--;)d.removeCssClass(this.cursors[a],"ace_hidden");this.element.style.visibility="",this.restartTimer()},this.restartTimer=function(){clearInterval(this.blinkId);if(!this.isVisible)return;var a=this.element;this.blinkId=setInterval(function(){a.style.visibility="hidden",setTimeout(function(){a.style.visibility="visible"},400)},1e3)},this.getPixelPosition=function(a,b){if(!this.config||!this.session)return{left:0,top:0};a||(a=this.session.selection.getCursor());var c=this.session.documentToScreenPosition(a),d=Math.round(this.$padding+c.column*this.config.characterWidth),e=(c.row-(b?this.config.firstRowScreen:0))*this.config.lineHeight;return{left:d,top:e}},this.update=function(a){this.config=a;if(this.session.selectionMarkerCount>1){var b=this.session.$selectionMarkers,c=0,d,e=0;for(var c=b.length;c--;){d=b[c];var f=this.getPixelPosition(d.cursor,!0),g=(this.cursors[e++]||this.addCursor()).style;g.left=f.left+"px",g.top=f.top+"px",g.width=a.characterWidth+"px",g.height=a.lineHeight+"px"}if(e>1)while(this.cursors.length>e)this.removeCursor()}else{var f=this.getPixelPosition(null,!0),g=this.cursor.style;g.left=f.left+"px",g.top=f.top+"px",g.width=a.characterWidth+"px",g.height=a.lineHeight+"px";while(this.cursors.length>1)this.removeCursor()}var h=this.session.getOverwrite();h!=this.overwrite&&this.$setOverite(h),this.restartTimer()},this.$setOverite=function(a){this.overwrite=a;for(var b=this.cursors.length;b--;)a?d.addCssClass(this.cursors[b],"ace_overwrite"):d.removeCssClass(this.cursors[b],"ace_overwrite")},this.destroy=function(){clearInterval(this.blinkId)}}).call(e.prototype),b.Cursor=e}),define("ace/scrollbar",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"],function(a,b,c){"use strict";var d=a("./lib/oop"),e=a("./lib/dom"),f=a("./lib/event"),g=a("./lib/event_emitter").EventEmitter,h=function(a){this.element=e.createElement("div"),this.element.className="ace_sb",this.inner=e.createElement("div"),this.element.appendChild(this.inner),a.appendChild(this.element),this.width=e.scrollbarWidth(a.ownerDocument),this.element.style.width=(this.width||15)+5+"px",f.addListener(this.element,"scroll",this.onScroll.bind(this))};(function(){d.implement(this,g),this.onScroll=function(){this._emit("scroll",{data:this.element.scrollTop})},this.getWidth=function(){return this.width},this.setHeight=function(a){this.element.style.height=a+"px"},this.setInnerHeight=function(a){this.inner.style.height=a+"px"},this.setScrollTop=function(a){this.element.scrollTop=a}}).call(h.prototype),b.ScrollBar=h}),define("ace/renderloop",["require","exports","module","ace/lib/event"],function(a,b,c){"use strict";var d=a("./lib/event"),e=function(a,b){this.onRender=a,this.pending=!1,this.changes=0,this.window=b||window};(function(){this.schedule=function(a){this.changes=this.changes|a;if(!this.pending){this.pending=!0;var b=this;d.nextTick(function(){b.pending=!1;var a;while(a=b.changes)b.changes=0,b.onRender(a)},this.window)}}}).call(e.prototype),b.RenderLoop=e}),define("text!ace/css/editor.css",[],".ace_editor {\n position: absolute;\n overflow: hidden;\n font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Droid Sans Mono', 'Consolas', monospace;\n font-size: 12px;\n}\n\n.ace_scroller {\n position: absolute;\n overflow-x: scroll;\n overflow-y: hidden;\n}\n\n.ace_content {\n position: absolute;\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n -webkit-box-sizing: border-box;\n cursor: text;\n}\n\n.ace_composition {\n position: absolute;\n background: #555;\n color: #DDD;\n z-index: 4;\n}\n\n.ace_gutter {\n position: absolute;\n overflow : hidden;\n height: 100%;\n width: auto;\n cursor: default;\n z-index: 1000;\n}\n\n.ace_gutter.horscroll {\n box-shadow: 0px 0px 20px rgba(0,0,0,0.4);\n}\n\n.ace_gutter-cell {\n padding-left: 19px;\n padding-right: 6px;\n}\n\n.ace_gutter-cell.ace_error {\n background-image: url(\"data:image/gif,GIF89a%10%00%10%00%D5%00%00%F5or%F5%87%88%F5nr%F4ns%EBmq%F5z%7F%DDJT%DEKS%DFOW%F1Yc%F2ah%CE(7%CE)8%D18E%DD%40M%F2KZ%EBU%60%F4%60m%DCir%C8%16(%C8%19*%CE%255%F1%3FR%F1%3FS%E6%AB%B5%CA%5DI%CEn%5E%F7%A2%9A%C9G%3E%E0a%5B%F7%89%85%F5yy%F6%82%80%ED%82%80%FF%BF%BF%E3%C4%C4%FF%FF%FF%FF%FF%FF%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00!%F9%04%01%00%00%25%00%2C%00%00%00%00%10%00%10%00%00%06p%C0%92pH%2C%1A%8F%C8%D2H%93%E1d4%23%E4%88%D3%09mB%1DN%B48%F5%90%40%60%92G%5B%94%20%3E%22%D2%87%24%FA%20%24%C5%06A%00%20%B1%07%02B%A38%89X.v%17%82%11%13q%10%0Fi%24%0F%8B%10%7BD%12%0Ei%09%92%09%0EpD%18%15%24%0A%9Ci%05%0C%18F%18%0B%07%04%01%04%06%A0H%18%12%0D%14%0D%12%A1I%B3%B4%B5IA%00%3B\");\n background-repeat: no-repeat;\n background-position: 2px center;\n}\n\n.ace_gutter-cell.ace_warning {\n background-image: url(\"data:image/gif,GIF89a%10%00%10%00%D5%00%00%FF%DBr%FF%DE%81%FF%E2%8D%FF%E2%8F%FF%E4%96%FF%E3%97%FF%E5%9D%FF%E6%9E%FF%EE%C1%FF%C8Z%FF%CDk%FF%D0s%FF%D4%81%FF%D5%82%FF%D5%83%FF%DC%97%FF%DE%9D%FF%E7%B8%FF%CCl%7BQ%13%80U%15%82W%16%81U%16%89%5B%18%87%5B%18%8C%5E%1A%94d%1D%C5%83-%C9%87%2F%C6%84.%C6%85.%CD%8B2%C9%871%CB%8A3%CD%8B5%DC%98%3F%DF%9BB%E0%9CC%E1%A5U%CB%871%CF%8B5%D1%8D6%DB%97%40%DF%9AB%DD%99B%E3%B0p%E7%CC%AE%FF%FF%FF%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00!%F9%04%01%00%00%2F%00%2C%00%00%00%00%10%00%10%00%00%06a%C0%97pH%2C%1A%8FH%A1%ABTr%25%87%2B%04%82%F4%7C%B9X%91%08%CB%99%1C!%26%13%84*iJ9(%15G%CA%84%14%01%1A%97%0C%03%80%3A%9A%3E%81%84%3E%11%08%B1%8B%20%02%12%0F%18%1A%0F%0A%03'F%1C%04%0B%10%16%18%10%0B%05%1CF%1D-%06%07%9A%9A-%1EG%1B%A0%A1%A0U%A4%A5%A6BA%00%3B\");\n background-repeat: no-repeat;\n background-position: 2px center;\n}\n\n.ace_gutter-cell.ace_info {\n background-image: url(\"data:image/gif;base64,R0lGODlhEAAQAMQAAAAAAEFBQVJSUl5eXmRkZGtra39/f4WFhYmJiZGRkaampry8vMPDw8zMzNXV1dzc3OTk5Orq6vDw8P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABQALAAAAAAQABAAAAUuICWOZGmeaBml5XGwFCQSBGyXRSAwtqQIiRuiwIM5BoYVbEFIyGCQoeJGrVptIQA7\");\n background-repeat: no-repeat;\n background-position: 2px center;\n}\n\n.ace_editor .ace_sb {\n position: absolute;\n overflow-x: hidden;\n overflow-y: scroll;\n right: 0;\n}\n\n.ace_editor .ace_sb div {\n position: absolute;\n width: 1px;\n left: 0;\n}\n\n.ace_editor .ace_print_margin_layer {\n z-index: 0;\n position: absolute;\n overflow: hidden;\n margin: 0;\n left: 0;\n height: 100%;\n width: 100%;\n}\n\n.ace_editor .ace_print_margin {\n position: absolute;\n height: 100%;\n}\n\n.ace_editor textarea {\n position: fixed;\n z-index: 0;\n width: 10px;\n height: 30px;\n opacity: 0;\n background: transparent;\n appearance: none;\n -moz-appearance: none;\n border: none;\n resize: none;\n outline: none;\n overflow: hidden;\n}\n\n.ace_layer {\n z-index: 1;\n position: absolute;\n overflow: hidden;\n white-space: nowrap;\n height: 100%;\n width: 100%;\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n -webkit-box-sizing: border-box;\n /* setting pointer-events: auto; on node under the mouse, which changes\n during scroll, will break mouse wheel scrolling in Safari */\n pointer-events: none;\n}\n\n.ace_gutter .ace_layer {\n position: relative;\n min-width: 40px;\n text-align: right;\n pointer-events: auto;\n}\n\n.ace_text-layer {\n color: black;\n}\n\n.ace_cjk {\n display: inline-block;\n text-align: center;\n}\n\n.ace_cursor-layer {\n z-index: 4;\n}\n\n.ace_cursor {\n z-index: 4;\n position: absolute;\n}\n\n.ace_cursor.ace_hidden {\n opacity: 0.2;\n}\n\n.ace_line {\n white-space: nowrap;\n}\n\n.ace_marker-layer .ace_step {\n position: absolute;\n z-index: 3;\n}\n\n.ace_marker-layer .ace_selection {\n position: absolute;\n z-index: 5;\n}\n\n.ace_marker-layer .ace_bracket {\n position: absolute;\n z-index: 6;\n}\n\n.ace_marker-layer .ace_active_line {\n position: absolute;\n z-index: 2;\n}\n\n.ace_gutter .ace_gutter_active_line{\n background-color : #dcdcdc;\n}\n\n.ace_marker-layer .ace_selected_word {\n position: absolute;\n z-index: 4;\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n -webkit-box-sizing: border-box;\n}\n\n.ace_line .ace_fold {\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n -webkit-box-sizing: border-box;\n \n display: inline-block;\n height: 11px;\n margin-top: -2px;\n vertical-align: middle;\n\n background-image: \n url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%11%00%00%00%09%08%06%00%00%00%D4%E8%C7%0C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%B5IDAT(%15%A5%91%3D%0E%02!%10%85ac%E1%05%D6%CE%D6%C6%CE%D2%E8%ED%CD%DE%C0%C6%D6N.%E0V%F8%3D%9Ca%891XH%C2%BE%D9y%3F%90!%E6%9C%C3%BFk%E5%011%C6-%F5%C8N%04%DF%BD%FF%89%DFt%83DN%60%3E%F3%AB%A0%DE%1A%5Dg%BE%10Q%97%1B%40%9C%A8o%10%8F%5E%828%B4%1B%60%87%F6%02%26%85%1Ch%1E%C1%2B%5Bk%FF%86%EE%B7j%09%9A%DA%9B%ACe%A3%F9%EC%DA!9%B4%D5%A6%81%86%86%98%CC%3C%5B%40%FA%81%B3%E9%CB%23%94%C16Azo%05%D4%E1%C1%95a%3B%8A'%A0%E8%CC%17%22%85%1D%BA%00%A2%FA%DC%0A%94%D1%D1%8D%8B%3A%84%17B%C7%60%1A%25Z%FC%8D%00%00%00%00IEND%AEB%60%82\"),\n url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%007%08%06%00%00%00%C4%DD%80C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%3AIDAT8%11c%FC%FF%FF%7F%18%03%1A%60%01%F2%3F%A0%891%80%04%FF%11-%F8%17%9BJ%E2%05%B1ZD%81v%26t%E7%80%F8%A3%82h%A12%1A%20%A3%01%02%0F%01%BA%25%06%00%19%C0%0D%AEF%D5%3ES%00%00%00%00IEND%AEB%60%82\");\n background-repeat: no-repeat, repeat-x;\n background-position: center center, top left;\n color: transparent;\n\n border: 1px solid black;\n -moz-border-radius: 2px;\n -webkit-border-radius: 2px;\n border-radius: 2px;\n \n cursor: pointer;\n pointer-events: auto;\n}\n\n.ace_dark .ace_fold {\n}\n\n.ace_fold:hover{\n background-image: \n url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%11%00%00%00%09%08%06%00%00%00%D4%E8%C7%0C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%B5IDAT(%15%A5%91%3D%0E%02!%10%85ac%E1%05%D6%CE%D6%C6%CE%D2%E8%ED%CD%DE%C0%C6%D6N.%E0V%F8%3D%9Ca%891XH%C2%BE%D9y%3F%90!%E6%9C%C3%BFk%E5%011%C6-%F5%C8N%04%DF%BD%FF%89%DFt%83DN%60%3E%F3%AB%A0%DE%1A%5Dg%BE%10Q%97%1B%40%9C%A8o%10%8F%5E%828%B4%1B%60%87%F6%02%26%85%1Ch%1E%C1%2B%5Bk%FF%86%EE%B7j%09%9A%DA%9B%ACe%A3%F9%EC%DA!9%B4%D5%A6%81%86%86%98%CC%3C%5B%40%FA%81%B3%E9%CB%23%94%C16Azo%05%D4%E1%C1%95a%3B%8A'%A0%E8%CC%17%22%85%1D%BA%00%A2%FA%DC%0A%94%D1%D1%8D%8B%3A%84%17B%C7%60%1A%25Z%FC%8D%00%00%00%00IEND%AEB%60%82\"),\n url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%007%08%06%00%00%00%C4%DD%80C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%003IDAT8%11c%FC%FF%FF%7F%3E%03%1A%60%01%F2%3F%A3%891%80%04%FFQ%26%F8w%C0%B43%A1%DB%0C%E2%8F%0A%A2%85%CAh%80%8C%06%08%3C%04%E8%96%18%00%A3S%0D%CD%CF%D8%C1%9D%00%00%00%00IEND%AEB%60%82\");\n background-repeat: no-repeat, repeat-x;\n background-position: center center, top left;\n}\n\n.ace_dragging .ace_content {\n cursor: move;\n}\n\n.ace_folding-enabled > .ace_gutter-cell {\n padding-right: 13px;\n}\n\n.ace_fold-widget {\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n -webkit-box-sizing: border-box;\n\n margin: 0 -12px 1px 1px;\n display: inline-block;\n height: 14px;\n width: 11px;\n vertical-align: text-bottom;\n \n background-image: url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%00%05%08%06%00%00%00%8Do%26%E5%00%00%004IDATx%DAe%8A%B1%0D%000%0C%C2%F2%2CK%96%BC%D0%8F9%81%88H%E9%D0%0E%96%C0%10%92%3E%02%80%5E%82%E4%A9*-%EEsw%C8%CC%11%EE%96w%D8%DC%E9*Eh%0C%151(%00%00%00%00IEND%AEB%60%82\");\n background-repeat: no-repeat;\n background-position: center 5px;\n\n border-radius: 3px;\n}\n\n.ace_fold-widget.end {\n background-image: url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%00%05%08%06%00%00%00%8Do%26%E5%00%00%004IDATx%DAm%C7%C1%09%000%08C%D1%8C%ECE%C8E(%8E%EC%02)%1EZJ%F1%C1'%04%07I%E1%E5%EE%CAL%F5%A2%99%99%22%E2%D6%1FU%B5%FE0%D9x%A7%26Wz5%0E%D5%00%00%00%00IEND%AEB%60%82\");\n}\n\n.ace_fold-widget.closed {\n background-image: url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%03%00%00%00%06%08%06%00%00%00%06%E5%24%0C%00%00%009IDATx%DA5%CA%C1%09%000%08%03%C0%AC*(%3E%04%C1%0D%BA%B1%23%A4Uh%E0%20%81%C0%CC%F8%82%81%AA%A2%AArGfr%88%08%11%11%1C%DD%7D%E0%EE%5B%F6%F6%CB%B8%05Q%2F%E9tai%D9%00%00%00%00IEND%AEB%60%82\");\n}\n\n.ace_fold-widget:hover {\n border: 1px solid rgba(0, 0, 0, 0.3);\n background-color: rgba(255, 255, 255, 0.2);\n -moz-box-shadow:inset 0 1px 1px rgba(255, 255, 255, 0.7);\n -moz-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);\n -webkit-box-shadow:inset 0 1px 1px rgba(255, 255, 255, 0.7);\n -webkit-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);\n box-shadow:inset 0 1px 1px rgba(255, 255, 255, 0.7);\n box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);\n background-position: center 4px;\n}\n\n.ace_fold-widget:active {\n border: 1px solid rgba(0, 0, 0, 0.4);\n background-color: rgba(0, 0, 0, 0.05);\n -moz-box-shadow:inset 0 1px 1px rgba(255, 255, 255);\n -moz-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);\n -webkit-box-shadow:inset 0 1px 1px rgba(255, 255, 255);\n -webkit-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);\n box-shadow:inset 0 1px 1px rgba(255, 255, 255);\n box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);\n}\n\n.ace_fold-widget.invalid {\n background-color: #FFB4B4;\n border-color: #DE5555;\n}\n"),define("ace/multi_select",["require","exports","module","ace/range_list","ace/range","ace/selection","ace/mouse/multi_select_handler","ace/commands/multi_select_commands","ace/search","ace/edit_session","ace/editor"],function(a,b,c){function j(a,b,c){return i.$options.wrap=!0,i.$options.needle=b,i.$options.backwards=c==-1,i.find(a)}function m(a,b){return a.row==b.row&&a.column==b.column}function n(a){a.$onAddRange=a.$onAddRange.bind(a),a.$onRemoveRange=a.$onRemoveRange.bind(a),a.$onMultiSelect=a.$onMultiSelect.bind(a),a.$onSingleSelect=a.$onSingleSelect.bind(a),b.onSessionChange.call(a,a),a.on("changeSession",b.onSessionChange.bind(a)),a.on("mousedown",g),a.commands.addCommands(b.commands.defaultCommands)}var d=a("./range_list").RangeList,e=a("./range").Range,f=a("./selection").Selection,g=a("./mouse/multi_select_handler").onMouseDown;b.commands=a("./commands/multi_select_commands");var h=a("./search").Search,i=new h,k=a("./edit_session").EditSession;(function(){this.getSelectionMarkers=function(){return this.$selectionMarkers}}).call(k.prototype),function(){this.ranges=null,this.rangeList=null,this.addRange=function(a){if(!this.inMultiSelectMode&&this.rangeCount==0){var b=this.toOrientedRange();if(!a||!a.isEqual(b))this.rangeList.add(b),this.$onAddRange(b)}if(!a)return;a.cursor||(a.cursor=a.end);var c=this.rangeList.add(a);this.$onAddRange(a),c.length&&this.$onRemoveRange(c),this.rangeCount>0&&!this.inMultiSelectMode&&(this._emit("multiSelect"),this.inMultiSelectMode=!0,this.session.$undoSelect=!1,this.rangeList.attach(this.session))},this.toSingleRange=function(a){a=a||this.ranges[0];var b=this.rangeList.removeAll();b.length&&this.$onRemoveRange(b),a&&this.fromOrientedRange(a)},this.substractPoint=function(a){var b=this.rangeList.substractPoint(a);if(b)return this.$onRemoveRange(b),b[0]},this.mergeOverlappingRanges=function(){var a=this.rangeList.merge();a.length?this.$onRemoveRange(a):this.ranges[0]&&this.fromOrientedRange(this.ranges[0])},this.$onAddRange=function(a){this.rangeCount=this.rangeList.ranges.length,this.ranges.unshift(a),this.fromOrientedRange(a),this._emit("addRange",{range:a})},this.$onRemoveRange=function(a){this.rangeCount=this.rangeList.ranges.length;if(this.rangeCount==1&&this.inMultiSelectMode){var b=this.rangeList.ranges.pop();a.push(b),this.rangeCount=0}for(var c=a.length;c--;){var d=this.ranges.indexOf(a[c]);this.ranges.splice(d,1)}this._emit("removeRange",{ranges:a}),this.rangeCount==0&&this.inMultiSelectMode&&(this.inMultiSelectMode=!1,this._emit("singleSelect"),this.session.$undoSelect=!0,this.rangeList.detach(this.session)),b=b||this.ranges[0],b&&!b.isEqual(this.getRange())&&this.fromOrientedRange(b)},this.$initRangeList=function(){if(this.rangeList)return;this.rangeList=new d,this.ranges=[],this.rangeCount=0},this.getAllRanges=function(){return this.rangeList.ranges.concat()},this.splitIntoLines=function(){if(this.rangeCount>1){var a=this.rangeList.ranges,b=a[a.length-1],c=e.fromPoints(a[0].start,b.end);this.toSingleRange(),this.setSelectionRange(c,b.cursor==b.start)}else{var d=this.session.documentToScreenPosition(this.selectionLead),f=this.session.documentToScreenPosition(this.selectionAnchor),g=this.rectangularRangeBlock(d,f);g.forEach(this.addRange,this)}},this.rectangularRangeBlock=function(a,b,c){var d=[],f=a.column<b.column;if(f)var g=a.column,h=b.column;else var g=b.column,h=a.column;var i=a.row<b.row;if(i)var j=a.row,k=b.row;else var j=b.row,k=a.row;g<0&&(g=0),j<0&&(j=0),j==k&&(c=!0);for(var l=j;l<=k;l++){var n=e.fromPoints(this.session.screenToDocumentPosition(l,g),this.session.screenToDocumentPosition(l,h));if(n.isEmpty()){if(o&&m(n.end,o))break;var o=n.end}n.cursor=f?n.start:n.end,d.push(n)}i&&d.reverse();if(!c){var p=d.length-1;while(d[p].isEmpty()&&p>0)p--;if(p>0){var q=0;while(d[q].isEmpty())q++}for(var r=p;r>=q;r--)d[r].isEmpty()&&d.splice(r,1)}return d}}.call(f.prototype);var l=a("./editor").Editor;(function(){this.updateSelectionMarkers=function(){this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.addSelectionMarker=function(a){a.cursor||(a.cursor=a.end);var b=this.getSelectionStyle();return a.marker=this.session.addMarker(a,"ace_selection",b),this.session.$selectionMarkers.push(a),this.session.selectionMarkerCount=this.session.$selectionMarkers.length,a},this.removeSelectionMarkers=function(a){for(var b=a.length;b--;){var c=a[b];if(!c.marker)continue;this.session.removeMarker(c.marker);var d=this.session.$selectionMarkers.indexOf(c);d!=-1&&this.session.$selectionMarkers.splice(d,1)}this.session.selectionMarkerCount=this.session.$selectionMarkers.length},this.$onAddRange=function(a){this.addSelectionMarker(a.range),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onRemoveRange=function(a){this.removeSelectionMarkers(a.ranges),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelect=function(a){if(this.inMultiSelectMode)return;this.inMultiSelectMode=!0,this.setStyle("multiselect"),this.keyBinding.addKeyboardHandler(b.commands.keyboardHandler),this.commands.on("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onSingleSelect=function(a){if(this.session.multiSelect.inVirtualMode)return;this.inMultiSelectMode=!1,this.unsetStyle("multiselect"),this.keyBinding.removeKeyboardHandler(b.commands.keyboardHandler),this.commands.removeEventListener("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelectExec=function(a){var b=a.command,c=a.editor;b.multiSelectAction?b.multiSelectAction=="forEach"?c.forEachSelection(b,a.args):b.multiSelectAction=="single"?(c.exitMultiSelectMode(),b.exec(c,a.args||{})):b.multiSelectAction(c,a.args||{}):(b.exec(c,a.args||{}),c.multiSelect.addRange(c.multiSelect.toOrientedRange()),c.multiSelect.mergeOverlappingRanges()),a.preventDefault()},this.forEachSelection=function(a,b){if(this.inVirtualSelectionMode)return;var c=this.session,d=this.selection,e=d.rangeList,g=d._eventRegistry;d._eventRegistry={};var h=new f(c);this.inVirtualSelectionMode=!0;for(var i=e.ranges.length;i--;)h.fromOrientedRange(e.ranges[i]),this.selection=c.selection=h,a.exec(this,b||{}),h.toOrientedRange(e.ranges[i]);h.detach(),this.selection=c.selection=d,this.inVirtualSelectionMode=!1,d._eventRegistry=g,d.mergeOverlappingRanges(),this.onCursorChange(),this.onSelectionChange()},this.exitMultiSelectMode=function(){if(this.inVirtualSelectionMode)return;this.multiSelect.toSingleRange()},this.getCopyText=function(){var a="";if(this.inMultiSelectMode){var b=this.multiSelect.rangeList.ranges;a=[];for(var c=0;c<b.length;c++)a.push(this.session.getTextRange(b[c]));a=a.join(this.session.getDocument().getNewLineCharacter())}else this.selection.isEmpty()||(a=this.session.getTextRange(this.getSelectionRange()));return a},this.selectMoreLines=function(a,b){var c=this.selection.toOrientedRange(),d=c.cursor==c.end,f=this.session.documentToScreenPosition(c.cursor);this.selection.$desiredColumn&&(f.column=this.selection.$desiredColumn);var g=this.session.screenToDocumentPosition(f.row+a,f.column);if(!c.isEmpty())var h=this.session.documentToScreenPosition(d?c.end:c.start),i=this.session.screenToDocumentPosition(h.row+a,h.column);else var i=g;if(d){var j=e.fromPoints(g,i);j.cursor=j.start}else{var j=e.fromPoints(i,g);j.cursor=j.end}j.desiredColumn=f.column;if(!this.selection.inMultiSelectMode)this.selection.addRange(c);else if(b)var k=c.cursor;this.selection.addRange(j),k&&this.selection.substractPoint(k)},this.transposeSelections=function(a){var b=this.session,c=b.multiSelect,d=c.ranges;for(var e=d.length;e--;){var f=d[e];if(f.isEmpty()){var g=b.getWordRange(f.start.row,f.start.column);f.start.row=g.start.row,f.start.column=g.start.column,f.end.row=g.end.row,f.end.column=g.end.column}}c.mergeOverlappingRanges();var h=[];for(var e=d.length;e--;){var f=d[e];h.unshift(b.getTextRange(f))}a<0?h.unshift(h.pop()):h.push(h.shift());for(var e=d.length;e--;){var f=d[e],g=f.clone();b.replace(f,h[e]),f.start.row=g.start.row,f.start.column=g.start.column}},this.selectMore=function(a,b){var c=this.session,d=c.multiSelect,e=d.toOrientedRange();if(e.isEmpty()){var e=c.getWordRange(e.start.row,e.start.column);e.cursor=e.end,this.multiSelect.addRange(e)}var f=c.getTextRange(e),g=j(c,f,a);g&&(g.cursor=a==-1?g.start:g.end,this.multiSelect.addRange(g)),b&&this.multiSelect.substractPoint(e.cursor)}}).call(l.prototype),b.onSessionChange=function(a){var b=a.session;b.multiSelect||(b.$selectionMarkers=[],b.selection.$initRangeList(),b.multiSelect=b.selection),this.multiSelect=b.multiSelect;var c=a.oldSession;c&&(c.multiSelect&&c.multiSelect.editor==this&&(c.multiSelect.editor=null),b.multiSelect.removeEventListener("addRange",this.$onAddRange),b.multiSelect.removeEventListener("removeRange",this.$onRemoveRange),b.multiSelect.removeEventListener("multiSelect",this.$onMultiSelect),b.multiSelect.removeEventListener("singleSelect",this.$onSingleSelect)),b.multiSelect.on("addRange",this.$onAddRange),b.multiSelect.on("removeRange",this.$onRemoveRange),b.multiSelect.on("multiSelect",this.$onMultiSelect),b.multiSelect.on("singleSelect",this.$onSingleSelect),this.inMultiSelectMode!=b.selection.inMultiSelectMode&&(b.selection.inMultiSelectMode?this.$onMultiSelect():this.$onSingleSelect())},b.MultiSelect=n}),define("ace/range_list",["require","exports","module"],function(a,b,c){"use strict";var d=function(){this.ranges=[]};(function(){this.comparePoints=function(a,b){return a.row-b.row||a.column-b.column},this.pointIndex=function(a,b){var c=this.ranges;for(var d=b||0;d<c.length;d++){var e=c[d],f=this.comparePoints(a,e.end);if(f>0)continue;return f==0?d:(f=this.comparePoints(a,e.start),f>=0?d:-d-1)}return-d-1},this.add=function(a){var b=this.pointIndex(a.start);b<0&&(b=-b-1);var c=this.pointIndex(a.end,b);return c<0?c=-c-1:c++,this.ranges.splice(b,c-b,a)},this.addList=function(a){var b=[];for(var c=a.length;c--;)b.push.call(b,this.add(a[c]));return b},this.substractPoint=function(a){var b=this.pointIndex(a);if(b>=0)return this.ranges.splice(b,1)},this.merge=function(){var a=[],b=this.ranges,c=b[0],d;for(var e=1;e<b.length;e++){d=c,c=b[e];var f=this.comparePoints(d.end,c.start);if(f<0)continue;if(f==0&&!d.isEmpty()&&!c.isEmpty())continue;this.comparePoints(d.end,c.end)<0&&(d.end.row=c.end.row,d.end.column=c.end.column),b.splice(e,1),a.push(c),c=d,e--}return a},this.contains=function(a,b){return this.pointIndex({row:a,column:b})>=0},this.containsPoint=function(a){return this.pointIndex(a)>=0},this.rangeAtPoint=function(a){var b=this.pointIndex(a);if(b>=0)return this.ranges[b]},this.clipRows=function(a,b){var c=this.ranges;if(c[0].start.row>b||c[c.length-1].start.row<a)return[];var d=this.pointIndex({row:a,column:0});d<0&&(d=-d-1);var e=this.pointIndex({row:b,column:0},d);e<0&&(e=-e-1);var f=[];for(var g=d;g<e;g++)f.push(c[g]);return f},this.removeAll=function(){return this.ranges.splice(0,this.ranges.length)},this.attach=function(a){this.session&&this.detach(),this.session=a,this.onChange=this.$onChange.bind(this),this.session.on("change",this.onChange)},this.detach=function(){if(!this.session)return;this.session.removeListener("change",this.onChange),this.session=null},this.$onChange=function(a){var b=a.data.range;if(a.data.action[0]=="i")var c=b.start,d=b.end;else var d=b.start,c=b.end;var e=c.row,f=d.row,g=f-e,h=-c.column+d.column,i=this.ranges;for(var j=0,k=i.length;j<k;j++){var l=i[j];if(l.end.row<e)continue;if(l.start.row>e)break;l.start.row==e&&l.start.column>=c.column&&(l.start.column+=h,l.start.row+=g),l.end.row==e&&l.end.column>=c.column&&(l.end.column+=h,l.end.row+=g)}if(g!=0&&j<k)for(;j<k;j++){var l=i[j];l.start.row+=g,l.end.row+=g}}}).call(d.prototype),b.RangeList=d}),define("ace/mouse/multi_select_handler",["require","exports","module","ace/lib/event"],function(a,b,c){function e(a,b){return a.row==b.row&&a.column==b.column}function f(a){var b=a.domEvent,c=b.altKey,f=b.shiftKey,g=a.getAccelKey(),h=a.getButton();if(!g&&!c){if(a.editor.inMultiSelectMode)if(h==0)a.editor.exitMultiSelectMode();else if(h==2){var i=a.editor,j=i.selection.isEmpty();i.textInput.onContextMenu({x:a.clientX,y:a.clientY},j),d.capture(i.container,function(){},i.textInput.onContextMenuClose),a.stop()}return}var i=a.editor,k=i.selection,l=i.inMultiSelectMode,m=a.getDocumentPosition(),n=k.getCursor(),o=a.inSelection()||k.isEmpty()&&e(m,n),p=a.pageX,q=a.pageY,r=function(a){p=d.getDocumentX(a),q=d.getDocumentY(a)},s=function(){var a=i.renderer.pixelToScreenCoordinates(p,q),b=t.screenToDocumentPosition(a.row,a.column);if(e(v,a)&&e(b,k.selectionLead))return;v=a,i.selection.moveCursorToPosition(b),i.selection.clearSelection(),i.renderer.scrollCursorIntoView(),i.removeSelectionMarkers(x),x=k.rectangularRangeBlock(v,u),x.forEach(i.addSelectionMarker,i),i.updateSelectionMarkers()},t=i.session,u=i.renderer.pixelToScreenCoordinates(p,q),v=u;if(g&&!f&&!c&&h==0){if(!l&&o)return;l||k.addRange(k.toOrientedRange());var w=k.rangeList.rangeAtPoint(m);d.capture(i.container,function(){},function(){var a=k.toOrientedRange();w&&a.isEmpty()&&e(w.cursor,a.cursor)?k.substractPoint(a.cursor):k.addRange(a)})}else if(!f&&c&&h==0){a.stop(),l&&!g?k.toSingleRange():!l&&g&&k.addRange(),k.moveCursorToPosition(m),k.clearSelection();var x=[],y=function(a){clearInterval(A),i.removeSelectionMarkers(x);for(var b=0;b<x.length;b++)k.addRange(x[b])},z=s;d.capture(i.container,r,y);var A=setInterval(function(){z()},20);return a.preventDefault()}}var d=a("../lib/event");b.onMouseDown=f}),define("ace/commands/multi_select_commands",["require","exports","module","ace/keyboard/hash_handler"],function(a,b,c){b.defaultCommands=[{name:"addCursorAbove",exec:function(a){a.selectMoreLines(-1)},bindKey:{win:"Ctrl-Alt-Up",mac:"Ctrl-Alt-Up"},readonly:!0},{name:"addCursorBelow",exec:function(a){a.selectMoreLines(1)},bindKey:{win:"Ctrl-Alt-Down",mac:"Ctrl-Alt-Down"},readonly:!0},{name:"addCursorAboveSkipCurrent",exec:function(a){a.selectMoreLines(-1,!0)},bindKey:{win:"Ctrl-Alt-Shift-Up",mac:"Ctrl-Alt-Shift-Up"},readonly:!0},{name:"addCursorBelowSkipCurrent",exec:function(a){a.selectMoreLines(1,!0)},bindKey:{win:"Ctrl-Alt-Shift-Down",mac:"Ctrl-Alt-Shift-Down"},readonly:!0},{name:"selectMoreBefore",exec:function(a){a.selectMore(-1)},bindKey:{win:"Ctrl-Alt-Left",mac:"Ctrl-Alt-Left"},readonly:!0},{name:"selectMoreAfter",exec:function(a){a.selectMore(1)},bindKey:{win:"Ctrl-Alt-Right",mac:"Ctrl-Alt-Right"},readonly:!0},{name:"selectNextBefore",exec:function(a){a.selectMore(-1,!0)},bindKey:{win:"Ctrl-Alt-Shift-Left",mac:"Ctrl-Alt-Shift-Left"},readonly:!0},{name:"selectNextAfter",exec:function(a){a.selectMore(1,!0)},bindKey:{win:"Ctrl-Alt-Shift-Right",mac:"Ctrl-Alt-Shift-Right"},readonly:!0},{name:"splitIntoLines",exec:function(a){a.multiSelect.splitIntoLines()},bindKey:{win:"Ctrl-Shift-L",mac:"Ctrl-Shift-L"},readonly:!0}],b.multiEditCommands=[{name:"singleSelection",bindKey:"esc",exec:function(a){a.exitMultiSelectMode()},readonly:!0}];var d=a("../keyboard/hash_handler").HashHandler;b.keyboardHandler=new d(b.multiEditCommands)}),define("ace/worker/worker_client",["require","exports","module","ace/lib/oop","ace/lib/event_emitter","ace/config"],function(a,b,c){"use strict";var d=a("../lib/oop"),e=a("../lib/event_emitter").EventEmitter,f=a("../config"),g=function(b,c,d,e){this.changeListener=this.changeListener.bind(this);if(f.get("packaged"))this.$worker=new Worker(f.get("workerPath")+"/"+c);else{var g=this.$normalizePath(a.nameToUrl("ace/worker/worker",null,"_"));this.$worker=new Worker(g);var h={};for(var i=0;i<b.length;i++){var j=b[i],k=this.$normalizePath(a.nameToUrl(j,null,"_").replace(/.js$/,""));h[j]=k}}this.$worker.postMessage({init:!0,tlns:h,module:d,classname:e}),this.callbackId=1,this.callbacks={};var l=this;this.$worker.onerror=function(a){throw window.console&&console.log&&console.log(a),a},this.$worker.onmessage=function(a){var b=a.data;switch(b.type){case"log":window.console&&console.log&&console.log(b.data);break;case"event":l._emit(b.name,{data:b.data});break;case"call":var c=l.callbacks[b.id];c&&(c(b.data),delete l.callbacks[b.id])}}};(function(){d.implement(this,e),this.$normalizePath=function(a){return a=a.replace(/^[a-z]+:\/\/[^\/]+/,""),a=location.protocol+"//"+location.host+(a.charAt(0)=="/"?"":location.pathname.replace(/\/[^\/]*$/,""))+"/"+a.replace(/^[\/]+/,""),a},this.terminate=function(){this._emit("terminate",{}),this.$worker.terminate(),this.$worker=null,this.$doc.removeEventListener("change",this.changeListener),this.$doc=null},this.send=function(a,b){this.$worker.postMessage({command:a,args:b})},this.call=function(a,b,c){if(c){var d=this.callbackId++;this.callbacks[d]=c,b.push(d)}this.send(a,b)},this.emit=function(a,b){try{this.$worker.postMessage({event:a,data:{data:b.data}})}catch(c){}},this.attachToDocument=function(a){this.$doc&&this.terminate(),this.$doc=a,this.call("setValue",[a.getValue()]),a.on("change",this.changeListener)},this.changeListener=function(a){a.range={start:a.data.range.start,end:a.data.range.end},this.emit("change",a)}}).call(g.prototype),b.WorkerClient=g}),define("ace/keyboard/state_handler",["require","exports","module"],function(a,b,c){function e(a){this.keymapping=this.$buildKeymappingRegex(a)}"use strict";var d=!1;e.prototype={$buildKeymappingRegex:function(a){for(var b in a)this.$buildBindingsRegex(a[b]);return a},$buildBindingsRegex:function(a){a.forEach(function(a){a.key?a.key=new RegExp("^"+a.key+"$"):Array.isArray(a.regex)?("key"in a||(a.key=new RegExp("^"+a.regex[1]+"$")),a.regex=new RegExp(a.regex.join("")+"$")):a.regex&&(a.regex=new RegExp(a.regex+"$"))})},$composeBuffer:function(a,b,c,d){if(a.state==null||a.buffer==null)a.state="start",a.buffer="";var e=[];b&1&&e.push("ctrl"),b&8&&e.push("command"),b&2&&e.push("option"),b&4&&e.push("shift"),c&&e.push(c);var f=e.join("-"),g=a.buffer+f;b!=2&&(a.buffer=g);var h={bufferToUse:g,symbolicName:f};return d&&(h.keyIdentifier=d.keyIdentifier),h},$find:function(a,b,c,e,f,g){var h={};return this.keymapping[a.state].some(function(i){var j;if(i.key&&!i.key.test(c))return!1;if(i.regex&&!(j=i.regex.exec(b)))return!1;if(i.match&&!i.match(b,e,f,c,g))return!1;if(i.disallowMatches)for(var k=0;k<i.disallowMatches.length;k++)if(!!j[i.disallowMatches[k]])return!1;if(i.exec){h.command=i.exec;if(i.params){var l;h.args={},i.params.forEach(function(a){a.match!=null&&j!=null?l=j[a.match]||a.defaultValue:l=a.defaultValue,a.type==="number"&&(l=parseInt(l)),h.args[a.name]=l})}a.buffer=""}return i.then&&(a.state=i.then,a.buffer=""),h.command==null&&(h.command="null"),d&&console.log("KeyboardStateMapper#find",i),!0}),h.command?h:(a.buffer="",!1)},handleKeyboard:function(a,b,c,e,f){if(b==0||c!=""&&c!=String.fromCharCode(0)){var g=this.$composeBuffer(a,b,c,f),h=g.bufferToUse,i=g.symbolicName,j=g.keyIdentifier;return g=this.$find(a,h,i,b,c,j),d&&console.log("KeyboardStateMapper#match",h,i,g),g}return null}},b.matchCharacterOnly=function(a,b,c,d){return b==0?!0:b==4&&c.length==1?!0:!1},b.StateHandler=e}),define("ace/placeholder",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/oop"],function(a,b,c){"use strict";var d=a("./range").Range,e=a("./lib/event_emitter").EventEmitter,f=a("./lib/oop"),g=function(a,b,c,d,e,f){var g=this;this.length=b,this.session=a,this.doc=a.getDocument(),this.mainClass=e,this.othersClass=f,this.$onUpdate=this.onUpdate.bind(this),this.doc.on("change",this.$onUpdate),this.$others=d,this.$onCursorChange=function(){setTimeout(function(){g.onCursorChange()})},this.$pos=c;var h=a.getUndoManager().$undoStack||a.getUndoManager().$undostack||{length:-1};this.$undoStackDepth=h.length,this.setup(),a.selection.on("changeCursor",this.$onCursorChange)};(function(){f.implement(this,e),this.setup=function(){var a=this,b=this.doc,c=this.session,e=this.$pos;this.pos=b.createAnchor(e.row,e.column),this.markerId=c.addMarker(new d(e.row,e.column,e.row,e.column+this.length),this.mainClass,null,!1),this.pos.on("change",function(b){c.removeMarker(a.markerId),a.markerId=c.addMarker(new d(b.value.row,b.value.column,b.value.row,b.value.column+a.length),a.mainClass,null,!1)}),this.others=[],this.$others.forEach(function(c){var d=b.createAnchor(c.row,c.column);a.others.push(d)}),c.setUndoSelect(!1)},this.showOtherMarkers=function(){if(this.othersActive)return;var a=this.session,b=this;this.othersActive=!0,this.others.forEach(function(c){c.markerId=a.addMarker(new d(c.row,c.column,c.row,c.column+b.length),b.othersClass,null,!1),c.on("change",function(e){a.removeMarker(c.markerId),c.markerId=a.addMarker(new d(e.value.row,e.value.column,e.value.row,e.value.column+b.length),b.othersClass,null,!1)})})},this.hideOtherMarkers=function(){if(!this.othersActive)return;this.othersActive=!1;for(var a=0;a<this.others.length;a++)this.session.removeMarker(this.others[a].markerId)},this.onUpdate=function(a){var b=a.data,c=b.range;if(c.start.row!==c.end.row)return;if(c.start.row!==this.pos.row)return;if(this.$updating)return;this.$updating=!0;var e=b.action==="insertText"?c.end.column-c.start.column:c.start.column-c.end.column;if(c.start.column>=this.pos.column&&c.start.column<=this.pos.column+this.length+1){var f=c.start.column-this.pos.column;this.length+=e;if(!this.session.$fromUndo){if(b.action==="insertText")for(var g=this.others.length-1;g>=0;g--){var h=this.others[g],i={row:h.row,column:h.column+f};h.row===c.start.row&&c.start.column<h.column&&(i.column+=e),this.doc.insert(i,b.text)}else if(b.action==="removeText")for(var g=this.others.length-1;g>=0;g--){var h=this.others[g],i={row:h.row,column:h.column+f};h.row===c.start.row&&c.start.column<h.column&&(i.column+=e),this.doc.remove(new d(i.row,i.column,i.row,i.column-e))}c.start.column===this.pos.column&&b.action==="insertText"?setTimeout(function(){this.pos.setPosition(this.pos.row,this.pos.column-e);for(var a=0;a<this.others.length;a++){var b=this.others[a],d={row:b.row,column:b.column-e};b.row===c.start.row&&c.start.column<b.column&&(d.column+=e),b.setPosition(d.row,d.column)}}.bind(this),0):c.start.column===this.pos.column&&b.action==="removeText"&&setTimeout(function(){for(var a=0;a<this.others.length;a++){var b=this.others[a];b.row===c.start.row&&c.start.column<b.column&&b.setPosition(b.row,b.column-e)}}.bind(this),0)}this.pos._emit("change",{value:this.pos});for(var g=0;g<this.others.length;g++)this.others[g]._emit("change",{value:this.others[g]})}this.$updating=!1},this.onCursorChange=function(a){if(this.$updating)return;var b=this.session.selection.getCursor();b.row===this.pos.row&&b.column>=this.pos.column&&b.column<=this.pos.column+this.length?(this.showOtherMarkers(),this._emit("cursorEnter",a)):(this.hideOtherMarkers(),this._emit("cursorLeave",a))},this.detach=function(){this.session.removeMarker(this.markerId),this.hideOtherMarkers(),this.doc.removeEventListener("change",this.$onUpdate),this.session.selection.removeEventListener("changeCursor",this.$onCursorChange),this.pos.detach();for(var a=0;a<this.others.length;a++)this.others[a].detach();this.session.setUndoSelect(!0)},this.cancel=function(){if(this.$undoStackDepth===-1)throw Error("Canceling placeholders only supported with undo manager attached to session.");var a=this.session.getUndoManager(),b=(a.$undoStack||a.$undostack).length-this.$undoStackDepth;for(var c=0;c<b;c++)a.undo(!0)}}).call(g.prototype),b.PlaceHolder=g}),define("ace/theme/textmate",["require","exports","module","ace/lib/dom"],function(a,b,c){"use strict",b.isDark=!1,b.cssClass="ace-tm",b.cssText=".ace-tm .ace_editor { border: 2px solid rgb(159, 159, 159);}.ace-tm .ace_editor.ace_focus { border: 2px solid #327fbd;}.ace-tm .ace_gutter { background: #e8e8e8; color: #333;}.ace-tm .ace_print_margin { width: 1px; background: #e8e8e8;}.ace-tm .ace_fold { background-color: #6B72E6;}.ace-tm .ace_text-layer { cursor: text;}.ace-tm .ace_cursor { border-left: 1px solid black;}.ace-tm .ace_cursor.ace_overwrite { border-left: 0px; border-bottom: 1px solid black;} .ace-tm .ace_line .ace_invisible { color: rgb(191, 191, 191);}.ace-tm .ace_line .ace_storage,.ace-tm .ace_line .ace_keyword { color: blue;}.ace-tm .ace_line .ace_constant { color: rgb(197, 6, 11);}.ace-tm .ace_line .ace_constant.ace_buildin { color: rgb(88, 72, 246);}.ace-tm .ace_line .ace_constant.ace_language { color: rgb(88, 92, 246);}.ace-tm .ace_line .ace_constant.ace_library { color: rgb(6, 150, 14);}.ace-tm .ace_line .ace_invalid { background-color: rgb(153, 0, 0); color: white;}.ace-tm .ace_line .ace_support.ace_function { color: rgb(60, 76, 114);}.ace-tm .ace_line .ace_support.ace_constant { color: rgb(6, 150, 14);}.ace-tm .ace_line .ace_support.ace_type,.ace-tm .ace_line .ace_support.ace_class { color: rgb(109, 121, 222);}.ace-tm .ace_line .ace_keyword.ace_operator { color: rgb(104, 118, 135);}.ace-tm .ace_line .ace_string { color: rgb(3, 106, 7);}.ace-tm .ace_line .ace_comment { color: rgb(76, 136, 107);}.ace-tm .ace_line .ace_comment.ace_doc { color: rgb(0, 102, 255);}.ace-tm .ace_line .ace_comment.ace_doc.ace_tag { color: rgb(128, 159, 191);}.ace-tm .ace_line .ace_constant.ace_numeric { color: rgb(0, 0, 205);}.ace-tm .ace_line .ace_variable { color: rgb(49, 132, 149);}.ace-tm .ace_line .ace_xml_pe { color: rgb(104, 104, 91);}.ace-tm .ace_entity.ace_name.ace_function { color: #0000A2;}.ace-tm .ace_markup.ace_markupine { text-decoration:underline;}.ace-tm .ace_markup.ace_heading { color: rgb(12, 7, 255);}.ace-tm .ace_markup.ace_list { color:rgb(185, 6, 144);}.ace-tm .ace_marker-layer .ace_selection { background: rgb(181, 213, 255);}.ace-tm .ace_marker-layer .ace_step { background: rgb(252, 255, 0);}.ace-tm .ace_marker-layer .ace_stack { background: rgb(164, 229, 101);}.ace-tm .ace_marker-layer .ace_bracket { margin: -1px 0 0 -1px; border: 1px solid rgb(192, 192, 192);}.ace-tm .ace_marker-layer .ace_active_line { background: rgba(0, 0, 0, 0.07);}.ace-tm .ace_marker-layer .ace_selected_word { background: rgb(250, 250, 255); border: 1px solid rgb(200, 200, 250);}.ace-tm .ace_meta.ace_tag { color:rgb(28, 2, 255);}.ace-tm .ace_string.ace_regex { color: rgb(255, 0, 0)}";var d=a("../lib/dom");d.importCssString(b.cssText,b.cssClass)}); (function() { window.require(["ace/ace"], function(a) { if (!window.ace) @@ -7,4 +7,3 @@ ace[key] = a[key]; }); })(); -
\ No newline at end of file diff --git a/apps/files_texteditor/js/editor.js b/apps/files_texteditor/js/editor.js index 70bb74a9101..3784ea1032f 100644 --- a/apps/files_texteditor/js/editor.js +++ b/apps/files_texteditor/js/editor.js @@ -222,9 +222,17 @@ function showFileEditor(dir,filename){ } }); // Add the ctrl+s event - window.aceEditor.commands.addCommand({
name: "save",
bindKey: {
win: "Ctrl-S",
mac: "Command-S",
sender: "editor"
},
exec: function(){ + window.aceEditor.commands.addCommand({ + name: "save", + bindKey: { + win: "Ctrl-S", + mac: "Command-S", + sender: "editor" + }, + exec: function(){ doFileSave(); - }
}); + } + }); }); } else { // Failed to get the file. @@ -297,11 +305,11 @@ $(window).resize(function() { var is_editor_shown = false; $(document).ready(function(){ if(typeof FileActions!=='undefined'){ - FileActions.register('text','Edit','',function(filename){ + FileActions.register('text','Edit', FileActions.PERMISSION_READ, '',function(filename){ showFileEditor($('#dir').val(),filename); }); FileActions.setDefault('text','Edit'); - FileActions.register('application/xml','Edit','',function(filename){ + FileActions.register('application/xml','Edit', FileActions.PERMISSION_READ, '',function(filename){ showFileEditor($('#dir').val(),filename); }); FileActions.setDefault('application/xml','Edit'); diff --git a/apps/files_sharing/list.php b/apps/files_versions/ajax/expireAll.php index 2fd24840d36..2a678c7f0a5 100644 --- a/apps/files_sharing/list.php +++ b/apps/files_versions/ajax/expireAll.php @@ -1,9 +1,10 @@ <?php + /** - * ownCloud + * ownCloud - user_migrate * - * @author Michael Gapczynski - * @copyright 2011 Michael Gapczynski GapczynskiM@gmail.com + * @author Sam Tuke + * @copyright 2012 Sam Tuke samtuke@owncloud.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -20,18 +21,24 @@ * */ +// TODO: Allow admins to expire versions of any user +// TODO: Provide feedback as to how many versions were deleted -require_once('lib_share.php'); - -OCP\User::checkLoggedIn(); -OCP\App::checkAppEnabled('files_sharing'); - -OCP\App::setActiveNavigationEntry("files_sharing_list"); - -OCP\Util::addscript("files_sharing", "list"); +// Check user and app status +OCP\JSON::checkLoggedIn(); +OCP\App::checkAppEnabled('files_versions'); +OCP\JSON::callCheck(); -$tmpl = new OCP\Template("files_sharing", "list", "user"); -$tmpl->assign("shared_items", OC_Share::getMySharedItems()); -$tmpl->printPage(); +$versions = new OCA_Versions\Storage(); -?> +if( $versions->expireAll() ){ + + OCP\JSON::success(); + die(); + +} else { + + OCP\JSON::error(); + die(); + +}
\ No newline at end of file diff --git a/apps/files_versions/ajax/getVersions.php b/apps/files_versions/ajax/getVersions.php index 92ca3ed8984..1a0e21732cc 100644 --- a/apps/files_versions/ajax/getVersions.php +++ b/apps/files_versions/ajax/getVersions.php @@ -1,15 +1,18 @@ <?php OCP\JSON::checkAppEnabled('files_versions'); -require_once('apps/files_versions/versions.php'); - $userDirectory = "/".OCP\USER::getUser()."/files"; $source = $_GET['source']; if( OCA_Versions\Storage::isversioned( $source ) ) { $count=5; //show the newest revisions - $versions = OCA_Versions\Storage::getversions( $source, $count); + $versions = OCA_Versions\Storage::getVersions( $source, $count); + $versionsFormatted = array(); + + foreach ( $versions AS $version ) { + $versionsFormatted[] = OCP\Util::formatDate( $version['version'] ); + } $versionsSorted = array_reverse( $versions ); diff --git a/apps/files_versions/ajax/rollbackVersion.php b/apps/files_versions/ajax/rollbackVersion.php index 77c6102ea76..24d71a914a4 100644 --- a/apps/files_versions/ajax/rollbackVersion.php +++ b/apps/files_versions/ajax/rollbackVersion.php @@ -3,8 +3,6 @@ OCP\JSON::checkAppEnabled('files_versions'); OCP\JSON::callCheck(); -require_once('apps/files_versions/versions.php'); - $userDirectory = "/".OCP\USER::getUser()."/files"; $file = $_GET['file']; diff --git a/apps/files_versions/ajax/togglesettings.php b/apps/files_versions/ajax/togglesettings.php index d513d12dd6c..546b37ae1aa 100644 --- a/apps/files_versions/ajax/togglesettings.php +++ b/apps/files_versions/ajax/togglesettings.php @@ -2,10 +2,9 @@ OCP\JSON::checkAppEnabled('files_versions'); OCP\JSON::checkAdminUser(); +OCP\JSON::callCheck(); if (OCP\Config::getSystemValue('versions', 'true')=='true') { OCP\Config::setSystemValue('versions', 'false'); } else { OCP\Config::setSystemValue('versions', 'true'); } - -?> diff --git a/apps/files_versions/appinfo/api.php b/apps/files_versions/appinfo/api.php new file mode 100644 index 00000000000..a7386bc2c9f --- /dev/null +++ b/apps/files_versions/appinfo/api.php @@ -0,0 +1,36 @@ +<?php +/** +* ownCloud +* +* @author Michael Gapczynski +* @copyright 2012 Michael Gapczynski mtgap@owncloud.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +return array( + 'list' => array('method' => 'GET', 'class' => 'Storage', 'function' => 'getVersions', + 'parameters' => array( + 'file' => array('required' => true, 'type' => 'string') + ) + ), + 'revert' => array('method' => 'POST', 'class' => 'Storage', 'function' => 'rollback', + 'parameters' => array( + 'file' => array('required' => true, 'type' => 'string'), + 'time' => array('required' => true, 'type' => 'int') + ) + ) +); + +?>
\ No newline at end of file diff --git a/apps/files_versions/appinfo/app.php b/apps/files_versions/appinfo/app.php index fd31a0bb67a..9ac7f6d5cde 100644 --- a/apps/files_versions/appinfo/app.php +++ b/apps/files_versions/appinfo/app.php @@ -1,17 +1,16 @@ <?php -require_once('apps/files_versions/versions.php'); - -// Add an entry in the app list -OCP\App::register( array( - 'order' => 10, - 'id' => 'files_versions', - 'name' => 'Versioning' )); +//require_once('files_versions/versions.php'); +OC::$CLASSPATH['OCA_Versions\Storage'] = 'apps/files_versions/lib/versions.php'; +OC::$CLASSPATH['OCA_Versions\Hooks'] = 'apps/files_versions/lib/hooks.php'; OCP\App::registerAdmin('files_versions', 'settings'); +OCP\App::registerPersonal('files_versions','settings-personal'); + OCP\Util::addscript('files_versions', 'versions'); // Listen to write signals -OCP\Util::connectHook(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, "OCA_Versions\Storage", "write_hook"); - -?> +OCP\Util::connectHook('OC_Filesystem', 'post_write', "OCA_Versions\Hooks", "write_hook"); +// Listen to delete and rename signals +OCP\Util::connectHook('OC_Filesystem', 'delete', "OCA_Versions\Hooks", "remove_hook"); +OCP\Util::connectHook('OC_Filesystem', 'rename', "OCA_Versions\Hooks", "rename_hook");
\ No newline at end of file diff --git a/apps/files_versions/appinfo/update.php b/apps/files_versions/appinfo/update.php new file mode 100644 index 00000000000..9569ca10485 --- /dev/null +++ b/apps/files_versions/appinfo/update.php @@ -0,0 +1,16 @@ +<?php + +$installedVersion=OCP\Config::getAppValue('files_versions', 'installed_version'); +// move versions to new directory +if (version_compare($installedVersion, '1.0.2', '<')) { + $users = \OCP\User::getUsers(); + $datadir = \OCP\Config::getSystemValue('datadirectory').'/'; + foreach ($users as $user) { + $oldPath = $datadir.$user.'/versions';
+ $newPath = $datadir.$user.'/files_versions'; + if(is_dir($oldPath)) { + rename($oldPath, $newPath); + } + } + +} diff --git a/apps/files_versions/appinfo/version b/apps/files_versions/appinfo/version index 7f207341d5d..e6d5cb833c6 100644 --- a/apps/files_versions/appinfo/version +++ b/apps/files_versions/appinfo/version @@ -1 +1 @@ -1.0.1
\ No newline at end of file +1.0.2
\ No newline at end of file diff --git a/apps/files_versions/history.php b/apps/files_versions/history.php index f12dc618f23..27dc8bfc382 100644 --- a/apps/files_versions/history.php +++ b/apps/files_versions/history.php @@ -4,7 +4,7 @@ * ownCloud - History page of the Versions App * * @author Frank Karlitschek - * @copyright 2011 Frank Karlitschek karlitschek@kde.org + * @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -28,23 +28,24 @@ $tmpl = new OCP\Template( 'files_versions', 'history', 'user' ); if ( isset( $_GET['path'] ) ) { $path = $_GET['path']; - $path = strip_tags( $path ); + $path = $path; $tmpl->assign( 'path', $path ); + $versions = new OCA_Versions\Storage(); // roll back to old version if button clicked if( isset( $_GET['revert'] ) ) { - if( \OCA_Versions\Storage::rollback( $path, $_GET['revert'] ) ) { + if( $versions->rollback( $path, $_GET['revert'] ) ) { $tmpl->assign( 'outcome_stat', 'success' ); - $tmpl->assign( 'outcome_msg', "File {$_GET['path']} was reverted to version ".OCP\Util::formatDate( $_GET['revert'] ) ); + $tmpl->assign( 'outcome_msg', "File {$_GET['path']} was reverted to version ".OCP\Util::formatDate( doubleval($_GET['revert']) ) ); } else { $tmpl->assign( 'outcome_stat', 'failure' ); - $tmpl->assign( 'outcome_msg', "File {$_GET['path']} could not be reverted to version ".OCP\Util::formatDate( $_GET['revert'] ) ); + $tmpl->assign( 'outcome_msg', "File {$_GET['path']} could not be reverted to version ".OCP\Util::formatDate( doubleval($_GET['revert']) ) ); } @@ -54,7 +55,7 @@ if ( isset( $_GET['path'] ) ) { if( OCA_Versions\Storage::isversioned( $path ) ) { $count = 999; //show the newest revisions - $versions = OCA_Versions\Storage::getversions( $path, $count ); + $versions = OCA_Versions\Storage::getVersions( $path, $count); $tmpl->assign( 'versions', array_reverse( $versions ) ); @@ -70,5 +71,3 @@ if ( isset( $_GET['path'] ) ) { } $tmpl->printPage( ); - -?> diff --git a/apps/files_versions/js/settings-personal.js b/apps/files_versions/js/settings-personal.js new file mode 100644 index 00000000000..6ea8c1a950f --- /dev/null +++ b/apps/files_versions/js/settings-personal.js @@ -0,0 +1,39 @@ +// TODO: allow the button to be clicked only once + +$( document ).ready(function(){ + // + $( '#expireAllBtn' ).click( + + function( event ) { + + // Prevent page from reloading + event.preventDefault(); + + // Show loading gif + $('.expireAllLoading').show(); + + $.getJSON( + OC.filePath('files_versions','ajax','expireAll.php'), + function(result){ + if (result.status == 'success') { + $('.expireAllLoading').hide(); + $('#expireAllBtn').html('Expiration successful'); + } else { + + // Cancel loading + $('#expireAllBtn').html('Expiration failed'); + + // Show Dialog + OC.dialogs.alert( + 'Something went wrong, your files may not have been expired', + 'An error has occurred', + function(){ + $('#expireAllBtn').html(t('files_versions', 'Expire all versions')+'<img style="display: none;" class="loading" src="'+OC.filePath('core','img','loading.gif')+'" />'); + } + ); + } + } + ); + } + ); +});
\ No newline at end of file diff --git a/apps/files_versions/js/versions.js b/apps/files_versions/js/versions.js index 5e46b2a0eed..c5c1553f1a8 100644 --- a/apps/files_versions/js/versions.js +++ b/apps/files_versions/js/versions.js @@ -11,7 +11,7 @@ $(document).ready(function() { $(document).ready(function(){ if (typeof FileActions !== 'undefined') { // Add history button to files/index.php - FileActions.register('file','History',function(){return OC.imagePath('core','actions/history')},function(filename){ + FileActions.register('file','History', FileActions.PERMISSION_UPDATE, function(){return OC.imagePath('core','actions/history')},function(filename){ if (scanFiles.scanning){return;}//workaround to prevent additional http request block scanning feedback @@ -33,7 +33,7 @@ $(document).ready(function(){ }); function createVersionsDropdown(filename, files) { - + var historyUrl = OC.linkTo('files_versions', 'history.php') + '?path='+encodeURIComponent( $( '#dir' ).val() ).replace( /%2F/g, '/' )+'/'+encodeURIComponent( filename ); var html = '<div id="dropdown" class="drop" data-file="'+files+'">'; @@ -104,9 +104,9 @@ function createVersionsDropdown(filename, files) { } function addVersion(revision ) { - name=formatDate(revision*1000); + name=formatDate(revision.version*1000); var version=$('<option/>'); - version.attr('value',revision); + version.attr('value',revision.version); version.text(name); // } else { diff --git a/apps/files_versions/l10n/.gitkeep b/apps/files_versions/l10n/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/apps/files_versions/l10n/.gitkeep diff --git a/apps/files_versions/l10n/ca.php b/apps/files_versions/l10n/ca.php new file mode 100644 index 00000000000..8388556bec6 --- /dev/null +++ b/apps/files_versions/l10n/ca.php @@ -0,0 +1,4 @@ +<?php $TRANSLATIONS = array( +"Expire all versions" => "Expira totes les versions", +"Enable Files Versioning" => "Habilita les versions de fitxers" +); diff --git a/apps/files_versions/l10n/cs_CZ.php b/apps/files_versions/l10n/cs_CZ.php new file mode 100644 index 00000000000..3297648fa30 --- /dev/null +++ b/apps/files_versions/l10n/cs_CZ.php @@ -0,0 +1,4 @@ +<?php $TRANSLATIONS = array( +"Expire all versions" => "VyprÅ¡enà vÅ¡ech verzÃ", +"Enable Files Versioning" => "Povolit verzovánà souborů" +); diff --git a/apps/files_versions/l10n/de.php b/apps/files_versions/l10n/de.php new file mode 100644 index 00000000000..3d1a0a43f5e --- /dev/null +++ b/apps/files_versions/l10n/de.php @@ -0,0 +1,4 @@ +<?php $TRANSLATIONS = array( +"Expire all versions" => "Alle Versionen löschen", +"Enable Files Versioning" => "Datei-Versionierung aktivieren" +); diff --git a/apps/files_versions/l10n/el.php b/apps/files_versions/l10n/el.php new file mode 100644 index 00000000000..8953c96bd11 --- /dev/null +++ b/apps/files_versions/l10n/el.php @@ -0,0 +1,4 @@ +<?php $TRANSLATIONS = array( +"Expire all versions" => "Λήξη όλων των εκδόσεων", +"Enable Files Versioning" => "ΕνεÏγοποίηση παÏακολοÏθησης εκδόσεων αÏχείων" +); diff --git a/apps/files_versions/l10n/eo.php b/apps/files_versions/l10n/eo.php new file mode 100644 index 00000000000..8ec0895638a --- /dev/null +++ b/apps/files_versions/l10n/eo.php @@ -0,0 +1,4 @@ +<?php $TRANSLATIONS = array( +"Expire all versions" => "Eksvalidigi ĉiujn eldonojn", +"Enable Files Versioning" => "Kapabligi dosiereldonkontrolon" +); diff --git a/apps/files_versions/l10n/es.php b/apps/files_versions/l10n/es.php new file mode 100644 index 00000000000..d5be72b1741 --- /dev/null +++ b/apps/files_versions/l10n/es.php @@ -0,0 +1,3 @@ +<?php $TRANSLATIONS = array( +"Enable Files Versioning" => "Habilitar versionamiento de archivos" +); diff --git a/apps/files_versions/l10n/et_EE.php b/apps/files_versions/l10n/et_EE.php new file mode 100644 index 00000000000..d136ae241ce --- /dev/null +++ b/apps/files_versions/l10n/et_EE.php @@ -0,0 +1,4 @@ +<?php $TRANSLATIONS = array( +"Expire all versions" => "Kõikide versioonide aegumine", +"Enable Files Versioning" => "Luba failide versioonihaldus" +); diff --git a/apps/files_versions/l10n/fa.php b/apps/files_versions/l10n/fa.php new file mode 100644 index 00000000000..e2dc6cba63f --- /dev/null +++ b/apps/files_versions/l10n/fa.php @@ -0,0 +1,4 @@ +<?php $TRANSLATIONS = array( +"Expire all versions" => "انقضای تمامی نسخه‌ها", +"Enable Files Versioning" => "Ùعال‌کردن پرونده‌های نسخه‌بندی" +); diff --git a/apps/files_versions/l10n/fi_FI.php b/apps/files_versions/l10n/fi_FI.php new file mode 100644 index 00000000000..c5092daf998 --- /dev/null +++ b/apps/files_versions/l10n/fi_FI.php @@ -0,0 +1,4 @@ +<?php $TRANSLATIONS = array( +"Expire all versions" => "Vanhenna kaikki versiot", +"Enable Files Versioning" => "Käytä tiedostojen versiointia" +); diff --git a/apps/files_versions/l10n/fr.php b/apps/files_versions/l10n/fr.php new file mode 100644 index 00000000000..02209f543b1 --- /dev/null +++ b/apps/files_versions/l10n/fr.php @@ -0,0 +1,4 @@ +<?php $TRANSLATIONS = array( +"Expire all versions" => "Supprimer les versions intermédiaires", +"Enable Files Versioning" => "Activer le versionnage" +); diff --git a/apps/files_versions/l10n/it.php b/apps/files_versions/l10n/it.php new file mode 100644 index 00000000000..9711ce18aa8 --- /dev/null +++ b/apps/files_versions/l10n/it.php @@ -0,0 +1,4 @@ +<?php $TRANSLATIONS = array( +"Expire all versions" => "Scadenza di tutte le versioni", +"Enable Files Versioning" => "Abilita controllo di versione" +); diff --git a/apps/files_versions/l10n/ja_JP.php b/apps/files_versions/l10n/ja_JP.php new file mode 100644 index 00000000000..ec5e32f3e29 --- /dev/null +++ b/apps/files_versions/l10n/ja_JP.php @@ -0,0 +1,4 @@ +<?php $TRANSLATIONS = array( +"Expire all versions" => "ã™ã¹ã¦ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’削除ã™ã‚‹", +"Enable Files Versioning" => "ファイルã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ç®¡ç†ã‚’有効ã«ã™ã‚‹" +); diff --git a/apps/files_versions/l10n/lt_LT.php b/apps/files_versions/l10n/lt_LT.php new file mode 100644 index 00000000000..5da209f31b9 --- /dev/null +++ b/apps/files_versions/l10n/lt_LT.php @@ -0,0 +1,4 @@ +<?php $TRANSLATIONS = array( +"Expire all versions" => "Panaikinti visų versijų galiojimÄ…", +"Enable Files Versioning" => "Ä®jungti failų versijų vedimÄ…" +); diff --git a/apps/files_versions/l10n/nb_NO.php b/apps/files_versions/l10n/nb_NO.php new file mode 100644 index 00000000000..55cc12113d7 --- /dev/null +++ b/apps/files_versions/l10n/nb_NO.php @@ -0,0 +1,3 @@ +<?php $TRANSLATIONS = array( +"Enable Files Versioning" => "SlÃ¥ pÃ¥ versjonering" +); diff --git a/apps/files_versions/l10n/pl.php b/apps/files_versions/l10n/pl.php new file mode 100644 index 00000000000..faf2d39e709 --- /dev/null +++ b/apps/files_versions/l10n/pl.php @@ -0,0 +1,4 @@ +<?php $TRANSLATIONS = array( +"Expire all versions" => "WygasajÄ… wszystkie wersje", +"Enable Files Versioning" => "WÅ‚Ä…cz wersjonowanie plików" +); diff --git a/apps/files_versions/l10n/sl.php b/apps/files_versions/l10n/sl.php new file mode 100644 index 00000000000..e7db55930c1 --- /dev/null +++ b/apps/files_versions/l10n/sl.php @@ -0,0 +1,4 @@ +<?php $TRANSLATIONS = array( +"Expire all versions" => "Zastaraj vse razliÄice", +"Enable Files Versioning" => "OmogoÄi sledenje razliÄicam datotek" +); diff --git a/apps/files_versions/l10n/sv.php b/apps/files_versions/l10n/sv.php new file mode 100644 index 00000000000..03d4d54d0b9 --- /dev/null +++ b/apps/files_versions/l10n/sv.php @@ -0,0 +1,4 @@ +<?php $TRANSLATIONS = array( +"Expire all versions" => "Upphör alla versioner", +"Enable Files Versioning" => "Aktivera versionshantering" +); diff --git a/apps/files_versions/l10n/th_TH.php b/apps/files_versions/l10n/th_TH.php new file mode 100644 index 00000000000..e880840f4b9 --- /dev/null +++ b/apps/files_versions/l10n/th_TH.php @@ -0,0 +1,4 @@ +<?php $TRANSLATIONS = array( +"Expire all versions" => "หมดà¸à¸²à¸¢à¸¸à¸—ุà¸à¸£à¸¸à¹ˆà¸™", +"Enable Files Versioning" => "เปิดใช้งานคุณสมบัติà¸à¸²à¸£à¹à¸¢à¸à¸ªà¸–านะรุ่นหรืà¸à¹€à¸§à¸à¸£à¹Œà¸Šà¸±à¹ˆà¸™à¸‚à¸à¸‡à¹„ฟล์" +); diff --git a/apps/files_versions/lib/hooks.php b/apps/files_versions/lib/hooks.php new file mode 100644 index 00000000000..bfc8fd3a378 --- /dev/null +++ b/apps/files_versions/lib/hooks.php @@ -0,0 +1,75 @@ +<?php +/** + * Copyright (c) 2012 Sam Tuke <samtuke@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +/** + * This class contains all hooks. + */ + +namespace OCA_Versions; + +class Hooks { + + /** + * listen to write event. + */ + public static function write_hook( $params ) { + + if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { + + $versions = new Storage( new \OC_FilesystemView('') ); + + $path = $params[\OC_Filesystem::signal_param_path]; + + if($path<>'') $versions->store( $path ); + + } + } + + + /**
+ * @brief Erase versions of deleted file
+ * @param array
+ *
+ * This function is connected to the delete signal of OC_Filesystem
+ * cleanup the versions directory if the actual file gets deleted
+ */
+ public static function remove_hook($params) { + $versions_fileview = \OCP\Files::getStorage('files_versions');
+ $rel_path = $params['path'];
+ $abs_path = \OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath('').$rel_path.'.v';
+ if(Storage::isversioned($rel_path)) {
+ $versions = Storage::getVersions($rel_path);
+ foreach ($versions as $v){
+ unlink($abs_path . $v['version']);
+ }
+ }
+ }
+
+ /**
+ * @brief rename/move versions of renamed/moved files
+ * @param array with oldpath and newpath
+ *
+ * This function is connected to the rename signal of OC_Filesystem and adjust the name and location
+ * of the stored versions along the actual file
+ */
+ public static function rename_hook($params) { + $versions_fileview = \OCP\Files::getStorage('files_versions');
+ $rel_oldpath = $params['oldpath'];
+ $abs_oldpath = \OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath('').$rel_oldpath.'.v';
+ $abs_newpath = \OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath('').$params['newpath'].'.v';
+ if(Storage::isversioned($rel_oldpath)) { + $info=pathinfo($abs_newpath);
+ if(!file_exists($info['dirname'])) mkdir($info['dirname'],0700,true);
+ $versions = Storage::getVersions($rel_oldpath);
+ foreach ($versions as $v){
+ rename($abs_oldpath.$v['version'], $abs_newpath.$v['version']);
+ }
+ }
+ } + +} diff --git a/apps/files_versions/lib/versions.php b/apps/files_versions/lib/versions.php new file mode 100644 index 00000000000..f146676757d --- /dev/null +++ b/apps/files_versions/lib/versions.php @@ -0,0 +1,306 @@ +<?php
+/**
+ * Copyright (c) 2012 Frank Karlitschek <frank@owncloud.org>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+/**
+ * Versions
+ *
+ * A class to handle the versioning of files.
+ */
+
+namespace OCA_Versions;
+
+class Storage {
+
+
+ // config.php configuration:
+ // - files_versions
+ // - files_versionsfolder
+ // - files_versionsblacklist
+ // - files_versionsmaxfilesize
+ // - files_versionsinterval
+ // - files_versionmaxversions
+ //
+ // todo:
+ // - finish porting to OC_FilesystemView to enable network transparency
+ // - add transparent compression. first test if it´s worth it.
+
+ const DEFAULTENABLED=true;
+ const DEFAULTBLACKLIST='avi mp3 mpg mp4 ctmp';
+ const DEFAULTMAXFILESIZE=1048576; // 10MB
+ const DEFAULTMININTERVAL=60; // 1 min
+ const DEFAULTMAXVERSIONS=50;
+
+ private $view;
+
+ function __construct() {
+
+ $this->view = \OCP\Files::getStorage('files_versions');
+
+ }
+
+ /**
+ * listen to write event.
+ */
+ public static function write_hook($params) {
+ if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
+ $path = $params[\OC_Filesystem::signal_param_path];
+ if($path<>'') $this->store($path);
+ }
+ }
+
+
+
+ /**
+ * store a new version of a file.
+ */
+ public function store($filename) {
+ if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
+
+ $files_view = \OCP\Files::getStorage("files");
+ $users_view = \OCP\Files::getStorage("files_versions");
+ $users_view->chroot(\OCP\User::getUser().'/');
+
+ if (\OCP\App::isEnabled('files_sharing') && $source = \OC_Share::getSource('/'.\OCP\User::getUser().'/files'.$filename)) {
+ $pos = strpos($source, '/files', 1);
+ $uid = substr($source, 1, $pos - 1);
+ $filename = substr($source, $pos + 6);
+ } else {
+ $uid = \OCP\User::getUser();
+ }
+
+ $versionsFolderName=\OCP\Config::getSystemValue('datadirectory') . $this->view->getAbsolutePath('');
+
+ //check if source file already exist as version to avoid recursions.
+ if ($users_view->file_exists($filename)) {
+ return false;
+ }
+
+ // check if filename is a directory
+ if($files_view->is_dir($filename)){
+ return false;
+ }
+
+ // check filetype blacklist
+ $blacklist=explode(' ',\OCP\Config::getSystemValue('files_versionsblacklist', Storage::DEFAULTBLACKLIST));
+ foreach($blacklist as $bl) {
+ $parts=explode('.', $filename);
+ $ext=end($parts);
+ if(strtolower($ext)==$bl) {
+ return false;
+ }
+ }
+
+ // check filesize
+ if($files_view->filesize($filename)>\OCP\Config::getSystemValue('files_versionsmaxfilesize', Storage::DEFAULTMAXFILESIZE)){
+ return false;
+ }
+
+
+ // check mininterval if the file is being modified by the owner (all shared files should be versioned despite mininterval)
+ if ($uid == \OCP\User::getUser()) {
+ $matches=glob($versionsFolderName.'/'.$filename.'.v*');
+ sort($matches);
+ $parts=explode('.v',end($matches));
+ if((end($parts)+Storage::DEFAULTMININTERVAL)>time()){
+ return false;
+ }
+ }
+
+
+ // create all parent folders
+ $info=pathinfo($filename);
+ if(!file_exists($versionsFolderName.'/'.$info['dirname'])) mkdir($versionsFolderName.'/'.$info['dirname'],0700,true);
+
+ // store a new version of a file
+ @$users_view->copy('files'.$filename, 'files_versions'.$filename.'.v'.time());
+
+ // expire old revisions if necessary
+ Storage::expire($filename);
+ }
+ }
+
+
+ /**
+ * rollback to an old version of a file.
+ */
+ public static function rollback($filename,$revision) {
+
+ if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
+ $users_view = \OCP\Files::getStorage("files_versions");
+ $users_view->chroot(\OCP\User::getUser().'/');
+
+ if (\OCP\App::isEnabled('files_sharing') && $source = \OC_Share::getSource('/'.\OCP\User::getUser().'/files'.$filename)) {
+ $pos = strpos($source, '/files', 1);
+ $uid = substr($source, 1, $pos - 1);
+ $filename = substr($source, $pos + 6);
+ } else {
+ $uid = \OCP\User::getUser();
+ }
+
+ // rollback
+ if( @$users_view->copy('files_versions'.$filename.'.v'.$revision, 'files'.$filename) ) {
+
+ return true;
+
+ }else{
+
+ return false;
+
+ }
+
+ }
+
+ }
+
+ /**
+ * check if old versions of a file exist.
+ */
+ public static function isversioned($filename) {
+ if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
+ $versions_fileview = \OCP\Files::getStorage("files_versions");
+ if (\OCP\App::isEnabled('files_sharing') && $source = \OC_Share::getSource('/'.\OCP\User::getUser().'/files'.$filename)) {
+ $pos = strpos($source, '/files', 1);
+ $filename = substr($source, $pos + 6);
+ }
+
+ $versionsFolderName=\OCP\Config::getSystemValue('datadirectory'). $versions_fileview->getAbsolutePath('');
+
+ // check for old versions
+ $matches=glob($versionsFolderName.$filename.'.v*');
+ if(count($matches)>0){
+ return true;
+ }else{
+ return false;
+ }
+ }else{
+ return(false);
+ }
+ }
+
+
+
+ /**
+ * @brief get a list of all available versions of a file in descending chronological order
+ * @param $filename file to find versions of, relative to the user files dir
+ * @param $count number of versions to return
+ * @returns array
+ */
+ public static function getVersions( $filename, $count = 0 ) {
+
+ if( \OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true' ) {
+
+ if (\OCP\App::isEnabled('files_sharing') && $source = \OC_Share::getSource('/'.\OCP\User::getUser().'/files'.$filename)) {
+ $pos = strpos($source, '/files', 1);
+ $uid = substr($source, 1, $pos - 1);
+ $filename = substr($source, $pos + 6);
+ } else {
+ $uid = \OCP\User::getUser();
+ }
+ $versions_fileview = \OCP\Files::getStorage('files_versions');
+ $versionsFolderName = \OCP\Config::getSystemValue('datadirectory'). $versions_fileview->getAbsolutePath('');
+ $versions = array();
+
+ // fetch for old versions
+ $matches = glob( $versionsFolderName.'/'.$filename.'.v*' );
+
+ sort( $matches );
+
+ $i = 0;
+
+ $files_view = \OCP\Files::getStorage('files');
+ $local_file = $files_view->getLocalFile($filename);
+ foreach( $matches as $ma ) {
+
+ $i++;
+ $versions[$i]['cur'] = 0;
+ $parts = explode( '.v', $ma );
+ $versions[$i]['version'] = ( end( $parts ) );
+
+ // if file with modified date exists, flag it in array as currently enabled version
+ ( \md5_file( $ma ) == \md5_file( $local_file ) ? $versions[$i]['fileMatch'] = 1 : $versions[$i]['fileMatch'] = 0 );
+
+ }
+
+ $versions = array_reverse( $versions );
+
+ foreach( $versions as $key => $value ) {
+
+ // flag the first matched file in array (which will have latest modification date) as current version
+ if ( $value['fileMatch'] ) {
+
+ $value['cur'] = 1;
+ break;
+
+ }
+
+ }
+
+ $versions = array_reverse( $versions );
+
+ // only show the newest commits
+ if( $count != 0 and ( count( $versions )>$count ) ) {
+
+ $versions = array_slice( $versions, count( $versions ) - $count );
+
+ }
+
+ return( $versions );
+
+
+ } else {
+
+ // if versioning isn't enabled then return an empty array
+ return( array() );
+
+ }
+
+ }
+
+ /**
+ * @brief Erase a file's versions which exceed the set quota
+ */
+ public static function expire($filename) {
+ if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
+
+ if (\OCP\App::isEnabled('files_sharing') && $source = \OC_Share::getSource('/'.\OCP\User::getUser().'/files'.$filename)) {
+ $pos = strpos($source, '/files', 1);
+ $uid = substr($source, 1, $pos - 1);
+ $filename = substr($source, $pos + 6);
+ } else {
+ $uid = \OCP\User::getUser();
+ }
+ $versions_fileview = \OCP\Files::getStorage("files_versions");
+ $versionsFolderName=\OCP\Config::getSystemValue('datadirectory'). $versions_fileview->getAbsolutePath('');
+
+ // check for old versions
+ $matches = glob( $versionsFolderName.'/'.$filename.'.v*' );
+
+ if( count( $matches ) > \OCP\Config::getSystemValue( 'files_versionmaxversions', Storage::DEFAULTMAXVERSIONS ) ) {
+
+ $numberToDelete = count( $matches-\OCP\Config::getSystemValue( 'files_versionmaxversions', Storage::DEFAULTMAXVERSIONS ) );
+
+ // delete old versions of a file
+ $deleteItems = array_slice( $matches, 0, $numberToDelete );
+
+ foreach( $deleteItems as $de ) {
+
+ unlink( $versionsFolderName.'/'.$filename.'.v'.$de );
+
+ }
+ }
+ }
+ }
+
+ /**
+ * @brief Erase all old versions of all user files
+ * @return true/false
+ */
+ public function expireAll() {
+ return $this->view->deleteAll('', true);
+ }
+}
diff --git a/apps/files_versions/settings-personal.php b/apps/files_versions/settings-personal.php new file mode 100644 index 00000000000..db80172979d --- /dev/null +++ b/apps/files_versions/settings-personal.php @@ -0,0 +1,8 @@ +<?php + +$tmpl = new OCP\Template( 'files_versions', 'settings-personal'); + +OCP\Util::addscript('files_versions','settings-personal'); + +return $tmpl->fetchPage(); +?>
\ No newline at end of file diff --git a/apps/files_versions/settings.php b/apps/files_versions/settings.php index 5f9e60fc589..f2873b8f7c2 100644 --- a/apps/files_versions/settings.php +++ b/apps/files_versions/settings.php @@ -7,4 +7,3 @@ OCP\Util::addscript( 'files_versions', 'versions' ); $tmpl = new OCP\Template( 'files_versions', 'settings'); return $tmpl->fetchPage(); -?> diff --git a/apps/files_versions/templates/history.php b/apps/files_versions/templates/history.php index 58fea75a0d0..1b442556421 100644 --- a/apps/files_versions/templates/history.php +++ b/apps/files_versions/templates/history.php @@ -20,11 +20,11 @@ if( isset( $_['message'] ) ) { echo('<p><em>Revert a file to a previous version by clicking on its revert button</em></p><br />'); foreach ( $_['versions'] as $v ) { - echo ' '; - echo OCP\Util::formatDate( $v ); - echo ' <a href="'.OCP\Util::linkTo('files_versions', 'history.php').'?path='.urlencode( $_['path'] ).'&revert='. $v .'" class="button">Revert</a><br /><br />'; - + echo OCP\Util::formatDate( doubleval($v['version']) ); + echo ' <a href="'.OCP\Util::linkTo('files_versions', 'history.php').'?path='.urlencode( $_['path'] ).'&revert='. $v['version'] .'" class="button">Revert</a><br /><br />'; + if ( $v['cur'] ) { echo ' (<b>Current</b>)'; } + echo '<br /><br />'; } } diff --git a/apps/files_versions/templates/settings-personal.php b/apps/files_versions/templates/settings-personal.php new file mode 100644 index 00000000000..fe9ba381e58 --- /dev/null +++ b/apps/files_versions/templates/settings-personal.php @@ -0,0 +1,9 @@ +<form id="versions"> + <fieldset class="personalblock"> + <legend> + <strong>Versions</strong><!-- translate using echo $l->t('foo'); --> + </legend> + <p>This will delete all existing backup versions of your files</p><!-- translate using echo $l->t('foo'); --> + <button id="expireAllBtn">Expire all versions<img style="display: none;" class="expireAllLoading" src="<?php echo OCP\Util::imagePath('core', 'loading.gif'); ?>" /></button> + </fieldset> +</form> diff --git a/apps/files_versions/versions.php b/apps/files_versions/versions.php deleted file mode 100644 index c8fc26dfb9c..00000000000 --- a/apps/files_versions/versions.php +++ /dev/null @@ -1,263 +0,0 @@ -<?php -/** - * Copyright (c) 2012 Frank Karlitschek <frank@owncloud.org> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -/** - * Versions - * - * A class to handle the versioning of files. - */ - -namespace OCA_Versions; - -class Storage { - - - // config.php configuration: - // - files_versions - // - files_versionsfolder - // - files_versionsblacklist - // - files_versionsmaxfilesize - // - files_versionsinterval - // - files_versionmaxversions - // - // todo: - // - port to oc_filesystem to enable network transparency - // - check if it works well together with encryption - // - implement expire all function. And find a place to call it ;-) - // - add transparent compression. first test if it´s worth it. - - const DEFAULTENABLED=true; - const DEFAULTFOLDER='versions'; - const DEFAULTBLACKLIST='avi mp3 mpg mp4 ctmp'; - const DEFAULTMAXFILESIZE=1048576; // 10MB - const DEFAULTMININTERVAL=120; // 2 min - const DEFAULTMAXVERSIONS=50; - - /** - * init the versioning and create the versions folder. - */ - public static function init() { - if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { - // create versions folder - $foldername=\OCP\Config::getSystemValue('datadirectory').'/'. \OCP\USER::getUser() .'/'.\OCP\Config::getSystemValue('files_versionsfolder', Storage::DEFAULTFOLDER); - if(!is_dir($foldername)){ - mkdir($foldername); - } - } - } - - - /** - * listen to write event. - */ - public static function write_hook($params) { - if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { - $path = $params[\OC_Filesystem::signal_param_path]; - if($path<>'') Storage::store($path); - } - } - - - - /** - * store a new version of a file. - */ - public static function store($filename) { - if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { - if (\OCP\App::isEnabled('files_sharing') && $source = \OC_Share::getSource('/'.\OCP\User::getUser().'/files'.$filename)) { - $pos = strpos($source, '/files', 1); - $uid = substr($source, 1, $pos - 1); - $filename = substr($source, $pos + 6); - } else { - $uid = \OCP\User::getUser(); - } - $versionsfoldername=\OCP\Config::getSystemValue('datadirectory').'/'. $uid .'/'.\OCP\Config::getSystemValue('files_versionsfolder', Storage::DEFAULTFOLDER); - $filesfoldername=\OCP\Config::getSystemValue('datadirectory').'/'. $uid .'/files'; - Storage::init(); - - // check if filename is a directory - if(is_dir($filesfoldername.'/'.$filename)){ - return false; - } - - // check filetype blacklist - $blacklist=explode(' ',\OCP\Config::getSystemValue('files_versionsblacklist', Storage::DEFAULTBLACKLIST)); - foreach($blacklist as $bl) { - $parts=explode('.', $filename); - $ext=end($parts); - if(strtolower($ext)==$bl) { - return false; - } - } - - // check filesize - if(filesize($filesfoldername.'/'.$filename)>\OCP\Config::getSystemValue('files_versionsmaxfilesize', Storage::DEFAULTMAXFILESIZE)){ - return false; - } - - - // check mininterval if the file is being modified by the owner (all shared files should be versioned despite mininterval) - if ($uid == \OCP\User::getUser()) { - $matches=glob($versionsfoldername.'/'.$filename.'.v*'); - sort($matches); - $parts=explode('.v',end($matches)); - if((end($parts)+Storage::DEFAULTMININTERVAL)>time()){ - return false; - } - } - - - // create all parent folders - $info=pathinfo($filename); - @mkdir($versionsfoldername.'/'.$info['dirname'],0700,true); - - // store a new version of a file - copy($filesfoldername.'/'.$filename,$versionsfoldername.'/'.$filename.'.v'.time()); - - // expire old revisions - Storage::expire($filename); - } - } - - - /** - * rollback to an old version of a file. - */ - public static function rollback($filename,$revision) { - - if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { - if (\OCP\App::isEnabled('files_sharing') && $source = \OC_Share::getSource('/'.\OCP\User::getUser().'/files'.$filename)) { - $pos = strpos($source, '/files', 1); - $uid = substr($source, 1, $pos - 1); - $filename = substr($source, $pos + 6); - } else { - $uid = \OCP\User::getUser(); - } - $versionsfoldername=\OCP\Config::getSystemValue('datadirectory').'/'.$uid .'/'.\OCP\Config::getSystemValue('files_versionsfolder', Storage::DEFAULTFOLDER); - - $filesfoldername=\OCP\Config::getSystemValue('datadirectory').'/'. $uid .'/files'; - - // rollback - if ( @copy($versionsfoldername.'/'.$filename.'.v'.$revision,$filesfoldername.'/'.$filename) ) { - - return true; - - }else{ - - return false; - - } - - } - - } - - /** - * check if old versions of a file exist. - */ - public static function isversioned($filename) { - if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { - if (\OCP\App::isEnabled('files_sharing') && $source = \OC_Share::getSource('/'.\OCP\User::getUser().'/files'.$filename)) { - $pos = strpos($source, '/files', 1); - $uid = substr($source, 1, $pos - 1); - $filename = substr($source, $pos + 6); - } else { - $uid = \OCP\User::getUser(); - } - $versionsfoldername=\OCP\Config::getSystemValue('datadirectory').'/'. $uid .'/'.\OCP\Config::getSystemValue('files_versionsfolder', Storage::DEFAULTFOLDER); - - // check for old versions - $matches=glob($versionsfoldername.'/'.$filename.'.v*'); - if(count($matches)>1){ - return true; - }else{ - return false; - } - }else{ - return(false); - } - } - - - - /** - * get a list of old versions of a file. - */ - public static function getversions($filename,$count=0) { - if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { - if (\OCP\App::isEnabled('files_sharing') && $source = \OC_Share::getSource('/'.\OCP\User::getUser().'/files'.$filename)) { - $pos = strpos($source, '/files', 1); - $uid = substr($source, 1, $pos - 1); - $filename = substr($source, $pos + 6); - } else { - $uid = \OCP\User::getUser(); - } - $versionsfoldername=\OCP\Config::getSystemValue('datadirectory').'/'. $uid .'/'.\OCP\Config::getSystemValue('files_versionsfolder', Storage::DEFAULTFOLDER); - $versions=array(); - - // fetch for old versions - $matches=glob($versionsfoldername.'/'.$filename.'.v*'); - sort($matches); - foreach($matches as $ma) { - $parts=explode('.v',$ma); - $versions[]=(end($parts)); - } - - // only show the newest commits - if($count<>0 and (count($versions)>$count)) { - $versions=array_slice($versions,count($versions)-$count); - } - - return($versions); - - - }else{ - return(array()); - } - } - - - - /** - * expire old versions of a file. - */ - public static function expire($filename) { - if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { - if (\OCP\App::isEnabled('files_sharing') && $source = \OC_Share::getSource('/'.\OCP\User::getUser().'/files'.$filename)) { - $pos = strpos($source, '/files', 1); - $uid = substr($source, 1, $pos - 1); - $filename = substr($source, $pos + 6); - } else { - $uid = \OCP\User::getUser(); - } - $versionsfoldername=\OCP\Config::getSystemValue('datadirectory').'/'. $uid .'/'.\OCP\Config::getSystemValue('files_versionsfolder', Storage::DEFAULTFOLDER); - - // check for old versions - $matches=glob($versionsfoldername.'/'.$filename.'.v*'); - if(count($matches)>\OCP\Config::getSystemValue('files_versionmaxversions', Storage::DEFAULTMAXVERSIONS)){ - $numbertodelete=count($matches-\OCP\Config::getSystemValue('files_versionmaxversions', Storage::DEFAULTMAXVERSIONS)); - - // delete old versions of a file - $deleteitems=array_slice($matches,0,$numbertodelete); - foreach($deleteitems as $de){ - unlink($versionsfoldername.'/'.$filename.'.v'.$de); - } - } - } - } - - /** - * expire all old versions. - */ - public static function expireall($filename) { - // todo this should go through all the versions directories and delete all the not needed files and not needed directories. - // useful to be included in a cleanup cronjob. - } - - -} diff --git a/apps/gallery/ajax/createAlbum.php b/apps/gallery/ajax/createAlbum.php index e13dac6ad1a..71e7fd1aa5e 100644 --- a/apps/gallery/ajax/createAlbum.php +++ b/apps/gallery/ajax/createAlbum.php @@ -29,5 +29,3 @@ OCP\JSON::callCheck(); OC_Gallery_Album::create(OCP\USER::getUser(), $_GET['album_name']); OCP\JSON::success(array('name' => $_GET['album_name'])); - -?> diff --git a/apps/gallery/ajax/galleryOp.php b/apps/gallery/ajax/galleryOp.php index 0cd825f3e50..ab8c64e28ad 100644 --- a/apps/gallery/ajax/galleryOp.php +++ b/apps/gallery/ajax/galleryOp.php @@ -42,7 +42,8 @@ function handleRemove($name) { function handleGetThumbnails($albumname) { OCP\Response::enableCaching(3600 * 24); // 24 hour - $thumbnail = OC::$CONFIG_DATADIRECTORY.'/../gallery/'.urldecode($albumname).'.png'; + $view = OCP\Files::getStorage('gallery'); + $thumbnail = $view->fopen(urldecode($albumname).'.png', 'r'); header('Content-Type: '.OC_Image::getMimeTypeForFile($thumbnail)); OCP\Response::sendFile($thumbnail); } @@ -185,4 +186,3 @@ if ($_GET['operation']) { OCP\JSON::error(array('cause' => 'Unknown operation')); } } -?> diff --git a/apps/gallery/ajax/sharing.php b/apps/gallery/ajax/sharing.php index c3d5989ae57..bf8169ef1e2 100644 --- a/apps/gallery/ajax/sharing.php +++ b/apps/gallery/ajax/sharing.php @@ -81,7 +81,8 @@ function handleGetThumbnail($token, $imgpath) { function handleGetAlbumThumbnail($token, $albumname) { $owner = OC_Gallery_Sharing::getTokenOwner($token); - $file = OCP\Config::getSystemValue("datadirectory").'/'. $owner .'/gallery/'.$albumname.'.png'; + $view = OCP\Files::getStorage('gallery'); + $file = $view->fopen($albumname.'.png', 'r'); $image = new OC_Image($file); if ($image->valid()) { $image->centerCrop(); @@ -94,7 +95,8 @@ function handleGetAlbumThumbnail($token, $albumname) function handleGetPhoto($token, $photo) { $owner = OC_Gallery_Sharing::getTokenOwner($token); - $file = OCP\Config::getSystemValue( "datadirectory", OC::$SERVERROOT."/data" ).'/'.$owner.'/files'.urldecode($photo); + $view = OCP\Files::getStorage('files'); + $file = $view->fopen(urldecode($photo), 'r'); header('Content-Type: '.OC_Image::getMimeTypeForFile($file)); OCP\Response::sendFile($file); } diff --git a/apps/gallery/ajax/thumbnail.php b/apps/gallery/ajax/thumbnail.php index 4fc9eba992d..5bf1d420dc4 100644 --- a/apps/gallery/ajax/thumbnail.php +++ b/apps/gallery/ajax/thumbnail.php @@ -23,6 +23,7 @@ OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('gallery'); +session_write_close(); require_once('apps/gallery/lib/managers.php'); diff --git a/apps/gallery/ajax/viewImage.php b/apps/gallery/ajax/viewImage.php new file mode 100644 index 00000000000..daf0ab741f0 --- /dev/null +++ b/apps/gallery/ajax/viewImage.php @@ -0,0 +1,32 @@ +<?php + +/** + * ownCloud - gallery application + * + * @author Ike Devolder + * @copyright 2012 Ike Devolder + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ +OCP\JSON::checkLoggedIn(); +OCP\JSON::checkAppEnabled('gallery'); + +$img = $_GET['img']; + +$image = OC_Gallery_Photo::getViewImage($img); +if ($image) { + OCP\Response::enableCaching(3600 * 24); // 24 hour + $image->show(); +} diff --git a/apps/gallery/appinfo/app.php b/apps/gallery/appinfo/app.php index 3d38b85b7e0..9103f66441d 100644 --- a/apps/gallery/appinfo/app.php +++ b/apps/gallery/appinfo/app.php @@ -21,20 +21,18 @@ * */ -OC::$CLASSPATH['OC_Gallery_Album'] = 'apps/gallery/lib/album.php'; -OC::$CLASSPATH['OC_Gallery_Photo'] = 'apps/gallery/lib/photo.php'; -OC::$CLASSPATH['OC_Gallery_Scanner'] = 'apps/gallery/lib/scanner.php'; -OC::$CLASSPATH['OC_Gallery_Sharing'] = 'apps/gallery/lib/sharing.php'; -OC::$CLASSPATH['OC_Gallery_Hooks_Handlers'] = 'apps/gallery/lib/hooks_handlers.php'; -OC::$CLASSPATH['Pictures_Managers'] = 'apps/gallery/lib/managers.php'; -OC::$CLASSPATH['Pictures_Tiles'] = 'apps/gallery/lib/tiles.php'; +OC::$CLASSPATH['OC_Gallery_Album'] = 'gallery/lib/album.php'; +OC::$CLASSPATH['OC_Gallery_Photo'] = 'gallery/lib/photo.php'; +OC::$CLASSPATH['OC_Gallery_Scanner'] = 'gallery/lib/scanner.php'; +OC::$CLASSPATH['OC_Gallery_Sharing'] = 'gallery/lib/sharing.php'; +OC::$CLASSPATH['OC_Gallery_Hooks_Handlers'] = 'gallery/lib/hooks_handlers.php'; +OC::$CLASSPATH['Pictures_Managers'] = 'gallery/lib/managers.php'; +OC::$CLASSPATH['Pictures_Tiles'] = 'gallery/lib/tiles.php'; +OC::$CLASSPATH['OC_Share_Backend_Photo'] = 'gallery/lib/share.php'; -$l = OC_L10N::get('gallery'); +// OCP\Share::registerBackend('photo', new OC_Share_Backend_Photo()); -OCP\App::register(array( - 'order' => 20, - 'id' => 'gallery', - 'name' => 'Pictures')); +$l = OC_L10N::get('gallery'); OCP\App::addNavigationEntry( array( 'id' => 'gallery_index', @@ -45,7 +43,7 @@ OCP\App::addNavigationEntry( array( class OC_GallerySearchProvider extends OC_Search_Provider{ function search($query){ - $stmt = OCP\DB::prepare('SELECT * FROM `*PREFIX*gallery_albums` WHERE `uid_owner` = ? AND `album_name` LIKE ?'); + $stmt = OCP\DB::prepare('SELECT * FROM *PREFIX*gallery_albums WHERE uid_owner = ? AND album_name LIKE ?'); $result = $stmt->execute(array(OCP\USER::getUser(),'%'.$query.'%')); $results=array(); while($row=$result->fetchRow()){ @@ -57,4 +55,4 @@ class OC_GallerySearchProvider extends OC_Search_Provider{ //OC_Search::registerProvider('OC_GallerySearchProvider'); -require_once('apps/gallery/lib/hooks_handlers.php'); +require_once('gallery/lib/hooks_handlers.php'); diff --git a/apps/gallery/appinfo/update.php b/apps/gallery/appinfo/update.php index c112bb3f890..c1d22127428 100644 --- a/apps/gallery/appinfo/update.php +++ b/apps/gallery/appinfo/update.php @@ -2,10 +2,10 @@ $currentVersion=OC_Appconfig::getValue('gallery', 'installed_version'); if (version_compare($currentVersion, '0.5.0', '<')) { - $stmt = OCP\DB::prepare('DROP TABLE IF EXISTS `*PREFIX*gallery_photos`'); + $stmt = OCP\DB::prepare('DROP TABLE IF EXISTS *PREFIX*gallery_photos'); $stmt->execute(); - $stmt = OCP\DB::prepare('DROP TABLE IF EXISTS `*PREFIX*gallery_albums`'); + $stmt = OCP\DB::prepare('DROP TABLE IF EXISTS *PREFIX*gallery_albums'); $stmt->execute(); - \OC_DB::createDbFromStructure(OC::$APPSROOT.'/apps/'.$appid.'/appinfo/database.xml'); + \OC_DB::createDbFromStructure(OC_App::getAppPath($appid).'/appinfo/database.xml'); } diff --git a/apps/gallery/appinfo/version b/apps/gallery/appinfo/version index 8f0916f768f..4b9fcbec101 100644 --- a/apps/gallery/appinfo/version +++ b/apps/gallery/appinfo/version @@ -1 +1 @@ -0.5.0 +0.5.1 diff --git a/apps/gallery/css/sharing.css b/apps/gallery/css/sharing.css index d061fc3e6c1..4712369cc84 100644 --- a/apps/gallery/css/sharing.css +++ b/apps/gallery/css/sharing.css @@ -5,4 +5,4 @@ div.gallery_box:hover { color: black; } div.gallery_box h1 {font-size: 17px; font-weight: normal;} div#breadcrumb { border: 0; width: 70%; margin: 0 auto; padding: 25px 0; font-family: Verdana; text-align: center;} span.breadcrumbelement { margin: 10px; margin-right: 0; cursor: pointer;} -span.inside { background-image: url('%appswebroot%/apps/gallery/img/breadcrumb.png'); padding-left: 20px; background-position: left; background-repeat: no-repeat;} +span.inside { background-image: url('%appswebroot%/gallery/img/breadcrumb.png'); padding-left: 20px; background-position: left; background-repeat: no-repeat;} diff --git a/apps/gallery/css/styles.css b/apps/gallery/css/styles.css index 98d36515493..63f645662dd 100644 --- a/apps/gallery/css/styles.css +++ b/apps/gallery/css/styles.css @@ -1,6 +1,14 @@ #gallerycontent { margin-top: 2.8em; overflow: visible; } -#g-settings {position: absolute; left 13.5em; top: 0;} +#g-settings {position: absolute; left: 13.5em; top: 0;} div#controls input[type=button] { -webkit-transition: opacity 0.5s ease-in-out; -moz-transition: opacity 0.5s ease-in-out; -o-transition: opacity 0.5s ease-in-out; opacity: 1; position:absolute; right:13.5em; top:0em; } input[type=button]:disabled { opacity: 0.5 } .ui-dialog tr {background-color: #eee;} .ui-dialog input {width: 90%;} + +div.gallery_div {position:relative; display: inline-block; height: 150px; width: 150px; margin: 5px;} +div.miniature_border {position:absolute; height: 150px; -moz-transition-duration: 0.2s; -o-transition-duration:0.2s; -webkit-transition-duration: .2s; background-position: 50%;} +div.line {display:inline-block; border: 0; width: auto; height: 160px} +div.gallery_div img{position:absolute; top: 1; left: 0; -moz-transition-duration: 0.3s; -o-transition-duration:0.3s; -webkit-transition-duration: 0.3s; height:150px; width: auto;} +div.gallery_div img.shrinker {width:80px !important;} +div.title { opacity: 0; text-align: center; vertical-align: middle; font-family: Arial; font-size: 12px; border: 0; position: absolute; text-overflow: ellipsis; bottom: 20px; right:-5px; height:auto; padding: 5px; width: 140px; background-color: black; color: white; -webkit-transition: opacity 0.5s; z-index:1000; border-radius: 7px} +div.visible { opacity: 0.8;} diff --git a/apps/gallery/css/supersized.css b/apps/gallery/css/supersized.css new file mode 100644 index 00000000000..57ee7e23a50 --- /dev/null +++ b/apps/gallery/css/supersized.css @@ -0,0 +1,25 @@ +/* + + Supersized - Fullscreen Slideshow jQuery Plugin + Version : 3.2.7 + Site : www.buildinternet.com/project/supersized + + Author : Sam Dunn + Company : One Mighty Roar (www.onemightyroar.com) + License : MIT License / GPL License + +*/ +#supersized-holder #supersized-loader { display:none; position:absolute; top:50%; left:50%; z-index:0; width:60px; height:60px; margin:-30px 0 0 -30px; text-indent:-999em; background:url('%appswebroot%/gallery/img/supersized/progress.gif') no-repeat center center;} + +#supersized-holder #supersized { visibility:hidden; display:block; position:fixed; left:0; top:0; overflow:hidden; z-index:200; height:100%; width:100%; } +#supersized-holder #supersized img { width:auto; height:auto; position:relative; display:none; outline:none; border:none; } +#supersized-holder #supersized.speed img { -ms-interpolation-mode:nearest-neighbor; image-rendering: -moz-crisp-edges; } /*Speed*/ +#supersized-holder #supersized.quality img { -ms-interpolation-mode:bicubic; image-rendering: optimizeQuality; } /*Quality*/ + +#supersized-holder #supersized li { display:block; list-style:none; z-index:150; position:fixed; overflow:hidden; top:0; left:0; width:100%; height:100%; background:#111; } +#supersized-holder #supersized a { width:100%; height:100%; display:block; } +#supersized-holder #supersized li.prevslide { z-index:160; } +#supersized-holder #supersized li.activeslide { z-index:170; } +#supersized-holder #supersized li.image-loading { background:#111 url('%appswebroot%/gallery/img/supersized/progress.gif') no-repeat center center; width:100%; height:100%; } +#supersized-holder #supersized li.image-loading img{ visibility:hidden; } +#supersized-holder #supersized li.prevslide img, #supersized-holder #supersized li.activeslide img{ display:inline; } diff --git a/apps/gallery/css/supersized.shutter.css b/apps/gallery/css/supersized.shutter.css new file mode 100644 index 00000000000..428c254c3b2 --- /dev/null +++ b/apps/gallery/css/supersized.shutter.css @@ -0,0 +1,74 @@ +/* + + Supersized - Fullscreen Slideshow jQuery Plugin + Version : 3.2.7 + Site : www.buildinternet.com/project/supersized + + Theme : Shutter 1.2 + Author : Sam Dunn + Company : One Mighty Roar (www.onemightyroar.com) + License : MIT License / GPL License + +*/ + +/* Controls Bar +----------------------------*/ +#slideshow-content #slideshow-controls-wrapper { margin:0 auto; height:42px; width:100%; bottom:0px; left:0; z-index:204; background:url('%appswebroot%/gallery/img/supersized/nav-bg.png') repeat-x; position:fixed; } +#slideshow-content #slideshow-controls { overflow:hidden; height:100%; position:relative; text-align:left; z-index:205; } +#slideshow-content #slidecounter { float:left; color:#999; font:14px "Helvetica Neue", Helvetica, Arial, sans-serif; text-shadow:#000 0 -1px 0; margin:0px 10px 0 15px; line-height:42px; } +#slideshow-content #slidecaption { overflow:hidden; float:left; color:#FFF; font:400 14px "Helvetica Neue", Helvetica, Arial, sans-serif; text-shadow:#000 1px 1px 2px; margin:0 20px 0 0; line-height:42px; } + +/*#navigation { float:right; margin:0px 20px 0 0; }*/ +#slideshow-content #play-button{ float:left; margin-top:1px;border-right:1px solid #333; background:url('%appswebroot%/gallery/img/supersized/bg-hover.png') repeat-x 0 44px; } +#slideshow-content #play-button:hover{ background-position:0 1px; cursor:pointer; } + +#slideshow-content #prevslide, #nextslide{ position:fixed; height:43px; width:43px; top:50%; margin-top:-21px; opacity:0.6; z-index:204; } +#slideshow-content #prevslide{ left:10px; background:url('%appswebroot%/gallery/img/supersized/back.png'); } +#slideshow-content #nextslide{ right:10px; background:url('%appswebroot%/gallery/img/supersized/forward.png'); } +#slideshow-content #prevslide:active, #nextslide:active{ margin-top:-19px; } +#slideshow-content #prevslide:hover, #nextslide:hover{ cursor:pointer; } + +#slideshow-content ul#slide-list{ padding:15px 0; float:left; position:absolute; left:50%; } +#slideshow-content ul#slide-list li{ list-style:none; width:12px; height:12px; float:left; margin:0 5px 0 0; } +#slideshow-content ul#slide-list li.current-slide a, ul#slide-list li.current-slide a:hover{ background-position:0 0px; } +#slideshow-content ul#slide-list li a{ display:block; width:12px; height:12px; background:url('%appswebroot%/gallery/img/supersized/nav-dot.png') no-repeat 0 -24px; } +#slideshow-content ul#slide-list li a:hover{ background-position:0 -12px; cursor:pointer; } + +#slideshow-content #tray-button{ float:right; margin-top:1px; border-left:1px solid #333; background:url('%appswebroot%/gallery/img/supersized/bg-hover.png') repeat-x 0 44px; } +#slideshow-content #tray-button:hover{ background-position:0 1px; cursor:pointer; } + + +/* Progress Bar +----------------------------*/ +#slideshow-content #progress-back{ z-index:205; position:fixed; bottom:42px; left:0; height:8px; width:100%; background:url('%appswebroot%/gallery/img/supersized/progress-back.png') repeat-x; } +#slideshow-content #progress-bar{ position:relative; height:8px; width:100%; background:url('%appswebroot%/gallery/img/supersized/progress-bar.png') repeat-x; } + + +/* Thumbnail Navigation +----------------------------*/ +#slideshow-content #nextthumb, #slideshow-content #prevthumb { z-index:202; display:none; position:fixed; bottom:61px; height:75px; width:100px; overflow:hidden; background:#ddd; border:1px solid #fff; -webkit-box-shadow:0 0 5px #000; } +#slideshow-content #nextthumb { right:12px; } +#slideshow-content #prevthumb { left:12px; } +#slideshow-content #nextthumb img, #slideshow-content #prevthumb img { width:150px; height:auto; } +#slideshow-content #nextthumb:active, #slideshow-content #prevthumb:active { bottom:59px; } +#slideshow-content #nextthumb:hover, #slideshow-content #prevthumb:hover { cursor:pointer; } + + +/* Thumbnail Tray +----------------------------*/ +#slideshow-content #thumb-tray{ position:fixed; z-index:203; bottom:0; left:0; background:url('%appswebroot%/gallery/img/supersized/bg-black.png'); height:150px; width:100%; overflow:hidden; text-align:center; -moz-box-shadow: 0px 0px 4px #000; -webkit-box-shadow: 0px 0px 4px #000; box-shadow: 0px 0px 4px #000; } + +#slideshow-content #thumb-back, #slideshow-content #thumb-forward{ position:absolute; z-index:5; bottom:42px; height:108px; width:40px; } +#slideshow-content #thumb-back{ left:0; background: url('%appswebroot%/gallery/img/supersized/thumb-back.png') no-repeat center center;} +#slideshow-content #thumb-forward{ right:0; background:url('%appswebroot%/gallery/img/supersized/thumb-forward.png') no-repeat center center;} +#slideshow-content #thumb-back:hover, #slideshow-content #thumb-forward:hover{ cursor:pointer; background-color:rgba(256,256,256, 0.1); } +#slideshow-content #thumb-back:hover{ border-right:1px solid rgba(256,256,256, 0.2); } +#slideshow-content #thumb-forward:hover{ border-left:1px solid rgba(256,256,256, 0.2); } + + +#slideshow-content ul#thumb-list{ display:inline-block; list-style:none; position:relative; left:0px; padding:0 0px; } +#slideshow-content ul#thumb-list li{ background:#111; list-style:none; display:inline; width:150px; height:108px; overflow:hidden; float:left; margin:0; } +#slideshow-content ul#thumb-list li img { width:200px; height:auto; opacity:0.5; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; filter:alpha(opacity=60); -webkit-transition: all 100ms ease-in-out; -moz-transition: all 100ms ease-in-out; -o-transition: all 100ms ease-in-out; -ms-transition: all 100ms ease-in-out; transition: all 100ms ease-in-out; } +#slideshow-content ul#thumb-list li.current-thumb img, #slideshow-content ul#thumb-list li:hover img{ opacity:1; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; filter:alpha(opacity=100); } +#slideshow-content ul#thumb-list li:hover{ cursor:pointer; } + diff --git a/apps/gallery/img/breadcrumb.png b/apps/gallery/img/breadcrumb.png Binary files differindex a252a751554..adbef1e576d 100644 --- a/apps/gallery/img/breadcrumb.png +++ b/apps/gallery/img/breadcrumb.png diff --git a/apps/gallery/img/delete.png b/apps/gallery/img/delete.png Binary files differindex bc0c782882d..fa8e18183ed 100644 --- a/apps/gallery/img/delete.png +++ b/apps/gallery/img/delete.png diff --git a/apps/gallery/img/rename.png b/apps/gallery/img/rename.png Binary files differindex 9993a092df1..3af6840071b 100644 --- a/apps/gallery/img/rename.png +++ b/apps/gallery/img/rename.png diff --git a/apps/gallery/img/share.png b/apps/gallery/img/share.png Binary files differindex 62c4627f317..099e4d6ab35 100644 --- a/apps/gallery/img/share.png +++ b/apps/gallery/img/share.png diff --git a/apps/gallery/img/supersized/back.png b/apps/gallery/img/supersized/back.png Binary files differnew file mode 100644 index 00000000000..f0e809ece09 --- /dev/null +++ b/apps/gallery/img/supersized/back.png diff --git a/apps/gallery/img/supersized/bg-black.png b/apps/gallery/img/supersized/bg-black.png Binary files differnew file mode 100644 index 00000000000..e7ce54412b8 --- /dev/null +++ b/apps/gallery/img/supersized/bg-black.png diff --git a/apps/gallery/img/supersized/bg-hover.png b/apps/gallery/img/supersized/bg-hover.png Binary files differnew file mode 100644 index 00000000000..1ca2022e106 --- /dev/null +++ b/apps/gallery/img/supersized/bg-hover.png diff --git a/apps/gallery/img/supersized/button-tray-down.png b/apps/gallery/img/supersized/button-tray-down.png Binary files differnew file mode 100644 index 00000000000..15645ae34ef --- /dev/null +++ b/apps/gallery/img/supersized/button-tray-down.png diff --git a/apps/gallery/img/supersized/button-tray-up.png b/apps/gallery/img/supersized/button-tray-up.png Binary files differnew file mode 100644 index 00000000000..0ee91d25ba0 --- /dev/null +++ b/apps/gallery/img/supersized/button-tray-up.png diff --git a/apps/gallery/img/supersized/forward.png b/apps/gallery/img/supersized/forward.png Binary files differnew file mode 100644 index 00000000000..a6408f75118 --- /dev/null +++ b/apps/gallery/img/supersized/forward.png diff --git a/apps/gallery/img/supersized/nav-bg.png b/apps/gallery/img/supersized/nav-bg.png Binary files differnew file mode 100644 index 00000000000..c1a9fc170f4 --- /dev/null +++ b/apps/gallery/img/supersized/nav-bg.png diff --git a/apps/gallery/img/supersized/nav-dot.png b/apps/gallery/img/supersized/nav-dot.png Binary files differnew file mode 100644 index 00000000000..4319ed04213 --- /dev/null +++ b/apps/gallery/img/supersized/nav-dot.png diff --git a/apps/gallery/img/supersized/pause.png b/apps/gallery/img/supersized/pause.png Binary files differnew file mode 100644 index 00000000000..1e81d1856b6 --- /dev/null +++ b/apps/gallery/img/supersized/pause.png diff --git a/apps/gallery/img/supersized/play.png b/apps/gallery/img/supersized/play.png Binary files differnew file mode 100644 index 00000000000..df94a034986 --- /dev/null +++ b/apps/gallery/img/supersized/play.png diff --git a/apps/gallery/img/supersized/progress-back.png b/apps/gallery/img/supersized/progress-back.png Binary files differnew file mode 100644 index 00000000000..95992f1410c --- /dev/null +++ b/apps/gallery/img/supersized/progress-back.png diff --git a/apps/gallery/img/supersized/progress-bar.png b/apps/gallery/img/supersized/progress-bar.png Binary files differnew file mode 100644 index 00000000000..9758d178831 --- /dev/null +++ b/apps/gallery/img/supersized/progress-bar.png diff --git a/apps/gallery/img/supersized/progress.gif b/apps/gallery/img/supersized/progress.gif Binary files differnew file mode 100644 index 00000000000..f3e45e0569c --- /dev/null +++ b/apps/gallery/img/supersized/progress.gif diff --git a/apps/gallery/img/supersized/supersized-logo.png b/apps/gallery/img/supersized/supersized-logo.png Binary files differnew file mode 100644 index 00000000000..cc106293292 --- /dev/null +++ b/apps/gallery/img/supersized/supersized-logo.png diff --git a/apps/gallery/img/supersized/thumb-back.png b/apps/gallery/img/supersized/thumb-back.png Binary files differnew file mode 100644 index 00000000000..b86a9110ee4 --- /dev/null +++ b/apps/gallery/img/supersized/thumb-back.png diff --git a/apps/gallery/img/supersized/thumb-forward.png b/apps/gallery/img/supersized/thumb-forward.png Binary files differnew file mode 100644 index 00000000000..bff5ec1108a --- /dev/null +++ b/apps/gallery/img/supersized/thumb-forward.png diff --git a/apps/gallery/index.php b/apps/gallery/index.php index b87d99bb6cc..ce79f8f8782 100644 --- a/apps/gallery/index.php +++ b/apps/gallery/index.php @@ -30,7 +30,76 @@ OCP\App::setActiveNavigationEntry( 'gallery_index' ); OCP\Util::addStyle('files', 'files'); OCP\Util::addStyle('gallery', 'styles'); OCP\Util::addScript('gallery', 'pictures'); +OCP\Util::addStyle( 'gallery', 'supersized' ); +OCP\Util::addStyle( 'gallery', 'supersized.shutter' ); +OCP\Util::addScript('gallery', 'slideshow'); +OCP\Util::addScript('gallery', 'jquery.easing.min'); +OCP\Util::addScript('gallery', 'supersized.3.2.7.min'); +OCP\Util::addScript('gallery', 'supersized.shutter.min'); + +include 'gallery/lib/tiles.php'; + +$root = !empty($_GET['root']) ? $_GET['root'] : '/'; +$images = \OC_FileCache::searchByMime('image', null, '/'.\OCP\USER::getUser().'/files'.$root); +sort($images); + +$tl = new \OC\Pictures\TilesLine(); +$ts = new \OC\Pictures\TileStack(array(), ''); +$previous_element = @$images[0]; + +$root_images = array(); +$second_level_images = array(); + +$fallback_images = array(); // if the folder only cotains subfolders with images -> these are taken for the stack preview + +for($i = 0; $i < count($images); $i++) { + $prev_dir_arr = explode('/', $previous_element); + $dir_arr = explode('/', $images[$i]); + + if(count($dir_arr) == 1) { // getting the images in this directory + $root_images[] = $root.$images[$i]; + } else { + if(strcmp($prev_dir_arr[0], $dir_arr[0]) != 0) { // if we entered a new directory + if(count($second_level_images) == 0) { // if we don't have images in this directory + if(count($fallback_images) != 0) { // but have fallback_images + $tl->addTile(new \OC\Pictures\TileStack($fallback_images, $prev_dir_arr[0])); + $fallback_images = array(); + } + } else { // if we collected images for this directory + $tl->addTile(new \OC\Pictures\TileStack($second_level_images, $prev_dir_arr[0])); + $fallback_images = array(); + $second_level_images = array(); + } + } + if (count($dir_arr) == 2) { // These are the pics in our current subdir + $second_level_images[] = $root.$images[$i]; + $fallback_images = array(); + } else { // These are images from the deeper directories + if(count($second_level_images) == 0) { + $fallback_images[] = $root.$images[$i]; + } + } + // have us a little something to compare against + $previous_element = $images[$i]; + } +} + +// if last element in the directory was a directory we don't want to miss it :) +if(count($second_level_images)>0) { + $tl->addTile(new \OC\Pictures\TileStack($second_level_images, $prev_dir_arr[0])); +} + +// if last element in the directory was a directory with no second_level_images we also don't want to miss it ... +if(count($fallback_images)>0) { + $tl->addTile(new \OC\Pictures\TileStack($fallback_images, $prev_dir_arr[0])); +} + +// and finally our images actually stored in the root folder +for($i = 0; $i<count($root_images); $i++) { + $tl->addTile(new \OC\Pictures\TileSingle($root_images[$i])); +} $tmpl = new OCP\Template( 'gallery', 'index', 'user' ); +$tmpl->assign('root', $root, false); +$tmpl->assign('tl', $tl, false); $tmpl->printPage(); -?> diff --git a/apps/gallery/js/albums.js b/apps/gallery/js/albums.js index 413c71471a3..62d3f783ece 100644 --- a/apps/gallery/js/albums.js +++ b/apps/gallery/js/albums.js @@ -79,7 +79,7 @@ Albums={ }); element.append(local); } - var photoDisplayTemplate = '<div class="gallery_box"><div class="dummy"></div><div><a rel="images" href="'+OC.linkTo('files','download.php')+'?file=URLPATH"><img src="'+OC.filePath('gallery','ajax','thumbnail.php')+'?img=IMGPATH"></a></div></div>'; + var photoDisplayTemplate = '<div class="gallery_box"><div class="dummy"></div><div><a rel="images" href="'+OC.linkTo('gallery/ajax','viewImage.php')+'?img=URLPATH"><img src="'+OC.filePath('gallery','ajax','thumbnail.php')+'?img=IMGPATH"></a></div></div>'; for (var i in Albums.photos) { element.append(photoDisplayTemplate.replace("IMGPATH", escape(Albums.photos[i])).replace("URLPATH", escape(Albums.photos[i]))); } diff --git a/apps/gallery/js/jquery.easing.min.js b/apps/gallery/js/jquery.easing.min.js new file mode 100644 index 00000000000..bbf8410391a --- /dev/null +++ b/apps/gallery/js/jquery.easing.min.js @@ -0,0 +1,71 @@ +/* + * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/ + * + * Uses the built in easing capabilities added In jQuery 1.1 + * to offer multiple easing options + * + * TERMS OF USE - jQuery Easing + * + * Open source under the BSD License. + * + * Copyright å© 2008 George McGinley Smith + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the author nor the names of contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * +*/ + +jQuery.easing.jswing=jQuery.easing.swing;jQuery.extend(jQuery.easing,{def:"easeOutQuad",swing:function(e,f,a,h,g){return jQuery.easing[jQuery.easing.def](e,f,a,h,g)},easeInQuad:function(e,f,a,h,g){return h*(f/=g)*f+a},easeOutQuad:function(e,f,a,h,g){return -h*(f/=g)*(f-2)+a},easeInOutQuad:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f+a}return -h/2*((--f)*(f-2)-1)+a},easeInCubic:function(e,f,a,h,g){return h*(f/=g)*f*f+a},easeOutCubic:function(e,f,a,h,g){return h*((f=f/g-1)*f*f+1)+a},easeInOutCubic:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f+a}return h/2*((f-=2)*f*f+2)+a},easeInQuart:function(e,f,a,h,g){return h*(f/=g)*f*f*f+a},easeOutQuart:function(e,f,a,h,g){return -h*((f=f/g-1)*f*f*f-1)+a},easeInOutQuart:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f*f+a}return -h/2*((f-=2)*f*f*f-2)+a},easeInQuint:function(e,f,a,h,g){return h*(f/=g)*f*f*f*f+a},easeOutQuint:function(e,f,a,h,g){return h*((f=f/g-1)*f*f*f*f+1)+a},easeInOutQuint:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f*f*f+a}return h/2*((f-=2)*f*f*f*f+2)+a},easeInSine:function(e,f,a,h,g){return -h*Math.cos(f/g*(Math.PI/2))+h+a},easeOutSine:function(e,f,a,h,g){return h*Math.sin(f/g*(Math.PI/2))+a},easeInOutSine:function(e,f,a,h,g){return -h/2*(Math.cos(Math.PI*f/g)-1)+a},easeInExpo:function(e,f,a,h,g){return(f==0)?a:h*Math.pow(2,10*(f/g-1))+a},easeOutExpo:function(e,f,a,h,g){return(f==g)?a+h:h*(-Math.pow(2,-10*f/g)+1)+a},easeInOutExpo:function(e,f,a,h,g){if(f==0){return a}if(f==g){return a+h}if((f/=g/2)<1){return h/2*Math.pow(2,10*(f-1))+a}return h/2*(-Math.pow(2,-10*--f)+2)+a},easeInCirc:function(e,f,a,h,g){return -h*(Math.sqrt(1-(f/=g)*f)-1)+a},easeOutCirc:function(e,f,a,h,g){return h*Math.sqrt(1-(f=f/g-1)*f)+a},easeInOutCirc:function(e,f,a,h,g){if((f/=g/2)<1){return -h/2*(Math.sqrt(1-f*f)-1)+a}return h/2*(Math.sqrt(1-(f-=2)*f)+1)+a},easeInElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k)==1){return e+l}if(!j){j=k*0.3}if(g<Math.abs(l)){g=l;var i=j/4}else{var i=j/(2*Math.PI)*Math.asin(l/g)}return -(g*Math.pow(2,10*(h-=1))*Math.sin((h*k-i)*(2*Math.PI)/j))+e},easeOutElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k)==1){return e+l}if(!j){j=k*0.3}if(g<Math.abs(l)){g=l;var i=j/4}else{var i=j/(2*Math.PI)*Math.asin(l/g)}return g*Math.pow(2,-10*h)*Math.sin((h*k-i)*(2*Math.PI)/j)+l+e},easeInOutElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k/2)==2){return e+l}if(!j){j=k*(0.3*1.5)}if(g<Math.abs(l)){g=l;var i=j/4}else{var i=j/(2*Math.PI)*Math.asin(l/g)}if(h<1){return -0.5*(g*Math.pow(2,10*(h-=1))*Math.sin((h*k-i)*(2*Math.PI)/j))+e}return g*Math.pow(2,-10*(h-=1))*Math.sin((h*k-i)*(2*Math.PI)/j)*0.5+l+e},easeInBack:function(e,f,a,i,h,g){if(g==undefined){g=1.70158}return i*(f/=h)*f*((g+1)*f-g)+a},easeOutBack:function(e,f,a,i,h,g){if(g==undefined){g=1.70158}return i*((f=f/h-1)*f*((g+1)*f+g)+1)+a},easeInOutBack:function(e,f,a,i,h,g){if(g==undefined){g=1.70158}if((f/=h/2)<1){return i/2*(f*f*(((g*=(1.525))+1)*f-g))+a}return i/2*((f-=2)*f*(((g*=(1.525))+1)*f+g)+2)+a},easeInBounce:function(e,f,a,h,g){return h-jQuery.easing.easeOutBounce(e,g-f,0,h,g)+a},easeOutBounce:function(e,f,a,h,g){if((f/=g)<(1/2.75)){return h*(7.5625*f*f)+a}else{if(f<(2/2.75)){return h*(7.5625*(f-=(1.5/2.75))*f+0.75)+a}else{if(f<(2.5/2.75)){return h*(7.5625*(f-=(2.25/2.75))*f+0.9375)+a}else{return h*(7.5625*(f-=(2.625/2.75))*f+0.984375)+a}}}},easeInOutBounce:function(e,f,a,h,g){if(f<g/2){return jQuery.easing.easeInBounce(e,f*2,0,h,g)*0.5+a}return jQuery.easing.easeOutBounce(e,f*2-g,0,h,g)*0.5+h*0.5+a}}); + +/* + * + * TERMS OF USE - EASING EQUATIONS + * + * Open source under the BSD License. + * + * Copyright å© 2001 Robert Penner + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the author nor the names of contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */
\ No newline at end of file diff --git a/apps/gallery/js/pictures.js b/apps/gallery/js/pictures.js index 678c9bcbf55..47f727e0dee 100644 --- a/apps/gallery/js/pictures.js +++ b/apps/gallery/js/pictures.js @@ -1,4 +1,3 @@ - function constructSharingPath() { return document.location.protocol + '//' + document.location.host + OC.linkTo('', 'public.php') + '?service=gallery&token=' + Albums.token; } @@ -35,3 +34,35 @@ function shareGallery() { }); }); } + +function explode(element) { + $('div', element).each(function(index, elem) { + if ($(elem).hasClass('title')) { + $(elem).addClass('visible'); + } else { + $(elem).css('margin-top', Math.floor(30-(Math.random()*60)) + 'px') + .css('margin-left', Math.floor(30-(Math.random()*60))+ 'px') + .css('z-index', '999'); + } + }); +} + +function deplode(element) { + $('div', element).each(function(index, elem) { + if ($(elem).hasClass('title')) { + $(elem).removeClass('visible'); + } else { + $(elem).css('margin-top', Math.floor(5-(Math.random()*10)) + 'px') + .css('margin-left', Math.floor(5-(Math.random()*10))+ 'px') + .css('z-index', '3'); + } + }); +} + +function openNewGal(album_name) { + root = root + decodeURIComponent(album_name) + "/"; + var url = window.location.protocol+"//"+window.location.hostname+OC.linkTo('gallery', 'index.php'); + url = url + "?root="+encodeURIComponent(root); + + window.location = url; +} diff --git a/apps/gallery/js/slideshow.js b/apps/gallery/js/slideshow.js new file mode 100644 index 00000000000..cc5dfc44a26 --- /dev/null +++ b/apps/gallery/js/slideshow.js @@ -0,0 +1,58 @@ +$(document).ready(function(){ + + $.endSlideshow = function () { + if($.supersized.vars.slideshow_interval){ + clearInterval($.supersized.vars.slideshow_interval); + }; + + $('#supersized-holder').remove(); + $('#slideshow-content').hide(); + $('#thumb-list').remove(); + } + + // add slideshow in holder div + $('#slideshow input.start').click(function(){ + + var images=[]; + $('#gallerycontent div a').each(function(i,a){ + images.push({image : a.href, title : a.title.replace(/</, '<').replace(/>/, '>'), thumb : a.children[0].src, url : 'javascript:$.endSlideshow()'}); + }); + + if (images.length <= 0) { + return; + } + + $('body').append("<div id='supersized-holder'></div>"); + $('#supersized-loader').remove(); + $('#supersized').remove(); + $('#supersized-holder').append("<div id='supersized-loader'></div><ul id='supersized'></ul>"); + $('#supersized').show(); + $('#slideshow-content').show(); + + + jQuery(function($){ + + $.supersized({ + + // Functionality + slide_interval : 3000, // Length between transitions + transition : 1, // 0-None, 1-Fade, 2-Slide Top, 3-Slide Right, 4-Slide Bottom, 5-Slide Left, 6-Carousel Right, 7-Carousel Left + transition_speed : 700, // Speed of transition + + // Components + slide_links : 'blank', // Individual links for each slide (Options: false, 'num', 'name', 'blank') + slides : images // Slideshow Images + + }); + }); + + }); + + //close slideshow on esc and remove holder + $(document).keyup(function(e) { + if (e.keyCode == 27) { // esc + $.endSlideshow(); + } + }); + +}); diff --git a/apps/gallery/js/supersized.3.2.7.js b/apps/gallery/js/supersized.3.2.7.js new file mode 100644 index 00000000000..f5a1c0bbc2d --- /dev/null +++ b/apps/gallery/js/supersized.3.2.7.js @@ -0,0 +1,930 @@ +/* + + Supersized - Fullscreen Slideshow jQuery Plugin + Version : 3.2.7 + Site : www.buildinternet.com/project/supersized + + Author : Sam Dunn + Company : One Mighty Roar (www.onemightyroar.com) + License : MIT License / GPL License + +*/ + +(function($){ + + /* Place Supersized Elements + ----------------------------*/ + $(document).ready(function() { + $('body').append('<div id="supersized-loader"></div><ul id="supersized"></ul>'); + }); + + + $.supersized = function(options){ + + /* Variables + ----------------------------*/ + var el = '#supersized', + base = this; + // Access to jQuery and DOM versions of element + base.$el = $(el); + base.el = el; + vars = $.supersized.vars; + // Add a reverse reference to the DOM object + base.$el.data("supersized", base); + api = base.$el.data('supersized'); + + base.init = function(){ + // Combine options and vars + $.supersized.vars = $.extend($.supersized.vars, $.supersized.themeVars); + $.supersized.vars.options = $.extend({},$.supersized.defaultOptions, $.supersized.themeOptions, options); + base.options = $.supersized.vars.options; + + base._build(); + }; + + + /* Build Elements + ----------------------------*/ + base._build = function(){ + // Add in slide markers + var thisSlide = 0, + slideSet = '', + markers = '', + markerContent, + thumbMarkers = '', + thumbImage; + + while(thisSlide <= base.options.slides.length-1){ + //Determine slide link content + switch(base.options.slide_links){ + case 'num': + markerContent = thisSlide; + break; + case 'name': + markerContent = base.options.slides[thisSlide].title; + break; + case 'blank': + markerContent = ''; + break; + } + + slideSet = slideSet+'<li class="slide-'+thisSlide+'"></li>'; + + if(thisSlide == base.options.start_slide-1){ + // Slide links + if (base.options.slide_links)markers = markers+'<li class="slide-link-'+thisSlide+' current-slide"><a>'+markerContent+'</a></li>'; + // Slide Thumbnail Links + if (base.options.thumb_links){ + base.options.slides[thisSlide].thumb ? thumbImage = base.options.slides[thisSlide].thumb : thumbImage = base.options.slides[thisSlide].image; + thumbMarkers = thumbMarkers+'<li class="thumb'+thisSlide+' current-thumb"><img src="'+thumbImage+'"/></li>'; + }; + }else{ + // Slide links + if (base.options.slide_links) markers = markers+'<li class="slide-link-'+thisSlide+'" ><a>'+markerContent+'</a></li>'; + // Slide Thumbnail Links + if (base.options.thumb_links){ + base.options.slides[thisSlide].thumb ? thumbImage = base.options.slides[thisSlide].thumb : thumbImage = base.options.slides[thisSlide].image; + thumbMarkers = thumbMarkers+'<li class="thumb'+thisSlide+'"><img src="'+thumbImage+'"/></li>'; + }; + } + thisSlide++; + } + + if (base.options.slide_links) $(vars.slide_list).html(markers); + if (base.options.thumb_links && vars.thumb_tray.length){ + $(vars.thumb_tray).append('<ul id="'+vars.thumb_list.replace('#','')+'">'+thumbMarkers+'</ul>'); + } + + $(base.el).append(slideSet); + + // Add in thumbnails + if (base.options.thumbnail_navigation){ + // Load previous thumbnail + vars.current_slide - 1 < 0 ? prevThumb = base.options.slides.length - 1 : prevThumb = vars.current_slide - 1; + $(vars.prev_thumb).show().html($("<img/>").attr("src", base.options.slides[prevThumb].image)); + + // Load next thumbnail + vars.current_slide == base.options.slides.length - 1 ? nextThumb = 0 : nextThumb = vars.current_slide + 1; + $(vars.next_thumb).show().html($("<img/>").attr("src", base.options.slides[nextThumb].image)); + } + + base._start(); // Get things started + }; + + + /* Initialize + ----------------------------*/ + base._start = function(){ + + // Determine if starting slide random + if (base.options.start_slide){ + vars.current_slide = base.options.start_slide - 1; + }else{ + vars.current_slide = Math.floor(Math.random()*base.options.slides.length); // Generate random slide number + } + + // If links should open in new window + var linkTarget = base.options.new_window ? ' target="_blank"' : ''; + + // Set slideshow quality (Supported only in FF and IE, no Webkit) + if (base.options.performance == 3){ + base.$el.addClass('speed'); // Faster transitions + } else if ((base.options.performance == 1) || (base.options.performance == 2)){ + base.$el.addClass('quality'); // Higher image quality + } + + // Shuffle slide order if needed + if (base.options.random){ + arr = base.options.slides; + for(var j, x, i = arr.length; i; j = parseInt(Math.random() * i), x = arr[--i], arr[i] = arr[j], arr[j] = x); // Fisher-Yates shuffle algorithm (jsfromhell.com/array/shuffle) + base.options.slides = arr; + } + + /*-----Load initial set of images-----*/ + + if (base.options.slides.length > 1){ + if(base.options.slides.length > 2){ + // Set previous image + vars.current_slide - 1 < 0 ? loadPrev = base.options.slides.length - 1 : loadPrev = vars.current_slide - 1; // If slide is 1, load last slide as previous + var imageLink = (base.options.slides[loadPrev].url) ? "href='" + base.options.slides[loadPrev].url + "'" : ""; + + var imgPrev = $('<img src="'+base.options.slides[loadPrev].image+'"/>'); + var slidePrev = base.el+' li:eq('+loadPrev+')'; + imgPrev.appendTo(slidePrev).wrap('<a ' + imageLink + linkTarget + '></a>').parent().parent().addClass('image-loading prevslide'); + + imgPrev.load(function(){ + $(this).data('origWidth', $(this).width()).data('origHeight', $(this).height()); + base.resizeNow(); // Resize background image + }); // End Load + } + } else { + // Slideshow turned off if there is only one slide + base.options.slideshow = 0; + } + + // Set current image + imageLink = (api.getField('url')) ? "href='" + api.getField('url') + "'" : ""; + var img = $('<img src="'+api.getField('image')+'"/>'); + + var slideCurrent= base.el+' li:eq('+vars.current_slide+')'; + img.appendTo(slideCurrent).wrap('<a ' + imageLink + linkTarget + '></a>').parent().parent().addClass('image-loading activeslide'); + + img.load(function(){ + base._origDim($(this)); + base.resizeNow(); // Resize background image + base.launch(); + if( typeof theme != 'undefined' && typeof theme._init == "function" ) theme._init(); // Load Theme + }); + + if (base.options.slides.length > 1){ + // Set next image + vars.current_slide == base.options.slides.length - 1 ? loadNext = 0 : loadNext = vars.current_slide + 1; // If slide is last, load first slide as next + imageLink = (base.options.slides[loadNext].url) ? "href='" + base.options.slides[loadNext].url + "'" : ""; + + var imgNext = $('<img src="'+base.options.slides[loadNext].image+'"/>'); + var slideNext = base.el+' li:eq('+loadNext+')'; + imgNext.appendTo(slideNext).wrap('<a ' + imageLink + linkTarget + '></a>').parent().parent().addClass('image-loading'); + + imgNext.load(function(){ + $(this).data('origWidth', $(this).width()).data('origHeight', $(this).height()); + base.resizeNow(); // Resize background image + }); // End Load + } + /*-----End load initial images-----*/ + + // Hide elements to be faded in + base.$el.css('visibility','hidden'); + $('.load-item').hide(); + + }; + + + /* Launch Supersized + ----------------------------*/ + base.launch = function(){ + + base.$el.css('visibility','visible'); + $('#supersized-loader').remove(); //Hide loading animation + + // Call theme function for before slide transition + if( typeof theme != 'undefined' && typeof theme.beforeAnimation == "function" ) theme.beforeAnimation('next'); + $('.load-item').show(); + + // Keyboard Navigation + if (base.options.keyboard_nav){ + $(document.documentElement).keyup(function (event) { + + if(vars.in_animation) return false; // Abort if currently animating + + // Left Arrow or Down Arrow + if ((event.keyCode == 37) || (event.keyCode == 40)) { + clearInterval(vars.slideshow_interval); // Stop slideshow, prevent buildup + base.prevSlide(); + + // Right Arrow or Up Arrow + } else if ((event.keyCode == 39) || (event.keyCode == 38)) { + clearInterval(vars.slideshow_interval); // Stop slideshow, prevent buildup + base.nextSlide(); + + // Spacebar + } else if (event.keyCode == 32 && !vars.hover_pause) { + clearInterval(vars.slideshow_interval); // Stop slideshow, prevent buildup + base.playToggle(); + } + + }); + } + + // Pause when hover on image + if (base.options.slideshow && base.options.pause_hover){ + $(base.el).hover(function() { + if(vars.in_animation) return false; // Abort if currently animating + vars.hover_pause = true; // Mark slideshow paused from hover + if(!vars.is_paused){ + vars.hover_pause = 'resume'; // It needs to resume afterwards + base.playToggle(); + } + }, function() { + if(vars.hover_pause == 'resume'){ + base.playToggle(); + vars.hover_pause = false; + } + }); + } + + if (base.options.slide_links){ + // Slide marker clicked + $(vars.slide_list+'> li').click(function(){ + + index = $(vars.slide_list+'> li').index(this); + targetSlide = index + 1; + + base.goTo(targetSlide); + return false; + + }); + } + + // Thumb marker clicked + if (base.options.thumb_links){ + $(vars.thumb_list+'> li').click(function(){ + + index = $(vars.thumb_list+'> li').index(this); + targetSlide = index + 1; + + api.goTo(targetSlide); + return false; + + }); + } + + // Start slideshow if enabled + if (base.options.slideshow && base.options.slides.length > 1){ + + // Start slideshow if autoplay enabled + if (base.options.autoplay && base.options.slides.length > 1){ + vars.slideshow_interval = setInterval(base.nextSlide, base.options.slide_interval); // Initiate slide interval + }else{ + vars.is_paused = true; // Mark as paused + } + + //Prevent navigation items from being dragged + $('.load-item img').bind("contextmenu mousedown",function(){ + return false; + }); + + } + + // Adjust image when browser is resized + $(window).resize(function(){ + base.resizeNow(); + }); + + }; + + + /* Resize Images + ----------------------------*/ + base.resizeNow = function(){ + + return base.$el.each(function() { + // Resize each image seperately + $('img', base.el).each(function(){ + + thisSlide = $(this); + var ratio = (thisSlide.data('origHeight')/thisSlide.data('origWidth')).toFixed(2); // Define image ratio + + // Gather browser size + var browserwidth = base.$el.width(), + browserheight = base.$el.height(), + offset; + + /*-----Resize Image-----*/ + if (base.options.fit_always){ // Fit always is enabled + if ((browserheight/browserwidth) > ratio){ + resizeWidth(); + } else { + resizeHeight(); + } + }else{ // Normal Resize + if ((browserheight <= base.options.min_height) && (browserwidth <= base.options.min_width)){ // If window smaller than minimum width and height + + if ((browserheight/browserwidth) > ratio){ + base.options.fit_landscape && ratio < 1 ? resizeWidth(true) : resizeHeight(true); // If landscapes are set to fit + } else { + base.options.fit_portrait && ratio >= 1 ? resizeHeight(true) : resizeWidth(true); // If portraits are set to fit + } + + } else if (browserwidth <= base.options.min_width){ // If window only smaller than minimum width + + if ((browserheight/browserwidth) > ratio){ + base.options.fit_landscape && ratio < 1 ? resizeWidth(true) : resizeHeight(); // If landscapes are set to fit + } else { + base.options.fit_portrait && ratio >= 1 ? resizeHeight() : resizeWidth(true); // If portraits are set to fit + } + + } else if (browserheight <= base.options.min_height){ // If window only smaller than minimum height + + if ((browserheight/browserwidth) > ratio){ + base.options.fit_landscape && ratio < 1 ? resizeWidth() : resizeHeight(true); // If landscapes are set to fit + } else { + base.options.fit_portrait && ratio >= 1 ? resizeHeight(true) : resizeWidth(); // If portraits are set to fit + } + + } else { // If larger than minimums + + if ((browserheight/browserwidth) > ratio){ + base.options.fit_landscape && ratio < 1 ? resizeWidth() : resizeHeight(); // If landscapes are set to fit + } else { + base.options.fit_portrait && ratio >= 1 ? resizeHeight() : resizeWidth(); // If portraits are set to fit + } + + } + } + /*-----End Image Resize-----*/ + + + /*-----Resize Functions-----*/ + + function resizeWidth(minimum){ + if (minimum){ // If minimum height needs to be considered + if(thisSlide.width() < browserwidth || thisSlide.width() < base.options.min_width ){ + if (thisSlide.width() * ratio >= base.options.min_height){ + thisSlide.width(base.options.min_width); + thisSlide.height(thisSlide.width() * ratio); + }else{ + resizeHeight(); + } + } + }else{ + if (base.options.min_height >= browserheight && !base.options.fit_landscape){ // If minimum height needs to be considered + if (browserwidth * ratio >= base.options.min_height || (browserwidth * ratio >= base.options.min_height && ratio <= 1)){ // If resizing would push below minimum height or image is a landscape + thisSlide.width(browserwidth); + thisSlide.height(browserwidth * ratio); + } else if (ratio > 1){ // Else the image is portrait + thisSlide.height(base.options.min_height); + thisSlide.width(thisSlide.height() / ratio); + } else if (thisSlide.width() < browserwidth) { + thisSlide.width(browserwidth); + thisSlide.height(thisSlide.width() * ratio); + } + }else{ // Otherwise, resize as normal + thisSlide.width(browserwidth); + thisSlide.height(browserwidth * ratio); + } + } + }; + + function resizeHeight(minimum){ + if (minimum){ // If minimum height needs to be considered + if(thisSlide.height() < browserheight){ + if (thisSlide.height() / ratio >= base.options.min_width){ + thisSlide.height(base.options.min_height); + thisSlide.width(thisSlide.height() / ratio); + }else{ + resizeWidth(true); + } + } + }else{ // Otherwise, resized as normal + if (base.options.min_width >= browserwidth){ // If minimum width needs to be considered + if (browserheight / ratio >= base.options.min_width || ratio > 1){ // If resizing would push below minimum width or image is a portrait + thisSlide.height(browserheight); + thisSlide.width(browserheight / ratio); + } else if (ratio <= 1){ // Else the image is landscape + thisSlide.width(base.options.min_width); + thisSlide.height(thisSlide.width() * ratio); + } + }else{ // Otherwise, resize as normal + thisSlide.height(browserheight); + thisSlide.width(browserheight / ratio); + } + } + }; + + /*-----End Resize Functions-----*/ + + if (thisSlide.parents('li').hasClass('image-loading')){ + $('.image-loading').removeClass('image-loading'); + } + + // Horizontally Center + if (base.options.horizontal_center){ + $(this).css('left', (browserwidth - $(this).width())/2); + } + + // Vertically Center + if (base.options.vertical_center){ + $(this).css('top', (browserheight - $(this).height())/2); + } + + }); + + // Basic image drag and right click protection + if (base.options.image_protect){ + + $('img', base.el).bind("contextmenu mousedown",function(){ + return false; + }); + + } + + return false; + + }); + + }; + + + /* Next Slide + ----------------------------*/ + base.nextSlide = function(){ + + if(vars.in_animation || !api.options.slideshow) return false; // Abort if currently animating + else vars.in_animation = true; // Otherwise set animation marker + + clearInterval(vars.slideshow_interval); // Stop slideshow + + var slides = base.options.slides, // Pull in slides array + liveslide = base.$el.find('.activeslide'); // Find active slide + $('.prevslide').removeClass('prevslide'); + liveslide.removeClass('activeslide').addClass('prevslide'); // Remove active class & update previous slide + + // Get the slide number of new slide + vars.current_slide + 1 == base.options.slides.length ? vars.current_slide = 0 : vars.current_slide++; + + var nextslide = $(base.el+' li:eq('+vars.current_slide+')'), + prevslide = base.$el.find('.prevslide'); + + // If hybrid mode is on drop quality for transition + if (base.options.performance == 1) base.$el.removeClass('quality').addClass('speed'); + + + /*-----Load Image-----*/ + + loadSlide = false; + + vars.current_slide == base.options.slides.length - 1 ? loadSlide = 0 : loadSlide = vars.current_slide + 1; // Determine next slide + + var targetList = base.el+' li:eq('+loadSlide+')'; + if (!$(targetList).html()){ + + // If links should open in new window + var linkTarget = base.options.new_window ? ' target="_blank"' : ''; + + imageLink = (base.options.slides[loadSlide].url) ? "href='" + base.options.slides[loadSlide].url + "'" : ""; // If link exists, build it + var img = $('<img src="'+base.options.slides[loadSlide].image+'"/>'); + + img.appendTo(targetList).wrap('<a ' + imageLink + linkTarget + '></a>').parent().parent().addClass('image-loading').css('visibility','hidden'); + + img.load(function(){ + base._origDim($(this)); + base.resizeNow(); + }); // End Load + }; + + // Update thumbnails (if enabled) + if (base.options.thumbnail_navigation == 1){ + + // Load previous thumbnail + vars.current_slide - 1 < 0 ? prevThumb = base.options.slides.length - 1 : prevThumb = vars.current_slide - 1; + $(vars.prev_thumb).html($("<img/>").attr("src", base.options.slides[prevThumb].image)); + + // Load next thumbnail + nextThumb = loadSlide; + $(vars.next_thumb).html($("<img/>").attr("src", base.options.slides[nextThumb].image)); + + } + + + + /*-----End Load Image-----*/ + + + // Call theme function for before slide transition + if( typeof theme != 'undefined' && typeof theme.beforeAnimation == "function" ) theme.beforeAnimation('next'); + + //Update slide markers + if (base.options.slide_links){ + $('.current-slide').removeClass('current-slide'); + $(vars.slide_list +'> li' ).eq(vars.current_slide).addClass('current-slide'); + } + + nextslide.css('visibility','hidden').addClass('activeslide'); // Update active slide + + switch(base.options.transition){ + case 0: case 'none': // No transition + nextslide.css('visibility','visible'); vars.in_animation = false; base.afterAnimation(); + break; + case 1: case 'fade': // Fade + nextslide.animate({opacity : 0},0).css('visibility','visible').animate({opacity : 1, avoidTransforms : false}, base.options.transition_speed, function(){ base.afterAnimation(); }); + break; + case 2: case 'slideTop': // Slide Top + nextslide.animate({top : -base.$el.height()}, 0 ).css('visibility','visible').animate({ top:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); }); + break; + case 3: case 'slideRight': // Slide Right + nextslide.animate({left : base.$el.width()}, 0 ).css('visibility','visible').animate({ left:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); }); + break; + case 4: case 'slideBottom': // Slide Bottom + nextslide.animate({top : base.$el.height()}, 0 ).css('visibility','visible').animate({ top:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); }); + break; + case 5: case 'slideLeft': // Slide Left + nextslide.animate({left : -base.$el.width()}, 0 ).css('visibility','visible').animate({ left:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); }); + break; + case 6: case 'carouselRight': // Carousel Right + nextslide.animate({left : base.$el.width()}, 0 ).css('visibility','visible').animate({ left:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); }); + liveslide.animate({ left: -base.$el.width(), avoidTransforms : false }, base.options.transition_speed ); + break; + case 7: case 'carouselLeft': // Carousel Left + nextslide.animate({left : -base.$el.width()}, 0 ).css('visibility','visible').animate({ left:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); }); + liveslide.animate({ left: base.$el.width(), avoidTransforms : false }, base.options.transition_speed ); + break; + } + return false; + }; + + + /* Previous Slide + ----------------------------*/ + base.prevSlide = function(){ + + if(vars.in_animation || !api.options.slideshow) return false; // Abort if currently animating + else vars.in_animation = true; // Otherwise set animation marker + + clearInterval(vars.slideshow_interval); // Stop slideshow + + var slides = base.options.slides, // Pull in slides array + liveslide = base.$el.find('.activeslide'); // Find active slide + $('.prevslide').removeClass('prevslide'); + liveslide.removeClass('activeslide').addClass('prevslide'); // Remove active class & update previous slide + + // Get current slide number + vars.current_slide == 0 ? vars.current_slide = base.options.slides.length - 1 : vars.current_slide-- ; + + var nextslide = $(base.el+' li:eq('+vars.current_slide+')'), + prevslide = base.$el.find('.prevslide'); + + // If hybrid mode is on drop quality for transition + if (base.options.performance == 1) base.$el.removeClass('quality').addClass('speed'); + + + /*-----Load Image-----*/ + + loadSlide = vars.current_slide; + + var targetList = base.el+' li:eq('+loadSlide+')'; + if (!$(targetList).html()){ + // If links should open in new window + var linkTarget = base.options.new_window ? ' target="_blank"' : ''; + imageLink = (base.options.slides[loadSlide].url) ? "href='" + base.options.slides[loadSlide].url + "'" : ""; // If link exists, build it + var img = $('<img src="'+base.options.slides[loadSlide].image+'"/>'); + + img.appendTo(targetList).wrap('<a ' + imageLink + linkTarget + '></a>').parent().parent().addClass('image-loading').css('visibility','hidden'); + + img.load(function(){ + base._origDim($(this)); + base.resizeNow(); + }); // End Load + }; + + // Update thumbnails (if enabled) + if (base.options.thumbnail_navigation == 1){ + + // Load previous thumbnail + //prevThumb = loadSlide; + loadSlide == 0 ? prevThumb = base.options.slides.length - 1 : prevThumb = loadSlide - 1; + $(vars.prev_thumb).html($("<img/>").attr("src", base.options.slides[prevThumb].image)); + + // Load next thumbnail + vars.current_slide == base.options.slides.length - 1 ? nextThumb = 0 : nextThumb = vars.current_slide + 1; + $(vars.next_thumb).html($("<img/>").attr("src", base.options.slides[nextThumb].image)); + } + + /*-----End Load Image-----*/ + + + // Call theme function for before slide transition + if( typeof theme != 'undefined' && typeof theme.beforeAnimation == "function" ) theme.beforeAnimation('prev'); + + //Update slide markers + if (base.options.slide_links){ + $('.current-slide').removeClass('current-slide'); + $(vars.slide_list +'> li' ).eq(vars.current_slide).addClass('current-slide'); + } + + nextslide.css('visibility','hidden').addClass('activeslide'); // Update active slide + + switch(base.options.transition){ + case 0: case 'none': // No transition + nextslide.css('visibility','visible'); vars.in_animation = false; base.afterAnimation(); + break; + case 1: case 'fade': // Fade + nextslide.animate({opacity : 0},0).css('visibility','visible').animate({opacity : 1, avoidTransforms : false}, base.options.transition_speed, function(){ base.afterAnimation(); }); + break; + case 2: case 'slideTop': // Slide Top (reverse) + nextslide.animate({top : base.$el.height()}, 0 ).css('visibility','visible').animate({ top:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); }); + break; + case 3: case 'slideRight': // Slide Right (reverse) + nextslide.animate({left : -base.$el.width()}, 0 ).css('visibility','visible').animate({ left:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); }); + break; + case 4: case 'slideBottom': // Slide Bottom (reverse) + nextslide.animate({top : -base.$el.height()}, 0 ).css('visibility','visible').animate({ top:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); }); + break; + case 5: case 'slideLeft': // Slide Left (reverse) + nextslide.animate({left : base.$el.width()}, 0 ).css('visibility','visible').animate({ left:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); }); + break; + case 6: case 'carouselRight': // Carousel Right (reverse) + nextslide.animate({left : -base.$el.width()}, 0 ).css('visibility','visible').animate({ left:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); }); + liveslide.animate({left : 0}, 0 ).animate({ left: base.$el.width(), avoidTransforms : false}, base.options.transition_speed ); + break; + case 7: case 'carouselLeft': // Carousel Left (reverse) + nextslide.animate({left : base.$el.width()}, 0 ).css('visibility','visible').animate({ left:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); }); + liveslide.animate({left : 0}, 0 ).animate({ left: -base.$el.width(), avoidTransforms : false }, base.options.transition_speed ); + break; + } + return false; + }; + + + /* Play/Pause Toggle + ----------------------------*/ + base.playToggle = function(){ + + if (vars.in_animation || !api.options.slideshow) return false; // Abort if currently animating + + if (vars.is_paused){ + + vars.is_paused = false; + + // Call theme function for play + if( typeof theme != 'undefined' && typeof theme.playToggle == "function" ) theme.playToggle('play'); + + // Resume slideshow + vars.slideshow_interval = setInterval(base.nextSlide, base.options.slide_interval); + + }else{ + + vars.is_paused = true; + + // Call theme function for pause + if( typeof theme != 'undefined' && typeof theme.playToggle == "function" ) theme.playToggle('pause'); + + // Stop slideshow + clearInterval(vars.slideshow_interval); + + } + + return false; + + }; + + + /* Go to specific slide + ----------------------------*/ + base.goTo = function(targetSlide){ + if (vars.in_animation || !api.options.slideshow) return false; // Abort if currently animating + + var totalSlides = base.options.slides.length; + + // If target outside range + if(targetSlide < 0){ + targetSlide = totalSlides; + }else if(targetSlide > totalSlides){ + targetSlide = 1; + } + targetSlide = totalSlides - targetSlide + 1; + + clearInterval(vars.slideshow_interval); // Stop slideshow, prevent buildup + + // Call theme function for goTo trigger + if (typeof theme != 'undefined' && typeof theme.goTo == "function" ) theme.goTo(); + + if (vars.current_slide == totalSlides - targetSlide){ + if(!(vars.is_paused)){ + vars.slideshow_interval = setInterval(base.nextSlide, base.options.slide_interval); + } + return false; + } + + // If ahead of current position + if(totalSlides - targetSlide > vars.current_slide ){ + + // Adjust for new next slide + vars.current_slide = totalSlides-targetSlide-1; + vars.update_images = 'next'; + base._placeSlide(vars.update_images); + + //Otherwise it's before current position + }else if(totalSlides - targetSlide < vars.current_slide){ + + // Adjust for new prev slide + vars.current_slide = totalSlides-targetSlide+1; + vars.update_images = 'prev'; + base._placeSlide(vars.update_images); + + } + + // set active markers + if (base.options.slide_links){ + $(vars.slide_list +'> .current-slide').removeClass('current-slide'); + $(vars.slide_list +'> li').eq((totalSlides-targetSlide)).addClass('current-slide'); + } + + if (base.options.thumb_links){ + $(vars.thumb_list +'> .current-thumb').removeClass('current-thumb'); + $(vars.thumb_list +'> li').eq((totalSlides-targetSlide)).addClass('current-thumb'); + } + + }; + + + /* Place Slide + ----------------------------*/ + base._placeSlide = function(place){ + + // If links should open in new window + var linkTarget = base.options.new_window ? ' target="_blank"' : ''; + + loadSlide = false; + + if (place == 'next'){ + + vars.current_slide == base.options.slides.length - 1 ? loadSlide = 0 : loadSlide = vars.current_slide + 1; // Determine next slide + + var targetList = base.el+' li:eq('+loadSlide+')'; + + if (!$(targetList).html()){ + // If links should open in new window + var linkTarget = base.options.new_window ? ' target="_blank"' : ''; + + imageLink = (base.options.slides[loadSlide].url) ? "href='" + base.options.slides[loadSlide].url + "'" : ""; // If link exists, build it + var img = $('<img src="'+base.options.slides[loadSlide].image+'"/>'); + + img.appendTo(targetList).wrap('<a ' + imageLink + linkTarget + '></a>').parent().parent().addClass('image-loading').css('visibility','hidden'); + + img.load(function(){ + base._origDim($(this)); + base.resizeNow(); + }); // End Load + }; + + base.nextSlide(); + + }else if (place == 'prev'){ + + vars.current_slide - 1 < 0 ? loadSlide = base.options.slides.length - 1 : loadSlide = vars.current_slide - 1; // Determine next slide + + var targetList = base.el+' li:eq('+loadSlide+')'; + + if (!$(targetList).html()){ + // If links should open in new window + var linkTarget = base.options.new_window ? ' target="_blank"' : ''; + + imageLink = (base.options.slides[loadSlide].url) ? "href='" + base.options.slides[loadSlide].url + "'" : ""; // If link exists, build it + var img = $('<img src="'+base.options.slides[loadSlide].image+'"/>'); + + img.appendTo(targetList).wrap('<a ' + imageLink + linkTarget + '></a>').parent().parent().addClass('image-loading').css('visibility','hidden'); + + img.load(function(){ + base._origDim($(this)); + base.resizeNow(); + }); // End Load + }; + base.prevSlide(); + } + + }; + + + /* Get Original Dimensions + ----------------------------*/ + base._origDim = function(targetSlide){ + targetSlide.data('origWidth', targetSlide.width()).data('origHeight', targetSlide.height()); + }; + + + /* After Slide Animation + ----------------------------*/ + base.afterAnimation = function(){ + + // If hybrid mode is on swap back to higher image quality + if (base.options.performance == 1){ + base.$el.removeClass('speed').addClass('quality'); + } + + // Update previous slide + if (vars.update_images){ + vars.current_slide - 1 < 0 ? setPrev = base.options.slides.length - 1 : setPrev = vars.current_slide-1; + vars.update_images = false; + $('.prevslide').removeClass('prevslide'); + $(base.el+' li:eq('+setPrev+')').addClass('prevslide'); + } + + vars.in_animation = false; + + // Resume slideshow + if (!vars.is_paused && base.options.slideshow){ + vars.slideshow_interval = setInterval(base.nextSlide, base.options.slide_interval); + if (base.options.stop_loop && vars.current_slide == base.options.slides.length - 1 ) base.playToggle(); + } + + // Call theme function for after slide transition + if (typeof theme != 'undefined' && typeof theme.afterAnimation == "function" ) theme.afterAnimation(); + + return false; + + }; + + base.getField = function(field){ + return base.options.slides[vars.current_slide][field]; + }; + + // Make it go! + base.init(); + }; + + + /* Global Variables + ----------------------------*/ + $.supersized.vars = { + + // Elements + thumb_tray : '#thumb-tray', // Thumbnail tray + thumb_list : '#thumb-list', // Thumbnail list + slide_list : '#slide-list', // Slide link list + + // Internal variables + current_slide : 0, // Current slide number + in_animation : false, // Prevents animations from stacking + is_paused : false, // Tracks paused on/off + hover_pause : false, // If slideshow is paused from hover + slideshow_interval : false, // Stores slideshow timer + update_images : false, // Trigger to update images after slide jump + options : {} // Stores assembled options list + + }; + + + /* Default Options + ----------------------------*/ + $.supersized.defaultOptions = { + + // Functionality + slideshow : 1, // Slideshow on/off + autoplay : 1, // Slideshow starts playing automatically + start_slide : 1, // Start slide (0 is random) + stop_loop : 0, // Stops slideshow on last slide + random : 0, // Randomize slide order (Ignores start slide) + slide_interval : 5000, // Length between transitions + transition : 1, // 0-None, 1-Fade, 2-Slide Top, 3-Slide Right, 4-Slide Bottom, 5-Slide Left, 6-Carousel Right, 7-Carousel Left + transition_speed : 750, // Speed of transition + new_window : 1, // Image links open in new window/tab + pause_hover : 0, // Pause slideshow on hover + keyboard_nav : 1, // Keyboard navigation on/off + performance : 1, // 0-Normal, 1-Hybrid speed/quality, 2-Optimizes image quality, 3-Optimizes transition speed // (Only works for Firefox/IE, not Webkit) + image_protect : 1, // Disables image dragging and right click with Javascript + + // Size & Position + fit_always : 0, // Image will never exceed browser width or height (Ignores min. dimensions) + fit_landscape : 0, // Landscape images will not exceed browser width + fit_portrait : 1, // Portrait images will not exceed browser height + min_width : 0, // Min width allowed (in pixels) + min_height : 0, // Min height allowed (in pixels) + horizontal_center : 1, // Horizontally center background + vertical_center : 1, // Vertically center background + + + // Components + slide_links : 1, // Individual links for each slide (Options: false, 'num', 'name', 'blank') + thumb_links : 1, // Individual thumb links for each slide + thumbnail_navigation : 0 // Thumbnail navigation + + }; + + $.fn.supersized = function(options){ + return this.each(function(){ + (new $.supersized(options)); + }); + }; + +})(jQuery); + diff --git a/apps/gallery/js/supersized.3.2.7.min.js b/apps/gallery/js/supersized.3.2.7.min.js new file mode 100644 index 00000000000..b9cea9cee1c --- /dev/null +++ b/apps/gallery/js/supersized.3.2.7.min.js @@ -0,0 +1,13 @@ +/* + + Supersized - Fullscreen Slideshow jQuery Plugin + Version : 3.2.7 + Site : www.buildinternet.com/project/supersized + + Author : Sam Dunn + Company : One Mighty Roar (www.onemightyroar.com) + License : MIT License / GPL License + +*/ + +(function(a){a(document).ready(function(){a("body").append('<div id="supersized-loader"></div><ul id="supersized"></ul>')});a.supersized=function(b){var c="#supersized",d=this;d.$el=a(c);d.el=c;vars=a.supersized.vars;d.$el.data("supersized",d);api=d.$el.data("supersized");d.init=function(){a.supersized.vars=a.extend(a.supersized.vars,a.supersized.themeVars);a.supersized.vars.options=a.extend({},a.supersized.defaultOptions,a.supersized.themeOptions,b);d.options=a.supersized.vars.options;d._build()};d._build=function(){var g=0,e="",j="",h,f="",i;while(g<=d.options.slides.length-1){switch(d.options.slide_links){case"num":h=g;break;case"name":h=d.options.slides[g].title;break;case"blank":h="";break}e=e+'<li class="slide-'+g+'"></li>';if(g==d.options.start_slide-1){if(d.options.slide_links){j=j+'<li class="slide-link-'+g+' current-slide"><a>'+h+"</a></li>"}if(d.options.thumb_links){d.options.slides[g].thumb?i=d.options.slides[g].thumb:i=d.options.slides[g].image;f=f+'<li class="thumb'+g+' current-thumb"><img src="'+i+'"/></li>'}}else{if(d.options.slide_links){j=j+'<li class="slide-link-'+g+'" ><a>'+h+"</a></li>"}if(d.options.thumb_links){d.options.slides[g].thumb?i=d.options.slides[g].thumb:i=d.options.slides[g].image;f=f+'<li class="thumb'+g+'"><img src="'+i+'"/></li>'}}g++}if(d.options.slide_links){a(vars.slide_list).html(j)}if(d.options.thumb_links&&vars.thumb_tray.length){a(vars.thumb_tray).append('<ul id="'+vars.thumb_list.replace("#","")+'">'+f+"</ul>")}a(d.el).append(e);if(d.options.thumbnail_navigation){vars.current_slide-1<0?prevThumb=d.options.slides.length-1:prevThumb=vars.current_slide-1;a(vars.prev_thumb).show().html(a("<img/>").attr("src",d.options.slides[prevThumb].image));vars.current_slide==d.options.slides.length-1?nextThumb=0:nextThumb=vars.current_slide+1;a(vars.next_thumb).show().html(a("<img/>").attr("src",d.options.slides[nextThumb].image))}d._start()};d._start=function(){if(d.options.start_slide){vars.current_slide=d.options.start_slide-1}else{vars.current_slide=Math.floor(Math.random()*d.options.slides.length)}var o=d.options.new_window?' target="_blank"':"";if(d.options.performance==3){d.$el.addClass("speed")}else{if((d.options.performance==1)||(d.options.performance==2)){d.$el.addClass("quality")}}if(d.options.random){arr=d.options.slides;for(var h,m,k=arr.length;k;h=parseInt(Math.random()*k),m=arr[--k],arr[k]=arr[h],arr[h]=m){}d.options.slides=arr}if(d.options.slides.length>1){if(d.options.slides.length>2){vars.current_slide-1<0?loadPrev=d.options.slides.length-1:loadPrev=vars.current_slide-1;var g=(d.options.slides[loadPrev].url)?"href='"+d.options.slides[loadPrev].url+"'":"";var q=a('<img src="'+d.options.slides[loadPrev].image+'"/>');var n=d.el+" li:eq("+loadPrev+")";q.appendTo(n).wrap("<a "+g+o+"></a>").parent().parent().addClass("image-loading prevslide");q.load(function(){a(this).data("origWidth",a(this).width()).data("origHeight",a(this).height());d.resizeNow()})}}else{d.options.slideshow=0}g=(api.getField("url"))?"href='"+api.getField("url")+"'":"";var l=a('<img src="'+api.getField("image")+'"/>');var f=d.el+" li:eq("+vars.current_slide+")";l.appendTo(f).wrap("<a "+g+o+"></a>").parent().parent().addClass("image-loading activeslide");l.load(function(){d._origDim(a(this));d.resizeNow();d.launch();if(typeof theme!="undefined"&&typeof theme._init=="function"){theme._init()}});if(d.options.slides.length>1){vars.current_slide==d.options.slides.length-1?loadNext=0:loadNext=vars.current_slide+1;g=(d.options.slides[loadNext].url)?"href='"+d.options.slides[loadNext].url+"'":"";var e=a('<img src="'+d.options.slides[loadNext].image+'"/>');var p=d.el+" li:eq("+loadNext+")";e.appendTo(p).wrap("<a "+g+o+"></a>").parent().parent().addClass("image-loading");e.load(function(){a(this).data("origWidth",a(this).width()).data("origHeight",a(this).height());d.resizeNow()})}d.$el.css("visibility","hidden");a(".load-item").hide()};d.launch=function(){d.$el.css("visibility","visible");a("#supersized-loader").remove();if(typeof theme!="undefined"&&typeof theme.beforeAnimation=="function"){theme.beforeAnimation("next")}a(".load-item").show();if(d.options.keyboard_nav){a(document.documentElement).keyup(function(e){if(vars.in_animation){return false}if((e.keyCode==37)||(e.keyCode==40)){clearInterval(vars.slideshow_interval);d.prevSlide()}else{if((e.keyCode==39)||(e.keyCode==38)){clearInterval(vars.slideshow_interval);d.nextSlide()}else{if(e.keyCode==32&&!vars.hover_pause){clearInterval(vars.slideshow_interval);d.playToggle()}}}})}if(d.options.slideshow&&d.options.pause_hover){a(d.el).hover(function(){if(vars.in_animation){return false}vars.hover_pause=true;if(!vars.is_paused){vars.hover_pause="resume";d.playToggle()}},function(){if(vars.hover_pause=="resume"){d.playToggle();vars.hover_pause=false}})}if(d.options.slide_links){a(vars.slide_list+"> li").click(function(){index=a(vars.slide_list+"> li").index(this);targetSlide=index+1;d.goTo(targetSlide);return false})}if(d.options.thumb_links){a(vars.thumb_list+"> li").click(function(){index=a(vars.thumb_list+"> li").index(this);targetSlide=index+1;api.goTo(targetSlide);return false})}if(d.options.slideshow&&d.options.slides.length>1){if(d.options.autoplay&&d.options.slides.length>1){vars.slideshow_interval=setInterval(d.nextSlide,d.options.slide_interval)}else{vars.is_paused=true}a(".load-item img").bind("contextmenu mousedown",function(){return false})}a(window).resize(function(){d.resizeNow()})};d.resizeNow=function(){return d.$el.each(function(){a("img",d.el).each(function(){thisSlide=a(this);var f=(thisSlide.data("origHeight")/thisSlide.data("origWidth")).toFixed(2);var e=d.$el.width(),h=d.$el.height(),i;if(d.options.fit_always){if((h/e)>f){g()}else{j()}}else{if((h<=d.options.min_height)&&(e<=d.options.min_width)){if((h/e)>f){d.options.fit_landscape&&f<1?g(true):j(true)}else{d.options.fit_portrait&&f>=1?j(true):g(true)}}else{if(e<=d.options.min_width){if((h/e)>f){d.options.fit_landscape&&f<1?g(true):j()}else{d.options.fit_portrait&&f>=1?j():g(true)}}else{if(h<=d.options.min_height){if((h/e)>f){d.options.fit_landscape&&f<1?g():j(true)}else{d.options.fit_portrait&&f>=1?j(true):g()}}else{if((h/e)>f){d.options.fit_landscape&&f<1?g():j()}else{d.options.fit_portrait&&f>=1?j():g()}}}}}function g(k){if(k){if(thisSlide.width()<e||thisSlide.width()<d.options.min_width){if(thisSlide.width()*f>=d.options.min_height){thisSlide.width(d.options.min_width);thisSlide.height(thisSlide.width()*f)}else{j()}}}else{if(d.options.min_height>=h&&!d.options.fit_landscape){if(e*f>=d.options.min_height||(e*f>=d.options.min_height&&f<=1)){thisSlide.width(e);thisSlide.height(e*f)}else{if(f>1){thisSlide.height(d.options.min_height);thisSlide.width(thisSlide.height()/f)}else{if(thisSlide.width()<e){thisSlide.width(e);thisSlide.height(thisSlide.width()*f)}}}}else{thisSlide.width(e);thisSlide.height(e*f)}}}function j(k){if(k){if(thisSlide.height()<h){if(thisSlide.height()/f>=d.options.min_width){thisSlide.height(d.options.min_height);thisSlide.width(thisSlide.height()/f)}else{g(true)}}}else{if(d.options.min_width>=e){if(h/f>=d.options.min_width||f>1){thisSlide.height(h);thisSlide.width(h/f)}else{if(f<=1){thisSlide.width(d.options.min_width);thisSlide.height(thisSlide.width()*f)}}}else{thisSlide.height(h);thisSlide.width(h/f)}}}if(thisSlide.parents("li").hasClass("image-loading")){a(".image-loading").removeClass("image-loading")}if(d.options.horizontal_center){a(this).css("left",(e-a(this).width())/2)}if(d.options.vertical_center){a(this).css("top",(h-a(this).height())/2)}});if(d.options.image_protect){a("img",d.el).bind("contextmenu mousedown",function(){return false})}return false})};d.nextSlide=function(){if(vars.in_animation||!api.options.slideshow){return false}else{vars.in_animation=true}clearInterval(vars.slideshow_interval);var h=d.options.slides,e=d.$el.find(".activeslide");a(".prevslide").removeClass("prevslide");e.removeClass("activeslide").addClass("prevslide");vars.current_slide+1==d.options.slides.length?vars.current_slide=0:vars.current_slide++;var g=a(d.el+" li:eq("+vars.current_slide+")"),i=d.$el.find(".prevslide");if(d.options.performance==1){d.$el.removeClass("quality").addClass("speed")}loadSlide=false;vars.current_slide==d.options.slides.length-1?loadSlide=0:loadSlide=vars.current_slide+1;var k=d.el+" li:eq("+loadSlide+")";if(!a(k).html()){var j=d.options.new_window?' target="_blank"':"";imageLink=(d.options.slides[loadSlide].url)?"href='"+d.options.slides[loadSlide].url+"'":"";var f=a('<img src="'+d.options.slides[loadSlide].image+'"/>');f.appendTo(k).wrap("<a "+imageLink+j+"></a>").parent().parent().addClass("image-loading").css("visibility","hidden");f.load(function(){d._origDim(a(this));d.resizeNow()})}if(d.options.thumbnail_navigation==1){vars.current_slide-1<0?prevThumb=d.options.slides.length-1:prevThumb=vars.current_slide-1;a(vars.prev_thumb).html(a("<img/>").attr("src",d.options.slides[prevThumb].image));nextThumb=loadSlide;a(vars.next_thumb).html(a("<img/>").attr("src",d.options.slides[nextThumb].image))}if(typeof theme!="undefined"&&typeof theme.beforeAnimation=="function"){theme.beforeAnimation("next")}if(d.options.slide_links){a(".current-slide").removeClass("current-slide");a(vars.slide_list+"> li").eq(vars.current_slide).addClass("current-slide")}g.css("visibility","hidden").addClass("activeslide");switch(d.options.transition){case 0:case"none":g.css("visibility","visible");vars.in_animation=false;d.afterAnimation();break;case 1:case"fade":g.animate({opacity:0},0).css("visibility","visible").animate({opacity:1,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 2:case"slideTop":g.animate({top:-d.$el.height()},0).css("visibility","visible").animate({top:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 3:case"slideRight":g.animate({left:d.$el.width()},0).css("visibility","visible").animate({left:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 4:case"slideBottom":g.animate({top:d.$el.height()},0).css("visibility","visible").animate({top:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 5:case"slideLeft":g.animate({left:-d.$el.width()},0).css("visibility","visible").animate({left:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 6:case"carouselRight":g.animate({left:d.$el.width()},0).css("visibility","visible").animate({left:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});e.animate({left:-d.$el.width(),avoidTransforms:false},d.options.transition_speed);break;case 7:case"carouselLeft":g.animate({left:-d.$el.width()},0).css("visibility","visible").animate({left:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});e.animate({left:d.$el.width(),avoidTransforms:false},d.options.transition_speed);break}return false};d.prevSlide=function(){if(vars.in_animation||!api.options.slideshow){return false}else{vars.in_animation=true}clearInterval(vars.slideshow_interval);var h=d.options.slides,e=d.$el.find(".activeslide");a(".prevslide").removeClass("prevslide");e.removeClass("activeslide").addClass("prevslide");vars.current_slide==0?vars.current_slide=d.options.slides.length-1:vars.current_slide--;var g=a(d.el+" li:eq("+vars.current_slide+")"),i=d.$el.find(".prevslide");if(d.options.performance==1){d.$el.removeClass("quality").addClass("speed")}loadSlide=vars.current_slide;var k=d.el+" li:eq("+loadSlide+")";if(!a(k).html()){var j=d.options.new_window?' target="_blank"':"";imageLink=(d.options.slides[loadSlide].url)?"href='"+d.options.slides[loadSlide].url+"'":"";var f=a('<img src="'+d.options.slides[loadSlide].image+'"/>');f.appendTo(k).wrap("<a "+imageLink+j+"></a>").parent().parent().addClass("image-loading").css("visibility","hidden");f.load(function(){d._origDim(a(this));d.resizeNow()})}if(d.options.thumbnail_navigation==1){loadSlide==0?prevThumb=d.options.slides.length-1:prevThumb=loadSlide-1;a(vars.prev_thumb).html(a("<img/>").attr("src",d.options.slides[prevThumb].image));vars.current_slide==d.options.slides.length-1?nextThumb=0:nextThumb=vars.current_slide+1;a(vars.next_thumb).html(a("<img/>").attr("src",d.options.slides[nextThumb].image))}if(typeof theme!="undefined"&&typeof theme.beforeAnimation=="function"){theme.beforeAnimation("prev")}if(d.options.slide_links){a(".current-slide").removeClass("current-slide");a(vars.slide_list+"> li").eq(vars.current_slide).addClass("current-slide")}g.css("visibility","hidden").addClass("activeslide");switch(d.options.transition){case 0:case"none":g.css("visibility","visible");vars.in_animation=false;d.afterAnimation();break;case 1:case"fade":g.animate({opacity:0},0).css("visibility","visible").animate({opacity:1,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 2:case"slideTop":g.animate({top:d.$el.height()},0).css("visibility","visible").animate({top:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 3:case"slideRight":g.animate({left:-d.$el.width()},0).css("visibility","visible").animate({left:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 4:case"slideBottom":g.animate({top:-d.$el.height()},0).css("visibility","visible").animate({top:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 5:case"slideLeft":g.animate({left:d.$el.width()},0).css("visibility","visible").animate({left:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 6:case"carouselRight":g.animate({left:-d.$el.width()},0).css("visibility","visible").animate({left:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});e.animate({left:0},0).animate({left:d.$el.width(),avoidTransforms:false},d.options.transition_speed);break;case 7:case"carouselLeft":g.animate({left:d.$el.width()},0).css("visibility","visible").animate({left:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});e.animate({left:0},0).animate({left:-d.$el.width(),avoidTransforms:false},d.options.transition_speed);break}return false};d.playToggle=function(){if(vars.in_animation||!api.options.slideshow){return false}if(vars.is_paused){vars.is_paused=false;if(typeof theme!="undefined"&&typeof theme.playToggle=="function"){theme.playToggle("play")}vars.slideshow_interval=setInterval(d.nextSlide,d.options.slide_interval)}else{vars.is_paused=true;if(typeof theme!="undefined"&&typeof theme.playToggle=="function"){theme.playToggle("pause")}clearInterval(vars.slideshow_interval)}return false};d.goTo=function(f){if(vars.in_animation||!api.options.slideshow){return false}var e=d.options.slides.length;if(f<0){f=e}else{if(f>e){f=1}}f=e-f+1;clearInterval(vars.slideshow_interval);if(typeof theme!="undefined"&&typeof theme.goTo=="function"){theme.goTo()}if(vars.current_slide==e-f){if(!(vars.is_paused)){vars.slideshow_interval=setInterval(d.nextSlide,d.options.slide_interval)}return false}if(e-f>vars.current_slide){vars.current_slide=e-f-1;vars.update_images="next";d._placeSlide(vars.update_images)}else{if(e-f<vars.current_slide){vars.current_slide=e-f+1;vars.update_images="prev";d._placeSlide(vars.update_images)}}if(d.options.slide_links){a(vars.slide_list+"> .current-slide").removeClass("current-slide");a(vars.slide_list+"> li").eq((e-f)).addClass("current-slide")}if(d.options.thumb_links){a(vars.thumb_list+"> .current-thumb").removeClass("current-thumb");a(vars.thumb_list+"> li").eq((e-f)).addClass("current-thumb")}};d._placeSlide=function(e){var h=d.options.new_window?' target="_blank"':"";loadSlide=false;if(e=="next"){vars.current_slide==d.options.slides.length-1?loadSlide=0:loadSlide=vars.current_slide+1;var g=d.el+" li:eq("+loadSlide+")";if(!a(g).html()){var h=d.options.new_window?' target="_blank"':"";imageLink=(d.options.slides[loadSlide].url)?"href='"+d.options.slides[loadSlide].url+"'":"";var f=a('<img src="'+d.options.slides[loadSlide].image+'"/>');f.appendTo(g).wrap("<a "+imageLink+h+"></a>").parent().parent().addClass("image-loading").css("visibility","hidden");f.load(function(){d._origDim(a(this));d.resizeNow()})}d.nextSlide()}else{if(e=="prev"){vars.current_slide-1<0?loadSlide=d.options.slides.length-1:loadSlide=vars.current_slide-1;var g=d.el+" li:eq("+loadSlide+")";if(!a(g).html()){var h=d.options.new_window?' target="_blank"':"";imageLink=(d.options.slides[loadSlide].url)?"href='"+d.options.slides[loadSlide].url+"'":"";var f=a('<img src="'+d.options.slides[loadSlide].image+'"/>');f.appendTo(g).wrap("<a "+imageLink+h+"></a>").parent().parent().addClass("image-loading").css("visibility","hidden");f.load(function(){d._origDim(a(this));d.resizeNow()})}d.prevSlide()}}};d._origDim=function(e){e.data("origWidth",e.width()).data("origHeight",e.height())};d.afterAnimation=function(){if(d.options.performance==1){d.$el.removeClass("speed").addClass("quality")}if(vars.update_images){vars.current_slide-1<0?setPrev=d.options.slides.length-1:setPrev=vars.current_slide-1;vars.update_images=false;a(".prevslide").removeClass("prevslide");a(d.el+" li:eq("+setPrev+")").addClass("prevslide")}vars.in_animation=false;if(!vars.is_paused&&d.options.slideshow){vars.slideshow_interval=setInterval(d.nextSlide,d.options.slide_interval);if(d.options.stop_loop&&vars.current_slide==d.options.slides.length-1){d.playToggle()}}if(typeof theme!="undefined"&&typeof theme.afterAnimation=="function"){theme.afterAnimation()}return false};d.getField=function(e){return d.options.slides[vars.current_slide][e]};d.init()};a.supersized.vars={thumb_tray:"#thumb-tray",thumb_list:"#thumb-list",slide_list:"#slide-list",current_slide:0,in_animation:false,is_paused:false,hover_pause:false,slideshow_interval:false,update_images:false,options:{}};a.supersized.defaultOptions={slideshow:1,autoplay:1,start_slide:1,stop_loop:0,random:0,slide_interval:5000,transition:1,transition_speed:750,new_window:1,pause_hover:0,keyboard_nav:1,performance:1,image_protect:1,fit_always:0,fit_landscape:0,fit_portrait:1,min_width:0,min_height:0,horizontal_center:1,vertical_center:1,slide_links:1,thumb_links:1,thumbnail_navigation:0};a.fn.supersized=function(b){return this.each(function(){(new a.supersized(b))})}})(jQuery);
\ No newline at end of file diff --git a/apps/gallery/js/supersized.shutter.js b/apps/gallery/js/supersized.shutter.js new file mode 100644 index 00000000000..cc3025a94a3 --- /dev/null +++ b/apps/gallery/js/supersized.shutter.js @@ -0,0 +1,337 @@ +/* + + Supersized - Fullscreen Slideshow jQuery Plugin + Version : 3.2.7 + Theme : Shutter 1.1 + + Site : www.buildinternet.com/project/supersized + Author : Sam Dunn + Company : One Mighty Roar (www.onemightyroar.com) + License : MIT License / GPL License + +*/ + +(function($){ + + theme = { + + + /* Initial Placement + ----------------------------*/ + _init : function(){ + + // Center Slide Links + if (api.options.slide_links) $(vars.slide_list).css('margin-left', -$(vars.slide_list).width()/2); + + // Start progressbar if autoplay enabled + if (api.options.autoplay){ + if (api.options.progress_bar) theme.progressBar(); + }else{ + if ($(vars.play_button).attr('src')) $(vars.play_button).attr("src", vars.image_path + "play.png"); // If pause play button is image, swap src + if (api.options.progress_bar) $(vars.progress_bar).stop().animate({left : -$(window).width()}, 0 ); // Place progress bar + } + + + /* Thumbnail Tray + ----------------------------*/ + // Hide tray off screen + $(vars.thumb_tray).animate({bottom : -$(vars.thumb_tray).height()}, 0 ); + + // Thumbnail Tray Toggle + $(vars.tray_button).toggle(function(){ + $(vars.thumb_tray).stop().animate({bottom : 0, avoidTransforms : true}, 300 ); + if ($(vars.tray_arrow).attr('src')) $(vars.tray_arrow).attr("src", vars.image_path + "button-tray-down.png"); + return false; + }, function() { + $(vars.thumb_tray).stop().animate({bottom : -$(vars.thumb_tray).height(), avoidTransforms : true}, 300 ); + if ($(vars.tray_arrow).attr('src')) $(vars.tray_arrow).attr("src", vars.image_path + "button-tray-up.png"); + return false; + }); + + // Make thumb tray proper size + $(vars.thumb_list).width($('> li', vars.thumb_list).length * $('> li', vars.thumb_list).outerWidth(true)); //Adjust to true width of thumb markers + + // Display total slides + if ($(vars.slide_total).length){ + $(vars.slide_total).html(api.options.slides.length); + } + + + /* Thumbnail Tray Navigation + ----------------------------*/ + if (api.options.thumb_links){ + //Hide thumb arrows if not needed + if ($(vars.thumb_list).width() <= $(vars.thumb_tray).width()){ + $(vars.thumb_back +','+vars.thumb_forward).fadeOut(0); + } + + // Thumb Intervals + vars.thumb_interval = Math.floor($(vars.thumb_tray).width() / $('> li', vars.thumb_list).outerWidth(true)) * $('> li', vars.thumb_list).outerWidth(true); + vars.thumb_page = 0; + + // Cycle thumbs forward + $(vars.thumb_forward).click(function(){ + if (vars.thumb_page - vars.thumb_interval <= -$(vars.thumb_list).width()){ + vars.thumb_page = 0; + $(vars.thumb_list).stop().animate({'left': vars.thumb_page}, {duration:500, easing:'easeOutExpo'}); + }else{ + vars.thumb_page = vars.thumb_page - vars.thumb_interval; + $(vars.thumb_list).stop().animate({'left': vars.thumb_page}, {duration:500, easing:'easeOutExpo'}); + } + }); + + // Cycle thumbs backwards + $(vars.thumb_back).click(function(){ + if (vars.thumb_page + vars.thumb_interval > 0){ + vars.thumb_page = Math.floor($(vars.thumb_list).width() / vars.thumb_interval) * -vars.thumb_interval; + if ($(vars.thumb_list).width() <= -vars.thumb_page) vars.thumb_page = vars.thumb_page + vars.thumb_interval; + $(vars.thumb_list).stop().animate({'left': vars.thumb_page}, {duration:500, easing:'easeOutExpo'}); + }else{ + vars.thumb_page = vars.thumb_page + vars.thumb_interval; + $(vars.thumb_list).stop().animate({'left': vars.thumb_page}, {duration:500, easing:'easeOutExpo'}); + } + }); + + } + + + /* Navigation Items + ----------------------------*/ + $(vars.next_slide).click(function() { + api.nextSlide(); + }); + + $(vars.prev_slide).click(function() { + api.prevSlide(); + }); + + // Full Opacity on Hover + if(jQuery.support.opacity){ + $(vars.prev_slide +','+vars.next_slide).mouseover(function() { + $(this).stop().animate({opacity:1},100); + }).mouseout(function(){ + $(this).stop().animate({opacity:0.6},100); + }); + } + + if (api.options.thumbnail_navigation){ + // Next thumbnail clicked + $(vars.next_thumb).click(function() { + api.nextSlide(); + }); + // Previous thumbnail clicked + $(vars.prev_thumb).click(function() { + api.prevSlide(); + }); + } + + $(vars.play_button).click(function() { + api.playToggle(); + }); + + + /* Thumbnail Mouse Scrub + ----------------------------*/ + if (api.options.mouse_scrub){ + $(vars.thumb_tray).mousemove(function(e) { + var containerWidth = $(vars.thumb_tray).width(), + listWidth = $(vars.thumb_list).width(); + if (listWidth > containerWidth){ + var mousePos = 1, + diff = e.pageX - mousePos; + if (diff > 10 || diff < -10) { + mousePos = e.pageX; + newX = (containerWidth - listWidth) * (e.pageX/containerWidth); + diff = parseInt(Math.abs(parseInt($(vars.thumb_list).css('left'))-newX )).toFixed(0); + $(vars.thumb_list).stop().animate({'left':newX}, {duration:diff*3, easing:'easeOutExpo'}); + } + } + }); + } + + + /* Window Resize + ----------------------------*/ + $(window).resize(function(){ + + // Delay progress bar on resize + if (api.options.progress_bar && !vars.in_animation){ + if (vars.slideshow_interval) clearInterval(vars.slideshow_interval); + if (api.options.slides.length - 1 > 0) clearInterval(vars.slideshow_interval); + + $(vars.progress_bar).stop().animate({left : -$(window).width()}, 0 ); + + if (!vars.progressDelay && api.options.slideshow){ + // Delay slideshow from resuming so Chrome can refocus images + vars.progressDelay = setTimeout(function() { + if (!vars.is_paused){ + theme.progressBar(); + vars.slideshow_interval = setInterval(api.nextSlide, api.options.slide_interval); + } + vars.progressDelay = false; + }, 1000); + } + } + + // Thumb Links + if (api.options.thumb_links && vars.thumb_tray.length){ + // Update Thumb Interval & Page + vars.thumb_page = 0; + vars.thumb_interval = Math.floor($(vars.thumb_tray).width() / $('> li', vars.thumb_list).outerWidth(true)) * $('> li', vars.thumb_list).outerWidth(true); + + // Adjust thumbnail markers + if ($(vars.thumb_list).width() > $(vars.thumb_tray).width()){ + $(vars.thumb_back +','+vars.thumb_forward).fadeIn('fast'); + $(vars.thumb_list).stop().animate({'left':0}, 200); + }else{ + $(vars.thumb_back +','+vars.thumb_forward).fadeOut('fast'); + } + + } + }); + + + }, + + + /* Go To Slide + ----------------------------*/ + goTo : function(){ + if (api.options.progress_bar && !vars.is_paused){ + $(vars.progress_bar).stop().animate({left : -$(window).width()}, 0 ); + theme.progressBar(); + } + }, + + /* Play & Pause Toggle + ----------------------------*/ + playToggle : function(state){ + + if (state =='play'){ + // If image, swap to pause + if ($(vars.play_button).attr('src')) $(vars.play_button).attr("src", vars.image_path + "pause.png"); + if (api.options.progress_bar && !vars.is_paused) theme.progressBar(); + }else if (state == 'pause'){ + // If image, swap to play + if ($(vars.play_button).attr('src')) $(vars.play_button).attr("src", vars.image_path + "play.png"); + if (api.options.progress_bar && vars.is_paused)$(vars.progress_bar).stop().animate({left : -$(window).width()}, 0 ); + } + + }, + + + /* Before Slide Transition + ----------------------------*/ + beforeAnimation : function(direction){ + if (api.options.progress_bar && !vars.is_paused) $(vars.progress_bar).stop().animate({left : -$(window).width()}, 0 ); + + /* Update Fields + ----------------------------*/ + // Update slide caption + if ($(vars.slide_caption).length){ + (api.getField('title')) ? $(vars.slide_caption).html(api.getField('title')) : $(vars.slide_caption).html(''); + } + // Update slide number + if (vars.slide_current.length){ + $(vars.slide_current).html(vars.current_slide + 1); + } + + + // Highlight current thumbnail and adjust row position + if (api.options.thumb_links){ + + $('.current-thumb').removeClass('current-thumb'); + $('li', vars.thumb_list).eq(vars.current_slide).addClass('current-thumb'); + + // If thumb out of view + if ($(vars.thumb_list).width() > $(vars.thumb_tray).width()){ + // If next slide direction + if (direction == 'next'){ + if (vars.current_slide == 0){ + vars.thumb_page = 0; + $(vars.thumb_list).stop().animate({'left': vars.thumb_page}, {duration:500, easing:'easeOutExpo'}); + } else if ($('.current-thumb').offset().left - $(vars.thumb_tray).offset().left >= vars.thumb_interval){ + vars.thumb_page = vars.thumb_page - vars.thumb_interval; + $(vars.thumb_list).stop().animate({'left': vars.thumb_page}, {duration:500, easing:'easeOutExpo'}); + } + // If previous slide direction + }else if(direction == 'prev'){ + if (vars.current_slide == api.options.slides.length - 1){ + vars.thumb_page = Math.floor($(vars.thumb_list).width() / vars.thumb_interval) * -vars.thumb_interval; + if ($(vars.thumb_list).width() <= -vars.thumb_page) vars.thumb_page = vars.thumb_page + vars.thumb_interval; + $(vars.thumb_list).stop().animate({'left': vars.thumb_page}, {duration:500, easing:'easeOutExpo'}); + } else if ($('.current-thumb').offset().left - $(vars.thumb_tray).offset().left < 0){ + if (vars.thumb_page + vars.thumb_interval > 0) return false; + vars.thumb_page = vars.thumb_page + vars.thumb_interval; + $(vars.thumb_list).stop().animate({'left': vars.thumb_page}, {duration:500, easing:'easeOutExpo'}); + } + } + } + + + } + + }, + + + /* After Slide Transition + ----------------------------*/ + afterAnimation : function(){ + if (api.options.progress_bar && !vars.is_paused) theme.progressBar(); // Start progress bar + }, + + + /* Progress Bar + ----------------------------*/ + progressBar : function(){ + $(vars.progress_bar).stop().animate({left : -$(window).width()}, 0 ).animate({left:0}, api.options.slide_interval); + } + + + }; + + + /* Theme Specific Variables + ----------------------------*/ + $.supersized.themeVars = { + + // Internal Variables + progress_delay : false, // Delay after resize before resuming slideshow + thumb_page : false, // Thumbnail page + thumb_interval : false, // Thumbnail interval + image_path : OC.webroot+"/apps/gallery/img/supersized/", // Default image path + + // General Elements + play_button : '#pauseplay', // Play/Pause button + next_slide : '#nextslide', // Next slide button + prev_slide : '#prevslide', // Prev slide button + next_thumb : '#nextthumb', // Next slide thumb button + prev_thumb : '#prevthumb', // Prev slide thumb button + + slide_caption : '#slidecaption', // Slide caption + slide_current : '.slidenumber', // Current slide number + slide_total : '.totalslides', // Total Slides + slide_list : '#slide-list', // Slide jump list + + thumb_tray : '#thumb-tray', // Thumbnail tray + thumb_list : '#thumb-list', // Thumbnail list + thumb_forward : '#thumb-forward', // Cycles forward through thumbnail list + thumb_back : '#thumb-back', // Cycles backwards through thumbnail list + tray_arrow : '#tray-arrow', // Thumbnail tray button arrow + tray_button : '#tray-button', // Thumbnail tray button + + progress_bar : '#progress-bar' // Progress bar + + }; + + /* Theme Specific Options + ----------------------------*/ + $.supersized.themeOptions = { + + progress_bar : 1, // Timer for each slide + mouse_scrub : 0 // Thumbnails move with mouse + + }; + + +})(jQuery); diff --git a/apps/gallery/js/supersized.shutter.min.js b/apps/gallery/js/supersized.shutter.min.js new file mode 100644 index 00000000000..52ea4a3384a --- /dev/null +++ b/apps/gallery/js/supersized.shutter.min.js @@ -0,0 +1,14 @@ +/* + + Supersized - Fullscreen Slideshow jQuery Plugin + Version : 3.2.7 + Theme : Shutter 1.1 + + Site : www.buildinternet.com/project/supersized + Author : Sam Dunn + Company : One Mighty Roar (www.onemightyroar.com) + License : MIT License / GPL License + +*/ + +(function(a){theme={_init:function(){if(api.options.slide_links){a(vars.slide_list).css("margin-left",-a(vars.slide_list).width()/2)}if(api.options.autoplay){if(api.options.progress_bar){theme.progressBar()}}else{if(a(vars.play_button).attr("src")){a(vars.play_button).attr("src",vars.image_path+"play.png")}if(api.options.progress_bar){a(vars.progress_bar).stop().animate({left:-a(window).width()},0)}}a(vars.thumb_tray).animate({bottom:-a(vars.thumb_tray).height()},0);a(vars.tray_button).toggle(function(){a(vars.thumb_tray).stop().animate({bottom:0,avoidTransforms:true},300);if(a(vars.tray_arrow).attr("src")){a(vars.tray_arrow).attr("src",vars.image_path+"button-tray-down.png")}return false},function(){a(vars.thumb_tray).stop().animate({bottom:-a(vars.thumb_tray).height(),avoidTransforms:true},300);if(a(vars.tray_arrow).attr("src")){a(vars.tray_arrow).attr("src",vars.image_path+"button-tray-up.png")}return false});a(vars.thumb_list).width(a("> li",vars.thumb_list).length*a("> li",vars.thumb_list).outerWidth(true));if(a(vars.slide_total).length){a(vars.slide_total).html(api.options.slides.length)}if(api.options.thumb_links){if(a(vars.thumb_list).width()<=a(vars.thumb_tray).width()){a(vars.thumb_back+","+vars.thumb_forward).fadeOut(0)}vars.thumb_interval=Math.floor(a(vars.thumb_tray).width()/a("> li",vars.thumb_list).outerWidth(true))*a("> li",vars.thumb_list).outerWidth(true);vars.thumb_page=0;a(vars.thumb_forward).click(function(){if(vars.thumb_page-vars.thumb_interval<=-a(vars.thumb_list).width()){vars.thumb_page=0;a(vars.thumb_list).stop().animate({left:vars.thumb_page},{duration:500,easing:"easeOutExpo"})}else{vars.thumb_page=vars.thumb_page-vars.thumb_interval;a(vars.thumb_list).stop().animate({left:vars.thumb_page},{duration:500,easing:"easeOutExpo"})}});a(vars.thumb_back).click(function(){if(vars.thumb_page+vars.thumb_interval>0){vars.thumb_page=Math.floor(a(vars.thumb_list).width()/vars.thumb_interval)*-vars.thumb_interval;if(a(vars.thumb_list).width()<=-vars.thumb_page){vars.thumb_page=vars.thumb_page+vars.thumb_interval}a(vars.thumb_list).stop().animate({left:vars.thumb_page},{duration:500,easing:"easeOutExpo"})}else{vars.thumb_page=vars.thumb_page+vars.thumb_interval;a(vars.thumb_list).stop().animate({left:vars.thumb_page},{duration:500,easing:"easeOutExpo"})}})}a(vars.next_slide).click(function(){api.nextSlide()});a(vars.prev_slide).click(function(){api.prevSlide()});if(jQuery.support.opacity){a(vars.prev_slide+","+vars.next_slide).mouseover(function(){a(this).stop().animate({opacity:1},100)}).mouseout(function(){a(this).stop().animate({opacity:0.6},100)})}if(api.options.thumbnail_navigation){a(vars.next_thumb).click(function(){api.nextSlide()});a(vars.prev_thumb).click(function(){api.prevSlide()})}a(vars.play_button).click(function(){api.playToggle()});if(api.options.mouse_scrub){a(vars.thumb_tray).mousemove(function(f){var c=a(vars.thumb_tray).width(),g=a(vars.thumb_list).width();if(g>c){var b=1,d=f.pageX-b;if(d>10||d<-10){b=f.pageX;newX=(c-g)*(f.pageX/c);d=parseInt(Math.abs(parseInt(a(vars.thumb_list).css("left"))-newX)).toFixed(0);a(vars.thumb_list).stop().animate({left:newX},{duration:d*3,easing:"easeOutExpo"})}}})}a(window).resize(function(){if(api.options.progress_bar&&!vars.in_animation){if(vars.slideshow_interval){clearInterval(vars.slideshow_interval)}if(api.options.slides.length-1>0){clearInterval(vars.slideshow_interval)}a(vars.progress_bar).stop().animate({left:-a(window).width()},0);if(!vars.progressDelay&&api.options.slideshow){vars.progressDelay=setTimeout(function(){if(!vars.is_paused){theme.progressBar();vars.slideshow_interval=setInterval(api.nextSlide,api.options.slide_interval)}vars.progressDelay=false},1000)}}if(api.options.thumb_links&&vars.thumb_tray.length){vars.thumb_page=0;vars.thumb_interval=Math.floor(a(vars.thumb_tray).width()/a("> li",vars.thumb_list).outerWidth(true))*a("> li",vars.thumb_list).outerWidth(true);if(a(vars.thumb_list).width()>a(vars.thumb_tray).width()){a(vars.thumb_back+","+vars.thumb_forward).fadeIn("fast");a(vars.thumb_list).stop().animate({left:0},200)}else{a(vars.thumb_back+","+vars.thumb_forward).fadeOut("fast")}}})},goTo:function(b){if(api.options.progress_bar&&!vars.is_paused){a(vars.progress_bar).stop().animate({left:-a(window).width()},0);theme.progressBar()}},playToggle:function(b){if(b=="play"){if(a(vars.play_button).attr("src")){a(vars.play_button).attr("src",vars.image_path+"pause.png")}if(api.options.progress_bar&&!vars.is_paused){theme.progressBar()}}else{if(b=="pause"){if(a(vars.play_button).attr("src")){a(vars.play_button).attr("src",vars.image_path+"play.png")}if(api.options.progress_bar&&vars.is_paused){a(vars.progress_bar).stop().animate({left:-a(window).width()},0)}}}},beforeAnimation:function(b){if(api.options.progress_bar&&!vars.is_paused){a(vars.progress_bar).stop().animate({left:-a(window).width()},0)}if(a(vars.slide_caption).length){(api.getField("title"))?a(vars.slide_caption).html(api.getField("title")):a(vars.slide_caption).html("")}if(vars.slide_current.length){a(vars.slide_current).html(vars.current_slide+1)}if(api.options.thumb_links){a(".current-thumb").removeClass("current-thumb");a("li",vars.thumb_list).eq(vars.current_slide).addClass("current-thumb");if(a(vars.thumb_list).width()>a(vars.thumb_tray).width()){if(b=="next"){if(vars.current_slide==0){vars.thumb_page=0;a(vars.thumb_list).stop().animate({left:vars.thumb_page},{duration:500,easing:"easeOutExpo"})}else{if(a(".current-thumb").offset().left-a(vars.thumb_tray).offset().left>=vars.thumb_interval){vars.thumb_page=vars.thumb_page-vars.thumb_interval;a(vars.thumb_list).stop().animate({left:vars.thumb_page},{duration:500,easing:"easeOutExpo"})}}}else{if(b=="prev"){if(vars.current_slide==api.options.slides.length-1){vars.thumb_page=Math.floor(a(vars.thumb_list).width()/vars.thumb_interval)*-vars.thumb_interval;if(a(vars.thumb_list).width()<=-vars.thumb_page){vars.thumb_page=vars.thumb_page+vars.thumb_interval}a(vars.thumb_list).stop().animate({left:vars.thumb_page},{duration:500,easing:"easeOutExpo"})}else{if(a(".current-thumb").offset().left-a(vars.thumb_tray).offset().left<0){if(vars.thumb_page+vars.thumb_interval>0){return false}vars.thumb_page=vars.thumb_page+vars.thumb_interval;a(vars.thumb_list).stop().animate({left:vars.thumb_page},{duration:500,easing:"easeOutExpo"})}}}}}}},afterAnimation:function(){if(api.options.progress_bar&&!vars.is_paused){theme.progressBar()}},progressBar:function(){a(vars.progress_bar).stop().animate({left:-a(window).width()},0).animate({left:0},api.options.slide_interval)}};a.supersized.themeVars={progress_delay:false,thumb_page:false,thumb_interval:false,image_path:OC.webroot+"/apps/gallery/img/supersized/",play_button:"#pauseplay",next_slide:"#nextslide",prev_slide:"#prevslide",next_thumb:"#nextthumb",prev_thumb:"#prevthumb",slide_caption:"#slidecaption",slide_current:".slidenumber",slide_total:".totalslides",slide_list:"#slide-list",thumb_tray:"#thumb-tray",thumb_list:"#thumb-list",thumb_forward:"#thumb-forward",thumb_back:"#thumb-back",tray_arrow:"#tray-arrow",tray_button:"#tray-button",progress_bar:"#progress-bar"};a.supersized.themeOptions={progress_bar:1,mouse_scrub:0}})(jQuery); diff --git a/apps/gallery/l10n/ca.php b/apps/gallery/l10n/ca.php index 165414fba20..1c5848cee28 100644 --- a/apps/gallery/l10n/ca.php +++ b/apps/gallery/l10n/ca.php @@ -1,9 +1,9 @@ <?php $TRANSLATIONS = array( "Pictures" => "Fotos", -"Settings" => "Arranjament", -"Rescan" => "Escaneja de nou", -"Stop" => "Atura", -"Share" => "Comparteix", +"Share gallery" => "Comperteix la galeria", +"Error: " => "Error: ", +"Internal error" => "Error intern", +"Slideshow" => "Passi de diapositives", "Back" => "Enrera", "Remove confirmation" => "Elimina la confirmació", "Do you want to remove album" => "Voleu eliminar l'à lbum", diff --git a/apps/gallery/l10n/cs_CZ.php b/apps/gallery/l10n/cs_CZ.php index d008e9d28b3..02d1c02d6e9 100644 --- a/apps/gallery/l10n/cs_CZ.php +++ b/apps/gallery/l10n/cs_CZ.php @@ -1,12 +1,7 @@ <?php $TRANSLATIONS = array( "Pictures" => "Obrázky", -"Settings" => "NastavenÃ", -"Rescan" => "Znovu prohledat", -"Stop" => "Zastavit", -"Share" => "SdÃlet", -"Back" => "ZpÄ›t", -"Remove confirmation" => "Potvrzenà odebránÃ", -"Do you want to remove album" => "Chcete odstranit album?", -"Change album name" => "ZmÄ›nit název alba", -"New album name" => "Název nového alba" +"Share gallery" => "SdÃlet galerii", +"Error: " => "Chyba: ", +"Internal error" => "VnitÅ™nà chyba", +"Slideshow" => "PÅ™ehrávánÃ" ); diff --git a/apps/gallery/l10n/de.php b/apps/gallery/l10n/de.php index 6c3d9fc7389..cd580cf303c 100644 --- a/apps/gallery/l10n/de.php +++ b/apps/gallery/l10n/de.php @@ -1,9 +1,9 @@ <?php $TRANSLATIONS = array( "Pictures" => "Bilder", -"Settings" => "Einstellungen", -"Rescan" => "Erneut Scannen", -"Stop" => "Stopp", -"Share" => "Teilen", +"Share gallery" => "Galerie teilen", +"Error: " => "Fehler:", +"Internal error" => "Interner Fehler", +"Slideshow" => "Slideshow", "Back" => "Zurück", "Remove confirmation" => "Bestätigung entfernen", "Do you want to remove album" => "Soll das Album entfernt werden", diff --git a/apps/gallery/l10n/el.php b/apps/gallery/l10n/el.php index 3983011a0c0..47bc3af2bb6 100644 --- a/apps/gallery/l10n/el.php +++ b/apps/gallery/l10n/el.php @@ -1,9 +1,9 @@ <?php $TRANSLATIONS = array( "Pictures" => "Εικόνες", -"Settings" => "Ρυθμίσεις", -"Rescan" => "ΕπανασάÏωση", -"Stop" => "Διακοπή", -"Share" => "Κοινοποίηση", +"Share gallery" => "Κοινοποίηση συλλογής", +"Error: " => "Σφάλμα: ", +"Internal error" => "ΕσωτεÏικό σφάλμα", +"Slideshow" => "Î Ïοβολή Διαφανειών", "Back" => "ΕπιστÏοφή", "Remove confirmation" => "ΑφαίÏεση επιβεβαίωσης", "Do you want to remove album" => "ΘÎλετε να αφαιÏÎσετε το άλμπουμ", diff --git a/apps/gallery/l10n/es.php b/apps/gallery/l10n/es.php index 03e8d6a4563..aa425a0bd04 100644 --- a/apps/gallery/l10n/es.php +++ b/apps/gallery/l10n/es.php @@ -1,9 +1,9 @@ <?php $TRANSLATIONS = array( "Pictures" => "Imágenes", -"Settings" => "Preferencias", -"Rescan" => "Refrescar", -"Stop" => "Parar", -"Share" => "Compartir", +"Share gallery" => "Compartir galerÃa", +"Error: " => "Fallo ", +"Internal error" => "Fallo interno", +"Slideshow" => "Presentación", "Back" => "Atrás", "Remove confirmation" => "Borrar confirmación", "Do you want to remove album" => "¿Quieres eliminar el álbum", diff --git a/apps/gallery/l10n/fi_FI.php b/apps/gallery/l10n/fi_FI.php index 267bb5e547e..659289ae41f 100644 --- a/apps/gallery/l10n/fi_FI.php +++ b/apps/gallery/l10n/fi_FI.php @@ -1,9 +1,9 @@ <?php $TRANSLATIONS = array( "Pictures" => "Kuvat", -"Settings" => "Asetukset", -"Rescan" => "Etsi uusia", -"Stop" => "Pysäytä", -"Share" => "Jaa", +"Share gallery" => "Jaa galleria", +"Error: " => "Virhe: ", +"Internal error" => "Sisäinen virhe", +"Slideshow" => "Diaesitys", "Back" => "Takaisin", "Remove confirmation" => "Poiston vahvistus", "Do you want to remove album" => "Tahdotko poistaa albumin", diff --git a/apps/gallery/l10n/fr.php b/apps/gallery/l10n/fr.php index dfd668ebe89..04421236e10 100644 --- a/apps/gallery/l10n/fr.php +++ b/apps/gallery/l10n/fr.php @@ -1,9 +1,9 @@ <?php $TRANSLATIONS = array( "Pictures" => "Images", -"Settings" => "Préférences", -"Rescan" => "Analyser à nouveau", -"Stop" => "Arrêter", -"Share" => "Partager", +"Share gallery" => "Partager la galerie", +"Error: " => "Erreur :", +"Internal error" => "Erreur interne", +"Slideshow" => "Diaporama", "Back" => "Retour", "Remove confirmation" => "Enlever la confirmation", "Do you want to remove album" => "Voulez-vous supprimer l'album", diff --git a/apps/gallery/l10n/it.php b/apps/gallery/l10n/it.php index e21a1d6524b..ef8d596e7eb 100644 --- a/apps/gallery/l10n/it.php +++ b/apps/gallery/l10n/it.php @@ -1,9 +1,9 @@ <?php $TRANSLATIONS = array( "Pictures" => "Immagini", -"Settings" => "Impostazioni", -"Rescan" => "Nuova scansione", -"Stop" => "Ferma", -"Share" => "Condividi", +"Share gallery" => "Condividi la galleria", +"Error: " => "Errore: ", +"Internal error" => "Errore interno", +"Slideshow" => "Presentazione", "Back" => "Indietro", "Remove confirmation" => "Rimuovi conferma", "Do you want to remove album" => "Vuoi rimuovere l'album", diff --git a/apps/gallery/l10n/pl.php b/apps/gallery/l10n/pl.php index 1ff636ac2a0..8c0bd0cb98d 100644 --- a/apps/gallery/l10n/pl.php +++ b/apps/gallery/l10n/pl.php @@ -1,9 +1,9 @@ <?php $TRANSLATIONS = array( "Pictures" => "ZdjÄ™cia", -"Settings" => "Ustawienia", -"Rescan" => "Przeszukaj", -"Stop" => "Stop", -"Share" => "Współdziel", +"Share gallery" => "UdostÄ™pnij galeriÄ™", +"Error: " => "BÅ‚Ä…d: ", +"Internal error" => "BÅ‚Ä…d wewnÄ™trzny", +"Slideshow" => "Pokaz slajdów", "Back" => "Wróć", "Remove confirmation" => "UsuÅ„ potwierdzenie", "Do you want to remove album" => "Czy chcesz usunąć album", diff --git a/apps/gallery/l10n/pt_PT.php b/apps/gallery/l10n/pt_PT.php index 54e99cf2456..8b7aa2a23bc 100644 --- a/apps/gallery/l10n/pt_PT.php +++ b/apps/gallery/l10n/pt_PT.php @@ -1,12 +1,7 @@ <?php $TRANSLATIONS = array( "Pictures" => "Imagens", -"Settings" => "Definições", -"Rescan" => "Atualizar", -"Stop" => "Parar", -"Share" => "Partilhar", -"Back" => "Voltar", -"Remove confirmation" => "Remove confirmação", -"Do you want to remove album" => "Deseja remover o album", -"Change album name" => "Mudar o nome do album", -"New album name" => "Novo nome do album" +"Share gallery" => "Partilhar a galeria", +"Error: " => "Erro: ", +"Internal error" => "Erro interno", +"Slideshow" => "Slideshow" ); diff --git a/apps/gallery/l10n/ru.php b/apps/gallery/l10n/ru.php index f1c530ed289..d6e33e4b017 100644 --- a/apps/gallery/l10n/ru.php +++ b/apps/gallery/l10n/ru.php @@ -1,12 +1,7 @@ <?php $TRANSLATIONS = array( "Pictures" => "РиÑунки", -"Settings" => "ÐаÑтройки", -"Rescan" => "Обновить", -"Stop" => "ОÑтановить", -"Share" => "ПоделитьÑÑ", -"Back" => "Ðазад", -"Remove confirmation" => "Подтверждение удалениÑ", -"Do you want to remove album" => "Ð’Ñ‹ хотите удалить альбом?", -"Change album name" => "Изменить Ð¸Ð¼Ñ Ð°Ð»ÑŒÐ±Ð¾Ð¼Ð°", -"New album name" => "Ðовое Ð¸Ð¼Ñ Ð°Ð»ÑŒÐ±Ð¾Ð¼Ð°" +"Share gallery" => "Опубликовать", +"Error: " => "Ошибка", +"Internal error" => "ВнутреннÑÑ Ð¾ÑˆÐ¸Ð±ÐºÐ°", +"Slideshow" => "Слайдшоу" ); diff --git a/apps/gallery/l10n/sl.php b/apps/gallery/l10n/sl.php index 5e061239862..8d3bb9f5887 100644 --- a/apps/gallery/l10n/sl.php +++ b/apps/gallery/l10n/sl.php @@ -1,9 +1,9 @@ <?php $TRANSLATIONS = array( "Pictures" => "Slike", -"Settings" => "Nastavitve", -"Rescan" => "Ponovno preiÅ¡Äi", -"Stop" => "Stop", -"Share" => "Deli", +"Share gallery" => "Daj galerijo v souporabo", +"Error: " => "Napaka: ", +"Internal error" => "Notranja napaka", +"Slideshow" => "predstavitev", "Back" => "Nazaj", "Remove confirmation" => "Odstrani potrditev", "Do you want to remove album" => "Ali želite odstraniti album", diff --git a/apps/gallery/l10n/sv.php b/apps/gallery/l10n/sv.php index 520d271df10..b63a89d90fc 100644 --- a/apps/gallery/l10n/sv.php +++ b/apps/gallery/l10n/sv.php @@ -1,9 +1,9 @@ <?php $TRANSLATIONS = array( "Pictures" => "Bilder", -"Settings" => "Inställningar", -"Rescan" => "Sök igen", -"Stop" => "Stoppa", -"Share" => "Dela", +"Share gallery" => "Dela galleri", +"Error: " => "Fel:", +"Internal error" => "Internt fel", +"Slideshow" => "Bildspel", "Back" => "Tillbaka", "Remove confirmation" => "Vill du säkert ta bort", "Do you want to remove album" => "Vill du ta bort albumet", diff --git a/apps/gallery/l10n/th_TH.php b/apps/gallery/l10n/th_TH.php index 34da8dc57fb..9bb699e2644 100644 --- a/apps/gallery/l10n/th_TH.php +++ b/apps/gallery/l10n/th_TH.php @@ -1,12 +1,7 @@ <?php $TRANSLATIONS = array( "Pictures" => "รูปภาพ", -"Settings" => "ตั้งค่า", -"Rescan" => "ตรวจสà¸à¸šà¸à¸µà¸à¸„รั้ง", -"Stop" => "หยุด", -"Share" => "à¹à¸Šà¸£à¹Œ", -"Back" => "ย้à¸à¸™à¸à¸¥à¸±à¸š", -"Remove confirmation" => "à¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¸à¸²à¸£à¸¥à¸š", -"Do you want to remove album" => "คุณต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¸à¸±à¸¥à¸šà¸±à¹‰à¸¡à¸à¸à¸à¸«à¸£à¸·à¸à¹„ม่", -"Change album name" => "เปลี่ยนชื่à¸à¸à¸±à¸¥à¸šà¸±à¹‰à¸¡", -"New album name" => "ชื่à¸à¹ƒà¸«à¸¡à¹ˆà¸‚à¸à¸‡à¸à¸±à¸¥à¸šà¸±à¹‰à¸¡" +"Share gallery" => "à¹à¸Šà¸£à¹Œà¸‚้à¸à¸¡à¸¹à¸¥à¹à¸à¸¥à¸à¸£à¸µà¹ˆ", +"Error: " => "พบข้à¸à¸œà¸´à¸”พลาด: ", +"Internal error" => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดภายในระบบ", +"Slideshow" => "ภาพสไลด์โชว์" ); diff --git a/apps/gallery/l10n/tr.php b/apps/gallery/l10n/tr.php index c42592448cb..7d007fa66e1 100644 --- a/apps/gallery/l10n/tr.php +++ b/apps/gallery/l10n/tr.php @@ -1,9 +1,9 @@ <?php $TRANSLATIONS = array( "Pictures" => "Resimler", -"Settings" => "Ayarlar", -"Rescan" => "Yeniden Tara ", -"Stop" => "Durdur", -"Share" => "PaylaÅŸ", +"Share gallery" => "Galeriyi paylaÅŸ", +"Error: " => "Hata: ", +"Internal error" => "İç hata", +"Slideshow" => "Slide Gösterim", "Back" => "Geri", "Remove confirmation" => "DoÄŸrulamayı kaldır", "Do you want to remove album" => "Albümü silmek istiyor musunuz", diff --git a/apps/gallery/l10n/vi.php b/apps/gallery/l10n/vi.php new file mode 100644 index 00000000000..d1d7fc64fca --- /dev/null +++ b/apps/gallery/l10n/vi.php @@ -0,0 +1,11 @@ +<?php $TRANSLATIONS = array( +"Pictures" => "Hình ảnh", +"Share gallery" => "Chia sẻ gallery", +"Error: " => "Lá»—i :", +"Internal error" => "Lá»—i ná»™i bá»™", +"Back" => "Trở lại", +"Remove confirmation" => "Xóa xác nháºn", +"Do you want to remove album" => "Bạn muốn xóa album nà y ", +"Change album name" => "Äổi tên album", +"New album name" => "Tên album má»›i" +); diff --git a/apps/gallery/l10n/zh_CN.php b/apps/gallery/l10n/zh_CN.php index ffa321f7de6..6aa4fc7f353 100644 --- a/apps/gallery/l10n/zh_CN.php +++ b/apps/gallery/l10n/zh_CN.php @@ -1,12 +1,7 @@ <?php $TRANSLATIONS = array( "Pictures" => "图片", -"Settings" => "设置", -"Rescan" => "é‡æ–°æ‰«æ", -"Stop" => "åœæ¢", -"Share" => "分享", -"Back" => "返回", -"Remove confirmation" => "移除确认", -"Do you want to remove album" => "您是å¦æƒ³è¦ç§»é™¤ç›¸å†Œ", -"Change album name" => "修改相册å称", -"New album name" => "新相册å称" +"Share gallery" => "分享图库", +"Error: " => "错误:", +"Internal error" => "内部错误", +"Slideshow" => "å¹»ç¯ç‰‡" ); diff --git a/apps/gallery/lib/album.php b/apps/gallery/lib/album.php index b9aa5356292..d1b29a59928 100644 --- a/apps/gallery/lib/album.php +++ b/apps/gallery/lib/album.php @@ -77,7 +77,7 @@ class OC_Gallery_Album { $sql .= ' AND `parent_path` = ?'; $args[] = $parent; } - $order = OCP\Config::getUserValue($owner, 'gallery', 'order', 'ASC'); + $order = OCP\Config::getUserValue($owner, 'gallery', 'order', 'ASC'); $sql .= ' ORDER BY `album_name` ' . $order; $stmt = OCP\DB::prepare($sql); @@ -90,25 +90,22 @@ class OC_Gallery_Album { } public static function changeThumbnailPath($oldname, $newname) { - - $thumbpath = OC::$CONFIG_DATADIRECTORY.'/../gallery/'; - rename($thumbpath.$oldname.'.png', $thumbpath.$newname.'.png'); + $view = OCP\Files::getStorage('gallery'); + $view->rename($oldname.'.png', $newname.'.png'); } public static function getAlbumSize($id){ $sql = 'SELECT COUNT(*) AS `size` FROM `*PREFIX*gallery_photos` WHERE `album_id` = ?'; - $stmt = OCP\DB::prepare($sql); - $result=$stmt->execute(array($id))->fetchRow(); - return $result['size']; + $stmt = OCP\DB::prepare($sql); + $result=$stmt->execute(array($id))->fetchRow(); + return $result['size']; } - public static function getIntermediateGallerySize($path) { - $path .= '%'; + public static function getIntermediateGallerySize($path) { + $path .= '%'; $sql = 'SELECT COUNT(*) AS `size` FROM `*PREFIX*gallery_photos` AS `photos`, `*PREFIX*gallery_albums` AS `albums` WHERE `photos`.`album_id` = `albums`.`album_id` AND `uid_owner` = ? AND `file_path` LIKE ?'; - $stmt = OCP\DB::prepare($sql); - $result = $stmt->execute(array(OCP\USER::getUser(), $path))->fetchRow(); - return $result['size']; - } + $stmt = OCP\DB::prepare($sql); + $result = $stmt->execute(array(OCP\USER::getUser(), $path))->fetchRow(); + return $result['size']; + } } - -?> diff --git a/apps/gallery/lib/hooks_handlers.php b/apps/gallery/lib/hooks_handlers.php index a9f4dc6affc..3bafdb5cf4d 100644 --- a/apps/gallery/lib/hooks_handlers.php +++ b/apps/gallery/lib/hooks_handlers.php @@ -21,7 +21,7 @@ * */ -OCP\Util::connectHook(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_delete, "OC_Gallery_Hooks_Handlers", "removePhoto"); +OCP\Util::connectHook('OC_Filesystem', 'delete', "OC_Gallery_Hooks_Handlers", "removePhoto"); //OCP\Util::connectHook(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_rename, "OC_Gallery_Hooks_Handlers", "renamePhoto"); require_once(OC::$CLASSPATH['Pictures_Managers']); @@ -38,5 +38,3 @@ class OC_Gallery_Hooks_Handlers { //TODO: implement this } } - -?> diff --git a/apps/gallery/lib/images_utils.php b/apps/gallery/lib/images_utils.php index ac3a383c977..f5e37cf1dee 100644 --- a/apps/gallery/lib/images_utils.php +++ b/apps/gallery/lib/images_utils.php @@ -60,5 +60,3 @@ function CroppedThumbnail($imgSrc,$thumbnail_width,$thumbnail_height, $tgtImg, $ imagedestroy($process); imagedestroy($myImage); } - -?> diff --git a/apps/gallery/lib/managers.php b/apps/gallery/lib/managers.php index 82356e54dda..666c6d68931 100644 --- a/apps/gallery/lib/managers.php +++ b/apps/gallery/lib/managers.php @@ -2,10 +2,9 @@ namespace OC\Pictures; -require_once('lib/base.php'); - -class DatabaseManager { +class DatabaseManager { private static $instance = null; + protected $cache = array(); const TAG = 'DatabaseManager'; public static function getInstance() { @@ -14,22 +13,44 @@ class DatabaseManager { return self::$instance; } + protected function getPathData($path) { + $stmt = \OCP\DB::prepare('SELECT * FROM `*PREFIX*pictures_images_cache` + WHERE `uid_owner` LIKE ? AND `path` LIKE ? AND `path` NOT LIKE ?'); + $path_match = $path.'/%'; + $path_notmatch = $path.'/%/%'; + $result = $stmt->execute(array(\OCP\USER::getUser(), $path_match, $path_notmatch)); + $this->cache[$path] = array(); + while (($row = $result->fetchRow()) != false) { + $this->cache[$path][$row['path']] = $row; + } + } + + public function setFileData($path, $width, $height) { + $stmt = \OCP\DB::prepare('INSERT INTO `*PREFIX*pictures_images_cache` (`uid_owner`, `path`, `width`, `height`) VALUES (?, ?, ?, ?)'); + $stmt->execute(array(\OCP\USER::getUser(), $path, $width, $height)); + $ret = array('path' => $path, 'width' => $width, 'height' => $height); + $dir = dirname($path); + $this->cache[$dir][$path] = $ret; + return $ret; + } + public function getFileData($path) { $gallery_path = \OCP\Config::getSystemValue( 'datadirectory' ).'/'.\OC_User::getUser().'/gallery'; $path = $gallery_path.$path; - $stmt = \OCP\DB::prepare('SELECT * FROM `*PREFIX*pictures_images_cache` WHERE `uid_owner` LIKE ? AND `path` = ?'); - $result = $stmt->execute(array(\OCP\USER::getUser(), $path)); - if (($row = $result->fetchRow()) != false) { - return $row; + $dir = dirname($path); + if (!isset($this->cache[$dir])) { + $this->getPathData($dir); + } + if (isset($this->cache[$dir][$path])) { + return $this->cache[$dir][$path]; } $image = new \OC_Image(); if (!$image->loadFromFile($path)) { return false; } - $stmt = \OCP\DB::prepare('INSERT INTO `*PREFIX*pictures_images_cache` (`uid_owner`, `path`, `width`, `height`) VALUES (?, ?, ?, ?)'); - $stmt->execute(array(\OCP\USER::getUser(), $path, $image->width(), $image->height())); - $ret = array('path' => $path, 'width' => $image->width(), 'height' => $image->height()); + $ret = $this->setFileData($path, $image->width(), $image->height()); unset($image); + $this->cache[$dir][$path] = $ret; return $ret; } @@ -40,6 +61,7 @@ class ThumbnailsManager { private static $instance = null; const TAG = 'ThumbnailManager'; + const THUMBNAIL_HEIGHT = 150; public static function getInstance() { if (self::$instance === null) @@ -48,9 +70,9 @@ class ThumbnailsManager { } public function getThumbnail($path) { - $gallery_path = \OCP\Config::getSystemValue( 'datadirectory' ).'/'.\OC_User::getUser().'/gallery'; - if (file_exists($gallery_path.$path)) { - return new \OC_Image($gallery_path.$path); + $gallery_storage = \OCP\Files::getStorage('gallery'); + if ($gallery_storage->file_exists($path)) { + return new \OC_Image($gallery_storage->getLocalFile($path)); } if (!\OC_Filesystem::file_exists($path)) { \OC_Log::write(self::TAG, 'File '.$path.' don\'t exists', \OC_Log::WARN); @@ -59,27 +81,39 @@ class ThumbnailsManager { $image = new \OC_Image(); $image->loadFromFile(\OC_Filesystem::getLocalFile($path)); if (!$image->valid()) return false; - + $image->fixOrientation(); - - $ret = $image->preciseResize(floor((150*$image->width())/$image->height()), 150); + + $ret = $image->preciseResize( floor((self::THUMBNAIL_HEIGHT*$image->width())/$image->height()), self::THUMBNAIL_HEIGHT ); if (!$ret) { \OC_Log::write(self::TAG, 'Couldn\'t resize image', \OC_Log::ERROR); unset($image); return false; } - - $image->save($gallery_path.'/'.$path); + $l = $gallery_storage->getLocalFile($path); + + $image->save($l); return $image; } - + + public function getThumbnailWidth($image) { + return floor((self::THUMBNAIL_HEIGHT*$image->widthTopLeft())/$image->heightTopLeft()); + } + public function getThumbnailInfo($path) { $arr = DatabaseManager::getInstance()->getFileData($path); if (!$arr) { - $thubnail = $this->getThumbnail($path); - unset($thubnail); - $arr = DatabaseManager::getInstance()->getFileData($path); + if (!\OC_Filesystem::file_exists($path)) { + \OC_Log::write(self::TAG, 'File '.$path.' don\'t exists', \OC_Log::WARN); + return false; + } + $image = new \OC_Image(); + $image->loadFromFile(\OC_Filesystem::getLocalFile($path)); + if (!$image->valid()) { + return false; + } + $arr = DatabaseManager::getInstance()->setFileData($path, $this->getThumbnailWidth($image), self::THUMBNAIL_HEIGHT); } $ret = array('filepath' => $arr['path'], 'width' => $arr['width'], @@ -88,13 +122,12 @@ class ThumbnailsManager { } public function delete($path) { - $thumbnail = \OCP\Config::getSystemValue('datadirectory').'/'.\OC_User::getUser()."/gallery".$path; - if (file_exists($thumbnail)) { - unlink($thumbnail); + $thumbnail_storage = \OCP\Files::getStorage('gallery'); + if ($thumbnail_storage->file_exists($path)) { + $thumbnail_storage->unlink($path); } } private function __construct() {} } -?> diff --git a/apps/gallery/lib/photo.php b/apps/gallery/lib/photo.php index 1b4f908773e..f073ac9cbbb 100644 --- a/apps/gallery/lib/photo.php +++ b/apps/gallery/lib/photo.php @@ -1,26 +1,25 @@ <?php /** -* ownCloud - gallery application -* -* @author Bartek Przybylski -* @copyright 2012 Bartek Przybylski bart.p.pl@gmail.com -* -* This library is free software; you can redistribute it and/or -* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE -* License as published by the Free Software Foundation; either -* version 3 of the License, or any later version. -* -* This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. -* -* You should have received a copy of the GNU Lesser General Public -* License along with this library. If not, see <http://www.gnu.org/licenses/>. -* -*/ - + * ownCloud - gallery application + * + * @author Bartek Przybylski + * @copyright 2012 Bartek Przybylski bart.p.pl@gmail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ class OC_Gallery_Photo { public static function create($albumId, $img){ $stmt = OCP\DB::prepare('INSERT INTO `*PREFIX*gallery_photos` (`album_id`, `file_path`) VALUES (?, ?)'); @@ -36,62 +35,95 @@ class OC_Gallery_Photo { $stmt = OCP\DB::prepare($sql); return $stmt->execute($args); } - public static function findForAlbum($owner, $album_name){ + + public static function findForAlbum($owner, $album_name) { $stmt = OCP\DB::prepare('SELECT *' - .' FROM `*PREFIX*gallery_photos photos`,' - .' `*PREFIX*gallery_albums albums`' - .' WHERE `albums`.`uid_owner` = ?' - .' AND `albums`.`album_name` = ?' - .' AND `photos`.`album_id` = `albums`.`album_id`'); + .' FROM `*PREFIX*gallery_photos photos`,' + .' `*PREFIX*gallery_albums albums`' + .' WHERE `albums`.`uid_owner` = ?' + .' AND `albums`.`album_name` = ?' + .' AND `photos`.`album_id` = `albums`.`album_id`'); return $stmt->execute(array($owner, $album_name)); } - public static function removeByPath($path, $album_id) { - $stmt = OCP\DB::prepare('DELETE FROM `*PREFIX*gallery_photos` WHERE `file_path` LIKE ? AND `album_id` = ?'); + public static function removeByPath($path, $album_id) { + $stmt = OCP\DB::prepare('DELETE FROM `*PREFIX*gallery_photos` WHERE `file_path` LIKE ? AND `album_id` = ?'); $stmt->execute(array($path, $album_id)); } public static function removeById($id) { - $stmt = OCP\DB::prepare('DELETE FROM `*PREFIX*gallery_photos` WHERE `photo_id` = ?'); + $stmt = OCP\DB::prepare('DELETE FROM *PREFIX*gallery_photos WHERE photo_id = ?'); $stmt->execute(array($id)); } public static function removeByAlbumId($albumid) { - $stmt = OCP\DB::prepare('DELETE FROM `*PREFIX*gallery_photos` WHERE `album_id` = ?'); + $stmt = OCP\DB::prepare('DELETE FROM *PREFIX*gallery_photos WHERE album_id = ?'); $stmt->execute(array($albumid)); } public static function changePath($oldAlbumId, $newAlbumId, $oldpath, $newpath) { - $stmt = OCP\DB::prepare("UPDATE `*PREFIX*gallery_photos` SET `file_path` = ?, `album_id` = ? WHERE `album_id` = ? AND `file_path` = ?"); + $stmt = OCP\DB::prepare("UPDATE *PREFIX*gallery_photos SET file_path = ?, album_id = ? WHERE album_id = ? and file_path = ?"); $stmt->execute(array($newpath, $newAlbumId, $oldAlbumId, $oldpath)); } public static function getThumbnail($image_name, $owner = null) { + if (!$owner) + $owner = OCP\USER::getUser(); + $view = OCP\Files::getStorage('gallery'); + $save_dir = dirname($image_name); + if (!$view->is_dir($save_dir)) { + $view->mkdir($save_dir); + } + $view->chroot($view->getRoot() . '/' . $save_dir); + $thumb_file = basename($image_name); + if ($view->file_exists($thumb_file)) { + $image = new OC_Image($view->fopen($thumb_file, 'r')); + } else { + $image_path = OC_Filesystem::getLocalFile($image_name); + if (!file_exists($image_path)) { + return null; + } + $image = new OC_Image($image_path); + if ($image->valid()) { + $image->centerCrop(200); + $image->fixOrientation(); + $image->save($view->getLocalFile($thumb_file)); + } + } + if ($image->valid()) { + return $image; + } else { + $image->destroy(); + } + return null; + } + + public static function getViewImage($image_name, $owner = null) { if (!$owner) $owner = OCP\USER::getUser(); - $save_dir = OCP\Config::getSystemValue("datadirectory").'/'. $owner .'/gallery/'; - $save_dir .= dirname($image_name). '/'; + $save_dir = OCP\Config::getSystemValue("datadirectory") . '/' . $owner . '/gallery'; + $save_dir .= dirname($image_name) . '/view/'; $image_path = $image_name; - $thumb_file = $save_dir . basename($image_name); + $view_file = $save_dir . basename($image_name); if (!is_dir($save_dir)) { mkdir($save_dir, 0777, true); } - if (file_exists($thumb_file)) { - $image = new OC_Image($thumb_file); + if (file_exists($view_file)) { + $image = new OC_Image($view_file); } else { $image_path = OC_Filesystem::getLocalFile($image_path); - if(!file_exists($image_path)) { + if (!file_exists($image_path)) { return null; } $image = new OC_Image($image_path); if ($image->valid()) { - $image->centerCrop(200); + $image->resize(1200); $image->fixOrientation(); - $image->save($thumb_file); + $image->save($view_file); } } if ($image->valid()) { return $image; - }else{ + } else { $image->destroy(); } return null; @@ -100,4 +132,5 @@ class OC_Gallery_Photo { public static function getGalleryRoot() { return OCP\Config::getUserValue(OCP\USER::getUser(), 'gallery', 'root', ''); } + } diff --git a/apps/gallery/lib/scanner.php b/apps/gallery/lib/scanner.php index b6c402022bc..79b8ad4923e 100644 --- a/apps/gallery/lib/scanner.php +++ b/apps/gallery/lib/scanner.php @@ -81,7 +81,8 @@ class OC_Gallery_Scanner { $image->destroy(); } } - imagepng($thumbnail, OCP\Config::getSystemValue("datadirectory").'/'. OCP\USER::getUser() .'/gallery/' . $albumName.'.png'); + $view = OCP\Files::getStorage('gallery'); + imagepng($thumbnail, $view->getLocalFile($albumName.'.png')); imagedestroy($thumbnail); } diff --git a/apps/gallery/lib/share.php b/apps/gallery/lib/share.php new file mode 100644 index 00000000000..d6c5f40d492 --- /dev/null +++ b/apps/gallery/lib/share.php @@ -0,0 +1,42 @@ +<?php +/** +* ownCloud +* +* @author Michael Gapczynski +* @copyright 2012 Michael Gapczynski mtgap@owncloud.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +abstract class OC_Share_Photo_Backend implements OCP\Share_Backend { + + public $dependsOn = 'file'; + public $supportedFileExtensions = array('jpg', 'png', 'gif'); + + public function getSource($item, $uid) { + return array('item' => 'blah.jpg', 'file' => $item); + } + + public function generateTarget($item, $uid, $exclude = null) { + // TODO Make sure target path doesn't exist already + return $item; + } + + public function formatItems($items, $format, $parameters = null) { + + } + +} + +?>
\ No newline at end of file diff --git a/apps/gallery/lib/tiles.php b/apps/gallery/lib/tiles.php index 53ea97ff05d..e36d26d3191 100644 --- a/apps/gallery/lib/tiles.php +++ b/apps/gallery/lib/tiles.php @@ -33,7 +33,7 @@ class TilesLine { } public function setAvailableSpace($space) { - $available_space = $space; + $this->available_space = $space; } public function getTilesCount() { @@ -95,7 +95,7 @@ class TileSingle extends TileBase { public function get($extra = '') { // !HACK! file path needs to be encoded twice because files app decode twice url, so any special chars like + or & in filename // !HACK! will result in failing of opening them - return '<a rel="images" title="'.htmlentities(basename($this->getPath())).'" href="'.\OCP\Util::linkTo('files', 'download.php').'?file='.urlencode(urlencode($this->getPath())).'"><img rel="images" src="'.\OCP\Util::linkTo('gallery', 'ajax/thumbnail.php').'&filepath='.urlencode($this->getPath()).'" '.$extra.'></a>'; + return '<a rel="images" title="'.htmlentities(basename($this->getPath())).'" href="'.\OCP\Util::linkTo('gallery','ajax/viewImage.php').'?img='.urlencode(urlencode($this->getPath())).'"><img rel="images" src="'.\OCP\Util::linkTo('gallery', 'ajax/thumbnail.php').'&filepath='.urlencode($this->getPath()).'" '.$extra.'></a>'; } public function getMiniatureSrc() { @@ -168,11 +168,9 @@ class TileStack extends TileBase { } public function getOnClickAction() { - return 'javascript:openNewGal(\''.htmlentities($this->stack_name).'\');'; + return 'javascript:openNewGal(\''.rawurlencode($this->stack_name).'\');'; } private $tiles_array; private $stack_name; } - -?> diff --git a/apps/gallery/lib/tiles_test.php b/apps/gallery/lib/tiles_test.php index 022a88f75cc..02d567c628d 100644 --- a/apps/gallery/lib/tiles_test.php +++ b/apps/gallery/lib/tiles_test.php @@ -83,5 +83,3 @@ if ($ts->getCount() != 0) { } echo $tl->get(); - -?> diff --git a/apps/gallery/templates/index.php b/apps/gallery/templates/index.php index a41bf3c47ba..b2efd5342ff 100644 --- a/apps/gallery/templates/index.php +++ b/apps/gallery/templates/index.php @@ -1,52 +1,6 @@ -<?php - -$l = OC_L10N::get('gallery'); -$root = !empty($_GET['root']) ? $_GET['root'] : '/'; -?> -<style> -div.gallery_div {position:relative; display: inline-block; height: 152px; width: 150px; margin: 5px;} -div.miniature_border {position:absolute; height: 150px; -moz-transition-duration: 0.2s; -o-transition-duration:0.2s; -webkit-transition-duration: .2s; background-position: 50%;} -div.line {display:inline-block; border: 0; width: auto; height: 160px} -div.gallery_div img{position:absolute; top: 1; left: 0; -moz-transition-duration: 0.3s; -o-transition-duration:0.3s; -webkit-transition-duration: 0.3s; height:150px; width: auto;} -div.gallery_div img.shrinker {width:80px !important;} -div.title { opacity: 0; text-align: center; vertical-align: middle; font-family: Arial; font-size: 12px; border: 0; position: absolute; text-overflow: ellipsis; bottom: 20px; right:-5px; height:auto; padding: 5px; width: 140px; background-color: black; color: white; -webkit-transition: opacity 0.5s; z-index:1000; border-radius: 7px} -div.visible { opacity: 0.8;} -</style> <script type="text/javascript"> -var root = "<?php echo htmlentities($root); ?>"; - -function explode(element) { - $('div', element).each(function(index, elem) { - if ($(elem).hasClass('title')) { - $(elem).addClass('visible'); - } else { - $(elem).css('margin-top', Math.floor(30-(Math.random()*60)) + 'px') - .css('margin-left', Math.floor(30-(Math.random()*60))+ 'px') - .css('z-index', '999'); - } - }); -} - -function deplode(element) { - $('div', element).each(function(index, elem) { - if ($(elem).hasClass('title')) { - $(elem).removeClass('visible'); - } else { - $(elem).css('margin-top', Math.floor(5-(Math.random()*10)) + 'px') - .css('margin-left', Math.floor(5-(Math.random()*10))+ 'px') - .css('z-index', '3'); - } - }); -} - -function openNewGal(album_name) { - root = root + album_name + "/"; - var url = window.location.toString().replace(window.location.search, ''); - url = url + "?app=gallery&root="+encodeURIComponent(root); - - window.location = url; -} +var root = "<?php echo $_['root']; ?>"; $(document).ready(function() { $("a[rel=images]").fancybox({ @@ -57,85 +11,74 @@ $(document).ready(function() { </script> <div id="controls"><?php - $sr = trim($root, '/'); + $sr = trim($_['root'], '/'); if (!empty($sr)) { $paths = explode('/', $sr); $path = '/'; for ($i = 0; $i < count($paths); $i++) { $path .= urlencode($paths[$i]).'/'; $classess = 'crumb'.($i == count($paths)-1?' last':''); - echo '<div class="'.$classess.'" style="background-image:url(\''.\OCP\image_path('core','breadcrumb.png').'\')"><a href="'.\OCP\Util::linkTo('gallery', 'index.php').'&root='.$path.'">'.\OCP\Util::sanitizeHTML($paths[$i]).'</a></div>'; + echo '<div class="'.$classess.'" style="background-image:url(\''.\OCP\image_path('core','breadcrumb.png').'\')"><a href="'.\OCP\Util::linkTo('gallery', 'index.php').'&root='.$path.'">'.OCP\Util::sanitizeHTML($paths[$i]).'</a></div>'; } } -?> <!--<a href="javascript:shareGallery();"><input type="button" value="<?php echo $l->t('Share');?>" /></a>--><br/> +?> + <div id="slideshow"> + <input type="button" class="start" value="<?php echo $l->t('Slideshow')?>" /> + </div> </div> <div id="gallerycontent"> <?php +session_write_close(); -include('apps/gallery/lib/tiles.php'); -$root = empty($_GET['root'])?'/':$_GET['root']; - -$images = \OC_FileCache::searchByMime('image', null, '/'.\OCP\USER::getUser().'/files'.$root); -sort($images); - -$tl = new \OC\Pictures\TilesLine(); -$ts = new \OC\Pictures\TileStack(array(), ''); -$previous_element = @$images[0]; - -$root_images = array(); -$second_level_images = array(); - -$fallback_images = array(); // if the folder only cotains subfolders with images -> these are taken for the stack preview - -for($i = 0; $i < count($images); $i++) { - $prev_dir_arr = explode('/', $previous_element); - $dir_arr = explode('/', $images[$i]); - - if(count($dir_arr) == 1) { // getting the images in this directory - $root_images[] = $root.$images[$i]; - } else { - if(strcmp($prev_dir_arr[0], $dir_arr[0]) != 0) { // if we entered a new directory - if(count($second_level_images) == 0) { // if we don't have images in this directory - if(count($fallback_images) != 0) { // but have fallback_images - $tl->addTile(new \OC\Pictures\TileStack($fallback_images, $prev_dir_arr[0])); - $fallback_images = array(); - } - } else { // if we collected images for this directory - $tl->addTile(new \OC\Pictures\TileStack($second_level_images, $prev_dir_arr[0])); - $fallback_images = array(); - $second_level_images = array(); - } - } - if (count($dir_arr) == 2) { // These are the pics in our current subdir - $second_level_images[] = $root.$images[$i]; - $fallback_images = array(); - } else { // These are images from the deeper directories - if(count($second_level_images) == 0) { - $fallback_images[] = $root.$images[$i]; - } - } - // have us a little something to compare against - $previous_element = $images[$i]; - } -} +echo $_['tl']->get(); -// if last element in the directory was a directory we don't want to miss it :) -if(count($second_level_images)>0) { - $tl->addTile(new \OC\Pictures\TileStack($second_level_images, $prev_dir_arr[0])); -} +?> +</div> -// if last element in the directory was a directory with no second_level_images we also don't want to miss it ... -if(count($fallback_images)>0) { - $tl->addTile(new \OC\Pictures\TileStack($fallback_images, $prev_dir_arr[0])); -} +<!-- start supersized block --> +<div id="slideshow-content" style="display:none;"> -// and finally our images actually stored in the root folder -for($i = 0; $i<count($root_images); $i++) { - $tl->addTile(new \OC\Pictures\TileSingle($root_images[$i])); -} + <!--Thumbnail Navigation--> + <div id="prevthumb"></div> + <div id="nextthumb"></div> -echo $tl->get(); + <!--Arrow Navigation--> + <a id="prevslide" class="load-item"></a> + <a id="nextslide" class="load-item"></a> -?> -</div> + <div id="thumb-tray" class="load-item"> + <div id="thumb-back"></div> + <div id="thumb-forward"></div> + </div> + + <!--Time Bar--> + <div id="progress-back" class="load-item"> + <div id="progress-bar"></div> + </div> + + <!--Control Bar--> + <div id="slideshow-controls-wrapper" class="load-item"> + <div id="slideshow-controls"> + + <a id="play-button"><img id="pauseplay" src="<?php echo OCP\image_path('gallery', 'supersized/pause.png'); ?>"/></a> + + <!--Slide counter--> + <div id="slidecounter"> + <span class="slidenumber"></span> / <span class="totalslides"></span> + </div> + + <!--Slide captions displayed here--> + <div id="slidecaption"></div> + + <!--Thumb Tray button--> + <a id="tray-button"><img id="tray-arrow" src="<?php echo OCP\image_path('gallery', 'supersized/button-tray-up.png'); ?>"/></a> + + <!--Navigation--> + <!-- + <ul id="slide-list"></ul> + --> + </div> + </div> + +</div><!-- end supersized block --> diff --git a/apps/gallery/templates/view_album.php b/apps/gallery/templates/view_album.php deleted file mode 100644 index c16ed69c065..00000000000 --- a/apps/gallery/templates/view_album.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php -OCP\Util::addStyle('gallery', 'styles'); -OCP\Util::addscript('gallery', 'albums'); -OCP\Util::addscript('gallery', 'album_cover'); -OCP\Util::addscript('files_imageviewer', 'jquery.mousewheel-3.0.4.pack'); -OCP\Util::addscript('files_imageviewer', 'jquery.fancybox-1.3.4.pack'); -OCP\Util::addStyle( 'files_imageviewer', 'jquery.fancybox-1.3.4' ); -$l = OC_L10N::get('gallery'); -?> -<script type="text/javascript"> - $(document).ready(function() { - $("a[rel=images]").fancybox({ - 'titlePosition': 'inside' - }); - }); -</script> - -<div id="controls"> - <a href="?"><input type="button" value="<?php echo $l->t('Back');?>" /></a> -<br/> -</div> - -<div id="gallery_list" class="leftcontent"> -</div> - -<div id="gallery_images" class="rightcontent"> -<?php -foreach ($_['photos'] as $a) { -?> -<a rel="images" href="../../files/download.php?file=<?php echo urlencode($a); ?>"><img src="ajax/thumbnail.php?img=<?php echo urlencode($a) ?>"></a> -<?php - } -?> -</div> - -<div id="dialog-confirm" title="<?php echo $l->t('Remove confirmation');?>" style="display: none"> - <p><span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 20px 0;"></span><?php echo $l->t('Do you want to remove album');?> <span id="albumName"></span>?</p> -</div> - -<div id="dialog-form" title="<?php echo $l->t('Change album name');?>" style="display:none"> - <form> - <fieldset> - <label for="name"><?php echo $l->t('New album name');?></label> - <input type="text" name="name" id="name" class="text ui-widget-content ui-corner-all" /> - </fieldset> - </form> -</div> diff --git a/apps/impress/appinfo/app.php b/apps/impress/appinfo/app.php new file mode 100644 index 00000000000..82f098a030d --- /dev/null +++ b/apps/impress/appinfo/app.php @@ -0,0 +1,38 @@ +<?php + +/** + * ownCloud - Impress App + * + * @author Frank Karlitschek + * @copyright 2011 Frank Karlitschek karlitschek@kde.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +OCP\Util::addStyle( 'impress', 'style'); + +OCP\App::register(array('order' => 70, 'id' => 'impress', 'name' => 'Impress')); + +OCP\App::addNavigationEntry(array( + 'id' => 'impress_index', + 'order' => 80, + 'href' => OCP\Util::linkTo('impress', 'index.php'), + 'icon' => OCP\Util::imagePath('impress', 'impress.png'), + 'name' => 'Impress') +); + + + +?> diff --git a/apps/impress/appinfo/info.xml b/apps/impress/appinfo/info.xml new file mode 100644 index 00000000000..0be9c170f65 --- /dev/null +++ b/apps/impress/appinfo/info.xml @@ -0,0 +1,10 @@ +<?xml version="1.0"?> +<info> + <id>impress</id> + <name>Impress</name> + <description>A Player for impress presentations. Many thanks to Bartek Szopka for creating the wonderful impress.js library who made this app possible.</description> + <licence>AGPL</licence> + <author>Frank Karlitschek</author> + <require>4</require> + <shipped>true</shipped> +</info> diff --git a/apps/impress/appinfo/version b/apps/impress/appinfo/version new file mode 100644 index 00000000000..9f8e9b69a33 --- /dev/null +++ b/apps/impress/appinfo/version @@ -0,0 +1 @@ +1.0
\ No newline at end of file diff --git a/apps/impress/css/player.css b/apps/impress/css/player.css new file mode 100644 index 00000000000..0ed7130fd0b --- /dev/null +++ b/apps/impress/css/player.css @@ -0,0 +1,703 @@ +/* + So you like the style of impress.js demo? + Or maybe you are just curious how it was done? + + You couldn't find a better place to find out! + + Welcome to the stylesheet impress.js demo presentation. + + Please remember that it is not meant to be a part of impress.js and is + not required by impress.js. + I expect that anyone creating a presentation for impress.js would create + their own set of styles. + + But feel free to read through it and learn how to get the most of what + impress.js provides. + + And let me be your guide. + + Shall we begin? +*/ + + +/* + We start with a good ol' reset. + That's the one by Eric Meyer http://meyerweb.com/eric/tools/css/reset/ + + You can probably argue if it is needed here, or not, but for sure it + doesn't do any harm and gives us a fresh start. +*/ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} + +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} + +/* + Now here is when interesting things start to appear. + + We set up <body> styles with default font and nice gradient in the background. + And yes, there is a lot of repetition there because of -prefixes but we don't + want to leave anybody behind. +*/ +body { + font-family: 'PT Sans', sans-serif; + min-height: 740px; + + background: rgb(215, 215, 215); + background: -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 500, from(rgb(240, 240, 240)), to(rgb(190, 190, 190))); + background: -webkit-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190)); + background: -moz-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190)); + background: -ms-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190)); + background: -o-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190)); + background: radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190)); +} + +/* + Now let's bring some text styles back ... +*/ +b, strong { font-weight: bold } +i, em { font-style: italic } + +/* + ... and give links a nice look. +*/ +a { + color: inherit; + text-decoration: none; + padding: 0 0.1em; + background: rgba(255,255,255,0.5); + text-shadow: -1px -1px 2px rgba(100,100,100,0.9); + border-radius: 0.2em; + + -webkit-transition: 0.5s; + -moz-transition: 0.5s; + -ms-transition: 0.5s; + -o-transition: 0.5s; + transition: 0.5s; +} + +a:hover, +a:focus { + background: rgba(255,255,255,1); + text-shadow: -1px -1px 2px rgba(100,100,100,0.5); +} + +/* + Because the main point behind the impress.js demo is to demo impress.js + we display a fallback message for users with browsers that don't support + all the features required by it. + + All of the content will be still fully accessible for them, but I want + them to know that they are missing something - that's what the demo is + about, isn't it? + + And then we hide the message, when support is detected in the browser. +*/ + +.fallback-message { + font-family: sans-serif; + line-height: 1.3; + + width: 780px; + padding: 10px 10px 0; + margin: 20px auto; + + border: 1px solid #E4C652; + border-radius: 10px; + background: #EEDC94; +} + +.fallback-message p { + margin-bottom: 10px; +} + +.impress-supported .fallback-message { + display: none; +} + +/* + Now let's style the presentation steps. + + We start with basics to make sure it displays correctly in everywhere ... +*/ + +.step { + position: relative; + width: 900px; + padding: 40px; + margin: 20px auto; + + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + -o-box-sizing: border-box; + box-sizing: border-box; + + font-family: 'PT Serif', georgia, serif; + font-size: 48px; + line-height: 1.5; +} + +/* + ... and we enhance the styles for impress.js. + + Basically we remove the margin and make inactive steps a little bit transparent. +*/ +.impress-enabled .step { + margin: 0; + opacity: 0.3; + + -webkit-transition: opacity 1s; + -moz-transition: opacity 1s; + -ms-transition: opacity 1s; + -o-transition: opacity 1s; + transition: opacity 1s; +} + +.impress-enabled .step.active { opacity: 1 } + +/* + These 'slide' step styles were heavily inspired by HTML5 Slides: + http://html5slides.googlecode.com/svn/trunk/styles.css + + ;) + + They cover everything what you see on first three steps of the demo. +*/ +.slide { + display: block; + + width: 900px; + height: 700px; + padding: 40px 60px; + + background-color: white; + border: 1px solid rgba(0, 0, 0, .3); + border-radius: 10px; + box-shadow: 0 2px 6px rgba(0, 0, 0, .1); + + color: rgb(102, 102, 102); + text-shadow: 0 2px 2px rgba(0, 0, 0, .1); + + font-family: 'Open Sans', Arial, sans-serif; + font-size: 30px; + line-height: 36px; + letter-spacing: -1px; +} + +.slide q { + display: block; + font-size: 50px; + line-height: 72px; + + margin-top: 100px; +} + +.slide q strong { + white-space: nowrap; +} + +/* + And now we start to style each step separately. + + I agree that this may be not the most efficient, object-oriented and + scalable way of styling, but most of steps have quite a custom look + and typography tricks here and there, so they had to be styles separately. + + First is the title step with a big <h1> (no room for padding) and some + 3D positioning along Z axis. +*/ + +#title { + padding: 0; +} + +#title .try { + font-size: 64px; + position: absolute; + top: -0.5em; + left: 1.5em; + + -webkit-transform: translateZ(20px); + -moz-transform: translateZ(20px); + -ms-transform: translateZ(20px); + -o-transform: translateZ(20px); + transform: translateZ(20px); +} + +#title h1 { + font-size: 190px; + + -webkit-transform: translateZ(50px); + -moz-transform: translateZ(50px); + -ms-transform: translateZ(50px); + -o-transform: translateZ(50px); + transform: translateZ(50px); +} + +#title .footnote { + font-size: 32px; +} + +/* + Second step is nothing special, just a text with a link, so it doesn't need + any special styling. + + Let's move to 'big thoughts' with centered text and custom font sizes. +*/ +#big { + width: 600px; + text-align: center; + font-size: 60px; + line-height: 1; +} + +#big b { + display: block; + font-size: 250px; + line-height: 250px; +} + +#big .thoughts { + font-size: 90px; + line-height: 150px; +} + +/* + 'Tiny ideas' just need some tiny styling. +*/ +#tiny { + width: 500px; + text-align: center; +} + +/* + This step has some animated text ... +*/ +#ing { width: 500px } + +/* + ... so we define display to `inline-block` to enable transforms and + transition duration to 0.5s ... +*/ +#ing b { + display: inline-block; + -webkit-transition: 0.5s; + -moz-transition: 0.5s; + -ms-transition: 0.5s; + -o-transition: 0.5s; + transition: 0.5s; +} + +/* + ... and we want 'positioning` word to move up a bit when the step gets + `present` class ... +*/ +#ing.present .positioning { + -webkit-transform: translateY(-10px); + -moz-transform: translateY(-10px); + -ms-transform: translateY(-10px); + -o-transform: translateY(-10px); + transform: translateY(-10px); +} + +/* + ... 'rotating' to rotate quater of a second later ... +*/ +#ing.present .rotating { + -webkit-transform: rotate(-10deg); + -moz-transform: rotate(-10deg); + -ms-transform: rotate(-10deg); + -o-transform: rotate(-10deg); + transform: rotate(-10deg); + + -webkit-transition-delay: 0.25s; + -moz-transition-delay: 0.25s; + -ms-transition-delay: 0.25s; + -o-transition-delay: 0.25s; + transition-delay: 0.25s; +} + +/* + ... and 'scaling' to scale down after another quater of a second. +*/ +#ing.present .scaling { + -webkit-transform: scale(0.7); + -moz-transform: scale(0.7); + -ms-transform: scale(0.7); + -o-transform: scale(0.7); + transform: scale(0.7); + + -webkit-transition-delay: 0.5s; + -moz-transition-delay: 0.5s; + -ms-transition-delay: 0.5s; + -o-transition-delay: 0.5s; + transition-delay: 0.5s; +} + +/* + The 'imagination' step is again some boring font-sizing. +*/ + +#imagination { + width: 600px; +} + +#imagination .imagination { + font-size: 78px; +} + +/* + There is nothing really special about 'use the source, Luke' step, too, + except maybe of the Yoda background. + + As you can see below I've 'hard-coded' it in data URL. + That's not the best way to serve images, but because that's just this one + I decided it will be OK to have it this way. + + Just make sure you don't blindly copy this approach. +*/ +#source { + width: 700px; + padding-bottom: 300px; + + /* Yoda Icon :: Pixel Art from Star Wars http://www.pixeljoint.com/pixelart/1423.htm */ + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARgAAAEYCAMAAACwUBm+AAAAAXNSR0IArs4c6QAAAKtQTFRFsAAAvbWSLUUrLEQqY1s8UYJMqJ1vNTEgOiIdIzYhjIFVLhsXZ6lgSEIsP2U8JhcCVzMsSXZEgXdOO145XJdWOl03LzAYMk4vSXNExr+hwcuxRTs1Qmk+RW9Am49eFRANQz4pUoNMQWc+OSMDTz0wLBsCNVMxa2NBOyUDUoNNSnlEWo9VRGxAVzYFl6tXCggHbLNmMUIcHhwTXkk5f3VNRT8wUT8xAAAACQocRBWFFwAAAAF0Uk5TAEDm2GYAAAPCSURBVHja7d3JctNAFIZRMwRCCGEmzPM8z/D+T8bu/ptbXXJFdij5fMt2Wuo+2UgqxVmtttq5WVotLzBgwIABAwYMGDCn0qVqbo69psPqVpWx+1XG5iaavF8wYMCAAQMGDBgwi4DJ6Y6qkxB1HNlcN3a92gbR5P2CAQMGDBgwYMCAWSxMlrU+UY5yu2l9okfV4bAxUVbf7TJnAwMGDBgwYMCAAbMLMHeqbGR82Zy+VR1Ht81nVca6R+UdTLaU24Ruzd3qM/e4yjnAgAEDBgwYMGDA7AJMd1l/3NRdVGcj3eX/2WEhCmDGxnM7yqygu8XIPjJj8iN/MGDAgAEDBgwYMAuDGb8q0RGlLCHLv1t9qDKWn3vdNHVuEI6HPaxO9Jo3GDBgwIABAwYMmIXBdC9ShGgMk+XnkXUeuGcsP/e1+lhNnZsL/G5Vs3OAAQMGDBgwYMCAWSxMR3SzOmraG5atdy9wZKzb+vg16qyqe2FltbnAgAEDBgwYMGDALAxmTJSuN3WA76rnVca6GTnemGN1WoEBAwYMGDBgwIBZGMxUomy4+xO899V4LAg5Xnc2MGDAgAEDBgwYMGA218Wq+2K1LDqvY9xZu8zN8fICdM6btYABAwYMGDBgwIABMzfH0+pGU5afze2tXebmeAfVz+p8BQYMGDBgwIABAwbMPBzZ+oWmfJrln1273FhkbHzee9WWbw7AgAEDBgwYMGDALAKm43hcdctKgblcPamOhuXnXlY5Xs6bsW4FGyQCAwYMGDBgwIABswiYMceZKgvMo+h8mrHLTdn676rj+FEFoTtHd8MwOxEYMGDAgAEDBgyYRcBM5UhXqiymW3R3c9ARhWO/OmjqfjVZy+xEYMCAAQMGDBgwYBYG073OnCV0RFNhMhaOa9WfKmOB6XjHMN1tQmaAAQMGDBgwYMCA2VWY7vXjz1U4croAzgPztwIDBgwYMGDAgAEDZhswh035NBw59Dww3RgYMGDAgAEDBgwYMJuD6f4tXT7NUqfCdBvZLkxXdgQGDBgwYMCAAQNmt2DGj8WzwAfV/w7T/aq7mxwwYMCAAQMGDBgwuwqTOo7uTwTngflSzQ3TdaJvAwEDBgwYMGDAgAED5gSvgbyo5oHZ4Pc+gwEDBgwYMGDAgAEzhOm+5G0qTGaAAQMGDBgwYMCAAXNaMOcnls3tNwWm+zRzp54NDBgwYMCAAQMGDJh5YNL36k1TLuGvVq+qnKMbS5n7tulT9asCAwYMGDBgwIABA2ZumKuztLnjgQEDBgwYMGDAgNl5mH/4/ltKA6vBNAAAAABJRU5ErkJggg==); + background-position: bottom right; + background-repeat: no-repeat; +} + +#source q { + font-size: 60px; +} + +/* + And the "it's in 3D" step again brings some 3D typography - just for fun. + + Because we want to position <span> elements in 3D we set transform-style to + `preserve-3d` on the paragraph. + It is not needed by webkit browsers, but it is in Firefox. It's hard to say + which behaviour is correct as 3D transforms spec is not very clear about it. +*/ +#its-in-3d p { + -webkit-transform-style: preserve-3d; + -moz-transform-style: preserve-3d; /* Y U need this Firefox?! */ + -ms-transform-style: preserve-3d; + -o-transform-style: preserve-3d; + transform-style: preserve-3d; +} + +/* + Below we position each word separately along Z axis and we want it to transition + to default position in 0.5s when the step gets `present` class. + + Quite a simple idea, but lot's of styles and prefixes. +*/ +#its-in-3d span, +#its-in-3d b { + display: inline-block; + -webkit-transform: translateZ(40px); + -moz-transform: translateZ(40px); + -ms-transform: translateZ(40px); + -o-transform: translateZ(40px); + transform: translateZ(40px); + + -webkit-transition: 0.5s; + -moz-transition: 0.5s; + -ms-transition: 0.5s; + -o-transition: 0.5s; + transition: 0.5s; +} + +#its-in-3d .have { + -webkit-transform: translateZ(-40px); + -moz-transform: translateZ(-40px); + -ms-transform: translateZ(-40px); + -o-transform: translateZ(-40px); + transform: translateZ(-40px); +} + +#its-in-3d .you { + -webkit-transform: translateZ(20px); + -moz-transform: translateZ(20px); + -ms-transform: translateZ(20px); + -o-transform: translateZ(20px); + transform: translateZ(20px); +} + +#its-in-3d .noticed { + -webkit-transform: translateZ(-40px); + -moz-transform: translateZ(-40px); + -ms-transform: translateZ(-40px); + -o-transform: translateZ(-40px); + transform: translateZ(-40px); +} + +#its-in-3d .its { + -webkit-transform: translateZ(60px); + -moz-transform: translateZ(60px); + -ms-transform: translateZ(60px); + -o-transform: translateZ(60px); + transform: translateZ(60px); +} + +#its-in-3d .in { + -webkit-transform: translateZ(-10px); + -moz-transform: translateZ(-10px); + -ms-transform: translateZ(-10px); + -o-transform: translateZ(-10px); + transform: translateZ(-10px); +} + +#its-in-3d .footnote { + font-size: 32px; + + -webkit-transform: translateZ(-10px); + -moz-transform: translateZ(-10px); + -ms-transform: translateZ(-10px); + -o-transform: translateZ(-10px); + transform: translateZ(-10px); +} + +#its-in-3d.present span, +#its-in-3d.present b { + -webkit-transform: translateZ(0px); + -moz-transform: translateZ(0px); + -ms-transform: translateZ(0px); + -o-transform: translateZ(0px); + transform: translateZ(0px); +} + +/* + The last step is an overview. + There is no content in it, so we make sure it's not visible because we want + to be able to click on other steps. + +*/ +#overview { display: none } + +/* + We also make other steps visible and give them a pointer cursor using the + `impress-on-` class. +*/ +.impress-on-overview .step { + opacity: 1; + cursor: pointer; +} + + +/* + Now, when we have all the steps styled let's give users a hint how to navigate + around the presentation. + + The best way to do this would be to use JavaScript, show a delayed hint for a + first time users, then hide it and store a status in cookie or localStorage... + + But I wanted to have some CSS fun and avoid additional scripting... + + Let me explain it first, so maybe the transition magic will be more readable + when you read the code. + + First of all I wanted the hint to appear only when user is idle for a while. + You can't detect the 'idle' state in CSS, but I delayed a appearing of the + hint by 5s using transition-delay. + + You also can't detect in CSS if the user is a first-time visitor, so I had to + make an assumption that I'll only show the hint on the first step. And when + the step is changed hide the hint, because I can assume that user already + knows how to navigate. + + To summarize it - hint is shown when the user is on the first step for longer + than 5 seconds. + + The other problem I had was caused by the fact that I wanted the hint to fade + in and out. It can be easily achieved by transitioning the opacity property. + But that also meant that the hint was always on the screen, even if totally + transparent. It covered part of the screen and you couldn't correctly clicked + through it. + Unfortunately you cannot transition between display `block` and `none` in pure + CSS, so I needed a way to not only fade out the hint but also move it out of + the screen. + + I solved this problem by positioning the hint below the bottom of the screen + with CSS transform and moving it up to show it. But I also didn't want this move + to be visible. I wanted the hint only to fade in and out visually, so I delayed + the fade in transition, so it starts when the hint is already in its correct + position on the screen. + + I know, it sounds complicated ... maybe it would be easier with the code? +*/ + +.hint { + /* + We hide the hint until presentation is started and from browsers not supporting + impress.js, as they will have a linear scrollable view ... + */ + display: none; + + /* + ... and give it some fixed position and nice styles. + */ + position: fixed; + left: 0; + right: 0; + bottom: 200px; + + background: rgba(0,0,0,0.5); + color: #EEE; + text-align: center; + + font-size: 50px; + padding: 20px; + + z-index: 100; + + /* + By default we don't want the hint to be visible, so we make it transparent ... + */ + opacity: 0; + + /* + ... and position it below the bottom of the screen (relative to it's fixed position) + */ + -webkit-transform: translateY(400px); + -moz-transform: translateY(400px); + -ms-transform: translateY(400px); + -o-transform: translateY(400px); + transform: translateY(400px); + + /* + Now let's imagine that the hint is visible and we want to fade it out and move out + of the screen. + + So we define the transition on the opacity property with 1s duration and another + transition on transform property delayed by 1s so it will happen after the fade out + on opacity finished. + + This way user will not see the hint moving down. + */ + -webkit-transition: opacity 1s, -webkit-transform 0.5s 1s; + -moz-transition: opacity 1s, -moz-transform 0.5s 1s; + -ms-transition: opacity 1s, -ms-transform 0.5s 1s; + -o-transition: opacity 1s, -o-transform 0.5s 1s; + transition: opacity 1s, transform 0.5s 1s; +} + +/* + Now we 'enable' the hint when presentation is initialized ... +*/ +.impress-enabled .hint { display: block } + +/* + ... and we will show it when the first step (with id 'bored') is active. +*/ +.impress-on-bored .hint { + /* + We remove the transparency and position the hint in its default fixed + position. + */ + opacity: 1; + + -webkit-transform: translateY(0px); + -moz-transform: translateY(0px); + -ms-transform: translateY(0px); + -o-transform: translateY(0px); + transform: translateY(0px); + + /* + Now for fade in transition we have the oposite situation from the one + above. + + First after 4.5s delay we animate the transform property to move the hint + into its correct position and after that we fade it in with opacity + transition. + */ + -webkit-transition: opacity 1s 5s, -webkit-transform 0.5s 4.5s; + -moz-transition: opacity 1s 5s, -moz-transform 0.5s 4.5s; + -ms-transition: opacity 1s 5s, -ms-transform 0.5s 4.5s; + -o-transition: opacity 1s 5s, -o-transform 0.5s 4.5s; + transition: opacity 1s 5s, transform 0.5s 4.5s; +} + +/* + And as the last thing there is a workaround for quite strange bug. + It happens a lot in Chrome. I don't remember if I've seen it in Firefox. + + Sometimes the element positioned in 3D (especially when it's moved back + along Z axis) is not clickable, because it falls 'behind' the <body> + element. + + To prevent this, I decided to make <body> non clickable by setting + pointer-events property to `none` value. + Value if this property is inherited, so to make everything else clickable + I bring it back on the #impress element. + + If you want to know more about `pointer-events` here are some docs: + https://developer.mozilla.org/en/CSS/pointer-events + + There is one very important thing to notice about this workaround - it makes + everything 'unclickable' except what's in #impress element. + + So use it wisely ... or don't use at all. +*/ +.impress-enabled { pointer-events: none } +.impress-enabled #impress { pointer-events: auto } + +/* + There is one funny thing I just realized. + + Thanks to this workaround above everything except #impress element is invisible + for click events. That means that the hint element is also not clickable. + So basically all of this transforms and delayed transitions trickery was probably + not needed at all... + + But it was fun to learn about it, wasn't it? +*/ + +/* + That's all I have for you in this file. + Thanks for reading. I hope you enjoyed it at least as much as I enjoyed writing it + for you. +*/ diff --git a/apps/impress/css/style.css b/apps/impress/css/style.css new file mode 100755 index 00000000000..4c7f60ef84b --- /dev/null +++ b/apps/impress/css/style.css @@ -0,0 +1,14 @@ +#emptyfolder { position:absolute; margin:10em 0 0 10em; font-size:1.5em; font-weight:bold; color:#888; text-shadow:#fff 0 1px 0; } + +.impresslist { margin-top:40px; padding:5px; width:100%;} +.impresslist tr:hover { backgound-color:#ccc; } +.impresslist tr td { padding:5px; } + +.docu { position: absolute; right: 13.5em; top: 0em; } + +#documentation { margin: 20px; } +#documentation h1 { font-size:1.8em; font-weight:bold; color:#888; } +#documentation h2 { font-size:1.5em; font-weight:bold; color:#888; } +#documentation h2 { font-size:1.2em; font-weight:bold; color:#888; } + +.examplecode { font-size:0.9em; font-family:"Courier New","Courier"; background-color:#eee; color:#333; width:90%; height:300px; padding:10px;}
\ No newline at end of file diff --git a/apps/impress/documentation.php b/apps/impress/documentation.php new file mode 100755 index 00000000000..17a97432a49 --- /dev/null +++ b/apps/impress/documentation.php @@ -0,0 +1,34 @@ +<?php + +/** + * ownCloud - Impress App + * + * @author Frank Karlitschek + * @copyright 2011 Frank Karlitschek karlitschek@kde.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +require_once('lib/impress.php'); + +OCP\User::checkLoggedIn(); +OCP\App::setActiveNavigationEntry( 'impress_index' ); + + + +$tmpl = new OCP\Template('impress', 'documentation', 'user'); +$tmpl->printPage(); + + diff --git a/apps/impress/img/impress.png b/apps/impress/img/impress.png Binary files differnew file mode 100644 index 00000000000..23e14fc623d --- /dev/null +++ b/apps/impress/img/impress.png diff --git a/apps/impress/img/impress.svg b/apps/impress/img/impress.svg new file mode 100755 index 00000000000..af1cf89d426 --- /dev/null +++ b/apps/impress/img/impress.svg @@ -0,0 +1,1718 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.0" + width="16" + height="16" + id="svg11300" + inkscape:version="0.48.2 r9819" + sodipodi:docname="impress.svg" + inkscape:export-filename="/Users/karlitschek/Desktop/impress.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + <metadata + id="metadata26"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <sodipodi:namedview + pagecolor="#cccccc" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1646" + inkscape:window-height="1184" + id="namedview24" + showgrid="true" + showguides="true" + inkscape:guide-bbox="true" + inkscape:zoom="32.000001" + inkscape:cx="4.7817607" + inkscape:cy="8.2037781" + inkscape:window-x="1667" + inkscape:window-y="42" + inkscape:window-maximized="0" + inkscape:current-layer="g4146"> + <inkscape:grid + type="xygrid" + id="grid4330" + empspacing="5" + dotted="true" + visible="true" + enabled="true" + snapvisiblegridlinesonly="true" /> + </sodipodi:namedview> + <defs + id="defs3"> + <linearGradient + id="linearGradient4136"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1;" + id="stop4138" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop4140" /> + </linearGradient> + <linearGradient + id="linearGradient4303"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop4305" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop4307" /> + </linearGradient> + <linearGradient + id="linearGradient4297"> + <stop + id="stop4299" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop4301" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient4115"> + <stop + id="stop4117" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop4119" + style="stop-color:#363636;stop-opacity:0.698" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient3785"> + <stop + id="stop3787" + style="stop-color:#505050;stop-opacity:1;" + offset="0" /> + <stop + id="stop3789" + style="stop-color:#878787;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient6954"> + <stop + id="stop6960" + style="stop-color:#f5f5f5;stop-opacity:1" + offset="0" /> + <stop + id="stop6962" + style="stop-color:#d2d2d2;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient3341"> + <stop + id="stop3343" + style="stop-color:white;stop-opacity:1" + offset="0" /> + <stop + id="stop3345" + style="stop-color:white;stop-opacity:0" + offset="1" /> + </linearGradient> + <radialGradient + cx="24.999998" + cy="28.659998" + r="16" + fx="24.999998" + fy="28.659998" + id="radialGradient2856" + xlink:href="#linearGradient6954" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.56186795,0,0,0.15787922,-6.1682604,5.3385209)" /> + <linearGradient + x1="30" + y1="25.084745" + x2="30" + y2="45" + id="linearGradient2858" + xlink:href="#linearGradient3785" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" /> + <radialGradient + cx="26.375898" + cy="12.31301" + r="8" + fx="26.375898" + fy="12.31301" + id="radialGradient2860" + xlink:href="#linearGradient6954" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.55250164,-0.0426402,0.04315608,0.50971914,-6.3026675,-1.9765067)" /> + <linearGradient + x1="30" + y1="5" + x2="30" + y2="44.678879" + id="linearGradient2862" + xlink:href="#linearGradient3785" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" /> + <linearGradient + x1="30" + y1="0.91818392" + x2="30" + y2="25.792814" + id="linearGradient2864" + xlink:href="#linearGradient3341" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" /> + <linearGradient + x1="29.955881" + y1="21.86607" + x2="29.955881" + y2="43.144382" + id="linearGradient2866" + xlink:href="#linearGradient3341" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1" + id="linearGradient7308" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.54681372,0,0,0.39376081,3.7325729,-0.29182867)" + x1="34.992828" + y1="0.94087797" + x2="34.992828" + y2="33.955856" /> + <linearGradient + id="linearGradient3587-6-5-3-4-5-4-0-1"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop3589-9-2-2-3-2-53-4-3" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop3591-7-4-73-7-9-86-9-3" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1" + id="linearGradient3796" + x1="8.3635759" + y1="15.028702" + x2="15.937561" + y2="11.00073" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1" + id="linearGradient3798" + x1="6.9951797" + y1="4.7478018" + x2="13.00482" + y2="4.7478018" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1" + id="linearGradient3815" + x1="209.34245" + y1="998.45801" + x2="209.34245" + y2="1013.451" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5" + id="linearGradient3815-3" + x1="209.34245" + y1="998.45801" + x2="209.34245" + y2="1013.451" + gradientUnits="userSpaceOnUse" /> + <linearGradient + id="linearGradient3587-6-5-3-4-5-4-0-1-5"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop3589-9-2-2-3-2-53-4-3-9" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop3591-7-4-73-7-9-86-9-3-0" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5" + id="linearGradient3831" + x1="209.34245" + y1="998.45801" + x2="209.34245" + y2="1013.451" + gradientUnits="userSpaceOnUse" /> + <linearGradient + id="linearGradient3833"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop3835" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop3837" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1" + id="linearGradient3874" + gradientUnits="userSpaceOnUse" + x1="209.34245" + y1="998.45801" + x2="209.34245" + y2="1013.451" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2" + id="linearGradient3892-2" + x1="8.7094374" + y1="1.0035814" + x2="8.6826077" + y2="16.052532" + gradientUnits="userSpaceOnUse" /> + <linearGradient + id="linearGradient3587-6-5-3-4-5-4-0-1-2"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop3589-9-2-2-3-2-53-4-3-1" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop3591-7-4-73-7-9-86-9-3-4" /> + </linearGradient> + <linearGradient + gradientTransform="matrix(0.96967712,0,0,0.96967712,0.26437941,-0.96950812)" + y2="16.052532" + x2="8.6826077" + y1="1.0035814" + x1="8.7094374" + gradientUnits="userSpaceOnUse" + id="linearGradient3909" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2" + inkscape:collect="always" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1" + id="linearGradient3984" + gradientUnits="userSpaceOnUse" + x1="209.34245" + y1="998.45801" + x2="209.34245" + y2="1013.451" /> + <linearGradient + id="linearGradient3587-6-5-3-4-5-4-0-1-2-1"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop3589-9-2-2-3-2-53-4-3-1-4" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop3591-7-4-73-7-9-86-9-3-4-6" /> + </linearGradient> + <linearGradient + gradientTransform="matrix(0.78786264,0,0,0.78786264,-1.5726929,-0.7389112)" + y2="16.052532" + x2="8.6826077" + y1="1.0035814" + x1="8.7094374" + gradientUnits="userSpaceOnUse" + id="linearGradient3909-3" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-2" + inkscape:collect="always" /> + <linearGradient + id="linearGradient3587-6-5-3-4-5-4-0-1-2-2"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop3589-9-2-2-3-2-53-4-3-1-7" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop3591-7-4-73-7-9-86-9-3-4-5" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4115-9" + id="linearGradient4113-3" + x1="0.86849999" + y1="13.895414" + x2="0.44923753" + y2="28.776533" + gradientUnits="userSpaceOnUse" /> + <linearGradient + id="linearGradient4115-9"> + <stop + id="stop4117-5" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop4119-6" + style="stop-color:#363636;stop-opacity:0.698" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3104" + id="linearGradient3815-4" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,0.35950872)" + x1="-51.786404" + y1="50.786446" + x2="-51.786404" + y2="2.9062471" /> + <linearGradient + id="linearGradient3104"> + <stop + id="stop3106" + style="stop-color:#aaaaaa;stop-opacity:1" + offset="0" /> + <stop + id="stop3108" + style="stop-color:#c8c8c8;stop-opacity:1" + offset="1" /> + </linearGradient> + <radialGradient + cx="13.138569" + cy="25.625349" + r="13.931416" + fx="13.138569" + fy="25.625349" + id="radialGradient2965" + xlink:href="#linearGradient3690-451" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0,0.92614711,-1.0546317,0,32.402583,-9.3345932)" /> + <linearGradient + id="linearGradient3690-451"> + <stop + id="stop2857" + style="stop-color:#e8e8e8;stop-opacity:1" + offset="0" /> + <stop + id="stop2859" + style="stop-color:#d8d8d8;stop-opacity:1" + offset="0.26238" /> + <stop + id="stop2861" + style="stop-color:#c2c2c2;stop-opacity:1" + offset="0.66093999" /> + <stop + id="stop2863" + style="stop-color:#a5a5a5;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="21.483376" + y1="36.255058" + x2="21.483376" + y2="9.5799999" + id="linearGradient2967" + xlink:href="#linearGradient3603-84" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762279)" /> + <linearGradient + id="linearGradient3603-84"> + <stop + id="stop2867" + style="stop-color:#707070;stop-opacity:1" + offset="0" /> + <stop + id="stop2869" + style="stop-color:#9e9e9e;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="11.566265" + y1="22.292103" + x2="15.214532" + y2="33.95525" + id="linearGradient3674-262" + xlink:href="#linearGradient8265-821-176-38-919-66-249-529" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4893617,0,0,0.4893617,1.7131795,22.728095)" /> + <linearGradient + id="linearGradient8265-821-176-38-919-66-249-529"> + <stop + id="stop2873" + style="stop-color:#ffffff;stop-opacity:0.27450982" + offset="0" /> + <stop + id="stop2875" + style="stop-color:#ffffff;stop-opacity:0" + offset="1" /> + </linearGradient> + <linearGradient + x1="24.046366" + y1="11.673002" + x2="24.046366" + y2="34.713669" + id="linearGradient3677-116" + xlink:href="#linearGradient3642-81" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)" /> + <linearGradient + id="linearGradient3642-81"> + <stop + id="stop2879" + style="stop-color:#ffffff;stop-opacity:1" + offset="0" /> + <stop + id="stop2881" + style="stop-color:#ffffff;stop-opacity:0" + offset="1" /> + </linearGradient> + <linearGradient + y2="34.713669" + x2="24.046366" + y1="11.673002" + x1="24.046366" + gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)" + gradientUnits="userSpaceOnUse" + id="linearGradient3037" + xlink:href="#linearGradient3642-81" + inkscape:collect="always" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3155-40" + id="linearGradient8639" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.415777,-0.4174938,0.518983,0.5146192,-15.747227,2.6503673)" + spreadMethod="pad" + x1="23.575972" + y1="25.356892" + x2="23.575972" + y2="31.210939" /> + <linearGradient + id="linearGradient3155-40"> + <stop + id="stop2541" + offset="0" + style="stop-color:#181818;stop-opacity:1;" /> + <stop + style="stop-color:#dbdbdb;stop-opacity:1;" + offset="0.13482948" + id="stop2543" /> + <stop + id="stop2545" + offset="0.20224422" + style="stop-color:#a4a4a4;stop-opacity:1;" /> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0.26965895" + id="stop2547" /> + <stop + id="stop2549" + offset="0.44650277" + style="stop-color:#8d8d8d;stop-opacity:1;" /> + <stop + style="stop-color:#959595;stop-opacity:1;" + offset="0.57114136" + id="stop2551" /> + <stop + id="stop2553" + offset="0.72038066" + style="stop-color:#cecece;stop-opacity:1;" /> + <stop + id="stop2555" + offset="1" + style="stop-color:#181818;stop-opacity:1;" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3240-279" + id="linearGradient8641" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.867764,0.6930272)" + x1="30.037716" + y1="24.989594" + x2="30.037716" + y2="30.000141" /> + <linearGradient + id="linearGradient3240-279"> + <stop + style="stop-color:#565656;stop-opacity:1;" + offset="0" + id="stop2559" /> + <stop + id="stop2561" + offset="0.5" + style="stop-color:#9a9a9a;stop-opacity:1;" /> + <stop + style="stop-color:#545454;stop-opacity:1;" + offset="1" + id="stop2563" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3223-789" + id="linearGradient8643" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.983472,0.8092126)" + x1="30.037716" + y1="24.989594" + x2="30.037716" + y2="30.000141" /> + <linearGradient + id="linearGradient3223-789"> + <stop + id="stop2567" + offset="0" + style="stop-color:#b1b1b1;stop-opacity:1;" /> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0.5" + id="stop2569" /> + <stop + id="stop2571" + offset="1" + style="stop-color:#8f8f8f;stop-opacity:1;" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3240-686" + id="linearGradient8645" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.465684,0.2892868)" + x1="30.037716" + y1="24.989594" + x2="30.037716" + y2="30.000141" /> + <linearGradient + id="linearGradient3240-686"> + <stop + style="stop-color:#565656;stop-opacity:1;" + offset="0" + id="stop2575" /> + <stop + id="stop2577" + offset="0.5" + style="stop-color:#9a9a9a;stop-opacity:1;" /> + <stop + style="stop-color:#545454;stop-opacity:1;" + offset="1" + id="stop2579" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3223-768" + id="linearGradient8647" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.581392,0.4054707)" + x1="30.037716" + y1="24.989594" + x2="30.037716" + y2="30.000141" /> + <linearGradient + id="linearGradient3223-768"> + <stop + id="stop2583" + offset="0" + style="stop-color:#b1b1b1;stop-opacity:1;" /> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0.5" + id="stop2585" /> + <stop + id="stop2587" + offset="1" + style="stop-color:#8f8f8f;stop-opacity:1;" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3240-907" + id="linearGradient8649" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.061661,-0.1164056)" + x1="30.037716" + y1="24.989594" + x2="30.037716" + y2="30.000141" /> + <linearGradient + id="linearGradient3240-907"> + <stop + style="stop-color:#565656;stop-opacity:1;" + offset="0" + id="stop2591" /> + <stop + id="stop2593" + offset="0.5" + style="stop-color:#9a9a9a;stop-opacity:1;" /> + <stop + style="stop-color:#545454;stop-opacity:1;" + offset="1" + id="stop2595" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3223-699" + id="linearGradient8651" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.177369,-2.1969969e-4)" + x1="30.037716" + y1="24.989594" + x2="30.037716" + y2="30.000141" /> + <linearGradient + id="linearGradient3223-699"> + <stop + id="stop2599" + offset="0" + style="stop-color:#b1b1b1;stop-opacity:1;" /> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0.5" + id="stop2601" /> + <stop + id="stop2603" + offset="1" + style="stop-color:#8f8f8f;stop-opacity:1;" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3290-678" + id="linearGradient8653" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.602268,-17.636692,0.462492)" + x1="9" + y1="29.056757" + x2="9" + y2="26.02973" /> + <linearGradient + id="linearGradient3290-678"> + <stop + id="stop2607" + offset="0" + style="stop-color:#ece5a5;stop-opacity:1;" /> + <stop + id="stop2609" + offset="1" + style="stop-color:#fcfbf2;stop-opacity:1;" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3191-577" + id="linearGradient8655" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.3763801,0.03615261,0.03669995,0.374874,-2.2182805,-1.1331002)" + x1="5.5178981" + y1="37.371799" + x2="9.5220556" + y2="41.391716" /> + <linearGradient + id="linearGradient3191-577"> + <stop + id="stop2613" + offset="0" + style="stop-color:#dbce48;stop-opacity:1;" /> + <stop + id="stop2615" + offset="1" + style="stop-color:#c5b625;stop-opacity:1;" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0" + id="linearGradient3934-0" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)" + x1="8.7094374" + y1="1.0035814" + x2="8.6826077" + y2="16.052532" /> + <linearGradient + id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop3589-9-2-2-3-2-53-4-3-1-4-9" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop3591-7-4-73-7-9-86-9-3-4-6-0" /> + </linearGradient> + <linearGradient + y2="16.052532" + x2="8.6826077" + y1="1.0035814" + x1="8.7094374" + gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)" + gradientUnits="userSpaceOnUse" + id="linearGradient4154-8" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9" + inkscape:collect="always" /> + <linearGradient + id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4" /> + </linearGradient> + <linearGradient + id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9-0"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8-3" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4-8" /> + </linearGradient> + <linearGradient + id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-3"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop3589-9-2-2-3-2-53-4-3-1-4-9-3" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop3591-7-4-73-7-9-86-9-3-4-6-0-6" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5" + id="linearGradient4326" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)" + x1="14.501121" + y1="-1.4095211" + x2="14.152531" + y2="20.074369" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5" + id="linearGradient4328" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)" + x1="8.7094374" + y1="1.0035814" + x2="8.6826077" + y2="16.052532" /> + <linearGradient + x1="-2.4040222" + y1="4.4573336" + x2="-2.4040222" + y2="18.967093" + id="linearGradient3878" + xlink:href="#linearGradient3587-6-5" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(13.927091,-3.4266134)" /> + <linearGradient + id="linearGradient3587-6-5"> + <stop + id="stop3589-9-2" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3591-7-4" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5" + id="linearGradient4357" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.0344828,0,0,1.0344828,8.0707628,-14.513825)" + x1="0.86849999" + y1="13.895414" + x2="0.44923753" + y2="28.776533" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1" + id="linearGradient4405" + gradientUnits="userSpaceOnUse" + x1="209.34245" + y1="998.45801" + x2="209.34245" + y2="1013.451" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55" + id="linearGradient4413-7" + gradientUnits="userSpaceOnUse" + x1="209.34245" + y1="998.45801" + x2="209.34245" + y2="1013.451" /> + <linearGradient + id="linearGradient3587-6-5-3-4-5-4-0-1-55"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop3589-9-2-2-3-2-53-4-3-95" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop3591-7-4-73-7-9-86-9-3-6" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-2" + id="linearGradient4411-3" + gradientUnits="userSpaceOnUse" + x1="209.34245" + y1="998.45801" + x2="209.34245" + y2="1013.451" /> + <linearGradient + id="linearGradient3587-6-5-2"> + <stop + id="stop3589-9-2-8" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3591-7-4-0" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-2-4" + id="linearGradient4466-9" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,60.359508)" + x1="-41.553459" + y1="2.2401412" + x2="-41.553459" + y2="54.703121" /> + <linearGradient + id="linearGradient3587-6-5-2-4"> + <stop + id="stop3589-9-2-8-7" + style="stop-color:#000000;stop-opacity:1;" + offset="0" /> + <stop + id="stop3591-7-4-0-3" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + y2="54.703121" + x2="-41.553459" + y1="2.2401412" + x1="-41.553459" + gradientTransform="matrix(0.21864454,0,0,0.26685422,17.618755,60.402242)" + gradientUnits="userSpaceOnUse" + id="linearGradient4483-3" + xlink:href="#linearGradient3587-6-5-2-4-9" + inkscape:collect="always" /> + <linearGradient + id="linearGradient3587-6-5-2-4-9"> + <stop + id="stop3589-9-2-8-7-2" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3591-7-4-0-3-8" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55" + id="linearGradient4564" + gradientUnits="userSpaceOnUse" + x1="209.34245" + y1="998.45801" + x2="209.34245" + y2="1013.451" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5" + id="linearGradient4566" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(13.927091,16.573387)" + x1="-2.4040222" + y1="4.4573336" + x2="-2.4040222" + y2="18.967093" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-2" + id="linearGradient4578" + gradientUnits="userSpaceOnUse" + x1="209.34245" + y1="998.45801" + x2="209.34245" + y2="1013.451" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55" + id="linearGradient4580" + gradientUnits="userSpaceOnUse" + x1="209.34245" + y1="998.45801" + x2="209.34245" + y2="1013.451" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3" + id="linearGradient4359-4" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.99998838,0,0,0.99998838,29.038238,-21.358617)" + x1="8.7094374" + y1="1.0035814" + x2="8.6826077" + y2="16.052532" /> + <linearGradient + id="linearGradient3587-6-5-3"> + <stop + id="stop3589-9-2-6" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3591-7-4-5" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3" + id="linearGradient4361-4" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)" + x1="8.7094374" + y1="1.0035814" + x2="8.6826077" + y2="16.052532" /> + <linearGradient + id="linearGradient4597"> + <stop + id="stop4599" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop4601" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + y2="16.052532" + x2="8.6826077" + y1="1.0035814" + x1="8.7094374" + gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)" + gradientUnits="userSpaceOnUse" + id="linearGradient4610" + xlink:href="#linearGradient3587-6-5-3" + inkscape:collect="always" /> + <linearGradient + x1="1.3333321" + y1="6.6666665" + x2="1.3333321" + y2="33.333332" + id="linearGradient2422" + xlink:href="#linearGradient3587-6-5-5" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4090909,0,0,0.375,7.4545459,0.5)" /> + <linearGradient + id="linearGradient3587-6-5-5"> + <stop + id="stop3589-9-2-4" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3591-7-4-3" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" + id="linearGradient3189" + xlink:href="#linearGradient3587-6-5-8" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)" /> + <linearGradient + id="linearGradient3587-6-5-8"> + <stop + id="stop3589-9-2-67" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3591-7-4-2" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" + id="linearGradient3203" + xlink:href="#linearGradient3587-6-5-8" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)" /> + <linearGradient + id="linearGradient3120"> + <stop + id="stop3122" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3124" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" + id="linearGradient3207" + xlink:href="#linearGradient3587-6-5-8" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)" /> + <linearGradient + id="linearGradient3127"> + <stop + id="stop3129" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3131" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" + id="linearGradient3211" + xlink:href="#linearGradient3587-6-5-8" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)" /> + <linearGradient + id="linearGradient3134"> + <stop + id="stop3136" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3138" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="11" + y1="6" + x2="11" + y2="17" + id="linearGradient2409" + xlink:href="#linearGradient3587-6-5-1" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.2403101,0,0,0.8988764,10.387597,0.2247191)" /> + <linearGradient + id="linearGradient3587-6-5-1"> + <stop + id="stop3589-9-2-0" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3591-7-4-21" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="40.805084" + y1="5.6271191" + x2="40.805084" + y2="17.627119" + id="linearGradient3206" + xlink:href="#linearGradient3587-8-5" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-32.805085,-3.6271193)" /> + <linearGradient + id="linearGradient3587-8-5"> + <stop + id="stop3589-2-7" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3591-3-5" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + y2="17.627119" + x2="40.805084" + y1="5.6271191" + x1="40.805084" + gradientTransform="translate(-32.805085,-3.6271193)" + gradientUnits="userSpaceOnUse" + id="linearGradient3180" + xlink:href="#linearGradient3587-8-5" + inkscape:collect="always" /> + <linearGradient + x1="1.3333321" + y1="6.6666665" + x2="1.3333321" + y2="33.333332" + id="linearGradient2422-1" + xlink:href="#linearGradient3587-6-5-86" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.2727273,0,0,0.375,9.636365,1.5)" /> + <linearGradient + id="linearGradient3587-6-5-86"> + <stop + id="stop3589-9-2-65" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3591-7-4-9" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="11" + y1="6" + x2="11" + y2="17" + id="linearGradient2427" + xlink:href="#linearGradient3587-6-5-86" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,1.5842703)" /> + <linearGradient + id="linearGradient3207-3"> + <stop + id="stop3209" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3211" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="11" + y1="6" + x2="11" + y2="17" + id="linearGradient2436" + xlink:href="#linearGradient3587-6-5-86" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,9.58427)" /> + <linearGradient + id="linearGradient3214"> + <stop + id="stop3216" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3218" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="11" + y1="6" + x2="11" + y2="17" + id="linearGradient2442" + xlink:href="#linearGradient3587-6-5-86" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,5.5842706)" /> + <linearGradient + id="linearGradient3221"> + <stop + id="stop3223" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3225" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="1.3333321" + y1="4.9755898" + x2="1.3333321" + y2="37.373981" + id="linearGradient2422-1-0" + xlink:href="#linearGradient3587-6-5-0" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.39871888,0,0,0.3091132,71.812715,15.470662)" /> + <linearGradient + id="linearGradient3587-6-5-0"> + <stop + id="stop3589-9-2-5" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3591-7-4-1" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="46.395508" + y1="12.707516" + x2="46.395508" + y2="38.409042" + id="linearGradient3795-2" + xlink:href="#linearGradient3587-6-5-3-5-7" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.4100229,0,0,0.5447147,28.02322,-5.9219706)" /> + <linearGradient + id="linearGradient3587-6-5-3-5-7"> + <stop + id="stop3589-9-2-2-6-2" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3591-7-4-73-5-1" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient3587-6-5-3-5"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop3589-9-2-2-6" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop3591-7-4-73-5" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-5" + id="linearGradient4872" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.4100229,0,0,0.5447147,19.329265,-26.729116)" + x1="100.77747" + y1="17.859186" + x2="100.77747" + y2="38.055252" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-8" + id="linearGradient4894" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)" + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-8" + id="linearGradient4900" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)" + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-8" + id="linearGradient4906" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)" + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-8" + id="linearGradient4912" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)" + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" /> + <linearGradient + id="linearGradient3587-6-5-8-6"> + <stop + id="stop3589-9-2-67-3" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3591-7-4-2-3" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient4935"> + <stop + id="stop4937" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop4939" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient4942"> + <stop + id="stop4944" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop4946" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-8-6" + id="linearGradient4912-4" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)" + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" /> + <linearGradient + id="linearGradient4949"> + <stop + id="stop4951" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop4953" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-8" + id="linearGradient5012" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)" + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-8" + id="linearGradient5015" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)" + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-8" + id="linearGradient5018" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)" + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-8" + id="linearGradient5021" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)" + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-2-4" + id="linearGradient3335" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.21864454,0,0,0.26685422,18.618755,-19.597758)" + x1="-41.553459" + y1="2.2401412" + x2="-41.553459" + y2="54.703121" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4136" + id="linearGradient4134" + x1="9" + y1="0" + x2="9" + y2="15" + gradientUnits="userSpaceOnUse" + spreadMethod="pad" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4136" + id="linearGradient4150" + gradientUnits="userSpaceOnUse" + spreadMethod="pad" + x1="9" + y1="0" + x2="9" + y2="15" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-2-4-6" + id="linearGradient3335-7" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.2186487,0,0,0.26685422,76.619476,1.402242)" + x1="-41.553459" + y1="2.2401412" + x2="-41.755585" + y2="47.208389" /> + <linearGradient + id="linearGradient3587-6-5-2-4-6"> + <stop + id="stop3589-9-2-8-7-8" + style="stop-color:#000000;stop-opacity:1;" + offset="0" /> + <stop + id="stop3591-7-4-0-3-4" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-2-4-6-0" + id="linearGradient3335-7-8" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)" + x1="-41.553459" + y1="2.2401412" + x2="-41.553459" + y2="54.703121" /> + <linearGradient + id="linearGradient3587-6-5-2-4-6-0"> + <stop + id="stop3589-9-2-8-7-8-7" + style="stop-color:#000000;stop-opacity:1;" + offset="0" /> + <stop + id="stop3591-7-4-0-3-4-7" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-2-4-6-4" + id="linearGradient3335-7-7" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)" + x1="-41.553459" + y1="2.2401412" + x2="-41.553459" + y2="54.703121" /> + <linearGradient + id="linearGradient3587-6-5-2-4-6-4"> + <stop + id="stop3589-9-2-8-7-8-2" + style="stop-color:#000000;stop-opacity:1;" + offset="0" /> + <stop + id="stop3591-7-4-0-3-4-2" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-2-4-6-7" + id="linearGradient3335-7-3" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,1.402242)" + x1="-41.553459" + y1="2.2401412" + x2="-41.755585" + y2="47.208389" /> + <linearGradient + id="linearGradient3587-6-5-2-4-6-7"> + <stop + id="stop3589-9-2-8-7-8-4" + style="stop-color:#000000;stop-opacity:1;" + offset="0" /> + <stop + id="stop3591-7-4-0-3-4-5" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-2-4-6-2" + id="linearGradient3335-7-1" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)" + x1="-39.421574" + y1="-5.2547116" + x2="-39.421574" + y2="47.208389" /> + <linearGradient + id="linearGradient3587-6-5-2-4-6-2"> + <stop + id="stop3589-9-2-8-7-8-77" + style="stop-color:#000000;stop-opacity:1;" + offset="0" /> + <stop + id="stop3591-7-4-0-3-4-9" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4136-9" + id="linearGradient4150-0" + gradientUnits="userSpaceOnUse" + spreadMethod="pad" + x1="9" + y1="0" + x2="9" + y2="15" /> + <linearGradient + id="linearGradient4136-9"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1;" + id="stop4138-6" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop4140-3" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4136-9" + id="linearGradient3457" + gradientUnits="userSpaceOnUse" + spreadMethod="pad" + x1="9" + y1="0" + x2="9" + y2="15" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-2-4-6-2-6" + id="linearGradient3335-7-1-7" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)" + x1="-39.421574" + y1="-5.2547116" + x2="-39.421574" + y2="47.208389" /> + <linearGradient + id="linearGradient3587-6-5-2-4-6-2-6"> + <stop + id="stop3589-9-2-8-7-8-77-4" + style="stop-color:#000000;stop-opacity:1;" + offset="0" /> + <stop + id="stop3591-7-4-0-3-4-9-3" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <filter + inkscape:collect="always" + id="filter4061"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.09528843" + id="feGaussianBlur4063" /> + </filter> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3785" + id="linearGradient4079" + x1="2.9539645" + y1="7.9553804" + x2="8.1727142" + y2="7.9553804" + gradientUnits="userSpaceOnUse" /> + </defs> + <g + transform="matrix(0.78786264,0,0,0.78786264,-3.1483699,0.44173984)" + id="g3743-3" + style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" /> + <rect + style="color:#000000;fill:#ccc000;fill-opacity:0;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect3136" + width="163.31035" + height="97.986206" + x="-62.896553" + y="-32.993103" /> + <g + transform="matrix(0.99998873,0,0,0.99998873,-3.996044,-20.001608)" + id="g3743-9-4" + style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" /> + <g + id="g4146"> + <path + sodipodi:type="star" + style="fill:#999999;fill-opacity:1;filter:url(#filter4061)" + id="path3296" + sodipodi:sides="3" + sodipodi:cx="9" + sodipodi:cy="8" + sodipodi:r1="4.6360717" + sodipodi:r2="2.3180358" + sodipodi:arg1="1.0407812" + sodipodi:arg2="2.0879787" + inkscape:flatsided="false" + inkscape:rounded="0" + inkscape:randomized="0" + d="M 11.343749,12 7.8538866,10.014873 4.3640238,8.0297465 7.8281252,6.0000001 11.292227,3.9702536 l 0.02576,4.0148731 z" + inkscape:transform-center-x="1.4833295" + inkscape:transform-center-y="-0.018522961" + transform="matrix(1.2942258,0,0,1.2453693,-0.64803223,-1.9444316)" + inkscape:export-filename="/Volumes/www/owncloud/owncloud/apps/impress/img/rect3288.png" + inkscape:export-xdpi="120.00002" + inkscape:export-ydpi="120.00002" /> + <rect + style="fill:url(#linearGradient4079);fill-opacity:1.0" + id="rect3288" + width="5.21875" + height="11.999999" + x="2.9539645" + y="1.955381" + inkscape:export-xdpi="120.00002" + inkscape:export-ydpi="120.00002" /> + </g> +</svg> diff --git a/apps/impress/img/impressbig.png b/apps/impress/img/impressbig.png Binary files differnew file mode 100644 index 00000000000..3c8d1632b35 --- /dev/null +++ b/apps/impress/img/impressbig.png diff --git a/apps/impress/img/impressbig.svg b/apps/impress/img/impressbig.svg new file mode 100755 index 00000000000..a4140e4ae15 --- /dev/null +++ b/apps/impress/img/impressbig.svg @@ -0,0 +1,1730 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.0" + width="16" + height="16" + id="svg11300" + inkscape:version="0.48.2 r9819" + sodipodi:docname="impressbig.svg" + inkscape:export-filename="/Users/karlitschek/Desktop/impress.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + <metadata + id="metadata26"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <sodipodi:namedview + pagecolor="#cccccc" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1646" + inkscape:window-height="1184" + id="namedview24" + showgrid="true" + showguides="true" + inkscape:guide-bbox="true" + inkscape:zoom="45.254835" + inkscape:cx="8.0044836" + inkscape:cy="9.2830031" + inkscape:window-x="1497" + inkscape:window-y="26" + inkscape:window-maximized="0" + inkscape:current-layer="g4146"> + <inkscape:grid + type="xygrid" + id="grid4330" + empspacing="5" + dotted="true" + visible="true" + enabled="true" + snapvisiblegridlinesonly="true" /> + </sodipodi:namedview> + <defs + id="defs3"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 8 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="16 : 8 : 1" + inkscape:persp3d-origin="8 : 5.3333333 : 1" + id="perspective4600" /> + <linearGradient + id="linearGradient4136"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1;" + id="stop4138" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop4140" /> + </linearGradient> + <linearGradient + id="linearGradient4303"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop4305" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop4307" /> + </linearGradient> + <linearGradient + id="linearGradient4297"> + <stop + id="stop4299" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop4301" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient4115"> + <stop + id="stop4117" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop4119" + style="stop-color:#363636;stop-opacity:0.698" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient3785"> + <stop + id="stop3787" + style="stop-color:#505050;stop-opacity:1;" + offset="0" /> + <stop + id="stop3789" + style="stop-color:#878787;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient6954"> + <stop + id="stop6960" + style="stop-color:#f5f5f5;stop-opacity:1" + offset="0" /> + <stop + id="stop6962" + style="stop-color:#d2d2d2;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient3341"> + <stop + id="stop3343" + style="stop-color:white;stop-opacity:1" + offset="0" /> + <stop + id="stop3345" + style="stop-color:white;stop-opacity:0" + offset="1" /> + </linearGradient> + <radialGradient + cx="24.999998" + cy="28.659998" + r="16" + fx="24.999998" + fy="28.659998" + id="radialGradient2856" + xlink:href="#linearGradient6954" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.56186795,0,0,0.15787922,-6.1682604,5.3385209)" /> + <linearGradient + x1="30" + y1="25.084745" + x2="30" + y2="45" + id="linearGradient2858" + xlink:href="#linearGradient3785" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" /> + <radialGradient + cx="26.375898" + cy="12.31301" + r="8" + fx="26.375898" + fy="12.31301" + id="radialGradient2860" + xlink:href="#linearGradient6954" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.55250164,-0.0426402,0.04315608,0.50971914,-6.3026675,-1.9765067)" /> + <linearGradient + x1="30" + y1="5" + x2="30" + y2="44.678879" + id="linearGradient2862" + xlink:href="#linearGradient3785" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" /> + <linearGradient + x1="30" + y1="0.91818392" + x2="30" + y2="25.792814" + id="linearGradient2864" + xlink:href="#linearGradient3341" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" /> + <linearGradient + x1="29.955881" + y1="21.86607" + x2="29.955881" + y2="43.144382" + id="linearGradient2866" + xlink:href="#linearGradient3341" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1" + id="linearGradient7308" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.54681372,0,0,0.39376081,3.7325729,-0.29182867)" + x1="34.992828" + y1="0.94087797" + x2="34.992828" + y2="33.955856" /> + <linearGradient + id="linearGradient3587-6-5-3-4-5-4-0-1"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop3589-9-2-2-3-2-53-4-3" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop3591-7-4-73-7-9-86-9-3" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1" + id="linearGradient3796" + x1="8.3635759" + y1="15.028702" + x2="15.937561" + y2="11.00073" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1" + id="linearGradient3798" + x1="6.9951797" + y1="4.7478018" + x2="13.00482" + y2="4.7478018" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1" + id="linearGradient3815" + x1="209.34245" + y1="998.45801" + x2="209.34245" + y2="1013.451" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5" + id="linearGradient3815-3" + x1="209.34245" + y1="998.45801" + x2="209.34245" + y2="1013.451" + gradientUnits="userSpaceOnUse" /> + <linearGradient + id="linearGradient3587-6-5-3-4-5-4-0-1-5"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop3589-9-2-2-3-2-53-4-3-9" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop3591-7-4-73-7-9-86-9-3-0" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5" + id="linearGradient3831" + x1="209.34245" + y1="998.45801" + x2="209.34245" + y2="1013.451" + gradientUnits="userSpaceOnUse" /> + <linearGradient + id="linearGradient3833"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop3835" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop3837" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1" + id="linearGradient3874" + gradientUnits="userSpaceOnUse" + x1="209.34245" + y1="998.45801" + x2="209.34245" + y2="1013.451" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2" + id="linearGradient3892-2" + x1="8.7094374" + y1="1.0035814" + x2="8.6826077" + y2="16.052532" + gradientUnits="userSpaceOnUse" /> + <linearGradient + id="linearGradient3587-6-5-3-4-5-4-0-1-2"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop3589-9-2-2-3-2-53-4-3-1" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop3591-7-4-73-7-9-86-9-3-4" /> + </linearGradient> + <linearGradient + gradientTransform="matrix(0.96967712,0,0,0.96967712,0.26437941,-0.96950812)" + y2="16.052532" + x2="8.6826077" + y1="1.0035814" + x1="8.7094374" + gradientUnits="userSpaceOnUse" + id="linearGradient3909" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2" + inkscape:collect="always" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1" + id="linearGradient3984" + gradientUnits="userSpaceOnUse" + x1="209.34245" + y1="998.45801" + x2="209.34245" + y2="1013.451" /> + <linearGradient + id="linearGradient3587-6-5-3-4-5-4-0-1-2-1"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop3589-9-2-2-3-2-53-4-3-1-4" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop3591-7-4-73-7-9-86-9-3-4-6" /> + </linearGradient> + <linearGradient + gradientTransform="matrix(0.78786264,0,0,0.78786264,-1.5726929,-0.7389112)" + y2="16.052532" + x2="8.6826077" + y1="1.0035814" + x1="8.7094374" + gradientUnits="userSpaceOnUse" + id="linearGradient3909-3" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-2" + inkscape:collect="always" /> + <linearGradient + id="linearGradient3587-6-5-3-4-5-4-0-1-2-2"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop3589-9-2-2-3-2-53-4-3-1-7" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop3591-7-4-73-7-9-86-9-3-4-5" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4115-9" + id="linearGradient4113-3" + x1="0.86849999" + y1="13.895414" + x2="0.44923753" + y2="28.776533" + gradientUnits="userSpaceOnUse" /> + <linearGradient + id="linearGradient4115-9"> + <stop + id="stop4117-5" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop4119-6" + style="stop-color:#363636;stop-opacity:0.698" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3104" + id="linearGradient3815-4" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,0.35950872)" + x1="-51.786404" + y1="50.786446" + x2="-51.786404" + y2="2.9062471" /> + <linearGradient + id="linearGradient3104"> + <stop + id="stop3106" + style="stop-color:#aaaaaa;stop-opacity:1" + offset="0" /> + <stop + id="stop3108" + style="stop-color:#c8c8c8;stop-opacity:1" + offset="1" /> + </linearGradient> + <radialGradient + cx="13.138569" + cy="25.625349" + r="13.931416" + fx="13.138569" + fy="25.625349" + id="radialGradient2965" + xlink:href="#linearGradient3690-451" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0,0.92614711,-1.0546317,0,32.402583,-9.3345932)" /> + <linearGradient + id="linearGradient3690-451"> + <stop + id="stop2857" + style="stop-color:#e8e8e8;stop-opacity:1" + offset="0" /> + <stop + id="stop2859" + style="stop-color:#d8d8d8;stop-opacity:1" + offset="0.26238" /> + <stop + id="stop2861" + style="stop-color:#c2c2c2;stop-opacity:1" + offset="0.66093999" /> + <stop + id="stop2863" + style="stop-color:#a5a5a5;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="21.483376" + y1="36.255058" + x2="21.483376" + y2="9.5799999" + id="linearGradient2967" + xlink:href="#linearGradient3603-84" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762279)" /> + <linearGradient + id="linearGradient3603-84"> + <stop + id="stop2867" + style="stop-color:#707070;stop-opacity:1" + offset="0" /> + <stop + id="stop2869" + style="stop-color:#9e9e9e;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="11.566265" + y1="22.292103" + x2="15.214532" + y2="33.95525" + id="linearGradient3674-262" + xlink:href="#linearGradient8265-821-176-38-919-66-249-529" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4893617,0,0,0.4893617,1.7131795,22.728095)" /> + <linearGradient + id="linearGradient8265-821-176-38-919-66-249-529"> + <stop + id="stop2873" + style="stop-color:#ffffff;stop-opacity:0.27450982" + offset="0" /> + <stop + id="stop2875" + style="stop-color:#ffffff;stop-opacity:0" + offset="1" /> + </linearGradient> + <linearGradient + x1="24.046366" + y1="11.673002" + x2="24.046366" + y2="34.713669" + id="linearGradient3677-116" + xlink:href="#linearGradient3642-81" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)" /> + <linearGradient + id="linearGradient3642-81"> + <stop + id="stop2879" + style="stop-color:#ffffff;stop-opacity:1" + offset="0" /> + <stop + id="stop2881" + style="stop-color:#ffffff;stop-opacity:0" + offset="1" /> + </linearGradient> + <linearGradient + y2="34.713669" + x2="24.046366" + y1="11.673002" + x1="24.046366" + gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)" + gradientUnits="userSpaceOnUse" + id="linearGradient3037" + xlink:href="#linearGradient3642-81" + inkscape:collect="always" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3155-40" + id="linearGradient8639" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.415777,-0.4174938,0.518983,0.5146192,-15.747227,2.6503673)" + spreadMethod="pad" + x1="23.575972" + y1="25.356892" + x2="23.575972" + y2="31.210939" /> + <linearGradient + id="linearGradient3155-40"> + <stop + id="stop2541" + offset="0" + style="stop-color:#181818;stop-opacity:1;" /> + <stop + style="stop-color:#dbdbdb;stop-opacity:1;" + offset="0.13482948" + id="stop2543" /> + <stop + id="stop2545" + offset="0.20224422" + style="stop-color:#a4a4a4;stop-opacity:1;" /> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0.26965895" + id="stop2547" /> + <stop + id="stop2549" + offset="0.44650277" + style="stop-color:#8d8d8d;stop-opacity:1;" /> + <stop + style="stop-color:#959595;stop-opacity:1;" + offset="0.57114136" + id="stop2551" /> + <stop + id="stop2553" + offset="0.72038066" + style="stop-color:#cecece;stop-opacity:1;" /> + <stop + id="stop2555" + offset="1" + style="stop-color:#181818;stop-opacity:1;" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3240-279" + id="linearGradient8641" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.867764,0.6930272)" + x1="30.037716" + y1="24.989594" + x2="30.037716" + y2="30.000141" /> + <linearGradient + id="linearGradient3240-279"> + <stop + style="stop-color:#565656;stop-opacity:1;" + offset="0" + id="stop2559" /> + <stop + id="stop2561" + offset="0.5" + style="stop-color:#9a9a9a;stop-opacity:1;" /> + <stop + style="stop-color:#545454;stop-opacity:1;" + offset="1" + id="stop2563" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3223-789" + id="linearGradient8643" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.983472,0.8092126)" + x1="30.037716" + y1="24.989594" + x2="30.037716" + y2="30.000141" /> + <linearGradient + id="linearGradient3223-789"> + <stop + id="stop2567" + offset="0" + style="stop-color:#b1b1b1;stop-opacity:1;" /> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0.5" + id="stop2569" /> + <stop + id="stop2571" + offset="1" + style="stop-color:#8f8f8f;stop-opacity:1;" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3240-686" + id="linearGradient8645" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.465684,0.2892868)" + x1="30.037716" + y1="24.989594" + x2="30.037716" + y2="30.000141" /> + <linearGradient + id="linearGradient3240-686"> + <stop + style="stop-color:#565656;stop-opacity:1;" + offset="0" + id="stop2575" /> + <stop + id="stop2577" + offset="0.5" + style="stop-color:#9a9a9a;stop-opacity:1;" /> + <stop + style="stop-color:#545454;stop-opacity:1;" + offset="1" + id="stop2579" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3223-768" + id="linearGradient8647" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.581392,0.4054707)" + x1="30.037716" + y1="24.989594" + x2="30.037716" + y2="30.000141" /> + <linearGradient + id="linearGradient3223-768"> + <stop + id="stop2583" + offset="0" + style="stop-color:#b1b1b1;stop-opacity:1;" /> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0.5" + id="stop2585" /> + <stop + id="stop2587" + offset="1" + style="stop-color:#8f8f8f;stop-opacity:1;" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3240-907" + id="linearGradient8649" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.061661,-0.1164056)" + x1="30.037716" + y1="24.989594" + x2="30.037716" + y2="30.000141" /> + <linearGradient + id="linearGradient3240-907"> + <stop + style="stop-color:#565656;stop-opacity:1;" + offset="0" + id="stop2591" /> + <stop + id="stop2593" + offset="0.5" + style="stop-color:#9a9a9a;stop-opacity:1;" /> + <stop + style="stop-color:#545454;stop-opacity:1;" + offset="1" + id="stop2595" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3223-699" + id="linearGradient8651" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.177369,-2.1969969e-4)" + x1="30.037716" + y1="24.989594" + x2="30.037716" + y2="30.000141" /> + <linearGradient + id="linearGradient3223-699"> + <stop + id="stop2599" + offset="0" + style="stop-color:#b1b1b1;stop-opacity:1;" /> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0.5" + id="stop2601" /> + <stop + id="stop2603" + offset="1" + style="stop-color:#8f8f8f;stop-opacity:1;" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3290-678" + id="linearGradient8653" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.602268,-17.636692,0.462492)" + x1="9" + y1="29.056757" + x2="9" + y2="26.02973" /> + <linearGradient + id="linearGradient3290-678"> + <stop + id="stop2607" + offset="0" + style="stop-color:#ece5a5;stop-opacity:1;" /> + <stop + id="stop2609" + offset="1" + style="stop-color:#fcfbf2;stop-opacity:1;" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3191-577" + id="linearGradient8655" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.3763801,0.03615261,0.03669995,0.374874,-2.2182805,-1.1331002)" + x1="5.5178981" + y1="37.371799" + x2="9.5220556" + y2="41.391716" /> + <linearGradient + id="linearGradient3191-577"> + <stop + id="stop2613" + offset="0" + style="stop-color:#dbce48;stop-opacity:1;" /> + <stop + id="stop2615" + offset="1" + style="stop-color:#c5b625;stop-opacity:1;" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0" + id="linearGradient3934-0" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)" + x1="8.7094374" + y1="1.0035814" + x2="8.6826077" + y2="16.052532" /> + <linearGradient + id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop3589-9-2-2-3-2-53-4-3-1-4-9" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop3591-7-4-73-7-9-86-9-3-4-6-0" /> + </linearGradient> + <linearGradient + y2="16.052532" + x2="8.6826077" + y1="1.0035814" + x1="8.7094374" + gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)" + gradientUnits="userSpaceOnUse" + id="linearGradient4154-8" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9" + inkscape:collect="always" /> + <linearGradient + id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4" /> + </linearGradient> + <linearGradient + id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9-0"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8-3" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4-8" /> + </linearGradient> + <linearGradient + id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-3"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop3589-9-2-2-3-2-53-4-3-1-4-9-3" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop3591-7-4-73-7-9-86-9-3-4-6-0-6" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5" + id="linearGradient4326" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)" + x1="14.501121" + y1="-1.4095211" + x2="14.152531" + y2="20.074369" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5" + id="linearGradient4328" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)" + x1="8.7094374" + y1="1.0035814" + x2="8.6826077" + y2="16.052532" /> + <linearGradient + x1="-2.4040222" + y1="4.4573336" + x2="-2.4040222" + y2="18.967093" + id="linearGradient3878" + xlink:href="#linearGradient3587-6-5" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(13.927091,-3.4266134)" /> + <linearGradient + id="linearGradient3587-6-5"> + <stop + id="stop3589-9-2" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3591-7-4" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5" + id="linearGradient4357" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.0344828,0,0,1.0344828,8.0707628,-14.513825)" + x1="0.86849999" + y1="13.895414" + x2="0.44923753" + y2="28.776533" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1" + id="linearGradient4405" + gradientUnits="userSpaceOnUse" + x1="209.34245" + y1="998.45801" + x2="209.34245" + y2="1013.451" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55" + id="linearGradient4413-7" + gradientUnits="userSpaceOnUse" + x1="209.34245" + y1="998.45801" + x2="209.34245" + y2="1013.451" /> + <linearGradient + id="linearGradient3587-6-5-3-4-5-4-0-1-55"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop3589-9-2-2-3-2-53-4-3-95" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop3591-7-4-73-7-9-86-9-3-6" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-2" + id="linearGradient4411-3" + gradientUnits="userSpaceOnUse" + x1="209.34245" + y1="998.45801" + x2="209.34245" + y2="1013.451" /> + <linearGradient + id="linearGradient3587-6-5-2"> + <stop + id="stop3589-9-2-8" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3591-7-4-0" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-2-4" + id="linearGradient4466-9" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,60.359508)" + x1="-41.553459" + y1="2.2401412" + x2="-41.553459" + y2="54.703121" /> + <linearGradient + id="linearGradient3587-6-5-2-4"> + <stop + id="stop3589-9-2-8-7" + style="stop-color:#000000;stop-opacity:1;" + offset="0" /> + <stop + id="stop3591-7-4-0-3" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + y2="54.703121" + x2="-41.553459" + y1="2.2401412" + x1="-41.553459" + gradientTransform="matrix(0.21864454,0,0,0.26685422,17.618755,60.402242)" + gradientUnits="userSpaceOnUse" + id="linearGradient4483-3" + xlink:href="#linearGradient3587-6-5-2-4-9" + inkscape:collect="always" /> + <linearGradient + id="linearGradient3587-6-5-2-4-9"> + <stop + id="stop3589-9-2-8-7-2" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3591-7-4-0-3-8" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55" + id="linearGradient4564" + gradientUnits="userSpaceOnUse" + x1="209.34245" + y1="998.45801" + x2="209.34245" + y2="1013.451" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5" + id="linearGradient4566" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(13.927091,16.573387)" + x1="-2.4040222" + y1="4.4573336" + x2="-2.4040222" + y2="18.967093" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-2" + id="linearGradient4578" + gradientUnits="userSpaceOnUse" + x1="209.34245" + y1="998.45801" + x2="209.34245" + y2="1013.451" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55" + id="linearGradient4580" + gradientUnits="userSpaceOnUse" + x1="209.34245" + y1="998.45801" + x2="209.34245" + y2="1013.451" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3" + id="linearGradient4359-4" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.99998838,0,0,0.99998838,29.038238,-21.358617)" + x1="8.7094374" + y1="1.0035814" + x2="8.6826077" + y2="16.052532" /> + <linearGradient + id="linearGradient3587-6-5-3"> + <stop + id="stop3589-9-2-6" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3591-7-4-5" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3" + id="linearGradient4361-4" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)" + x1="8.7094374" + y1="1.0035814" + x2="8.6826077" + y2="16.052532" /> + <linearGradient + id="linearGradient4597"> + <stop + id="stop4599" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop4601" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + y2="16.052532" + x2="8.6826077" + y1="1.0035814" + x1="8.7094374" + gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)" + gradientUnits="userSpaceOnUse" + id="linearGradient4610" + xlink:href="#linearGradient3587-6-5-3" + inkscape:collect="always" /> + <linearGradient + x1="1.3333321" + y1="6.6666665" + x2="1.3333321" + y2="33.333332" + id="linearGradient2422" + xlink:href="#linearGradient3587-6-5-5" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4090909,0,0,0.375,7.4545459,0.5)" /> + <linearGradient + id="linearGradient3587-6-5-5"> + <stop + id="stop3589-9-2-4" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3591-7-4-3" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" + id="linearGradient3189" + xlink:href="#linearGradient3587-6-5-8" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)" /> + <linearGradient + id="linearGradient3587-6-5-8"> + <stop + id="stop3589-9-2-67" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3591-7-4-2" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" + id="linearGradient3203" + xlink:href="#linearGradient3587-6-5-8" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)" /> + <linearGradient + id="linearGradient3120"> + <stop + id="stop3122" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3124" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" + id="linearGradient3207" + xlink:href="#linearGradient3587-6-5-8" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)" /> + <linearGradient + id="linearGradient3127"> + <stop + id="stop3129" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3131" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" + id="linearGradient3211" + xlink:href="#linearGradient3587-6-5-8" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)" /> + <linearGradient + id="linearGradient3134"> + <stop + id="stop3136" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3138" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="11" + y1="6" + x2="11" + y2="17" + id="linearGradient2409" + xlink:href="#linearGradient3587-6-5-1" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.2403101,0,0,0.8988764,10.387597,0.2247191)" /> + <linearGradient + id="linearGradient3587-6-5-1"> + <stop + id="stop3589-9-2-0" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3591-7-4-21" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="40.805084" + y1="5.6271191" + x2="40.805084" + y2="17.627119" + id="linearGradient3206" + xlink:href="#linearGradient3587-8-5" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-32.805085,-3.6271193)" /> + <linearGradient + id="linearGradient3587-8-5"> + <stop + id="stop3589-2-7" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3591-3-5" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + y2="17.627119" + x2="40.805084" + y1="5.6271191" + x1="40.805084" + gradientTransform="translate(-32.805085,-3.6271193)" + gradientUnits="userSpaceOnUse" + id="linearGradient3180" + xlink:href="#linearGradient3587-8-5" + inkscape:collect="always" /> + <linearGradient + x1="1.3333321" + y1="6.6666665" + x2="1.3333321" + y2="33.333332" + id="linearGradient2422-1" + xlink:href="#linearGradient3587-6-5-86" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.2727273,0,0,0.375,9.636365,1.5)" /> + <linearGradient + id="linearGradient3587-6-5-86"> + <stop + id="stop3589-9-2-65" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3591-7-4-9" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="11" + y1="6" + x2="11" + y2="17" + id="linearGradient2427" + xlink:href="#linearGradient3587-6-5-86" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,1.5842703)" /> + <linearGradient + id="linearGradient3207-3"> + <stop + id="stop3209" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3211" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="11" + y1="6" + x2="11" + y2="17" + id="linearGradient2436" + xlink:href="#linearGradient3587-6-5-86" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,9.58427)" /> + <linearGradient + id="linearGradient3214"> + <stop + id="stop3216" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3218" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="11" + y1="6" + x2="11" + y2="17" + id="linearGradient2442" + xlink:href="#linearGradient3587-6-5-86" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,5.5842706)" /> + <linearGradient + id="linearGradient3221"> + <stop + id="stop3223" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3225" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="1.3333321" + y1="4.9755898" + x2="1.3333321" + y2="37.373981" + id="linearGradient2422-1-0" + xlink:href="#linearGradient3587-6-5-0" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.39871888,0,0,0.3091132,71.812715,15.470662)" /> + <linearGradient + id="linearGradient3587-6-5-0"> + <stop + id="stop3589-9-2-5" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3591-7-4-1" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="46.395508" + y1="12.707516" + x2="46.395508" + y2="38.409042" + id="linearGradient3795-2" + xlink:href="#linearGradient3587-6-5-3-5-7" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.4100229,0,0,0.5447147,28.02322,-5.9219706)" /> + <linearGradient + id="linearGradient3587-6-5-3-5-7"> + <stop + id="stop3589-9-2-2-6-2" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3591-7-4-73-5-1" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient3587-6-5-3-5"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1" + id="stop3589-9-2-2-6" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop3591-7-4-73-5" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-3-5" + id="linearGradient4872" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.4100229,0,0,0.5447147,19.329265,-26.729116)" + x1="100.77747" + y1="17.859186" + x2="100.77747" + y2="38.055252" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-8" + id="linearGradient4894" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)" + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-8" + id="linearGradient4900" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)" + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-8" + id="linearGradient4906" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)" + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-8" + id="linearGradient4912" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)" + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" /> + <linearGradient + id="linearGradient3587-6-5-8-6"> + <stop + id="stop3589-9-2-67-3" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop3591-7-4-2-3" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient4935"> + <stop + id="stop4937" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop4939" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient4942"> + <stop + id="stop4944" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop4946" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-8-6" + id="linearGradient4912-4" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)" + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" /> + <linearGradient + id="linearGradient4949"> + <stop + id="stop4951" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop4953" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-8" + id="linearGradient5012" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)" + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-8" + id="linearGradient5015" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)" + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-8" + id="linearGradient5018" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)" + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-8" + id="linearGradient5021" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)" + x1="26.045763" + y1="9.6223383" + x2="26.045763" + y2="19.490837" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-2-4" + id="linearGradient3335" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.21864454,0,0,0.26685422,18.618755,-19.597758)" + x1="-41.553459" + y1="2.2401412" + x2="-41.553459" + y2="54.703121" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4136" + id="linearGradient4134" + x1="9" + y1="0" + x2="9" + y2="15" + gradientUnits="userSpaceOnUse" + spreadMethod="pad" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4136" + id="linearGradient4150" + gradientUnits="userSpaceOnUse" + spreadMethod="pad" + x1="9" + y1="0" + x2="9" + y2="15" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-2-4-6" + id="linearGradient3335-7" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.2186487,0,0,0.26685422,76.619476,1.402242)" + x1="-41.553459" + y1="2.2401412" + x2="-41.755585" + y2="47.208389" /> + <linearGradient + id="linearGradient3587-6-5-2-4-6"> + <stop + id="stop3589-9-2-8-7-8" + style="stop-color:#000000;stop-opacity:1;" + offset="0" /> + <stop + id="stop3591-7-4-0-3-4" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-2-4-6-0" + id="linearGradient3335-7-8" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)" + x1="-41.553459" + y1="2.2401412" + x2="-41.553459" + y2="54.703121" /> + <linearGradient + id="linearGradient3587-6-5-2-4-6-0"> + <stop + id="stop3589-9-2-8-7-8-7" + style="stop-color:#000000;stop-opacity:1;" + offset="0" /> + <stop + id="stop3591-7-4-0-3-4-7" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-2-4-6-4" + id="linearGradient3335-7-7" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)" + x1="-41.553459" + y1="2.2401412" + x2="-41.553459" + y2="54.703121" /> + <linearGradient + id="linearGradient3587-6-5-2-4-6-4"> + <stop + id="stop3589-9-2-8-7-8-2" + style="stop-color:#000000;stop-opacity:1;" + offset="0" /> + <stop + id="stop3591-7-4-0-3-4-2" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-2-4-6-7" + id="linearGradient3335-7-3" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,1.402242)" + x1="-41.553459" + y1="2.2401412" + x2="-41.755585" + y2="47.208389" /> + <linearGradient + id="linearGradient3587-6-5-2-4-6-7"> + <stop + id="stop3589-9-2-8-7-8-4" + style="stop-color:#000000;stop-opacity:1;" + offset="0" /> + <stop + id="stop3591-7-4-0-3-4-5" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-2-4-6-2" + id="linearGradient3335-7-1" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)" + x1="-39.421574" + y1="-5.2547116" + x2="-39.421574" + y2="47.208389" /> + <linearGradient + id="linearGradient3587-6-5-2-4-6-2"> + <stop + id="stop3589-9-2-8-7-8-77" + style="stop-color:#000000;stop-opacity:1;" + offset="0" /> + <stop + id="stop3591-7-4-0-3-4-9" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4136-9" + id="linearGradient4150-0" + gradientUnits="userSpaceOnUse" + spreadMethod="pad" + x1="9" + y1="0" + x2="9" + y2="15" /> + <linearGradient + id="linearGradient4136-9"> + <stop + offset="0" + style="stop-color:#000000;stop-opacity:1;" + id="stop4138-6" /> + <stop + offset="1" + style="stop-color:#363636;stop-opacity:1" + id="stop4140-3" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4136-9" + id="linearGradient3457" + gradientUnits="userSpaceOnUse" + spreadMethod="pad" + x1="9" + y1="0" + x2="9" + y2="15" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3587-6-5-2-4-6-2-6" + id="linearGradient3335-7-1-7" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)" + x1="-39.421574" + y1="-5.2547116" + x2="-39.421574" + y2="47.208389" /> + <linearGradient + id="linearGradient3587-6-5-2-4-6-2-6"> + <stop + id="stop3589-9-2-8-7-8-77-4" + style="stop-color:#000000;stop-opacity:1;" + offset="0" /> + <stop + id="stop3591-7-4-0-3-4-9-3" + style="stop-color:#363636;stop-opacity:1" + offset="1" /> + </linearGradient> + <filter + inkscape:collect="always" + id="filter4061"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.09528843" + id="feGaussianBlur4063" /> + </filter> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3785" + id="linearGradient4079" + x1="2.9539645" + y1="7.9553804" + x2="8.1727142" + y2="7.9553804" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3191-577" + id="linearGradient4626" + x1="-1.7639756" + y1="2.3130295" + x2="2.824636" + y2="2.3130295" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(0.70147562,-1.6260591)" /> + </defs> + <g + transform="matrix(0.78786264,0,0,0.78786264,-3.1483699,0.44173984)" + id="g3743-3" + style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" /> + <rect + style="color:#000000;fill:#ccc000;fill-opacity:0;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect3136" + width="163.31035" + height="97.986206" + x="-62.896553" + y="-32.993103" /> + <g + transform="matrix(0.99998873,0,0,0.99998873,-3.996044,-20.001608)" + id="g3743-9-4" + style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" /> + <g + id="g4146"> + <rect + style="fill:#b3b3b3;fill-opacity:1;stroke:#b1b1b1;stroke-width:2.22857141;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect4632" + width="12" + height="12" + x="2" + y="2" /> + <path + sodipodi:type="star" + style="fill:#4d4d4d;fill-opacity:1;stroke:#4e4e4e;stroke-width:0.84385167;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="path4634" + sodipodi:sides="3" + sodipodi:cx="6.3639607" + sodipodi:cy="-1.3683099" + sodipodi:r1="3.1380975" + sodipodi:r2="1.5690486" + sodipodi:arg1="-0.014083576" + sodipodi:arg2="1.033114" + inkscape:flatsided="false" + inkscape:rounded="0" + inkscape:randomized="0" + d="M 9.5017471,-1.412504 7.1675439,-0.02065715 4.8333409,1.3711899 4.7950677,-1.3462128 4.7567943,-4.0636154 7.1292707,-2.7380596 z" + inkscape:transform-center-x="-0.86652303" + inkscape:transform-center-y="0.022845587" + transform="matrix(1.1322508,0,0,1.0338718,0.24163929,9.4603481)" /> + </g> +</svg> diff --git a/apps/impress/index.php b/apps/impress/index.php new file mode 100755 index 00000000000..c7213d86614 --- /dev/null +++ b/apps/impress/index.php @@ -0,0 +1,37 @@ +<?php + +/** + * ownCloud - Impress App + * + * @author Frank Karlitschek + * @copyright 2011 Frank Karlitschek karlitschek@kde.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +require_once('lib/impress.php'); + +OCP\User::checkLoggedIn(); +OCP\App::setActiveNavigationEntry( 'impress_index' ); + + + +$list=\OCA_Impress\Storage::getPresentations(); + +$tmpl = new OCP\Template('impress', 'presentations', 'user'); +$tmpl->assign('list', $list); +$tmpl->printPage(); + + diff --git a/apps/impress/js/impress.js b/apps/impress/js/impress.js new file mode 100644 index 00000000000..224a5e2e7e6 --- /dev/null +++ b/apps/impress/js/impress.js @@ -0,0 +1,800 @@ +/** + * impress.js + * + * impress.js is a presentation tool based on the power of CSS3 transforms and transitions + * in modern browsers and inspired by the idea behind prezi.com. + * + * + * Copyright 2011-2012 Bartek Szopka (@bartaz) + * + * Released under the MIT and GPL Licenses. + * + * ------------------------------------------------ + * author: Bartek Szopka + * version: 0.5.3 + * url: http://bartaz.github.com/impress.js/ + * source: http://github.com/bartaz/impress.js/ + */ + +/*jshint bitwise:true, curly:true, eqeqeq:true, forin:true, latedef:true, newcap:true, + noarg:true, noempty:true, undef:true, strict:true, browser:true */ + +// You are one of those who like to know how thing work inside? +// Let me show you the cogs that make impress.js run... +(function ( document, window ) { + 'use strict'; + + // HELPER FUNCTIONS + + // `pfx` is a function that takes a standard CSS property name as a parameter + // and returns it's prefixed version valid for current browser it runs in. + // The code is heavily inspired by Modernizr http://www.modernizr.com/ + var pfx = (function () { + + var style = document.createElement('dummy').style, + prefixes = 'Webkit Moz O ms Khtml'.split(' '), + memory = {}; + + return function ( prop ) { + if ( typeof memory[ prop ] === "undefined" ) { + + var ucProp = prop.charAt(0).toUpperCase() + prop.substr(1), + props = (prop + ' ' + prefixes.join(ucProp + ' ') + ucProp).split(' '); + + memory[ prop ] = null; + for ( var i in props ) { + if ( style[ props[i] ] !== undefined ) { + memory[ prop ] = props[i]; + break; + } + } + + } + + return memory[ prop ]; + }; + + })(); + + // `arraify` takes an array-like object and turns it into real Array + // to make all the Array.prototype goodness available. + var arrayify = function ( a ) { + return [].slice.call( a ); + }; + + // `css` function applies the styles given in `props` object to the element + // given as `el`. It runs all property names through `pfx` function to make + // sure proper prefixed version of the property is used. + var css = function ( el, props ) { + var key, pkey; + for ( key in props ) { + if ( props.hasOwnProperty(key) ) { + pkey = pfx(key); + if ( pkey !== null ) { + el.style[pkey] = props[key]; + } + } + } + return el; + }; + + // `toNumber` takes a value given as `numeric` parameter and tries to turn + // it into a number. If it is not possible it returns 0 (or other value + // given as `fallback`). + var toNumber = function (numeric, fallback) { + return isNaN(numeric) ? (fallback || 0) : Number(numeric); + }; + + // `byId` returns element with given `id` - you probably have guessed that ;) + var byId = function ( id ) { + return document.getElementById(id); + }; + + // `$` returns first element for given CSS `selector` in the `context` of + // the given element or whole document. + var $ = function ( selector, context ) { + context = context || document; + return context.querySelector(selector); + }; + + // `$$` return an array of elements for given CSS `selector` in the `context` of + // the given element or whole document. + var $$ = function ( selector, context ) { + context = context || document; + return arrayify( context.querySelectorAll(selector) ); + }; + + // `triggerEvent` builds a custom DOM event with given `eventName` and `detail` data + // and triggers it on element given as `el`. + var triggerEvent = function (el, eventName, detail) { + var event = document.createEvent("CustomEvent"); + event.initCustomEvent(eventName, true, true, detail); + el.dispatchEvent(event); + }; + + // `translate` builds a translate transform string for given data. + var translate = function ( t ) { + return " translate3d(" + t.x + "px," + t.y + "px," + t.z + "px) "; + }; + + // `rotate` builds a rotate transform string for given data. + // By default the rotations are in X Y Z order that can be reverted by passing `true` + // as second parameter. + var rotate = function ( r, revert ) { + var rX = " rotateX(" + r.x + "deg) ", + rY = " rotateY(" + r.y + "deg) ", + rZ = " rotateZ(" + r.z + "deg) "; + + return revert ? rZ+rY+rX : rX+rY+rZ; + }; + + // `scale` builds a scale transform string for given data. + var scale = function ( s ) { + return " scale(" + s + ") "; + }; + + // `perspective` builds a perspective transform string for given data. + var perspective = function ( p ) { + return " perspective(" + p + "px) "; + }; + + // `getElementFromHash` returns an element located by id from hash part of + // window location. + var getElementFromHash = function () { + // get id from url # by removing `#` or `#/` from the beginning, + // so both "fallback" `#slide-id` and "enhanced" `#/slide-id` will work + return byId( window.location.hash.replace(/^#\/?/,"") ); + }; + + // `computeWindowScale` counts the scale factor between window size and size + // defined for the presentation in the config. + var computeWindowScale = function ( config ) { + var hScale = window.innerHeight / config.height, + wScale = window.innerWidth / config.width, + scale = hScale > wScale ? wScale : hScale; + + if (config.maxScale && scale > config.maxScale) { + scale = config.maxScale; + } + + if (config.minScale && scale < config.minScale) { + scale = config.minScale; + } + + return scale; + }; + + // CHECK SUPPORT + var body = document.body; + + var ua = navigator.userAgent.toLowerCase(); + var impressSupported = + // browser should support CSS 3D transtorms + ( pfx("perspective") !== null ) && + + // and `classList` and `dataset` APIs + ( body.classList ) && + ( body.dataset ) && + + // but some mobile devices need to be blacklisted, + // because their CSS 3D support or hardware is not + // good enough to run impress.js properly, sorry... + ( ua.search(/(iphone)|(ipod)|(android)/) === -1 ); + + if (!impressSupported) { + // we can't be sure that `classList` is supported + body.className += " impress-not-supported "; + } else { + body.classList.remove("impress-not-supported"); + body.classList.add("impress-supported"); + } + + // GLOBALS AND DEFAULTS + + // This is were the root elements of all impress.js instances will be kept. + // Yes, this means you can have more than one instance on a page, but I'm not + // sure if it makes any sense in practice ;) + var roots = {}; + + // some default config values. + var defaults = { + width: 1024, + height: 768, + maxScale: 1, + minScale: 0, + + perspective: 1000, + + transitionDuration: 1000 + }; + + // it's just an empty function ... and a useless comment. + var empty = function () { return false; }; + + // IMPRESS.JS API + + // And that's where interesting things will start to happen. + // It's the core `impress` function that returns the impress.js API + // for a presentation based on the element with given id ('impress' + // by default). + var impress = window.impress = function ( rootId ) { + + // If impress.js is not supported by the browser return a dummy API + // it may not be a perfect solution but we return early and avoid + // running code that may use features not implemented in the browser. + if (!impressSupported) { + return { + init: empty, + goto: empty, + prev: empty, + next: empty + }; + } + + rootId = rootId || "impress"; + + // if given root is already initialized just return the API + if (roots["impress-root-" + rootId]) { + return roots["impress-root-" + rootId]; + } + + // data of all presentation steps + var stepsData = {}; + + // element of currently active step + var activeStep = null; + + // current state (position, rotation and scale) of the presentation + var currentState = null; + + // array of step elements + var steps = null; + + // configuration options + var config = null; + + // scale factor of the browser window + var windowScale = null; + + // root presentation elements + var root = byId( rootId ); + var canvas = document.createElement("div"); + + var initialized = false; + + // STEP EVENTS + // + // There are currently two step events triggered by impress.js + // `impress:stepenter` is triggered when the step is shown on the + // screen (the transition from the previous one is finished) and + // `impress:stepleave` is triggered when the step is left (the + // transition to next step just starts). + + // reference to last entered step + var lastEntered = null; + + // `onStepEnter` is called whenever the step element is entered + // but the event is triggered only if the step is different than + // last entered step. + var onStepEnter = function (step) { + if (lastEntered !== step) { + triggerEvent(step, "impress:stepenter"); + lastEntered = step; + } + }; + + // `onStepLeave` is called whenever the step element is left + // but the event is triggered only if the step is the same as + // last entered step. + var onStepLeave = function (step) { + if (lastEntered === step) { + triggerEvent(step, "impress:stepleave"); + lastEntered = null; + } + }; + + // `initStep` initializes given step element by reading data from its + // data attributes and setting correct styles. + var initStep = function ( el, idx ) { + var data = el.dataset, + step = { + translate: { + x: toNumber(data.x), + y: toNumber(data.y), + z: toNumber(data.z) + }, + rotate: { + x: toNumber(data.rotateX), + y: toNumber(data.rotateY), + z: toNumber(data.rotateZ || data.rotate) + }, + scale: toNumber(data.scale, 1), + el: el + }; + + if ( !el.id ) { + el.id = "step-" + (idx + 1); + } + + stepsData["impress-" + el.id] = step; + + css(el, { + position: "absolute", + transform: "translate(-50%,-50%)" + + translate(step.translate) + + rotate(step.rotate) + + scale(step.scale), + transformStyle: "preserve-3d" + }); + }; + + // `init` API function that initializes (and runs) the presentation. + var init = function () { + if (initialized) { return; } + + // First we set up the viewport for mobile devices. + // For some reason iPad goes nuts when it is not done properly. + var meta = $("meta[name='viewport']") || document.createElement("meta"); + meta.content = "width=device-width, minimum-scale=1, maximum-scale=1, user-scalable=no"; + if (meta.parentNode !== document.head) { + meta.name = 'viewport'; + document.head.appendChild(meta); + } + + // initialize configuration object + var rootData = root.dataset; + config = { + width: toNumber( rootData.width, defaults.width ), + height: toNumber( rootData.height, defaults.height ), + maxScale: toNumber( rootData.maxScale, defaults.maxScale ), + minScale: toNumber( rootData.minScale, defaults.minScale ), + perspective: toNumber( rootData.perspective, defaults.perspective ), + transitionDuration: toNumber( rootData.transitionDuration, defaults.transitionDuration ) + }; + + windowScale = computeWindowScale( config ); + + // wrap steps with "canvas" element + arrayify( root.childNodes ).forEach(function ( el ) { + canvas.appendChild( el ); + }); + root.appendChild(canvas); + + // set initial styles + document.documentElement.style.height = "100%"; + + css(body, { + height: "100%", + overflow: "hidden" + }); + + var rootStyles = { + position: "absolute", + transformOrigin: "top left", + transition: "all 0s ease-in-out", + transformStyle: "preserve-3d" + }; + + css(root, rootStyles); + css(root, { + top: "50%", + left: "50%", + transform: perspective( config.perspective/windowScale ) + scale( windowScale ) + }); + css(canvas, rootStyles); + + body.classList.remove("impress-disabled"); + body.classList.add("impress-enabled"); + + // get and init steps + steps = $$(".step", root); + steps.forEach( initStep ); + + // set a default initial state of the canvas + currentState = { + translate: { x: 0, y: 0, z: 0 }, + rotate: { x: 0, y: 0, z: 0 }, + scale: 1 + }; + + initialized = true; + + triggerEvent(root, "impress:init", { api: roots[ "impress-root-" + rootId ] }); + }; + + // `getStep` is a helper function that returns a step element defined by parameter. + // If a number is given, step with index given by the number is returned, if a string + // is given step element with such id is returned, if DOM element is given it is returned + // if it is a correct step element. + var getStep = function ( step ) { + if (typeof step === "number") { + step = step < 0 ? steps[ steps.length + step] : steps[ step ]; + } else if (typeof step === "string") { + step = byId(step); + } + return (step && step.id && stepsData["impress-" + step.id]) ? step : null; + }; + + // used to reset timeout for `impress:stepenter` event + var stepEnterTimeout = null; + + // `goto` API function that moves to step given with `el` parameter (by index, id or element), + // with a transition `duration` optionally given as second parameter. + var goto = function ( el, duration ) { + + if ( !initialized || !(el = getStep(el)) ) { + // presentation not initialized or given element is not a step + return false; + } + + // Sometimes it's possible to trigger focus on first link with some keyboard action. + // Browser in such a case tries to scroll the page to make this element visible + // (even that body overflow is set to hidden) and it breaks our careful positioning. + // + // So, as a lousy (and lazy) workaround we will make the page scroll back to the top + // whenever slide is selected + // + // If you are reading this and know any better way to handle it, I'll be glad to hear about it! + window.scrollTo(0, 0); + + var step = stepsData["impress-" + el.id]; + + if ( activeStep ) { + activeStep.classList.remove("active"); + body.classList.remove("impress-on-" + activeStep.id); + } + el.classList.add("active"); + + body.classList.add("impress-on-" + el.id); + + // compute target state of the canvas based on given step + var target = { + rotate: { + x: -step.rotate.x, + y: -step.rotate.y, + z: -step.rotate.z + }, + translate: { + x: -step.translate.x, + y: -step.translate.y, + z: -step.translate.z + }, + scale: 1 / step.scale + }; + + // Check if the transition is zooming in or not. + // + // This information is used to alter the transition style: + // when we are zooming in - we start with move and rotate transition + // and the scaling is delayed, but when we are zooming out we start + // with scaling down and move and rotation are delayed. + var zoomin = target.scale >= currentState.scale; + + duration = toNumber(duration, config.transitionDuration); + var delay = (duration / 2); + + // if the same step is re-selected, force computing window scaling, + // because it is likely to be caused by window resize + if (el === activeStep) { + windowScale = computeWindowScale(config); + } + + var targetScale = target.scale * windowScale; + + // trigger leave of currently active element (if it's not the same step again) + if (activeStep && activeStep !== el) { + onStepLeave(activeStep); + } + + // Now we alter transforms of `root` and `canvas` to trigger transitions. + // + // And here is why there are two elements: `root` and `canvas` - they are + // being animated separately: + // `root` is used for scaling and `canvas` for translate and rotations. + // Transitions on them are triggered with different delays (to make + // visually nice and 'natural' looking transitions), so we need to know + // that both of them are finished. + css(root, { + // to keep the perspective look similar for different scales + // we need to 'scale' the perspective, too + transform: perspective( config.perspective / targetScale ) + scale( targetScale ), + transitionDuration: duration + "ms", + transitionDelay: (zoomin ? delay : 0) + "ms" + }); + + css(canvas, { + transform: rotate(target.rotate, true) + translate(target.translate), + transitionDuration: duration + "ms", + transitionDelay: (zoomin ? 0 : delay) + "ms" + }); + + // Here is a tricky part... + // + // If there is no change in scale or no change in rotation and translation, it means there was actually + // no delay - because there was no transition on `root` or `canvas` elements. + // We want to trigger `impress:stepenter` event in the correct moment, so here we compare the current + // and target values to check if delay should be taken into account. + // + // I know that this `if` statement looks scary, but it's pretty simple when you know what is going on + // - it's simply comparing all the values. + if ( currentState.scale === target.scale || + (currentState.rotate.x === target.rotate.x && currentState.rotate.y === target.rotate.y && + currentState.rotate.z === target.rotate.z && currentState.translate.x === target.translate.x && + currentState.translate.y === target.translate.y && currentState.translate.z === target.translate.z) ) { + delay = 0; + } + + // store current state + currentState = target; + activeStep = el; + + // And here is where we trigger `impress:stepenter` event. + // We simply set up a timeout to fire it taking transition duration (and possible delay) into account. + // + // I really wanted to make it in more elegant way. The `transitionend` event seemed to be the best way + // to do it, but the fact that I'm using transitions on two separate elements and that the `transitionend` + // event is only triggered when there was a transition (change in the values) caused some bugs and + // made the code really complicated, cause I had to handle all the conditions separately. And it still + // needed a `setTimeout` fallback for the situations when there is no transition at all. + // So I decided that I'd rather make the code simpler than use shiny new `transitionend`. + // + // If you want learn something interesting and see how it was done with `transitionend` go back to + // version 0.5.2 of impress.js: http://github.com/bartaz/impress.js/blob/0.5.2/js/impress.js + window.clearTimeout(stepEnterTimeout); + stepEnterTimeout = window.setTimeout(function() { + onStepEnter(activeStep); + }, duration + delay); + + return el; + }; + + // `prev` API function goes to previous step (in document order) + var prev = function () { + var prev = steps.indexOf( activeStep ) - 1; + prev = prev >= 0 ? steps[ prev ] : steps[ steps.length-1 ]; + + return goto(prev); + }; + + // `next` API function goes to next step (in document order) + var next = function () { + var next = steps.indexOf( activeStep ) + 1; + next = next < steps.length ? steps[ next ] : steps[ 0 ]; + + return goto(next); + }; + + // Adding some useful classes to step elements. + // + // All the steps that have not been shown yet are given `future` class. + // When the step is entered the `future` class is removed and the `present` + // class is given. When the step is left `present` class is replaced with + // `past` class. + // + // So every step element is always in one of three possible states: + // `future`, `present` and `past`. + // + // There classes can be used in CSS to style different types of steps. + // For example the `present` class can be used to trigger some custom + // animations when step is shown. + root.addEventListener("impress:init", function(){ + // STEP CLASSES + steps.forEach(function (step) { + step.classList.add("future"); + }); + + root.addEventListener("impress:stepenter", function (event) { + event.target.classList.remove("past"); + event.target.classList.remove("future"); + event.target.classList.add("present"); + }, false); + + root.addEventListener("impress:stepleave", function (event) { + event.target.classList.remove("present"); + event.target.classList.add("past"); + }, false); + + }, false); + + // Adding hash change support. + root.addEventListener("impress:init", function(){ + + // last hash detected + var lastHash = ""; + + // `#/step-id` is used instead of `#step-id` to prevent default browser + // scrolling to element in hash. + // + // And it has to be set after animation finishes, because in Chrome it + // makes transtion laggy. + // BUG: http://code.google.com/p/chromium/issues/detail?id=62820 + root.addEventListener("impress:stepenter", function (event) { + window.location.hash = lastHash = "#/" + event.target.id; + }, false); + + window.addEventListener("hashchange", function () { + // When the step is entered hash in the location is updated + // (just few lines above from here), so the hash change is + // triggered and we would call `goto` again on the same element. + // + // To avoid this we store last entered hash and compare. + if (window.location.hash !== lastHash) { + goto( getElementFromHash() ); + } + }, false); + + // START + // by selecting step defined in url or first step of the presentation + goto(getElementFromHash() || steps[0], 0); + }, false); + + body.classList.add("impress-disabled"); + + // store and return API for given impress.js root element + return (roots[ "impress-root-" + rootId ] = { + init: init, + goto: goto, + next: next, + prev: prev + }); + + }; + + // flag that can be used in JS to check if browser have passed the support test + impress.supported = impressSupported; + +})(document, window); + +// NAVIGATION EVENTS + +// As you can see this part is separate from the impress.js core code. +// It's because these navigation actions only need what impress.js provides with +// its simple API. +// +// In future I think about moving it to make them optional, move to separate files +// and treat more like a 'plugins'. +(function ( document, window ) { + 'use strict'; + + // throttling function calls, by Remy Sharp + // http://remysharp.com/2010/07/21/throttling-function-calls/ + var throttle = function (fn, delay) { + var timer = null; + return function () { + var context = this, args = arguments; + clearTimeout(timer); + timer = setTimeout(function () { + fn.apply(context, args); + }, delay); + }; + }; + + // wait for impress.js to be initialized + document.addEventListener("impress:init", function (event) { + // Getting API from event data. + // So you don't event need to know what is the id of the root element + // or anything. `impress:init` event data gives you everything you + // need to control the presentation that was just initialized. + var api = event.detail.api; + + // KEYBOARD NAVIGATION HANDLERS + + // Prevent default keydown action when one of supported key is pressed. + document.addEventListener("keydown", function ( event ) { + if ( event.keyCode === 9 || ( event.keyCode >= 32 && event.keyCode <= 34 ) || (event.keyCode >= 37 && event.keyCode <= 40) ) { + event.preventDefault(); + } + }, false); + + // Trigger impress action (next or prev) on keyup. + + // Supported keys are: + // [space] - quite common in presentation software to move forward + // [up] [right] / [down] [left] - again common and natural addition, + // [pgdown] / [pgup] - often triggered by remote controllers, + // [tab] - this one is quite controversial, but the reason it ended up on + // this list is quite an interesting story... Remember that strange part + // in the impress.js code where window is scrolled to 0,0 on every presentation + // step, because sometimes browser scrolls viewport because of the focused element? + // Well, the [tab] key by default navigates around focusable elements, so clicking + // it very often caused scrolling to focused element and breaking impress.js + // positioning. I didn't want to just prevent this default action, so I used [tab] + // as another way to moving to next step... And yes, I know that for the sake of + // consistency I should add [shift+tab] as opposite action... + document.addEventListener("keyup", function ( event ) { + if ( event.keyCode === 9 || ( event.keyCode >= 32 && event.keyCode <= 34 ) || (event.keyCode >= 37 && event.keyCode <= 40) ) { + switch( event.keyCode ) { + case 33: // pg up + case 37: // left + case 38: // up + api.prev(); + break; + case 9: // tab + case 32: // space + case 34: // pg down + case 39: // right + case 40: // down + api.next(); + break; + } + + event.preventDefault(); + } + }, false); + + // delegated handler for clicking on the links to presentation steps + document.addEventListener("click", function ( event ) { + // event delegation with "bubbling" + // check if event target (or any of its parents is a link) + var target = event.target; + while ( (target.tagName !== "A") && + (target !== document.documentElement) ) { + target = target.parentNode; + } + + if ( target.tagName === "A" ) { + var href = target.getAttribute("href"); + + // if it's a link to presentation step, target this step + if ( href && href[0] === '#' ) { + target = document.getElementById( href.slice(1) ); + } + } + + if ( api.goto(target) ) { + event.stopImmediatePropagation(); + event.preventDefault(); + } + }, false); + + // delegated handler for clicking on step elements + document.addEventListener("click", function ( event ) { + var target = event.target; + // find closest step element that is not active + while ( !(target.classList.contains("step") && !target.classList.contains("active")) && + (target !== document.documentElement) ) { + target = target.parentNode; + } + + if ( api.goto(target) ) { + event.preventDefault(); + } + }, false); + + // touch handler to detect taps on the left and right side of the screen + // based on awesome work of @hakimel: https://github.com/hakimel/reveal.js + document.addEventListener("touchstart", function ( event ) { + if (event.touches.length === 1) { + var x = event.touches[0].clientX, + width = window.innerWidth * 0.3, + result = null; + + if ( x < width ) { + result = api.prev(); + } else if ( x > window.innerWidth - width ) { + result = api.next(); + } + + if (result) { + event.preventDefault(); + } + } + }, false); + + // rescale presentation when window is resized + window.addEventListener("resize", throttle(function () { + // force going to active step again, to trigger rescaling + api.goto( document.querySelector(".active"), 500 ); + }, 250), false); + + }, false); + +})(document, window); + +// THAT'S ALL FOLKS! +// +// Thanks for reading it all. +// Or thanks for scrolling down and reading the last part. +// +// I've learnt a lot when building impress.js and I hope this code and comments +// will help somebody learn at least some part of it. diff --git a/apps/impress/lib/impress.php b/apps/impress/lib/impress.php new file mode 100755 index 00000000000..ca33dbb2325 --- /dev/null +++ b/apps/impress/lib/impress.php @@ -0,0 +1,118 @@ +<?php + +/** + * ownCloud - Impress App + * + * @author Frank Karlitschek + * @copyright 2012 Frank Karlitschek frank@owncloud.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + + + +/* + +Todo: + enable fullscreen presentation + +*/ + +namespace OCA_Impress; + +class Storage { + + public static function getPresentations() { + $presentations=array(); + $list=\OC_FileCache::searchByMime('text','impress' ); + foreach($list as $l) { + $info=pathinfo($l); + $size=\OC_Filesystem::filesize($l); + $mtime=\OC_Filesystem::filemtime($l); + + $entry=array('url'=>$l,'name'=>$info['filename'],'size'=>$size,'mtime'=>$mtime); + $presentations[]=$entry; + } + + + return $presentations; + } + + + public static function showHeader($title){ + + echo(' + + <!doctype html> + <html lang="en"> + <head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=1024" /> + <title>'.$title.'</title> + + + <link href="http://fonts.googleapis.com/css?family=Open+Sans:regular,semibold,italic,italicsemibold|PT+Sans:400,700,400italic,700italic|PT+Serif:400,700,400italic,700italic" rel="stylesheet" /> + + <link href="'.\OCP\Util::linkToAbsolute('impress','css/player.css').'" rel="stylesheet" /> + </head> + +<body class="impress-not-supported"> + + + <div class="fallback-message"> + <p>Your browser <b>does not support the features required</b> by impress.js, so you are presented with a simplified version of this presentation.</p> + <p>For the best experience please use the latest <b>Chrome</b>, <b>Safari</b> or <b>Firefox</b> browser.</p> + </div> + + + <div id="impress"> + + + + '); + + } + + public static function showFooter(){ + + echo(' + + <div class="hint"> + <p>Make fullscreen and use a spacebar or arrow keys to navigate</p> + </div> + <script> + if ("ontouchstart" in document.documentElement) { + document.querySelector(".hint").innerHTML = "<p>Tap on the left or right to navigate</p>"; + } + </script> + + <script src="'.\OCP\Util::linkToAbsolute('impress','js/impress.js').'"></script> + <script>impress().init();</script> + + <script> + + + + </script> + + </body></html> + '); + + } + + +} + +?> diff --git a/apps/impress/player.php b/apps/impress/player.php new file mode 100755 index 00000000000..12497de54c1 --- /dev/null +++ b/apps/impress/player.php @@ -0,0 +1,49 @@ +<?php + +/** +* ownCloud - impress player +* +* @author Frank Karlitschek +* @copyright 2012 Frank Karlitschek ink@owncloud.org +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +* +*/ + +require_once('lib/impress.php'); + +// Check if we are a user +OCP\User::checkLoggedIn(); + +$filename = strip_tags($_GET['file']); +$title = strip_tags($_GET['name']); + +if(!OC_Filesystem::file_exists($filename)){ + header("HTTP/1.0 404 Not Found"); + $tmpl = new OCP\Template( '', '404', 'guest' ); + $tmpl->assign('file',$filename); + $tmpl->printPage(); + exit; +} + +header('Content-Type: text/html',true); +OCP\Response::disableCaching(); +header('Content-Length: '.OC_Filesystem::filesize($filename)); + +@ob_end_clean(); + +\OCA_Impress\Storage::showHeader($title); +OC_Filesystem::readfile( $filename ); +\OCA_Impress\Storage::showFooter(); + diff --git a/apps/impress/templates/documentation.php b/apps/impress/templates/documentation.php new file mode 100755 index 00000000000..1995bece255 --- /dev/null +++ b/apps/impress/templates/documentation.php @@ -0,0 +1,223 @@ + + +<div id="documentation"> + +<h1>Impress Documentation</h1> +<br /> + + +<h2>What is Impress?</h2> +Impress is an ownCloud application that can play presentation based on the amazing <a href="http://bartaz.github.com/impress.js">impress.js</a> library. +<br /><br /> + +<h2>How do I use it?</h2> +You have to define your presentation by writing a HTML file manually. But there are great examples and it isn´t that difficult. +Just put a HTML file with the file extension .impress into your ownCloud and it will show up here in the Impress app automatically. +If you click on it it opens up in a new windows. +<br /><br /> + +<h2>How do I define the presentation?</h2> +The best way to learn it is to look at the example presenation. We suggest that you copy it and place it into your ownCloud with the name demo.impress and play around with it. You can edit in ownCloud directly with the internal text editor and play it here in the Impress app. +<br /> + +<textarea class="examplecode"> + <!-- + Each step of the presentation should be an element inside the `#impress` with a class name + of `step`. These step elements are positioned, rotated and scaled by impress.js, and + the 'camera' shows them on each step of the presentation. + + Positioning information is passed through data attributes. + + In the example below we only specify x and y position of the step element with `data-x="-1000"` + and `data-y="-1500` attributes. This means that **the center** of the element (yes, the center) + will be positioned in point x = -1000px and y = -1500px of the presentation 'canvas'. + + It will not be rotated or scaled. + + --> + <div id="bored" class="step slide active present" data-x="-1000" data-y="-1500" style="position: absolute; -webkit-transform: translate(-50%, -50%) translate3d(-1000px, -1500px, 0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg) scale(1); -webkit-transform-style: preserve-3d; "> + <q>Aren't you just <b>bored</b> with all those slides-based presentations?</q> + </div> + + <!-- + + The `id` attribute of the step element is used to identify it in the URL, but it's optional. + If it is not defined, it will get a default value of `step-N` where N is a number of slide. + + So in the example below it'll be `step-2`. + + The hash part of the url when this step is active will be `#/step-2`. + + You can also use `#step-2` in a link, to point directly to this particular step. + + Please note, that while `#/step-2` (with slash) would also work in a link it's not recommended. + Using classic `id`-based links like `#step-2` makes these links usable also in fallback mode. + + --> + <div class="step slide future" data-x="0" data-y="-1500" id="step-2" style="position: absolute; -webkit-transform: translate(-50%, -50%) translate3d(0px, -1500px, 0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg) scale(1); -webkit-transform-style: preserve-3d; "> + <q>Don't you think that presentations given <strong>in modern browsers</strong> shouldn't <strong>copy the limits</strong> of 'classic' slide decks?</q> + </div> + + <div class="step slide future" data-x="1000" data-y="-1500" id="step-3" style="position: absolute; -webkit-transform: translate(-50%, -50%) translate3d(1000px, -1500px, 0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg) scale(1); -webkit-transform-style: preserve-3d; "> + <q>Would you like to <strong>impress your audience</strong> with <strong>stunning visualization</strong> of your talk?</q> + </div> + + <!-- + + This is an example of step element being scaled. + + Again, we use a `data-` attribute, this time it's `data-scale="4"`, so it means that this + element will be 4 times larger than the others. + From presentation and transitions point of view it means, that it will have to be scaled + down (4 times) to make it back to it's correct size. + + --> + <div id="title" class="step future" data-x="0" data-y="0" data-scale="4" style="position: absolute; -webkit-transform: translate(-50%, -50%) translate3d(0px, 0px, 0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg) scale(4); -webkit-transform-style: preserve-3d; "> + <span class="try">then you should try</span> + <h1>impress.js<sup>*</sup></h1> + <span class="footnote"><sup>*</sup> no rhyme intended</span> + </div> + + <!-- + + This element introduces rotation. + + Notation shouldn't be a surprise. We use `data-rotate="90"` attribute, meaning that this + element should be rotated by 90 degrees clockwise. + + --> + <div id="its" class="step future" data-x="850" data-y="3000" data-rotate="90" data-scale="5" style="position: absolute; -webkit-transform: translate(-50%, -50%) translate3d(850px, 3000px, 0px) rotateX(0deg) rotateY(0deg) rotateZ(90deg) scale(5); -webkit-transform-style: preserve-3d; "> + <p>It's a <strong>presentation tool</strong> <br> + inspired by the idea behind <a href="http://prezi.com/">prezi.com</a> <br> + and based on the <strong>power of CSS3 transforms and transitions</strong> in modern browsers.</p> + </div> + + <div id="big" class="step future" data-x="3500" data-y="2100" data-rotate="180" data-scale="6" style="position: absolute; -webkit-transform: translate(-50%, -50%) translate3d(3500px, 2100px, 0px) rotateX(0deg) rotateY(0deg) rotateZ(180deg) scale(6); -webkit-transform-style: preserve-3d; "> + <p>visualize your <b>big</b> <span class="thoughts">thoughts</span></p> + </div> + + <!-- + + And now it gets really exiting! We move into third dimension! + + Along with `data-x` and `data-y`, you can define the position on third (Z) axis, with + `data-z`. In the example below we use `data-z="-3000"` meaning that element should be + positioned far away from us (by 3000px). + + --> + <div id="tiny" class="step future" data-x="2825" data-y="2325" data-z="-3000" data-rotate="300" data-scale="1" style="position: absolute; -webkit-transform: translate(-50%, -50%) translate3d(2825px, 2325px, -3000px) rotateX(0deg) rotateY(0deg) rotateZ(300deg) scale(1); -webkit-transform-style: preserve-3d; "> + <p>and <b>tiny</b> ideas</p> + </div> + + <!-- + + This step here doesn't introduce anything new when it comes to data attributes, but you + should notice in the demo that some words of this text are being animated. + It's a very basic CSS transition that is applied to the elements when this step element is + reached. + + At the very beginning of the presentation all step elements are given the class of `future`. + It means that they haven't been visited yet. + + When the presentation moves to given step `future` is changed to `present` class name. + That's how animation on this step works - text moves when the step has `present` class. + + Finally when the step is left the `present` class is removed from the element and `past` + class is added. + + So basically every step element has one of three classes: `future`, `present` and `past`. + Only one current step has the `present` class. + + --> + <div id="ing" class="step future" data-x="3500" data-y="-850" data-rotate="270" data-scale="6" style="position: absolute; -webkit-transform: translate(-50%, -50%) translate3d(3500px, -850px, 0px) rotateX(0deg) rotateY(0deg) rotateZ(270deg) scale(6); -webkit-transform-style: preserve-3d; "> + <p>by <b class="positioning">positioning</b>, <b class="rotating">rotating</b> and <b class="scaling">scaling</b> them on an infinite canvas</p> + </div> + + <div id="imagination" class="step future" data-x="6700" data-y="-300" data-scale="6" style="position: absolute; -webkit-transform: translate(-50%, -50%) translate3d(6700px, -300px, 0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg) scale(6); -webkit-transform-style: preserve-3d; "> + <p>the only <b>limit</b> is your <b class="imagination">imagination</b></p> + </div> + + <div id="source" class="step future" data-x="6300" data-y="2000" data-rotate="20" data-scale="4" style="position: absolute; -webkit-transform: translate(-50%, -50%) translate3d(6300px, 2000px, 0px) rotateX(0deg) rotateY(0deg) rotateZ(20deg) scale(4); -webkit-transform-style: preserve-3d; "> + <p>want to know more?</p> + <q><a href="http://github.com/bartaz/impress.js">use the source</a>, Luke!</q> + </div> + + <div id="one-more-thing" class="step future" data-x="6000" data-y="4000" data-scale="2" style="position: absolute; -webkit-transform: translate(-50%, -50%) translate3d(6000px, 4000px, 0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg) scale(2); -webkit-transform-style: preserve-3d; "> + <p>one more thing...</p> + </div> + + <!-- + + And the last one shows full power and flexibility of impress.js. + + You can not only position element in 3D, but also rotate it around any axis. + So this one here will get rotated by -40 degrees (40 degrees anticlockwise) around X axis and + 10 degrees (clockwise) around Y axis. + + You can of course rotate it around Z axis with `data-rotate-z` - it has exactly the same effect + as `data-rotate` (these two are basically aliases). + + --> + <div id="its-in-3d" class="step future" data-x="6200" data-y="4300" data-z="-100" data-rotate-x="-40" data-rotate-y="10" data-scale="2" style="position: absolute; -webkit-transform: translate(-50%, -50%) translate3d(6200px, 4300px, -100px) rotateX(-40deg) rotateY(10deg) rotateZ(0deg) scale(2); -webkit-transform-style: preserve-3d; "> + <p><span class="have">have</span> <span class="you">you</span> <span class="noticed">noticed</span> <span class="its">it's</span> <span class="in">in</span> <b>3D<sup>*</sup></b>?</p> + <span class="footnote">* beat that, prezi ;)</span> + </div> + + <!-- + + So to make a summary of all the possible attributes used to position presentation steps, we have: + + * `data-x`, `data-y`, `data-z` - they define the position of **the center** of step element on + the canvas in pixels; their default value is 0; + * `data-rotate-x`, `data-rotate-y`, 'data-rotate-z`, `data-rotate` - they define the rotation of + the element around given axis in degrees; their default value is 0; `data-rotate` and `data-rotate-z` + are exactly the same; + * `data-scale` - defines the scale of step element; default value is 1 + + These values are used by impress.js in CSS transformation functions, so for more information consult + CSS transfrom docs: https://developer.mozilla.org/en/CSS/transform + + --> + <div id="overview" class="step future" data-x="3000" data-y="1500" data-scale="10" style="position: absolute; -webkit-transform: translate(-50%, -50%) translate3d(3000px, 1500px, 0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg) scale(10); -webkit-transform-style: preserve-3d; "> + </div> + + </div></div> + + <!-- + + Hint is not related to impress.js in any way. + + But it can show you how to use impress.js features in creative way. + + When the presentation step is shown (selected) its element gets the class of "active" and the body element + gets the class based on active step id `impress-on-ID` (where ID is the step's id)... It may not be + so clear because of all these "ids" in previous sentence, so for example when the first step (the one with + the id of `bored`) is active, body element gets a class of `impress-on-bored`. + + This class is used by this hint below. Check CSS file to see how it's shown with delayed CSS animation when + the first step of presentation is visible for a couple of seconds. + + ... + + And when it comes to this piece of JavaScript below ... kids, don't do this at home ;) + It's just a quick and dirty workaround to get different hint text for touch devices. + In a real world it should be at least placed in separate JS file ... and the touch content should be + probably just hidden somewhere in HTML - not hard-coded in the script. + + Just sayin' ;) + + --> +</textarea> + + +<br /><br /> + + + +<h2>Credits</h2> +Many thanks to Bartek Szopka for creating the wonderful impress.js library and also the demo presentation who made this app possible. +<br /><br /> + + + +</div>
\ No newline at end of file diff --git a/apps/impress/templates/presentations.php b/apps/impress/templates/presentations.php new file mode 100755 index 00000000000..ab75802b359 --- /dev/null +++ b/apps/impress/templates/presentations.php @@ -0,0 +1,30 @@ +<?php + + + +// show toolbar +echo('<div id="controls"> + <a href="'.\OCP\Util::linkToAbsolute('impress','documentation.php').'" class="button docu">'.$l->t('Documentation').'</a> + </div> +'); + + +if(empty($_['list'])) { + + echo('<div id="emptyfolder">No Impress files are found in your ownCloud. Please upload a .impress file.</div>'); + +}else{ + + echo('<table class="impresslist" >'); + foreach($_['list'] as $entry) { + + echo('<tr><td width="1"><a target="_blank" href="'.\OCP\Util::linkToAbsolute('impress','player.php').'&file='.urlencode($entry['url']).'&name='.urlencode($entry['name']).'"><img align="left" src="'.\OCP\Util::linkToAbsolute('impress','img/impressbig.png').'"></a></td><td><a target="_blank" href="'.\OCP\Util::linkToAbsolute('impress','player.php').'&file='.urlencode($entry['url']).'&name='.urlencode($entry['name']).'">'.$entry['name'].'</a></td><td>'.\OCP\Util::formatDate($entry['mtime']).'</td><td>'.\OCP\Util::humanFileSize($entry['size']).'</td></tr>'); + + } + echo('</table>'); + + + +} + + diff --git a/apps/media/ajax/api.php b/apps/media/ajax/api.php index 37dc6380198..7f5cdb22c12 100644 --- a/apps/media/ajax/api.php +++ b/apps/media/ajax/api.php @@ -24,8 +24,6 @@ header('Content-type: text/html; charset=UTF-8') ; OCP\JSON::checkAppEnabled('media'); -require_once(OC::$APPSROOT . '/apps/media/lib_collection.php'); -require_once(OC::$APPSROOT . '/apps/media/lib_scanner.php'); error_reporting(E_ALL); //no script error reporting because of getID3 @@ -48,6 +46,9 @@ if(!isset($arguments['album'])){ if(!isset($arguments['search'])){ $arguments['search']=''; } + +session_write_close(); + OC_MEDIA_COLLECTION::$uid=OCP\USER::getUser(); if($arguments['action']){ switch($arguments['action']){ @@ -105,6 +106,10 @@ if($arguments['action']){ @ob_end_clean(); $ftype=OC_Filesystem::getMimeType( $arguments['path'] ); + if(substr($ftype,0,5)!='audio' and $ftype!='application/ogg'){ + echo 'Not an audio file'; + exit(); + } $songId=OC_MEDIA_COLLECTION::getSongByPath($arguments['path']); OC_MEDIA_COLLECTION::registerPlay($songId); @@ -126,4 +131,3 @@ if($arguments['action']){ exit; } } -?>
\ No newline at end of file diff --git a/apps/media/ajax/autoupdate.php b/apps/media/ajax/autoupdate.php index 3122c7e6754..a5801f1a0e0 100644 --- a/apps/media/ajax/autoupdate.php +++ b/apps/media/ajax/autoupdate.php @@ -23,11 +23,6 @@ header('Content-type: text/html; charset=UTF-8') ; -//no apps or filesystem -$RUNTIME_NOAPPS=true; -$RUNTIME_NOSETUPFS=true; - - OCP\JSON::checkAppEnabled('media'); $autoUpdate=(isset($_GET['autoupdate']) and $_GET['autoupdate']=='true'); @@ -35,4 +30,3 @@ $autoUpdate=(isset($_GET['autoupdate']) and $_GET['autoupdate']=='true'); OCP\Config::setUserValue(OCP\USER::getUser(),'media','autoupdate',(integer)$autoUpdate); OCP\JSON::success(array('data' => $autoUpdate)); -?> diff --git a/apps/media/appinfo/app.php b/apps/media/appinfo/app.php index 26cb2045549..75015d627b4 100644 --- a/apps/media/appinfo/app.php +++ b/apps/media/appinfo/app.php @@ -22,13 +22,26 @@ $l=OC_L10N::get('media'); -require_once('apps/media/lib_media.php'); +OC::$CLASSPATH['OC_MEDIA'] = 'media/lib_media.php'; +OC::$CLASSPATH['OC_MediaSearchProvider'] = 'media/lib_media.php'; +OC::$CLASSPATH['OC_MEDIA_COLLECTION'] = 'media/lib_collection.php'; +OC::$CLASSPATH['OC_MEDIA_SCANNER'] = 'media/lib_scanner.php'; + +//we need to have the sha256 hash of passwords for ampache +OCP\Util::connectHook('OC_User','post_login','OC_MEDIA','loginListener'); + +//connect to the filesystem for auto updating +OCP\Util::connectHook('OC_Filesystem','post_write','OC_MEDIA','updateFile'); + +//listen for file deletions to clean the database if a song is deleted +OCP\Util::connectHook('OC_Filesystem','post_delete','OC_MEDIA','deleteFile'); + +//list for file moves to update the database +OCP\Util::connectHook('OC_Filesystem','post_rename','OC_MEDIA','moveFile'); OCP\Util::addscript('media','loader'); OCP\App::registerPersonal('media','settings'); -OCP\App::register( array( 'order' => 3, 'id' => 'media', 'name' => 'Media' )); - OCP\App::addNavigationEntry(array('id' => 'media_index', 'order' => 2, 'href' => OCP\Util::linkTo('media', 'index.php'), 'icon' => OCP\Util::imagePath('core', 'places/music.svg'), 'name' => $l->t('Music'))); OC_Search::registerProvider('OC_MediaSearchProvider'); diff --git a/apps/media/appinfo/version b/apps/media/appinfo/version index e6adf3fc7bb..44bb5d1f743 100644 --- a/apps/media/appinfo/version +++ b/apps/media/appinfo/version @@ -1 +1 @@ -0.4
\ No newline at end of file +0.4.1
\ No newline at end of file diff --git a/apps/media/index.php b/apps/media/index.php index 906d7bacb6e..ae85abc8aab 100644 --- a/apps/media/index.php +++ b/apps/media/index.php @@ -28,9 +28,6 @@ OCP\User::checkLoggedIn(); OCP\App::checkAppEnabled('media'); -require_once(OC::$APPSROOT . '/apps/media/lib_collection.php'); -require_once(OC::$APPSROOT . '/apps/media/lib_scanner.php'); - OCP\Util::addscript('media','player'); OCP\Util::addscript('media','music'); OCP\Util::addscript('media','playlist'); @@ -43,5 +40,3 @@ OCP\App::setActiveNavigationEntry( 'media_index' ); $tmpl = new OCP\Template( 'media', 'music', 'user' ); $tmpl->printPage(); -?> - diff --git a/apps/media/js/loader.js b/apps/media/js/loader.js index 393f8ba914e..ffe9c1cdd61 100644 --- a/apps/media/js/loader.js +++ b/apps/media/js/loader.js @@ -45,8 +45,8 @@ $(document).ready(function() { // FileActions.register('application/ogg','Add to playlist','',addAudio); if(typeof FileActions!=='undefined'){ - FileActions.register('audio','Play','',playAudio); - FileActions.register('application/ogg','','Play',playAudio); + FileActions.register('audio','Play', FileActions.PERMISSION_READ, '',playAudio); + FileActions.register('application/ogg', FileActions.PERMISSION_READ, '','Play',playAudio); FileActions.setDefault('audio','Play'); FileActions.setDefault('application/ogg','Play'); } diff --git a/apps/media/js/player.js b/apps/media/js/player.js index ad406830833..867ea802363 100644 --- a/apps/media/js/player.js +++ b/apps/media/js/player.js @@ -40,7 +40,7 @@ var PlayList={ PlayList.init(items[index].type,null); // init calls load that calls play }else{ PlayList.player.jPlayer("setMedia", items[PlayList.current]); - $(".jp-current-song").text(items[PlayList.current].name); + $(".jp-current-song").html(items[PlayList.current].name); items[index].playcount++; PlayList.player.jPlayer("play",time); if(index>0){ diff --git a/apps/media/l10n/ar.php b/apps/media/l10n/ar.php index a335f36a1f2..655589df8aa 100644 --- a/apps/media/l10n/ar.php +++ b/apps/media/l10n/ar.php @@ -1,5 +1,6 @@ <?php $TRANSLATIONS = array( "Music" => "الموسيقى", +"Add album to playlist" => "أض٠الالبوم الى القائمه", "Play" => "إلعب", "Pause" => "تجميد", "Previous" => "السابق", diff --git a/apps/media/l10n/bg_BG.php b/apps/media/l10n/bg_BG.php index 1b71b26a165..e6c3c02d17f 100644 --- a/apps/media/l10n/bg_BG.php +++ b/apps/media/l10n/bg_BG.php @@ -1,5 +1,6 @@ <?php $TRANSLATIONS = array( "Music" => "Музика", +"Add album to playlist" => "ДобавÑне на албума към ÑпиÑъка за изпълнение", "Play" => "ПуÑни", "Pause" => "Пауза", "Previous" => "Предишна", diff --git a/apps/media/l10n/eo.php b/apps/media/l10n/eo.php index 429247bbc33..084dbaa480e 100644 --- a/apps/media/l10n/eo.php +++ b/apps/media/l10n/eo.php @@ -1,9 +1,10 @@ <?php $TRANSLATIONS = array( "Music" => "Muziko", +"Add album to playlist" => "Aldoni albumon al ludlisto", "Play" => "Ludi", "Pause" => "PaÅzigi", -"Previous" => "AntaÅa", -"Next" => "Sekva", +"Previous" => "Maljena", +"Next" => "Jena", "Mute" => "Silentigi", "Unmute" => "Malsilentigi", "Rescan Collection" => "Reskani la aron", diff --git a/apps/media/l10n/fr.php b/apps/media/l10n/fr.php index c96e84d73fd..313a918d021 100644 --- a/apps/media/l10n/fr.php +++ b/apps/media/l10n/fr.php @@ -1,6 +1,7 @@ <?php $TRANSLATIONS = array( "Music" => "Musique", -"Play" => "Play", +"Add album to playlist" => "Ajouter l'album à la playlist", +"Play" => "Lire", "Pause" => "Pause", "Previous" => "Précédent", "Next" => "Suivant", diff --git a/apps/media/l10n/sv.php b/apps/media/l10n/sv.php index 75670a48515..1cf5497e7e2 100644 --- a/apps/media/l10n/sv.php +++ b/apps/media/l10n/sv.php @@ -1,5 +1,6 @@ <?php $TRANSLATIONS = array( "Music" => "Musik", +"Add album to playlist" => "Lägg till album till spellistan", "Play" => "Spela", "Pause" => "Paus", "Previous" => "FöregÃ¥ende", diff --git a/apps/media/l10n/uk.php b/apps/media/l10n/uk.php new file mode 100644 index 00000000000..4ac7abbf2b2 --- /dev/null +++ b/apps/media/l10n/uk.php @@ -0,0 +1,14 @@ +<?php $TRANSLATIONS = array( +"Music" => "Музика", +"Add album to playlist" => "Додати альбом до плейлиÑта", +"Play" => "Грати", +"Pause" => "Пауза", +"Previous" => "Попередній", +"Next" => "ÐаÑтупний", +"Mute" => "Звук вкл.", +"Unmute" => "Звук викл.", +"Rescan Collection" => "Повторне ÑÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð»ÐµÐºÑ†Ñ–Ñ—", +"Artist" => "Виконавець", +"Album" => "Ðльбом", +"Title" => "Заголовок" +); diff --git a/apps/media/l10n/vi.php b/apps/media/l10n/vi.php new file mode 100644 index 00000000000..01942ba173f --- /dev/null +++ b/apps/media/l10n/vi.php @@ -0,0 +1,14 @@ +<?php $TRANSLATIONS = array( +"Music" => "Âm nhạc", +"Add album to playlist" => "Thêm album và o playlist", +"Play" => "Play", +"Pause" => "Tạm dừng", +"Previous" => "Trang trÆ°á»›c", +"Next" => "Tiếp theo", +"Mute" => "Tắt", +"Unmute" => "Báºt", +"Rescan Collection" => "Quét lại bá»™ sÆ°u táºp", +"Artist" => "Nghệ sỹ", +"Album" => "Album", +"Title" => "Tiêu Ä‘á»" +); diff --git a/apps/media/l10n/zh_CN.GB2312.php b/apps/media/l10n/zh_CN.GB2312.php new file mode 100644 index 00000000000..de7e98acd9e --- /dev/null +++ b/apps/media/l10n/zh_CN.GB2312.php @@ -0,0 +1,14 @@ +<?php $TRANSLATIONS = array( +"Music" => "音ä¹", +"Add album to playlist" => "æ·»åŠ ä¸“è¾‘åˆ°æ’放列表", +"Play" => "æ’放", +"Pause" => "æš‚åœ", +"Previous" => "å‰é¢çš„", +"Next" => "下一个", +"Mute" => "é™éŸ³", +"Unmute" => "å–消é™éŸ³", +"Rescan Collection" => "é‡æ–°æ‰«æ收è—", +"Artist" => "艺术家", +"Album" => "专辑", +"Title" => "æ ‡é¢˜" +); diff --git a/apps/media/lib/share/album.php b/apps/media/lib/share/album.php new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/apps/media/lib/share/album.php diff --git a/apps/media/lib/share/artist.php b/apps/media/lib/share/artist.php new file mode 100644 index 00000000000..7218fa1a279 --- /dev/null +++ b/apps/media/lib/share/artist.php @@ -0,0 +1,65 @@ +<?php +/** +* ownCloud +* +* @author Michael Gapczynski +* @copyright 2012 Michael Gapczynski mtgap@owncloud.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +class OC_Share_Backend_Artist extends OCP\Share_Backend { + + public function getSource($item, $uid) { + $query = OCP\DB::prepare('SELECT artist_id FROM *PREFIX*media_artists WHERE artist_id = ? AND song_user = ?'); + $result = $query->execute(array($item, $uid))->fetchRow(); + if (is_array($result)) { + return array('item' => $item, 'file' => $result['song_path']); + } + return false; + } + + public function generateTarget($item, $uid, $exclude = null) { + // TODO Make sure target path doesn't exist already + return '/Shared'.$item; + } + + public function formatItems($items, $format) { + $ids = array(); + foreach ($items as $id => $info) { + $ids[] = $id; + } + $ids = "'".implode("','", $ids)."'"; + switch ($format) { + case self::FORMAT_SOURCE_PATH: + $query = OCP\DB::prepare('SELECT path FROM *PREFIX*fscache WHERE id IN ('.$ids.')'); + return $query->execute()->fetchAll(); + case self::FORMAT_FILE_APP: + $query = OCP\DB::prepare('SELECT id, path, name, ctime, mtime, mimetype, size, encrypted, versioned, writable FROM *PREFIX*fscache WHERE id IN ('.$ids.')'); + $result = $query->execute(); + $files = array(); + while ($file = $result->fetchRow()) { + // Set target path + $file['path'] = $items[$file['id']]['item_target']; + $file['name'] = basename($file['path']); + // TODO Set permissions: $file['writable'] + $files[] = $file; + } + return $files; + } + } + +} + +?>
\ No newline at end of file diff --git a/apps/media/lib/share/song.php b/apps/media/lib/share/song.php new file mode 100644 index 00000000000..fc69975f353 --- /dev/null +++ b/apps/media/lib/share/song.php @@ -0,0 +1,65 @@ +<?php +/** +* ownCloud +* +* @author Michael Gapczynski +* @copyright 2012 Michael Gapczynski mtgap@owncloud.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +class OC_Share_Backend_Song extends OCP\Share_Backend { + + public function getSource($item, $uid) { + $query = OCP\DB::prepare('SELECT song_path FROM *PREFIX*media_songs WHERE song_id = ? AND song_user = ?'); + $result = $query->execute(array($item, $uid))->fetchRow(); + if (is_array($result)) { + return array('item' => $item, 'file' => $result['song_path']); + } + return false; + } + + public function generateTarget($item, $uid, $exclude = null) { + // TODO Make sure target path doesn't exist already + return '/Shared'.$item; + } + + public function formatItems($items, $format) { + $ids = array(); + foreach ($items as $id => $info) { + $ids[] = $id; + } + $ids = "'".implode("','", $ids)."'"; + switch ($format) { + case self::FORMAT_SOURCE_PATH: + $query = OCP\DB::prepare('SELECT path FROM *PREFIX*fscache WHERE id IN ('.$ids.')'); + return $query->execute()->fetchAll(); + case self::FORMAT_FILE_APP: + $query = OCP\DB::prepare('SELECT id, path, name, ctime, mtime, mimetype, size, encrypted, versioned, writable FROM *PREFIX*fscache WHERE id IN ('.$ids.')'); + $result = $query->execute(); + $files = array(); + while ($file = $result->fetchRow()) { + // Set target path + $file['path'] = $items[$file['id']]['item_target']; + $file['name'] = basename($file['path']); + // TODO Set permissions: $file['writable'] + $files[] = $file; + } + return $files; + } + } + +} + +?>
\ No newline at end of file diff --git a/apps/media/lib_ampache.php b/apps/media/lib_ampache.php index 8f8f1e985a6..807d94bcdeb 100644 --- a/apps/media/lib_ampache.php +++ b/apps/media/lib_ampache.php @@ -77,7 +77,7 @@ class OC_MEDIA_AMPACHE{ $songs=OC_MEDIA_COLLECTION::getSongCount(); $artists=OC_MEDIA_COLLECTION::getArtistCount(); $albums=OC_MEDIA_COLLECTION::getAlbumCount(); - $query=OCP\DB::prepare("INSERT INTO `*PREFIX*media_sessions` (`session_id`, `token`, `user_id`, `start`) VALUES (NULL, ?, ?, now());"); + $query=OCP\DB::prepare("INSERT INTO `*PREFIX*media_sessions` (`token`, `user_id`, `start`) VALUES (?, ?, now());"); $query->execute(array($token,$user)); $expire=date('c',time()+600); echo('<?xml version="1.0" encoding="UTF-8"?>'); @@ -136,8 +136,14 @@ class OC_MEDIA_AMPACHE{ return false; } } + $CONFIG_DBTYPE = OCP\Config::getSystemValue( "dbtype", "sqlite" ); + if($CONFIG_DBTYPE == 'psql'){ + $interval = ' \'600s\'::interval '; + }else { + $interval = '600'; + } //remove old sessions - $query=OCP\DB::prepare("DELETE FROM `*PREFIX*media_sessions` WHERE `start`<(NOW()-600)"); + $query=OCP\DB::prepare("DELETE FROM `*PREFIX*media_sessions` WHERE `start`<(NOW() - ".$interval.")"); $query->execute(); $query=OCP\DB::prepare("SELECT `user_id` FROM `*PREFIX*media_sessions` WHERE `token`=?"); @@ -266,7 +272,6 @@ class OC_MEDIA_AMPACHE{ </root>"); return; } - global $SITEROOT; $filter = isset($params['filter']) ? $params['filter'] : ''; $albums=OC_MEDIA_COLLECTION::getAlbums($filter); $artist=OC_MEDIA_COLLECTION::getArtistName($filter); @@ -414,5 +419,3 @@ class OC_MEDIA_AMPACHE{ echo('</root>'); } } - -?> diff --git a/apps/media/lib_collection.php b/apps/media/lib_collection.php index b59e6f82103..c7265caaecb 100644 --- a/apps/media/lib_collection.php +++ b/apps/media/lib_collection.php @@ -27,7 +27,6 @@ class OC_MEDIA_COLLECTION{ public static $uid; private static $artistIdCache=array(); private static $albumIdCache=array(); - private static $songIdCache=array(); private static $queries=array(); /** @@ -152,7 +151,7 @@ class OC_MEDIA_COLLECTION{ return $artistId; }else{ $query=OCP\DB::prepare("INSERT INTO `*PREFIX*media_artists` (`artist_name`) VALUES (?)"); - $result=$query->execute(array($name)); + $query->execute(array($name)); return self::getArtistId($name);; } } @@ -387,5 +386,3 @@ class OC_MEDIA_COLLECTION{ $query->execute(array($newPath,$oldPath)); } } - -?> diff --git a/apps/media/lib_media.php b/apps/media/lib_media.php index f9f10584ddf..ff58e4e7350 100644 --- a/apps/media/lib_media.php +++ b/apps/media/lib_media.php @@ -21,26 +21,14 @@ * */ -//we need to have the sha256 hash of passwords for ampache -OCP\Util::connectHook('OC_User','post_login','OC_MEDIA','loginListener'); - -//connect to the filesystem for auto updating -OCP\Util::connectHook('OC_Filesystem','post_write','OC_MEDIA','updateFile'); - -//listen for file deletions to clean the database if a song is deleted -OCP\Util::connectHook('OC_Filesystem','post_delete','OC_MEDIA','deleteFile'); - -//list for file moves to update the database -OCP\Util::connectHook('OC_Filesystem','post_rename','OC_MEDIA','moveFile'); - class OC_MEDIA{ /** * get the sha256 hash of the password needed for ampache * @param array $params, parameters passed from OC_Hook */ public static function loginListener($params){ - if(isset($_POST['user']) and $_POST['password']){ - $name=$_POST['user']; + if(isset($params['uid']) and $params['password']){ + $name=$params['uid']; $query=OCP\DB::prepare("SELECT `user_id` from `*PREFIX*media_users` WHERE `user_id` LIKE ?"); $uid=$query->execute(array($name))->fetchAll(); if(count($uid)==0){ diff --git a/apps/media/lib_scanner.php b/apps/media/lib_scanner.php index a8218c3a4d0..3c32879eeeb 100644 --- a/apps/media/lib_scanner.php +++ b/apps/media/lib_scanner.php @@ -61,7 +61,6 @@ class OC_MEDIA_SCANNER{ * @return boolean */ public static function scanFile($path){ - $file=OC_Filesystem::getLocalFile($path); if(!self::isMusic($path)){ return; } @@ -69,6 +68,7 @@ class OC_MEDIA_SCANNER{ self::$getID3=@new getID3(); self::$getID3->encoding='UTF-8'; } + $file=OC_Filesystem::getLocalFile($path); $data=@self::$getID3->analyze($file); getid3_lib::CopyTagsToComments($data); if(!isset($data['comments'])){ diff --git a/apps/media/remote.php b/apps/media/remote.php index 01add42b315..0535077cef1 100644 --- a/apps/media/remote.php +++ b/apps/media/remote.php @@ -5,7 +5,7 @@ $RUNTIME_APPTYPES=array('filesystem','authentication'); OC_App::loadApps($RUNTIME_APPTYPES); if($path_info == '/ampache' || $path_info == '/ampache/'){ - require_once(OC::$APPSROOT . '/apps/media/index.php'); + require_once(OC_App::getAppPath('media').'/index.php'); }else{ - require_once(OC::$APPSROOT . '/apps/media/server/xml.server.php'); + require_once(OC_App::getAppPath('media').'/server/xml.server.php'); } diff --git a/apps/media/server/xml.server.php b/apps/media/server/xml.server.php index 6cb6c91ca06..796da130a47 100644 --- a/apps/media/server/xml.server.php +++ b/apps/media/server/xml.server.php @@ -22,8 +22,8 @@ */ OCP\App::checkAppEnabled('media'); - require_once(OC::$APPSROOT . '/apps/media/lib_collection.php'); - require_once(OC::$APPSROOT . '/apps/media/lib_ampache.php'); + require_once(OC_App::getAppPath('media').'/lib_collection.php'); + require_once(OC_App::getAppPath('media').'/lib_ampache.php'); $arguments=$_POST; if(!isset($_POST['action']) and isset($_GET['action'])){ diff --git a/apps/media/settings.php b/apps/media/settings.php index 227298fafec..53738f02f9f 100644 --- a/apps/media/settings.php +++ b/apps/media/settings.php @@ -3,4 +3,3 @@ $tmpl = new OCP\Template( 'media', 'settings'); return $tmpl->fetchPage(); -?> diff --git a/apps/remoteStorage/ajax/revokeToken.php b/apps/remoteStorage/ajax/revokeToken.php index 699b9e9aeec..e6a68189945 100644 --- a/apps/remoteStorage/ajax/revokeToken.php +++ b/apps/remoteStorage/ajax/revokeToken.php @@ -5,7 +5,7 @@ * * Original: * @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org +* @copyright 2012 Frank Karlitschek frank@owncloud.org * * Adapted: * @author Michiel de Jong, 2012 @@ -25,10 +25,6 @@ * */ - -// Do not load FS ... -$RUNTIME_NOSETUPFS = true; - OCP\App::checkAppEnabled('remoteStorage'); require_once('remoteStorage/lib_remoteStorage.php'); diff --git a/apps/remoteStorage/appinfo/app.php b/apps/remoteStorage/appinfo/app.php index 14b8a3d11df..c278fd73056 100644 --- a/apps/remoteStorage/appinfo/app.php +++ b/apps/remoteStorage/appinfo/app.php @@ -1,6 +1,2 @@ <?php -OCP\App::register( array( - 'order' => 10, - 'id' => 'remoteStorage', - 'name' => 'remoteStorage compatibility' )); OCP\App::registerPersonal('remoteStorage','settings'); diff --git a/apps/remoteStorage/appinfo/info.xml b/apps/remoteStorage/appinfo/info.xml index fa878762a05..1388ad9c316 100644 --- a/apps/remoteStorage/appinfo/info.xml +++ b/apps/remoteStorage/appinfo/info.xml @@ -7,4 +7,7 @@ <author>Michiel de Jong</author> <require>4</require> <shipped>true</shipped> + <remote> + <remoteStorage>webdav.php</remoteStorage> + </remote> </info> diff --git a/apps/remoteStorage/appinfo/version b/apps/remoteStorage/appinfo/version index 490f510fc27..0e2c93950bb 100644 --- a/apps/remoteStorage/appinfo/version +++ b/apps/remoteStorage/appinfo/version @@ -1 +1 @@ -0.6
\ No newline at end of file +0.7
\ No newline at end of file diff --git a/apps/remoteStorage/appinfo/webfinger.php b/apps/remoteStorage/appinfo/webfinger.php index 5d481f315f8..e8b237628c4 100644 --- a/apps/remoteStorage/appinfo/webfinger.php +++ b/apps/remoteStorage/appinfo/webfinger.php @@ -1,8 +1,8 @@ -<?php if(OC_User::userExists(WF_USER)) { ?> +<?php if(OC_User::userExists(WF_USER)): ?> { "rel":"remoteStorage", - "template":"<?php echo WF_BASEURL; ?>/apps/remoteStorage/WebDAV.php/<?php echo WF_USER; ?>/remoteStorage/{category}/", + "template":"<?php echo WF_BASEURL; ?>/remote.php/remoteStorage/<?php echo WF_USER; ?>/remoteStorage/{category}/", "api":"WebDAV", "auth":"<?php echo WF_BASEURL; ?>/?app=remoteStorage&getfile=auth.php&userid=<?php echo WF_USER; ?>" } -<?php } ?> +<?php endif ?> diff --git a/apps/remoteStorage/auth.php b/apps/remoteStorage/auth.php index a54be37b2e6..91ca43ea076 100644 --- a/apps/remoteStorage/auth.php +++ b/apps/remoteStorage/auth.php @@ -5,7 +5,7 @@ * * Original: * @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org +* @copyright 2012 Frank Karlitschek frank@owncloud.org * * Adapted: * @author Michiel de Jong, 2012 @@ -25,9 +25,7 @@ * */ - -// Do not load FS ... -$RUNTIME_NOSETUPFS = true; +header("X-Frame-Options: Sameorigin"); OCP\App::checkAppEnabled('remoteStorage'); require_once('Sabre/autoload.php'); @@ -43,9 +41,9 @@ foreach($_GET as $k => $v) { $userId=$v; } else if($k=='redirect_uri'){ $appUrlParts=explode('/', $v); - $appUrl = $appUrlParts[2];//bit dodgy i guess + $appUrl = htmlentities($appUrlParts[2]);//TODO: check if this is equal to client_id } else if($k=='scope'){ - $categories=$v; + $categories=htmlentities($v); } } $currUser = OCP\USER::getUser(); @@ -59,64 +57,23 @@ if($userId && $appUrl && $categories) { header('Location: '.$_GET['redirect_uri'].'#access_token='.$existingToken.'&token_type=bearer'); } else { //params ok, logged in ok, but need to click Allow still: -?> -<!DOCTYPE html> -<html> - <head> - <title>ownCloud</title> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - <link rel="shortcut icon" href="../../../core/img/favicon.png" /><link rel="apple-touch-icon-precomposed" href="../../../core/img/favicon-touch.png" /> - <link rel="stylesheet" href="../../../core/css/styles.css" type="text/css" media="screen" /> - <link rel="stylesheet" href="../../../core/css/auth.css" type="text/css" media="screen" /> - </head> - <body id="body-login"> - <div id="login"> - <header> - <div id="header"> - <img src="../../../core/img/logo.png" alt="ownCloud" /> - </div> - </header> - <section id="main"> - <div id="oauth"> - <h2><img src="../../../core/img/remoteStorage-big.png" alt="remoteStorage" /></h2> - <p><strong><?php $appUrlParts = explode('/', $_GET['redirect_uri']); echo htmlentities($appUrlParts[2]); ?></strong> - requests read & write access to your - <?php - $categories = explode(',', htmlentities($_GET['scope'])); - if(!count($categories)) { - echo htmlentities($_GET['scope']); - } else { - echo '<em>'.$categories[0].'</em>'; - if(count($categories)==2) { - echo ' and <em>'.$categories[1].'</em>'; - } else if(count($categories)>2) { - for($i=1; $i<count($categories)-1; $i++) { - echo ', <em>'.$categories[$i].'</em>'; - } - echo ', and <em>'.$categories[$i].'</em>'; - } - } - ?>. - </p> - <form accept-charset="UTF-8" method="post"> - <input id="allow-auth" name="allow" type="submit" value="Allow" /> - <input id="deny-auth" name="deny" type="submit" value="Deny" /> - </form> - </div> - </section> - </div> - <footer><p class="info"><a href="http://owncloud.org/">ownCloud</a> – web services under your control</p></footer> - </body> -</html> -<?php - }//end 'need to click Allow still' + $appUrlParts = explode('/', $_GET['redirect_uri']); + $host = $appUrlParts[2]; + $categories = explode(',', $_GET['scope']); + OCP\Util::addStyle('', 'auth'); + OCP\Template::printGuestPage('remoteStorage', 'auth', array( + 'host' => $host, + 'categories' => $categories, + )); + }//end 'need to click Allow still' } else {//login not ok if($currUser) { - die('You are logged in as '.$currUser.' instead of '.$userId); + die('You are logged in as '.$currUser.' instead of '.htmlentities($userId)); } else { - header('Location: /?redirect_url='.urlencode('/apps/remoteStorage/auth.php'.$_SERVER['PATH_INFO'].'?'.$_SERVER['QUERY_STRING'])); + // this will display the login page for us + OCP\Util::checkLoggedIn(); } } } else {//params not ok - die('please use e.g. /?app=remoteStorage&getfile=auth.php&userid=admin&redirect_uri=http://host/path&scope=...'); + die('please use e.g. '.OCP\Util::linkTo('remoteStorage', 'auth.php').'?userid=admin&redirect_uri=http://host/path&scope=...'); } diff --git a/apps/remoteStorage/img/remoteStorage.png b/apps/remoteStorage/img/remoteStorage.png Binary files differnew file mode 100644 index 00000000000..10c2be243c2 --- /dev/null +++ b/apps/remoteStorage/img/remoteStorage.png diff --git a/apps/remoteStorage/lib_remoteStorage.php b/apps/remoteStorage/lib_remoteStorage.php index b6934e87d67..b9e23680037 100644 --- a/apps/remoteStorage/lib_remoteStorage.php +++ b/apps/remoteStorage/lib_remoteStorage.php @@ -2,8 +2,8 @@ class OC_remoteStorage { public static function getValidTokens($ownCloudUser, $category) { - $query=OCP\DB::prepare("SELECT `token`,`appUrl`,`category` FROM `*PREFIX*authtoken` WHERE `user`=?",100); - $result=$query->execute(array($ownCloudUser)); + $stmt=OCP\DB::prepare("SELECT `token`,`appUrl`,`category` FROM `*PREFIX*authtoken` WHERE `user`=?",100); + $result=$stmt->execute(array($ownCloudUser)); $ret = array(); while($row=$result->fetchRow()){ if(in_array($category, explode(',', $row['category']))) { @@ -15,20 +15,19 @@ class OC_remoteStorage { public static function getTokenFor($appUrl, $categories) { $user=OCP\USER::getUser(); - $query=OCP\DB::prepare("SELECT `token` FROM `*PREFIX*authtoken` WHERE `user`=? AND `appUrl`=? AND `category`=?",1); - $result=$query->execute(array($user, $appUrl, $categories)); - $ret = array(); + $stmt=OCP\DB::prepare("SELECT `token` FROM `*PREFIX*authtoken` WHERE `user`=? AND `appUrl`=? AND `category`=?",1); + $result=$stmt->execute(array($user, $appUrl, $categories)); if($row=$result->fetchRow()) { - return base64_encode('remoteStorage:'.$row['token']); - } else { - return false; - } + return base64_encode('remoteStorage:'.$row['token']); + } else { + return false; + } } public static function getAllTokens() { $user=OCP\USER::getUser(); - $query=OCP\DB::prepare("SELECT `token`,`appUrl`,`category` FROM `*PREFIX*authtoken` WHERE `user`=?",100); - $result=$query->execute(array($user)); + $stmt=OCP\DB::prepare("SELECT `token`,`appUrl`,`category` FROM `*PREFIX*authtoken` WHERE `user`=?",100); + $result=$stmt->execute(array($user)); $ret = array(); while($row=$result->fetchRow()){ $ret[$row['token']] = array( @@ -41,14 +40,14 @@ class OC_remoteStorage { public static function deleteToken($token) { $user=OCP\USER::getUser(); - $query=OCP\DB::prepare("DELETE FROM `*PREFIX*authtoken` WHERE `token`=? AND `user`=?"); - $result=$query->execute(array($token,$user)); + $stmt=OCP\DB::prepare("DELETE FROM `*PREFIX*authtoken` WHERE `token`=? AND `user`=?"); + $stmt->execute(array($token,$user)); return 'unknown';//how can we see if any rows were affected? } private static function addToken($token, $appUrl, $categories){ $user=OCP\USER::getUser(); - $query=OCP\DB::prepare("INSERT INTO `*PREFIX*authtoken` (`token`,`appUrl`,`user`,`category`) VALUES(?,?,?,?)"); - $result=$query->execute(array($token,$appUrl,$user,$categories)); + $stmt=OCP\DB::prepare("INSERT INTO `*PREFIX*authtoken` (`token`,`appUrl`,`user`,`category`) VALUES(?,?,?,?)"); + $stmt->execute(array($token,$appUrl,$user,$categories)); } public static function createCategories($appUrl, $categories) { $token=uniqid(); diff --git a/apps/remoteStorage/oauth_ro_auth.php b/apps/remoteStorage/oauth_ro_auth.php index 12d02d1cf5d..bed3093c3b3 100644 --- a/apps/remoteStorage/oauth_ro_auth.php +++ b/apps/remoteStorage/oauth_ro_auth.php @@ -9,10 +9,10 @@ class OC_Connector_Sabre_Auth_ro_oauth extends Sabre_DAV_Auth_Backend_AbstractBasic { private $validTokens; - private $category; + private $category; public function __construct($validTokensArg, $categoryArg) { $this->validTokens = $validTokensArg; - $this->category = $categoryArg; + $this->category = $categoryArg; } /** @@ -25,16 +25,16 @@ class OC_Connector_Sabre_Auth_ro_oauth extends Sabre_DAV_Auth_Backend_AbstractBa */ protected function validateUserPass($username, $password){ //always give read-only: - if(($_SERVER['REQUEST_METHOD'] == 'OPTIONS') + if(($_SERVER['REQUEST_METHOD'] == 'OPTIONS') || (isset($this->validTokens[$password])) - || (($_SERVER['REQUEST_METHOD'] == 'GET') && ($this->category == 'public')) - ) { + || (($_SERVER['REQUEST_METHOD'] == 'GET') && ($this->category == 'public')) + ) { OC_Util::setUpFS(); return true; } else { - //var_export($_SERVER); - //var_export($this->validTokens); - //die('not getting in with "'.$username.'"/"'.$password.'"!'); + //var_export($_SERVER); + //var_export($this->validTokens); + //die('not getting in with "'.$username.'"/"'.$password.'"!'); return false; } } @@ -48,8 +48,8 @@ class OC_Connector_Sabre_Auth_ro_oauth extends Sabre_DAV_Auth_Backend_AbstractBa $userpass = $auth->getUserPass(); if (!$userpass) { if(($_SERVER['REQUEST_METHOD'] == 'OPTIONS') - ||(($_SERVER['REQUEST_METHOD'] == 'GET') && ($this->category == 'public')) - ) { + ||(($_SERVER['REQUEST_METHOD'] == 'GET') && ($this->category == 'public')) + ) { $userpass = array('', ''); } else { $auth->requireLogin(); diff --git a/apps/remoteStorage/remoteStorage.png b/apps/remoteStorage/remoteStorage.png Binary files differdeleted file mode 100644 index 6b751c09997..00000000000 --- a/apps/remoteStorage/remoteStorage.png +++ /dev/null diff --git a/apps/remoteStorage/settings.php b/apps/remoteStorage/settings.php index 9c48549fe6d..3be8b0984d8 100644 --- a/apps/remoteStorage/settings.php +++ b/apps/remoteStorage/settings.php @@ -4,4 +4,3 @@ require_once('lib_remoteStorage.php'); $tmpl = new OCP\Template( 'remoteStorage', 'settings'); return $tmpl->fetchPage(); -?> diff --git a/apps/remoteStorage/templates/auth.php b/apps/remoteStorage/templates/auth.php new file mode 100644 index 00000000000..6a7054eabb0 --- /dev/null +++ b/apps/remoteStorage/templates/auth.php @@ -0,0 +1,28 @@ + <section id="main"> + <div id="oauth"> + <h2><img src="<?php echo image_path('', 'remoteStorage-big.png'); ?>" alt="remoteStorage" /></h2> + <p><strong><?php echo $_['host'] ?></strong> + requests read & write access to your + <?php + $categories = $_['categories']; + if(!count($categories)) { + echo $categories[0]; + } else { + echo '<em>'.$categories[0].'</em>'; + if(count($categories)==2) { + echo ' and <em>'.$categories[1].'</em>'; + } else if(count($categories)>2) { + for($i=1; $i<count($categories)-1; $i++) { + echo ', <em>'.$categories[$i].'</em>'; + } + echo ', and <em>'.$categories[$i].'</em>'; + } + } + ?>. + </p> + <form accept-charset="UTF-8" method="post"> + <input id="allow-auth" name="allow" type="submit" value="Allow" /> + <input id="deny-auth" name="deny" type="submit" value="Deny" /> + </form> + </div> + </section> diff --git a/apps/remoteStorage/templates/settings.php b/apps/remoteStorage/templates/settings.php index 147378dda39..1d2a188f527 100644 --- a/apps/remoteStorage/templates/settings.php +++ b/apps/remoteStorage/templates/settings.php @@ -1,10 +1,6 @@ <fieldset class="personalblock"> - <?php - echo '<img src="../apps/remoteStorage/remoteStorage.png" style="width:16px"> ' - .'<strong>'.$l->t('remoteStorage').'</strong> user address: ' - .OCP\USER::getUser().'@'.$_SERVER['SERVER_NAME'] - .' (<a href="http://unhosted.org/">more info</a>)'; - ?> + <img src="<?php echo image_path('remoteStorage', 'remoteStorage.png') ?>" style="width:16px"> + <strong><?php echo $l->t('remoteStorage') ?></strong> user address: <?php echo OCP\USER::getUser().'@'.$_SERVER['SERVER_NAME'] ?> (<a href="http://unhosted.org/">more info</a>) <p><em>Apps that currently have access to your ownCloud:</em></p> <script> function revokeToken(token) { @@ -14,15 +10,13 @@ } </script> <ul> - <?php - foreach(OC_remoteStorage::getAllTokens() as $token => $details) { - echo '<li onmouseover="' - .'document.getElementById(\'revoke_'.$token.'\').style.display=\'inline\';"' - .'onmouseout="document.getElementById(\'revoke_'.$token.'\').style.display=\'none\';"' - .'> <strong>'.$details['appUrl'].'</strong>: '.$details['categories'] - .' <a href="#" title="Revoke" class="action" style="display:none" id="revoke_'.$token.'" onclick="' - .'revokeToken(\''.$token.'\');this.parentNode.style.display=\'none\';"' - .'><img src="/core/img/actions/delete.svg"></a></li>'."\n"; - } - ?></ul> + <?php foreach(OC_remoteStorage::getAllTokens() as $token => $details) { ?> + <li onmouseover="$('#revoke_<?php echo $token ?>').show();" onmouseout="$('#revoke_<?php echo $token ?>').hide();"> + <strong><?php echo $details['appUrl'] ?></strong>: <?php echo $details['categories'] ?> + <a href="#" title="Revoke" class="action" style="display:none" id="revoke_<?php echo $token ?>" onclick="revokeToken('<?php echo $token ?>');$(this).hide();"> + <img src="<?php echo OCP\Util::imagePath('core', 'actions/delete.svg') ?>"> + </a> + </li> + <?php } ?> + </ul> </fieldset> diff --git a/apps/remoteStorage/WebDAV.php b/apps/remoteStorage/webdav.php index ab498db07da..8d8ec6a45a1 100644 --- a/apps/remoteStorage/WebDAV.php +++ b/apps/remoteStorage/webdav.php @@ -5,7 +5,7 @@ * * Original: * @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org +* @copyright 2012 Frank Karlitschek frank@owncloud.org * * Adapted: * @author Michiel de Jong, 2011 @@ -25,22 +25,7 @@ * */ - -// Do not load FS ... -$RUNTIME_NOSETUPFS = true; - - -require_once('../../lib/base.php'); - -require_once('../../lib/user.php'); -require_once('../../lib/public/user.php'); - -require_once('../../lib/app.php'); -require_once('../../lib/public/app.php'); - -require_once('../../3rdparty/Sabre/DAV/Auth/IBackend.php'); -require_once('../../3rdparty/Sabre/DAV/Auth/Backend/AbstractBasic.php'); -require_once('../../lib/connector/sabre/auth.php'); +OC_App::loadApps(array('filesystem','authentication')); OCP\App::checkAppEnabled('remoteStorage'); require_once('lib_remoteStorage.php'); @@ -61,14 +46,15 @@ if(isset($_SERVER['HTTP_ORIGIN'])) { header('Access-Control-Allow-Origin: *'); } -$path = substr($_SERVER["REQUEST_URI"], strlen($_SERVER["SCRIPT_NAME"])); +$path = substr($_SERVER["REQUEST_URI"], strlen($baseuri)); + $pathParts = explode('/', $path); // for webdav: -// 0/ 1 / 2 / 3... -// /$ownCloudUser/remoteStorage/$category/ +// 0 / 1 / 2... +// $ownCloudUser/remoteStorage/$category/ -if(count($pathParts) >= 3 && $pathParts[0] == '') { - list($dummy, $ownCloudUser, $dummy2, $category) = $pathParts; +if(count($pathParts) >= 2) { + list($ownCloudUser, $dummy2, $category) = $pathParts; OC_Util::setupFS($ownCloudUser); @@ -77,13 +63,13 @@ if(count($pathParts) >= 3 && $pathParts[0] == '') { $server = new Sabre_DAV_Server($publicDir); // Path to our script - $server->setBaseUri(OC::$WEBROOT."/apps/remoteStorage/WebDAV.php/$ownCloudUser"); + $server->setBaseUri($baseuri.$ownCloudUser); // Auth backend $authBackend = new OC_Connector_Sabre_Auth_ro_oauth( - OC_remoteStorage::getValidTokens($ownCloudUser, $category), - $category - ); + OC_remoteStorage::getValidTokens($ownCloudUser, $category), + $category + ); $authPlugin = new Sabre_DAV_Auth_Plugin($authBackend,'ownCloud');//should use $validTokens here $server->addPlugin($authPlugin); diff --git a/apps/tasks/ajax/addtask.php b/apps/tasks/ajax/addtask.php index 188e179236a..d98fdbf3888 100644 --- a/apps/tasks/ajax/addtask.php +++ b/apps/tasks/ajax/addtask.php @@ -22,7 +22,7 @@ $request['description'] = null; $vcalendar = OC_Task_App::createVCalendarFromRequest($request); $id = OC_Calendar_Object::add($cid, $vcalendar->serialize()); -$user_timezone = OCP\Config::getUserValue(OCP\User::getUser(), 'calendar', 'timezone', date_default_timezone_get()); +$user_timezone = OC_Calendar_App::getTimezone(); $task = OC_Task_App::arrayForJSON($id, $vcalendar->VTODO, $user_timezone); OCP\JSON::success(array('task' => $task)); diff --git a/apps/tasks/ajax/addtaskform.php b/apps/tasks/ajax/addtaskform.php deleted file mode 100644 index 2795f393732..00000000000 --- a/apps/tasks/ajax/addtaskform.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php - -// Init owncloud -OCP\JSON::checkLoggedIn(); -OCP\JSON::checkAppEnabled('tasks'); -OCP\JSON::callCheck(); - -$calendars = OC_Calendar_Calendar::allCalendars(OCP\User::getUser(), true); -$category_options = OC_Calendar_App::getCategoryOptions(); -$percent_options = range(0, 100, 10); -$priority_options = OC_Task_App::getPriorityOptions(); -$tmpl = new OCP\Template('tasks','part.addtaskform'); -$tmpl->assign('calendars',$calendars); -$tmpl->assign('category_options', $category_options); -$tmpl->assign('percent_options', $percent_options); -$tmpl->assign('priority_options', $priority_options); -$tmpl->assign('details', new OC_VObject('VTODO')); -$tmpl->assign('categories', ''); -$page = $tmpl->fetchPage(); - -OCP\JSON::success(array('data' => array( 'page' => $page ))); diff --git a/apps/tasks/ajax/edittask.php b/apps/tasks/ajax/edittask.php deleted file mode 100644 index 77ecff13e66..00000000000 --- a/apps/tasks/ajax/edittask.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -// Init owncloud -OCP\JSON::checkLoggedIn(); -OCP\JSON::checkAppEnabled('tasks'); -OCP\JSON::callCheck(); - -$l10n = new OC_L10N('tasks'); - -$id = $_POST['id']; -$vcalendar = OC_Calendar_App::getVCalendar($id); - -$errors = OC_Task_App::validateRequest($_POST); -if (!empty($errors)) { - OCP\JSON::error(array('data' => array( 'errors' => $errors ))); - exit(); -} - -OC_Task_App::updateVCalendarFromRequest($_POST, $vcalendar); -OC_Calendar_Object::edit($id, $vcalendar->serialize()); - -$priority_options = OC_Task_App::getPriorityOptions(); -$tmpl = new OCP\Template('tasks','part.details'); -$tmpl->assign('priority_options', $priority_options); -$tmpl->assign('details', $vcalendar->VTODO); -$tmpl->assign('id', $id); -$page = $tmpl->fetchPage(); - -$user_timezone = OCP\Config::getUserValue(OCP\User::getUser(), 'calendar', 'timezone', date_default_timezone_get()); -$task = OC_Task_App::arrayForJSON($id, $vcalendar->VTODO, $user_timezone); - -OCP\JSON::success(array('data' => array( 'id' => $id, 'page' => $page, 'task' => $task ))); diff --git a/apps/tasks/ajax/edittaskform.php b/apps/tasks/ajax/edittaskform.php deleted file mode 100644 index e5a0a7297c5..00000000000 --- a/apps/tasks/ajax/edittaskform.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -// Init owncloud -OCP\JSON::checkLoggedIn(); -OCP\JSON::checkAppEnabled('tasks'); - -$id = $_GET['id']; -$details = OC_Calendar_App::getVCalendar($id)->VTODO; -$categories = $details->getAsString('CATEGORIES'); - -$category_options = OC_Calendar_App::getCategoryOptions(); -$percent_options = range(0, 100, 10); -$priority_options = OC_Task_App::getPriorityOptions(); - -$tmpl = new OCP\Template('tasks','part.edittaskform'); -$tmpl->assign('category_options', $category_options); -$tmpl->assign('percent_options', $percent_options); -$tmpl->assign('priority_options', $priority_options); -$tmpl->assign('id',$id); -$tmpl->assign('details',$details); -$tmpl->assign('categories', $categories); -$page = $tmpl->fetchPage(); - -OCP\JSON::success(array('data' => array( 'page' => $page ))); diff --git a/apps/tasks/ajax/getdetails.php b/apps/tasks/ajax/getdetails.php deleted file mode 100644 index 4ce469e0c9c..00000000000 --- a/apps/tasks/ajax/getdetails.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -// Init owncloud -OCP\JSON::checkLoggedIn(); -OCP\JSON::checkAppEnabled('tasks'); - -$l10n = new OC_L10N('tasks'); - -$id = $_GET['id']; -$task = OC_Calendar_Object::find($id); -$details = OC_VObject::parse($task['calendardata']); -if (!$details){ - OCP\JSON::error(); - exit; -} - -$priority_options = OC_Task_App::getPriorityOptions(); -$tmpl = new OCP\Template('tasks','part.details'); -$tmpl->assign('priority_options', $priority_options); -$tmpl->assign('details',$details->VTODO); -$tmpl->assign('id',$id); -$page = $tmpl->fetchPage(); - -OCP\JSON::success(array('data' => array( 'id' => $id, 'page' => $page ))); diff --git a/apps/tasks/ajax/gettasks.php b/apps/tasks/ajax/gettasks.php index 011730d0a13..b6183d9cb65 100644 --- a/apps/tasks/ajax/gettasks.php +++ b/apps/tasks/ajax/gettasks.php @@ -11,7 +11,7 @@ OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('tasks'); $calendars = OC_Calendar_Calendar::allCalendars(OCP\User::getUser(), true); -$user_timezone = OCP\Config::getUserValue(OCP\User::getUser(), 'calendar', 'timezone', date_default_timezone_get()); +$user_timezone = OC_Calendar_App::getTimezone(); $tasks = array(); foreach( $calendars as $calendar ){ diff --git a/apps/tasks/ajax/update_property.php b/apps/tasks/ajax/update_property.php index 46521cf6c58..679cfdefe48 100644 --- a/apps/tasks/ajax/update_property.php +++ b/apps/tasks/ajax/update_property.php @@ -9,6 +9,7 @@ // Init owncloud OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('tasks'); +OCP\JSON::callCheck(); $id = $_POST['id']; $property = $_POST['type']; @@ -38,7 +39,7 @@ switch($property) { $type = null; if ($due != 'false') { try { - $timezone = OCP\Config::getUserValue(OCP\User::getUser(), 'calendar', 'timezone', date_default_timezone_get()); + $timezone = OC_Calendar_App::getTimezone(); $timezone = new DateTimeZone($timezone); $due = new DateTime('@'.$due); $due->setTimezone($timezone); @@ -63,6 +64,6 @@ switch($property) { } OC_Calendar_Object::edit($id, $vcalendar->serialize()); -$user_timezone = OCP\Config::getUserValue(OCP\User::getUser(), 'calendar', 'timezone', date_default_timezone_get()); +$user_timezone = OC_Calendar_App::getTimezone(); $task_info = OC_Task_App::arrayForJSON($id, $vtodo, $user_timezone); OCP\JSON::success(array('data' => $task_info)); diff --git a/apps/tasks/appinfo/app.php b/apps/tasks/appinfo/app.php index f346e2aa4c0..e7c82d6f247 100644 --- a/apps/tasks/appinfo/app.php +++ b/apps/tasks/appinfo/app.php @@ -3,11 +3,6 @@ $l=new OC_L10N('tasks'); OC::$CLASSPATH['OC_Calendar_Calendar'] = 'apps/calendar/lib/calendar.php'; OC::$CLASSPATH['OC_Task_App'] = 'apps/tasks/lib/app.php'; -OCP\App::register( array( - 'order' => 11, - 'id' => 'tasks', - 'name' => 'Tasks' )); - OCP\App::addNavigationEntry( array( 'id' => 'tasks_index', 'order' => 11, diff --git a/apps/tasks/css/style.css b/apps/tasks/css/style.css index f6333f57b8a..80b6e777b5d 100644 --- a/apps/tasks/css/style.css +++ b/apps/tasks/css/style.css @@ -9,7 +9,7 @@ #tasks_lists .active{font-weight:bold;} #tasks_list h1{background-color:#1D2D44;color:white;font-size:120%;padding:0 0.5em;} -.task{border-radius:0.4em;position:relative;padding:0.5em 1em;} +.task{border-radius:0.4em;position:relative;padding:0.4em 1em;} .task:nth-child(odd){background-color:#F4F4F4;} .task:hover {background-color:#DDDDDD;} @@ -42,18 +42,19 @@ .task:hover .tag{opacity:0.5} .task:hover .tag:hover{opacity:0.8;} -.task .categories{position:absolute;right:12em;text-align:right;top:0.4em} +.task .categories{position:absolute;right:12em;text-align:right;top:0.5em} .task .categories a{background-color:#1d2d44;color:white;} .task .categories .tag.active{display:none;} .task input.categories{display:none;top:0;text-align:left;} -.task .location{background-color:#442d44;color:white;position:absolute;right:0.6em;width:9.2em;text-align:left;top:0.4em} +.task .location{background-color:#442d44;color:white;position:absolute;right:0.6em;width:9.2em;text-align:left;top:0.5em} .task input.location{display:none;top:0;text-align:left;right:0.3em;background-color:white;color:#333333;} .task .more{display:none;margin-top:0.5em;} .task_less{display:none;} .task .description{position:relative;left:4em;} +.task textarea.description{width:35em;height:4em;} .task .due{position:absolute;right:0.3em;} .task .due .date{width:6em;} .task .due .time{width:6em;} diff --git a/apps/tasks/img/icon.png b/apps/tasks/img/icon.png Binary files differindex df281f3ba86..e2802ae9387 100644 --- a/apps/tasks/img/icon.png +++ b/apps/tasks/img/icon.png diff --git a/apps/tasks/index.php b/apps/tasks/index.php index 5e17ca454ec..f1c4d1e765c 100644 --- a/apps/tasks/index.php +++ b/apps/tasks/index.php @@ -21,12 +21,11 @@ OCP\Util::addScript('3rdparty/timepicker', 'jquery.ui.timepicker'); OCP\Util::addStyle('3rdparty/timepicker', 'jquery.ui.timepicker'); OCP\Util::addScript('tasks', 'tasks'); OCP\Util::addStyle('tasks', 'style'); -OCP\Util::addScript('contacts','jquery.multi-autocomplete'); -OCP\Util::addScript('','oc-vcategories'); +OCP\Util::addScript('contacts', 'jquery.multi-autocomplete'); +OCP\Util::addScript('', 'oc-vcategories'); OCP\App::setActiveNavigationEntry('tasks_index'); $categories = OC_Calendar_App::getCategoryOptions(); -$l10n = new OC_L10N('tasks'); $priority_options = OC_Task_App::getPriorityOptions(); $output = new OCP\Template('tasks', 'tasks', 'user'); $output->assign('priority_options', $priority_options); diff --git a/apps/tasks/js/tasks.js b/apps/tasks/js/tasks.js index bc92965bb0b..de627927507 100644 --- a/apps/tasks/js/tasks.js +++ b/apps/tasks/js/tasks.js @@ -469,67 +469,5 @@ $(document).ready(function(){ return false; }); - $('#tasks_addtaskform input[type="submit"]').live('click',function(){ - $.post('ajax/addtask.php',$('#tasks_addtaskform').serialize(),function(jsondata){ - if(jsondata.status == 'success'){ - $('#task_details').data('id',jsondata.data.id); - $('#task_details').html(jsondata.data.page); - $('#tasks_list').append(OC.Tasks.create_task_div(jsondata.data.task)); - } - else{ - alert(jsondata.data.message); - } - }, 'json'); - return false; - }); - - $('#tasks_edit').live('click',function(){ - var id = $('#task_details').data('id'); - $.getJSON('ajax/edittaskform.php',{'id':id},function(jsondata){ - if(jsondata.status == 'success'){ - $('#task_details').html(jsondata.data.page); - $('#task_details #categories').multiple_autocomplete({source: categories}); - } - else{ - alert(jsondata.data.message); - } - }); - return false; - }); - - $('#tasks_edittaskform #percent_complete').live('change',function(event){ - if ($(event.target).val() == 100){ - $('#tasks_edittaskform #complete').show(); - }else{ - $('#tasks_edittaskform #complete').hide(); - } - }); - - $('#tasks_edittaskform input[type="submit"]').live('click',function(){ - $.post('ajax/edittask.php',$('#tasks_edittaskform').serialize(),function(jsondata){ - $('.error_msg').remove(); - $('.error').removeClass('error'); - if(jsondata.status == 'success'){ - var id = jsondata.data.id; - $('#task_details').data('id',id); - $('#task_details').html(jsondata.data.page); - var task = jsondata.data.task; - $('#tasks .task[data-id='+id+']') - .data('task', task) - .html(OC.Tasks.create_task_div(task).html()); - } - else{ - var errors = jsondata.data.errors; - for (k in errors){ - $('#'+k).addClass('error') - .after('<span class="error_msg">'+errors[k]+'</span>'); - } - $('.error_msg').effect('highlight', {}, 3000); - $('.error').effect('highlight', {}, 3000); - } - }, 'json'); - return false; - }); - OCCategories.app = 'calendar'; }); diff --git a/apps/tasks/l10n/.gitkeep b/apps/tasks/l10n/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/apps/tasks/l10n/.gitkeep diff --git a/apps/tasks/l10n/ca.php b/apps/tasks/l10n/ca.php new file mode 100644 index 00000000000..2608d8b9b17 --- /dev/null +++ b/apps/tasks/l10n/ca.php @@ -0,0 +1,24 @@ +<?php $TRANSLATIONS = array( +"Invalid date/time" => "data/hora incorrecta", +"Tasks" => "Tasques", +"No category" => "Cap categoria", +"Unspecified" => "Sense especificar", +"1=highest" => "1=major", +"5=medium" => "5=mitjana", +"9=lowest" => "9=inferior", +"Empty Summary" => "Elimina el resum", +"Invalid percent complete" => "Percentatge completat no và lid", +"Invalid priority" => "Prioritat no và lida", +"Add Task" => "Afegeix una tasca", +"Order Due" => "Ordena per", +"Order List" => "Ordena per llista", +"Order Complete" => "Ordena els complets", +"Order Location" => "Ordena per ubicació", +"Order Priority" => "Ordena per prioritat", +"Order Label" => "Ordena per etiqueta", +"Loading tasks..." => "Carregant les tasques...", +"Important" => "Important", +"More" => "Més", +"Less" => "Menys", +"Delete" => "Elimina" +); diff --git a/apps/tasks/l10n/cs_CZ.php b/apps/tasks/l10n/cs_CZ.php new file mode 100644 index 00000000000..9da5f7ab607 --- /dev/null +++ b/apps/tasks/l10n/cs_CZ.php @@ -0,0 +1,15 @@ +<?php $TRANSLATIONS = array( +"Invalid date/time" => "Neplatné datum/Äas", +"Tasks" => "Úkoly", +"No category" => "Bez kategorie", +"1=highest" => "1=nejvyÅ¡Å¡Ã", +"5=medium" => "5=stÅ™ednÃ", +"9=lowest" => "9=nejnižšÃ", +"Invalid priority" => "Neplatná priorita", +"Add Task" => "PÅ™idat úkol", +"Loading tasks..." => "NaÄÃtám úkoly...", +"Important" => "Důležité", +"More" => "VÃce", +"Less" => "MénÄ›", +"Delete" => "Smazat" +); diff --git a/apps/tasks/l10n/da.php b/apps/tasks/l10n/da.php new file mode 100644 index 00000000000..a413f22d17b --- /dev/null +++ b/apps/tasks/l10n/da.php @@ -0,0 +1,16 @@ +<?php $TRANSLATIONS = array( +"Invalid date/time" => "Ugyldig dato/tid", +"Tasks" => "Opgaver", +"No category" => "Ingen kategori", +"Unspecified" => "Uspecificeret", +"1=highest" => "1=højeste", +"5=medium" => "5=mellem", +"9=lowest" => "9=laveste", +"Empty Summary" => "Tom beskrivelse", +"Add Task" => "Tilføj opgave", +"Loading tasks..." => "Indlæser opgaver...", +"Important" => "vigtigt", +"More" => "Mere", +"Less" => "Mindre", +"Delete" => "Slet" +); diff --git a/apps/tasks/l10n/de.php b/apps/tasks/l10n/de.php new file mode 100644 index 00000000000..29b9bf41ec6 --- /dev/null +++ b/apps/tasks/l10n/de.php @@ -0,0 +1,24 @@ +<?php $TRANSLATIONS = array( +"Invalid date/time" => "Datum/Uhrzeit ungültig", +"Tasks" => "Aufgaben", +"No category" => "Keine Kategorie", +"Unspecified" => "Nicht angegeben", +"1=highest" => "1 = am höchsten", +"5=medium" => "5 = Durchschnitt", +"9=lowest" => "9 = am niedrigsten", +"Empty Summary" => "Leere Zusammenfassung", +"Invalid percent complete" => "Ungültige Prozent abgeschlossen", +"Invalid priority" => "Falsche Priorität", +"Add Task" => "Aufgabe hinzufügen", +"Order Due" => "Nach Fälligkeit sortieren", +"Order List" => "Nach Kategorie sortieren ", +"Order Complete" => "Nach Fertigstellung sortieren", +"Order Location" => "Nach Ort sortieren", +"Order Priority" => "Nach Priorität sortieren", +"Order Label" => "Nach Label sortieren", +"Loading tasks..." => "Lade Aufgaben ...", +"Important" => "Wichtig", +"More" => "Mehr", +"Less" => "Weniger", +"Delete" => "Löschen" +); diff --git a/apps/tasks/l10n/el.php b/apps/tasks/l10n/el.php new file mode 100644 index 00000000000..cdf2f609e5a --- /dev/null +++ b/apps/tasks/l10n/el.php @@ -0,0 +1,18 @@ +<?php $TRANSLATIONS = array( +"Invalid date/time" => "Μην ÎγκυÏη ημεÏομηνία / ÏŽÏα", +"Tasks" => "ΕÏγασίες", +"No category" => "ΧωÏίς κατηγοÏία", +"Unspecified" => "Μη οÏισμÎνο", +"1=highest" => "1=υψηλότεÏο", +"5=medium" => "5=μÎÏ„Ïιο", +"9=lowest" => "9=χαμηλότεÏο", +"Empty Summary" => "Άδεια πεÏίληψη", +"Invalid percent complete" => "Μη ÎγκυÏο ποσοστό ολοκλήÏωσης", +"Invalid priority" => "Μη ÎγκυÏη Ï€ÏοτεÏαιότητα ", +"Add Task" => "Î Ïοσθήκη εÏγασίας", +"Loading tasks..." => "ΦόÏτωση εÏγασιών...", +"Important" => "Σημαντικό ", +"More" => "ΠεÏισσότεÏα", +"Less" => "ΛιγότεÏα", +"Delete" => "ΔιαγÏαφή" +); diff --git a/apps/tasks/l10n/eo.php b/apps/tasks/l10n/eo.php new file mode 100644 index 00000000000..9919a6ea63e --- /dev/null +++ b/apps/tasks/l10n/eo.php @@ -0,0 +1,24 @@ +<?php $TRANSLATIONS = array( +"Invalid date/time" => "Nevalida dato/horo", +"Tasks" => "Taskoj", +"No category" => "Neniu kategorio", +"Unspecified" => "Nespecifita", +"1=highest" => "1=plej alta", +"5=medium" => "5=meza", +"9=lowest" => "9=plej malalta", +"Empty Summary" => "Malplena resumo", +"Invalid percent complete" => "Nevalida plenuma elcento", +"Invalid priority" => "Nevalida pligravo", +"Add Task" => "Aldoni taskon", +"Order Due" => "Ordigi laÅ limdato", +"Order List" => "Ordigi laÅ listo", +"Order Complete" => "Ordigi laÅ plenumo", +"Order Location" => "Ordigi laÅ loko", +"Order Priority" => "Ordigi laÅ pligravo", +"Order Label" => "Ordigi laÅ etikedo", +"Loading tasks..." => "Åœargante taskojn...", +"Important" => "Grava", +"More" => "Pli", +"Less" => "Malpli", +"Delete" => "Forigi" +); diff --git a/apps/tasks/l10n/es.php b/apps/tasks/l10n/es.php new file mode 100644 index 00000000000..e5f0981de55 --- /dev/null +++ b/apps/tasks/l10n/es.php @@ -0,0 +1,24 @@ +<?php $TRANSLATIONS = array( +"Invalid date/time" => "Fecha/hora inválida", +"Tasks" => "Tareas", +"No category" => "Sin categorÃa", +"Unspecified" => "Sin especificar", +"1=highest" => "1=mayor", +"5=medium" => "5=media", +"9=lowest" => "9=menor", +"Empty Summary" => "Resumen vacÃo", +"Invalid percent complete" => "Porcentaje completado inválido", +"Invalid priority" => "Prioridad inválida", +"Add Task" => "Agregar tarea", +"Order Due" => "Ordenar por", +"Order List" => "Ordenar por lista", +"Order Complete" => "Ordenar por completadas", +"Order Location" => "Ordenar por ubicación", +"Order Priority" => "Ordenar por prioridad", +"Order Label" => "Ordenar por etiqueta", +"Loading tasks..." => "Cargando tareas...", +"Important" => "Importante", +"More" => "Más", +"Less" => "Menos", +"Delete" => "Borrar" +); diff --git a/apps/tasks/l10n/et_EE.php b/apps/tasks/l10n/et_EE.php new file mode 100644 index 00000000000..56db6ee0327 --- /dev/null +++ b/apps/tasks/l10n/et_EE.php @@ -0,0 +1,24 @@ +<?php $TRANSLATIONS = array( +"Invalid date/time" => "Vigane kuupäev/kellaaeg", +"Tasks" => "Ãœlesanded", +"No category" => "Kategooriat pole", +"Unspecified" => "Määramata", +"1=highest" => "1=kõrgeim", +"5=medium" => "5=keskmine", +"9=lowest" => "9=madalaim", +"Empty Summary" => "Tühi kokkuvõte", +"Invalid percent complete" => "Vigane edenemise protsent", +"Invalid priority" => "Vigane tähtsus", +"Add Task" => "Lisa ülesanne", +"Order Due" => "Tähtaja järgi", +"Order List" => "Nimekirja järgi", +"Order Complete" => "Edenemise järgi", +"Order Location" => "Asukoha järgi", +"Order Priority" => "Tähtsuse järjekorras", +"Order Label" => "Sildi järgi", +"Loading tasks..." => "Ãœlesannete laadimine...", +"Important" => "Tähtis", +"More" => "Rohkem", +"Less" => "Vähem", +"Delete" => "Kustuta" +); diff --git a/apps/tasks/l10n/fa.php b/apps/tasks/l10n/fa.php new file mode 100644 index 00000000000..40b5b610f4e --- /dev/null +++ b/apps/tasks/l10n/fa.php @@ -0,0 +1,11 @@ +<?php $TRANSLATIONS = array( +"Tasks" => "وظایÙ", +"1=highest" => "1=بیش‌ترین", +"5=medium" => "5=متوسط", +"9=lowest" => "9=کم‌ترین", +"Loading tasks..." => "درØال بارگزاری وظایÙ", +"Important" => "مهم", +"More" => "بیش‌تر", +"Less" => "کم‌تر", +"Delete" => "ØØ°Ù" +); diff --git a/apps/tasks/l10n/fi_FI.php b/apps/tasks/l10n/fi_FI.php new file mode 100644 index 00000000000..7948e712e9c --- /dev/null +++ b/apps/tasks/l10n/fi_FI.php @@ -0,0 +1,17 @@ +<?php $TRANSLATIONS = array( +"Invalid date/time" => "Virheellinen päivä tai aika", +"Tasks" => "Tehtävät", +"No category" => "Ei luokkaa", +"Unspecified" => "Määrittelemätön", +"1=highest" => "1=korkein", +"5=medium" => "5=keskitaso", +"9=lowest" => "9=matalin", +"Empty Summary" => "Tyhjä yhteenveto", +"Invalid priority" => "Virheellinen prioriteetti", +"Add Task" => "Lisää tehtävä", +"Loading tasks..." => "Ladataan tehtäviä...", +"Important" => "Tärkeä", +"More" => "Enemmän", +"Less" => "Vähemmän", +"Delete" => "Poista" +); diff --git a/apps/tasks/l10n/fr.php b/apps/tasks/l10n/fr.php new file mode 100644 index 00000000000..5b0a0321a87 --- /dev/null +++ b/apps/tasks/l10n/fr.php @@ -0,0 +1,24 @@ +<?php $TRANSLATIONS = array( +"Invalid date/time" => "date/heure invalide", +"Tasks" => "Tâches", +"No category" => "Sans catégorie", +"Unspecified" => "Non spécifié", +"1=highest" => "1=le plus important", +"5=medium" => "5=importance moyenne", +"9=lowest" => "9=le moins important", +"Empty Summary" => "Résumé vide", +"Invalid percent complete" => "Pourcentage d'achèvement invalide", +"Invalid priority" => "Priorité invalide", +"Add Task" => "Ajouter une tâche", +"Order Due" => "Echéance tâche", +"Order List" => "Liste tâche", +"Order Complete" => "Tâche réalisée", +"Order Location" => "Lieu", +"Order Priority" => "Priorité", +"Order Label" => "Etiquette tâche", +"Loading tasks..." => "Chargement des tâches…", +"Important" => "Important", +"More" => "Plus", +"Less" => "Moins", +"Delete" => "Supprimer" +); diff --git a/apps/tasks/l10n/it.php b/apps/tasks/l10n/it.php new file mode 100644 index 00000000000..1aac8aac886 --- /dev/null +++ b/apps/tasks/l10n/it.php @@ -0,0 +1,24 @@ +<?php $TRANSLATIONS = array( +"Invalid date/time" => "Ora/Data non valida", +"Tasks" => "Attività ", +"No category" => "Nessuna categoria", +"Unspecified" => "Non specificata", +"1=highest" => "1=massima", +"5=medium" => "5=media", +"9=lowest" => "9=minima", +"Empty Summary" => "Riepilogo vuoto", +"Invalid percent complete" => "Percentuale di completamento non valida", +"Invalid priority" => "Priorità non valida", +"Add Task" => "Aggiungi attività ", +"Order Due" => "Ordina per scadenza", +"Order List" => "Ordina per elenco", +"Order Complete" => "Ordina per completamento", +"Order Location" => "Ordina per posizione", +"Order Priority" => "Ordina per priorità ", +"Order Label" => "Ordina per etichetta", +"Loading tasks..." => "Caricamento attività in corso...", +"Important" => "Importante", +"More" => "Più", +"Less" => "Meno", +"Delete" => "Elimina" +); diff --git a/apps/tasks/l10n/ja_JP.php b/apps/tasks/l10n/ja_JP.php new file mode 100644 index 00000000000..b5b526c595a --- /dev/null +++ b/apps/tasks/l10n/ja_JP.php @@ -0,0 +1,24 @@ +<?php $TRANSLATIONS = array( +"Invalid date/time" => "無効ãªæ—¥ä»˜ï¼æ™‚刻", +"Tasks" => "タスク", +"No category" => "カテゴリ無ã—", +"Unspecified" => "未指定", +"1=highest" => "1=高", +"5=medium" => "5=ä¸", +"9=lowest" => "9=低", +"Empty Summary" => "è¦æ—¨ãŒæœªè¨˜å…¥", +"Invalid percent complete" => "進æ—ï¼…ãŒä¸æ£", +"Invalid priority" => "無効ãªå„ªå…ˆåº¦", +"Add Task" => "ã‚¿ã‚¹ã‚¯ã‚’è¿½åŠ ", +"Order Due" => "期日ã§ä¸¦ã¹æ›¿ãˆ", +"Order List" => "リストã§ä¸¦ã³æ›¿ãˆ", +"Order Complete" => "完了ã§ä¸¦ã¹æ›¿ãˆ", +"Order Location" => "å ´æ‰€ã§ä¸¦ã¹æ›¿ãˆ", +"Order Priority" => "優先度ã§ä¸¦ã¹æ›¿ãˆ", +"Order Label" => "ラベルã§ä¸¦ã¹æ›¿ãˆ", +"Loading tasks..." => "タスクをãƒãƒ¼ãƒ‰ä¸...", +"Important" => "é‡è¦", +"More" => "詳細", +"Less" => "é–‰ã˜ã‚‹", +"Delete" => "削除" +); diff --git a/apps/tasks/l10n/lt_LT.php b/apps/tasks/l10n/lt_LT.php new file mode 100644 index 00000000000..7c17fc22242 --- /dev/null +++ b/apps/tasks/l10n/lt_LT.php @@ -0,0 +1,10 @@ +<?php $TRANSLATIONS = array( +"Invalid date/time" => "Netinkama data/laikas", +"No category" => "Be kategorijos", +"Empty Summary" => "TuÅ¡Äias apraÅ¡ymas", +"Invalid percent complete" => "Netinkamas baigimo procentas", +"Important" => "SvarbÅ«s", +"More" => "Daugiau", +"Less" => "Mažiau", +"Delete" => "IÅ¡trinti" +); diff --git a/apps/tasks/l10n/nb_NO.php b/apps/tasks/l10n/nb_NO.php new file mode 100644 index 00000000000..a6b398857b0 --- /dev/null +++ b/apps/tasks/l10n/nb_NO.php @@ -0,0 +1,17 @@ +<?php $TRANSLATIONS = array( +"Invalid date/time" => "feil i dato/klokkeslett", +"Tasks" => "Oppgaver", +"No category" => "Ingen kategori", +"Unspecified" => "Uspesifisert", +"1=highest" => "1=høyest", +"5=medium" => "5=middels", +"9=lowest" => "9=lavest", +"Invalid percent complete" => "Feil i prosent fullført", +"Invalid priority" => "Ulovlig prioritet", +"Add Task" => "Legg til oppgave", +"Loading tasks..." => "Henter oppgaver...", +"Important" => "Viktig", +"More" => "Mer", +"Less" => "Mindre", +"Delete" => "Slett" +); diff --git a/apps/tasks/l10n/pl.php b/apps/tasks/l10n/pl.php new file mode 100644 index 00000000000..48796284869 --- /dev/null +++ b/apps/tasks/l10n/pl.php @@ -0,0 +1,24 @@ +<?php $TRANSLATIONS = array( +"Invalid date/time" => "ZÅ‚a data/czas", +"Tasks" => "Zadania", +"No category" => "Brak kategorii", +"Unspecified" => "NieokreÅ›lona", +"1=highest" => "1=najwyższy", +"5=medium" => "5=Å›redni", +"9=lowest" => "9=maÅ‚o ważny ", +"Empty Summary" => "Podsumowanie puste", +"Invalid percent complete" => "NieprawidÅ‚owy procent wykonania", +"Invalid priority" => "NieprawidÅ‚owy priorytet", +"Add Task" => "Dodaj zadanie", +"Order Due" => "Kolejność - domyÅ›lna", +"Order List" => "Kolejność - wg lista", +"Order Complete" => "Kolejność - wg kompletnoÅ›ci", +"Order Location" => "Kolejność - wg lokalizacja", +"Order Priority" => "Kolejność - wg priorytetu", +"Order Label" => "Kolejność - wg nazywy", +"Loading tasks..." => "ÅadujÄ™ zadania", +"Important" => "Ważne", +"More" => "WiÄ™cej", +"Less" => "Mniej", +"Delete" => "UsuÅ„" +); diff --git a/apps/tasks/l10n/ro.php b/apps/tasks/l10n/ro.php new file mode 100644 index 00000000000..54958582f5b --- /dev/null +++ b/apps/tasks/l10n/ro.php @@ -0,0 +1,24 @@ +<?php $TRANSLATIONS = array( +"Invalid date/time" => "Data/timpul invalid", +"Tasks" => "Sarcini", +"No category" => "Fără categorie", +"Unspecified" => "Nespecificat", +"1=highest" => "1=cel mai înalt", +"5=medium" => "5=mediu", +"9=lowest" => "9=cel mai jos", +"Empty Summary" => "Rezumat gol", +"Invalid percent complete" => "Completare procentuală greÈ™ită", +"Invalid priority" => "Prioritare greÈ™ită", +"Add Task" => "Adaugă sarcină", +"Order Due" => "Comandă până la", +"Order List" => "Lista de comenzi", +"Order Complete" => "Comandă executată", +"Order Location" => "LocaÈ›ia comenzii", +"Order Priority" => "Prioritarea comenzii", +"Order Label" => "Eticheta comenzii", +"Loading tasks..." => "ÃŽncărcare sarcini", +"Important" => "Important", +"More" => "Mai mult", +"Less" => "Mai puÈ›in", +"Delete" => "Șterge" +); diff --git a/apps/tasks/l10n/sl.php b/apps/tasks/l10n/sl.php new file mode 100644 index 00000000000..c235ff0bc47 --- /dev/null +++ b/apps/tasks/l10n/sl.php @@ -0,0 +1,24 @@ +<?php $TRANSLATIONS = array( +"Invalid date/time" => "Neveljaven datum/Äas", +"Tasks" => "Opravila", +"No category" => "Ni kategorije", +"Unspecified" => "NedoloÄen", +"1=highest" => "1=najviÅ¡je", +"5=medium" => "5=srednje", +"9=lowest" => "9=najnižje", +"Empty Summary" => "Prazen povzetek", +"Invalid percent complete" => "Neveljaven odstotek dokonÄanja", +"Invalid priority" => "Neveljavna prednost", +"Add Task" => "Dodaj opravilo", +"Order Due" => "Razvrsti po roku", +"Order List" => "Razvrsti v seznam", +"Order Complete" => "Razvrsti po zakljuÄenosti", +"Order Location" => "Razvrsti po lokacijah", +"Order Priority" => "Razvrsti po prednosti", +"Order Label" => "Razvrsti po oznakah", +"Loading tasks..." => "Nalagam opravila...", +"Important" => "Pomembno", +"More" => "VeÄ", +"Less" => "Manj", +"Delete" => "IzbriÅ¡i" +); diff --git a/apps/tasks/l10n/sv.php b/apps/tasks/l10n/sv.php new file mode 100644 index 00000000000..33bab14448f --- /dev/null +++ b/apps/tasks/l10n/sv.php @@ -0,0 +1,24 @@ +<?php $TRANSLATIONS = array( +"Invalid date/time" => "Felaktigt datum/tid", +"Tasks" => "Uppgifter", +"No category" => "Ingen kategori", +"Unspecified" => "Ospecificerad ", +"1=highest" => "1=högsta", +"5=medium" => "5=mellan", +"9=lowest" => "9=lägsta", +"Empty Summary" => "Tom sammanfattning", +"Invalid percent complete" => "Ogiltig andel procent klar", +"Invalid priority" => "Felaktig prioritet", +"Add Task" => "Lägg till uppgift", +"Order Due" => "Förfaller", +"Order List" => "Kategori", +"Order Complete" => "Slutförd", +"Order Location" => "Plats", +"Order Priority" => "Prioritet", +"Order Label" => "Etikett", +"Loading tasks..." => "Laddar uppgifter...", +"Important" => "Viktigt", +"More" => "Mer", +"Less" => "Mindre", +"Delete" => "Radera" +); diff --git a/apps/tasks/l10n/th_TH.php b/apps/tasks/l10n/th_TH.php new file mode 100644 index 00000000000..e4212bf291c --- /dev/null +++ b/apps/tasks/l10n/th_TH.php @@ -0,0 +1,24 @@ +<?php $TRANSLATIONS = array( +"Invalid date/time" => "วันที่ / เวลา ไม่ถูà¸à¸•à¹‰à¸à¸‡", +"Tasks" => "งาน", +"No category" => "ไม่มีหมวดหมู่", +"Unspecified" => "ยังไม่ได้ระบุ", +"1=highest" => "1=สูงสุด", +"5=medium" => "5=ปานà¸à¸¥à¸²à¸‡", +"9=lowest" => "9=ต่ำสุด", +"Empty Summary" => "ข้à¸à¸¡à¸¹à¸¥à¸ªà¸£à¸¸à¸›à¸¢à¸±à¸‡à¸§à¹ˆà¸²à¸‡à¸à¸¢à¸¹à¹ˆ", +"Invalid percent complete" => "สัดส่วนเปà¸à¸£à¹Œà¹€à¸‹à¹‡à¸™à¸•à¹Œà¸„วามสมบูรณ์ไม่ถูà¸à¸•à¹‰à¸à¸‡", +"Invalid priority" => "ความสำคัà¸à¹„ม่ถูà¸à¸•à¹‰à¸à¸‡", +"Add Task" => "เพิ่มงานใหม่", +"Order Due" => "จัดเรียงตามà¸à¸³à¸«à¸™à¸”เวลา", +"Order List" => "จัดเรียงตามรายชื่à¸", +"Order Complete" => "จัดเรียงตามความสมบูรณ์", +"Order Location" => "จัดเรียงตามตำà¹à¸«à¸™à¹ˆà¸‡à¸—ี่à¸à¸¢à¸¹à¹ˆ", +"Order Priority" => "จัดเรียงตามระดับความสำคัà¸", +"Order Label" => "จัดเรียงตามป้ายชื่à¸", +"Loading tasks..." => "à¸à¸³à¸¥à¸±à¸‡à¹‚หลดข้à¸à¸¡à¸¹à¸¥à¸‡à¸²à¸™...", +"Important" => "สำคัà¸", +"More" => "มาà¸", +"Less" => "น้à¸à¸¢", +"Delete" => "ลบ" +); diff --git a/apps/tasks/lib/app.php b/apps/tasks/lib/app.php index 7b908420333..a97c6b95d1d 100644 --- a/apps/tasks/lib/app.php +++ b/apps/tasks/lib/app.php @@ -77,24 +77,24 @@ class OC_Task_App { public static function validateRequest($request) { $errors = array(); - if($request['summary'] == ''){ + if($request['summary'] == '') { $errors['summary'] = self::$l10n->t('Empty Summary'); } try { - $timezone = OCP\Config::getUserValue(OCP\User::getUser(), "calendar", "timezone", "Europe/London"); + $timezone = OC_Calendar_App::getTimezone(); $timezone = new DateTimeZone($timezone); new DateTime($request['due'], $timezone); } catch (Exception $e) { $errors['due'] = self::$l10n->t('Invalid date/time'); } - if ($request['percent_complete'] < 0 || $request['percent_complete'] > 100){ + if ($request['percent_complete'] < 0 || $request['percent_complete'] > 100) { $errors['percent_complete'] = self::$l10n->t('Invalid percent complete'); } - if ($request['percent_complete'] == 100 && !empty($request['completed'])){ + if ($request['percent_complete'] == 100 && !empty($request['completed'])) { try { - $timezone = OCP\Config::getUserValue(OCP\User::getUser(), "calendar", "timezone", "Europe/London"); + $timezone = OC_Calendar_App::getTimezone(); $timezone = new DateTimeZone($timezone); new DateTime($request['completed'], $timezone); } catch (Exception $e) { @@ -147,7 +147,7 @@ class OC_Task_App { $vtodo->setString('PRIORITY', $priority); if ($due) { - $timezone = OCP\Config::getUserValue(OCP\User::getUser(), 'calendar', 'timezone', date_default_timezone_get()); + $timezone = OC_Calendar_App::getTimezone(); $timezone = new DateTimeZone($timezone); $due = new DateTime($due, $timezone); $vtodo->setDateTime('DUE', $due); @@ -168,18 +168,19 @@ class OC_Task_App { $vtodo->__unset('PERCENT-COMPLETE'); } - if ($percent_complete == 100){ - if (!$completed){ + if ($percent_complete == 100) { + if (!$completed) { $completed = 'now'; } } else { $completed = null; } if ($completed) { - $timezone = OCP\Config::getUserValue(OCP\User::getUser(), 'calendar', 'timezone', date_default_timezone_get()); + $timezone = OC_Calendar_App::getTimezone(); $timezone = new DateTimeZone($timezone); $completed = new DateTime($completed, $timezone); $vtodo->setDateTime('COMPLETED', $completed); + OCP\Util::emitHook('OC_Task', 'taskCompleted', $vtodo); } else { unset($vtodo->COMPLETED); } diff --git a/apps/tasks/templates/part.addtaskform.php b/apps/tasks/templates/part.addtaskform.php deleted file mode 100644 index 0fad5592aa7..00000000000 --- a/apps/tasks/templates/part.addtaskform.php +++ /dev/null @@ -1,15 +0,0 @@ -<form id="tasks_addtaskform"> - <?php if(count($_['calendars'])==1): ?> - <input type="hidden" name="id" value="<?php echo $_['calendars'][0]['id']; ?>"> - <?php else: ?> - <label for="id"><?php echo $l->t('Calendar'); ?></label> - <select name="id" size="1"> - <?php foreach($_['calendars'] as $calendar): ?> - <option value="<?php echo $calendar['id']; ?>"><?php echo $calendar['displayname']; ?></option> - <?php endforeach; ?> - </select> - <br> - <?php endif; ?> - <?php echo $this->inc('part.taskform'); ?> - <input type="submit" name="submit" value="<?php echo $l->t('Create Task'); ?>"> -</form> diff --git a/apps/tasks/templates/part.details.php b/apps/tasks/templates/part.details.php deleted file mode 100644 index 89636b6e762..00000000000 --- a/apps/tasks/templates/part.details.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php if(isset($_['details']->SUMMARY)): ?> -<table> -<?php -echo $this->inc('part.property', array('label' => $l->t('Summary'), 'property' => $_['details']->SUMMARY)); -if(isset($_['details']->LOCATION)): - echo $this->inc('part.property', array('label' => $l->t('Location'), 'property' => $_['details']->LOCATION)); -endif; -if(isset($_['details']->CATEGORIES)): - echo $this->inc('part.property', array('label' => $l->t('Categories'), 'property' => $_['details']->CATEGORIES)); -endif; -if(isset($_['details']->DUE)): - echo $this->inc('part.property', array('label' => $l->t('Due'), 'property' => $_['details']->DUE[0])); -endif; -if(isset($_['details']->PRIORITY)): - echo $this->inc('part.property', array('label' => $l->t('Priority'), 'property' => $_['details']->PRIORITY[0], 'options' => $_['priority_options'])); -endif; -if($_['details']->__isset('PERCENT-COMPLETE') || isset($_['details']->COMPLETED)): -?> -<tr> - <th> - <?php echo $l->t('Complete') ?> - </th> - <td> -<?php if($_['details']->__isset('PERCENT-COMPLETE')): - echo $_['details']->__get('PERCENT-COMPLETE')->value.' % '; - endif; - if(isset($_['details']->COMPLETED)): - echo $l->t('on '). $l->l('datetime', $_['details']->COMPLETED[0]->getDateTime()); - endif; - echo '</tr>'; -endif; -if(isset($_['details']->DESCRIPTION)): - echo $this->inc('part.property', array('label' => $l->t('Description'), 'property' => $_['details']->DESCRIPTION)); -endif; ?> -</table> -<form> - <input type="button" id="tasks_delete" value="<?php echo $l->t('Delete');?>"> - <input type="button" id="tasks_edit" value="<?php echo $l->t('Edit');?>"> -</form> -<?php else: ?> -<?php //var_dump($_['details']); ?> -<?php endif ?> diff --git a/apps/tasks/templates/part.edittaskform.php b/apps/tasks/templates/part.edittaskform.php deleted file mode 100644 index fe123f07ac6..00000000000 --- a/apps/tasks/templates/part.edittaskform.php +++ /dev/null @@ -1,5 +0,0 @@ -<form id="tasks_edittaskform"> - <input type="hidden" name="id" value="<?php echo $_['id']; ?>"> - <?php echo $this->inc('part.taskform'); ?> - <input type="submit" name="submit" value="<?php echo $l->t('Update Task'); ?>"> -</form> diff --git a/apps/tasks/templates/part.property.php b/apps/tasks/templates/part.property.php deleted file mode 100644 index 591fd363e6f..00000000000 --- a/apps/tasks/templates/part.property.php +++ /dev/null @@ -1,22 +0,0 @@ -<tr> - <th> - <?php echo $_['label'] ?> - </th> - <td> - <?php - switch (get_class($_['property'])) - { - case 'Sabre_VObject_Element_DateTime': - echo $l->l('datetime', $_['property']->getDateTime()); - break; - default: - $value = $_['property']->value; - if (isset($_['options'])) - { - $value = $_['options'][$value]; - } - echo nl2br($value); - } - ?> - </td> -</tr> diff --git a/apps/tasks/templates/part.taskform.php b/apps/tasks/templates/part.taskform.php deleted file mode 100644 index c00560903b0..00000000000 --- a/apps/tasks/templates/part.taskform.php +++ /dev/null @@ -1,36 +0,0 @@ - <label for="summary"><?php echo $l->t('Summary'); ?></label> - <input type="text" id="summary" name="summary" placeholder="<?php echo $l->t('Summary of the task');?>" value="<?php echo isset($_['details']->SUMMARY) ? $_['details']->SUMMARY[0]->value : '' ?>"> - <br> - <label for="location"><?php echo $l->t('Location'); ?></label> - <input type="text" id="location" name="location" placeholder="<?php echo $l->t('Location of the task');?>" value="<?php echo isset($_['details']->LOCATION) ? $_['details']->LOCATION[0]->value : '' ?>"> - <br> - <label for="categories"><?php echo $l->t('Categories'); ?></label> - <input id="categories" name="categories" type="text" placeholder="<?php echo $l->t('Separate categories with commas'); ?>" value="<?php echo isset($_['categories']) ? htmlspecialchars($_['categories']) : '' ?>"> - <a class="action edit" onclick="$(this).tipsy('hide');OCCategories.edit();" title="<?php echo $l->t('Edit categories'); ?>"><img alt="<?php echo $l->t('Edit categories'); ?>" src="<?php echo OCP\image_path('core','actions/rename.svg')?>" class="svg action" style="width: 16px; height: 16px;"></a> - <br> - <label for="due"><?php echo $l->t('Due'); ?></label> - <input type="text" id="due" name="due" placeholder="<?php echo $l->t('Due date') ?>" value="<?php echo isset($_['details']->DUE) ? $l->l('datetime', $_['details']->DUE[0]->getDateTime()) : '' ?>"> - <br> - <select name="percent_complete" id="percent_complete"> - <?php - foreach($_['percent_options'] as $percent){ - echo '<option value="' . $percent . '"' . (($_['details']->__get('PERCENT-COMPLETE') && $percent == $_['details']->__get('PERCENT-COMPLETE')->value) ? ' selected="selected"' : '') . '>' . $percent . ' %</option>'; - } - ?> - </select> - <label for="percent_complete"><?php echo $l->t('Complete'); ?></label> - <span id="complete"<?php echo ($_['details']->__get('PERCENT-COMPLETE') && $_['details']->__get('PERCENT-COMPLETE')->value == 100) ? '' : ' style="display:none;"' ?>><label for="completed"><?php echo $l->t('completed on'); ?></label> - <input type="text" id="completed" name="completed" value="<?php echo isset($_['details']->COMPLETED) ? $l->l('datetime', $_['details']->COMPLETED[0]->getDateTime()) : '' ?>"></span> - <br> - <label for="priority"><?php echo $l->t('Priority'); ?></label> - <select name="priority"> - <?php - foreach($_['priority_options'] as $priority => $label){ - echo '<option value="' . $priority . '"' . ((isset($_['details']->PRIORITY) && $priority == $_['details']->PRIORITY->value) ? ' selected="selected"' : '') . '>' . $label . '</option>'; - } - ?> - </select> - <br> - <label for="description"><?php echo $l->t('Description'); ?></label><br> - <textarea placeholder="<?php echo $l->t('Description of the task');?>" name="description"><?php echo isset($_['details']->DESCRIPTION) ? $_['details']->DESCRIPTION[0]->value : '' ?></textarea> - <br> diff --git a/apps/tasks/templates/part.tasks.php b/apps/tasks/templates/part.tasks.php deleted file mode 100644 index 50be1cd6bed..00000000000 --- a/apps/tasks/templates/part.tasks.php +++ /dev/null @@ -1,3 +0,0 @@ -<?php foreach( $_['tasks'] as $task ): ?> - <li data-id="<?php echo $task['id']; ?>"><a href="index.php?id=<?php echo $task['id']; ?>"><?php echo $task['name']; ?></a> </li> -<?php endforeach; ?> diff --git a/apps/user_external/appinfo/app.php b/apps/user_external/appinfo/app.php new file mode 100644 index 00000000000..c7408ec30d9 --- /dev/null +++ b/apps/user_external/appinfo/app.php @@ -0,0 +1,4 @@ +<?php +OC::$CLASSPATH['OC_User_IMAP']='apps/user_external/lib/imap.php'; +OC::$CLASSPATH['OC_User_SMB']='apps/user_external/lib/smb.php'; +OC::$CLASSPATH['OC_User_FTP']='apps/user_external/lib/ftp.php'; diff --git a/apps/user_external/appinfo/info.xml b/apps/user_external/appinfo/info.xml new file mode 100644 index 00000000000..1d1dcee5401 --- /dev/null +++ b/apps/user_external/appinfo/info.xml @@ -0,0 +1,13 @@ +<?xml version="1.0"?> +<info> + <id>user_external</id> + <name>External user support</name> + <description>Use external user authentication methods</description> + <licence>AGPL</licence> + <author>Robin Appelman</author> + <require>4</require> + <shipped>true</shipped> + <types> + <authentication/> + </types> +</info> diff --git a/apps/user_external/appinfo/version b/apps/user_external/appinfo/version new file mode 100644 index 00000000000..ceab6e11ece --- /dev/null +++ b/apps/user_external/appinfo/version @@ -0,0 +1 @@ +0.1
\ No newline at end of file diff --git a/apps/user_external/lib/ftp.php b/apps/user_external/lib/ftp.php new file mode 100644 index 00000000000..e03e17d2b6a --- /dev/null +++ b/apps/user_external/lib/ftp.php @@ -0,0 +1,45 @@ +<?php +/** + * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_User_FTP extends OC_User_Backend{ + private $host; + private $secure; + private $protocol; + + public function __construct($host,$secure=false){ + $this->host=$host; + $this->secure=$secure; + $this->protocol='ftp'; + if($this->secure){ + $this->protocol.='s'; + } + $this->protocol.='://'; + } + + /** + * @brief Check if the password is correct + * @param $uid The username + * @param $password The password + * @returns true/false + * + * Check if the password is correct without logging in the user + */ + public function checkPassword($uid, $password){ + $url=$this->protocol.$uid.':'.$password.'@'.$this->host.'/'; + $result=@opendir($url); + if(is_resource($result)){ + return $uid; + }else{ + return false; + } + } + + public function userExists($uid){ + return true; + } +} diff --git a/apps/user_external/lib/imap.php b/apps/user_external/lib/imap.php new file mode 100644 index 00000000000..584e9804b18 --- /dev/null +++ b/apps/user_external/lib/imap.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_User_IMAP extends OC_User_Backend{ + private $mailbox; + + public function __construct($mailbox){ + $this->mailbox=$mailbox; + } + + /** + * @brief Check if the password is correct + * @param $uid The username + * @param $password The password + * @returns true/false + * + * Check if the password is correct without logging in the user + */ + public function checkPassword($uid, $password){ + $mbox = @imap_open($this->mailbox, $uid, $password); + imap_errors(); + imap_alerts(); + if($mbox){ + imap_close($mbox); + return $uid; + }else{ + return false; + } + } + + public function userExists($uid){ + return true; + } +} + diff --git a/apps/user_external/lib/smb.php b/apps/user_external/lib/smb.php new file mode 100644 index 00000000000..44d2b7903d8 --- /dev/null +++ b/apps/user_external/lib/smb.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_User_SMB extends OC_User_Backend{ + private $host; + + const smbclient='smbclient'; + const loginError='NT_STATUS_LOGON_FAILURE'; + + public function __construct($host){ + $this->host=$host; + } + + /** + * @brief Check if the password is correct + * @param $uid The username + * @param $password The password + * @returns true/false + * + * Check if the password is correct without logging in the user + */ + public function checkPassword($uid, $password){ + $uidEscaped=escapeshellarg($uid); + $password=escapeshellarg($password); + $result=array(); + $command=self::smbclient.' //'.$this->host.'/dummy -U'.$uidEscaped.'%'.$password; + $result=exec($command,$result); + if(substr($result,-strlen(self::loginError))==self::loginError){ + return false; + }else{ + return $uid; + } + } + + public function userExists($uid){ + return true; + } +}
\ No newline at end of file diff --git a/apps/user_external/tests/config.php b/apps/user_external/tests/config.php new file mode 100644 index 00000000000..64ee141d32d --- /dev/null +++ b/apps/user_external/tests/config.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +return array( + 'imap'=>array( + 'run'=>false, + 'mailbox'=>'{imap.gmail.com:993/imap/ssl}INBOX', //see http://php.net/manual/en/function.imap-open.php + 'user'=>'foo',//valid username/password combination + 'password'=>'bar', + ), + 'smb'=>array( + 'run'=>false, + 'host'=>'localhost', + 'user'=>'test',//valid username/password combination + 'password'=>'test', + ), + 'ftp'=>array( + 'run'=>false, + 'host'=>'localhost', + 'user'=>'test',//valid username/password combination + 'password'=>'test', + ), +); diff --git a/apps/user_external/tests/ftp.php b/apps/user_external/tests/ftp.php new file mode 100644 index 00000000000..0cf7565f9c6 --- /dev/null +++ b/apps/user_external/tests/ftp.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class Test_User_FTP extends UnitTestCase{ + /** + * @var OC_User_IMAP $instance + */ + private $instance; + + private function getConfig(){ + return include(__DIR__.'/config.php'); + } + + function skip(){ + $config=$this->getConfig(); + $this->skipUnless($config['ftp']['run']); + } + + function setUp(){ + $config=$this->getConfig(); + $this->instance=new OC_User_FTP($config['ftp']['host']); + } + + function testLogin(){ + $config=$this->getConfig(); + $this->assertEqual($config['ftp']['user'],$this->instance->checkPassword($config['ftp']['user'],$config['ftp']['password'])); + $this->assertFalse($this->instance->checkPassword($config['ftp']['user'],$config['ftp']['password'].'foo')); + } +} diff --git a/apps/user_external/tests/imap.php b/apps/user_external/tests/imap.php new file mode 100644 index 00000000000..c703b32107f --- /dev/null +++ b/apps/user_external/tests/imap.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class Test_User_Imap extends UnitTestCase{ + /** + * @var OC_User_IMAP $instance + */ + private $instance; + + private function getConfig(){ + return include(__DIR__.'/config.php'); + } + + function skip(){ + $config=$this->getConfig(); + $this->skipUnless($config['imap']['run']); + } + + function setUp(){ + $config=$this->getConfig(); + $this->instance=new OC_User_IMAP($config['imap']['mailbox']); + } + + function testLogin(){ + $config=$this->getConfig(); + $this->assertEqual($config['imap']['user'],$this->instance->checkPassword($config['imap']['user'],$config['imap']['password'])); + $this->assertFalse($this->instance->checkPassword($config['imap']['user'],$config['imap']['password'].'foo')); + } +} diff --git a/apps/user_external/tests/smb.php b/apps/user_external/tests/smb.php new file mode 100644 index 00000000000..1ed7eb934be --- /dev/null +++ b/apps/user_external/tests/smb.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class Test_User_SMB extends UnitTestCase{ + /** + * @var OC_User_IMAP $instance + */ + private $instance; + + private function getConfig(){ + return include(__DIR__.'/config.php'); + } + + function skip(){ + $config=$this->getConfig(); + $this->skipUnless($config['smb']['run']); + } + + function setUp(){ + $config=$this->getConfig(); + $this->instance=new OC_User_SMB($config['smb']['host']); + } + + function testLogin(){ + $config=$this->getConfig(); + $this->assertEqual($config['smb']['user'],$this->instance->checkPassword($config['smb']['user'],$config['smb']['password'])); + $this->assertFalse($this->instance->checkPassword($config['smb']['user'],$config['smb']['password'].'foo')); + } +} diff --git a/apps/user_ldap/ajax/testConfiguration.php b/apps/user_ldap/ajax/testConfiguration.php new file mode 100644 index 00000000000..a82f7e4c17b --- /dev/null +++ b/apps/user_ldap/ajax/testConfiguration.php @@ -0,0 +1,39 @@ +<?php + +/** + * ownCloud - user_ldap + * + * @author Arthur Schiwon + * @copyright 2012 Arthur Schiwon blizzz@owncloud.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +// Check user and app status +OCP\JSON::checkAdminUser(); +OCP\JSON::checkAppEnabled('user_ldap'); +OCP\JSON::callCheck(); + +$connection = new \OCA\user_ldap\lib\Connection(null); +if($connection->setConfiguration($_POST)) { + //Configuration is okay + if($connection->bind()) { + OCP\JSON::success(array('message' => 'The configuration is valid and the connection could be established!')); + } else { + OCP\JSON::error(array('message' => 'The configuration is valid, but the Bind failed. Please check the server settings and credentials.')); + } +} else { + OCP\JSON::error(array('message' => 'The configuration is invalid. Please look in the ownCloud log for further details.')); +} diff --git a/apps/user_ldap/appinfo/app.php b/apps/user_ldap/appinfo/app.php index 330574c1d42..0eec7829a4a 100644 --- a/apps/user_ldap/appinfo/app.php +++ b/apps/user_ldap/appinfo/app.php @@ -21,15 +21,17 @@ * */ -require_once('apps/user_ldap/lib_ldap.php'); -require_once('apps/user_ldap/user_ldap.php'); -require_once('apps/user_ldap/group_ldap.php'); +OCP\App::registerAdmin('user_ldap', 'settings'); -OCP\App::registerAdmin('user_ldap','settings'); +$connector = new OCA\user_ldap\lib\Connection('user_ldap'); +$userBackend = new OCA\user_ldap\USER_LDAP(); +$userBackend->setConnector($connector); +$groupBackend = new OCA\user_ldap\GROUP_LDAP(); +$groupBackend->setConnector($connector); // register user backend -OC_User::useBackend( 'LDAP' ); -OC_Group::useBackend( new OC_GROUP_LDAP() ); +OC_User::useBackend($userBackend); +OC_Group::useBackend($groupBackend); // add settings page to navigation $entry = array( @@ -38,3 +40,5 @@ $entry = array( 'href' => OCP\Util::linkTo( 'user_ldap', 'settings.php' ), 'name' => 'LDAP' ); + +OCP\Backgroundjob::addRegularTask('OCA\user_ldap\lib\Jobs', 'updateGroups'); diff --git a/apps/user_ldap/appinfo/database.xml b/apps/user_ldap/appinfo/database.xml index b228fa2796d..a785bbf4221 100644 --- a/apps/user_ldap/appinfo/database.xml +++ b/apps/user_ldap/appinfo/database.xml @@ -28,6 +28,14 @@ <default></default> </field> + <field> + <name>directory_uuid</name> + <type>text</type> + <notnull>true</notnull> + <length>255</length> + <default></default> + </field> + <index> <name>ldap_dn_users</name> <unique>true</unique> @@ -71,6 +79,14 @@ <default></default> </field> + <field> + <name>directory_uuid</name> + <type>text</type> + <notnull>true</notnull> + <length>255</length> + <default></default> + </field> + <index> <name>ldap_dn_groups</name> <unique>true</unique> @@ -92,4 +108,37 @@ </table> + + <table> + + <name>*dbprefix*ldap_group_members</name> + + <declaration> + + <field> + <name>owncloudname</name> + <type>text</type> + <notnull>true</notnull> + <length>255</length> + <default></default> + </field> + + <field> + <name>owncloudusers</name> + <type>clob</type> + <notnull>true</notnull> + </field> + + <index> + <name>ldap_group_members</name> + <unique>true</unique> + <field> + <name>owncloudname</name> + </field> + </index> + + </declaration> + + </table> + </database>
\ No newline at end of file diff --git a/apps/user_ldap/appinfo/update.php b/apps/user_ldap/appinfo/update.php index 3ebb19c2fca..9cf1814cf6f 100644 --- a/apps/user_ldap/appinfo/update.php +++ b/apps/user_ldap/appinfo/update.php @@ -2,6 +2,11 @@ //from version 0.1 to 0.2 +//ATTENTION +//Upgrade from ownCloud 3 (LDAP backend 0.1) to ownCloud 4.5 (LDAP backend 0.3) is not supported!! +//You must do upgrade to ownCloud 4.0 first! +//The upgrade stuff in the section from 0.1 to 0.2 is just to minimize the bad efffects. + //settings $pw = OCP\Config::getAppValue('user_ldap', 'ldap_password'); if(!is_null($pw)) { @@ -12,25 +17,37 @@ if(!is_null($pw)) { //detect if we can switch on naming guidelines. We won't do it on conflicts. //it's a bit spaghetti, but hey. -$state = OCP\Config::getSystemValue('ldapIgnoreNamingRules', 'doCheck'); -if($state == 'doCheck'){ - $sqlCleanMap = 'DELETE FROM `*PREFIX*ldap_user_mapping`'; - - require_once(OC::$APPSROOT.'/apps/user_ldap/lib_ldap.php'); - require_once(OC::$APPSROOT.'/apps/user_ldap/user_ldap.php'); - - OCP\Config::setSystemValue('ldapIgnoreNamingRules', true); - $LDAP_USER = new OC_USER_LDAP(); - $users_old = $LDAP_USER->getUsers(); - $query = OCP\DB::prepare($sqlCleanMap); - $query->execute(); +$state = OCP\Config::getSystemValue('ldapIgnoreNamingRules', 'unset'); +if($state == 'unset'){ OCP\Config::setSystemValue('ldapIgnoreNamingRules', false); - OC_LDAP::init(true); - $users_new = $LDAP_USER->getUsers(); - $query = OCP\DB::prepare($sqlCleanMap); - $query->execute(); - if($users_old !== $users_new) { - //we don't need to check Groups, because they were not supported in 3' - OCP\Config::setSystemValue('ldapIgnoreNamingRules', true); +} + +// ### SUPPORTED upgrade path starts here ### + +//from version 0.2 to 0.3 (0.2.0.x dev version) +$objects = array('user', 'group'); + +$connector = new \OCA\user_ldap\lib\Connection('user_ldap'); +$userBE = new \OCA\user_ldap\USER_LDAP(); +$userBE->setConnector($connector); +$groupBE = new \OCA\user_ldap\GROUP_LDAP(); +$groupBE->setConnector($connector); + +foreach($objects as $object) { + $fetchDNSql = 'SELECT ldap_dn from *PREFIX*ldap_'.$object.'_mapping'; + $updateSql = 'UPDATE *PREFIX*ldap_'.$object.'_mapping SET ldap_DN = ?, directory_uuid = ? WHERE ldap_dn = ?'; + + $query = OCP\DB::prepare($fetchDNSql); + $res = $query->execute(); + $DNs = $res->fetchAll(); + $updateQuery = OCP\DB::prepare($updateSql); + foreach($DNs as $dn) { + $newDN = mb_strtolower($dn['ldap_dn'], 'UTF-8'); + if($object == 'user') { + $uuid = $userBE->getUUID($newDN); + } else { + $uuid = $groupBE->getUUID($newDN); + } + $updateQuery->execute(array($newDN, $uuid, $dn['ldap_dn'])); } -}
\ No newline at end of file +} diff --git a/apps/user_ldap/appinfo/version b/apps/user_ldap/appinfo/version index 2f4536184bc..e689e4949ef 100644 --- a/apps/user_ldap/appinfo/version +++ b/apps/user_ldap/appinfo/version @@ -1 +1 @@ -0.2
\ No newline at end of file +0.2.0.26
\ No newline at end of file diff --git a/apps/user_ldap/css/settings.css b/apps/user_ldap/css/settings.css new file mode 100644 index 00000000000..30c5c175c9b --- /dev/null +++ b/apps/user_ldap/css/settings.css @@ -0,0 +1,10 @@ +#ldap fieldset p label { + width: 20%; + max-width: 200px; + display: inline-block; +} + +#ldap fieldset input { + width: 70%; + display: inline-block; +}
\ No newline at end of file diff --git a/apps/user_ldap/group_ldap.php b/apps/user_ldap/group_ldap.php index a3117b5a41e..b29ebe30c51 100644 --- a/apps/user_ldap/group_ldap.php +++ b/apps/user_ldap/group_ldap.php @@ -21,24 +21,17 @@ * */ -class OC_GROUP_LDAP extends OC_Group_Backend { -// //group specific settings - protected $ldapGroupFilter; - protected $ldapGroupMemberAssocAttr; - protected $configured = false; +namespace OCA\user_ldap; - protected $_group_user = array(); - protected $_user_groups = array(); - protected $_group_users = array(); - protected $_groups = array(); +class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { + protected $enabled = false; - public function __construct() { - $this->ldapGroupFilter = OCP\Config::getAppValue('user_ldap', 'ldap_group_filter', '(objectClass=posixGroup)'); - $this->ldapGroupMemberAssocAttr = OCP\Config::getAppValue('user_ldap', 'ldap_group_member_assoc_attribute', 'uniqueMember'); - - if(!empty($this->ldapGroupFilter) && !empty($this->ldapGroupMemberAssocAttr)) { - $this->configured = true; + public function setConnector(lib\Connection &$connection) { + parent::setConnector($connection); + if(empty($this->connection->ldapGroupFilter) || empty($this->connection->ldapGroupMemberAssocAttr)) { + $this->enabled = false; } + $this->enabled = true; } /** @@ -50,31 +43,33 @@ class OC_GROUP_LDAP extends OC_Group_Backend { * Checks whether the user is member of a group or not. */ public function inGroup($uid, $gid) { - if(!$this->configured) { + if(!$this->enabled) { return false; } - if(isset($this->_group_user[$gid][$uid])) { - return $this->_group_user[$gid][$uid]; + if($this->connection->isCached('inGroup'.$uid.':'.$gid)) { + return $this->connection->getFromCache('inGroup'.$uid.':'.$gid); } - $dn_user = OC_LDAP::username2dn($uid); - $dn_group = OC_LDAP::groupname2dn($gid); + $dn_user = $this->username2dn($uid); + $dn_group = $this->groupname2dn($gid); // just in case if(!$dn_group || !$dn_user) { + $this->connection->writeToCache('inGroup'.$uid.':'.$gid, false); return false; } //usually, LDAP attributes are said to be case insensitive. But there are exceptions of course. - $members = OC_LDAP::readAttribute($dn_group, $this->ldapGroupMemberAssocAttr); + $members = $this->readAttribute($dn_group, $this->connection->ldapGroupMemberAssocAttr); if(!$members) { + $this->connection->writeToCache('inGroup'.$uid.':'.$gid, false); return false; } //extra work if we don't get back user DNs //TODO: this can be done with one LDAP query - if(strtolower($this->ldapGroupMemberAssocAttr) == 'memberuid') { + if(strtolower($this->connection->ldapGroupMemberAssocAttr) == 'memberuid') { $dns = array(); foreach($members as $mid) { - $filter = str_replace('%uid', $mid, OC_LDAP::conf('ldapLoginFilter')); - $ldap_users = OC_LDAP::fetchListOfUsers($filter, 'dn'); + $filter = str_replace('%uid', $mid, $this->connection->ldapLoginFilter); + $ldap_users = $this->fetchListOfUsers($filter, 'dn'); if(count($ldap_users) < 1) { continue; } @@ -83,8 +78,10 @@ class OC_GROUP_LDAP extends OC_Group_Backend { $members = $dns; } - $this->_group_user[$gid][$uid] = in_array($dn_user, $members); - return $this->_group_user[$gid][$uid]; + $isInGroup = in_array($dn_user, $members); + $this->connection->writeToCache('inGroup'.$uid.':'.$gid, $isInGroup); + + return $isInGroup; } /** @@ -96,86 +93,105 @@ class OC_GROUP_LDAP extends OC_Group_Backend { * if the user exists at all. */ public function getUserGroups($uid) { - if(!$this->configured) { + if(!$this->enabled) { return array(); } - if(isset($this->_user_groups[$uid])) { - return $this->_user_groups[$uid]; + if($this->connection->isCached('getUserGroups'.$uid)) { + return $this->connection->getFromCache('getUserGroups'.$uid); } - $userDN = OC_LDAP::username2dn($uid); + $userDN = $this->username2dn($uid); if(!$userDN) { - $this->_user_groups[$uid] = array(); + $this->connection->writeToCache('getUserGroups'.$uid, array()); return array(); } //uniqueMember takes DN, memberuid the uid, so we need to distinguish - if((strtolower($this->ldapGroupMemberAssocAttr) == 'uniquemember') - || (strtolower($this->ldapGroupMemberAssocAttr) == 'member')) { + if((strtolower($this->connection->ldapGroupMemberAssocAttr) == 'uniquemember') + || (strtolower($this->connection->ldapGroupMemberAssocAttr) == 'member') + ) { $uid = $userDN; - } else if(strtolower($this->ldapGroupMemberAssocAttr) == 'memberuid') { - $result = OC_LDAP::readAttribute($userDN, 'uid'); + } else if(strtolower($this->connection->ldapGroupMemberAssocAttr) == 'memberuid') { + $result = $this->readAttribute($userDN, 'uid'); $uid = $result[0]; } else { // just in case $uid = $userDN; } - $filter = OC_LDAP::combineFilterWithAnd(array( - $this->ldapGroupFilter, - $this->ldapGroupMemberAssocAttr.'='.$uid + $filter = $this->combineFilterWithAnd(array( + $this->connection->ldapGroupFilter, + $this->connection->ldapGroupMemberAssocAttr.'='.$uid )); - $groups = OC_LDAP::fetchListOfGroups($filter, array(OC_LDAP::conf('ldapGroupDisplayName'),'dn')); - $this->_user_groups[$uid] = array_unique(OC_LDAP::ownCloudGroupNames($groups), SORT_LOCALE_STRING); + $groups = $this->fetchListOfGroups($filter, array($this->connection->ldapGroupDisplayName,'dn')); + $groups = array_unique($this->ownCloudGroupNames($groups), SORT_LOCALE_STRING); + $this->connection->writeToCache('getUserGroups'.$uid, $groups); - return $this->_user_groups[$uid]; + return $groups; } /** * @brief get a list of all users in a group * @returns array with user ids */ - public function usersInGroup($gid) { - if(!$this->configured) { + public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) { + if(!$this->enabled) { return array(); } - if(isset($this->_group_users[$gid])) { - return $this->_group_users[$gid]; + $this->groupSearch = $search; + if($this->connection->isCached('usersInGroup'.$gid)) { + $groupUsers = $this->connection->getFromCache('usersInGroup'.$gid); + if(!empty($this->groupSearch)) { + $groupUsers = array_filter($groupUsers, array($this, 'groupMatchesFilter')); + } + if($limit = -1) { + $limit = null; + } + return array_slice($groupUsers, $offset, $limit); } - $groupDN = OC_LDAP::groupname2dn($gid); + $groupDN = $this->groupname2dn($gid); if(!$groupDN) { - $this->_group_users[$gid] = array(); + $this->connection->writeToCache('usersInGroup'.$gid, array()); return array(); } - $members = OC_LDAP::readAttribute($groupDN, $this->ldapGroupMemberAssocAttr); + $members = $this->readAttribute($groupDN, $this->connection->ldapGroupMemberAssocAttr); if(!$members) { - $this->_group_users[$gid] = array(); + $this->connection->writeToCache('usersInGroup'.$gid, array()); return array(); } $result = array(); - $isMemberUid = (strtolower($this->ldapGroupMemberAssocAttr) == 'memberuid'); + $isMemberUid = (strtolower($this->connection->ldapGroupMemberAssocAttr) == 'memberuid'); foreach($members as $member) { if($isMemberUid) { - $filter = str_replace('%uid', $member, OC_LDAP::conf('ldapLoginFilter')); - $ldap_users = OC_LDAP::fetchListOfUsers($filter, 'dn'); + $filter = \OCP\Util::mb_str_replace('%uid', $member, $this->connection->ldapLoginFilter, 'UTF-8'); + $ldap_users = $this->fetchListOfUsers($filter, 'dn'); if(count($ldap_users) < 1) { continue; } - $result[] = OC_LDAP::dn2username($ldap_users[0]); + $result[] = $this->dn2username($ldap_users[0]); continue; } else { - if($ocname = OC_LDAP::dn2username($member)){ + if($ocname = $this->dn2username($member)) { $result[] = $ocname; } } } if(!$isMemberUid) { - $result = array_intersect($result, OCP\User::getUsers()); + $result = array_intersect($result, \OCP\User::getUsers()); } - $this->_group_users[$gid] = array_unique($result, SORT_LOCALE_STRING); - return $this->_group_users[$gid]; + $groupUsers = array_unique($result, SORT_LOCALE_STRING); + $this->connection->writeToCache('usersInGroup'.$gid, $groupUsers); + + if(!empty($this->groupSearch)) { + $groupUsers = array_filter($groupUsers, array($this, 'groupMatchesFilter')); + } + if($limit = -1) { + $limit = null; + } + return array_slice($groupUsers, $offset, $limit); + } /** @@ -184,15 +200,30 @@ class OC_GROUP_LDAP extends OC_Group_Backend { * * Returns a list with all groups */ - public function getGroups() { - if(!$this->configured) { + public function getGroups($search = '', $limit = -1, $offset = 0) { + if(!$this->enabled) { return array(); } - if(empty($this->_groups)) { - $ldap_groups = OC_LDAP::fetchListOfGroups($this->ldapGroupFilter, array(OC_LDAP::conf('ldapGroupDisplayName'), 'dn')); - $this->_groups = OC_LDAP::ownCloudGroupNames($ldap_groups); + + if($this->connection->isCached('getGroups')) { + $ldap_groups = $this->connection->getFromCache('getGroups'); + } else { + $ldap_groups = $this->fetchListOfGroups($this->connection->ldapGroupFilter, array($this->connection->ldapGroupDisplayName, 'dn')); + $ldap_groups = $this->ownCloudGroupNames($ldap_groups); + $this->connection->writeToCache('getGroups', $ldap_groups); + } + $this->groupSearch = $search; + if(!empty($this->groupSearch)) { + $ldap_groups = array_filter($ldap_groups, array($this, 'groupMatchesFilter')); } - return $this->_groups; + if($limit = -1) { + $limit = null; + } + return array_slice($ldap_groups, $offset, $limit); + } + + public function groupMatchesFilter($group) { + return (strripos($group, $this->groupSearch) !== false); } /** @@ -203,4 +234,17 @@ class OC_GROUP_LDAP extends OC_Group_Backend { public function groupExists($gid){ return in_array($gid, $this->getGroups()); } + + /** + * @brief Check if backend implements actions + * @param $actions bitwise-or'ed actions + * @returns boolean + * + * Returns the supported actions as int to be + * compared with OC_USER_BACKEND_CREATE_USER etc. + */ + public function implementsActions($actions) { + //always returns false, because possible actions are modifying actions. We do not write to LDAP, at least for now. + return false; + } }
\ No newline at end of file diff --git a/apps/user_ldap/js/settings.js b/apps/user_ldap/js/settings.js index cae9655e3df..7063eead96a 100644 --- a/apps/user_ldap/js/settings.js +++ b/apps/user_ldap/js/settings.js @@ -1,3 +1,24 @@ $(document).ready(function() { $('#ldapSettings').tabs(); + $('#ldap_action_test_connection').button(); + $('#ldap_action_test_connection').click(function(event){ + event.preventDefault(); + $.post( + OC.filePath('user_ldap','ajax','testConfiguration.php'), + $('#ldap').serialize(), + function (result) { + if (result.status == 'success') { + OC.dialogs.alert( + result.message, + 'Connection test succeeded' + ); + } else { + OC.dialogs.alert( + result.message, + 'Connection test failed' + ); + } + } + ); + }); });
\ No newline at end of file diff --git a/apps/user_ldap/l10n/.gitkeep b/apps/user_ldap/l10n/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/apps/user_ldap/l10n/.gitkeep diff --git a/apps/user_ldap/l10n/ca.php b/apps/user_ldap/l10n/ca.php new file mode 100644 index 00000000000..04b0f8997db --- /dev/null +++ b/apps/user_ldap/l10n/ca.php @@ -0,0 +1,36 @@ +<?php $TRANSLATIONS = array( +"Host" => "Mà quina", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Podeu ometre el protocol, excepte si requeriu SSL. Llavors comenceu amb ldaps://", +"Base DN" => "DN Base", +"You can specify Base DN for users and groups in the Advanced tab" => "Podeu especificar DN Base per usuaris i grups a la pestanya Avançat", +"User DN" => "DN Usuari", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "La DN de l'usuari client amb la que s'haurà de fer, per exemple uid=agent,dc=exemple,dc=com. Per un accés anònim, deixeu la DN i la contrasenya en blanc.", +"Password" => "Contrasenya", +"For anonymous access, leave DN and Password empty." => "Per un accés anònim, deixeu la DN i la contrasenya en blanc.", +"User Login Filter" => "Filtre d'inici de sessió d'usuari", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Defineix el filtre a aplicar quan s'intenta l'inici de sessió. %%uid reemplaça el nom d'usuari en l'acció d'inici de sessió.", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "useu el parà metre de substitució %%uid, per exemple \"uid=%%uid\"", +"User List Filter" => "Llista de filtres d'usuari", +"Defines the filter to apply, when retrieving users." => "Defineix el filtre a aplicar quan es mostren usuaris", +"without any placeholder, e.g. \"objectClass=person\"." => "sense cap parà metre de substitució, per exemple \"objectClass=persona\"", +"Group Filter" => "Filtre de grup", +"Defines the filter to apply, when retrieving groups." => "Defineix el filtre a aplicar quan es mostren grups.", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "sense cap parà metre de substitució, per exemple \"objectClass=grupPosix\".", +"Port" => "Port", +"Base User Tree" => "Arbre base d'usuaris", +"Base Group Tree" => "Arbre base de grups", +"Group-Member association" => "Associació membres-grup", +"Use TLS" => "Usa TLS", +"Do not use it for SSL connections, it will fail." => "No ho useu en connexions SSL, fallarà .", +"Case insensitve LDAP server (Windows)" => "Servidor LDAP sense distinció entre majúscules i minúscules (Windows)", +"Turn off SSL certificate validation." => "Desactiva la validació de certificat SSL.", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Si la connexió només funciona amb aquesta opció, importeu el certificat SSL del servidor LDAP en el vostre servidor ownCloud.", +"Not recommended, use for testing only." => "No recomanat, ús només per proves.", +"User Display Name Field" => "Camp per mostrar el nom d'usuari", +"The LDAP attribute to use to generate the user`s ownCloud name." => "Atribut LDAP a usar per generar el nom d'usuari ownCloud.", +"Group Display Name Field" => "Camp per mostrar el nom del grup", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "Atribut LDAP a usar per generar el nom de grup ownCloud.", +"in bytes" => "en bytes", +"in seconds. A change empties the cache." => "en segons. Un canvi buidarà la memòria de cau.", +"Help" => "Ajuda" +); diff --git a/apps/user_ldap/l10n/cs_CZ.php b/apps/user_ldap/l10n/cs_CZ.php new file mode 100644 index 00000000000..6c0f227fa7a --- /dev/null +++ b/apps/user_ldap/l10n/cs_CZ.php @@ -0,0 +1,36 @@ +<?php $TRANSLATIONS = array( +"Host" => "Hostitel", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Nelze vynechat protokol vyžadujÃcà SSL. ZaÄnÄ›te s ldaps://", +"Base DN" => "Base DN", +"You can specify Base DN for users and groups in the Advanced tab" => "V RozÅ¡ÃÅ™eném nastavenà můžete specifikovat pro své uživatele a skupiny element Base DN", +"User DN" => "DN uživatele", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN klentského uživatele ke kterému tvoÅ™Ãte vazbu, napÅ™. uid=agent,dc=example,dc=com. Pro anonymnà pÅ™Ãstup ponechte údaje DN and Heslo prázdné.", +"Password" => "Heslo", +"For anonymous access, leave DN and Password empty." => "Pro anonymnà pÅ™Ãstup ponechte údaje DN and Heslo prázdné.", +"User Login Filter" => "Filtr uživatelských loginů", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Definuje filtr, který je aplikován v průbÄ›hu logovánÃ. %%uid nahrazuje uživatelské jméno bÄ›hem logovánÃ.", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "použijte %%uid pro rezervované mÃsto, napÅ™. \"uid=%%uid\"", +"User List Filter" => "Filtr uživateslkých seznamů", +"Defines the filter to apply, when retrieving users." => "Defunije filtr, který je plaikován pÅ™i návratu uživatelů.", +"without any placeholder, e.g. \"objectClass=person\"." => "bez rezervace mÃsta, napÅ™. \"objectClass=person\".", +"Group Filter" => "Filtr skupiny", +"Defines the filter to apply, when retrieving groups." => "Definuje filtr, který je aplikován pÅ™i návratu skupin", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "bez rezervace mÃsta, napÅ™. \"objectClass=posixGroup\".", +"Port" => "Port", +"Base User Tree" => "Základnà uživatelský strom", +"Base Group Tree" => "Základnà skupinový strom", +"Group-Member association" => "Asociace Älena skupiny", +"Use TLS" => "Použijte TLS", +"Do not use it for SSL connections, it will fail." => "NepoužÃvejte pro SSL pÅ™ipojenÃ, pÅ™ipojenà selže.", +"Case insensitve LDAP server (Windows)" => "LDAP server nerozliÅ¡ujÃcà velikost znaků (Windows)", +"Turn off SSL certificate validation." => "VypnÄ›te ověřovánà SSL certifikátu", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "pokud pracuje pÅ™ipojenà pouze pokud je teto volba aktivnÃ, importujte SSL certifikát LDAP serveru do VaÅ¡eho serveru ownCloud.", +"Not recommended, use for testing only." => "Nenà doporuÄeno, pouze pro úÄely testovánÃ.", +"User Display Name Field" => "Pole pro zobrazované jméno uživatele", +"The LDAP attribute to use to generate the user`s ownCloud name." => "Atribut LDAP použitý k vytvoÅ™enà jména uživatele ownCloud", +"Group Display Name Field" => "Pole pro zobrazenà jména skupiny", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "Atribut LDAP použitý k vytvoÅ™enà jména skupiny ownCloud", +"in bytes" => "v bytech", +"in seconds. A change empties the cache." => "ve vteÅ™inách. ZmÄ›na vyprázdnà doÄasnou paměť.", +"Help" => "NápovÄ›da" +); diff --git a/apps/user_ldap/l10n/da.php b/apps/user_ldap/l10n/da.php new file mode 100644 index 00000000000..f01c7b71108 --- /dev/null +++ b/apps/user_ldap/l10n/da.php @@ -0,0 +1,9 @@ +<?php $TRANSLATIONS = array( +"Host" => "Host", +"Base DN" => "Base DN", +"Password" => "Kodeord", +"Port" => "Port", +"Use TLS" => "Brug TLS", +"Not recommended, use for testing only." => "Anbefales ikke, brug kun for at teste.", +"Help" => "Hjælp" +); diff --git a/apps/user_ldap/l10n/de.php b/apps/user_ldap/l10n/de.php new file mode 100644 index 00000000000..9f917f277c4 --- /dev/null +++ b/apps/user_ldap/l10n/de.php @@ -0,0 +1,36 @@ +<?php $TRANSLATIONS = array( +"Host" => "Host", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Sie können das Protokoll auslassen, außer wenn Sie SSL benötigen. Beginnen Sie dann mit ldaps://", +"Base DN" => "Basis-DN", +"You can specify Base DN for users and groups in the Advanced tab" => "Sie können Basis-DN für Benutzer und Gruppen in dem \"Erweitert\"-Reiter konfigurieren", +"User DN" => "Benutzer-DN", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Der DN des Benutzers für LDAP-Bind, z.B.: uid=agent,dc=example,dc=com. Für anonymen Zugriff lassen Sie DN und Passwort leer.", +"Password" => "Passwort", +"For anonymous access, leave DN and Password empty." => "Lassen Sie die Felder von DN und Passwort für anonymen Zugang leer.", +"User Login Filter" => "Benutzer-Login-Filter", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Bestimmt den angewendeten Filter, wenn eine Anmeldung versucht wird. %%uid ersetzt den Benutzernamen bei dem Anmeldeversuch.", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "verwende %%uid Platzhalter, z. B. \"uid=%%uid\"", +"User List Filter" => "Benutzer-Filter-Liste", +"Defines the filter to apply, when retrieving users." => "Definiert den Filter für die Anfrage der Benutzer.", +"without any placeholder, e.g. \"objectClass=person\"." => "ohne Platzhalter, z.B.: \"objectClass=person\"", +"Group Filter" => "Gruppen-Filter", +"Defines the filter to apply, when retrieving groups." => "Definiert den Filter für die Anfrage der Gruppen.", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "ohne Platzhalter, z.B.: \"objectClass=posixGroup\"", +"Port" => "Port", +"Base User Tree" => "Basis-Benutzerbaum", +"Base Group Tree" => "Basis-Gruppenbaum", +"Group-Member association" => "Assoziation zwischen Gruppe und Benutzer", +"Use TLS" => "Nutze TLS", +"Do not use it for SSL connections, it will fail." => "Verwenden Sie es nicht für SSL-Verbindungen, es wird fehlschlagen.", +"Case insensitve LDAP server (Windows)" => "LDAP-Server (Windows: Groß- und Kleinschreibung bleibt unbeachtet)", +"Turn off SSL certificate validation." => "Schalte die SSL Zertifikatsprüfung aus.", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Falls die Verbindung es erfordert, wird das SSL-Zertifikat des LDAP-Server importiert werden.", +"Not recommended, use for testing only." => "Nicht empfohlen, nur zu Testzwecken.", +"User Display Name Field" => "Feld für den Anzeigenamen des Benutzers", +"The LDAP attribute to use to generate the user`s ownCloud name." => "Das LDAP-Attribut für die Generierung des Benutzernamens in ownCloud. ", +"Group Display Name Field" => "Feld für den Anzeigenamen der Gruppe", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "Das LDAP-Attribut für die Generierung des Gruppennamens in ownCloud. ", +"in bytes" => "in Bytes", +"in seconds. A change empties the cache." => "in Sekunden. Eine Änderung leert den Cache.", +"Help" => "Hilfe" +); diff --git a/apps/user_ldap/l10n/el.php b/apps/user_ldap/l10n/el.php new file mode 100644 index 00000000000..2f3c747a672 --- /dev/null +++ b/apps/user_ldap/l10n/el.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Password" => "Συνθηματικό", +"Port" => "ΘÏÏα", +"in bytes" => "σε bytes", +"Help" => "Βοήθεια" +); diff --git a/apps/user_ldap/l10n/eo.php b/apps/user_ldap/l10n/eo.php new file mode 100644 index 00000000000..6f18506b095 --- /dev/null +++ b/apps/user_ldap/l10n/eo.php @@ -0,0 +1,32 @@ +<?php $TRANSLATIONS = array( +"Host" => "Gastigo", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Vi povas neglekti la protokolon, escepte se vi bezonas SSL-on. Tiuokaze, komencu per ldaps://", +"Base DN" => "Baz-DN", +"User DN" => "Uzanto-DN", +"Password" => "Pasvorto", +"For anonymous access, leave DN and Password empty." => "Por sennoman aliron, lasu DN-on kaj Pasvorton malplenaj.", +"User Login Filter" => "Filtrilo de uzantensaluto", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Äœi difinas la filtrilon aplikotan, kiam oni provas ensaluti. %%uid anstataÅigas la uzantonomon en la ensaluta ago.", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "uzu la referencilon %%uid, ekz.: \"uid=%%uid\"", +"User List Filter" => "Filtrilo de uzantolisto", +"Defines the filter to apply, when retrieving users." => "Äœi difinas la filtrilon aplikotan, kiam veniÄas uzantoj.", +"without any placeholder, e.g. \"objectClass=person\"." => "sen ajna referencilo, ekz.: \"objectClass=person\".", +"Group Filter" => "Filtrilo de grupo", +"Defines the filter to apply, when retrieving groups." => "Äœi difinas la filtrilon aplikotan, kiam veniÄas grupoj.", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "sen ajna referencilo, ekz.: \"objectClass=posixGroup\".", +"Port" => "Pordo", +"Base User Tree" => "Baza uzantarbo", +"Base Group Tree" => "Baza gruparbo", +"Use TLS" => "Uzi TLS-on", +"Do not use it for SSL connections, it will fail." => "Ne uzu Äin por SSL-konektoj, Äi malsukcesos.", +"Case insensitve LDAP server (Windows)" => "LDAP-servilo blinda je litergrandeco (Vindozo)", +"Turn off SSL certificate validation." => "Malkapabligi validkontrolon de SSL-atestiloj.", +"Not recommended, use for testing only." => "Ne rekomendata, uzu Äin nur por testoj.", +"User Display Name Field" => "Kampo de vidignomo de uzanto", +"The LDAP attribute to use to generate the user`s ownCloud name." => "La atributo de LDAP uzota por generi la ownCloud-an nomon de la uzanto.", +"Group Display Name Field" => "Kampo de vidignomo de grupo", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "La atributo de LDAP uzota por generi la ownCloud-an nomon de la grupo.", +"in bytes" => "duumoke", +"in seconds. A change empties the cache." => "sekunde. Ajna ÅanÄo malplenigas la kaÅmemoron.", +"Help" => "Helpo" +); diff --git a/apps/user_ldap/l10n/es.php b/apps/user_ldap/l10n/es.php new file mode 100644 index 00000000000..55abf7b88e0 --- /dev/null +++ b/apps/user_ldap/l10n/es.php @@ -0,0 +1,7 @@ +<?php $TRANSLATIONS = array( +"Password" => "Contraseña", +"Port" => "Puerto", +"Use TLS" => "Usar TLS", +"in bytes" => "en bytes", +"Help" => "Ayuda" +); diff --git a/apps/user_ldap/l10n/fi_FI.php b/apps/user_ldap/l10n/fi_FI.php new file mode 100644 index 00000000000..f6d16f3acc1 --- /dev/null +++ b/apps/user_ldap/l10n/fi_FI.php @@ -0,0 +1,31 @@ +<?php $TRANSLATIONS = array( +"Host" => "Isäntä", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Voit jättää protokollan määrittämättä, paitsi kun käytät SSL:ää. Aloita silloin ldaps://", +"Base DN" => "Oletus DN", +"You can specify Base DN for users and groups in the Advanced tab" => "Voit määrittää käyttäjien ja ryhmien oletus DN:n (distinguished name) 'tarkemmat asetukset' välilehdeltä ", +"User DN" => "Käyttäjän DN", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Asiakasohjelman DN, jolla yhdistäminen tehdään, ts. uid=agent,dc=example,dc=com. Mahdollistaaksesi anonyymin yhteyden, jätä DN ja salasana tyhjäksi.", +"Password" => "Salasana", +"For anonymous access, leave DN and Password empty." => "Jos haluat mahdollistaa anonyymin pääsyn, jätä DN ja Salasana tyhjäksi ", +"User Login Filter" => "Login suodatus", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "käytä %%uid paikanvaraajaa, ts. \"uid=%%uid\"", +"User List Filter" => "Käyttäjien suodatus", +"Defines the filter to apply, when retrieving users." => "Määrittelee käytettävän suodattimen, kun käyttäjiä haetaan. ", +"without any placeholder, e.g. \"objectClass=person\"." => "ilman paikanvaraustermiä, ts. \"objectClass=person\".", +"Group Filter" => "Ryhmien suodatus", +"Defines the filter to apply, when retrieving groups." => "Määrittelee käytettävän suodattimen, kun ryhmiä haetaan. ", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "ilman paikanvaraustermiä, ts. \"objectClass=posixGroup\".", +"Port" => "Portti", +"Base User Tree" => "Oletus käyttäjäpuu", +"Base Group Tree" => "Ryhmien juuri", +"Group-Member association" => "Ryhmä-jäsen assosiaatio (yhteys)", +"Use TLS" => "Käytä TLS:ää", +"Do not use it for SSL connections, it will fail." => "Älä käytä SSL yhteyttä varten, se epäonnistuu. ", +"Case insensitve LDAP server (Windows)" => "Kirjainkoosta piittamaton LDAP-palvelin (Windows)", +"Turn off SSL certificate validation." => "Sulje SSL sertifikaatin käyttö", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Jos yhteys toimii vain tällä optiolla, siirrä LDAP palvelimen SSL sertifikaatti onwCloud palvelimellesi. ", +"Not recommended, use for testing only." => "Ei suositella, käytä vain testausta varten.", +"in bytes" => "tavuissa", +"in seconds. A change empties the cache." => "sekunneissa. Muutos tyhjentää välimuistin.", +"Help" => "Ohje" +); diff --git a/apps/user_ldap/l10n/fr.php b/apps/user_ldap/l10n/fr.php new file mode 100644 index 00000000000..64edf3b4680 --- /dev/null +++ b/apps/user_ldap/l10n/fr.php @@ -0,0 +1,32 @@ +<?php $TRANSLATIONS = array( +"Host" => "Hôte", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Vous pouvez omettre le protocole, sauf si vous avez besoin de SSL. Dans ce cas préfixez avec ldaps://", +"Base DN" => "DN de base", +"You can specify Base DN for users and groups in the Advanced tab" => "Vous pouvez spécifier le DN de base pour les utilisateurs et les groupes dans l'onglet Avancé", +"User DN" => "DN Utilisateur", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Le DN de l'utilisateur client avec lequel la liaison doit se faire, par exemple uid=agent,dc=example,dc=com. Pour l'accès anonyme, laisser le DN et le mot de passe vides.", +"Password" => "Mot de passe", +"For anonymous access, leave DN and Password empty." => "Pour l'accès anonyme, laisser le DN et le mot de passe vides.", +"User Login Filter" => "Filtre d'identifiants utilisateur", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Définit le filtre à appliquer lors d'une tentative de connexion. %%uid remplace le nom d'utilisateur lors de la connexion.", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "veuillez utiliser le champ %%uid , ex.: \"uid=%%uid\"", +"User List Filter" => "Filtre d'utilisateurs", +"Defines the filter to apply, when retrieving users." => "Définit le filtre à appliquer lors de la récupération des utilisateurs.", +"without any placeholder, e.g. \"objectClass=person\"." => "sans élément de substitution, par exemple \"objectClass=person\".", +"Group Filter" => "Filtre de groupes", +"Defines the filter to apply, when retrieving groups." => "Définit le filtre à appliquer lors de la récupération des groupes.", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "sans élément de substitution, par exemple \"objectClass=posixGroup\".", +"Port" => "Port", +"Group-Member association" => "Association groupe-membre", +"Use TLS" => "Utiliser TLS", +"Do not use it for SSL connections, it will fail." => "Ne pas utiliser pour les connexions SSL, car cela échouera.", +"Case insensitve LDAP server (Windows)" => "Serveur LDAP insensible à la casse (Windows)", +"Turn off SSL certificate validation." => "Désactiver la validation du certificat SSL", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Si la connexion ne fonctionne qu'avec cette option, importez le certificat SSL du serveur LDAP dans le serveur ownCloud.", +"Not recommended, use for testing only." => "Non recommendé, utilisation pour tests uniquement.", +"The LDAP attribute to use to generate the user`s ownCloud name." => "L'attribut LDAP utilisé pour générer les noms d'utilisateurs d'ownCloud", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "L'attribut LDAP utilisé pour générer les noms de groupes d'ownCloud", +"in bytes" => "en octets", +"in seconds. A change empties the cache." => "en secondes. Tout changement vide le cache.", +"Help" => "Aide" +); diff --git a/apps/user_ldap/l10n/it.php b/apps/user_ldap/l10n/it.php new file mode 100644 index 00000000000..c86b4ea2a57 --- /dev/null +++ b/apps/user_ldap/l10n/it.php @@ -0,0 +1,36 @@ +<?php $TRANSLATIONS = array( +"Host" => "Host", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "È possibile omettere il protocollo, ad eccezione se è necessario SSL. Quindi inizia con ldaps://", +"Base DN" => "DN base", +"You can specify Base DN for users and groups in the Advanced tab" => "Puoi specificare una DN base per gli utenti ed i gruppi nella scheda Avanzate", +"User DN" => "DN utente", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Il DN per il client dell'utente con cui deve essere associato, ad esempio uid=agent,dc=example,dc=com. Per l'accesso anonimo, lasciare vuoti i campi DN e Password", +"Password" => "Password", +"For anonymous access, leave DN and Password empty." => "Per l'accesso anonimo, lasciare vuoti i campi DN e Password", +"User Login Filter" => "Filtro per l'accesso utente", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Specifica quale filtro utilizzare quando si tenta l'accesso. %%uid sostituisce il nome utente all'atto dell'accesso.", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "utilizza il segnaposto %%uid, ad esempio \"uid=%%uid\"", +"User List Filter" => "Filtro per l'elenco utenti", +"Defines the filter to apply, when retrieving users." => "Specifica quale filtro utilizzare durante il recupero degli utenti.", +"without any placeholder, e.g. \"objectClass=person\"." => "senza nessun segnaposto, per esempio \"objectClass=person\".", +"Group Filter" => "Filtro per il gruppo", +"Defines the filter to apply, when retrieving groups." => "Specifica quale filtro utilizzare durante il recupero dei gruppi.", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "senza nessun segnaposto, per esempio \"objectClass=posixGroup\".", +"Port" => "Porta", +"Base User Tree" => "Struttura base dell'utente", +"Base Group Tree" => "Struttura base del gruppo", +"Group-Member association" => "Associazione gruppo-utente ", +"Use TLS" => "Usa TLS", +"Do not use it for SSL connections, it will fail." => "Non utilizzare per le connessioni SSL, fallirà .", +"Case insensitve LDAP server (Windows)" => "Case insensitve LDAP server (Windows)", +"Turn off SSL certificate validation." => "Disattiva il controllo del certificato SSL.", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Se la connessione funziona esclusivamente con questa opzione, importa il certificato SSL del server LDAP nel tuo server ownCloud.", +"Not recommended, use for testing only." => "Non consigliato, utilizzare solo per test.", +"User Display Name Field" => "Campo per la visualizzazione del nome utente", +"The LDAP attribute to use to generate the user`s ownCloud name." => "L'attributo LDAP da usare per generare il nome dell'utente ownCloud.", +"Group Display Name Field" => "Campo per la visualizzazione del nome del gruppo", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "L'attributo LDAP da usare per generare il nome del gruppo ownCloud.", +"in bytes" => "in byte", +"in seconds. A change empties the cache." => "in secondi. Il cambio svuota la cache.", +"Help" => "Aiuto" +); diff --git a/apps/user_ldap/l10n/ja_JP.php b/apps/user_ldap/l10n/ja_JP.php new file mode 100644 index 00000000000..8d4473b4e28 --- /dev/null +++ b/apps/user_ldap/l10n/ja_JP.php @@ -0,0 +1,36 @@ +<?php $TRANSLATIONS = array( +"Host" => "ホスト", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "SSL通信ã—ãªã„å ´åˆã«ã¯ã€ãƒ—ãƒãƒˆã‚³ãƒ«åã‚’çœç•¥ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ãã†ã§ãªã„å ´åˆã«ã¯ã€ldaps:// ã‹ã‚‰å§‹ã‚ã¦ãã ã•ã„。", +"Base DN" => "ベースDN", +"You can specify Base DN for users and groups in the Advanced tab" => "拡張タブã§ãƒ¦ãƒ¼ã‚¶ã¨ã‚°ãƒ«ãƒ¼ãƒ—ã®ãƒ™ãƒ¼ã‚¹DNを指定ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚", +"User DN" => "ユーザDN", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "クライアントユーザーã®DNã¯ã€ç‰¹å®šã®ã‚‚ã®ã«çµã³ã¤ã‘ã‚‹ã“ã¨ã¯ã—ã¾ã›ã‚“。 例ãˆã° uid=agent,dc=example,dc=com. ã ã¨åŒ¿åアクセスã®å ´åˆã€DNã¨ãƒ‘スワードã¯ç©ºã®ã¾ã¾ã§ã™ã€‚", +"Password" => "パスワード", +"For anonymous access, leave DN and Password empty." => "匿åアクセスã®å ´åˆã¯ã€DNã¨ãƒ‘スワードを空ã«ã—ã¦ãã ã•ã„。", +"User Login Filter" => "ユーザãƒã‚°ã‚¤ãƒ³ãƒ•ã‚£ãƒ«ã‚¿", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "ãƒã‚°ã‚¤ãƒ³ã™ã‚‹ã¨ãã«é©ç”¨ã™ã‚‹ãƒ•ã‚£ãƒ«ã‚¿ãƒ¼ã‚’定義ã™ã‚‹ã€‚%%uid ãŒãƒã‚°ã‚¤ãƒ³æ™‚ã«ãƒ¦ãƒ¼ã‚¶ãƒ¼åã«ç½®ãæ›ãˆã‚‰ã‚Œã¾ã™ã€‚", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "%%uid プレースホルダーを利用ã—ã¦ãã ã•ã„。例 \"uid=%%uid\"", +"User List Filter" => "ユーザリストフィルタ", +"Defines the filter to apply, when retrieving users." => "ユーザーをå–å¾—ã™ã‚‹ã¨ãã«é©ç”¨ã™ã‚‹ãƒ•ã‚£ãƒ«ã‚¿ãƒ¼ã‚’定義ã™ã‚‹ã€‚", +"without any placeholder, e.g. \"objectClass=person\"." => "プレースホルダーを利用ã—ãªã„ã§ãã ã•ã„。例 \"objectClass=person\"", +"Group Filter" => "グループフィルタ", +"Defines the filter to apply, when retrieving groups." => "グループをå–å¾—ã™ã‚‹ã¨ãã«é©ç”¨ã™ã‚‹ãƒ•ã‚£ãƒ«ã‚¿ãƒ¼ã‚’定義ã™ã‚‹ã€‚", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "プレースホルダーを利用ã—ãªã„ã§ãã ã•ã„。例 \"objectClass=posixGroup\"", +"Port" => "ãƒãƒ¼ãƒˆ", +"Base User Tree" => "ベースユーザツリー", +"Base Group Tree" => "ベースグループツリー", +"Group-Member association" => "グループã¨ãƒ¡ãƒ³ãƒãƒ¼ã®é–¢é€£ä»˜ã‘", +"Use TLS" => "TLSを利用", +"Do not use it for SSL connections, it will fail." => "SSL接続ã«åˆ©ç”¨ã—ãªã„ã§ãã ã•ã„ã€å¤±æ•—ã—ã¾ã™ã€‚", +"Case insensitve LDAP server (Windows)" => "大文å—ï¼å°æ–‡å—を区別ã—ãªã„LDAPサーãƒï¼ˆWindows)", +"Turn off SSL certificate validation." => "SSL証明書ã®ç¢ºèªã‚’無効ã«ã™ã‚‹ã€‚", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "接続ãŒã“ã®ã‚ªãƒ—ションã§ã®ã¿å‹•ä½œã™ã‚‹å ´åˆã¯ã€LDAPサーãƒã®SSL証明書をownCloudサーãƒã«ã‚¤ãƒ³ãƒãƒ¼ãƒˆã—ã¦ãã ã•ã„。", +"Not recommended, use for testing only." => "推奨ã—ã¾ã›ã‚“ã€ãƒ†ã‚¹ãƒˆç›®çš„ã§ã®ã¿åˆ©ç”¨ã—ã¦ãã ã•ã„。", +"User Display Name Field" => "ユーザ表示åã®ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰", +"The LDAP attribute to use to generate the user`s ownCloud name." => "ユーザã®ownCloudåã®ç”Ÿæˆã«åˆ©ç”¨ã™ã‚‹LDAP属性。", +"Group Display Name Field" => "グループ表示åã®ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "グループã®ownCloudåã®ç”Ÿæˆã«åˆ©ç”¨ã™ã‚‹LDAP属性。", +"in bytes" => "ãƒã‚¤ãƒˆ", +"in seconds. A change empties the cache." => "秒。変更後ã«ã‚ャッシュãŒã‚¯ãƒªã‚¢ã•ã‚Œã¾ã™ã€‚", +"Help" => "ヘルプ" +); diff --git a/apps/user_ldap/l10n/lt_LT.php b/apps/user_ldap/l10n/lt_LT.php new file mode 100644 index 00000000000..809ed571bd0 --- /dev/null +++ b/apps/user_ldap/l10n/lt_LT.php @@ -0,0 +1,9 @@ +<?php $TRANSLATIONS = array( +"Password" => "Slaptažodis", +"Group Filter" => "GrupÄ—s filtras", +"Port" => "Prievadas", +"Use TLS" => "Naudoti TLS", +"Turn off SSL certificate validation." => "IÅ¡jungti SSL sertifikato tikrinimÄ….", +"Not recommended, use for testing only." => "Nerekomenduojama, naudokite tik testavimui.", +"Help" => "Pagalba" +); diff --git a/apps/user_ldap/l10n/pl.php b/apps/user_ldap/l10n/pl.php new file mode 100644 index 00000000000..fa7618d68dc --- /dev/null +++ b/apps/user_ldap/l10n/pl.php @@ -0,0 +1,36 @@ +<?php $TRANSLATIONS = array( +"Host" => "Host", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Można pominąć protokół, z wyjÄ…tkiem wymaganego protokoÅ‚u SSL. NastÄ™pnie uruchom z ldaps://", +"Base DN" => "Baza DN", +"You can specify Base DN for users and groups in the Advanced tab" => "BazÄ™ DN można okreÅ›lić dla użytkowników i grup w karcie Zaawansowane", +"User DN" => "Użytkownik DN", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN użytkownika klienta, z którym powiÄ…zanie wykonuje siÄ™, np. uid=agent,dc=example,dc=com. Dla dostÄ™pu anonimowego pozostawić DN i hasÅ‚o puste", +"Password" => "HasÅ‚o", +"For anonymous access, leave DN and Password empty." => "Dla dostÄ™pu anonimowego pozostawić DN i hasÅ‚o puste.", +"User Login Filter" => "Filtr logowania użytkownika", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Definiuje filtr do zastosowania, gdy podejmowana jest próba logowania. %%uid zastÄ™puje nazwÄ™ użytkownika w dziaÅ‚aniu logowania.", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "Użyj %%uid zastÄ™pczy, np. \"uid=%%uid\"", +"User List Filter" => "Lista filtrów użytkownika", +"Defines the filter to apply, when retrieving users." => "Definiuje filtry do zastosowania, podczas pobierania użytkowników.", +"without any placeholder, e.g. \"objectClass=person\"." => "bez żadnych symboli zastÄ™pczych np. \"objectClass=person\".", +"Group Filter" => "Grupa filtrów", +"Defines the filter to apply, when retrieving groups." => "Definiuje filtry do zastosowania, podczas pobierania grup.", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "bez żadnych symboli zastÄ™pczych np. \"objectClass=posixGroup\".", +"Port" => "Port", +"Base User Tree" => "Drzewo bazy użytkowników", +"Base Group Tree" => "Drzewo bazy grup", +"Group-Member association" => "CzÅ‚onek grupy stowarzyszenia", +"Use TLS" => "Użyj TLS", +"Do not use it for SSL connections, it will fail." => "Nie używaj SSL dla poÅ‚Ä…czeÅ„, jeÅ›li siÄ™ nie powiedzie.", +"Case insensitve LDAP server (Windows)" => "Wielkość liter serwera LDAP (Windows)", +"Turn off SSL certificate validation." => "WyÅ‚Ä…czyć sprawdzanie poprawnoÅ›ci certyfikatu SSL.", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "JeÅ›li poÅ‚Ä…czenie dziaÅ‚a tylko z tÄ… opcjÄ…, zaimportuj certyfikat SSL serwera LDAP w serwerze ownCloud.", +"Not recommended, use for testing only." => "Niezalecane, użyj tylko testowo.", +"User Display Name Field" => "Pole wyÅ›wietlanej nazwy użytkownika", +"The LDAP attribute to use to generate the user`s ownCloud name." => "Atrybut LDAP sÅ‚uży do generowania nazwy użytkownika ownCloud.", +"Group Display Name Field" => "Pole wyÅ›wietlanej nazwy grupy", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "Atrybut LDAP sÅ‚uży do generowania nazwy grup ownCloud.", +"in bytes" => "w bajtach", +"in seconds. A change empties the cache." => "w sekundach. Zmiana opróżnia pamięć podrÄ™cznÄ….", +"Help" => "Pomoc" +); diff --git a/apps/user_ldap/l10n/ro.php b/apps/user_ldap/l10n/ro.php new file mode 100644 index 00000000000..326e47f96fe --- /dev/null +++ b/apps/user_ldap/l10n/ro.php @@ -0,0 +1,36 @@ +<?php $TRANSLATIONS = array( +"Host" => "Gazdă", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "PuteÈ›i omite protocolul, decât dacă folosiÈ›i SSL. Atunci se începe cu ldaps://", +"Base DN" => "DN de bază", +"You can specify Base DN for users and groups in the Advanced tab" => "PuteÈ›i să specificaÈ›i DN de bază pentru utilizatori È™i grupuri în fila Avansat", +"User DN" => "DN al utilizatorului", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN-ul clientului utilizator cu care se va efectua conectarea, d.e. uid=agent,dc=example,dc=com. Pentru acces anonim, lăsăți DN È™i Parolă libere.", +"Password" => "Parolă", +"For anonymous access, leave DN and Password empty." => "Pentru acces anonim, lăsaÈ›i DN È™i Parolă libere.", +"User Login Filter" => "Filtrare după Nume Utilizator", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "DefineÈ™te fitrele care trebuie aplicate, când se încearcă conectarea. %%uid înlocuieÈ™te numele utilizatorului în procesul de conectare.", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "folosiÈ›i substituentul %%uid , d.e. \"uid=%%uid\"", +"User List Filter" => "Filtrarea după lista utilizatorilor", +"Defines the filter to apply, when retrieving users." => "DefineÈ™te filtrele care trebui aplicate, când se peiau utilzatorii.", +"without any placeholder, e.g. \"objectClass=person\"." => "fără substituenÈ›i, d.e. \"objectClass=person\".", +"Group Filter" => "Fitrare Grup", +"Defines the filter to apply, when retrieving groups." => "DefineÈ™te filtrele care se aplică, când se preiau grupurile.", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "fără substituenÈ›i, d.e. \"objectClass=posixGroup\"", +"Port" => "Portul", +"Base User Tree" => "Arborele de bază al Utilizatorilor", +"Base Group Tree" => "Arborele de bază al Grupurilor", +"Group-Member association" => "Asocierea Grup-Membru", +"Use TLS" => "Utilizează TLS", +"Do not use it for SSL connections, it will fail." => "A nu se utiliza pentru conexiuni SSL, va eÈ™ua.", +"Case insensitve LDAP server (Windows)" => "Server LDAP insensibil la majuscule (Windows)", +"Turn off SSL certificate validation." => "OpreÈ™te validarea certificatelor SSL ", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Dacă conexiunea lucrează doar cu această opÈ›iune, importează certificatul SSL al serverului LDAP în serverul ownCloud.", +"Not recommended, use for testing only." => "Nu este recomandat, a se utiliza doar pentru testare.", +"User Display Name Field" => "Câmpul cu numele vizibil al utilizatorului", +"The LDAP attribute to use to generate the user`s ownCloud name." => "Atributul LDAP folosit pentru a genera numele de utilizator din ownCloud.", +"Group Display Name Field" => "Câmpul cu numele grupului", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "Atributul LDAP folosit pentru a genera numele grupurilor din ownCloud", +"in bytes" => "în octeÈ›i", +"in seconds. A change empties the cache." => "în secunde. O schimbare curăță memoria tampon.", +"Help" => "Ajutor" +); diff --git a/apps/user_ldap/l10n/sl.php b/apps/user_ldap/l10n/sl.php new file mode 100644 index 00000000000..312346958be --- /dev/null +++ b/apps/user_ldap/l10n/sl.php @@ -0,0 +1,36 @@ +<?php $TRANSLATIONS = array( +"Host" => "Gostitelj", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Protokol lahko izpustite, razen Äe zahtevate SSL. V tem primeru zaÄnite z ldaps://", +"Base DN" => "Osnovni DN", +"You can specify Base DN for users and groups in the Advanced tab" => "Osnovni DN za uporabnike in skupine lahko doloÄite v zavihku Napredno", +"User DN" => "Uporabnik DN", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN uporabnikovega odjemalca, s katerim naj se opravi vezava, npr. uid=agent,dc=example,dc=com. Za anonimni dostop pustite polji DN in geslo prazni.", +"Password" => "Geslo", +"For anonymous access, leave DN and Password empty." => "Za anonimni dostop pustite polji DN in geslo prazni.", +"User Login Filter" => "Filter prijav uporabnikov", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "DoloÄi filter uporabljen pri prijavi. %%uid nadomesti uporaniÅ¡ko ime pri prijavi.", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "Uporabite ogrado %%uid, npr. \"uid=%%uid\".", +"User List Filter" => "Filter seznama uporabnikov", +"Defines the filter to apply, when retrieving users." => "DoloÄi filter za uporabo med pridobivanjem uporabnikov.", +"without any placeholder, e.g. \"objectClass=person\"." => "Brez katerekoli ograde, npr. \"objectClass=person\".", +"Group Filter" => "Filter skupin", +"Defines the filter to apply, when retrieving groups." => "DoloÄi filter za uporabo med pridobivanjem skupin.", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "Brez katerekoli ograde, npr. \"objectClass=posixGroup\".", +"Port" => "Vrata", +"Base User Tree" => "Osnovno uporabniÅ¡ko drevo", +"Base Group Tree" => "Osnovno drevo skupine", +"Group-Member association" => "Povezava Älana skupine", +"Use TLS" => "Uporabi TLS", +"Do not use it for SSL connections, it will fail." => "Ne uporabljajte ga za SSL povezave, saj ne bo delovalo.", +"Case insensitve LDAP server (Windows)" => "LDAP strežnik je neobÄutljiv na velikost Ärk (Windows)", +"Turn off SSL certificate validation." => "OnemogoÄi potrditev veljavnosti SSL certifikata.", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "ÄŒe povezava deluje samo s to možnostjo, uvozite SSL potrdilo iz LDAP strežnika v vaÅ¡ ownCloud strežnik.", +"Not recommended, use for testing only." => "Odsvetovano, uporabite le v namene preizkuÅ¡anja.", +"User Display Name Field" => "Polje za uporabnikovo prikazano ime", +"The LDAP attribute to use to generate the user`s ownCloud name." => "LDAP atribut uporabljen pri ustvarjanju ownCloud uporabniÅ¡kih imen.", +"Group Display Name Field" => "Polje za prikazano ime skupine", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "LDAP atribut uporabljen pri ustvarjanju ownCloud imen skupin.", +"in bytes" => "v bajtih", +"in seconds. A change empties the cache." => "v sekundah. Sprememba izprazni predpomnilnik.", +"Help" => "PomoÄ" +); diff --git a/apps/user_ldap/l10n/sv.php b/apps/user_ldap/l10n/sv.php new file mode 100644 index 00000000000..a23cc094b4d --- /dev/null +++ b/apps/user_ldap/l10n/sv.php @@ -0,0 +1,36 @@ +<?php $TRANSLATIONS = array( +"Host" => "Server", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Du behöver inte ange protokoll förutom om du använder SSL. Starta dÃ¥ med ldaps://", +"Base DN" => "Start DN", +"You can specify Base DN for users and groups in the Advanced tab" => "Du kan ange start DN för användare och grupper under fliken Avancerat", +"User DN" => "Användare DN", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN för användaren som skall användas, t.ex. uid=agent, dc=example, dc=com. För anonym Ã¥tkomst, lämna DN och lösenord tomt.", +"Password" => "Lösenord", +"For anonymous access, leave DN and Password empty." => "För anonym Ã¥tkomst, lämna DN och lösenord tomt.", +"User Login Filter" => "Filter logga in användare", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Definierar filter att tillämpa vid inloggningsförsök. %% uid ersätter användarnamn i loginÃ¥tgärden.", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "använd platshÃ¥llare %%uid, t ex \"uid=%%uid\"", +"User List Filter" => "Filter lista användare", +"Defines the filter to apply, when retrieving users." => "Definierar filter att tillämpa vid listning av användare.", +"without any placeholder, e.g. \"objectClass=person\"." => "utan platshÃ¥llare, t.ex. \"objectClass=person\".", +"Group Filter" => "Gruppfilter", +"Defines the filter to apply, when retrieving groups." => "Definierar filter att tillämpa vid listning av grupper.", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "utan platshÃ¥llare, t.ex. \"objectClass=posixGroup\".", +"Port" => "Port", +"Base User Tree" => "Bas för användare i katalogtjänst", +"Base Group Tree" => "Bas för grupper i katalogtjänst", +"Group-Member association" => "Attribut för gruppmedlemmar", +"Use TLS" => "Använd TLS", +"Do not use it for SSL connections, it will fail." => "Använd inte för SSL-anslutningar, det kommer inte att fungera.", +"Case insensitve LDAP server (Windows)" => "LDAP-servern är okänslig för gemener och versaler (Windows)", +"Turn off SSL certificate validation." => "Stäng av verifiering av SSL-certifikat.", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Om anslutningen bara fungerar med det här alternativet, importera LDAP-serverns SSL-certifikat i din ownCloud-server.", +"Not recommended, use for testing only." => "Rekommenderas inte, använd bara för test. ", +"User Display Name Field" => "Attribut för användarnamn", +"The LDAP attribute to use to generate the user`s ownCloud name." => "Attribut som används för att generera användarnamn i ownCloud.", +"Group Display Name Field" => "Attribut för gruppnamn", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "Attribut som används för att generera gruppnamn i ownCloud.", +"in bytes" => "i bytes", +"in seconds. A change empties the cache." => "i sekunder. En förändring tömmer cache.", +"Help" => "Hjälp" +); diff --git a/apps/user_ldap/l10n/th_TH.php b/apps/user_ldap/l10n/th_TH.php new file mode 100644 index 00000000000..a1baa648135 --- /dev/null +++ b/apps/user_ldap/l10n/th_TH.php @@ -0,0 +1,36 @@ +<?php $TRANSLATIONS = array( +"Host" => "โฮสต์", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "คุณสามารถปล่à¸à¸¢à¸Šà¹ˆà¸à¸‡à¹‚ปรโตคà¸à¸¥à¹€à¸§à¹‰à¸™à¹„ว้ได้, ยà¸à¹€à¸§à¹‰à¸™à¸à¸£à¸“ีที่คุณต้à¸à¸‡à¸à¸²à¸£à¹ƒà¸Šà¹‰ SSL จาà¸à¸™à¸±à¹‰à¸™à¹€à¸£à¸´à¹ˆà¸¡à¸•à¹‰à¸™à¸”้วย ldaps://", +"Base DN" => "DN à¸à¸²à¸™", +"You can specify Base DN for users and groups in the Advanced tab" => "คุณสามารถระบุ DN หลัà¸à¸ªà¸³à¸«à¸£à¸±à¸šà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¹à¸¥à¸°à¸à¸¥à¸¸à¹ˆà¸¡à¸•à¹ˆà¸²à¸‡à¹†à¹ƒà¸™à¹à¸—็บขั้นสูงได้", +"User DN" => "DN ขà¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN ขà¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸—ี่เป็นลูà¸à¸„้าà¸à¸°à¹„รà¸à¹‡à¸•à¸²à¸¡à¸—ี่ผูà¸à¸à¸¢à¸¹à¹ˆà¸”้วย เช่น uid=agent, dc=example, dc=com, สำหรับà¸à¸²à¸£à¹€à¸‚้าถึงโดยบุคคลนิรนาม, ให้เว้นว่าง DN à¹à¸¥à¸° รหัสผ่านเà¸à¸²à¹„ว้", +"Password" => "รหัสผ่าน", +"For anonymous access, leave DN and Password empty." => "สำหรับà¸à¸²à¸£à¹€à¸‚้าถึงโดยบุคคลนิรนาม ให้เว้นว่าง DN à¹à¸¥à¸°à¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™à¹„ว้", +"User Login Filter" => "ตัวà¸à¸£à¸à¸‡à¸‚้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¹€à¸‚้าสู่ระบบขà¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "à¸à¸³à¸«à¸™à¸”ตัวà¸à¸£à¸à¸‡à¸‚้à¸à¸¡à¸¹à¸¥à¸—ี่ต้à¸à¸‡à¸à¸²à¸£à¸™à¸³à¹„ปใช้งาน, เมื่à¸à¸¡à¸µà¸„วามพยายามในà¸à¸²à¸£à¹€à¸‚้าสู่ระบบ %%uid จะถูà¸à¸™à¸³à¹„ปà¹à¸—นที่ชื่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¹ƒà¸™à¸à¸²à¸£à¸à¸£à¸°à¸—ำขà¸à¸‡à¸à¸²à¸£à¹€à¸‚้าสู่ระบบ", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "ใช้ตัวยึด %%uid, เช่น \"uid=%%uid\"", +"User List Filter" => "ตัวà¸à¸£à¸à¸‡à¸‚้à¸à¸¡à¸¹à¸¥à¸£à¸²à¸¢à¸Šà¸·à¹ˆà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™", +"Defines the filter to apply, when retrieving users." => "ระบุตัวà¸à¸£à¸à¸‡à¸‚้à¸à¸¡à¸¹à¸¥à¸—ี่ต้à¸à¸‡à¸à¸²à¸£à¸™à¸³à¹„ปใช้งาน, เมื่à¸à¸”ึงข้à¸à¸¡à¸¹à¸¥à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™", +"without any placeholder, e.g. \"objectClass=person\"." => "โดยไม่ต้à¸à¸‡à¸¡à¸µà¸•à¸±à¸§à¸¢à¸¶à¸”ใดๆ, เช่น \"objectClass=person\",", +"Group Filter" => "ตัวà¸à¸£à¸à¸‡à¸‚้à¸à¸¡à¸¹à¸¥à¸à¸¥à¸¸à¹ˆà¸¡", +"Defines the filter to apply, when retrieving groups." => "ระบุตัวà¸à¸£à¸à¸‡à¸‚้à¸à¸¡à¸¹à¸¥à¸—ี่ต้à¸à¸‡à¸à¸²à¸£à¸™à¸³à¹„ปใช้งาน, เมื่à¸à¸”ึงข้à¸à¸¡à¸¹à¸¥à¸à¸¥à¸¸à¹ˆà¸¡", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "โดยไม่ต้à¸à¸‡à¸¡à¸µà¸•à¸±à¸§à¸¢à¸¶à¸”ใดๆ, เช่น \"objectClass=posixGroup\",", +"Port" => "พà¸à¸£à¹Œà¸•", +"Base User Tree" => "รายà¸à¸²à¸£à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸«à¸¥à¸±à¸à¹à¸šà¸š Tree", +"Base Group Tree" => "รายà¸à¸²à¸£à¸à¸¥à¸¸à¹ˆà¸¡à¸«à¸¥à¸±à¸à¹à¸šà¸š Tree", +"Group-Member association" => "ความสัมพันธ์ขà¸à¸‡à¸ªà¸¡à¸²à¸Šà¸´à¸à¹ƒà¸™à¸à¸¥à¸¸à¹ˆà¸¡", +"Use TLS" => "ใช้ TLS", +"Do not use it for SSL connections, it will fail." => "à¸à¸£à¸¸à¸“าà¸à¸¢à¹ˆà¸²à¹ƒà¸Šà¹‰à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸à¸¡à¸•à¹ˆà¸à¹à¸šà¸š SSL à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸à¸¡à¸•à¹ˆà¸à¸ˆà¸°à¹€à¸à¸´à¸”à¸à¸²à¸£à¸¥à¹‰à¸¡à¹€à¸«à¸¥à¸§", +"Case insensitve LDAP server (Windows)" => "เซิร์ฟเวà¸à¸£à¹Œ LDAP ประเภท Case insensitive (วินโดวส์)", +"Turn off SSL certificate validation." => "ปิดใช้งานà¸à¸²à¸£à¸•à¸£à¸§à¸ˆà¸ªà¸à¸šà¸„วามถูà¸à¸•à¹‰à¸à¸‡à¸‚à¸à¸‡à¹ƒà¸šà¸£à¸±à¸šà¸£à¸à¸‡à¸„วามปลà¸à¸”ภัย SSL", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "หาà¸à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸à¸¡à¸•à¹ˆà¸à¸ªà¸²à¸¡à¸²à¸£à¸–ทำงานได้เฉพาะà¸à¸±à¸šà¸•à¸±à¸§à¹€à¸¥à¸·à¸à¸à¸™à¸µà¹‰à¹€à¸—่านั้น, ให้นำเข้าข้à¸à¸¡à¸¹à¸¥à¹ƒà¸šà¸£à¸±à¸šà¸£à¸à¸‡à¸„วามปลà¸à¸”ภัยà¹à¸šà¸š SSL ขà¸à¸‡à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ LDAP ดังà¸à¸¥à¹ˆà¸²à¸§à¹€à¸‚้าไปไว้ในเซิร์ฟเวà¸à¸£à¹Œ ownCloud", +"Not recommended, use for testing only." => "ไม่à¹à¸™à¸°à¸™à¸³à¹ƒà¸«à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™, ใช้สำหรับà¸à¸²à¸£à¸—ดสà¸à¸šà¹€à¸—่านั้น", +"User Display Name Field" => "ช่à¸à¸‡à¹à¸ªà¸”งชื่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸—ี่ต้à¸à¸‡à¸à¸²à¸£", +"The LDAP attribute to use to generate the user`s ownCloud name." => "คุณลัà¸à¸©à¸“ะ LDAP ที่ต้à¸à¸‡à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸ªà¸³à¸«à¸£à¸±à¸šà¸ªà¸£à¹‰à¸²à¸‡à¸Šà¸·à¹ˆà¸à¸‚à¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ ownCloud", +"Group Display Name Field" => "ช่à¸à¸‡à¹à¸ªà¸”งชื่à¸à¸à¸¥à¸¸à¹ˆà¸¡à¸—ี่ต้à¸à¸‡à¸à¸²à¸£", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "คุณลัà¸à¸©à¸“ะ LDAP ที่ต้à¸à¸‡à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸ªà¸£à¹‰à¸²à¸‡à¸Šà¸·à¹ˆà¸à¸à¸¥à¸¸à¹ˆà¸¡à¸‚à¸à¸‡ ownCloud", +"in bytes" => "ในหน่วยไบต์", +"in seconds. A change empties the cache." => "ในà¸à¸µà¸à¹„ม่à¸à¸µà¹ˆà¸§à¸´à¸™à¸²à¸—ี ระบบจะเปลี่ยนà¹à¸›à¸¥à¸‡à¸‚้à¸à¸¡à¸¹à¸¥à¹ƒà¸™à¹à¸„ชให้ว่างเปล่า", +"Help" => "ช่วยเหลืà¸" +); diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php new file mode 100644 index 00000000000..be9aa21c3d2 --- /dev/null +++ b/apps/user_ldap/lib/access.php @@ -0,0 +1,676 @@ +<?php + +/** + * ownCloud – LDAP Access + * + * @author Arthur Schiwon + * @copyright 2012 Arthur Schiwon blizzz@owncloud.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\user_ldap\lib; + +abstract class Access { + protected $connection; + + public function setConnector(Connection &$connection) { + $this->connection = $connection; + } + + private function checkConnection() { + return ($this->connection instanceof Connection); + } + + /** + * @brief reads a given attribute for an LDAP record identified by a DN + * @param $dn the record in question + * @param $attr the attribute that shall be retrieved + * @returns the values in an array on success, false otherwise + * + * Reads an attribute from an LDAP entry + */ + public function readAttribute($dn, $attr) { + if(!$this->checkConnection()) { + \OCP\Util::writeLog('user_ldap', 'No LDAP Connector assigned, access impossible for readAttribute.', \OCP\Util::WARN); + return false; + } + $cr = $this->connection->getConnectionResource(); + if(!is_resource($cr)) { + //LDAP not available + \OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG); + return false; + } + $rr = @ldap_read($cr, $dn, 'objectClass=*', array($attr)); + if(!is_resource($rr)) { + \OCP\Util::writeLog('user_ldap', 'readAttribute '.$attr.' failed for DN '.$dn, \OCP\Util::DEBUG); + //in case an error occurs , e.g. object does not exist + return false; + } + $er = ldap_first_entry($cr, $rr); + //LDAP attributes are not case sensitive + $result = \OCP\Util::mb_array_change_key_case(ldap_get_attributes($cr, $er), MB_CASE_LOWER, 'UTF-8'); + $attr = mb_strtolower($attr, 'UTF-8'); + + if(isset($result[$attr]) && $result[$attr]['count'] > 0) { + $values = array(); + for($i=0;$i<$result[$attr]['count'];$i++) { + $values[] = $this->resemblesDN($attr) ? $this->sanitizeDN($result[$attr][$i]) : $result[$attr][$i]; + } + return $values; + } + \OCP\Util::writeLog('user_ldap', 'Requested attribute '.$attr.' not found for '.$dn, \OCP\Util::DEBUG); + return false; + } + + /** + * @brief checks wether the given attribute`s valua is probably a DN + * @param $attr the attribute in question + * @return if so true, otherwise false + */ + private function resemblesDN($attr) { + $resemblingAttributes = array( + 'dn', + 'uniquemember', + 'member' + ); + return in_array($attr, $resemblingAttributes); + } + + /** + * @brief sanitizes a DN received from the LDAP server + * @param $dn the DN in question + * @return the sanitized DN + */ + private function sanitizeDN($dn) { + //OID sometimes gives back DNs with whitespace after the comma a la "uid=foo, cn=bar, dn=..." We need to tackle this! + $dn = preg_replace('/([^\\\]),(\s+)/u', '\1,', $dn); + + //make comparisons and everything work + $dn = mb_strtolower($dn, 'UTF-8'); + + return $dn; + } + + /** + * gives back the database table for the query + */ + private function getMapTable($isUser) { + if($isUser) { + return '*PREFIX*ldap_user_mapping'; + } else { + return '*PREFIX*ldap_group_mapping'; + } + } + + /** + * @brief returns the LDAP DN for the given internal ownCloud name of the group + * @param $name the ownCloud name in question + * @returns string with the LDAP DN on success, otherwise false + * + * returns the LDAP DN for the given internal ownCloud name of the group + */ + public function groupname2dn($name) { + return $this->ocname2dn($name, false); + } + + /** + * @brief returns the LDAP DN for the given internal ownCloud name of the user + * @param $name the ownCloud name in question + * @returns string with the LDAP DN on success, otherwise false + * + * returns the LDAP DN for the given internal ownCloud name of the user + */ + public function username2dn($name) { + $dn = $this->ocname2dn($name, true); + if($dn) { + return $dn; + } else { + //fallback: user is not mapped + $filter = $this->combineFilterWithAnd(array( + $this->connection->ldapUserFilter, + $this->connection->ldapUserDisplayName . '=' . $name, + )); + $result = $this->searchUsers($filter, 'dn'); + if(isset($result[0]['dn'])) { + $this->mapComponent($result[0], $name, true); + return $result[0]; + } + } + + return false; + } + + /** + * @brief returns the LDAP DN for the given internal ownCloud name + * @param $name the ownCloud name in question + * @param $isUser is it a user? otherwise group + * @returns string with the LDAP DN on success, otherwise false + * + * returns the LDAP DN for the given internal ownCloud name + */ + private function ocname2dn($name, $isUser) { + $table = $this->getMapTable($isUser); + + $query = \OCP\DB::prepare(' + SELECT ldap_dn + FROM '.$table.' + WHERE owncloud_name = ? + '); + + $record = $query->execute(array($name))->fetchOne(); + return $record; + } + + /** + * @brief returns the internal ownCloud name for the given LDAP DN of the group + * @param $dn the dn of the group object + * @param $ldapname optional, the display name of the object + * @returns string with with the name to use in ownCloud, false on DN outside of search DN + * + * returns the internal ownCloud name for the given LDAP DN of the group, false on DN outside of search DN or failure + */ + public function dn2groupname($dn, $ldapname = null) { + if(mb_strripos($dn, $this->sanitizeDN($this->connection->ldapBaseGroups), 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($this->sanitizeDN($this->connection->ldapBaseGroups), 'UTF-8'))) { + return false; + } + return $this->dn2ocname($dn, $ldapname, false); + } + + /** + * @brief returns the internal ownCloud name for the given LDAP DN of the user + * @param $dn the dn of the user object + * @param $ldapname optional, the display name of the object + * @returns string with with the name to use in ownCloud + * + * returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN or failure + */ + public function dn2username($dn, $ldapname = null) { + if(mb_strripos($dn, $this->sanitizeDN($this->connection->ldapBaseUsers), 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($this->sanitizeDN($this->connection->ldapBaseUsers), 'UTF-8'))) { + return false; + } + return $this->dn2ocname($dn, $ldapname, true); + } + + /** + * @brief returns an internal ownCloud name for the given LDAP DN + * @param $dn the dn of the user object + * @param $ldapname optional, the display name of the object + * @param $isUser optional, wether it is a user object (otherwise group assumed) + * @returns string with with the name to use in ownCloud + * + * returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN + */ + public function dn2ocname($dn, $ldapname = null, $isUser = true) { + $dn = $this->sanitizeDN($dn); + $table = $this->getMapTable($isUser); + if($isUser) { + $nameAttribute = $this->connection->ldapUserDisplayName; + } else { + $nameAttribute = $this->connection->ldapGroupDisplayName; + } + + $query = \OCP\DB::prepare(' + SELECT owncloud_name + FROM '.$table.' + WHERE ldap_dn = ? + '); + + //let's try to retrieve the ownCloud name from the mappings table + $component = $query->execute(array($dn))->fetchOne(); + if($component) { + return $component; + } + + //second try: get the UUID and check if it is known. Then, update the DN and return the name. + $uuid = $this->getUUID($dn); + if($uuid) { + $query = \OCP\DB::prepare(' + SELECT owncloud_name + FROM '.$table.' + WHERE directory_uuid = ? + '); + $component = $query->execute(array($uuid))->fetchOne(); + if($component) { + $query = \OCP\DB::prepare(' + UPDATE '.$table.' + SET ldap_dn = ? + WHERE directory_uuid = ? + '); + $query->execute(array($dn, $uuid)); + return $component; + } + } + + if(is_null($ldapname)) { + $ldapname = $this->readAttribute($dn, $nameAttribute); + if(!isset($ldapname[0]) && empty($ldapname[0])) { + \OCP\Util::writeLog('user_ldap', 'No or empty name for '.$dn.'.', \OCP\Util::INFO); + return false; + } + $ldapname = $ldapname[0]; + } + $ldapname = $this->sanitizeUsername($ldapname); + + //a new user/group! Then let's try to add it. We're shooting into the blue with the user/group name, assuming that in most cases there will not be a conflict. Otherwise an error will occur and we will continue with our second shot. + if($this->mapComponent($dn, $ldapname, $isUser)) { + return $ldapname; + } + + //doh! There is a conflict. We need to distinguish between users/groups. Adding indexes is an idea, but not much of a help for the user. The DN is ugly, but for now the only reasonable way. But we transform it to a readable format and remove the first part to only give the path where this object is located. + $oc_name = $this->alternateOwnCloudName($ldapname, $dn); + if($this->mapComponent($dn, $oc_name, $isUser)) { + return $oc_name; + } + + //if everything else did not help.. + \OCP\Util::writeLog('user_ldap', 'Could not create unique ownCloud name for '.$dn.'.', \OCP\Util::INFO); + } + + /** + * @brief gives back the user names as they are used ownClod internally + * @param $ldapGroups an array with the ldap Users result in style of array ( array ('dn' => foo, 'uid' => bar), ... ) + * @returns an array with the user names to use in ownCloud + * + * gives back the user names as they are used ownClod internally + */ + public function ownCloudUserNames($ldapUsers) { + return $this->ldap2ownCloudNames($ldapUsers, true); + } + + /** + * @brief gives back the group names as they are used ownClod internally + * @param $ldapGroups an array with the ldap Groups result in style of array ( array ('dn' => foo, 'cn' => bar), ... ) + * @returns an array with the group names to use in ownCloud + * + * gives back the group names as they are used ownClod internally + */ + public function ownCloudGroupNames($ldapGroups) { + return $this->ldap2ownCloudNames($ldapGroups, false); + } + + private function ldap2ownCloudNames($ldapObjects, $isUsers) { + if($isUsers) { + $knownObjects = $this->mappedUsers(); + $nameAttribute = $this->connection->ldapUserDisplayName; + } else { + $knownObjects = $this->mappedGroups(); + $nameAttribute = $this->connection->ldapGroupDisplayName; + } + $ownCloudNames = array(); + + foreach($ldapObjects as $ldapObject) { + $key = \OCP\Util::recursiveArraySearch($knownObjects, $ldapObject['dn']); + + //everything is fine when we know the group + if($key !== false) { + $ownCloudNames[] = $knownObjects[$key]['owncloud_name']; + continue; + } + + //we do not take empty usernames + if(!isset($ldapObject[$nameAttribute]) || empty($ldapObject[$nameAttribute])) { + \OCP\Util::writeLog('user_ldap', 'No or empty name for '.$ldapObject['dn'].', skipping.', \OCP\Util::INFO); + continue; + } + + //a new group! Then let's try to add it. We're shooting into the blue with the group name, assuming that in most cases there will not be a conflict. But first make sure, that the display name contains only allowed characters. + $ocname = $this->sanitizeUsername($ldapObject[$nameAttribute]); + if($this->mapComponent($ldapObject['dn'], $ocname, $isUsers)) { + $ownCloudNames[] = $ocname; + continue; + } + + //doh! There is a conflict. We need to distinguish between groups. Adding indexes is an idea, but not much of a help for the user. The DN is ugly, but for now the only reasonable way. But we transform it to a readable format and remove the first part to only give the path where this entry is located. + $ocname = $this->alternateOwnCloudName($ocname, $ldapObject['dn']); + if($this->mapComponent($ldapObject['dn'], $ocname, $isUsers)) { + $ownCloudNames[] = $ocname; + continue; + } + + //if everything else did not help.. + \OCP\Util::writeLog('user_ldap', 'Could not create unique ownCloud name for '.$ldapObject['dn'].', skipping.', \OCP\Util::INFO); + } + return $ownCloudNames; + } + + /** + * @brief creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object + * @param $name the display name of the object + * @param $dn the dn of the object + * @returns string with with the name to use in ownCloud + * + * creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object + */ + private function alternateOwnCloudName($name, $dn) { + $ufn = ldap_dn2ufn($dn); + $name = $name . '@' . trim(\OCP\Util::mb_substr_replace($ufn, '', 0, mb_strpos($ufn, ',', 0, 'UTF-8'), 'UTF-8')); + $name = $this->sanitizeUsername($name); + return $name; + } + + /** + * @brief retrieves all known groups from the mappings table + * @returns array with the results + * + * retrieves all known groups from the mappings table + */ + private function mappedGroups() { + return $this->mappedComponents(false); + } + + /** + * @brief retrieves all known users from the mappings table + * @returns array with the results + * + * retrieves all known users from the mappings table + */ + private function mappedUsers() { + return $this->mappedComponents(true); + } + + private function mappedComponents($isUsers) { + $table = $this->getMapTable($isUsers); + + $query = \OCP\DB::prepare(' + SELECT ldap_dn, owncloud_name + FROM '. $table + ); + + return $query->execute()->fetchAll(); + } + + /** + * @brief inserts a new user or group into the mappings table + * @param $dn the record in question + * @param $ocname the name to use in ownCloud + * @param $isUser is it a user or a group? + * @returns true on success, false otherwise + * + * inserts a new user or group into the mappings table + */ + private function mapComponent($dn, $ocname, $isUser = true) { + $table = $this->getMapTable($isUser); + $dn = $this->sanitizeDN($dn); + + $sqlAdjustment = ''; + $dbtype = \OCP\Config::getSystemValue('dbtype'); + if($dbtype == 'mysql') { + $sqlAdjustment = 'FROM dual'; + } + + $insert = \OCP\DB::prepare(' + INSERT INTO '.$table.' (ldap_dn, owncloud_name, directory_uuid) + SELECT ?,?,? + '.$sqlAdjustment.' + WHERE NOT EXISTS ( + SELECT 1 + FROM '.$table.' + WHERE ldap_dn = ? + OR owncloud_name = ?) + '); + + //feed the DB + $res = $insert->execute(array($dn, $ocname, $this->getUUID($dn), $dn, $ocname)); + + if(\OCP\DB::isError($res)) { + return false; + } + + $insRows = $res->numRows(); + + if($insRows == 0) { + return false; + } + + return true; + } + + public function fetchListOfUsers($filter, $attr) { + return $this->fetchList($this->searchUsers($filter, $attr), (count($attr) > 1)); + } + + public function fetchListOfGroups($filter, $attr) { + return $this->fetchList($this->searchGroups($filter, $attr), (count($attr) > 1)); + } + + private function fetchList($list, $manyAttributes) { + if(is_array($list)) { + if($manyAttributes) { + return $list; + } else { + return array_unique($list, SORT_LOCALE_STRING); + } + } + + //error cause actually, maybe throw an exception in future. + return array(); + } + + /** + * @brief executes an LDAP search, optimized for Users + * @param $filter the LDAP filter for the search + * @param $attr optional, when a certain attribute shall be filtered out + * @returns array with the search result + * + * Executes an LDAP search + */ + public function searchUsers($filter, $attr = null) { + return $this->search($filter, $this->connection->ldapBaseUsers, $attr); + } + + /** + * @brief executes an LDAP search, optimized for Groups + * @param $filter the LDAP filter for the search + * @param $attr optional, when a certain attribute shall be filtered out + * @returns array with the search result + * + * Executes an LDAP search + */ + public function searchGroups($filter, $attr = null) { + return $this->search($filter, $this->connection->ldapBaseGroups, $attr); + } + + /** + * @brief executes an LDAP search + * @param $filter the LDAP filter for the search + * @param $base the LDAP subtree that shall be searched + * @param $attr optional, when a certain attribute shall be filtered out + * @returns array with the search result + * + * Executes an LDAP search + */ + private function search($filter, $base, $attr = null) { + if(!is_null($attr) && !is_array($attr)) { + $attr = array(mb_strtolower($attr, 'UTF-8')); + } + + // See if we have a resource + $link_resource = $this->connection->getConnectionResource(); + if(is_resource($link_resource)) { + $sr = ldap_search($link_resource, $base, $filter, $attr); + $findings = ldap_get_entries($link_resource, $sr ); + + // if we're here, probably no connection resource is returned. + // to make ownCloud behave nicely, we simply give back an empty array. + if(is_null($findings)) { + return array(); + } + } else { + // Seems like we didn't find any resource. + // Return an empty array just like before. + \OCP\Util::writeLog('user_ldap', 'Could not search, because resource is missing.', \OCP\Util::DEBUG); + return array(); + } + + if(!is_null($attr)) { + $selection = array(); + $multiarray = false; + if(count($attr) > 1) { + $multiarray = true; + $i = 0; + } + foreach($findings as $item) { + if(!is_array($item)) { + continue; + } + $item = \OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8'); + + if($multiarray) { + foreach($attr as $key) { + $key = mb_strtolower($key, 'UTF-8'); + if(isset($item[$key])) { + if($key != 'dn') { + $selection[$i][$key] = $this->resemblesDN($key) ? $this->sanitizeDN($item[$key][0]) : $item[$key][0]; + } else { + $selection[$i][$key] = $this->sanitizeDN($item[$key]); + } + } + + } + $i++; + } else { + //tribute to case insensitivity + $key = mb_strtolower($attr[0], 'UTF-8'); + + if(isset($item[$key])) { + if($this->resemblesDN($key)) { + $selection[] = $this->sanitizeDN($item[$key]); + } else { + $selection[] = $item[$key]; + } + } + } + } + return $selection; + } + return $findings; + } + + public function sanitizeUsername($name) { + if($this->connection->ldapIgnoreNamingRules) { + return $name; + } + + //REPLACEMENTS + $name = \OCP\Util::mb_str_replace(' ', '_', $name, 'UTF-8'); + + //every remaining unallowed characters will be removed + $name = preg_replace('/[^a-zA-Z0-9_.@-]/u', '', $name); + + return $name; + } + + /** + * @brief combines the input filters with AND + * @param $filters array, the filters to connect + * @returns the combined filter + * + * Combines Filter arguments with AND + */ + public function combineFilterWithAnd($filters) { + return $this->combineFilter($filters, '&'); + } + + /** + * @brief combines the input filters with AND + * @param $filters array, the filters to connect + * @returns the combined filter + * + * Combines Filter arguments with AND + */ + public function combineFilterWithOr($filters) { + return $this->combineFilter($filters, '|'); + } + + /** + * @brief combines the input filters with given operator + * @param $filters array, the filters to connect + * @param $operator either & or | + * @returns the combined filter + * + * Combines Filter arguments with AND + */ + private function combineFilter($filters, $operator) { + $combinedFilter = '('.$operator; + foreach($filters as $filter) { + if($filter[0] != '(') { + $filter = '('.$filter.')'; + } + $combinedFilter.=$filter; + } + $combinedFilter.=')'; + return $combinedFilter; + } + + public function areCredentialsValid($name, $password) { + $testConnection = clone $this->connection; + $credentials = array( + 'ldapAgentName' => $name, + 'ldapAgentPassword' => $password + ); + if(!$testConnection->setConfiguration($credentials)) { + return false; + } + return $testConnection->bind(); + } + + /** + * @brief auto-detects the directory's UUID attribute + * @param $dn a known DN used to check against + * @param $force the detection should be run, even if it is not set to auto + * @returns true on success, false otherwise + */ + private function detectUuidAttribute($dn, $force = false) { + if(($this->connection->ldapUuidAttribute != 'auto') && !$force) { + return true; + } + + //for now, supported (known) attributes are entryUUID, nsuniqueid, objectGUID + $testAttributes = array('entryuuid', 'nsuniqueid', 'objectguid'); + + foreach($testAttributes as $attribute) { + \OCP\Util::writeLog('user_ldap', 'Testing '.$attribute.' as UUID attr', \OCP\Util::DEBUG); + + $value = $this->readAttribute($dn, $attribute); + if(is_array($value) && isset($value[0]) && !empty($value[0])) { + \OCP\Util::writeLog('user_ldap', 'Setting '.$attribute.' as UUID attr', \OCP\Util::DEBUG); + $this->connection->ldapUuidAttribute = $attribute; + return true; + } + \OCP\Util::writeLog('user_ldap', 'The looked for uuid attr is not '.$attribute.', result was '.print_r($value,true), \OCP\Util::DEBUG); + } + + return false; + } + + public function getUUID($dn) { + if($this->detectUuidAttribute($dn)) { + $uuid = $this->readAttribute($dn, $this->connection->ldapUuidAttribute); + if(!is_array($uuid) && $this->connection->ldapOverrideUuidAttribute) { + $this->detectUuidAttribute($dn, true); + $uuid = $this->readAttribute($dn, $this->connection->ldapUuidAttribute); + } + if(is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) { + $uuid = $uuid[0]; + } else { + $uuid = false; + } + } else { + $uuid = false; + } + return $uuid; + } +}
\ No newline at end of file diff --git a/apps/user_ldap/lib/connection.php b/apps/user_ldap/lib/connection.php new file mode 100644 index 00000000000..dc160a1642d --- /dev/null +++ b/apps/user_ldap/lib/connection.php @@ -0,0 +1,358 @@ +<?php + +/** + * ownCloud – LDAP Access + * + * @author Arthur Schiwon + * @copyright 2012 Arthur Schiwon blizzz@owncloud.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\user_ldap\lib; + +class Connection { + private $ldapConnectionRes = null; + private $configID; + private $configured = false; + + //cache handler + protected $cache; + + //settings + protected $config = array( + 'ldapHost' => null, + 'ldapPort' => null, + 'ldapBase' => null, + 'ldapBaseUsers' => null, + 'ldapBaseGroups' => null, + 'ldapAgentName' => null, + 'ldapAgentPassword' => null, + 'ldapTLS' => null, + 'ldapNoCase' => null, + 'turnOffCertCheck' => null, + 'ldapIgnoreNamingRules' => null, + 'ldapUserDisplayName' => null, + 'ldapUserFilter' => null, + 'ldapGroupFilter' => null, + 'ldapGroupDisplayName' => null, + 'ldapLoginFilter' => null, + 'ldapQuotaAttribute' => null, + 'ldapQuotaDefault' => null, + 'ldapEmailAttribute' => null, + 'ldapCacheTTL' => null, + 'ldapUuidAttribute' => null, + 'ldapOverrideUuidAttribute' => null, + ); + + public function __construct($configID = 'user_ldap') { + $this->configID = $configID; + $this->cache = \OC_Cache::getGlobalCache(); + } + + public function __destruct() { + @ldap_unbind($this->ldapConnectionRes); + } + + public function __get($name) { + if(!$this->configured) { + $this->readConfiguration(); + } + + if(isset($this->config[$name])) { + return $this->config[$name]; + } + } + + public function __set($name, $value) { + $changed = false; + //omly few options are writable + if($name == 'ldapUuidAttribute') { + \OCP\Util::writeLog('user_ldap', 'Set config ldapUuidAttribute to '.$value, \OCP\Util::DEBUG); + $this->config[$name] = $value; + if(!empty($this->configID)) { + \OCP\Config::getAppValue($this->configID, 'ldap_uuid_attribute', $value); + } + $changed = true; + } + if($changed) { + $this->validateConfiguration(); + } + } + + /** + * @brief initializes the LDAP backend + * @param $force read the config settings no matter what + * + * initializes the LDAP backend + */ + public function init($force = false) { + $this->readConfiguration($force); + $this->establishConnection(); + } + + /** + * Returns the LDAP handler + */ + public function getConnectionResource() { + if(!$this->ldapConnectionRes) { + $this->init(); + } else if(!is_resource($this->ldapConnectionRes)) { + $this->ldapConnectionRes = null; + $this->establishConnection(); + } + if(is_null($this->ldapConnectionRes)) { + \OCP\Util::writeLog('user_ldap', 'Connection could not be established', \OCP\Util::ERROR); + } + return $this->ldapConnectionRes; + } + + private function getCacheKey($key) { + $prefix = 'LDAP-'.$this->configID.'-'; + if(is_null($key)) { + return $prefix; + } + return $prefix.md5($key); + } + + public function getFromCache($key) { + if(!$this->configured) { + $this->readConfiguration(); + } + if(!$this->config['ldapCacheTTL']) { + return null; + } + if(!$this->isCached($key)) { + return null; + + } + $key = $this->getCacheKey($key); + + return unserialize(base64_decode($this->cache->get($key))); + } + + public function isCached($key) { + if(!$this->configured) { + $this->readConfiguration(); + } + if(!$this->config['ldapCacheTTL']) { + return false; + } + $key = $this->getCacheKey($key); + return $this->cache->hasKey($key); + } + + public function writeToCache($key, $value) { + if(!$this->configured) { + $this->readConfiguration(); + } + if(!$this->config['ldapCacheTTL']) { + return null; + } + $key = $this->getCacheKey($key); + $value = base64_encode(serialize($value)); + $this->cache->set($key, $value, $this->config['ldapCacheTTL']); + } + + public function clearCache() { + $this->cache->clear($this->getCacheKey(null)); + } + + /** + * Caches the general LDAP configuration. + */ + private function readConfiguration($force = false) { + \OCP\Util::writeLog('user_ldap','Checking conf state: isConfigured? '.print_r($this->configured, true).' isForce? '.print_r($force, true).' configID? '.print_r($this->configID, true), \OCP\Util::DEBUG); + if((!$this->configured || $force) && !is_null($this->configID)) { + \OCP\Util::writeLog('user_ldap','Reading the configuration', \OCP\Util::DEBUG); + $this->config['ldapHost'] = \OCP\Config::getAppValue($this->configID, 'ldap_host', ''); + $this->config['ldapPort'] = \OCP\Config::getAppValue($this->configID, 'ldap_port', 389); + $this->config['ldapAgentName'] = \OCP\Config::getAppValue($this->configID, 'ldap_dn',''); + $this->config['ldapAgentPassword'] = base64_decode(\OCP\Config::getAppValue($this->configID, 'ldap_agent_password','')); + $this->config['ldapBase'] = \OCP\Config::getAppValue($this->configID, 'ldap_base', ''); + $this->config['ldapBaseUsers'] = \OCP\Config::getAppValue($this->configID, 'ldap_base_users',$this->config['ldapBase']); + $this->config['ldapBaseGroups'] = \OCP\Config::getAppValue($this->configID, 'ldap_base_groups', $this->config['ldapBase']); + $this->config['ldapTLS'] = \OCP\Config::getAppValue($this->configID, 'ldap_tls',0); + $this->config['ldapNoCase'] = \OCP\Config::getAppValue($this->configID, 'ldap_nocase', 0); + $this->config['turnOffCertCheck'] = \OCP\Config::getAppValue($this->configID, 'ldap_turn_off_cert_check', 0); + $this->config['ldapUserDisplayName'] = mb_strtolower(\OCP\Config::getAppValue($this->configID, 'ldap_display_name', 'uid'), 'UTF-8'); + $this->config['ldapUserFilter'] = \OCP\Config::getAppValue($this->configID, 'ldap_userlist_filter','objectClass=person'); + $this->config['ldapGroupFilter'] = \OCP\Config::getAppValue($this->configID, 'ldap_group_filter','(objectClass=posixGroup)'); + $this->config['ldapLoginFilter'] = \OCP\Config::getAppValue($this->configID, 'ldap_login_filter', '(uid=%uid)'); + $this->config['ldapGroupDisplayName'] = mb_strtolower(\OCP\Config::getAppValue($this->configID, 'ldap_group_display_name', 'uid'), 'UTF-8'); + $this->config['ldapQuotaAttribute'] = \OCP\Config::getAppValue($this->configID, 'ldap_quota_attr', ''); + $this->config['ldapQuotaDefault'] = \OCP\Config::getAppValue($this->configID, 'ldap_quota_def', ''); + $this->config['ldapEmailAttribute'] = \OCP\Config::getAppValue($this->configID, 'ldap_email_attr', ''); + $this->config['ldapGroupMemberAssocAttr'] = \OCP\Config::getAppValue($this->configID, 'ldap_group_member_assoc_attribute', 'uniqueMember'); + $this->config['ldapIgnoreNamingRules'] = \OCP\Config::getSystemValue('ldapIgnoreNamingRules', false); + $this->config['ldapCacheTTL'] = \OCP\Config::getAppValue($this->configID, 'ldap_cache_ttl', 10*60); + $this->config['ldapUuidAttribute'] = \OCP\Config::getAppValue($this->configID, 'ldap_uuid_attribute', 'auto'); + $this->config['ldapOverrideUuidAttribute'] = \OCP\Config::getAppValue($this->configID, 'ldap_override_uuid_attribute', 0); + + $this->configured = $this->validateConfiguration(); + } + } + + /** + * @brief set LDAP configuration with values delivered by an array, not read from configuration + * @param $config array that holds the config parameters in an associated array + * @param &$setParameters optional; array where the set fields will be given to + * @return true if config validates, false otherwise. Check with $setParameters for detailed success on single parameters + */ + public function setConfiguration($config, &$setParameters = null) { + if(!is_array($config)) { + return false; + } + + $params = array('ldap_host'=>'ldapHost', 'ldap_port'=>'ldapPort', 'ldap_dn'=>'ldapAgentName', 'ldap_agent_password'=>'ldapAgentPassword', 'ldap_base'=>'ldapBase', 'ldap_base_users'=>'ldapBaseUsers', 'ldap_base_groups'=>'ldapBaseGroups', 'ldap_userlist_filter'=>'ldapUserFilter', 'ldap_login_filter'=>'ldapLoginFilter', 'ldap_group_filter'=>'ldapGroupFilter', 'ldap_display_name'=>'ldapUserDisplayName', 'ldap_group_display_name'=>'ldapGroupDisplayName', + + 'ldap_tls'=>'ldapTLS', 'ldap_nocase'=>'ldapNoCase', 'ldap_quota_def'=>'ldapQuotaDefault', 'ldap_quota_attr'=>'ldapQuotaAttribute', 'ldap_email_attr'=>'ldapEmailAttribute', 'ldap_group_member_assoc_attribute'=>'ldapGroupMemberAssocAttr', 'ldap_cache_ttl'=>'ldapCacheTTL'); + + foreach($config as $parameter => $value) { + if(isset($this->config[$parameter])) { + $this->config[$parameter] = $value; + if(is_array($setParameters)) { + $setParameters[] = $parameter; + } + } else if(isset($params[$parameter])) { + $this->config[$params[$parameter]] = $value; + if(is_array($setParameters)) { + $setParameters[] = $params[$parameter]; + } + } + } + + $this->configured = $this->validateConfiguration(); + + return $this->configured; + } + + /** + * @brief Validates the user specified configuration + * @returns true if configuration seems OK, false otherwise + */ + private function validateConfiguration() { + //first step: "soft" checks: settings that are not really necessary, but advisable. If left empty, give an info message + if(empty($this->config['ldapBaseUsers'])) { + \OCP\Util::writeLog('user_ldap', 'Base tree for Users is empty, using Base DN', \OCP\Util::INFO); + $this->config['ldapBaseUsers'] = $this->config['ldapBase']; + } + if(empty($this->config['ldapBaseGroups'])) { + \OCP\Util::writeLog('user_ldap', 'Base tree for Groups is empty, using Base DN', \OCP\Util::INFO); + $this->config['ldapBaseGroups'] = $this->config['ldapBase']; + } + if(empty($this->config['ldapGroupFilter']) && empty($this->config['ldapGroupMemberAssocAttr'])) { + \OCP\Util::writeLog('user_ldap', 'No group filter is specified, LDAP group feature will not be used.', \OCP\Util::INFO); + } + if(!in_array($this->config['ldapUuidAttribute'], array('auto','entryuuid', 'nsuniqueid', 'objectguid'))) { + \OCP\Config::setAppValue($this->configID, 'ldap_uuid_attribute', 'auto'); + \OCP\Util::writeLog('user_ldap', 'Illegal value for the UUID Attribute, reset to autodetect.', \OCP\Util::INFO); + } + + + //second step: critical checks. If left empty or filled wrong, set as unconfigured and give a warning. + $configurationOK = true; + if(empty($this->config['ldapHost'])) { + \OCP\Util::writeLog('user_ldap', 'No LDAP host given, won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + if(empty($this->config['ldapPort'])) { + \OCP\Util::writeLog('user_ldap', 'No LDAP Port given, won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + if((empty($this->config['ldapAgentName']) && !empty($this->config['ldapAgentPassword'])) + || (!empty($this->config['ldapAgentName']) && empty($this->config['ldapAgentPassword']))) { + \OCP\Util::writeLog('user_ldap', 'Either no password given for the user agent or a password is given, but no LDAP agent; won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + //TODO: check if ldapAgentName is in DN form + if(empty($this->config['ldapBase']) && (empty($this->config['ldapBaseUsers']) && empty($this->config['ldapBaseGroups']))) { + \OCP\Util::writeLog('user_ldap', 'No Base DN given, won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + if(empty($this->config['ldapUserDisplayName'])) { + \OCP\Util::writeLog('user_ldap', 'No user display name attribute specified, won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + if(empty($this->config['ldapGroupDisplayName'])) { + \OCP\Util::writeLog('user_ldap', 'No group display name attribute specified, won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + if(empty($this->config['ldapLoginFilter'])) { + \OCP\Util::writeLog('user_ldap', 'No login filter specified, won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + if(mb_strpos($this->config['ldapLoginFilter'], '%uid', 0, 'UTF-8') === false) { + \OCP\Util::writeLog('user_ldap', 'Login filter does not contain %uid place holder, won`t connect.', \OCP\Util::WARN); + \OCP\Util::writeLog('user_ldap', 'Login filter was ' . $this->config['ldapLoginFilter'], \OCP\Util::DEBUG); + $configurationOK = false; + } + + return $configurationOK; + } + + /** + * Connects and Binds to LDAP + */ + private function establishConnection() { + static $phpLDAPinstalled = true; + if(!$phpLDAPinstalled) { + return false; + } + if(!$this->configured) { + \OCP\Util::writeLog('user_ldap', 'Configuration is invalid, cannot connect', \OCP\Util::WARN); + return false; + } + if(!$this->ldapConnectionRes) { + if(!function_exists('ldap_connect')) { + $phpLDAPinstalled = false; + \OCP\Util::writeLog('user_ldap', 'function ldap_connect is not available. Make sure that the PHP ldap module is installed.', \OCP\Util::ERROR); + + return false; + } + if($this->config['turnOffCertCheck']) { + if(putenv('LDAPTLS_REQCERT=never')) { + \OCP\Util::writeLog('user_ldap', 'Turned off SSL certificate validation successfully.', \OCP\Util::WARN); + } else { + \OCP\Util::writeLog('user_ldap', 'Could not turn off SSL certificate validation.', \OCP\Util::WARN); + } + } + $this->ldapConnectionRes = ldap_connect($this->config['ldapHost'], $this->config['ldapPort']); + if(ldap_set_option($this->ldapConnectionRes, LDAP_OPT_PROTOCOL_VERSION, 3)) { + if(ldap_set_option($this->ldapConnectionRes, LDAP_OPT_REFERRALS, 0)) { + if($this->config['ldapTLS']) { + ldap_start_tls($this->ldapConnectionRes); + } + } + } + + return $this->bind(); + } + } + + /** + * Binds to LDAP + */ + public function bind() { + $ldapLogin = @ldap_bind($this->getConnectionResource(), $this->config['ldapAgentName'], $this->config['ldapAgentPassword']); + if(!$ldapLogin) { + \OCP\Util::writeLog('user_ldap', 'Bind failed: ' . ldap_errno($this->ldapConnectionRes) . ': ' . ldap_error($this->ldapConnectionRes), \OCP\Util::ERROR); + $this->ldapConnectionRes = null; + return false; + } + return true; + } + +}
\ No newline at end of file diff --git a/apps/user_ldap/lib/jobs.php b/apps/user_ldap/lib/jobs.php new file mode 100644 index 00000000000..d478731b84c --- /dev/null +++ b/apps/user_ldap/lib/jobs.php @@ -0,0 +1,157 @@ +<?php + +/** + * ownCloud – LDAP Background Jobs + * + * @author Arthur Schiwon + * @copyright 2012 Arthur Schiwon blizzz@owncloud.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\user_ldap\lib; + +class Jobs { + static private $groupsFromDB; + + static private $groupBE; + static private $connector; + + static public function updateGroups() { + \OCP\Util::writeLog('user_ldap', 'Run background job "updateGroups"', \OCP\Util::DEBUG); + $lastUpdate = \OCP\Config::getAppValue('user_ldap', 'bgjUpdateGroupsLastRun', 0); + if((time() - $lastUpdate) < self::getRefreshInterval()) { + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – last run too fresh, aborting.', \OCP\Util::DEBUG); + //komm runter Werner die Maurer geben ein aus + return; + } + + $knownGroups = array_keys(self::getKnownGroups()); + $actualGroups = self::getGroupBE()->getGroups(); + + if(empty($actualGroups) && empty($knownGroups)) { + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – groups do not seem to be configured properly, aborting.', \OCP\Util::INFO); + \OCP\setAppValue('user_ldap', 'bgjUpdateGroupsLastRun', time()); + return; + } + + self::handleKnownGroups(array_intersect($actualGroups, $knownGroups)); + self::handleCreatedGroups(array_diff($actualGroups, $knownGroups)); + self::handleRemovedGroups(array_diff($knownGroups, $actualGroups)); + + \OCP\Config::setAppValue('user_ldap', 'bgjUpdateGroupsLastRun', time()); + + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – Finished.', \OCP\Util::DEBUG); + } + + static private function getRefreshInterval() { + //defaults to every hour + return \OCP\Config::getAppValue('user_ldap', 'bgjRefreshInterval', 3600); + } + + static private function handleKnownGroups($groups) { + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – Dealing with known Groups.', \OCP\Util::DEBUG); + $query = \OCP\DB::prepare(' + UPDATE *PREFIX*ldap_group_members + SET owncloudusers = ? + WHERE owncloudname = ? + '); + foreach($groups as $group) { + //we assume, that self::$groupsFromDB has been retrieved already + $knownUsers = unserialize(self::$groupsFromDB[$group]['owncloudusers']); + $actualUsers = self::getGroupBE()->usersInGroup($group); + $hasChanged = false; + foreach(array_diff($knownUsers, $actualUsers) as $removedUser) { + \OCP\Util::emitHook('OC_User', 'post_removeFromGroup', array('uid' => $removedUser, 'gid' => $group)); + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – "'.$removedUser.'" removed from "'.$group.'".', \OCP\Util::INFO); + $hasChanged = true; + } + foreach(array_diff($actualUsers, $knownUsers) as $addedUser) { + \OCP\Util::emitHook('OC_User', 'post_addFromGroup', array('uid' => $addedUser, 'gid' => $group)); + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – "'.$addedUser.'" added to "'.$group.'".', \OCP\Util::INFO); + $hasChanged = true; + } + if($hasChanged) { + $query->execute(array(serialize($actualUsers), $group)); + } + } + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – FINISHED dealing with known Groups.', \OCP\Util::DEBUG); + } + + static private function handleCreatedGroups($createdGroups) { + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – dealing with created Groups.', \OCP\Util::DEBUG); + $query = \OCP\DB::prepare(' + INSERT + INTO *PREFIX*ldap_group_members (owncloudname, owncloudusers) + VALUES (?, ?) + '); + foreach($createdGroups as $createdGroup) { + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – new group "'.$createdGroup.'" found.', \OCP\Util::INFO); + $users = serialize(self::getGroupBE()->usersInGroup($createdGroup)); + $query->execute(array($createdGroup, $users)); + } + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – FINISHED dealing with created Groups.', \OCP\Util::DEBUG); + } + + static private function handleRemovedGroups($removedGroups) { + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – dealing with removed groups.', \OCP\Util::DEBUG); + $query = \OCP\DB::prepare(' + DELETE + FROM *PREFIX*ldap_group_members + WHERE owncloudname = ? + '); + foreach($removedGroups as $removedGroup) { + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – group "'.$createdGroup.'" was removed.', \OCP\Util::INFO); + $query->execute(array($removedGroup)); + } + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – FINISHED dealing with removed groups.', \OCP\Util::DEBUG); + } + + static private function getConnector() { + if(!is_null(self::$connector)) { + return self::$connector; + } + self::$connector = new \OCA\user_ldap\lib\Connection('user_ldap'); + return self::$connector; + } + + static private function getGroupBE() { + if(!is_null(self::$groupBE)) { + return self::$groupBE; + } + self::getConnector(); + self::$groupBE = new \OCA\user_ldap\GROUP_LDAP(); + self::$groupBE->setConnector(self::$connector); + + return self::$groupBE; + } + + static private function getKnownGroups() { + if(is_array(self::$groupsFromDB)) { + return self::$groupsFromDB; + } + $query = \OCP\DB::prepare(' + SELECT owncloudname, owncloudusers + FROM *PREFIX*ldap_group_members + '); + $result = $query->execute()->fetchAll(); + self::$groupsFromDB = array(); + foreach($result as $dataset) { + self::$groupsFromDB[$dataset['owncloudname']] = $dataset; + } + + return self::$groupsFromDB; + } +}
\ No newline at end of file diff --git a/apps/user_ldap/lib_ldap.php b/apps/user_ldap/lib_ldap.php deleted file mode 100644 index 70b4664542a..00000000000 --- a/apps/user_ldap/lib_ldap.php +++ /dev/null @@ -1,765 +0,0 @@ -<?php - -/** - * ownCloud – LDAP lib - * - * @author Arthur Schiwon - * @copyright 2012 Arthur Schiwon blizzz@owncloud.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - */ - -define('LDAP_GROUP_MEMBER_ASSOC_ATTR','uniqueMember'); -define('LDAP_GROUP_DISPLAY_NAME_ATTR','cn'); - -//needed to unbind, because we use OC_LDAP only statically -class OC_LDAP_DESTRUCTOR { - public function __destruct() { - OC_LDAP::destruct(); - } -} - -class OC_LDAP { - static protected $ldapConnectionRes = false; - static protected $configured = false; - - //cached settings - static protected $ldapHost; - static protected $ldapPort; - static protected $ldapBase; - static protected $ldapBaseUsers; - static protected $ldapBaseGroups; - static protected $ldapAgentName; - static protected $ldapAgentPassword; - static protected $ldapTLS; - static protected $ldapNoCase; - static protected $ldapIgnoreNamingRules; - // user and group settings, that are needed in both backends - static protected $ldapUserDisplayName; - static protected $ldapUserFilter; - static protected $ldapGroupDisplayName; - static protected $ldapLoginFilter; - - static protected $__d; - - /** - * @brief initializes the LDAP backend - * @param $force read the config settings no matter what - * - * initializes the LDAP backend - */ - static public function init($force = false) { - if(is_null(self::$__d)) { - self::$__d = new OC_LDAP_DESTRUCTOR(); - } - self::readConfiguration($force); - self::establishConnection(); - } - - static public function destruct() { - @ldap_unbind(self::$ldapConnectionRes); - } - - /** - * @brief returns a read-only configuration value - * @param $key the name of the configuration value - * @returns the value on success, otherwise null - * - * returns a read-only configuration values - * - * we cannot work with getters, because it is a static class - */ - static public function conf($key) { - if(!self::$configured) { - self::init(); - } - - $availableProperties = array( - 'ldapUserDisplayName', - 'ldapGroupDisplayName', - 'ldapLoginFilter' - ); - - if(in_array($key, $availableProperties)) { - return self::$$key; - } - - return null; - } - - /** - * gives back the database table for the query - */ - static private function getMapTable($isUser) { - if($isUser) { - return '*PREFIX*ldap_user_mapping'; - } else { - return '*PREFIX*ldap_group_mapping'; - } - } - - /** - * @brief returns the LDAP DN for the given internal ownCloud name of the group - * @param $name the ownCloud name in question - * @returns string with the LDAP DN on success, otherwise false - * - * returns the LDAP DN for the given internal ownCloud name of the group - */ - static public function groupname2dn($name) { - return self::ocname2dn($name, false); - } - - /** - * @brief returns the LDAP DN for the given internal ownCloud name of the user - * @param $name the ownCloud name in question - * @returns string with the LDAP DN on success, otherwise false - * - * returns the LDAP DN for the given internal ownCloud name of the user - */ - static public function username2dn($name) { - $dn = self::ocname2dn($name, true); - if($dn) { - return $dn; - } else { - //fallback: user is not mapped - self::init(); - $filter = self::combineFilterWithAnd(array( - self::$ldapUserFilter, - self::$ldapUserDisplayName . '=' . $name, - )); - $result = self::searchUsers($filter, 'dn'); - if(isset($result[0]['dn'])) { - self::mapUser($result[0], $name); - return $result[0]; - } - } - - return false; - } - - static private function ocname2dn($name, $isUser) { - $table = self::getMapTable($isUser); - - $query = OCP\DB::prepare(' - SELECT `ldap_dn` - FROM `'.$table.'` - WHERE `owncloud_name` = ? - '); - - $record = $query->execute(array($name))->fetchOne(); - return $record; - } - - /** - * @brief returns the internal ownCloud name for the given LDAP DN of the group - * @param $dn the dn of the group object - * @param $ldapname optional, the display name of the object - * @returns string with with the name to use in ownCloud, false on DN outside of search DN - * - * returns the internal ownCloud name for the given LDAP DN of the group, false on DN outside of search DN or failure - */ - static public function dn2groupname($dn, $ldapname = null) { - if(strripos($dn, self::$ldapBaseGroups) !== (strlen($dn)-strlen(self::$ldapBaseGroups))) { - return false; - } - return self::dn2ocname($dn, $ldapname, false); - } - - /** - * @brief returns the internal ownCloud name for the given LDAP DN of the user - * @param $dn the dn of the user object - * @param $ldapname optional, the display name of the object - * @returns string with with the name to use in ownCloud - * - * returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN or failure - */ - static public function dn2username($dn, $ldapname = null) { - if(strripos($dn, self::$ldapBaseUsers) !== (strlen($dn)-strlen(self::$ldapBaseUsers))) { - return false; - } - return self::dn2ocname($dn, $ldapname, true); - } - - static public function dn2ocname($dn, $ldapname = null, $isUser = true) { - $dn = self::sanitizeDN($dn); - $table = self::getMapTable($isUser); - if($isUser) { - $nameAttribute = self::conf('ldapUserDisplayName'); - } else { - $nameAttribute = self::conf('ldapGroupDisplayName'); - } - - $query = OCP\DB::prepare(' - SELECT `owncloud_name` - FROM `'.$table.'` - WHERE `ldap_dn` = ? - '); - - $component = $query->execute(array($dn))->fetchOne(); - if($component) { - return $component; - } - - if(is_null($ldapname)) { - $ldapname = self::readAttribute($dn, $nameAttribute); - //we do not accept empty usernames - if(!isset($ldapname[0]) && empty($ldapname[0])) { - OCP\Util::writeLog('user_ldap', 'No or empty name for '.$dn.'.', OCP\Util::INFO); - return false; - } - $ldapname = $ldapname[0]; - } - $ldapname = self::sanitizeUsername($ldapname); - - //a new user/group! Then let's try to add it. We're shooting into the blue with the user/group name, assuming that in most cases there will not be a conflict. Otherwise an error will occur and we will continue with our second shot. - if(self::mapComponent($dn, $ldapname, $isUser)) { - return $ldapname; - } - - //doh! There is a conflict. We need to distinguish between users/groups. Adding indexes is an idea, but not much of a help for the user. The DN is ugly, but for now the only reasonable way. But we transform it to a readable format and remove the first part to only give the path where this object is located. - $oc_name = self::alternateOwnCloudName($ldapname, $dn); - if(self::mapComponent($dn, $oc_name, $isUser)) { - return $oc_name; - } - - //if everything else did not help.. - OCP\Util::writeLog('user_ldap', 'Could not create unique ownCloud name for '.$dn.'.', OCP\Util::INFO); - } - - /** - * @brief gives back the user names as they are used ownClod internally - * @param $ldapGroups an array with the ldap Users result in style of array ( array ('dn' => foo, 'uid' => bar), ... ) - * @returns an array with the user names to use in ownCloud - * - * gives back the user names as they are used ownClod internally - */ - static public function ownCloudUserNames($ldapUsers) { - return self::ldap2ownCloudNames($ldapUsers, true); - } - - /** - * @brief gives back the group names as they are used ownClod internally - * @param $ldapGroups an array with the ldap Groups result in style of array ( array ('dn' => foo, 'cn' => bar), ... ) - * @returns an array with the group names to use in ownCloud - * - * gives back the group names as they are used ownClod internally - */ - static public function ownCloudGroupNames($ldapGroups) { - return self::ldap2ownCloudNames($ldapGroups, false); - } - - static private function ldap2ownCloudNames($ldapObjects, $isUsers) { - if($isUsers) { - $knownObjects = self::mappedUsers(); - $nameAttribute = self::conf('ldapUserDisplayName'); - } else { - $knownObjects = self::mappedGroups(); - $nameAttribute = self::conf('ldapGroupDisplayName'); - } - $ownCloudNames = array(); - - foreach($ldapObjects as $ldapObject) { - $key = self::recursiveArraySearch($knownObjects, $ldapObject['dn']); - - //everything is fine when we know the group - if($key !== false) { - $ownCloudNames[] = $knownObjects[$key]['owncloud_name']; - continue; - } - - //we do not take empty usernames - if(!isset($ldapObject[$nameAttribute]) || empty($ldapObject[$nameAttribute])) { - OCP\Util::writeLog('user_ldap', 'No or empty name for '.$ldapObject['dn'].', skipping.', OCP\Util::INFO); - continue; - } - - //a new group! Then let's try to add it. We're shooting into the blue with the group name, assuming that in most cases there will not be a conflict. But first make sure, that the display name contains only allowed characters. - $ocname = self::sanitizeUsername($ldapObject[$nameAttribute]); - if(self::mapComponent($ldapObject['dn'], $ocname, $isUsers)) { - $ownCloudNames[] = $ocname; - continue; - } - - //doh! There is a conflict. We need to distinguish between groups. Adding indexes is an idea, but not much of a help for the user. The DN is ugly, but for now the only reasonable way. But we transform it to a readable format and remove the first part to only give the path where this entry is located. - $ocname = self::alternateOwnCloudName($ocname, $ldapObject['dn']); - if(self::mapComponent($ldapObject['dn'], $ocname, $isUsers)) { - $ownCloudNames[] = $ocname; - continue; - } - - //if everything else did not help.. - OCP\Util::writeLog('user_ldap', 'Could not create unique ownCloud name for '.$ldapObject['dn'].', skipping.', OCP\Util::INFO); - } - return $ownCloudNames; - } - - /** - * @brief creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object - * @param $name the display name of the object - * @param $dn the dn of the object - * @returns string with with the name to use in ownCloud - * - * creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object - */ - static private function alternateOwnCloudName($name, $dn) { - $ufn = ldap_dn2ufn($dn); - $name = $name . '@' . trim(substr_replace($ufn, '', 0, strpos($ufn, ','))); - $name = self::sanitizeUsername($name); - return $name; - } - - /** - * @brief retrieves all known groups from the mappings table - * @returns array with the results - * - * retrieves all known groups from the mappings table - */ - static private function mappedGroups() { - return self::mappedComponents(false); - } - - /** - * @brief retrieves all known users from the mappings table - * @returns array with the results - * - * retrieves all known users from the mappings table - */ - static private function mappedUsers() { - return self::mappedComponents(true); - } - - static private function mappedComponents($isUsers) { - $table = self::getMapTable($isUsers); - - $query = OCP\DB::prepare(' - SELECT `ldap_dn`, `owncloud_name` - FROM `'. $table .'`' - ); - - return $query->execute()->fetchAll(); - } - - /** - * @brief inserts a new group into the mappings table - * @param $dn the record in question - * @param $ocname the name to use in ownCloud - * @returns true on success, false otherwise - * - * inserts a new group into the mappings table - */ - static private function mapGroup($dn, $ocname) { - return self::mapComponent($dn, $ocname, false); - } - - /** - * @brief inserts a new user into the mappings table - * @param $dn the record in question - * @param $ocname the name to use in ownCloud - * @returns true on success, false otherwise - * - * inserts a new user into the mappings table - */ - static private function mapUser($dn, $ocname) { - return self::mapComponent($dn, $ocname, true); - } - - /** - * @brief inserts a new user or group into the mappings table - * @param $dn the record in question - * @param $ocname the name to use in ownCloud - * @param $isUser is it a user or a group? - * @returns true on success, false otherwise - * - * inserts a new user or group into the mappings table - */ - static private function mapComponent($dn, $ocname, $isUser = true) { - $table = self::getMapTable($isUser); - $dn = self::sanitizeDN($dn); - - $sqlAdjustment = ''; - $dbtype = OCP\Config::getSystemValue('dbtype'); - if($dbtype == 'mysql') { - $sqlAdjustment = 'FROM `dual`'; - } - - $insert = OCP\DB::prepare(' - INSERT INTO `'.$table.'` (`ldap_dn`, `owncloud_name`) - SELECT ?,? - '.$sqlAdjustment.' - WHERE NOT EXISTS ( - SELECT 1 - FROM `'.$table.'` - WHERE `ldap_dn` = ? - OR `owncloud_name` = ? ) - '); - - $res = $insert->execute(array($dn, $ocname, $dn, $ocname)); - - if(OCP\DB::isError($res)) { - return false; - } - - $insRows = $res->numRows(); - - if($insRows == 0) { - return false; - } - - return true; - } - - static public function fetchListOfUsers($filter, $attr) { - return self::fetchList(OC_LDAP::searchUsers($filter, $attr), (count($attr) > 1)); - } - - static public function fetchListOfGroups($filter, $attr) { - return self::fetchList(OC_LDAP::searchGroups($filter, $attr), (count($attr) > 1)); - } - - static private function fetchList($list, $manyAttributes) { - if(is_array($list)) { - if($manyAttributes) { - return $list; - } else { - return array_unique($list, SORT_LOCALE_STRING); - } - } - - //error cause actually, maybe throw an exception in future. - return array(); - } - - /** - * @brief reads a given attribute for an LDAP record identified by a DN - * @param $dn the record in question - * @param $attr the attribute that shall be retrieved - * @returns the values in an array on success, false otherwise - * - * Reads an attribute from an LDAP entry - */ - static public function readAttribute($dn, $attr) { - $cr = self::getConnectionResource(); - if(!is_resource($cr)) { - //LDAP not available - return false; - } - $rr = ldap_read($cr, $dn, 'objectClass=*', array($attr)); - $er = ldap_first_entry($cr, $rr); - //LDAP attributes are not case sensitive - $result = array_change_key_case(ldap_get_attributes($cr, $er)); - $attr = strtolower($attr); - - if(isset($result[$attr]) && $result[$attr]['count'] > 0){ - $values = array(); - for($i=0;$i<$result[$attr]['count'];$i++) { - $values[] = self::resemblesDN($attr) ? self::sanitizeDN($result[$attr][$i]) : $result[$attr][$i]; - } - return $values; - } - return false; - } - - /** - * @brief executes an LDAP search, optimized for Users - * @param $filter the LDAP filter for the search - * @param $attr optional, when a certain attribute shall be filtered out - * @returns array with the search result - * - * Executes an LDAP search - */ - static public function searchUsers($filter, $attr = null) { - self::init(); - return self::search($filter, self::$ldapBaseUsers, $attr); - } - - /** - * @brief executes an LDAP search, optimized for Groups - * @param $filter the LDAP filter for the search - * @param $attr optional, when a certain attribute shall be filtered out - * @returns array with the search result - * - * Executes an LDAP search - */ - static public function searchGroups($filter, $attr = null) { - self::init(); - return self::search($filter, self::$ldapBaseGroups, $attr); - } - - /** - * @brief executes an LDAP search - * @param $filter the LDAP filter for the search - * @param $base the LDAP subtree that shall be searched - * @param $attr optional, when a certain attribute shall be filtered out - * @returns array with the search result - * - * Executes an LDAP search - */ - static private function search($filter, $base, $attr = null) { - if(!is_null($attr) && !is_array($attr)) { - $attr = array(strtolower($attr)); - } - $cr = self::getConnectionResource(); - if(!is_resource($cr)) { - //LDAP not available - return array(); - } - $sr = @ldap_search(self::getConnectionResource(), $base, $filter, $attr); - $findings = @ldap_get_entries(self::getConnectionResource(), $sr ); - // if we're here, probably no connection ressource is returned. - // to make ownCloud behave nicely, we simply give back an empty array. - if(is_null($findings)) { - return array(); - } - - if(!is_null($attr)) { - $selection = array(); - $multiarray = false; - if(count($attr) > 1) { - $multiarray = true; - $i = 0; - } - foreach($findings as $item) { - if(!is_array($item)) { - continue; - } - $item = array_change_key_case($item); - - if($multiarray) { - foreach($attr as $key) { - $key = strtolower($key); - if(isset($item[$key])) { - if($key != 'dn'){ - $selection[$i][$key] = self::resemblesDN($key) ? self::sanitizeDN($item[$key][0]) : $item[$key][0]; - } else { - $selection[$i][$key] = self::sanitizeDN($item[$key]); - } - } - - } - $i++; - } else { - //tribute to case insensitivity - $key = strtolower($attr[0]); - - if(isset($item[$key])) { - if(self::resemblesDN($key)) { - $selection[] = self::sanitizeDN($item[$key]); - } else { - $selection[] = $item[$key]; - } - } - } - - } - return $selection; - } - - return $findings; - } - - static private function resemblesDN($attr) { - $resemblingAttributes = array( - 'dn', - 'uniquemember', - 'member' - ); - return in_array($attr, $resemblingAttributes); - } - - static private function sanitizeDN($dn) { - //OID sometimes gives back DNs with whitespace after the comma a la "uid=foo, cn=bar, dn=..." We need to tackle this! - $dn = preg_replace('/([^\\\]),(\s+)/','\1,',$dn); - - //make comparisons and everything work - $dn = strtolower($dn); - - return $dn; - } - - static private function sanitizeUsername($name) { - if(self::$ldapIgnoreNamingRules) { - return $name; - } - - //REPLACEMENTS - $name = str_replace(' ', '_', $name); - - //every remaining unallowed characters will be removed - $name = preg_replace('/[^a-zA-Z0-9_.@-]/', '', $name); - - return $name; - } - - /** - * @brief combines the input filters with AND - * @param $filters array, the filters to connect - * @returns the combined filter - * - * Combines Filter arguments with AND - */ - static public function combineFilterWithAnd($filters) { - return self::combineFilter($filters,'&'); - } - - /** - * @brief combines the input filters with AND - * @param $filters array, the filters to connect - * @returns the combined filter - * - * Combines Filter arguments with AND - */ - static public function combineFilterWithOr($filters) { - return self::combineFilter($filters,'|'); - } - - /** - * @brief combines the input filters with given operator - * @param $filters array, the filters to connect - * @param $operator either & or | - * @returns the combined filter - * - * Combines Filter arguments with AND - */ - static private function combineFilter($filters, $operator) { - $combinedFilter = '('.$operator; - foreach($filters as $filter) { - if($filter[0] != '(') { - $filter = '('.$filter.')'; - } - $combinedFilter.=$filter; - } - $combinedFilter.=')'; - return $combinedFilter; - } - - /** - * Returns the LDAP handler - */ - static private function getConnectionResource() { - if(!self::$ldapConnectionRes) { - self::init(); - } - if(is_null(self::$ldapConnectionRes)) { - OCP\Util::writeLog('ldap', 'Connection could not be established', OCP\Util::INFO); - } - return self::$ldapConnectionRes; - } - - /** - * Caches the general LDAP configuration. - */ - static private function readConfiguration($force = false) { - if(!self::$configured || $force) { - self::$ldapHost = OCP\Config::getAppValue('user_ldap', 'ldap_host', ''); - self::$ldapPort = OCP\Config::getAppValue('user_ldap', 'ldap_port', 389); - self::$ldapAgentName = OCP\Config::getAppValue('user_ldap', 'ldap_dn',''); - self::$ldapAgentPassword = base64_decode(OCP\Config::getAppValue('user_ldap', 'ldap_agent_password','')); - self::$ldapBase = self::sanitizeDN(OCP\Config::getAppValue('user_ldap', 'ldap_base', '')); - self::$ldapBaseUsers = self::sanitizeDN(OCP\Config::getAppValue('user_ldap', 'ldap_base_users',self::$ldapBase)); - self::$ldapBaseGroups = self::sanitizeDN(OCP\Config::getAppValue('user_ldap', 'ldap_base_groups', self::$ldapBase)); - self::$ldapTLS = OCP\Config::getAppValue('user_ldap', 'ldap_tls',0); - self::$ldapNoCase = OCP\Config::getAppValue('user_ldap', 'ldap_nocase', 0); - self::$ldapUserDisplayName = strtolower(OCP\Config::getAppValue('user_ldap', 'ldap_display_name', 'uid')); - self::$ldapUserFilter = OCP\Config::getAppValue('user_ldap', 'ldap_userlist_filter','objectClass=person'); - self::$ldapLoginFilter = OCP\Config::getAppValue('user_ldap', 'ldap_login_filter', '(uid=%uid)'); - self::$ldapGroupDisplayName = strtolower(OCP\Config::getAppValue('user_ldap', 'ldap_group_display_name', LDAP_GROUP_DISPLAY_NAME_ATTR)); - self::$ldapIgnoreNamingRules = OCP\Config::getSystemValue('ldapIgnoreNamingRules', false); - - if(empty(self::$ldapBaseUsers)) { - OCP\Util::writeLog('ldap', 'Base for Users is empty, using Base DN', OCP\Util::INFO); - self::$ldapBaseUsers = self::$ldapBase; - } - if(empty(self::$ldapBaseGroups)) { - OCP\Util::writeLog('ldap', 'Base for Groups is empty, using Base DN', OCP\Util::INFO); - self::$ldapBaseGroups = self::$ldapBase; - } - - if( - !empty(self::$ldapHost) - && !empty(self::$ldapPort) - && ( - (!empty(self::$ldapAgentName) && !empty(self::$ldapAgentPassword)) - || ( empty(self::$ldapAgentName) && empty(self::$ldapAgentPassword)) - ) - && !empty(self::$ldapBase) - && !empty(self::$ldapUserDisplayName) - ) - { - self::$configured = true; - } - } - } - - /** - * Connects and Binds to LDAP - */ - static private function establishConnection() { - static $phpLDAPinstalled = true; - if(!$phpLDAPinstalled) { - return false; - } - if(!self::$configured) { - OCP\Util::writeLog('ldap', 'Configuration is invalid, cannot connect', OCP\Util::INFO); - return false; - } - if(!self::$ldapConnectionRes) { - //check if php-ldap is installed - if(!function_exists('ldap_connect')) { - $phpLDAPinstalled = false; - OCP\Util::writeLog('user_ldap', 'function ldap_connect is not available. Make sure that the PHP ldap module is installed.', OCP\Util::ERROR); - - return false; - } - self::$ldapConnectionRes = ldap_connect(self::$ldapHost, self::$ldapPort); - if(ldap_set_option(self::$ldapConnectionRes, LDAP_OPT_PROTOCOL_VERSION, 3)) { - if(ldap_set_option(self::$ldapConnectionRes, LDAP_OPT_REFERRALS, 0)) { - if(self::$ldapTLS) { - ldap_start_tls(self::$ldapConnectionRes); - } - } - } - - $ldapLogin = @ldap_bind(self::$ldapConnectionRes, self::$ldapAgentName, self::$ldapAgentPassword ); - if(!$ldapLogin) { - OCP\Util::writeLog('ldap', 'Bind failed: ' . ldap_errno(self::$ldapConnectionRes) . ': ' . ldap_error(self::$ldapConnectionRes), OCP\Util::ERROR); - return false; - } - } - } - - static public function areCredentialsValid($name, $password) { - return @ldap_bind(self::getConnectionResource(), $name, $password); - } - - /** - * taken from http://www.php.net/manual/en/function.array-search.php#97645 - * TODO: move somewhere, where its better placed since it is not LDAP specific. OC_Helper maybe? - */ - static public function recursiveArraySearch($haystack, $needle, $index = null) { - $aIt = new RecursiveArrayIterator($haystack); - $it = new RecursiveIteratorIterator($aIt); - - while($it->valid()) { - if (((isset($index) AND ($it->key() == $index)) OR (!isset($index))) AND ($it->current() == $needle)) { - return $aIt->key(); - } - - $it->next(); - } - - return false; - } - - } diff --git a/apps/user_ldap/settings.php b/apps/user_ldap/settings.php index 0c29e70b5ab..39aa3296265 100644 --- a/apps/user_ldap/settings.php +++ b/apps/user_ldap/settings.php @@ -20,34 +20,43 @@ * License along with this library. If not, see <http://www.gnu.org/licenses/>. * */ -$params = array('ldap_host', 'ldap_port', 'ldap_dn', 'ldap_agent_password', 'ldap_base', 'ldap_base_users', 'ldap_base_groups', 'ldap_userlist_filter', 'ldap_login_filter', 'ldap_group_filter', 'ldap_display_name', 'ldap_group_display_name', 'ldap_tls', 'ldap_nocase', 'ldap_quota_def', 'ldap_quota_attr', 'ldap_email_attr', 'ldap_group_member_assoc_attribute'); +$params = array('ldap_host', 'ldap_port', 'ldap_dn', 'ldap_agent_password', 'ldap_base', 'ldap_base_users', 'ldap_base_groups', 'ldap_userlist_filter', 'ldap_login_filter', 'ldap_group_filter', 'ldap_display_name', 'ldap_group_display_name', 'ldap_tls', 'ldap_turn_off_cert_check', 'ldap_nocase', 'ldap_quota_def', 'ldap_quota_attr', 'ldap_email_attr', 'ldap_group_member_assoc_attribute', 'ldap_cache_ttl'); OCP\Util::addscript('user_ldap', 'settings'); +OCP\Util::addstyle('user_ldap', 'settings'); if ($_POST) { foreach($params as $param){ if(isset($_POST[$param])){ if('ldap_agent_password' == $param) { OCP\Config::setAppValue('user_ldap', $param, base64_encode($_POST[$param])); + } elseif('ldap_cache_ttl' == $param) { + if(OCP\Config::getAppValue('user_ldap', $param,'') != $_POST[$param]) { + $ldap = new \OCA\user_ldap\lib\Connection('user_ldap'); + $ldap->clearCache(); + OCP\Config::setAppValue('user_ldap', $param, $_POST[$param]); + } } else { OCP\Config::setAppValue('user_ldap', $param, $_POST[$param]); } } elseif('ldap_tls' == $param) { // unchecked checkboxes are not included in the post paramters - OCP\Config::setAppValue('user_ldap', $param, 0); + OCP\Config::setAppValue('user_ldap', $param, 0); } elseif('ldap_nocase' == $param) { OCP\Config::setAppValue('user_ldap', $param, 0); } - + elseif('ldap_turn_off_cert_check' == $param) { + OCP\Config::setAppValue('user_ldap', $param, 0); + } } } // fill template $tmpl = new OCP\Template( 'user_ldap', 'settings'); foreach($params as $param){ - $value = htmlentities(OCP\Config::getAppValue('user_ldap', $param,'')); + $value = OCP\Config::getAppValue('user_ldap', $param,''); $tmpl->assign($param, $value); } @@ -57,5 +66,6 @@ $tmpl->assign( 'ldap_display_name', OCP\Config::getAppValue('user_ldap', 'ldap_d $tmpl->assign( 'ldap_group_display_name', OCP\Config::getAppValue('user_ldap', 'ldap_group_display_name', 'cn')); $tmpl->assign( 'ldap_group_member_assoc_attribute', OCP\Config::getAppValue('user_ldap', 'ldap_group_member_assoc_attribute', 'uniqueMember')); $tmpl->assign( 'ldap_agent_password', base64_decode(OCP\Config::getAppValue('user_ldap', 'ldap_agent_password'))); +$tmpl->assign( 'ldap_cache_ttl', OCP\Config::getAppValue('user_ldap', 'ldap_cache_ttl', '600')); return $tmpl->fetchPage(); diff --git a/apps/user_ldap/templates/settings.php b/apps/user_ldap/templates/settings.php index 31f453b5a5a..861c9ba6052 100644 --- a/apps/user_ldap/templates/settings.php +++ b/apps/user_ldap/templates/settings.php @@ -5,29 +5,30 @@ <li><a href="#ldapSettings-2">Advanced</a></li> </ul> <fieldset id="ldapSettings-1"> - <p><label for="ldap_host"><?php echo $l->t('Host');?><input type="text" id="ldap_host" name="ldap_host" value="<?php echo $_['ldap_host']; ?>"></label> <label for="ldap_base"><?php echo $l->t('Base');?></label><input type="text" id="ldap_base" name="ldap_base" value="<?php echo $_['ldap_base']; ?>" /></p> - <p><label for="ldap_dn"><?php echo $l->t('Name');?></label><input type="text" id="ldap_dn" name="ldap_dn" value="<?php echo $_['ldap_dn']; ?>" /> - <label for="ldap_agent_password"><?php echo $l->t('Password');?></label><input type="password" id="ldap_agent_password" name="ldap_agent_password" value="<?php echo $_['ldap_agent_password']; ?>" /> - <small><?php echo $l->t('Leave both empty for anonymous bind for search, then bind with users credentials.');?></small></p> - <p><label for="ldap_login_filter"><?php echo $l->t('User Login Filter');?></label><input type="text" id="ldap_login_filter" name="ldap_login_filter" value="<?php echo $_['ldap_login_filter']; ?>" /><small><?php echo $l->t('use %%uid placeholder, e.g. uid=%%uid');?></small></p> - <p><label for="ldap_userlist_filter"><?php echo $l->t('User List Filter');?></label><input type="text" id="ldap_userlist_filter" name="ldap_userlist_filter" value="<?php echo $_['ldap_userlist_filter']; ?>" /><small><?php echo $l->t('without any placeholder, e.g. "objectClass=person".');?></small></p> - <p><label for="ldap_group_filter"><?php echo $l->t('Group Filter');?></label><input type="text" id="ldap_group_filter" name="ldap_group_filter" value="<?php echo $_['ldap_group_filter']; ?>" /><small><?php echo $l->t('without any placeholder, e.g. "objectClass=posixGroup".');?></small></p> + <p><label for="ldap_host"><?php echo $l->t('Host');?></label><input type="text" id="ldap_host" name="ldap_host" value="<?php echo $_['ldap_host']; ?>" title="<?php echo $l->t('You can omit the protocol, except you require SSL. Then start with ldaps://');?>"></p> + <p><label for="ldap_base"><?php echo $l->t('Base DN');?></label><input type="text" id="ldap_base" name="ldap_base" value="<?php echo $_['ldap_base']; ?>" title="<?php echo $l->t('You can specify Base DN for users and groups in the Advanced tab');?>" /></p> + <p><label for="ldap_dn"><?php echo $l->t('User DN');?></label><input type="text" id="ldap_dn" name="ldap_dn" value="<?php echo $_['ldap_dn']; ?>" title="<?php echo $l->t('The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty.');?>" /></p> + <p><label for="ldap_agent_password"><?php echo $l->t('Password');?></label><input type="password" id="ldap_agent_password" name="ldap_agent_password" value="<?php echo $_['ldap_agent_password']; ?>" title="<?php echo $l->t('For anonymous access, leave DN and Password empty.');?>" /></p> + <p><label for="ldap_login_filter"><?php echo $l->t('User Login Filter');?></label><input type="text" id="ldap_login_filter" name="ldap_login_filter" value="<?php echo $_['ldap_login_filter']; ?>" title="<?php echo $l->t('Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action.');?>" /><br /><small><?php echo $l->t('use %%uid placeholder, e.g. "uid=%%uid"');?></small></p> + <p><label for="ldap_userlist_filter"><?php echo $l->t('User List Filter');?></label><input type="text" id="ldap_userlist_filter" name="ldap_userlist_filter" value="<?php echo $_['ldap_userlist_filter']; ?>" title="<?php echo $l->t('Defines the filter to apply, when retrieving users.');?>" /><br /><small><?php echo $l->t('without any placeholder, e.g. "objectClass=person".');?></small></p> + <p><label for="ldap_group_filter"><?php echo $l->t('Group Filter');?></label><input type="text" id="ldap_group_filter" name="ldap_group_filter" value="<?php echo $_['ldap_group_filter']; ?>" title="<?php echo $l->t('Defines the filter to apply, when retrieving groups.');?>" /><br /><small><?php echo $l->t('without any placeholder, e.g. "objectClass=posixGroup".');?></small></p> </fieldset> <fieldset id="ldapSettings-2"> <p><label for="ldap_port"><?php echo $l->t('Port');?></label><input type="text" id="ldap_port" name="ldap_port" value="<?php echo $_['ldap_port']; ?>" /></p> <p><label for="ldap_base_users"><?php echo $l->t('Base User Tree');?></label><input type="text" id="ldap_base_users" name="ldap_base_users" value="<?php echo $_['ldap_base_users']; ?>" /></p> <p><label for="ldap_base_groups"><?php echo $l->t('Base Group Tree');?></label><input type="text" id="ldap_base_groups" name="ldap_base_groups" value="<?php echo $_['ldap_base_groups']; ?>" /></p> <p><label for="ldap_group_member_assoc_attribute"><?php echo $l->t('Group-Member association');?></label><select id="ldap_group_member_assoc_attribute" name="ldap_group_member_assoc_attribute"><option value="uniqueMember"<?php if (isset($_['ldap_group_member_assoc_attribute']) && ($_['ldap_group_member_assoc_attribute'] == 'uniqueMember')) echo ' selected'; ?>>uniqueMember</option><option value="memberUid"<?php if (isset($_['ldap_group_member_assoc_attribute']) && ($_['ldap_group_member_assoc_attribute'] == 'memberUid')) echo ' selected'; ?>>memberUid</option><option value="member"<?php if (isset($_['ldap_group_member_assoc_attribute']) && ($_['ldap_group_member_assoc_attribute'] == 'member')) echo ' selected'; ?>>member (AD)</option></select></p> - <p><input type="checkbox" id="ldap_tls" name="ldap_tls" value="1"<?php if ($_['ldap_tls']) echo ' checked'; ?>><label for="ldap_tls"><?php echo $l->t('Use TLS');?></label></p> - <p><input type="checkbox" id="ldap_nocase" name="ldap_nocase" value="1"<?php if (isset($_['ldap_nocase']) && ($_['ldap_nocase'])) echo ' checked'; ?>><label for="ldap_nocase"><?php echo $l->t('Case insensitve LDAP server (Windows)');?></label></p> - <p><label for="ldap_display_name"><?php echo $l->t('Display Name Field');?></label><input type="text" id="ldap_display_name" name="ldap_display_name" value="<?php echo $_['ldap_display_name']; ?>" /> - <small><?php echo $l->t('Currently the display name field needs to be the same you matched %%uid against in the filter above, because ownCloud doesn\'t distinguish between user id and user name.');?></small></p> - <p><label for="ldap_group_display_name"><?php echo $l->t('Group Display Name Field');?></label><input type="text" id="ldap_group_display_name" name="ldap_group_display_name" value="<?php echo $_['ldap_group_display_name']; ?>" /></p> - <p><label for="ldap_quota_attr">Quota Attribute</label><input type="text" id="ldap_quota_attr" name="ldap_quota_attr" value="<?php echo $_['ldap_quota_attr']; ?>" /> - <label for="ldap_quota_def">Quota Default</label><input type="text" id="ldap_quota_def" name="ldap_quota_def" value="<?php if (isset($_['ldap_quota_def'])) echo $_['ldap_quota_def']; ?>" />bytes</p> - <p><label for="ldap_email_attr">Email Attribute</label><input type="text" id="ldap_email_attr" name="ldap_email_attr" value="<?php echo $_['ldap_email_attr']; ?>" /></p> + <p><label for="ldap_tls"><?php echo $l->t('Use TLS');?></label><input type="checkbox" id="ldap_tls" name="ldap_tls" value="1"<?php if ($_['ldap_tls']) echo ' checked'; ?> title="<?php echo $l->t('Do not use it for SSL connections, it will fail.');?>" /></p> + <p><label for="ldap_nocase"><?php echo $l->t('Case insensitve LDAP server (Windows)');?></label> <input type="checkbox" id="ldap_nocase" name="ldap_nocase" value="1"<?php if (isset($_['ldap_nocase']) && ($_['ldap_nocase'])) echo ' checked'; ?>></p> + <p><label for="ldap_turn_off_cert_check"><?php echo $l->t('Turn off SSL certificate validation.');?></label><input type="checkbox" id="ldap_turn_off_cert_check" name="ldap_turn_off_cert_check" title="<?php echo $l->t('If connection only works with this option, import the LDAP server\'s SSL certificate in your ownCloud server.');?>" value="1"<?php if ($_['ldap_turn_off_cert_check']) echo ' checked'; ?>><br/><small><?php echo $l->t('Not recommended, use for testing only.');?></small></p> + <p><label for="ldap_display_name"><?php echo $l->t('User Display Name Field');?></label><input type="text" id="ldap_display_name" name="ldap_display_name" value="<?php echo $_['ldap_display_name']; ?>" title="<?php echo $l->t('The LDAP attribute to use to generate the user`s ownCloud name.');?>" /></p> + <p><label for="ldap_group_display_name"><?php echo $l->t('Group Display Name Field');?></label><input type="text" id="ldap_group_display_name" name="ldap_group_display_name" value="<?php echo $_['ldap_group_display_name']; ?>" title="<?php echo $l->t('The LDAP attribute to use to generate the groups`s ownCloud name.');?>" /></p> + <p><label for="ldap_quota_attr">Quota Field</label><input type="text" id="ldap_quota_attr" name="ldap_quota_attr" value="<?php echo $_['ldap_quota_attr']; ?>" /> + <label for="ldap_quota_def">Quota Default</label><input type="text" id="ldap_quota_def" name="ldap_quota_def" value="<?php if (isset($_['ldap_quota_def'])) echo $_['ldap_quota_def']; ?>" title="<?php echo $l->t('in bytes');?>" /></p> + <p><label for="ldap_email_attr">Email Field</label><input type="text" id="ldap_email_attr" name="ldap_email_attr" value="<?php echo $_['ldap_email_attr']; ?>" /></p> + <p><label for="ldap_cache_ttl">Cache Time-To-Live</label><input type="text" id="ldap_cache_ttl" name="ldap_cache_ttl" value="<?php echo $_['ldap_cache_ttl']; ?>" title="<?php echo $l->t('in seconds. A change empties the cache.');?>" /></p> </fieldset> - <input type="submit" value="Save" /> <a href="http://owncloud.org/support/ldap-backend/" target="_blank"><img src="<?php echo OCP\Util::imagePath('','actions/info.png'); ?>" style="height:1.75ex" /> <?php echo $l->t('Help');?></a> + <input type="submit" value="Save" /> <button id="ldap_action_test_connection" name="ldap_action_test_connection">Test Configuration</button> <a href="http://owncloud.org/support/ldap-backend/" target="_blank"><img src="<?php echo OCP\Util::imagePath('','actions/info.png'); ?>" style="height:1.75ex" /> <?php echo $l->t('Help');?></a> </div> </form> diff --git a/apps/user_ldap/tests/group_ldap.php b/apps/user_ldap/tests/group_ldap.php index 2be6b46fb23..106459580fa 100644 --- a/apps/user_ldap/tests/group_ldap.php +++ b/apps/user_ldap/tests/group_ldap.php @@ -26,8 +26,8 @@ class Test_Group_Ldap extends UnitTestCase { } function testSingleBackend(){ - OC_Group::useBackend(new OC_GROUP_LDAP()); - $group_ldap = new OC_GROUP_LDAP(); + OC_Group::useBackend(new OCA\user_ldap\GROUP_LDAP()); + $group_ldap = new OCA\user_ldap\GROUP_LDAP(); $this->assertIsA(OC_Group::getGroups(),gettype(array())); $this->assertIsA($group_ldap->getGroups(),gettype(array())); diff --git a/apps/user_ldap/user_ldap.php b/apps/user_ldap/user_ldap.php index da99e167fd1..a97df7b4fd1 100644 --- a/apps/user_ldap/user_ldap.php +++ b/apps/user_ldap/user_ldap.php @@ -23,53 +23,36 @@ * */ -class OC_USER_LDAP extends OC_User_Backend { +namespace OCA\user_ldap; - // cached settings - protected $ldapUserFilter; - protected $ldapQuotaAttribute; - protected $ldapQuotaDefault; - protected $ldapEmailAttribute; - - // will be retrieved from LDAP server - protected $ldap_dc = false; - - // cache getUsers() - protected $_users = null; - - public function __construct() { - $this->ldapUserFilter = OCP\Config::getAppValue('user_ldap', 'ldap_userlist_filter', '(objectClass=posixAccount)'); - $this->ldapQuotaAttribute = OCP\Config::getAppValue('user_ldap', 'ldap_quota_attr', ''); - $this->ldapQuotaDefault = OCP\Config::getAppValue('user_ldap', 'ldap_quota_def', ''); - $this->ldapEmailAttribute = OCP\Config::getAppValue('user_ldap', 'ldap_email_attr', ''); - } +class USER_LDAP extends lib\Access implements \OCP\UserInterface { private function updateQuota($dn) { $quota = null; - if(!empty($this->ldapQuotaDefault)) { - $quota = $this->ldapQuotaDefault; + if(!empty($this->connection->ldapQuotaDefault)) { + $quota = $this->connection->ldapQuotaDefault; } - if(!empty($this->ldapQuotaAttribute)) { - $aQuota = OC_LDAP::readAttribute($dn, $this->ldapQuotaAttribute); + if(!empty($this->connection->ldapQuotaAttribute)) { + $aQuota = $this->readAttribute($dn, $this->connection->ldapQuotaAttribute); if($aQuota && (count($aQuota) > 0)) { $quota = $aQuota[0]; } } if(!is_null($quota)) { - OCP\Config::setUserValue(OC_LDAP::dn2username($dn), 'files', 'quota', OCP\Util::computerFileSize($quota)); + \OCP\Config::setUserValue($this->dn2username($dn), 'files', 'quota', \OCP\Util::computerFileSize($quota)); } } private function updateEmail($dn) { $email = null; - if(!empty($this->ldapEmailAttribute)) { - $aEmail = OC_LDAP::readAttribute($dn, $this->ldapEmailAttribute); + if(!empty($this->connection->ldapEmailAttribute)) { + $aEmail = $this->readAttribute($dn, $this->connection->ldapEmailAttribute); if($aEmail && (count($aEmail) > 0)) { $email = $aEmail[0]; } - if(!is_null($email)){ - OCP\Config::setUserValue(OC_LDAP::dn2username($dn), 'settings', 'email', $email); + if(!is_null($email)) { + \OCP\Config::setUserValue($this->dn2username($dn), 'settings', 'email', $email); } } } @@ -84,26 +67,27 @@ class OC_USER_LDAP extends OC_User_Backend { */ public function checkPassword($uid, $password){ //find out dn of the user name - $filter = str_replace('%uid', $uid, OC_LDAP::conf('ldapLoginFilter')); - $ldap_users = OC_LDAP::fetchListOfUsers($filter, 'dn'); + $filter = \OCP\Util::mb_str_replace('%uid', $uid, $this->connection->ldapLoginFilter, 'UTF-8'); + $ldap_users = $this->fetchListOfUsers($filter, 'dn'); if(count($ldap_users) < 1) { return false; } $dn = $ldap_users[0]; //are the credentials OK? - if(!OC_LDAP::areCredentialsValid($dn, $password)) { + if(!$this->areCredentialsValid($dn, $password)) { return false; } //do we have a username for him/her? - $ocname = OC_LDAP::dn2username($dn); + $ocname = $this->dn2username($dn); if($ocname){ //update some settings, if necessary $this->updateQuota($dn); $this->updateEmail($dn); + //give back the display name return $ocname; } @@ -116,12 +100,25 @@ class OC_USER_LDAP extends OC_User_Backend { * * Get a list of all users. */ - public function getUsers(){ - if(is_null($this->_users)) { - $ldap_users = OC_LDAP::fetchListOfUsers($this->ldapUserFilter, array(OC_LDAP::conf('ldapUserDisplayName'), 'dn')); - $this->_users = OC_LDAP::ownCloudUserNames($ldap_users); + public function getUsers($search = '', $limit = 10, $offset = 0){ + $ldap_users = $this->connection->getFromCache('getUsers'); + if(is_null($ldap_users)) { + $ldap_users = $this->fetchListOfUsers($this->connection->ldapUserFilter, array($this->connection->ldapUserDisplayName, 'dn')); + $ldap_users = $this->ownCloudUserNames($ldap_users); + $this->connection->writeToCache('getUsers', $ldap_users); + } + $this->userSearch = $search; + if(!empty($this->userSearch)) { + $ldap_users = array_filter($ldap_users, array($this, 'userMatchesFilter')); } - return $this->_users; + if($limit = -1) { + $limit = null; + } + return array_slice($ldap_users, $offset, $limit); + } + + public function userMatchesFilter($user) { + return (strripos($user, $this->userSearch) !== false); } /** @@ -130,19 +127,49 @@ class OC_USER_LDAP extends OC_User_Backend { * @return boolean */ public function userExists($uid){ + if($this->connection->isCached('userExists'.$uid)) { + return $this->connection->getFromCache('userExists'.$uid); + } + //getting dn, if false the user does not exist. If dn, he may be mapped only, requires more checking. - $dn = OC_LDAP::username2dn($uid); + $dn = $this->username2dn($uid); if(!$dn) { + $this->connection->writeToCache('userExists'.$uid, false); return false; } - //if user really still exists, we will be able to read his cn - $cn = OC_LDAP::readAttribute($dn, 'cn'); - if(!$cn || empty($cn)) { + //if user really still exists, we will be able to read his objectclass + $objcs = $this->readAttribute($dn, 'objectclass'); + if(!$objcs || empty($objcs)) { + $this->connection->writeToCache('userExists'.$uid, false); return false; } + $this->connection->writeToCache('userExists'.$uid, true); return true; } + /** + * @brief delete a user + * @param $uid The username of the user to delete + * @returns true/false + * + * Deletes a user + */ + public function deleteUser($uid) { + return false; + } + + /** + * @brief Check if backend implements actions + * @param $actions bitwise-or'ed actions + * @returns boolean + * + * Returns the supported actions as int to be + * compared with OC_USER_BACKEND_CREATE_USER etc. + */ + public function implementsActions($actions) { + return (bool)(OC_USER_BACKEND_CHECK_PASSWORD & $actions); + } + }
\ No newline at end of file diff --git a/apps/user_migrate/appinfo/app.php b/apps/user_migrate/appinfo/app.php index 9d314b59ce7..366c4004932 100644 --- a/apps/user_migrate/appinfo/app.php +++ b/apps/user_migrate/appinfo/app.php @@ -31,4 +31,3 @@ $entry = array( 'href' => OCP\Util::linkTo( "user_migrate", "admin.php" ), 'name' => 'Import' ); -?> diff --git a/apps/user_migrate/l10n/.gitkeep b/apps/user_migrate/l10n/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/apps/user_migrate/l10n/.gitkeep diff --git a/apps/user_migrate/l10n/ca.php b/apps/user_migrate/l10n/ca.php new file mode 100644 index 00000000000..6e423c01b0b --- /dev/null +++ b/apps/user_migrate/l10n/ca.php @@ -0,0 +1,10 @@ +<?php $TRANSLATIONS = array( +"Export" => "Exporta", +"Something went wrong while the export file was being generated" => "Alguna cosa ha anat malament en generar el fitxer d'exportació", +"An error has occurred" => "S'ha produït un error", +"Export your user account" => "Exportar el compte d'usuari", +"This will create a compressed file that contains your ownCloud account." => "Això crearà un fitxer comprimit que conté el vostre compte ownCloud.", +"Import user account" => "Importar el compte d'usuari", +"ownCloud User Zip" => "zip d'usuari ownCloud", +"Import" => "Importa" +); diff --git a/apps/user_migrate/l10n/cs_CZ.php b/apps/user_migrate/l10n/cs_CZ.php new file mode 100644 index 00000000000..81a5a45d978 --- /dev/null +++ b/apps/user_migrate/l10n/cs_CZ.php @@ -0,0 +1,10 @@ +<?php $TRANSLATIONS = array( +"Export" => "Export", +"Something went wrong while the export file was being generated" => "BÄ›hem vytvářenà souboru exportu doÅ¡lo k chybÄ›", +"An error has occurred" => "Nastala chyba", +"Export your user account" => "Export VaÅ¡eho uživatelského úÄtu", +"This will create a compressed file that contains your ownCloud account." => "Bude vytvoÅ™en komprimovaný soubor, obsahujÃcà Váš ownCloud úÄet.", +"Import user account" => "Import uživatelského úÄtu", +"ownCloud User Zip" => "Zip soubor uživatele ownCloud", +"Import" => "Import" +); diff --git a/apps/user_migrate/l10n/de.php b/apps/user_migrate/l10n/de.php new file mode 100644 index 00000000000..928a211dd4e --- /dev/null +++ b/apps/user_migrate/l10n/de.php @@ -0,0 +1,10 @@ +<?php $TRANSLATIONS = array( +"Export" => "Export", +"Something went wrong while the export file was being generated" => "Beim Export der Datei ist etwas schiefgegangen.", +"An error has occurred" => "Es ist ein Fehler aufgetreten.", +"Export your user account" => "Ihr Konto exportieren", +"This will create a compressed file that contains your ownCloud account." => "Eine komprimierte Datei wird erzeugt, die Ihr ownCloud-Konto enthält.", +"Import user account" => "Konto importieren", +"ownCloud User Zip" => "Zip-Archiv mit Benutzerdaten", +"Import" => "Importieren" +); diff --git a/apps/user_migrate/l10n/el.php b/apps/user_migrate/l10n/el.php new file mode 100644 index 00000000000..5b57e69a637 --- /dev/null +++ b/apps/user_migrate/l10n/el.php @@ -0,0 +1,8 @@ +<?php $TRANSLATIONS = array( +"Export" => "Εξαγωγή", +"An error has occurred" => "ΠαÏουσιάστηκε σφάλμα", +"Export your user account" => "Εξαγωγή του λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï Ï‡Ïήστη σας", +"This will create a compressed file that contains your ownCloud account." => "Αυτό θα δημιουÏγήσει Îνα συμπιεσμÎνο αÏχείο που θα πεÏιÎχει τον λογαÏιασμό σας ownCloud.", +"Import user account" => "Εισαγωγή λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï Ï‡Ïήστη", +"Import" => "Εισαγωγή" +); diff --git a/apps/user_migrate/l10n/eo.php b/apps/user_migrate/l10n/eo.php new file mode 100644 index 00000000000..ca918061691 --- /dev/null +++ b/apps/user_migrate/l10n/eo.php @@ -0,0 +1,10 @@ +<?php $TRANSLATIONS = array( +"Export" => "Malenporti", +"Something went wrong while the export file was being generated" => "Io malsukcesis dum la enportota dosiero generiÄis", +"An error has occurred" => "Eraro okazis", +"Export your user account" => "Malenporti vian uzantokonton", +"This will create a compressed file that contains your ownCloud account." => "Ĉi tio kreos densigitan dosieron, kiu enhavas vian konton de ownCloud.", +"Import user account" => "Enporti uzantokonton", +"ownCloud User Zip" => "ZIP-dosiero de uzanto de ownCloud", +"Import" => "Enporti" +); diff --git a/apps/user_migrate/l10n/es.php b/apps/user_migrate/l10n/es.php new file mode 100644 index 00000000000..6a0551fe235 --- /dev/null +++ b/apps/user_migrate/l10n/es.php @@ -0,0 +1,5 @@ +<?php $TRANSLATIONS = array( +"Export" => "Exportar", +"ownCloud User Zip" => "Zip de usuario de ownCloud", +"Import" => "Importar" +); diff --git a/apps/user_migrate/l10n/fi_FI.php b/apps/user_migrate/l10n/fi_FI.php new file mode 100644 index 00000000000..2616bee6f2a --- /dev/null +++ b/apps/user_migrate/l10n/fi_FI.php @@ -0,0 +1,9 @@ +<?php $TRANSLATIONS = array( +"Export" => "Vie", +"Something went wrong while the export file was being generated" => "Jokin meni pieleen vientiä suorittaessa", +"An error has occurred" => "Tapahtui virhe", +"Export your user account" => "Vie käyttäjätilisi", +"This will create a compressed file that contains your ownCloud account." => "Tämä luo ownCloud-käyttäjätilisi sisältävän pakatun tiedoston.", +"Import user account" => "Tuo käyttäjätili", +"Import" => "Tuo" +); diff --git a/apps/user_migrate/l10n/fr.php b/apps/user_migrate/l10n/fr.php new file mode 100644 index 00000000000..70ab2f8f22d --- /dev/null +++ b/apps/user_migrate/l10n/fr.php @@ -0,0 +1,10 @@ +<?php $TRANSLATIONS = array( +"Export" => "Exporter", +"Something went wrong while the export file was being generated" => "Une erreur s'est produite pendant la génération du fichier d'export", +"An error has occurred" => "Une erreur s'est produite", +"Export your user account" => "Exportez votre compte utilisateur", +"This will create a compressed file that contains your ownCloud account." => "Cette action va créer une archive compressée qui contiendra les données de votre compte ownCloud.", +"Import user account" => "Importer un compte utilisateur", +"ownCloud User Zip" => "Archive Zip de l'utilisateur", +"Import" => "Importer" +); diff --git a/apps/user_migrate/l10n/it.php b/apps/user_migrate/l10n/it.php new file mode 100644 index 00000000000..f3eeb15dab5 --- /dev/null +++ b/apps/user_migrate/l10n/it.php @@ -0,0 +1,10 @@ +<?php $TRANSLATIONS = array( +"Export" => "Esporta", +"Something went wrong while the export file was being generated" => "Si è verificato un errore durante la creazione del file di esportazione", +"An error has occurred" => "Si è verificato un errore", +"Export your user account" => "Esporta il tuo account utente", +"This will create a compressed file that contains your ownCloud account." => "Questa operazione creerà un file compresso che contiene il tuo account ownCloud.", +"Import user account" => "Importa account utente", +"ownCloud User Zip" => "Zip account utente", +"Import" => "Importa" +); diff --git a/apps/user_migrate/l10n/ja_JP.php b/apps/user_migrate/l10n/ja_JP.php new file mode 100644 index 00000000000..86c66e6319c --- /dev/null +++ b/apps/user_migrate/l10n/ja_JP.php @@ -0,0 +1,10 @@ +<?php $TRANSLATIONS = array( +"Export" => "エクスãƒãƒ¼ãƒˆ", +"Something went wrong while the export file was being generated" => "エクスãƒãƒ¼ãƒˆãƒ•ã‚¡ã‚¤ãƒ«ã®ç”Ÿæˆæ™‚ã«ä½•ã‹ä¸å…·åˆãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚", +"An error has occurred" => "エラーãŒç™ºç”Ÿã—ã¾ã—ãŸ", +"Export your user account" => "ユーザアカウントã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ", +"This will create a compressed file that contains your ownCloud account." => "ã‚ãªãŸã®ownCloudアカウントをå«ã‚€åœ§ç¸®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’生æˆã—ã¾ã™ã€‚", +"Import user account" => "ユーザアカウントをインãƒãƒ¼ãƒˆ", +"ownCloud User Zip" => "ownCloudユーザZip", +"Import" => "インãƒãƒ¼ãƒˆ" +); diff --git a/apps/user_migrate/l10n/lt_LT.php b/apps/user_migrate/l10n/lt_LT.php new file mode 100644 index 00000000000..20add8037bf --- /dev/null +++ b/apps/user_migrate/l10n/lt_LT.php @@ -0,0 +1,10 @@ +<?php $TRANSLATIONS = array( +"Export" => "Eksportuoti", +"Something went wrong while the export file was being generated" => "Ä®vyko klaida kuriant eksportuojamÄ… failÄ…", +"An error has occurred" => "Ä®vyko klaida", +"Export your user account" => "Eksportuoti jÅ«sų vartotojo paskyrÄ…", +"This will create a compressed file that contains your ownCloud account." => "Bus sukurtas suglaudintas failas su jÅ«sų ownCloud vartotojo paskyra.", +"Import user account" => "Importuoti vartotojo paskyrÄ…", +"ownCloud User Zip" => "ownCloud vartotojo paskyros Zip archyvas", +"Import" => "Importuoti" +); diff --git a/apps/user_migrate/l10n/pl.php b/apps/user_migrate/l10n/pl.php new file mode 100644 index 00000000000..c3a6332578e --- /dev/null +++ b/apps/user_migrate/l10n/pl.php @@ -0,0 +1,10 @@ +<?php $TRANSLATIONS = array( +"Export" => "Eksport", +"Something went wrong while the export file was being generated" => "CoÅ› poszÅ‚o źle, podczas generowania pliku eksportu", +"An error has occurred" => "WystÄ…piÅ‚ bÅ‚Ä…d", +"Export your user account" => "Eksportuj konto użytkownika", +"This will create a compressed file that contains your ownCloud account." => "Spowoduje to utworzenie pliku skompresowanego, który zawiera konto ownCloud.", +"Import user account" => "Importuj konto użytkownika", +"ownCloud User Zip" => "paczka Zip użytkownika ownCloud", +"Import" => "Importuj" +); diff --git a/apps/user_migrate/l10n/sl.php b/apps/user_migrate/l10n/sl.php new file mode 100644 index 00000000000..c14bca0f709 --- /dev/null +++ b/apps/user_migrate/l10n/sl.php @@ -0,0 +1,10 @@ +<?php $TRANSLATIONS = array( +"Export" => "Izvozi", +"Something went wrong while the export file was being generated" => "Med ustvarjanjem datoteke za izvoz je priÅ¡lo do napake", +"An error has occurred" => "PriÅ¡lo je do napake", +"Export your user account" => "Izvozi vaÅ¡ uporabniÅ¡ki raÄun", +"This will create a compressed file that contains your ownCloud account." => "Ustvarjena bo stisnjena datoteka z vaÅ¡im ownCloud raÄunom.", +"Import user account" => "Uvozi uporabniÅ¡ki raÄun", +"ownCloud User Zip" => "Zip datoteka ownCloud uporabnika", +"Import" => "Uvozi" +); diff --git a/apps/user_migrate/l10n/sv.php b/apps/user_migrate/l10n/sv.php new file mode 100644 index 00000000000..98e649632b8 --- /dev/null +++ b/apps/user_migrate/l10n/sv.php @@ -0,0 +1,10 @@ +<?php $TRANSLATIONS = array( +"Export" => "Exportera", +"Something went wrong while the export file was being generated" => "NÃ¥got gick fel när exportfilen skulle genereras", +"An error has occurred" => "Ett fel har uppstÃ¥tt", +"Export your user account" => "Exportera ditt användarkonto", +"This will create a compressed file that contains your ownCloud account." => "Detta vill skapa en komprimerad fil som innehÃ¥ller ditt ownCloud-konto.", +"Import user account" => "Importera ett användarkonto", +"ownCloud User Zip" => "ownCloud Zip-fil", +"Import" => "Importera" +); diff --git a/apps/user_migrate/l10n/th_TH.php b/apps/user_migrate/l10n/th_TH.php new file mode 100644 index 00000000000..d2c36bb4edd --- /dev/null +++ b/apps/user_migrate/l10n/th_TH.php @@ -0,0 +1,10 @@ +<?php $TRANSLATIONS = array( +"Export" => "ส่งà¸à¸à¸", +"Something went wrong while the export file was being generated" => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดบางประà¸à¸²à¸£à¹ƒà¸™à¸£à¸°à¸«à¸§à¹ˆà¸²à¸‡à¸à¸²à¸£à¸ªà¹ˆà¸‡à¸à¸à¸à¹„ฟล์", +"An error has occurred" => "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดบางประà¸à¸²à¸£", +"Export your user account" => "ส่งà¸à¸à¸à¸šà¸±à¸à¸Šà¸µà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸‚à¸à¸‡à¸„ุณ", +"This will create a compressed file that contains your ownCloud account." => "ส่วนนี้จะเป็นà¸à¸²à¸£à¸ªà¸£à¹‰à¸²à¸‡à¹„ฟล์บีบà¸à¸±à¸”ที่บรรจุข้à¸à¸¡à¸¹à¸¥à¸šà¸±à¸à¸Šà¸µà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ ownCloud ขà¸à¸‡à¸„ุณ", +"Import user account" => "นำเข้าบัà¸à¸Šà¸µà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™", +"ownCloud User Zip" => "ไฟล์ Zip ผู้ใช้งาน ownCloud", +"Import" => "นำเข้า" +); diff --git a/apps/user_migrate/templates/settings.php b/apps/user_migrate/templates/settings.php index 1718abe9e0f..d6f3e2f8f6b 100644 --- a/apps/user_migrate/templates/settings.php +++ b/apps/user_migrate/templates/settings.php @@ -2,7 +2,7 @@ <legend><strong><?php echo $l->t('Export your user account');?></strong></legend> <p><?php echo $l->t('This will create a compressed file that contains your ownCloud account.');?> </p> - <button id="exportbtn">Export<img style="display: none;" class="loading" src="<?php echo OCP\Util::linkTo('core', 'img/loading.gif'); ?>" /></button> + <button id="exportbtn">Export<img style="display: none;" class="loading" src="<?php echo OCP\Util::imagePath('core', 'loading.gif'); ?>" /></button> </fieldset> <form id="import" action="#" method="post" enctype="multipart/form-data"> <fieldset class="personalblock"> @@ -12,7 +12,7 @@ <?php } ?> <legend><strong><?php echo $l->t('Import user account');?></strong></legend> </p> - <p><input type="file" id="owncloud_import" name="owncloud_import" style="width:180px;"><label for="owncloud_import"> <?php echo $l->t('ownCloud User Zip');?></label> + <p><input type="file" id="owncloud_import" name="owncloud_import" style="width:280px;"><label for="owncloud_import"> <?php echo $l->t('ownCloud User Zip');?></label> </p> <input type="submit" name="user_import" value="<?php echo $l->t('Import'); ?>" /> </fieldset> diff --git a/apps/user_openid/appinfo/app.php b/apps/user_openid/appinfo/app.php index c683254101f..fe57b189fac 100644 --- a/apps/user_openid/appinfo/app.php +++ b/apps/user_openid/appinfo/app.php @@ -27,7 +27,7 @@ OC_User::useBackend('openid'); //check for results from openid requests if(isset($_GET['openid_mode']) and $_GET['openid_mode'] == 'id_res'){ OCP\Util::writeLog('user_openid','openid retured',OCP\Util::DEBUG); - $openid = new SimpleOpenID; + $openid = new SimpleOpenID(); $openid->SetIdentity($_GET['openid_identity']); $openid_validation_result = $openid->ValidateWithServer(); if ($openid_validation_result == true){ // OK HERE KEY IS VALID @@ -50,5 +50,3 @@ if(isset($_GET['openid_mode']) and $_GET['openid_mode'] == 'id_res'){ OCP\Util::writeLog('user_openid','USER CANCELED REQUEST',OCP\Util::DEBUG); return false; } - -?> diff --git a/apps/user_openid/appinfo/info.xml b/apps/user_openid/appinfo/info.xml index 268af239738..7aae4271fa8 100644 --- a/apps/user_openid/appinfo/info.xml +++ b/apps/user_openid/appinfo/info.xml @@ -8,6 +8,7 @@ <require>4</require> <shipped>true</shipped> <types> + <prelogin/> <authentication/> </types> </info> diff --git a/apps/user_openid/appinfo/version b/apps/user_openid/appinfo/version index 6da28dde76d..d917d3e26ad 100644 --- a/apps/user_openid/appinfo/version +++ b/apps/user_openid/appinfo/version @@ -1 +1 @@ -0.1.1
\ No newline at end of file +0.1.2 diff --git a/apps/user_openid/class.openid.v3.php b/apps/user_openid/class.openid.v3.php deleted file mode 100644 index 8afb9e5b817..00000000000 --- a/apps/user_openid/class.openid.v3.php +++ /dev/null @@ -1,328 +0,0 @@ -<?php -/* - FREE TO USE - Under License: GPLv3 - Simple OpenID PHP Class - - Some modifications by Eddie Roosenmaallen, eddie@roosenmaallen.com - --=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= - -This Class was written to make easy for you to integrate OpenID on your website. -This is just a client, which checks for user's identity. This Class Requires CURL Module. -It should be easy to use some other HTTP Request Method, but remember, often OpenID servers -are using SSL. -We need to be able to perform SSL Verification on the background to check for valid signature. - -HOW TO USE THIS CLASS: - STEP 1) - $openid = new SimpleOpenID; - :: SET IDENTITY :: - $openid->SetIdentity($_POST['openid_url']); - :: SET RETURN URL :: - $openid->SetApprovedURL('http://www.yoursite.com/return.php'); // Script which handles a response from OpenID Server - :: SET TRUST ROOT :: - $openid->SetTrustRoot('http://www.yoursite.com/'); - :: FETCH SERVER URL FROM IDENTITY PAGE :: [Note: It is recomended to cache this (Session, Cookie, Database)] - $openid->GetOpenIDServer(); // Returns false if server is not found - :: REDIRECT USER TO OPEN ID SERVER FOR APPROVAL :: - - :: (OPTIONAL) SET OPENID SERVER :: - $openid->SetOpenIDServer($server_url); // If you have cached previously this, you don't have to call GetOpenIDServer and set value this directly - - STEP 2) - Once user gets returned we must validate signature - :: VALIDATE REQUEST :: - true|false = $openid->ValidateWithServer(); - - ERRORS: - array = $openid->GetError(); // Get latest Error code - - FIELDS: - OpenID allowes you to retreive a profile. To set what fields you'd like to get use (accepts either string or array): - $openid->SetRequiredFields(array('email','fullname','dob','gender','postcode','country','language','timezone')); - or - $openid->SetOptionalFields('postcode'); - -IMPORTANT TIPS: -OPENID as is now, is not trust system. It is a great single-sign on method. If you want to -store information about OpenID in your database for later use, make sure you handle url identities -properly. - For example: - https://steve.myopenid.com/ - https://steve.myopenid.com - http://steve.myopenid.com/ - http://steve.myopenid.com - ... are representing one single user. Some OpenIDs can be in format openidserver.com/users/user/ - keep this in mind when storing identities - - To help you store an OpenID in your DB, you can use function: - $openid_db_safe = $openid->OpenID_Standarize($upenid); - This may not be comatible with current specs, but it works in current enviroment. Use this function to get openid - in one format like steve.myopenid.com (without trailing slashes and http/https). - Use output to insert Identity to database. Don't use this for validation - it may fail. - -*/ - -class SimpleOpenID{ - var $openid_url_identity; - var $URLs = array(); - var $error = array(); - var $fields = array( - 'required' => array(), - 'optional' => array(), - ); - - function SimpleOpenID(){ - if (!function_exists('curl_exec')) { - die('Error: Class SimpleOpenID requires curl extension to work'); - } - } - function SetOpenIDServer($a){ - $this->URLs['openid_server'] = $a; - } - function SetTrustRoot($a){ - $this->URLs['trust_root'] = $a; - } - function SetCancelURL($a){ - $this->URLs['cancel'] = $a; - } - function SetApprovedURL($a){ - $this->URLs['approved'] = $a; - } - function SetRequiredFields($a){ - if (is_array($a)){ - $this->fields['required'] = $a; - }else{ - $this->fields['required'][] = $a; - } - } - function SetOptionalFields($a){ - if (is_array($a)){ - $this->fields['optional'] = $a; - }else{ - $this->fields['optional'][] = $a; - } - } - function SetIdentity($a){ // Set Identity URL - if ((stripos($a, 'http://') === false) - && (stripos($a, 'https://') === false)){ - $a = 'http://'.$a; - } -/* - $u = parse_url(trim($a)); - if (!isset($u['path'])){ - $u['path'] = '/'; - }else if(substr($u['path'],-1,1) == '/'){ - $u['path'] = substr($u['path'], 0, strlen($u['path'])-1); - } - if (isset($u['query'])){ // If there is a query string, then use identity as is - $identity = $a; - }else{ - $identity = $u['scheme'] . '://' . $u['host'] . $u['path']; - } -//*/ - $this->openid_url_identity = $a; - } - function GetIdentity(){ // Get Identity - return $this->openid_url_identity; - } - function GetError(){ - $e = $this->error; - return array('code'=>$e[0],'description'=>$e[1]); - } - - function ErrorStore($code, $desc = null){ - $errs['OPENID_NOSERVERSFOUND'] = 'Cannot find OpenID Server TAG on Identity page.'; - if ($desc == null){ - $desc = $errs[$code]; - } - $this->error = array($code,$desc); - } - - function IsError(){ - if (count($this->error) > 0){ - return true; - }else{ - return false; - } - } - - function splitResponse($response) { - $r = array(); - $response = explode("\n", $response); - foreach($response as $line) { - $line = trim($line); - if ($line != "") { - list($key, $value) = explode(":", $line, 2); - $r[trim($key)] = trim($value); - } - } - return $r; - } - - function OpenID_Standarize($openid_identity = null){ - if ($openid_identity === null) - $openid_identity = $this->openid_url_identity; - - $u = parse_url(strtolower(trim($openid_identity))); - - if (!isset($u['path']) || ($u['path'] == '/')) { - $u['path'] = ''; - } - if(substr($u['path'],-1,1) == '/'){ - $u['path'] = substr($u['path'], 0, strlen($u['path'])-1); - } - if (isset($u['query'])){ // If there is a query string, then use identity as is - return $u['host'] . $u['path'] . '?' . $u['query']; - }else{ - return $u['host'] . $u['path']; - } - } - - function array2url($arr){ // converts associated array to URL Query String - if (!is_array($arr)){ - return false; - } - $query = ''; - foreach($arr as $key => $value){ - $query .= $key . "=" . $value . "&"; - } - return $query; - } - function FSOCK_Request($url, $method="GET", $params = ""){ - $fp = fsockopen("ssl://www.myopenid.com", 443, $errno, $errstr, 3); // Connection timeout is 3 seconds - if (!$fp) { - $this->ErrorStore('OPENID_SOCKETERROR', $errstr); - return false; - } else { - $request = $method . " /server HTTP/1.0\r\n"; - $request .= "User-Agent: Simple OpenID PHP Class (http://www.phpclasses.org/simple_openid)\r\n"; - $request .= "Connection: close\r\n\r\n"; - fwrite($fp, $request); - stream_set_timeout($fp, 4); // Connection response timeout is 4 seconds - $res = fread($fp, 2000); - $info = stream_get_meta_data($fp); - fclose($fp); - - if ($info['timed_out']) { - $this->ErrorStore('OPENID_SOCKETTIMEOUT'); - } else { - return $res; - } - } - } - function CURL_Request($url, $method="GET", $params = "") { // Remember, SSL MUST BE SUPPORTED - if (is_array($params)) $params = $this->array2url($params); - $curl = curl_init($url . ($method == "GET" && $params != "" ? "?" . $params : "")); - curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($curl, CURLOPT_HEADER, false); - curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($curl, CURLOPT_HTTPGET, ($method == "GET")); - curl_setopt($curl, CURLOPT_POST, ($method == "POST")); - if ($method == "POST") curl_setopt($curl, CURLOPT_POSTFIELDS, $params); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - $response = curl_exec($curl); - - if (curl_errno($curl) == 0){ - $response; - }else{ - $this->ErrorStore('OPENID_CURL', curl_error($curl)); - } - return $response; - } - - function HTML2OpenIDServer($content) { - $get = array(); - - // Get details of their OpenID server and (optional) delegate - preg_match_all('/<link[^>]*rel=[\'"](openid2.provider )?openid.server[\'"][^>]*href=[\'"]([^\'"]+)[\'"][^>]*\/?>/i', $content, $matches1); - preg_match_all('/<link[^>]*href=\'"([^\'"]+)[\'"][^>]*rel=[\'"](openid2.provider )?openid.server[\'"][^>]*\/?>/i', $content, $matches2); - $servers = array_merge($matches1[2], $matches2[1]); - - preg_match_all('/<link[^>]*rel=[\'"]openid.delegate[\'"][^>]*href=[\'"]([^\'"]+)[\'"][^>]*\/?>/i', $content, $matches1); - - preg_match_all('/<link[^>]*href=[\'"]([^\'"]+)[\'"][^>]*rel=[\'"]openid.delegate[\'"][^>]*\/?>/i', $content, $matches2); - - $delegates = array_merge($matches1[1], $matches2[1]); - - $ret = array($servers, $delegates); - return $ret; - } - - function GetOpenIDServer(){ - $response = $this->CURL_Request($this->openid_url_identity); - list($servers, $delegates) = $this->HTML2OpenIDServer($response); - if (count($servers) == 0){ - $this->ErrorStore('OPENID_NOSERVERSFOUND'); - return false; - } - if (isset($delegates[0]) - && ($delegates[0] != "")){ - $this->SetIdentity($delegates[0]); - } - $this->SetOpenIDServer($servers[0]); - return $servers[0]; - } - - function GetRedirectURL(){ - $params = array(); - $params['openid.return_to'] = urlencode($this->URLs['approved']); - $params['openid.mode'] = 'checkid_setup'; - $params['openid.identity'] = urlencode($this->openid_url_identity); - $params['openid.trust_root'] = urlencode($this->URLs['trust_root']); - - if (isset($this->fields['required']) - && (count($this->fields['required']) > 0)) { - $params['openid.sreg.required'] = implode(',',$this->fields['required']); - } - if (isset($this->fields['optional']) - && (count($this->fields['optional']) > 0)) { - $params['openid.sreg.optional'] = implode(',',$this->fields['optional']); - } - return $this->URLs['openid_server'] . "?". $this->array2url($params); - } - - function Redirect(){ - $redirect_to = $this->GetRedirectURL(); - if (headers_sent()){ // Use JavaScript to redirect if content has been previously sent (not recommended, but safe) - echo '<script language="JavaScript" type="text/javascript">window.location=\''; - echo $redirect_to; - echo '\';</script>'; - }else{ // Default Header Redirect - header('Location: ' . $redirect_to); - } - } - - function ValidateWithServer(){ - $params = array( - 'openid.assoc_handle' => urlencode($_GET['openid_assoc_handle']), - 'openid.signed' => urlencode($_GET['openid_signed']), - 'openid.sig' => urlencode($_GET['openid_sig']) - ); - // Send only required parameters to confirm validity - $arr_signed = explode(",",str_replace('sreg.','sreg_',$_GET['openid_signed'])); - for ($i=0; $i<count($arr_signed); $i++){ - $s = str_replace('sreg_','sreg.', $arr_signed[$i]); - $c = $_GET['openid_' . $arr_signed[$i]]; - // if ($c != ""){ - $params['openid.' . $s] = urlencode($c); - // } - } - $params['openid.mode'] = "check_authentication"; - - $openid_server = $this->GetOpenIDServer(); - if ($openid_server == false){ - return false; - } - $response = $this->CURL_Request($openid_server,'POST',$params); - $data = $this->splitResponse($response); - - if ($data['is_valid'] == "true") { - return true; - }else{ - return false; - } - } -} - -?>
\ No newline at end of file diff --git a/apps/user_openid/l10n/.gitkeep b/apps/user_openid/l10n/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/apps/user_openid/l10n/.gitkeep diff --git a/apps/user_openid/l10n/ca.php b/apps/user_openid/l10n/ca.php new file mode 100644 index 00000000000..d203bfb4d9d --- /dev/null +++ b/apps/user_openid/l10n/ca.php @@ -0,0 +1,11 @@ +<?php $TRANSLATIONS = array( +"This is an OpenID server endpoint. For more information, see " => "Això és un punt final de servidor OpenID. Per més informació consulteu", +"Identity: <b>" => "Identitat:<b>", +"Realm: <b>" => "Domini:<b>", +"User: <b>" => "Usuari:<b>", +"Login" => "Inici de sessió", +"Error: <b>No user Selected" => "Error:<b>No heu seleccionat cap usuari", +"you can authenticate to other sites with this address" => "podeu autenticar altres llocs web amb aquesta adreça", +"Authorized OpenID provider" => "Servidor OpenID autoritzat", +"Your address at Wordpress, Identi.ca, …" => "La vostra adreça a Wordpress, Identi.ca …" +); diff --git a/apps/user_openid/l10n/cs_CZ.php b/apps/user_openid/l10n/cs_CZ.php new file mode 100644 index 00000000000..9c041a89a68 --- /dev/null +++ b/apps/user_openid/l10n/cs_CZ.php @@ -0,0 +1,11 @@ +<?php $TRANSLATIONS = array( +"This is an OpenID server endpoint. For more information, see " => "Toto je OpenID server endpoint. VÃce informacà na", +"Identity: <b>" => "Identita: <b>", +"Realm: <b>" => "Oblast: <b>", +"User: <b>" => "Uživatel: <b>", +"Login" => "Login", +"Error: <b>No user Selected" => "Chyba: <b>Uživatel nenà zvolen", +"you can authenticate to other sites with this address" => "s touto adresou se můžete autrorizovat na dalÅ¡Ã strany", +"Authorized OpenID provider" => "Autorizovaný OpenID poskytovatel", +"Your address at Wordpress, Identi.ca, …" => "VaÅ¡e adresa na Wordpressu, Identi.ca, …" +); diff --git a/apps/user_openid/l10n/de.php b/apps/user_openid/l10n/de.php new file mode 100644 index 00000000000..7bfe678574c --- /dev/null +++ b/apps/user_openid/l10n/de.php @@ -0,0 +1,11 @@ +<?php $TRANSLATIONS = array( +"This is an OpenID server endpoint. For more information, see " => "Dies ist ein OpenID-Server-Endpunkt. Weitere Informationen finden Sie unter:", +"Identity: <b>" => "Identität: <b>", +"Realm: <b>" => "Bereich: <b>", +"User: <b>" => "Benutzer: <b>", +"Login" => "Anmelden", +"Error: <b>No user Selected" => "Fehler: <b> Kein Benutzer ausgewählt", +"you can authenticate to other sites with this address" => "Sie können sich auf anderen Seiten mit dieser Adresse authentifizieren.", +"Authorized OpenID provider" => "Authorisierter OpenID-Anbieter", +"Your address at Wordpress, Identi.ca, …" => "Ihre Adresse bei Wordpress, Identi.ca, …" +); diff --git a/apps/user_openid/l10n/el.php b/apps/user_openid/l10n/el.php new file mode 100644 index 00000000000..a80f5bdb246 --- /dev/null +++ b/apps/user_openid/l10n/el.php @@ -0,0 +1,8 @@ +<?php $TRANSLATIONS = array( +"Identity: <b>" => "Ταυτότητα: <b>", +"User: <b>" => "ΧÏήστης: <b>", +"Login" => "ΣÏνδεση", +"Error: <b>No user Selected" => "Σφάλμα: <b> Δεν Îχει επιλεχθεί κάποιος χÏήστης", +"Authorized OpenID provider" => "ΕξουσιοδοτημÎνος παÏοχÎας OpenID", +"Your address at Wordpress, Identi.ca, …" => "Η διευθυνσή σας σε Wordpress, Identi.ca, …" +); diff --git a/apps/user_openid/l10n/eo.php b/apps/user_openid/l10n/eo.php new file mode 100644 index 00000000000..665d1990949 --- /dev/null +++ b/apps/user_openid/l10n/eo.php @@ -0,0 +1,11 @@ +<?php $TRANSLATIONS = array( +"This is an OpenID server endpoint. For more information, see " => "Ĉi tio estas finpunkto de OpenID-servilo. Por pli da informo, vidu", +"Identity: <b>" => "Idento: <b>", +"Realm: <b>" => "Regno: <b>", +"User: <b>" => "Uzanto: <b>", +"Login" => "Ensaluti", +"Error: <b>No user Selected" => "Eraro: <b>neniu uzanto estas elektita", +"you can authenticate to other sites with this address" => "Vi povas ensaluti en aliaj ejoj per tiu ĉi adreso", +"Authorized OpenID provider" => "Rajtigita OpenID-provizanto", +"Your address at Wordpress, Identi.ca, …" => "Via adreso ĉe Wordpress, Identi.ca…" +); diff --git a/apps/user_openid/l10n/es.php b/apps/user_openid/l10n/es.php new file mode 100644 index 00000000000..048336d3dba --- /dev/null +++ b/apps/user_openid/l10n/es.php @@ -0,0 +1,5 @@ +<?php $TRANSLATIONS = array( +"Identity: <b>" => "Identidad: <b>", +"User: <b>" => "Usuario: <b>", +"Login" => "Iniciar sesión" +); diff --git a/apps/user_openid/l10n/et_EE.php b/apps/user_openid/l10n/et_EE.php new file mode 100644 index 00000000000..f04bcafb249 --- /dev/null +++ b/apps/user_openid/l10n/et_EE.php @@ -0,0 +1,5 @@ +<?php $TRANSLATIONS = array( +"This is an OpenID server endpoint. For more information, see " => "See on OpenID serveri lõpp-punkt. Lisainfot vaata", +"Identity: <b>" => "Identiteet: <b>", +"Realm: <b>" => "Tsoon: <b>" +); diff --git a/apps/user_openid/l10n/fi_FI.php b/apps/user_openid/l10n/fi_FI.php new file mode 100644 index 00000000000..1cde4ae6600 --- /dev/null +++ b/apps/user_openid/l10n/fi_FI.php @@ -0,0 +1,7 @@ +<?php $TRANSLATIONS = array( +"Identity: <b>" => "Identiteetti: <b>", +"Realm: <b>" => "Alue: <b>", +"User: <b>" => "Käyttäjä: <b>", +"Login" => "Kirjaudu", +"Error: <b>No user Selected" => "Virhe: <b>Käyttäjää ei valittu" +); diff --git a/apps/user_openid/l10n/fr.php b/apps/user_openid/l10n/fr.php new file mode 100644 index 00000000000..ec3d00853d9 --- /dev/null +++ b/apps/user_openid/l10n/fr.php @@ -0,0 +1,11 @@ +<?php $TRANSLATIONS = array( +"This is an OpenID server endpoint. For more information, see " => "Ce serveur est un point d'accès OpenID. Pour plus d'informations, veuillez consulter", +"Identity: <b>" => "Identité : <b>", +"Realm: <b>" => "Domaine : <b>", +"User: <b>" => "Utilisateur : <b>", +"Login" => "Connexion", +"Error: <b>No user Selected" => "Erreur : <b>Aucun nom d'utilisateur n'a été saisi", +"you can authenticate to other sites with this address" => "vous pouvez vous authentifier sur d'autres sites grâce à cette adresse", +"Authorized OpenID provider" => "Fournisseur d'identité OpenID autorisé", +"Your address at Wordpress, Identi.ca, …" => "Votre adresse Wordpress, Identi.ca, …" +); diff --git a/apps/user_openid/l10n/it.php b/apps/user_openid/l10n/it.php new file mode 100644 index 00000000000..0a9dd951b5d --- /dev/null +++ b/apps/user_openid/l10n/it.php @@ -0,0 +1,11 @@ +<?php $TRANSLATIONS = array( +"This is an OpenID server endpoint. For more information, see " => "Questo è un server OpenID. Per ulteriori informazioni, vedi ", +"Identity: <b>" => "Identità : <b>", +"Realm: <b>" => "Dominio: <b>", +"User: <b>" => "Utente: <b>", +"Login" => "Accesso", +"Error: <b>No user Selected" => "Errore: <b>nessun utente selezionato", +"you can authenticate to other sites with this address" => "puoi autenticarti ad altri siti con questo indirizzo", +"Authorized OpenID provider" => "Fornitore OpenID autorizzato", +"Your address at Wordpress, Identi.ca, …" => "Il tuo indirizzo su Wordpress, Identi.ca, …" +); diff --git a/apps/user_openid/l10n/ja_JP.php b/apps/user_openid/l10n/ja_JP.php new file mode 100644 index 00000000000..12a2692cb67 --- /dev/null +++ b/apps/user_openid/l10n/ja_JP.php @@ -0,0 +1,11 @@ +<?php $TRANSLATIONS = array( +"This is an OpenID server endpoint. For more information, see " => "ã“ã‚Œã¯OpenIDサーãƒã®ã‚¨ãƒ³ãƒ‰ãƒã‚¤ãƒ³ãƒˆã§ã™ã€‚詳細ã¯ä¸‹è¨˜ã‚’ãƒã‚§ãƒƒã‚¯ã—ã¦ãã ã•ã„。", +"Identity: <b>" => "è˜åˆ¥å: <b>", +"Realm: <b>" => "レルム: <b>", +"User: <b>" => "ユーザ: <b>", +"Login" => "ãƒã‚°ã‚¤ãƒ³", +"Error: <b>No user Selected" => "エラー: <b>ユーザãŒé¸æŠžã•ã‚Œã¦ã„ã¾ã›ã‚“", +"you can authenticate to other sites with this address" => "ä»–ã®ã‚µã‚¤ãƒˆã«ã“ã®ã‚¢ãƒ‰ãƒ¬ã‚¹ã§èªè¨¼ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™", +"Authorized OpenID provider" => "èªè¨¼ã•ã‚ŒãŸOpenIDプãƒãƒã‚¤ãƒ€", +"Your address at Wordpress, Identi.ca, …" => "Wordpressã®ã‚¢ãƒ‰ãƒ¬ã‚¹ã€Identi.ca々" +); diff --git a/apps/user_openid/l10n/pl.php b/apps/user_openid/l10n/pl.php new file mode 100644 index 00000000000..c9572851e39 --- /dev/null +++ b/apps/user_openid/l10n/pl.php @@ -0,0 +1,11 @@ +<?php $TRANSLATIONS = array( +"This is an OpenID server endpoint. For more information, see " => "To jest punkt koÅ„cowy serwera OpenID. Aby uzyskać wiÄ™cej informacji zobacz", +"Identity: <b>" => "Tożsamość: <b>", +"Realm: <b>" => "Obszar: <b>", +"User: <b>" => "Użytkownik: <b>", +"Login" => "Login", +"Error: <b>No user Selected" => "BÅ‚Ä…d:<b>Nie wybrano użytkownika", +"you can authenticate to other sites with this address" => "można uwierzytelniać do innych witryn z tego adresu", +"Authorized OpenID provider" => "Autoryzowani dostawcy OpenID", +"Your address at Wordpress, Identi.ca, …" => "Twój adres na Wordpress, Identi.ca, …" +); diff --git a/apps/user_openid/l10n/sl.php b/apps/user_openid/l10n/sl.php new file mode 100644 index 00000000000..0464214a427 --- /dev/null +++ b/apps/user_openid/l10n/sl.php @@ -0,0 +1,11 @@ +<?php $TRANSLATIONS = array( +"This is an OpenID server endpoint. For more information, see " => "To je OpenID strežniÅ¡ka konÄna toÄka. Za veÄ informacij si oglejte", +"Identity: <b>" => "Identiteta: <b>", +"Realm: <b>" => "PodroÄje: <b>", +"User: <b>" => "Uporabnik:", +"Login" => "Prijava", +"Error: <b>No user Selected" => "Napaka: <b>Uporabnik ni bil izbran", +"you can authenticate to other sites with this address" => "s tem naslovom se lahko overite tudi na drugih straneh", +"Authorized OpenID provider" => "Odobren ponudnik OpenID", +"Your address at Wordpress, Identi.ca, …" => "VaÅ¡ naslov pri Wordpress, Identi.ca, …" +); diff --git a/apps/user_openid/l10n/sv.php b/apps/user_openid/l10n/sv.php new file mode 100644 index 00000000000..fb2c4e2ff7e --- /dev/null +++ b/apps/user_openid/l10n/sv.php @@ -0,0 +1,11 @@ +<?php $TRANSLATIONS = array( +"This is an OpenID server endpoint. For more information, see " => "Detta är en OpenID-server slutpunkt. För mer information, se", +"Identity: <b>" => "Identitet: <b>", +"Realm: <b>" => "Realm: <b>", +"User: <b>" => "Användare: <b>", +"Login" => "Logga in", +"Error: <b>No user Selected" => "Fel: <b>Ingen användare vald", +"you can authenticate to other sites with this address" => "du kan autentisera till andra webbplatser med denna adress", +"Authorized OpenID provider" => "Godkänd openID leverantör", +"Your address at Wordpress, Identi.ca, …" => "Din adress pÃ¥ Wordpress, Identi.ca, …" +); diff --git a/apps/user_openid/l10n/th_TH.php b/apps/user_openid/l10n/th_TH.php new file mode 100644 index 00000000000..fbf0bbc60a5 --- /dev/null +++ b/apps/user_openid/l10n/th_TH.php @@ -0,0 +1,11 @@ +<?php $TRANSLATIONS = array( +"This is an OpenID server endpoint. For more information, see " => "นี่คืà¸à¸›à¸¥à¸²à¸¢à¸—างขà¸à¸‡à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ OpenID สำหรับรายละเà¸à¸µà¸¢à¸”เพิ่มเติม, à¸à¸£à¸¸à¸“าดูที่", +"Identity: <b>" => "ข้à¸à¸¡à¸¹à¸¥à¸›à¸£à¸°à¸ˆà¸³à¸•à¸±à¸§ <b>", +"Realm: <b>" => "ขà¸à¸šà¹€à¸‚ตพื้นที่ <b>", +"User: <b>" => "ผู้ใช้งาน: <b>", +"Login" => "เข้าสู่ระบบ", +"Error: <b>No user Selected" => "พบข้à¸à¸œà¸´à¸”พลาด <b> ยังไม่ได้เลืà¸à¸à¸Šà¸·à¹ˆà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™", +"you can authenticate to other sites with this address" => "คุณสามารถได้รับสิทธิ์เพื่à¸à¹€à¸‚้าใช้งานเว็บไซต์à¸à¸·à¹ˆà¸™à¹†à¹‚ดยใช้ที่à¸à¸¢à¸¹à¹ˆà¸™à¸µà¹‰", +"Authorized OpenID provider" => "ผู้ให้บริà¸à¸²à¸£ OpenID ที่ได้รับà¸à¸™à¸¸à¸à¸²à¸•", +"Your address at Wordpress, Identi.ca, …" => "ที่à¸à¸¢à¸¹à¹ˆà¸‚à¸à¸‡à¸„ุณที่ Wordpress, Identi.ca, …" +); diff --git a/apps/user_openid/phpmyid.php b/apps/user_openid/phpmyid.php deleted file mode 100644 index 5aaab642856..00000000000 --- a/apps/user_openid/phpmyid.php +++ /dev/null @@ -1,1708 +0,0 @@ -<?php -// PLEASE DO NOT EDIT THIS FILE UNLESS YOU KNOW WHAT YOU ARE DOING! - -/** - * phpMyID - A standalone, single user, OpenID Identity Provider - * - * @package phpMyID - * @author CJ Niemira <siege (at) siege (dot) org> - * @copyright 2006-2008 - * @license http://www.gnu.org/licenses/gpl.html GNU Public License - * @url http://siege.org/projects/phpMyID - * @version 0.9 - */ - -/** - * Set a constant to indicate that phpMyID is running - */ -define('PHPMYID_STARTED', true); - -/** - * List the known types and modes - * @name $known - * @global array $GLOBALS['known'] - */ -$GLOBALS['known'] = array( - 'assoc_types' => array('HMAC-SHA1'), - - 'openid_modes' => array('accept', - 'associate', - 'authorize', - 'cancel', - 'checkid_immediate', - 'checkid_setup', - 'check_authentication', - 'error', - 'id_res', - 'login', - 'logout', - 'test'), - - 'session_types' => array('', - 'DH-SHA1'), - - 'bigmath_types' => array('DH-SHA1'), -); - -/** - * Defined by OpenID spec - * @name $g - * @global integer $GLOBALS['g'] - */ -$GLOBALS['g'] = 2; - -/** - * Defined by OpenID spec - * @name $p - * @global integer $GLOBALS['p'] - */ -$GLOBALS['p'] = '155172898181473697471232257763715539915724801966915404479707' . -'7953140576293785419175806512274236981889937278161526466314385615958256881888' . -'8995127215884267541995034125870655654980358010487053768147672651325574704076' . -'5857479291291572334510643245094715007229621094194349783925984760375594985848' . -'253359305585439638443'; - - -// Runmode functions - -/** - * Allow the user to accept trust on a URL - * @global array $profile - */ -function accept_mode () { - global $profile; - - // this is a user session - user_session(); - - // the user needs refresh urls in their session to access this mode - if (! isset($_SESSION['post_accept_url']) || ! isset($_SESSION['cancel_accept_url']) || ! isset($_SESSION['unaccepted_url'])) - error_500('You may not access this mode directly.'); - - // has the user accepted the trust_root? - $accepted = @strlen($_REQUEST['accepted']) - ? $_REQUEST['accepted'] - : null; - - // if so, refresh back to post_accept_url - if ($accepted === 'yes') { - $_SESSION['accepted_url'] = $_SESSION['unaccepted_url']; - wrap_redirect($_SESSION['post_accept_url']); - - // if they rejected it, return to the client - } elseif ($accepted === 'no') { - wrap_redirect($_SESSION['cancel_accept_url']); - } - - // if neither, offer the trust request - $q = strpos($profile['req_url'], '?') ? '&' : '?'; - $yes = $profile['req_url'] . $q . 'accepted=yes'; - $no = $profile['req_url'] . $q . 'accepted=no'; - - wrap_html('The client site you are attempting to log into has requested that you trust the following URL:<br/><b>' . $_SESSION['unaccepted_url'] . '</b><br/><br/>Do you wish to continue?<br/><a href="' . $yes . '">Yes</a> | <a href="' . $no . '">No</a>'); -} - -/** * Perform an association with a consumer - * @global array $known - * @global array $profile - * @global integer $g - * @global integer $p - */ -function associate_mode () { - global $g, $known, $p, $profile; - - // Validate the request - if (! isset($_REQUEST['openid_mode']) || $_REQUEST['openid_mode'] != 'associate') - error_400(); - - // Get the request options, using defaults as necessary - $assoc_type = (@strlen($_REQUEST['openid_assoc_type']) - && in_array($_REQUEST['openid_assoc_type'], $known['assoc_types'])) - ? $_REQUEST['openid_assoc_type'] - : 'HMAC-SHA1'; - - $session_type = (@strlen($_REQUEST['openid_session_type']) - && in_array($_REQUEST['openid_session_type'], $known['session_types'])) - ? $_REQUEST['openid_session_type'] - : ''; - - $dh_modulus = (@strlen($_REQUEST['openid_dh_modulus'])) - ? long(base64_decode($_REQUEST['openid_dh_modulus'])) - : ($session_type == 'DH-SHA1' - ? $p - : null); - - $dh_gen = (@strlen($_REQUEST['openid_dh_gen'])) - ? long(base64_decode($_REQUEST['openid_dh_gen'])) - : ($session_type == 'DH-SHA1' - ? $g - : null); - - $dh_consumer_public = (@strlen($_REQUEST['openid_dh_consumer_public'])) - ? $_REQUEST['openid_dh_consumer_public'] - : ($session_type == 'DH-SHA1' - ? error_post('dh_consumer_public was not specified') - : null); - - $lifetime = time() + $profile['lifetime']; - - // Create standard keys - $keys = array( - 'assoc_type' => $assoc_type, - 'expires_in' => $profile['lifetime'] - ); - - // If I can't handle bigmath, default to plaintext sessions - if (in_array($session_type, $known['bigmath_types']) && $profile['use_bigmath'] === false) - $session_type = null; - - // Add response keys based on the session type - switch ($session_type) { - case 'DH-SHA1': - // Create the associate id and shared secret now - list ($assoc_handle, $shared_secret) = new_assoc($lifetime); - - // Compute the Diffie-Hellman stuff - $private_key = random($dh_modulus); - $public_key = bmpowmod($dh_gen, $private_key, $dh_modulus); - $remote_key = long(base64_decode($dh_consumer_public)); - $ss = bmpowmod($remote_key, $private_key, $dh_modulus); - - $keys['assoc_handle'] = $assoc_handle; - $keys['session_type'] = $session_type; - $keys['dh_server_public'] = base64_encode(bin($public_key)); - $keys['enc_mac_key'] = base64_encode(x_or(sha1_20(bin($ss)), $shared_secret)); - - break; - - default: - // Create the associate id and shared secret now - list ($assoc_handle, $shared_secret) = new_assoc($lifetime); - - $keys['assoc_handle'] = $assoc_handle; - $keys['mac_key'] = base64_encode($shared_secret); - } - - // Return the keys - wrap_kv($keys); -} - - -/** - * Perform a user authorization - * @global array $profile - */ -function authorize_mode () { - global $profile; - global $USERNAME; - global $IDENTITY; - - // this is a user session - - // the user needs refresh urls in their session to access this mode - if (! isset($_SESSION['post_auth_url']) || ! isset($_SESSION['cancel_auth_url'])) - error_500('You may not access this mode directly.'); - - $profile['idp_url']=$IDENTITY; - if (isset($_SERVER['PHP_AUTH_USER']) && $profile['authorized'] === false && $_SERVER['PHP_AUTH_USER']==$USERNAME) { - if (OCP\User::checkPassword($USERNAME, $_SERVER['PHP_AUTH_PW'])) {// successful login! - // return to the refresh url if they get in - $_SESSION['openid_auth']=true; - $_SESSION['openid_user']=$USERNAME; - wrap_redirect($_SESSION['post_auth_url']); - - // failed login - } else { - $_SESSION['failures']++; - debug('Login failed'); - debug('Fail count: ' . $_SESSION['failures']); - } - - } - - // if we get this far the user is not authorized, so send the headers - $uid = uniqid(mt_rand(1,9)); - $_SESSION['uniqid'] = $uid; - -// debug('Prompting user to log in. Stale? ' . $stale); - header('HTTP/1.0 401 Unauthorized'); -// header(sprintf('WWW-Authenticate: Digest qop="auth-int, auth", realm="%s", domain="%s", nonce="%s", opaque="%s", stale="%s", algorithm="MD5"', $profile['auth_realm'], $profile['auth_domain'], $uid, md5($profile['auth_realm']), $stale ? 'true' : 'false')); - header('WWW-Authenticate: Basic realm="ownCloud"'); - $q = strpos($_SESSION['cancel_auth_url'], '?') ? '&' : '?'; - wrap_refresh($_SESSION['cancel_auth_url'] . $q . 'openid.mode=cancel'); -// die('401 Unauthorized'); -} - - -/** - * Handle a consumer's request for cancellation. - */ -function cancel_mode () { - wrap_html('Request cancelled.'); -} - - -/** - * Handle a consumer's request to see if the user is authenticated - */ -function check_authentication_mode () { - // Validate the request - if (! isset($_REQUEST['openid_mode']) || $_REQUEST['openid_mode'] != 'check_authentication') - error_400(); - - $assoc_handle = @strlen($_REQUEST['openid_assoc_handle']) - ? $_REQUEST['openid_assoc_handle'] - : error_post('Missing assoc_handle'); - - $sig = @strlen($_REQUEST['openid_sig']) - ? $_REQUEST['openid_sig'] - : error_post('Missing sig'); - - $signed = @strlen($_REQUEST['openid_signed']) - ? $_REQUEST['openid_signed'] - : error_post('Missing signed'); - - // Prepare the return keys - $keys = array( - 'openid.mode' => 'id_res' - ); - - // Invalidate the assoc handle if we need to - if (@strlen($_REQUEST['openid_invalidate_handle'])) { - destroy_assoc_handle($_REQUEST['openid_invalidate_handle']); - - $keys['invalidate_handle'] = $_REQUEST['openid_invalidate_handle']; - } - - // Validate the sig by recreating the kv pair and signing - $_REQUEST['openid_mode'] = 'id_res'; - $tokens = ''; - foreach (explode(',', $signed) as $param) { - $post = preg_replace('/\./', '_', $param); - $tokens .= sprintf("%s:%s\n", $param, $_REQUEST['openid_' . $post]); - } - - // Add the sreg stuff, if we've got it - if (isset($sreg_required)) { - foreach (explode(',', $sreg_required) as $key) { - if (! isset($sreg[$key])) - continue; - $skey = 'sreg.' . $key; - - $tokens .= sprintf("%s:%s\n", $skey, $sreg[$key]); - $keys[$skey] = $sreg[$key]; - $fields[] = $skey; - } - } - - // Look up the consumer's shared_secret and timeout - list ($shared_secret, $expires) = secret($assoc_handle); - - // if I can't verify the assoc_handle, or if it's expired - if ($shared_secret == false || (is_numeric($expires) && $expires < time())) { - $keys['is_valid'] = 'false'; - - } else { - $ok = base64_encode(hmac($shared_secret, $tokens)); - $keys['is_valid'] = ($sig == $ok) ? 'true' : 'false'; - } - - // Return the keys - wrap_kv($keys); -} - - -/** - * Handle a consumer's request to see if the end user is logged in - * @global array $known - * @global array $profile - * @global array $sreg - */ -function checkid ( $wait ) { - global $known, $profile, $sreg; - global $USERNAME; - - // This is a user session - user_session(); - - // Get the options, use defaults as necessary - $return_to = isset($_REQUEST['openid_return_to']) - ? $_REQUEST['openid_return_to'] - : error_400('Missing return_to'); - - $identity = isset($_REQUEST['openid_identity']) - ? $_REQUEST['openid_identity'] - : error_get($return_to, 'Missing identity'); - - $assoc_handle = isset($_REQUEST['openid_assoc_handle']) - ? $_REQUEST['openid_assoc_handle'] - : null; - - $trust_root = isset($_REQUEST['openid_trust_root']) - ? $_REQUEST['openid_trust_root'] - : $return_to; - - $sreg_required = isset($_REQUEST['openid_sreg_required']) - ? $_REQUEST['openid_sreg.required'] - : ''; - - $sreg_optional = isset($_REQUEST['openid_sreg_optional']) - ? $_REQUEST['openid_sreg.optional'] - : ''; - - // determine the cancel url - $q = strpos($return_to, '?') ? '&' : '?'; - $cancel_url = $return_to . $q . 'openid.mode=cancel'; - - // required and optional make no difference to us - $sreg_required .= ',' . $sreg_optional; - // do the trust_root analysis - if ($trust_root != $return_to) { - // the urls are not the same, be sure return decends from trust - if (! url_descends($return_to, $trust_root)) - error_500('Invalid trust_root: "' . $trust_root . '"'); - - } - - // transfer the user to the url accept mode if they're paranoid - if ($wait == 1 && isset($profile['paranoid']) && $profile['paranoid'] === true && (! isset($_SESSION['accepted_url']) || $_SESSION['accepted_url'] != $trust_root)) { - $_SESSION['cancel_accept_url'] = $cancel_url; - $_SESSION['post_accept_url'] = $profile['req_url']; - $_SESSION['unaccepted_url'] = $trust_root; - - debug('Transferring to acceptance mode.'); - debug('Cancel URL: ' . $_SESSION['cancel_accept_url']); - debug('Post URL: ' . $_SESSION['post_accept_url']); - - $q = strpos($profile['idp_url'], '?') ? '&' : '?'; - wrap_redirect($profile['idp_url'] . $q . 'openid.mode=accept'); - } - - // make sure i am this identifier -// if ($identity != $profile['idp_url']) { -// debug("Invalid identity: $identity"); -// debug("IdP URL: " . $profile['idp_url']); -// error_get($return_to, "Invalid identity: '$identity'"); -// } - - // begin setting up return keys - $keys = array( - 'mode' => 'id_res' - ); - - // if the user is not logged in, transfer to the authorization mode - if ($USERNAME=='' || $_SESSION['openid_auth'] === false || $USERNAME != $_SESSION['openid_user']) { - // users can only be logged in to one url at a time - $_SESSION['openid_user'] = null; - $_SESSION['auth_url'] = null; - - if ($wait) { - unset($_SESSION['uniqid']); - - $_SESSION['cancel_auth_url'] = $cancel_url; - $_SESSION['post_auth_url'] = $profile['req_url']; - - debug('Transferring to authorization mode.'); - debug('Cancel URL: ' . $_SESSION['cancel_auth_url']); - debug('Post URL: ' . $_SESSION['post_auth_url']); - - $q = strpos($profile['idp_url'], '?') ? '&' : '?'; - wrap_redirect($profile['idp_url'] . $q . 'openid.mode=authorize'); - } else { - $keys['user_setup_url'] = $profile['idp_url']; - } - - // the user is logged in - } else { - // remove the refresh URLs if set - unset($_SESSION['cancel_auth_url']); - unset($_SESSION['post_auth_url']); - - // check the assoc handle - list($shared_secret, $expires) = secret($assoc_handle); - - // if I can't verify the assoc_handle, or if it's expired - if ($shared_secret == false || (is_numeric($expires) && $expires < time())) { - debug("Session expired or missing key: $expires < " . time()); - if ($assoc_handle != null) { - $keys['invalidate_handle'] = $assoc_handle; - destroy_assoc_handle($assoc_handle); - } - - $lifetime = time() + $profile['lifetime']; - list ($assoc_handle, $shared_secret) = new_assoc($lifetime); - } - - $keys['identity'] = $profile['idp_url']; - $keys['assoc_handle'] = $assoc_handle; - $keys['return_to'] = $return_to; - - $fields = array_keys($keys); - $tokens = ''; - foreach ($fields as $key) - $tokens .= sprintf("%s:%s\n", $key, $keys[$key]); - - // add sreg keys - foreach (explode(',', $sreg_required) as $key) { - if (! isset($sreg[$key])) - continue; - $skey = 'sreg.' . $key; - - $tokens .= sprintf("%s:%s\n", $skey, $sreg[$key]); - $keys[$skey] = $sreg[$key]; - $fields[] = $skey; - } - - $keys['signed'] = implode(',', $fields); - $keys['sig'] = base64_encode(hmac($shared_secret, $tokens)); - } - - wrap_keyed_redirect($return_to, $keys); -} - - -/** - * Handle a consumer's request to see if the user is already logged in - */ -function checkid_immediate_mode () { - if (! isset($_REQUEST['openid_mode']) || $_REQUEST['openid_mode'] != 'checkid_immediate') - error_500(); - - checkid(false); -} - - -/** - * Handle a consumer's request to see if the user is logged in, but be willing - * to wait for them to perform a login if they're not - */ -function checkid_setup_mode () { - if (! isset($_REQUEST['openid_mode']) || $_REQUEST['openid_mode'] != 'checkid_setup') - error_500(); - - checkid(true); -} - - -/** - * Handle errors - */ -function error_mode () { - isset($_REQUEST['openid_error']) - ? wrap_html($_REQUEST['openid_error']) - : error_500(); -} - - -/** - * Show a user if they are logged in or not - * @global array $profile - */ -function id_res_mode () { - global $profile; - - user_session(); - - if ($profile['authorized']) - wrap_html('You are logged in as ' . $_SESSION['auth_username']); - - wrap_html('You are not logged in'); -} - - -/** - * Allow a user to perform a static login - * @global array $profile - */ -function login_mode () { - global $profile; - - user_session(); - - if ($profile['authorized']) - id_res_mode(); - - $keys = array( - 'mode' => 'checkid_setup', - 'identity' => $profile['idp_url'], - 'return_to' => $profile['idp_url'] - ); - - wrap_keyed_redirect($profile['idp_url'], $keys); -} - - -/** - * Allow a user to perform a static logout - * @global array $profile - */ -function logout_mode () { - global $profile; - - user_session(); - - if (! $profile['authorized']) - wrap_html('You were not logged in'); - - $_SESSION = array(); - session_destroy(); - debug('User session destroyed.'); - - header('HTTP/1.0 401 Unauthorized'); - wrap_redirect($profile['idp_url']); -} - - -/** - * The default information screen - * @global array $profile - */ -function no_mode () { - global $USERNAME, $profile; - $tmpl = new OCP\Template( 'user_openid', 'nomode', 'guest' ); - if(substr($profile['req_url'],-1,1)!=='/'){//the identity should always end with a / - $profile['req_url'].='/'; - } - $tmpl->addHeader('link',array('rel'=>'openid.server', 'href'=>$profile['req_url'])); - $tmpl->addHeader('link',array('rel'=>'openid.delegate', 'href'=>$profile['idp_url'])); - $tmpl->assign('user',$USERNAME); - $tmpl->printPage(); -} - - -/** - * Testing for setup - * @global array $profile - */ -function test_mode () { - global $profile, $p, $g; - - if ($profile['allow_test'] != true) - error_403(); - - @ini_set('max_execution_time', 180); - - $test_expire = time() + 120; - $test_ss_enc = 'W7hvmld2yEYdDb0fHfSkKhQX+PM='; - $test_ss = base64_decode($test_ss_enc); - $test_token = "alpha:bravo\ncharlie:delta\necho:foxtrot"; - $test_server_private = '11263846781670293092494395517924811173145217135753406847875706165886322533899689335716152496005807017390233667003995430954419468996805220211293016296351031812246187748601293733816011832462964410766956326501185504714561648498549481477143603650090931135412673422192550825523386522507656442905243832471167330268'; - $test_client_public = base64_decode('AL63zqI5a5p8HdXZF5hFu8p+P9GOb816HcHuvNOhqrgkKdA3fO4XEzmldlb37nv3+xqMBgWj6gxT7vfuFerEZLBvuWyVvR7IOGZmx0BAByoq3fxYd3Fpe2Coxngs015vK37otmH8e83YyyGo5Qua/NAf13yz1PVuJ5Ctk7E+YdVc'); - - $res = array(); - - // bcmath - $res['bcmath'] = extension_loaded('bcmath') - ? 'pass' : 'warn - not loaded'; - - // gmp - if ($profile['allow_gmp']) { - $res['gmp'] = extension_loaded('gmp') - ? 'pass' : 'warn - not loaded'; - } else { - $res['gmp'] = 'pass - n/a'; - } - - // get_temp_dir - $res['logfile'] = is_writable($profile['logfile']) - ? 'pass' : "warn - log is not writable"; - - // session & new_assoc - user_session(); - list($test_assoc, $test_new_ss) = new_assoc($test_expire); - $res['session'] = ($test_assoc != session_id()) - ? 'pass' : 'fail'; - - // secret - @session_unregister('shared_secret'); - list($check, $check2) = secret($test_assoc); - $res['secret'] = ($check == $test_new_ss) - ? 'pass' : 'fail'; - - // expire - $res['expire'] = ($check2 <= $test_expire) - ? 'pass' : 'fail'; - - // base64 - $res['base64'] = (base64_encode($test_ss) == $test_ss_enc) - ? 'pass' : 'fail'; - - // hmac - $test_sig = base64_decode('/VXgHvZAOdoz/OTa5+XJXzSGhjs='); - $check = hmac($test_ss, $test_token); - $res['hmac'] = ($check == $test_sig) - ? 'pass' : sprintf("fail - '%s'", base64_encode($check)); - - if ($profile['use_bigmath']) { - // bigmath powmod - $test_server_public = '102773334773637418574009974502372885384288396853657336911033649141556441102566075470916498748591002884433213640712303846640842555822818660704173387461364443541327856226098159843042567251113889701110175072389560896826887426539315893475252988846151505416694218615764823146765717947374855806613410142231092856731'; - $check = bmpowmod($g, $test_server_private, $p); - $res['bmpowmod-1'] = ($check == $test_server_public) - ? 'pass' : sprintf("fail - '%s'", $check); - - // long - $test_client_long = '133926731803116519408547886573524294471756220428015419404483437186057383311250738749035616354107518232016420809434801736658109316293127101479053449990587221774635063166689561125137927607200322073086097478667514042144489248048756916881344442393090205172004842481037581607299263456852036730858519133859409417564'; - $res['long'] = (long($test_client_public) == $test_client_long) - ? 'pass' : 'fail'; - - // bigmath powmod 2 - $test_client_share = '19333275433742428703546496981182797556056709274486796259858099992516081822015362253491867310832140733686713353304595602619444380387600756677924791671971324290032515367930532292542300647858206600215875069588627551090223949962823532134061941805446571307168890255137575975911397744471376862555181588554632928402'; - $check = bmpowmod($test_client_long, $test_server_private, $p); - $res['bmpowmod-2'] = ($check == $test_client_share) - ? 'pass' : sprintf("fail - '%s'", $check); - - // bin - $test_client_mac_s1 = base64_decode('G4gQQkYM6QmAzhKbVKSBahFesPL0nL3F2MREVwEtnVRRYI0ifl9zmPklwTcvURt3QTiGBd+9Dn3ESLk5qka6IO5xnILcIoBT8nnGVPiOZvTygfuzKp4tQ2mXuIATJoa7oXRGmBWtlSdFapH5Zt6NJj4B83XF/jzZiRwdYuK4HJI='); - $check = bin($test_client_share); - $res['bin'] = ($check == $test_client_mac_s1) - ? 'pass' : sprintf("fail - '%s'", base64_encode($check)); - - } else { - $res['bigmath'] = 'fail - big math functions are not available.'; - } - - // sha1_20 - $test_client_mac_s1 = base64_decode('G4gQQkYM6QmAzhKbVKSBahFesPL0nL3F2MREVwEtnVRRYI0ifl9zmPklwTcvURt3QTiGBd+9Dn3ESLk5qka6IO5xnILcIoBT8nnGVPiOZvTygfuzKp4tQ2mXuIATJoa7oXRGmBWtlSdFapH5Zt6NJj4B83XF/jzZiRwdYuK4HJI='); - $test_client_mac_s2 = base64_decode('0Mb2t9d/HvAZyuhbARJPYdx3+v4='); - $check = sha1_20($test_client_mac_s1); - $res['sha1_20'] = ($check == $test_client_mac_s2) - ? 'pass' : sprintf("fail - '%s'", base64_encode($check)); - - // x_or - $test_client_mac_s3 = base64_decode('i36ZLYAJ1rYEx1VEHObrS8hgAg0='); - $check = x_or($test_client_mac_s2, $test_ss); - $res['x_or'] = ($check == $test_client_mac_s3) - ? 'pass' : sprintf("fail - '%s'", base64_encode($check)); - - $out = "<table border=1 cellpadding=4>\n"; - foreach ($res as $test => $stat) { - $code = substr($stat, 0, 4); - $color = ($code == 'pass') ? '#9f9' - : (($code == 'warn') ? '#ff9' : '#f99'); - $out .= sprintf("<tr><th>%s</th><td style='background:%s'>%s</td></tr>\n", $test, $color, $stat); - } - $out .= "</table>"; - - wrap_html( $out ); -} - - -// Support functions - -/** - * Prefix the keys of an array with 'openid.' - * @param array $array - * @return array - */ -function append_openid ($array) { - $keys = array_keys($array); - $vals = array_values($array); - - $r = array(); - for ($i=0; $i<sizeof($keys); $i++) - $r['openid.' . $keys[$i]] = $vals[$i]; - return $r; -} - -/** - * Create a big math addition function - * @param string $l - * @param string $r - * @return string - * @url http://www.icosaedro.it/bigint Inspired by - */ -function bmadd($l, $r) { - if (function_exists('bcadd')) - return bcadd($l, $r); - - global $profile; - if ($profile['use_gmp']) - return gmp_strval(gmp_add($l, $r)); - - $l = strval($l); $r = strval($r); - $ll = strlen($l); $rl = strlen($r); - if ($ll < $rl) { - $l = str_repeat("0", $rl-$ll) . $l; - $o = $rl; - - } elseif ( $ll > $rl ) { - $r = str_repeat("0", $ll-$rl) . $r; - $o = $ll; - - } else { - $o = $ll; - } - - $v = ''; - $carry = 0; - - for ($i = $o-1; $i >= 0; $i--) { - $d = (int)$l[$i] + (int)$r[$i] + $carry; - if ($d <= 9) { - $carry = 0; - - } else { - $carry = 1; - $d -= 10; - } - $v = (string) $d . $v; - } - - if ($carry > 0) - $v = "1" . $v; - - return $v; -} - -/** - * Create a big math comparison function - * @param string $l - * @param string $r - * @return string - */ -function bmcomp($l, $r) { - if (function_exists('bccomp')) - return bccomp($l, $r); - - global $profile; - if ($profile['use_gmp']) - return gmp_strval(gmp_cmp($l, $r)); - - $l = strval($l); $r = strval($r); - $ll = strlen($l); $lr = strlen($r); - if ($ll != $lr) - return ($ll > $lr) ? 1 : -1; - - return strcmp($l, $r); -} - -/** - * Create a big math division function - * @param string $l - * @param string $r - * @param int $z - * @return string - * @url http://www.icosaedro.it/bigint Inspired by - */ -function bmdiv($l, $r, $z = 0) { - if (function_exists('bcdiv')) - return ($z == 0) ? bcdiv($l, $r) : bcmod($l, $r); - - global $profile; - if ($profile['use_gmp']) - return gmp_strval(($z == 0) ? gmp_div_q($l, $r) : gmp_mod($l, $r)); - - $l = strval($l); $r = strval($r); - $v = '0'; - - while (true) { - if( bmcomp($l, $r) < 0 ) - break; - - $delta = strlen($l) - strlen($r); - if ($delta >= 1) { - $zeroes = str_repeat("0", $delta); - $r2 = $r . $zeroes; - - if (strcmp($l, $r2) >= 0) { - $v = bmadd($v, "1" . $zeroes); - $l = bmsub($l, $r2); - - } else { - $zeroes = str_repeat("0", $delta - 1); - $v = bmadd($v, "1" . $zeroes); - $l = bmsub($l, $r . $zeroes); - } - - } else { - $l = bmsub($l, $r); - $v = bmadd($v, "1"); - } - } - - return ($z == 0) ? $v : $l; -} - -/** - * Create a big math multiplication function - * @param string $l - * @param string $r - * @return string - * @url http://www.icosaedro.it/bigint Inspired by - */ -function bmmul($l, $r) { - if (function_exists('bcmul')) - return bcmul($l, $r); - - global $profile; - if ($profile['use_gmp']) - return gmp_strval(gmp_mul($l, $r)); - - $l = strval($l); $r = strval($r); - - $v = '0'; - $z = ''; - - for( $i = strlen($r)-1; $i >= 0; $i-- ){ - $bd = (int) $r[$i]; - $carry = 0; - $p = ""; - for( $j = strlen($l)-1; $j >= 0; $j-- ){ - $ad = (int) $l[$j]; - $pd = $ad * $bd + $carry; - if( $pd <= 9 ){ - $carry = 0; - } else { - $carry = (int) ($pd / 10); - $pd = $pd % 10; - } - $p = (string) $pd . $p; - } - if( $carry > 0 ) - $p = (string) $carry . $p; - $p = $p . $z; - $z .= "0"; - $v = bmadd($v, $p); - } - - return $v; -} - -/** - * Create a big math modulus function - * @param string $value - * @param string $mod - * @return string - */ -function bmmod( $value, $mod ) { - if (function_exists('bcmod')) - return bcmod($value, $mod); - - global $profile; - if ($profile['use_gmp']) - return gmp_strval(gmp_mod($value, $mod)); - - $r = bmdiv($value, $mod, 1); - return $r; -} - -/** - * Create a big math power function - * @param string $value - * @param string $exponent - * @return string - */ -function bmpow ($value, $exponent) { - if (function_exists('bcpow')) - return bcpow($value, $exponent); - - global $profile; - if ($profile['use_gmp']) - return gmp_strval(gmp_pow($value, $exponent)); - - $r = '1'; - while ($exponent) { - $r = bmmul($r, $value, 100); - $exponent--; - } - return (string)rtrim($r, '0.'); -} - -/** - * Create a big math 'powmod' function - * @param string $value - * @param string $exponent - * @param string $mod - * @return string - * @url http://php.net/manual/en/function.bcpowmod.php#72704 Borrowed from - */ -function bmpowmod ($value, $exponent, $mod) { - if (function_exists('bcpowmod')) - return bcpowmod($value, $exponent, $mod); - - global $profile; - if ($profile['use_gmp']) - return gmp_strval(gmp_powm($value, $exponent, $mod)); - - $r = ''; - while ($exponent != '0') { - $t = bmmod($exponent, '4096'); - $r = substr("000000000000" . decbin(intval($t)), -12) . $r; - $exponent = bmdiv($exponent, '4096'); - } - - $r = preg_replace("!^0+!","",$r); - - if ($r == '') - $r = '0'; - $value = bmmod($value, $mod); - $erb = strrev($r); - $q = '1'; - $a[0] = $value; - - for ($i = 1; $i < strlen($erb); $i++) { - $a[$i] = bmmod( bmmul($a[$i-1], $a[$i-1]), $mod ); - } - - for ($i = 0; $i < strlen($erb); $i++) { - if ($erb[$i] == "1") { - $q = bmmod( bmmul($q, $a[$i]), $mod ); - } - } - - return($q); -} - -/** - * Create a big math subtraction function - * @param string $l - * @param string $r - * @return string - * @url http://www.icosaedro.it/bigint Inspired by - */ -function bmsub($l, $r) { - if (function_exists('bcsub')) - return bcsub($l, $r); - - global $profile; - if ($profile['use_gmp']) - return gmp_strval(gmp_sub($l, $r)); - - - $l = strval($l); $r = strval($r); - $ll = strlen($l); $rl = strlen($r); - - if ($ll < $rl) { - $l = str_repeat("0", $rl-$ll) . $l; - $o = $rl; - } elseif ( $ll > $rl ) { - $r = str_repeat("0", $ll-$rl) . (string)$r; - $o = $ll; - } else { - $o = $ll; - } - - if (strcmp($l, $r) >= 0) { - $sign = ''; - } else { - $x = $l; $l = $r; $r = $x; - $sign = '-'; - } - - $v = ''; - $carry = 0; - - for ($i = $o-1; $i >= 0; $i--) { - $d = ($l[$i] - $r[$i]) - $carry; - if ($d < 0) { - $carry = 1; - $d += 10; - } else { - $carry = 0; - } - $v = (string) $d . $v; - } - - return $sign . ltrim($v, '0'); -} - - -/** - * Get a binary value - * @param integer $n - * @return string - * @url http://openidenabled.com Borrowed from PHP-OpenID - */ -function bin ($n) { - $bytes = array(); - while (bmcomp($n, 0) > 0) { - array_unshift($bytes, bmmod($n, 256)); - $n = bmdiv($n, bmpow(2,8)); - } - - if ($bytes && ($bytes[0] > 127)) - array_unshift($bytes, 0); - - $b = ''; - foreach ($bytes as $byte) - $b .= pack('C', $byte); - - return $b; -} - - -/** - * Debug logging - * @param mixed $x - * @param string $m - */ -function debug ($x, $m = null) { - global $profile; - - if (! isset($profile['debug']) || $profile['debug'] === false) - return true; - - if (! is_writable(dirname($profile['logfile'])) &! is_writable($profile['logfile'])) - error_500('Cannot write to debug log: ' . $profile['logfile']); - - if (is_array($x)) { - ob_start(); - print_r($x); - $x = $m . ($m != null ? "\n" : '') . ob_get_clean(); - - } else { - $x .= "\n"; - } -} - - -/** - * Destroy a consumer's assoc handle - * @param string $id - */ -function destroy_assoc_handle ( $id ) { - debug("Destroying session: $id"); - - $sid = session_id(); - session_write_close(); - - session_id($id); - if (OCP\Config::getSystemValue( "forcessl", false )) { - ini_set("session.cookie_secure", "on"); - } - session_start(); - session_destroy(); - - session_id($sid); - session_start(); -} - - -/** - * Return an error message to the user - * @param string $message - */ -function error_400 ( $message = 'Bad Request' ) { - header("HTTP/1.1 400 Bad Request"); - wrap_html($message); -} - - -/** - * Return an error message to the user - * @param string $message - */ -function error_403 ( $message = 'Forbidden' ) { - header("HTTP/1.1 403 Forbidden"); - wrap_html($message); -} - - -/** - * Return an error message to the user - * @param string $message - */ -function error_500 ( $message = 'Internal Server Error' ) { - header("HTTP/1.1 500 Internal Server Error"); - wrap_html($message); -} - - -/** - * Return an error message to the consumer - * @param string $message - */ -function error_get ( $url, $message = 'Bad Request') { - wrap_keyed_redirect($url, array('mode' => 'error', 'error' => $message)); -} - - -/** - * Return an error message to the consumer - * @param string $message - */ -function error_post ( $message = 'Bad Request' ) { - header("HTTP/1.1 400 Bad Request"); - echo ('error:' . $message); - exit(0); -} - - -/** - * Do an HMAC - * @param string $key - * @param string $data - * @param string $hash - * @return string - * @url http://php.net/manual/en/function.sha1.php#39492 Borrowed from - */ -function hmac($key, $data, $hash = 'sha1_20') { - $blocksize=64; - - if (strlen($key) > $blocksize) - $key = $hash($key); - - $key = str_pad($key, $blocksize,chr(0x00)); - $ipad = str_repeat(chr(0x36),$blocksize); - $opad = str_repeat(chr(0x5c),$blocksize); - - $h1 = $hash(($key ^ $ipad) . $data); - $hmac = $hash(($key ^ $opad) . $h1); - return $hmac; -} - - -if (! function_exists('http_build_query')) { -/** - * Create function if missing - * @param array $array - * @return string - */ -function http_build_query ($array) { - $r = array(); - foreach ($array as $key => $val) - $r[] = sprintf('%s=%s', urlencode($key), urlencode($val)); - return implode('&', $r); -}} - - -/** - * Turn a binary back into a long - * @param string $b - * @return integer - * @url http://openidenabled.com Borrowed from PHP-OpenID - */ -function long($b) { - $bytes = array_merge(unpack('C*', $b)); - $n = 0; - foreach ($bytes as $byte) { - $n = bmmul($n, bmpow(2,8)); - $n = bmadd($n, $byte); - } - return $n; -} - - -/** - * Create a new consumer association - * @param integer $expiration - * @return array - */ -function new_assoc ( $expiration ) { - if (isset($_SESSION) && is_array($_SESSION)) { - $sid = session_id(); - $dat = session_encode(); - session_write_close(); - } - - if (OCP\Config::getSystemValue( "forcessl", false )) { - ini_set("session.cookie_secure", "on"); - } - session_start(); - session_regenerate_id('false'); - - $id = session_id(); - $shared_secret = new_secret(); - debug('Started new assoc session: ' . $id); - - $_SESSION = array(); - $_SESSION['expiration'] = $expiration; - $_SESSION['shared_secret'] = base64_encode($shared_secret); - - session_write_close(); - - if (isset($sid)) { - session_id($sid); - session_start(); - $_SESSION = array(); - session_decode($dat); - } - - return array($id, $shared_secret); -} - - -/** - * Create a new shared secret - * @return string - */ -function new_secret () { - $r = ''; - for($i=0; $i<20; $i++) - $r .= chr(mt_rand(0, 255)); - - debug("Generated new key: hash = '" . md5($r) . "', length = '" . strlen($r) . "'"); - return $r; -} - - -/** - * Random number generation - * @param integer max - * @return integer - */ -function random ( $max ) { - if (strlen($max) < 4) - return mt_rand(1, $max - 1); - - $r = ''; - for($i=1; $i<strlen($max) - 1; $i++) - $r .= mt_rand(0,9); - $r .= mt_rand(1,9); - - return $r; -} - -/** - * Get the shared secret and expiration time for the specified assoc_handle - * @param string $handle assoc_handle to look up - * @return array (shared_secret, expiration_time) - */ -function secret ( $handle ) { - if (! preg_match('/^\w+$/', $handle)) - return array(false, 0); - - if (isset($_SESSION) && is_array($_SESSION)) { - $sid = session_id(); - $dat = session_encode(); - session_write_close(); - } - - session_id($handle); - if (OCP\Config::getSystemValue( "forcessl", false )) { - ini_set("session.cookie_secure", "on"); - } - session_start(); - debug('Started session to acquire key: ' . session_id()); - - $secret = isset($_SESSION['shared_secret']) - ? base64_decode($_SESSION['shared_secret']) - : false; - - $expiration = isset($_SESSION['expiration']) - ? $_SESSION['expiration'] - : null; - - session_write_close(); - - if (isset($sid)) { - session_id($sid); - session_start(); - $_SESSION = array(); - session_decode($dat); - } - - debug("Found key: hash = '" . md5($secret) . "', length = '" . strlen($secret) . "', expiration = '$expiration'"); - return array($secret, $expiration); -} - - -/** - * Do an internal self check - * @global array $profile - * @global array $sreg - */ -function self_check () { - global $profile, $sreg; - -// if (! isset($profile) || ! is_array($profile)) -// error_500('No configuration found, you shouldn\'t access this file directly.'); - - if (version_compare(phpversion(), '4.2.0', 'lt')) - error_500('The required minimum version of PHP is 4.2.0, you are running ' . phpversion()); - - $extension_r = array('session', 'pcre'); - foreach ($extension_r as $x) { - if (! extension_loaded($x)) - @dl($x); - if (! extension_loaded($x)) - error_500("Required extension '$x' is missing."); - } - -// $extension_b = array('suhosin'); -// foreach ($extension_b as $x) { -// if (extension_loaded($x) &! $profile["allow_$x"]) -// error_500("phpMyID is not compatible with '$x'"); -// } -// -// $keys = array('auth_username', 'auth_password'); -// foreach ($keys as $key) { -// if (! array_key_exists($key, $profile)) -// error_500("'$key' is missing from your profile."); -// } - - if (! isset($sreg) || ! is_array($sreg)) - $sreg = array(); -} - - -/** - * Do SHA1 20 byte encryption - * @param string $v - * @return string - * @url http://openidenabled.com Borrowed from PHP-OpenID - */ -function sha1_20 ($v) { - if (version_compare(phpversion(), '5.0.0', 'ge')) - return sha1($v, true); - - $hex = sha1($v); - $r = ''; - for ($i = 0; $i < 40; $i += 2) { - $hexcode = substr($hex, $i, 2); - $charcode = base_convert($hexcode, 16, 10); - $r .= chr($charcode); - } - return $r; -} - - -/** - * Look for the point of differentiation in two strings - * @param string $a - * @param string $b - * @return int - */ -function str_diff_at ($a, $b) { - if ($a == $b) - return -1; - $n = min(strlen($a), strlen($b)); - for ($i = 0; $i < $n; $i++) - if ($a[$i] != $b[$i]) - return $i; - return $n; -} - -/** - * Determine if a child URL actually decends from the parent, and that the - * parent is a good URL. - * THIS IS EXPERIMENTAL - * @param string $parent - * @param string $child - * @return bool - */ -function url_descends ( $child, $parent ) { - if ($child == $parent) - return true; - - $keys = array(); - $parts = array(); - - $req = array('scheme', 'host'); - $bad = array('fragment', 'pass', 'user'); - - foreach (array('parent', 'child') as $name) { - $parts[$name] = @parse_url($$name); - if ($parts[$name] === false) - return false; - - $keys[$name] = array_keys($parts[$name]); - - if (array_intersect($keys[$name], $req) != $req) - return false; - - if (array_intersect($keys[$name], $bad) != array()) - return false; - - if (! preg_match('/^https?$/i', strtolower($parts[$name]['scheme']))) - return false; - - if (! array_key_exists('port', $parts[$name])) - $parts[$name]['port'] = (strtolower($parts[$name]['scheme']) == 'https') ? 443 : 80; - - if (! array_key_exists('path', $parts[$name])) - $parts[$name]['path'] = '/'; - } - - // port and scheme must match - if ($parts['parent']['scheme'] != $parts['child']['scheme'] || - $parts['parent']['port'] != $parts['child']['port']) - return false; - - // compare the hosts by reversing the strings - $cr_host = strtolower(strrev($parts['child']['host'])); - $pr_host = strtolower(strrev($parts['parent']['host'])); - - $break = str_diff_at($cr_host, $pr_host); - if ($break >= 0 && ($pr_host[$break] != '*' || substr_count(substr($pr_host, 0, $break), '.') < 2)) - return false; - - // now compare the paths - $break = str_diff_at($parts['child']['path'], $parts['parent']['path']); - if ($break >= 0 - && ($break < strlen($parts['parent']['path']) && $parts['parent']['path'][$break] != '*') - || ($break > strlen($parts['child']['path']))) - return false; - - return true; -} - - -/** - * Create a user session - * @global array $profile - * @global array $proto - */ -function user_session () { - global $proto, $profile; - - session_name('phpMyID_Server'); - if (OCP\Config::getSystemValue( "forcessl", false )) { - ini_set("session.cookie_secure", "on"); - } - @session_start(); - - $profile['authorized'] = (isset($_SESSION['auth_username']) - && $_SESSION['auth_username'] == $profile['auth_username']) - ? true - : false; - - debug('Started user session: ' . session_id() . ' Auth? ' . $profile['authorized']); -} - - -/** - * Return HTML - * @global string $charset - * @param string $message - */ -function wrap_html ( $message ) { - global $charset, $profile; - header('Content-Type: text/html; charset=' . $charset); - $html= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html> -<head> -<title>phpMyID</title> -<link rel="openid.server" href="' . $profile['req_url'] . '" /> -<link rel="openid.delegate" href="' . $profile['idp_url'] . '" /> -' . implode("\n", $profile['opt_headers']) . ' -<meta name="charset" content="' . $charset . '" /> -<meta name="robots" content="noindex,nofollow" /> -</head> -<body> -<p>' . $message . '</p> -</body> -</html> -'; - echo $html; - exit(0); -} - - -/** - * Return a key-value pair in plain text - * @global string $charset - * @param array $keys - */ -function wrap_kv ( $keys ) { - global $charset; - - debug($keys, 'Wrapped key/vals'); - header('Content-Type: text/plain; charset=' . $charset); - foreach ($keys as $key => $value) - printf("%s:%s\n", $key, $value); - - exit(0); -} - - -/** - * Redirect, with OpenID keys - * @param string $url - * @param array @keys - */ -function wrap_keyed_redirect ($url, $keys) { - $keys = append_openid($keys); - debug($keys, 'Location keys'); - - $q = strpos($url, '?') ? '&' : '?'; - wrap_redirect($url . $q . http_build_query($keys)); -} - - -/** - * Redirect the browser - * @global string $charset - * @param string $url - */ -function wrap_redirect ($url) { - header('HTTP/1.1 302 Found'); - header('Location: ' . $url); - debug('Location: ' . $url); - exit(0); -} - -/** - * Return an HTML refresh - * @global string $charset - * @param string $url - */ -function wrap_refresh ($url) { - global $charset; - - header('Content-Type: text/html; charset=' . $charset); - echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html> -<head> -<title>phpMyID</title> -<meta http-equiv="refresh" content="0;url=' . $url . '"> -</head> -<body> -<p>Redirecting to <a href="' . $url . '">' . $url . '</a></p> -</body> -</html> -'; - - debug('Refresh: ' . $url); - exit(0); -} - - -/** - * Implement binary x_or - * @param string $a - * @param string $b - * @return string - */ -function x_or ($a, $b) { - $r = ""; - - for ($i = 0; $i < strlen($b); $i++) - $r .= $a[$i] ^ $b[$i]; - debug("Xor size: " . strlen($r)); - return $r; -} - - - -/* - * App Initialization - */ -// Determine the charset to use -$GLOBALS['charset'] = 'iso-8859-1'; - -// Set the internal encoding -if (function_exists('mb_internal_encoding')) - mb_internal_encoding($charset); - -// Avoid problems with non-default arg_separator.output settings -// Credit for this goes to user 'prelog' on the forums -ini_set('arg_separator.output', '&'); - -// Do a check to be sure everything is set up correctly -self_check(); - - -/** - * Determine the HTTP request port - * @name $port - * @global integer $GLOBALS['port'] - */ -$GLOBALS['port'] = ((isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on' && $_SERVER['SERVER_PORT'] == 443) - || $_SERVER['SERVER_PORT'] == 80) - ? '' - : ':' . $_SERVER['SERVER_PORT']; - - -/** - * Determine the HTTP request protocol - * @name $proto - * @global string $GLOBALS['proto'] - */ -$GLOBALS['proto'] = (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on') ? 'https' : 'http'; - -// Set the authorization state - DO NOT OVERRIDE -$profile['authorized'] = false; - -global $IDENTITY; -global $USERNAME; - -// Set a default IDP URL -if (! array_key_exists('idp_url', $profile)) - $profile['idp_url'] = $IDENTITY; - -//Determine the requested URL - DO NOT OVERRIDE -$profile['req_url'] = sprintf("%s://%s%s", - $proto, - OCP\Util::getServerHost(), -// $port,//host already includes the path - $_SERVER["REQUEST_URI"]); - - -// Set the default allowance for testing -if (! array_key_exists('allow_test', $profile)) - $profile['allow_test'] = false; - -// Set the default allowance for gmp -if (! array_key_exists('allow_gmp', $profile)) - $profile['allow_gmp'] = false; - -// Set the default force bigmath - BAD IDEA to override this -if (! array_key_exists('force_bigmath', $profile)) - $profile['force_bigmath'] = false; - -// Determine if GMP is usable -$profile['use_gmp'] = (extension_loaded('gmp') && $profile['allow_gmp']) ? true : false; - -// Determine if I can perform big math functions -$profile['use_bigmath'] = (extension_loaded('bcmath') || $profile['use_gmp'] || $profile['force_bigmath']) ? true : false; - -// Set a default authentication domain -if (! array_key_exists('auth_domain', $profile)) - $profile['auth_domain'] = $profile['req_url'] . ' ' . $profile['idp_url']; - -// Set a default authentication realm -if (! array_key_exists('auth_realm', $profile)) - $profile['auth_realm'] = 'ownCloud'; - -// Determine the realm for digest authentication - DO NOT OVERRIDE -$profile['php_realm'] = $profile['auth_realm'] . (ini_get('safe_mode') ? '-' . getmyuid() : ''); - -// Set a default lifetime - the lesser of GC and cache time -if (! array_key_exists('lifetime', $profile)) { - $sce = session_cache_expire() * 60; - $gcm = ini_get('session.gc_maxlifetime'); - $profile['lifetime'] = $sce < $gcm ? $sce : $gcm; -} - -// Set a default log file -if (! array_key_exists('logfile', $profile)) - $profile['logfile'] = get_temp_dir() . DIRECTORY_SEPARATOR . $profile['auth_realm'] . '.debug.log'; - - -/* - * Optional Initialization - */ -// Setup optional headers -$profile['opt_headers'] = array(); - -// Determine if I should add microid stuff -if (array_key_exists('microid', $profile)) { - $hash = sha1($profile['idp_url']); - $values = is_array($profile['microid']) ? $profile['microid'] : array($profile['microid']); - - foreach ($values as $microid) { - preg_match('/^([a-z]+)/i', $microid, $mtx); - $profile['opt_headers'][] = sprintf('<meta name="microid" content="%s+%s:sha1:%s" />', $mtx[1], $proto, sha1(sha1($microid) . $hash)); - } -} - -// Determine if I should add pavatar stuff -if (array_key_exists('pavatar', $profile)) - $profile['opt_headers'][] = sprintf('<link rel="pavatar" href="%s" />', $profile['pavatar']); - - -/* - * Do it - */ -// Decide which runmode, based on user request or default -$run_mode = (isset($_REQUEST['openid_mode']) - && in_array($_REQUEST['openid_mode'], $known['openid_modes'])) - ? $_REQUEST['openid_mode'] - : 'no'; - -// Run in the determined runmode -debug("Run mode: $run_mode at: " . time()); -debug($_REQUEST, 'Request params'); -call_user_func($run_mode . '_mode'); -?> diff --git a/apps/user_openid/user.php b/apps/user_openid/user.php index 392424795f8..88571ba618e 100644 --- a/apps/user_openid/user.php +++ b/apps/user_openid/user.php @@ -44,7 +44,4 @@ if(!OCP\User::userExists($USERNAME)){ } $IDENTITY=OCP\Util::linkToAbsolute( "user_openid", "user.php" ).'/'.$USERNAME; -require_once 'phpmyid.php'; - - -?> +require_once 'openid/phpmyid.php'; diff --git a/apps/user_openid/user_openid.php b/apps/user_openid/user_openid.php index 8fb694f75c6..9654001cbc5 100644 --- a/apps/user_openid/user_openid.php +++ b/apps/user_openid/user_openid.php @@ -21,10 +21,10 @@ * */ -require_once('class.openid.v3.php'); +require_once('openid/class.openid.v3.php'); /** - * Class for user management in a SQL Database (e.g. MySQL, SQLite) + * Class for user OpenId backend */ class OC_USER_OPENID extends OC_User_Backend { /** @@ -37,7 +37,7 @@ class OC_USER_OPENID extends OC_User_Backend { */ public function checkPassword( $uid, $password ){ // Get identity from user and redirect browser to OpenID Server - $openid = new SimpleOpenID; + $openid = new SimpleOpenID(); $openid->SetIdentity($uid); $openid->SetTrustRoot('http://' . OCP\Util::getServerHost()); if ($openid->GetOpenIDServer()){ @@ -63,7 +63,3 @@ class OC_USER_OPENID extends OC_User_Backend { } } } - - - -?> diff --git a/apps/user_webfinger/.htaccess b/apps/user_webfinger/.htaccess new file mode 100644 index 00000000000..1b13cf788ff --- /dev/null +++ b/apps/user_webfinger/.htaccess @@ -0,0 +1,5 @@ +<IfModule mod_rewrite.c> +RewriteEngine on +RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}] +RewriteRule ^host-meta host-meta.php [QSA,L] +</IfModule> diff --git a/apps/user_webfinger/appinfo/app.php b/apps/user_webfinger/appinfo/app.php index a45efd96a46..b3d9bbc7f37 100644 --- a/apps/user_webfinger/appinfo/app.php +++ b/apps/user_webfinger/appinfo/app.php @@ -1,7 +1 @@ <?php -OCP\App::register( array( - 'order' => 11, - 'id' => 'user_webfinger', - 'name' => 'Webfinger' )); -OCP\CONFIG::setAppValue('core', 'public_host-meta', '/apps/user_webfinger/host-meta.php'); -OCP\CONFIG::setAppValue('core', 'public_webfinger', '/apps/user_webfinger/webfinger.php'); diff --git a/apps/user_webfinger/appinfo/info.xml b/apps/user_webfinger/appinfo/info.xml index 4e28814a26c..f74e5d3f93a 100644 --- a/apps/user_webfinger/appinfo/info.xml +++ b/apps/user_webfinger/appinfo/info.xml @@ -7,4 +7,8 @@ <author>Michiel de Jong, Florian Hülsmann</author> <require>4</require> <shipped>true</shipped> + <public> + <host-meta>host-meta.php</host-meta> + <webfinger>webfinger.php</webfinger> + </public> </info> diff --git a/apps/user_webfinger/appinfo/version b/apps/user_webfinger/appinfo/version index 1d71ef97443..a2268e2de44 100644 --- a/apps/user_webfinger/appinfo/version +++ b/apps/user_webfinger/appinfo/version @@ -1 +1 @@ -0.3
\ No newline at end of file +0.3.1
\ No newline at end of file diff --git a/apps/user_webfinger/host-meta.php b/apps/user_webfinger/host-meta.php index a4f494ce837..6f6ac46eb91 100644 --- a/apps/user_webfinger/host-meta.php +++ b/apps/user_webfinger/host-meta.php @@ -3,12 +3,28 @@ if (!OCP\App::isEnabled("user_webfinger")) { return; } +if(class_exists('OC')){ + $WEBROOT=OC::$WEBROOT; +}else{//not called trough remote.php try to guess the webroot the best we can from here + // calculate the root directories + $SERVERROOT=str_replace("\\",'/',substr(__FILE__,0,-strlen('apps/user_webfinger/host-meta.php'))); + $WEBROOT=substr($SERVERROOT,strlen(realpath($_SERVER['DOCUMENT_ROOT']))); + + if($WEBROOT!='' and $WEBROOT[0]!=='/'){ + $WEBROOT='/'.$WEBROOT; + } +} + +if(substr($WEBROOT,-1)==='/'){ + $WEBROOT=substr($WEBROOT,0,-1); +} + $hostMetaHeader = array( 'Access-Control-Allow-Origin' => '*', 'Content-Type' => 'application/xrd+json' ); $serverName = $_SERVER['SERVER_NAME']; -$hostMetaContents = '{"links":[{"rel":"lrdd","template":"http'.(isset($_SERVER['HTTPS'])?'s':'').'://'.$serverName.'/public.php?service=webfinger&q={uri}"}]}'; +$hostMetaContents = '{"links":[{"rel":"lrdd","template":"http'.(isset($_SERVER['HTTPS'])?'s':'').'://'.$serverName.$WEBROOT.'/public.php?service=webfinger&q={uri}"}]}'; foreach($hostMetaHeader as $header => $value) { header($header . ": " . $value); } diff --git a/apps/user_webfinger/webfinger.php b/apps/user_webfinger/webfinger.php index 0f882a96cf8..b725937948a 100644 --- a/apps/user_webfinger/webfinger.php +++ b/apps/user_webfinger/webfinger.php @@ -7,27 +7,20 @@ header("Access-Control-Allow-Origin: *"); header("Content-Type: application/xrd+json"); /** - * To include your app in the webfinger XML, add a new script with file name + * To include your app in the webfinger JSON, add a new script with file name * 'webfinger.php' to /apps/yourapp/appinfo/, which prints out the XML parts * to be included. That script can make use of the constants WF_USER (e. g. * "user"), WF_ID (user@host) and WF_BASEURL (e. g. https://host/owncloud). * An example could look like this: * - * <Link - * rel="myProfile" - * type="text/html" - * href="<?php echo WF_BASEURL; ?>/apps/myApp/profile.php?user=<?php echo WF_USER; ?>"> - * </Link> + * { + * "rel":"myProfile", + * "type":"text/html", + * "href":"<?php echo WF_BASEURL; ?>/apps/myApp/profile.php?user=<?php echo WF_USER; ?>" + * } * * but can also use complex database queries to generate the webfinger result **/ -// calculate the documentroot -// modified version of the one in lib/base.php that takes the .well-known symlink into account -/*$DOCUMENTROOT=realpath($_SERVER['DOCUMENT_ROOT']); -$SERVERROOT=str_replace("\\",'/',dirname(dirname(dirname(dirname(__FILE__))))); -$SUBURI=substr(realpath($_SERVER["SCRIPT_FILENAME"]),strlen($SERVERROOT)); -$WEBROOT=substr($SUBURI,0,-34); -*/ $userName = ''; $hostName = ''; @@ -63,10 +56,9 @@ echo "{\"links\":["; $apps = OC_Appconfig::getApps(); foreach($apps as $app) { if(OCP\App::isEnabled($app)) { - if(is_file(OC::$APPSROOT . '/apps/' . $app . '/appinfo/webfinger.php')) { + if(is_file(OC_App::getAppPath($app). '/appinfo/webfinger.php')) { require($app . '/appinfo/webfinger.php'); } } } echo "]}"; -?> |