Форум » [x]Harbour » Облачные сервисы (WebDAV, CalDAV &Co) » Ответить

Облачные сервисы (WebDAV, CalDAV &Co)

Dr. Oldwarez: После некоторого разбирательства с прогой удалось организовать импорт из ICAL-файлов и экспорт в оные. Но тут шеф захотел, чтобы был прямой контакт с CalDav через интернет. Я знаю, что на питоне такое возможно. На дельфине и даже Xojo (RealBasic) тоже - через спецбиблиотеку. Но как реализовать подключение к облачному сервису в Harbour/Minigui? Возможно ли это вообще?

Ответов - 174, стр: 1 2 3 4 5 6 7 8 9 All

SergKis: Dr. Oldwarez Посмотрите hb ...\contrib\hbtip\... и поиск по *.prg "http " в MiniGui\samples

SergKis: PS Если сервер htpps, то смотрите curl утилиту (в ней был сертификат ssl)

Петр: SergKis пишет: смотрите curl утилиту + И в дополнение Use a webdav service with curl Если получится с curl, то следующим шагом может стать переход на contrib\hbcurl


MIKHAIL: SergKis пишет: Если сервер htpps, то смотрите curl утилиту (в ней был сертификат ssl) про https: тут писали http://clipper.borda.ru/?1-4-0-00000774-000-10001-0-1570774946

Dr. Oldwarez: Это по скачиванию веб-страниц и считыванию данных с них. У меня немножко другой случай. Не веб-страница, а календарь. И много классов, которые я не нахожу в своей документации. Например TIpClientHttp, THtmlDocument. Скачал специально harbour c примерами и там в contrib\hbcurl что-то про выгрузку данных на ftp, но никак уж про CalDav. Да и формат там отнюдь не веб-страничный.

Петр: Dr. Oldwarez пишет: Скачал специально harbour c примерами и там в contrib\hbcurl что-то про выгрузку данных на ftp, но никак уж про CalDav. Там что-то про использование libcurl (инициализация, заполнение структур) на примере работы с ftp. Другие примеры на C можно найти на странице проекта. Use a webdav service with curl - это о чем?

alkresin: Не знаю, что такое CalDav и не вспомню навскидку о webdav, но предложение SergKis использовать curl мне кажется перспективным. Поиск на Яндексе по 'curl webdav' дает кучу ссылок - т.е., curl с webdav работает. Я обычно советую использовать не hbcurl, чтобы не тратить время на его изучение, а запускать непосредственно утилиту командной строки curl.exe, передавая ей файл с нужными параметрами.

Dr. Oldwarez: Про CalDAV здесь CalDAV Но мне бы всё-таки хотелось встроенную библиотеку для удобства дистрибуции. Изучить я смогу. Только дайте мне документацию, а то hbd я не знаю, чем прочесть. И несколько примеров.

SergKis: Dr. Oldwarez Рекомендую начать с curl, можно найти описание и даже примеры, а встроенную библиотеку, придется изучать по исходникам. Вот практически первый ответ на запрос google: "curl.exe и caldav запросы" https://www.atmail.com/blog/caldav-carddav/

Dr. Oldwarez: Скачал этот CURL теперь вот вопрос, что ставить в PROPFIND [pre2]curl -k --user "${login}:${password}" -X PROPFIND "https://owncloud.jujens.eu/remote.php/caldav/calendars/jujens/defaultcalendar"[/pre2] Вместо https://owncloud.jujens.eu/remote.php/caldav/calendars/jujens/defaultcalendar У меня в календаре на вкладке Subscription выходят пять адресов CalDAV Subscription URL CalDAV Account URL WebDAV/ICS Subscription URL Feed URL Embed Script Что именно ставить туда?

SergKis: Попробуйте первых 3, добавьте только параметр -O (в тек. каталоге файл)

Dr. Oldwarez: SergKis пишет: Попробуйте первых 3, добавьте только параметр -O (в тек. каталоге файл) Выходит так [pre2]c:\curl\bin>curl -k -o --user "${USERNAME}:${PASSWORD}" -X PROPFIND "https://webmail.kleeblatt.com/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7" Warning: The file name argument '--user' looks like a flag. curl: (3) URL using bad/illegal format or missing URL <?xml version="1.0" encoding="utf-8"?> <d:error xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"> <s:exception>Sabre\DAV\Exception\NotAuthenticated</s:exception> <s:message>No basic authentication headers were found</s:message> <s:sabredav-version>1.8.12</s:sabredav-version> </d:error>[/pre2]

SergKis: Dr. Oldwarez флаг --user правильно задан ? curl -u username:password ...

Петр: SergKis пишет: флаг --user правильно задан ? Скорее всего правильно: флаги -u и --user равноценны. "${USERNAME}:${PASSWORD}" - пытается получить значения из переменных окружения (хотя хз, что там на самом деле). а вот -o - вывод в файл, а не stdout после -o должен следовать аргумент имя локального файла, он не указан поэтому и получили Warning: The file name argument '--user' looks like a flag

SergKis: Петр пишет хотя хз, что там на самом деле Именно это и говорил, раз получили такое сообщение Warning:... после -o должен следовать аргумент имя локального файла, он не указан после -o да, после -O не обязательно, если не указан берет имя файла из адреса без пути (для пробы и тестов пробовать можно)

Петр: SergKis пишет: после -o да Ну так у него -o а вот "${USERNAME}:${PASSWORD}" - это что-то не из винды, непонятно может msys использует. Интересно что это значит: curl: (3) URL using bad/illegal format or missing URL У меня при curl -k -oout.txt -O --user "${USERNAME}:${PASSWORD}" -X PROPFIND "https://webmail.kleeblatt.com/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7" получилось <?xml version="1.0" encoding="utf-8"?> <d:error xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"> <s:exception>Sabre\DAV\Exception\NotAuthenticated</s:exception> <s:message>Username or password does not match</s:message> <s:sabredav-version>1.8.12</s:sabredav-version> </d:error> что в принципе правильно

SergKis: Петр пишет Интересно что это значит: Из показанной выше ссылке curl и CalDav (google перевод) понял, что на сервере надо иметь рег. запись и по ней делать пошаговые запросы, получая адреса. Возможно, сообщение, как следствие не коннекта, не сталкивался с таким. Пробовал работу с ftp и передавать, получать json данные с чужим сервером.

Dr. Oldwarez: Петр пишет: У меня при curl -k -oout.txt -O --user "${USERNAME}:${PASSWORD}" -X PROPFIND "https://webmail.kleeblatt.com/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7" получилось <?xml version="1.0" encoding="utf-8"?> <d:error xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"> <s:exception>Sabre\DAV\Exception\NotAuthenticated</s:exception> <s:message>Username or password does not match</s:message> <s:sabredav-version>1.8.12</s:sabredav-version> </d:error> что в принципе правильно Вроде что-то получилось [pre2]curl -k -o test.xml --user USERNAME:PASSWORD -X PROPFIND "https://webmail.kleeblatt.com/rpc.php/principals/illya@kleeblatt.com/"[/pre2] Вышло [pre2] <?xml version="1.0" encoding="utf-8"?> <d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/" xmlns:card="urn:ietf:params:xml:ns:carddav"> <d:response><d:href>/rpc.php/principals/illya@kleeblatt.com/</d:href><d:propstat><d:prop><d:getlastmodified>Fri, 19 Mar 2021 20:06:47 GMT</d:getlastmodified><d:resourcetype><d:principal/> </d:resourcetype></d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat></d:response></d:multistatus>[/pre2] Теперь, как, собственно говоря, войти в календарь и просмотреть его содержимое?

Петр: SergKis пишет: Возможно, сообщение, как следствие не коннекта, Вот нашел API error: curl: (3) URL using bad/illegal format or missing URL

Петр: Dr. Oldwarez пишет: Вроде что-то получилось curl -k -o test.xml --user USERNAME:PASSWORD -X PROPFIND "https://webmail.kleeblatt.com/rpc.php/principals/illya@kleeblatt.com/" Куда этот url ссылается? Эту часть почему отбросили /calendar~GhGVum1xWexaffEfhiVkNN7

SergKis: Dr. Oldwarez пишет Теперь, как, собственно говоря, войти в календарь и просмотреть его содержимое? <d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/" xmlns:card="urn:ietf:params:xml:ns:carddav"> Похожее видел в той статье[pre2] <?xml version="1.0"?> <d:multistatus xmlns:d="DAV:" xmlns:s="https://sabredav.org/ns" xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="https://calendarserver.org/ns/" xmlns:card="urn:ietf:params:xml:ns:carddav"> ... Но у нас также есть еще одна интересная информация, возвращенная в теле запроса, а именно другая коллекция, которую мы можем изучить: /calendars/user@demo.atmail.com/ Мы будем выполнять рекурсию, насколько это возможно, с глубиной, установленной на бесконечность, но передаем некоторые данные запроса, чтобы указать, что нас интересуют только объекты календаря, и чтобы вернуть только URL-адрес, тип ресурса и отображаемое имя коллекции: $ curl --user "user@demo.atmail.com:ThePassword" -sD /dev/stderr -H "Content-Type: application/xml" -X PROPFIND -H "Depth: infinity" --data '<d:propfind xmlns:d="DAV:" xmlns:cs="https://calendarserver.org/ns/"><d:prop><d:resourcetype /><d:displayname /></d:prop></d:propfind>' https://demo-server.atmail.com:8443/calendars/user@demo.atmail.com/ | xmllint -format - ... [/pre2] и т.д. по статье, наверно, так

Dr. Oldwarez: Петр пишет:  цитата: Вроде что-то получилось curl -k -o test.xml --user USERNAME:PASSWORD -X PROPFIND "https://webmail.kleeblatt.com/rpc.php/principals/illya@kleeblatt.com/" Куда этот url ссылается? Эту часть почему отбросили /calendar~GhGVum1xWexaffEfhiVkNN7 Добавил потерянную часть. И вот [pre2]<?xml version="1.0" encoding="utf-8"?> <d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/" xmlns:card="urn:ietf:params:xml:ns:carddav"><d:response> <d:href>/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/</d:href> <d:propstat><d:prop><d:resourcetype><d:collection/><cal:calendar/></d:resourcetype></d:prop> <d:status>HTTP/1.1 200 OK</d:status></d:propstat></d:response><d:response><d:href>/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/w7klsLDepGCBYAA7Gmr4GIe.ics</d:href> <d:propstat><d:prop><d:getlastmodified>Sat, 13 Mar 2021 16:58:11 GMT</d:getlastmodified> <d:getcontentlength>500</d:getcontentlength><d:resourcetype/><d:getetag>"bb0f09c6bf34469cad5f872725ed93b9"</d:getetag> <d:getcontenttype>text/calendar; charset=utf-8</d:getcontenttype></d:prop><d:status>HTTP/1.1 200 OK</d:status> </d:propstat></d:response> <d:response><d:href>/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/8gpZuoZBJQDnLlr-66brhHG.ics</d:href> <d:propstat><d:prop><d:getlastmodified>Thu, 04 Mar 2021 16:51:14 GMT</d:getlastmodified><d:getcontentlength>515</d:getcontentlength><d:resourcetype/> <d:getetag>"c1daa4a100ad72550dc468e539047aac"</d:getetag><d:getcontenttype>text/calendar; charset=utf-8</d:getcontenttype></d:prop> <d:status>HTTP/1.1 200 OK</d:status></d:propstat></d:response><d:response> <d:href>/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/VM3wwLrVEm3Zwn-UkKAzXra.ics</d:href> <d:propstat><d:prop><d:getlastmodified>Thu, 04 Mar 2021 16:51:33 GMT</d:getlastmodified><d:getcontentlength>577</d:getcontentlength> <d:resourcetype/><d:getetag>"da02d9cfbc83085498615e55e87d25ea"</d:getetag><d:getcontenttype>text/calendar; charset=utf-8</d:getcontenttype></d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat></d:response> <d:response><d:href>/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/e5aab12a-1a2a-4309-96d4-cccb1b67d9ba.ics</d:href> <d:propstat><d:prop><d:getlastmodified>Mon, 08 Mar 2021 06:59:00 GMT</d:getlastmodified><d:getcontentlength>659</d:getcontentlength> <d:resourcetype/><d:getetag>"ff0f3e0392c3a16579e8b87442f60ff4"</d:getetag> <d:getcontenttype>text/calendar; charset=utf-8</d:getcontenttype></d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat></d:response> <d:response><d:href>/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/seukIp8ajv0rOXXx8IG4Q9j.ics</d:href><d:propstat> <d:prop><d:getlastmodified>Mon, 15 Mar 2021 12:56:41 GMT</d:getlastmodified><d:getcontentlength>546</d:getcontentlength><d:resourcetype/> <d:getetag>"fa79fe0b28dea03c0db457af39980eae"</d:getetag><d:getcontenttype>text/calendar; charset=utf-8</d:getcontenttype></d:prop> <d:status>HTTP/1.1 200 OK</d:status></d:propstat></d:response><d:response> <d:href>/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/83sEK6nFBn4-TQ1XcZoPvWz.ics</d:href><d:propstat><d:prop> <d:getlastmodified>Mon, 15 Mar 2021 12:52:23 GMT</d:getlastmodified><d:getcontentlength>526</d:getcontentlength><d:resourcetype/> <d:getetag>"11735b683c59f7e3c0a60842c1df4101"</d:getetag><d:getcontenttype>text/calendar; charset=utf-8</d:getcontenttype></d:prop> <d:status>HTTP/1.1 200 OK</d:status></d:propstat></d:response></d:multistatus>[/pre2]

Петр: Dr. Oldwarez пишет: Я так понял, это список ICS файлов. Теперь надо открывать каждый ICS по отдельности, чтобы получить записи календаря? Наверное. Попробуйте стащить один и поработать с ним оффлайн.

SergKis: Dr. Oldwarez пишет Добавил потерянную часть. И вот Смотреть без переноса трудно, но похоже (по статье) это коллекция календарей, опять по статье след. шаг Итак, заглянем внутрь одной из этих коллекций (календарей). Выберем календарь «Дни рождения» и посмотрим, что там: $ curl --user "user@demo.atmail.com:ThePassword" -sD /dev/stderr -H "Content-Type: application/xml" -X PROPFIND -H "Depth: infinity" https://demo-server.atmail.com:8443/calendars/user@demo.atmail.com/02f29dd9516f1f0c415527e0a60cb3f7/ | xmllint -format - ...ответ В данном случае это календарное событие, и мы можем сказать об этом через свойство: <d: getcontenttype> текст / календарь; charset = utf-8; компонент = vevent </ d: getcontenttype> Наконец, давайте извлечем это событие с помощью обычного старого HTTP GET и посмотрим, что у нас есть: curl --user "user@demo.atmail.com:ThePassword" -i -X GET https://demo-server.atmail.com:8443/calendars/user@demo.atmail.com/02f29dd9516f1f0c415527e0a60cb3f7/33992bd8-d3fe-4b07-baaf-c43d0042fae8.ics HTTP/1.1 200 OK Server: nginx/1.16.1 Date: Sat, 09 May 2020 05:28:59 GMT Content-Type: text/calendar; charset=utf-8; component=vevent Content-Length: 554 Connection: keep-alive Last-Modified: Sat, 09 May 2020 01:24:06 GMT ETag: "d34b216dac0dcf327a3cf2d79f95a226" X-Content-Type-Options: nosniff BEGIN:VCALENDAR VERSION:2.0 PRODID:-//apiserver//API Server DAV//EN BEGIN:VEVENT UID:33992bd8-d3fe-4b07-baaf-c43d0042fae8 DTSTART;VALUE=DATE:20200506 DTEND;VALUE=DATE:20200507 CREATED:20190124T104704Z DTSTAMP:20190128T234044Z LAST-MODIFIED;X-VOBJ-FLOATINGTIME-ALLOWED=TRUE:20200503T203005 RRULE:FREQ=YEARLY;INTERVAL=1;WKST=MO SEQUENCE:30 STATUS:CONFIRMED SUMMARY:Dad's Birthday TRANSP:OPAQUE BEGIN:VALARM ACKNOWLEDGED:20190128T234044Z ACTION:DISPLAY DESCRIPTION:Default Description TRIGGER:-P1D END:VALARM END:VEVENT END:VCALENDAR по статье такие действия для извл. данных

Петр: SergKis пишет: Смотреть без переноса трудно Code Beautify

SergKis: Петр пишет Code Beautify Через эту штуку и надо сюда выкладывать, а то страницы расползлись по ширине хз как, центрованные цитаты вправо улетели далеко за пределы экрана, не говоря о самом тексте

Петр: Надеюсь топикстартер (или модератор) пост №259 удалит, а №260 отформатирует как следует.

Dr. Oldwarez: Петр пишет:  Отправлено: Сегодня 10:01. Заголовок: Надеюсь топикстартер.. - новое! Надеюсь топикстартер (или модератор) пост №259 удалит, а №260 отформатирует как следует. Подформатировал пост 259. 260 удалил как дубликат. Извините за созданные проблемы - я просто не имею опыта с xml файлами

Dr. Oldwarez: SergKis пишет: Смотреть без переноса трудно, но похоже (по статье) это коллекция календарей, опять по статье след. шаг  цитата: Итак, заглянем внутрь одной из этих коллекций (календарей). Выберем календарь «Дни рождения» и посмотрим, что там: $ curl --user "user@demo.atmail.com:ThePassword" -sD /dev/stderr -H "Content-Type: application/xml" -X PROPFIND -H "Depth: infinity" https://demo-server.atmail.com:8443/calendars/user@demo.atmail.com/02f29dd9516f1f0c415527e0a60cb3f7/ | xmllint -format - ...ответ В данном случае это календарное событие, и мы можем сказать об этом через свойство: <d: getcontenttype> текст / календарь; charset = utf-8; компонент = vevent </ d: getcontenttype> Наконец, давайте извлечем это событие с помощью обычного старого HTTP GET и посмотрим, что у нас есть: curl --user "user@demo.atmail.com:ThePassword" -i -X GET https://demo-server.atmail.com:8443/calendars/user@demo.atmail.com/02f29dd9516f1f0c415527e0a60cb3f7/33992bd8-d3fe-4b07-baaf-c43d0042fae8.ics HTTP/1.1 200 OK Server: nginx/1.16.1 Date: Sat, 09 May 2020 05:28:59 GMT Content-Type: text/calendar; charset=utf-8; component=vevent Content-Length: 554 Connection: keep-alive Last-Modified: Sat, 09 May 2020 01:24:06 GMT ETag: "d34b216dac0dcf327a3cf2d79f95a226" X-Content-Type-Options: nosniff BEGIN:VCALENDAR VERSION:2.0 PRODID:-//apiserver//API Server DAV//EN BEGIN:VEVENT UID:33992bd8-d3fe-4b07-baaf-c43d0042fae8 DTSTART;VALUE=DATE:20200506 DTEND;VALUE=DATE:20200507 CREATED:20190124T104704Z DTSTAMP:20190128T234044Z LAST-MODIFIED;X-VOBJ-FLOATINGTIME-ALLOWED=TRUE:20200503T203005 RRULE:FREQ=YEARLY;INTERVAL=1;WKST=MO SEQUENCE:30 STATUS:CONFIRMED SUMMARY:Dad's Birthday TRANSP:OPAQUE BEGIN:VALARM ACKNOWLEDGED:20190128T234044Z ACTION:DISPLAY DESCRIPTION:Default Description TRIGGER:-P1D END:VALARM END:VEVENT END:VCALENDAR по статье такие действия для извл. данных Да, я понял. Но сначала нужно весь список ics-ов считать в массив внешним циклом, а потом каждый ics ещё просмотреть, чтобы получить из него данные календарей. Это очень много вызовов curl и автоматически созданных промежуточных файлов получается. Нельзя ли так, чтобы сразу подключение и считывание прямо программой, минуя многочисленные промежуточные стадии? И как в Harbour считывать данные из XML в массив?

Петр: Dr. Oldwarez пишет: Но мне бы всё-таки хотелось встроенную библиотеку для удобства дистрибуции. Изучить я смогу. Только дайте мне документацию, а то hbd я не знаю, чем прочесть. И несколько примеров. Dr. Oldwarez пишет: Нельзя ли так, чтобы сразу подключение и считывание прямо программой, минуя многочисленные промежуточные стадии? Добейтесь конечного результата с curl, а тогда будете оптимизировать. Сейчас у вас нет ни знаний, ни умений на свои хотелки. Все придет, может быть, а может и не быть. К тому же инсталлятор весом меньше 1 Га в наше время моветон

Петр: Dr. Oldwarez пишет: И как в Harbour считывать данные из XML в массив? Все просто - считываете данные из XML и добавляете в массив. В Harbour нет встроенных функций для роботы с XML, в отличии от того же json или cvs, ini. Значит у вас есть выбор 1) Можете просто разбирать строку с помощью строковых функций; 2) Можете использовать библиотеку hbexpat из contrib, примеры там же; 3) Можете использовать библиотеку hbmxml из contrib, примеры там же; 4) Можете использовать библиотеку hbxml из состава Minigui, пример SAMPLES\Advanced\XmlDemo и др; 5) Можете использовать стандартные технологии MS OLE и Msxml, пример SAMPLES\Advanced\GoogleCharts; и т.д. и т.п.

SergKis: Петр пишет Можете просто разбирать строку с помощью строковых функций; Простой пример выкусить из xml поста 259[pre2] LOCAL cXml := "_ics.xml" // файл с данными из поста 259 LOCAL cBuf := hb_memoread(cXml) LOCAL aBuf, nI IF CRLF $ cBuf ; cBuf := StrTran(cBuf, CRLF, "") ENDIF aBuf := hb_ATokens(cBuf, "<d:response><d:href>") ? aBuf FOR nI := 1 TO Len(aBuf) cBuf := aBuf[nI] ? nI, left(cBuf, AT("</", cBuf)-1) NEXT ... результат ARRAY[8] 1 '' 2 /rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/ 3 /rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/w7klsLDepGCBYAA7Gmr4GIe.ics 4 /rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/8gpZuoZBJQDnLlr-66brhHG.ics 5 /rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/VM3wwLrVEm3Zwn-UkKAzXra.ics 6 /rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/e5aab12a-1a2a-4309-96d4-cccb1b67d9ba.ics 7 /rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/seukIp8ajv0rOXXx8IG4Q9j.ics 8 /rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/83sEK6nFBn4-TQ1XcZoPvWz.ics [/pre2]

SergKis: Dr. Oldwarez пишет Нельзя ли так, чтобы сразу подключение и считывание прямо программой, минуя многочисленные промежуточные стадии? Если пройдете по статье еще раз, то увидите, что каждый вызов curl это запрос к серверу и обработка результата и снова запрос ..., т.е. и без curl будет точно такая же схема работы. Если оформите запрос в ф-ю (вызов curl и обработка результата) с возвратом результата, то если (может и не понадобится) потом надо будет переделаете на исп. lib

SergKis: PS Давать календари надо будет в цивилизованном виде, т.е. таблица dbf\array - "список календарей" -> выбираем -> запрос[ы] curl к серверу -> ответ и таблица dbf\array - "события календаря" если работа online, при offline тот же путь выборки и куда то сохранить первичные запросы (потом обновлять, если ics меняли)

SergKis: PS2 Чуть усложнил и получил таблицу календарей в массиве [pre2] /* * MINIGUI - Harbour Win32 GUI library Demo */ #define _HMG_OUTLOG #include "hmg.ch" #define SHOW_TITLE "Read list ics calendar from xml" REQUEST HB_CODEPAGE_RU1251, HB_CODEPAGE_RU866 REQUEST HB_CODEPAGE_UTF8 REQUEST DBFCDX, DBFFPT REQUEST DBFNTX, DBFDBT //////////////////////////////////////////////////////////////////////////// FUNCTION Main() LOCAL cXml := "_ics.xml" LOCAL cBuf := hb_memoread(cXml) LOCAL aBuf, nI, cI, nK, cK, nN LOCAL aIcs := {} SET CODEPAGE TO RUSSIAN SET LANGUAGE TO RUSSIAN rddSetDefault( "DBFCDX" ) SET CENTURY ON SET DATE GERMAN SET DATE FORMAT "DD.MM.YY" SET DELETED OFF SET EXCLUSIVE ON SET EPOCH TO 2000 SET AUTOPEN ON SET EXACT ON SET SOFTSEEK ON SET NAVIGATION EXTENDED SET FONT TO "Arial", 11 SET OOP ON fErase(".\_MsgLog.txt") IF CRLF $ cBuf ; cBuf := StrTran(cBuf, CRLF, "") ENDIF aBuf := hb_ATokens(cBuf, "<d:response><d:href>") ? aBuf, "Адреса:" nN := 0 FOR nI := 1 TO Len(aBuf) cI := aBuf[nI] nK := AT("</", cI) cK := trim(left(cI, nK-1)) IF right(cK, 4) == ".ics" nN += 1 AAdd(aIcs, { cK, "", "ics "+hb_ntos(nN) }) ENDIF ? nI, cK NEXT ? aBuf := hb_ATokens(cBuf, "<d:propstat><d:prop><d:getlastmodified>") ? aBuf, "Модификация:" nN := 0 FOR nI := 1 TO Len(aBuf) cI := aBuf[nI] cK := "" IF ! "<?xml " $ cI nN += 1 nK := AT("</", cI) cK := left(cI, nK-1) aIcs[nN][2] := cK ENDIF ? nI, cK NEXT ? ? "ICS =", aIcs ?v aIcs ? RETURN NIL Результат ARRAY[8] Адреса: 1 '' 2 /rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/ 3 /rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/w7klsLDepGCBYAA7Gmr4GIe.ics 4 /rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/8gpZuoZBJQDnLlr-66brhHG.ics 5 /rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/VM3wwLrVEm3Zwn-UkKAzXra.ics 6 /rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/e5aab12a-1a2a-4309-96d4-cccb1b67d9ba.ics 7 /rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/seukIp8ajv0rOXXx8IG4Q9j.ics 8 /rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/83sEK6nFBn4-TQ1XcZoPvWz.ics ARRAY[7] Модификация: 1 '' 2 Sat, 13 Mar 2021 16:58:11 GMT 3 Thu, 04 Mar 2021 16:51:14 GMT 4 Thu, 04 Mar 2021 16:51:33 GMT 5 Mon, 08 Mar 2021 06:59:00 GMT 6 Mon, 15 Mar 2021 12:56:41 GMT 7 Mon, 15 Mar 2021 12:52:23 GMT ICS = ARRAY[6] 1 {"/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/w7klsLDepGCBYAA7Gmr4GIe.ics", "Sat, 13 Mar 2021 16:58:11 GMT", "ics 1"} 2 {"/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/8gpZuoZBJQDnLlr-66brhHG.ics", "Thu, 04 Mar 2021 16:51:14 GMT", "ics 2"} 3 {"/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/VM3wwLrVEm3Zwn-UkKAzXra.ics", "Thu, 04 Mar 2021 16:51:33 GMT", "ics 3"} 4 {"/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/e5aab12a-1a2a-4309-96d4-cccb1b67d9ba.ics", "Mon, 08 Mar 2021 06:59:00 GMT", "ics 4"} 5 {"/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/seukIp8ajv0rOXXx8IG4Q9j.ics", "Mon, 15 Mar 2021 12:56:41 GMT", "ics 5"} 6 {"/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/83sEK6nFBn4-TQ1XcZoPvWz.ics", "Mon, 15 Mar 2021 12:52:23 GMT", "ics 6"} [/pre2]

Dr. Oldwarez: SergKis пишет: Давать календари надо будет в цивилизованном виде, т.е. таблица dbf\array - "список календарей" -> выбираем -> запрос[ы] curl к серверу -> ответ и таблица dbf\array - "события календаря" если работа online, при offline тот же путь выборки и куда то сохранить первичные запросы (потом обновлять, если ics меняли) Спасибо за помощь! Я уже понял, примерно, как это. Но вот вопрос: если календарь заполнен и в нём 500 событий, это что, надо 500 раз соединяться? Такого DDOSа не выдержат ни сеть, ни комп. Профильтровать их тоже не получится - пока не откроешь ICS, его параметры будут неизвестны.

SergKis: Dr. Oldwarez пишет Такого DDOSа не выдержат ни сеть, ни комп. Это не DDOS, а REST API спец. механизм для доступа и обмена данными, смело используйте Профильтровать их тоже не получится - пока не откроешь ICS, его параметры будут неизвестны. Если сохранять в dbf, то можно опираться на дату модификации сначала ics, потом события при изм. календаря Если нет команды получить все события, получаем по одному

SergKis: Dr. Oldwarez пишет Но вот вопрос: если календарь заполнен и в нём 500 событий, это что, надо 500 раз соединяться? Вы не сделали запроса к конкретному ics, после списка, по статье, я так понял, должны получить на календарь список всех событий с датой создания и модификации. Т.е. если уже в dbf занесли данные, то можно выбрать из списка только изменненные и по ним делать запрос для получения данных VEVENT и изменять dbf только по ним

Dr. Oldwarez: SergKis пишет: Если нет команды получить все события, получаем по одному А мне надо именно, что ВСЕ события получить. Дата модификации неинформативна. Не дата модификации мне нужна, а дата и время события. Это и есть первичный ключ в моём локальном календаре

Петр: Dr. Oldwarez пишет: А мне надо именно, что ВСЕ события получить. Так втащите весь календарь на локальный комп (за один запрос), а потом синхронизируйте его с сервером Пример с GET делали или нет?

SergKis: Dr. Oldwarez пишет А мне надо именно, что ВСЕ события получить. Дата модификации неинформативна. Не дата модификации мне нужна, а дата и время события. Это и есть первичный ключ в моём локальном календаре Причем здесь, информативно\не информативно и ваш первичный ключ. Есть архитектура, устройство календаря и оно такое какое есть. Есть правила доступа и вы их соблюдаете или нет. Как минимум, надо сделать все запросы для понимания ситуевины и от нее переходить к вашему устройству базы, но точно не наоборот

Dr. Oldwarez: Петр пишет: Да втащите весь календарь на локальный комп (за один запрос), а потом синхронизируйте его с сервером Пример с GET делали или нет? Сделал [pre2]curl -o test2.txt --user USERNAME:PASSWORD -i -X GET https://webmail.kleeblatt.com/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/[/pre2] И вот... [pre2]HTTP/1.1 200 OK Date: Sun, 21 Mar 2021 17:51:23 GMT Server: Apache X-Powered-By: PHP/7.3.27 Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate Pragma: no-cache Vary: Accept-Encoding Set-Cookie: PHPSESSID=e5ud2bc6fab8mv59hf8hanbhd8; path=/ Set-Cookie: horde_secret_key=e5ud2bc6fab8mv59hf8hanbhd8; path=/; domain=webmail.kleeblatt.com; HttpOnly Set-Cookie: default_horde_view=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; domain=webmail.kleeblatt.com X-Powered-By: PleskLin Transfer-Encoding: chunked Content-Type: text/html; charset=utf-8 <html> <head> <title>Index for calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/ - SabreDAV 1.8.12-stable</title> <style type="text/css"> body { Font-family: arial} h1 { font-size: 150% } </style> <link rel="shortcut icon" href="/rpc.php/?sabreAction=asset&assetName=favicon.ico" type="image/vnd.microsoft.icon" /></head> <body> <h1>Index for calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/</h1> <table> <tr><th width="24"></th><th>Name</th><th>Type</th><th>Size</th><th>Last modified</th></tr> <tr><td colspan="5"><hr /></td></tr><tr> <td><a href="/rpc.php/calendars/illya@kleeblatt.com"> <img src="/rpc.php/?sabreAction=asset&assetName=icons%2Fparent.png" width="24" alt="Parent" /></a></td> <td><a href="/rpc.php/calendars/illya@kleeblatt.com">..</a></td> <td>[parent]</td> <td></td> <td></td> </tr><tr> <td><a href="/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/w7klsLDepGCBYAA7Gmr4GIe.ics"> <img src="/rpc.php/?sabreAction=asset&assetName=icons%2Ffile.png" alt="" width="24" /></a></td> <td><a href="/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/w7klsLDepGCBYAA7Gmr4GIe.ics">w7klsLDepGCBYAA7Gmr4GIe.ics</a></td> <td>text/calendar; charset=utf-8</td> <td>500</td> <td>2021-03-13T16:58:11+00:00</td> </tr><tr> <td><a href="/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/8gpZuoZBJQDnLlr-66brhHG.ics"> <img src="/rpc.php/?sabreAction=asset&assetName=icons%2Ffile.png" alt="" width="24" /></a></td> <td><a href="/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/8gpZuoZBJQDnLlr-66brhHG.ics">8gpZuoZBJQDnLlr-66brhHG.ics</a> </td> <td>text/calendar; charset=utf-8</td>[/pre2]

Петр: Dr. Oldwarez пишет: curl -o test2.txt --user USERNAME:PASSWORD -i -X GET https://webmail.kleeblatt.com/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/ И что мы пытались скачать ?! Присобачьте к url имя календаря, например, seukIp8ajv0rOXXx8IG4Q9j.ics ... GET https://webmail.kleeblatt.com/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/seukIp8ajv0rOXXx8IG4Q9j.ics

Dr. Oldwarez: Петр пишет: Присобачьте к url имя календаря, например, seukIp8ajv0rOXXx8IG4Q9j.ics ... GET https://webmail.kleeblatt.com/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/seukIp8ajv0rOXXx8IG4Q9j.ics Йееес!!! Получилось! [pre2] BEGIN:VCALENDAR VERSION:2.0 X-WR-CALNAME:Kalender von illya@kleeblatt.com PRODID:-//The Horde Project//Horde iCalendar Library//EN BEGIN:VEVENT DTSTART:20210318T140000Z DTEND:20210318T150000Z DTSTAMP:20210321T192856Z UID:20210315125442.qQtI4Rdk7FakWsfY8RiLVTg@webmail.kleeblatt.com CREATED:20210315T125442Z LAST-MODIFIED:20210315T125641Z SUMMARY:Testing of Import DESCRIPTION:Not so easy, but I will try to do it LOCATION:Somewhere on the Earth CLASS:PUBLIC STATUS:CONFIRMED TRANSP:OPAQUE END:VEVENT END:VCALENDAR [/pre2]

Петр: Dr. Oldwarez пишет: Йееес!!! Получилось! Вери велл.. Результат применения PROPFIND / GET к колекции мы видели. Сделайте еще PROPFIND для календаря ... PROPFIND https://webmail.kleeblatt.com/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/seukIp8ajv0rOXXx8IG4Q9j.ics

Dr. Oldwarez: Петр пишет: Вери велл.. Результат применения PROPFIND / GET к колекции мы видели. Сделайте еще PROPFIND для календаря ... PROPFIND https://webmail.kleeblatt.com/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/seukIp8ajv0rOXXx8IG4Q9j.ics [pre2] <?xml version="1.0" encoding="utf-8"?> <d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/" xmlns:card="urn:ietf:params:xml:ns:carddav"> <d:response> <d:href>/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/seukIp8ajv0rOXXx8IG4Q9j.ics</d:href> <d:propstat> <d:prop> <d:getlastmodified>Mon, 15 Mar 2021 12:56:41 GMT</d:getlastmodified> <d:getcontentlength>546</d:getcontentlength> <d:resourcetype/> <d:getetag>"fa79fe0b28dea03c0db457af39980eae"</d:getetag> <d:getcontenttype>text/calendar; charset=utf-8</d:getcontenttype> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> </d:response> </d:multistatus> [/pre2] И что дальше?

Петр: Dr. Oldwarez пишет: И что дальше? Все. Спасибо. Дальше сами, согласно техзадания шефа.

SergKis: Dr. Oldwarez пишет И что дальше? Теперь, как минимум, знаете устройство календаря (по запросам), т.е. 1. Список календарей (пост 259) 2. Оглавление календаря (список событий, предыдущий ваш пост 267). Идентификатор события <d:getetag>"fa79fe0b28dea03c0db457af39980eae"</d:getetag> 3. Содержимое календаря (GET запрос), правда вы дали его в усеченном виде, т.к. нет идентификатора события События оглавления содержат дату <d:getlastmodified>Mon, 15 Mar 2021 12:56:41 GMT</d:getlastmodified> и Содержимое события содержит дату LAST-MODIFIED:20210315T125641Z Т.е. получая Оглавление календаря, а не все содержимое, можно знать менялось событие в календаре на сервере, сравнивая со значением в dbf и брать с сервера только измененные данные (содержимое).

Dr. Oldwarez: SergKis пишет: Содержимое события содержит дату LAST-MODIFIED:20210315T125641Z Т.е. получая Оглавление календаря, а не все содержимое, можно знать менялось событие в календаре на сервере, сравнивая со значением в dbf и брать с сервера только измененные данные (содержимое). О, точно, как же я сразу до этого не додумался? Надо всё фильтровать по дате последнего обновления, которая также должна храниться в INI Тогда фильтруются только те ICSы, которые не были актуализированы в прошлый раз. И ещё маленький вопрос напоследок. Как с помощью CURL GET сделать так, чтобы НЕСКОЛЬКО событий вывелось в один файл А то у меня так (Strings - список названий ics, TEMPDIR - директория временных файлов, создаваемых в процессе подключения к календарю): [pre2] ... FOR k:=1 TO LEN(aStrings) cCURL2:='curl -o '+cTempDIR+IIF(RIGHT(cTempLW,1)="\",'','\')+'temp_'+STRZERO(k,4)+'.txt --user '+cCALLOgin+':'+cCALPWD+' -i -X GET '+cCalName+; IIF(RIGHT(cCalName,1)='/','','/')+aStrings[k] RUN(cCurl2) NEXT k ... [/pre2] То есть, каждый ICS на одно событие генерит свой собственный файл. Нельзя ли так, чтобы несколько событий отправлялись в один файл? (Настроить вывод на дозапись вместо открытия нового файла)

SergKis: Dr. Oldwarez пишет Нельзя ли так, чтобы несколько событий отправлялись в один файл? Зачем ? Есть a := Directory(<маска>) и собирайте, если надо или проще по одному элементу отрабатывать массив a

SergKis: PS А может так ?[pre2] FOR k:=1 TO LEN(aStrings) cOut := cTempDIR+IIF(RIGHT(cTempLW,1)="\",'','\')+'temp_'+STRZERO(k,4)+'.txt' fErase(cOut) cCURL2:='curl -o '+cOut+' --user '+cCALLOgin+':'+cCALPWD+' -i -X GET '+cCalName+; IIF(RIGHT(cCalName,1)='/','','/')+aStrings[k] RUN(cCurl2) IF file(cOut) // обработка ELSE ? k, "Error", cOut ENDIF NEXT k [/pre2]

Dr. Oldwarez: SergKis пишет: ICS = ARRAY[6] 1 {"/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/w7klsLDepGCBYAA7Gmr4GIe.ics", "Sat, 13 Mar 2021 16:58:11 GMT", "ics 1"} 2 {"/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/8gpZuoZBJQDnLlr-66brhHG.ics", "Thu, 04 Mar 2021 16:51:14 GMT", "ics 2"} 3 {"/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/VM3wwLrVEm3Zwn-UkKAzXra.ics", "Thu, 04 Mar 2021 16:51:33 GMT", "ics 3"} 4 {"/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/e5aab12a-1a2a-4309-96d4-cccb1b67d9ba.ics", "Mon, 08 Mar 2021 06:59:00 GMT", "ics 4"} 5 {"/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/seukIp8ajv0rOXXx8IG4Q9j.ics", "Mon, 15 Mar 2021 12:56:41 GMT", "ics 5"} 6 {"/rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7/83sEK6nFBn4-TQ1XcZoPvWz.ics", "Mon, 15 Mar 2021 12:52:23 GMT", "ics 6"} Отлично! У меня получилось. Теперь вот вопрос по формату времени. Мой шеф проживает в зоне CET (Гринвич+1). Я - в зоне EET. И хотелось бы привести это всё к одному формату, принятому в ICS: YYYYMMDDTHHMMSSZ И тогда уже можно сделать выбор по дате - только тех, кто позже даты последней автосинхронизации, заносимой в регистр автоматически при нажатии на кнопку "Синхронизация"

SergKis: Dr. Oldwarez пишет И хотелось бы привести это всё к одному формату, принятому в ICS: Для этого, вроде, все есть[pre2] c := DtoS(Date()) m := {} FOR i := 1 TO 12 ; AAdd(m, left(cMonth(StoD( left(c,4)+strzero(i,2)+"01" )), 3)) NEXT a := hb_ATokens("Mon, 15 Mar 2021 12:56:41 GMT"," ") cDay := a[2] cMes := a]3] cGod := a[4] cTim := a[5] cMes := StrZero(AScan(m, cMes), 2) cTim := StrTran(cTim, ":", "") cDat := cGod+cMes+cDay+cTim [/pre2]

Петр: Dr. Oldwarez пишет: Отлично! У меня получилось. В цикле выполнить вызов curl? Поздравляю.

Dr. Oldwarez: Петр пишет: В цикле выполнить вызов curl? Поздравляю. Этак вот [pre2] PROCEDURE SyncDlg() LOCAL cXML:=cTempDIR+IIF(RIGHT(cTempLW,1)="\",'','\')+'listall_ics.xml' LOCAL cRun:='curl -k -o '+cXML+' --user '+cCALLOgin+':'+cCALPWD+' -X PROPFIND "'+cCALName+'"' LOCAL cBuf:="",aBuf:={},nI:=1,aStrings:={},aICS:={},j:=1,cCURL2:="",aHeaders:={"CALENDAR","CHANGE DATE","NUMBER"},aLen:={700,150,80} LOCAL nN:=1,cK:="",cI:="" LOCAL aDimLen := {280,80,50,80,50,200,350} LOCAL aDimHeaders:={"Subject","Start Date", "Start Time","End Date","End Time","Location","Description"},aCalStr:={},aDim:={} LOCAL cLocation:="",cDescription:="",cSubject:="",dStartDate:=CTOD(""),cStartTime:="00:00",dEndDate:=CTOD(""),cEndTime:="00:00",; cStartDateTime:="",cEndDateTime:="" RUN (cRun) cBuf := hb_memoread(cXml) IF CRLF $ cBuf cBuf := StrTran(cBuf, CRLF, "") ENDIF aBuf := hb_ATokens(cBuf, "<d:response><d:href>") nN := 0 FOR nI := 1 TO Len(aBuf) cI := aBuf[nI] nK := AT("</", cI) cK := trim(left(cI, nK-1)) IF right(cK, 4) == ".ics" nN += 1 AAdd(aIcs, { cK, "", "ics "+hb_ntos(nN) }) ENDIF NEXT aBuf := hb_ATokens(cBuf, "<d:propstat><d:prop><d:getlastmodified>") nN := 0 FOR nI := 1 TO Len(aBuf) cI := aBuf[nI] cK := "" IF ! "<?xml " $ cI nN += 1 nK := AT("</", cI) cK := left(cI, nK-1) aIcs[nN][2] := cK ENDIF NEXT FOR i:=1 TO LEN(aICS) //IF aICS[ i ][2]> AADD(aStrings,SUBSTR(aICS[ i ][1],RAT("/",aICS[ i ][1])+1)) //ENDIF NEXT i FOR k:=1 TO LEN(aStrings) cOut := cTempLW+IIF(RIGHT(cTempLW,1)="\",'','\')+'temp_'+STRZERO(k,4)+'.txt' fErase(cOut) cCURL2:='curl -o '+cOut+' --user '+cCALLOgin+':'+cCALPWD+' -i -X GET '+cCalName+; IIF(RIGHT(cCalName,1)='/','','/')+aStrings[k] RUN(cCurl2) IF FILE(cOut) aCalStr:=hb_ATokens(MEMOREAD(cOut),CRLF) FOR nBuf:=1 TO LEN(aCalStr) IF aCalStr[nBuf]="DTSTART" cDateTime:=SUBSTR(aCalStr[nBuf],AT(":",aCalStr[nBuf])+1) dStartDate:=STOD(LEFT(cDateTime,8)) cStartTime:=SUBSTR(cDateTime,10,2)+":"+SUBSTR(cDateTime,12,2) ENDIF IF aCalStr[nBuf]="DTEND" cEndDateTime:=SUBSTR(aCalStr[nBuf],AT(":",aCalStr[nBuf])+1) dEndDate:=STOD(LEFT(cEndDateTime,8)) cEndTime:=SUBSTR(cEndDateTime,10,2)+":"+SUBSTR(cEndDateTime,12,2) ENDIF IF aCalStr[nBuf]="SUMMARY" cSubject:=SUBSTR(aCalStr[nBuf],AT(":",aCalStr[nBuf])+1) ENDIF IF aCalStr[nBuf]="LOCATION" cLocation:=SUBSTR(aCalStr[nBuf],AT(":",aCalStr[nBuf])+1) ENDIF IF aCalStr[nBuf]="DESCRIPTION" cDescription:=SUBSTR(aCalStr[nBuf],AT(":",aCalStr[nBuf])+1) ENDIF NEXT nBuf AADD(aDim,{cSubject,dStartDate,cStartTime,dEndDate,cEndTime,cLocation,cDescription}) // Obrabotka ENDIF NEXT k DEFINE WINDOW wndSync; AT 50,0; WIDTH 1020 HEIGHT 670; CHILD; TITLE "Calendar synchronization" IF LEN(aDim)>0 DEFINE TBROWSE brwImport; AT 10,10; WIDTH 950 HEIGHT 350; GRID brwImport:=SetArrayTo("brwImport","wndSync",aDim) END TBROWSE ENDIF END WINDOW ACTIVATE WINDOW wndSync [/pre2]

Dr. Oldwarez: Петр пишет: К тому же инсталлятор весом меньше 1 Га в наше время моветон Вот только моему шефу этого не говорите. Для него это как красная тряпка для быка

SergKis: Dr. Oldwarez Поправьте в посте 270 [ i ] без пробелов (это служебный тэг), на [ i ] с пробелами, искажение текста не будет

SergKis: Dr. Oldwarez пишет Вот только моему шефу этого не говорите. Для него это как красная тряпка для быка В Windows 10 build 17063 и более поздних версиях теперь включен Curl, так что вы можете запускать его непосредственно из Cmd.exe или PowerShell.exe.

Dima: SergKis пишет: Поправьте в посте 270 [ i ] без пробелов (это служебный тэг), на [ i ] с пробелами, искажение текста не будет я поправил

Dr. Oldwarez: SergKis пишет:  цитата: В Windows 10 build 17063 и более поздних версиях теперь включен Curl, так что вы можете запускать его непосредственно из Cmd.exe или PowerShell.exe. И это радует. А то придётся уже через libcurl продираться.

Петр: Dr. Oldwarez пишет: Этак вот Спасибо, прямо вот так взять процедуру и использовать не получится, но все же. Позвольте дать несколько советов. 1) Вот так будет лучше PROCEDURE SyncDlg(cTempDIR,cTempLW,cCALLOgin,cCALPWD,cCALName) Если вы захотите использовать функцию еще где-нибудь, сделать это будет проще. 2) Используйте опции компилятора -w3 -es2 Это позволит вам избежать ошибок и код, возможно, работать будет быстрее. Например мы получили Variable 'NN' is assigned but not used in function 'SYNCDLG(9)' Действительно, LOCAL nN:=1 и дальше nN := 0 Значение nN, присвоенное при инициализации переменной не используется. Вы можете в MinGUI добавить опции в батфайл для сборки compile.bat myprogram /s -w3 /s -es2 %1 %2 %3 %4 %5 %6 %7 %8 %9 или прямо указать в коде #pragma -w3 #pragma -es2

Петр: 3) По возможности используйте стандартные функции Harbour. Это поможет избежать ошибок, свойственных пользовательскому коду, а также увеличит переносимость программ (кода) на другие платформы. Ну и часто код выглядит лучше LOCAL cXML:=cTempDIR+IIF(RIGHT(cTempLW,1)="\",'','\')+'listall_ics.xml' == LOCAL cXML:=cTempDIR+hb_DirSepAdd(cTempLW)+'listall_ics.xml'

Dr. Oldwarez: SergKis пишет: Для этого, вроде, все есть c := DtoS(Date()) m := {} FOR i := 1 TO 12 ; AAdd(m, left(cMonth(StoD( left(c,4)+strzero(i,2)+"01" )), 3)) NEXT a := hb_ATokens("Mon, 15 Mar 2021 12:56:41 GMT"," ") cDay := a[2] cMes := a]3] cGod := a[4] cTim := a[5] cMes := StrZero(AScan(m, cMes), 2) cTim := StrTran(cTim, ":", "") cDat := cGod+cMes+cDay+cTim Вот что-то у меня совсем не то. aICS[ i ][2] - это и есть полный формат даты с днями недели и месяцами. [pre2] FOR i:=1 TO LEN(aICS) IF DTIMETOS(aICS[ i ][2],1)>DTOS(dSyncDate)+'T'+STRTRAN(cSyncTime,":","")+'00Z' AADD(aStrings,SUBSTR(aICS[ i ][1],RAT("/",aICS[ i ][1])+1)) ENDIF NEXT i [/pre2] И функция конвертации [pre2]FUNCTION DTIMETOS(cFull,nUTC) LOCAL c := DtoS(Date()),m := {},a:=ARRAY(7),cDat:='',cDay:='',cMonth:='',cYear:='',cTime:='' FOR i := 1 TO 12 AAdd(m, left(cMonth(StoD( left(c,4)+strzero(i,2)+"01" )), 3)) NEXT a := hb_ATokens(cFull," ") IF LEN(a)>=5 cDay := a[2] cMonth := a[3] cYear := a[4] cTime := a[5] cMonth := StrZero(AScan(m, cMonth), 2) cTime := StrTran(cTime, ":", "") ENDIF cDat := cYear+cMonth+cDay+'T'+cTime+'00Z' RETURN CDat [/pre2] При попытке обратиться к ней из IF выдаёт дикие ошибки обращения к массиву. Хотя значения в поле BROWSE выводит нормально. Это почему так?

Dr. Oldwarez: Петр пишет: 3) По возможности используйте стандартные функции Harbour. Это поможет избежать ошибок, свойственных пользовательскому коду, а также увеличит переносимость программ (кода) на другие платформы. Ну и часто код выглядит лучше LOCAL cXML:=cTempDIR+IIF(RIGHT(cTempLW,1)="\",'','\')+'listall_ics.xml' == LOCAL cXML:=cTempDIR+hb_DirSepAdd(cTempLW)+'listall_ics.xml' Да, намного удобнее использовать готовые функции, а не изобретать велосипед. Но где к ним найти мануал?

Петр: Dr. Oldwarez пишет: Да, намного удобнее использовать готовые функции, а не изобретать велосипед. Но где к ним найти мануал? Например здесь

Петр: Dr. Oldwarez пишет: При попытке обратиться к ней из IF выдаёт дикие ошибки обращения к массиву. Хотя значения в поле BROWSE выводит нормально. Это почему так? А что у вас в программе с настройками set language to ?? или hb_langSelect(??) А заодно и set date to проверьте

SergKis: Dr. Oldwarez пишет При попытке обратиться к ней из IF выдаёт дикие ошибки обращения к массиву. Хотя значения в поле BROWSE выводит нормально. Это почему так? [pre2] FUNCTION DTIMETOS(cFull,nUTC) LOCAL c := DtoS(Date()),m := {},a:=ARRAY(7),cDat:='',cDay:='',cMonth:='',cYear:='',cTime:='' LOCAL i FOR i := 1 TO 12 i перекрылось из процедуры выше с FOR i:=1 TO LEN(aICS) [/pre2]

rvu: SergKis пишет: В Windows 10 build 17063 и более поздних версиях теперь включен Curl Я раньше использовал внешний Curl, потом в Минигуи перешел на внутренний, пока что проблем нет. Есть ли у внешнего преимущества?

Петр: rvu пишет: Я раньше использовал внешний Curl, потом в Минигуи перешел на внутренний, пока что проблем нет. Есть ли у внешнего преимущества? Внутренний - это libcurl?

Петр: Короче, curl написан с использованием функций libcurl Парсер командной строки + функциональность libcurl Понятно, что curl могут использовать не только программисты. curl удобно использовать на этапе отладки. С curl меньше шансов словить ошибку, не предусмотрев обработку результата выполнения библиотечных функций или неправильно заполнив какую-то структуру. Вот и все.

Петр: SergKis пишет: FUNCTION DTIMETOS(cFull,nUTC) LOCAL c := DtoS(Date()),m := {},a:=ARRAY(7),cDat:='',cDay:='',cMonth:='',cYear:='',cTime:='' LOCAL i FOR i := 1 TO 12 i перекрылось из процедуры выше с FOR i:=1 TO LEN(aICS) Кстати, вот вам наглядный пример почему нужно использовать -w3 -es2

Петр: Dr. Oldwarez пишет: IF DTIMETOS(aICS[ i ][2],1)>DTOS(dSyncDate)+'T'+STRTRAN(cSyncTime,":","")+'00Z' Мне кажется - это не правильный подход. В добавок к используемому еще со времен Clipper скалярному типу данных date, в Harbour добавлена поддержка типа timestamp (datetime). tValue := t"2013-11-06;15:14:45" tValue := hb_DateTime() Вы можете создать поля баз данных этого типа, есть куча встроенных функций, можна применять разные операции, в т.ч. сравнения. Поэтому правильнее будет написать функции конвертации ICS DateTime в Harbour TIMESTAMP (DateTime) и наоборот.

Петр: Один из возможных вариантов реализации [pre2]// "Mon, 15 Mar 2021 12:56:41 GMT" FUNCTION DTimeToTS(cFull, cLang) LOCAL m := GET_MONTHS(cLang) LOCAL cMonth LOCAL aDateTime aDateTime := hb_ATokens(hb_defaultValue(cFull, "")," ") IF LEN(aDateTime) >= 5 cMonth := StrZero(AScan(m, aDateTime[3]), 2) RETURN hb_StrToTS(hb_strFormat("%s-%s-%s %s", aDateTime[4], cMonth, aDateTime[2], aDateTime[5])) ENDIF RETURN hb_StrToTS("") STATIC FUNCTION GET_MONTHS(cL) LOCAL cLang LOCAL i, n, aMonths[12], dt := Date() dt -= Day(dt) - 1 cLang := hb_langSelect(cL) FOR i := 1 TO 12 n := Month( dt ) aMonths[ n ] := Left(CMonth(dt), 3) dt += 31 dt -= Day(dt) - 1 NEXT hb_langSelect(cLang) RETURN aMonths[/pre2]

Петр: Проверочный код (консоль) [pre2] //C:\MiniGUI\SAMPLES\MySamples\DateTime\demo.prg REQUEST HB_LANG_RU REQUEST HB_CODEPAGE_RU1251 REQUEST HB_CODEPAGE_RU866 FUNCTION main() hb_cdpSelect( "RU1251" ) hb_langSelect("RU") hb_setTermCP( "RU866", "RU1251" ) ?? CMonth(hb_DateTime()) ? ? hb_DateTime() ? DTimeToTS("Mon, 15 Mar 2021 12:56:41 GMT", "en") ? hb_DateTime() > DTimeToTS("Mon, 15 Mar 2021 12:56:41 GMT", "en") RETURN 0 [/pre2] Если вы используете harbour из поставки MiniGUI ваш compile.bat должен выглядеть так call ..\..\..\batch\compile.bat demo /c %1 %2 %3 %4 %5 %6 %7 %8 %9 PS. почему я спрашивал про set language to .. догадаетесь сами.

Петр: 4) Вызов внешних программ RUN (cRun) Вызов внешних программ без проверки возвращаемых ими кодов ошибок является неиссякаемым источником ошибок. Тем более если такие программы работают с сетью. Но команда RUN - это обертка встроенной __Run(), а эта функция совместима с Clipper и результатов не возвращает. Значит нужно использовать hb_Run() #define CURLE_OK 0 nResult := hb_Run(cRun) IF nResult == CURLE_OK // Все хорошо, начинаем обработку данных ELSE // Обработчик ошибок END Список кодов ошибок, которые может вернуть curl P.S. Для своего проекта можно позаимствовать hbcurl.ch из contrib\hbcurl даже не используя hbcurl

Петр: 5) Использование hb_StrFormat [pre2] #define _DIR_BIN_ (hb_DirBase() + "utils") .. LOCAL cArgs LOCAL cCurlOpt := "--no-progress-meter -k" LOCAL cLogin := "illya@kleeblatt.com" LOCAL cPwd := "**********" LOCAL cCalDav := "https://webmail.kleeblatt.com" LOCAL cHRef := "rpc.php/calendars/illya@kleeblatt.com/calendar~GhGVum1xWexaffEfhiVkNN7" ... // Формируем строку вызова cArgs := hb_StrFormat(; '%s\curl %s -u%s:%s -X PROPFIND "%s%s"', _DIR_BIN_, cCurlOpt, cLogin, cPwd, cCalDav, cHRef) ? cArgs [/pre2] Крайне удобно и позволяет переопределять строку аргументов on the fly.

SergKis: Петр пишет Один из возможных вариантов реализации TimeStamp возможный вариант , но он содержит миллисекунды за Timestamp.123, т.е. нельзя делать hhb_DateTime() == DTimeToTS("Mon, 15 Mar 2021 12:56:41 GMT", "en") На мой взгляд, с ics больше подходит строковый формат, только без всяких T и 00Z внутри, если иметь ввиду индексы с участием строковых дат и событий календаря, то ключ получается проще в работе, если учесть хранение в dbf данных в символьном виде (физически), то нет лишних телодвижений rdd по преобразованию данных. Вид даты "2021031512541" больше подходит в данном случае, а TimeStamp в типах =@T или производных от них. Но это мое понимание ситуации.

Петр: SergKis пишет: нельзя делать hhb_DateTime() == DTimeToTS("Mon, 15 Mar 2021 12:56:41 GMT", "en") Это еще почему? ts := hb_StrToTS("2021-03-15 12:56:41") ? ts == DTimeToTS("Mon, 15 Mar 2021 12:56:41 GMT", "en") TimeStamp в harbour поддерживает ISO 8601 и совместим с большинством реализаций. Операции с TimeStamp быстрее строковых операций. Но вольному воля..

SergKis: Петр пишет ? hb_datetime() результат 2021-03-27 01:04:47.133 Операции с TimeStamp быстрее строковых операций Разговор о решениях с isc. Составьте выражения ключей индекса и ключей для scope - выбрать выполненное за год\месяц\квартал\неделя\день ... - выбрать не выполненное за год\месяц\квартал\неделя\день ... ... строковые решения будут проще. А так конечно, решать не нам, что и как делать

Dr. Oldwarez: SergKis пишет: FUNCTION DTIMETOS(cFull,nUTC) LOCAL c := DtoS(Date()),m := {},a:=ARRAY(7),cDat:='',cDay:='',cMonth:='',cYear:='',cTime:='' LOCAL i FOR i := 1 TO 12 i перекрылось из процедуры выше с FOR i:=1 TO LEN(aICS) Вот спасибо!!! Заработало! Теперь надо встречное движение делать - из DBF в календарь. Это означает, генерить пакет ics-ов и забрасывать их в календарь уже хорошо знакомым CURLом. Выбирать все, чья автозаносимая дата создания позже даты последней синхронизации. Можно их в один ics запихнуть, или опять каждому событию - по своему ICSу, и потом уже циклом закидывать их в календарь.

Dr. Oldwarez: Петр пишет: TimeStamp в harbour поддерживает ISO 8601 и совместим с большинством реализаций. Операции с TimeStamp быстрее строковых операций. Но вольному воля.. TimeStamp - это хорошо, но в формате DBF, созданном в мохнатом 1983 году нет поддержки таких полей. Тогда было только отдельное поле типа даты и строковое короткое поле для времени. Можно создать переменную типа DateTime, а в базу данных её как сохранить? Это тебе не Oracle (ещё одна "любимая" программа моего шефа), где можно пользовательские типы лепить.

Петр: SergKis пишет: строковые решения будут проще Возможно для кого-то и проще. Но и сложного там ничего нет [pre2] REQUEST HB_LANG_RU REQUEST HB_CODEPAGE_RU1251 REQUEST HB_CODEPAGE_RU866 REQUEST DBFCDX PROCEDURE Main() LOCAL tBottomDate, tTopDate USE events NEW INDEX ON hb_TSToStr(_FIELD->DUE) TO Due tTopDate := hb_DateTime(2021, 5, 1) tBottomDate := hb_DateTime(2021, 6, 15) SET SCOPETOP TO hb_TSToStr(tTopDate) SET SCOPEBOTTOM TO hb_TSToStr(tBottomDate) GO TOP DO WHILE .NOT. EOF() ? FIELD->UID, FIELD->DUE SKIP +1 ENDDO RETURN INIT PROCEDURE OnInit() LOCAL aStru LOCAL cFileDbf := hb_DirBase() + "events.dbf" LOCAL nI hb_cdpSelect( "RU1251" ) hb_langSelect("RU") hb_setTermCP( "RU866", "RU1251" ) rddSetDefault("DBFCDX") SET DATE FORMAT TO "DD.MM.YYYY" IF .NOT. DbExists(cFileDbf) aStru := {} AADD( aStru, {"UID", "C", 36, 0} ) AADD( aStru, {"DUE", "@", 8, 0} ) DbCreate(cFileDbf, aStru, "DBFCDX", .t., "EVENTS", , "RU1251" ) FOR nI := 1 TO 1000 dbAppend() FIELD->UID := "5981ddad-f421-47ea-bd2d-f442865b" + STRZERO(nI, 4) FIELD->DUE := hb_StrToTS(hb_strFormat("%04d-%02d-%02d", YEAR(DATE()), hb_RandomInt(1,12), hb_RandomInt(1,28))) NEXT nI dbCloseAll() ENDIF RETURN [/pre2]

SergKis: Петр пишет Но и сложного там ничего нет Что то не срослось ваш demo.prg чуть модифицированный под hmg сборку[pre2] /* * MINIGUI - Harbour Win32 GUI library Demo */ #define _HMG_OUTLOG #include "hmg.ch" REQUEST HB_LANG_RU REQUEST HB_CODEPAGE_RU1251 REQUEST HB_CODEPAGE_RU866 REQUEST DBFCDX PROCEDURE Main() LOCAL tBottomDate, tTopDate hb_cdpSelect( "RU1251" ) hb_langSelect("RU") hb_setTermCP( "RU866", "RU1251" ) Sets() USE events NEW INDEX ON hb_TSToStr(_FIELD->DUE) TO Due tTopDate := hb_DateTime(2021, 5, 1) tBottomDate := hb_DateTime(2021, 6, 15) SET SCOPETOP TO hb_TSToStr(tTopDate) SET SCOPEBOTTOM TO hb_TSToStr(tBottomDate) ? "Scope =",tTopDate, tBottomDate, OrdKeyCount() GO TOP DO WHILE .NOT. EOF() ? RecNo(), FIELD->UID, FIELD->DUE SKIP 1 ENDDO RETURN FUNC Sets() LOCAL aStru LOCAL cFileDbf := hb_DirBase() + "events.dbf" LOCAL nI rddSetDefault("DBFCDX") SET DATE FORMAT TO "DD.MM.YYYY" IF .NOT. DbExists(cFileDbf) aStru := {} AADD( aStru, {"UID", "C", 36, 0} ) AADD( aStru, {"DUE", "@", 8, 0} ) AADD( aStru, {"DTM", "=", 8, 0} ) DbCreate(cFileDbf, aStru, "DBFCDX", .t., "EVENTS", , "RU1251" ) FOR nI := 1 TO 100 dbAppend() FIELD->UID := "5981ddad-f421-47ea-bd2d-f442865b" + STRZERO(nI, 4) FIELD->DUE := hb_StrToTS(hb_strFormat("%04d-%02d-%02d", YEAR(DATE()), hb_RandomInt(1,12), hb_RandomInt(1,28))) NEXT nI GO TOP ? cFileDbf, "LastRec =", LastRec() DO WHILE !EOF() ? RecNo(), FIELD->UID, FIELD->DUE, FIELD->DTM SKIP ENDDO ? dbCloseAll() ENDIF RETURN Nil [/pre2] Результат работы в _MsgLog.txt [pre2] C:\MiniGuiBcc58\SAMPLES\_Test\_hb_datetime\events.dbf LastRec = 100 1 '' 00:00 2021-03-27 16:17:16.458 2 '' 00:00 2021-03-27 16:17:16.458 3 '' 00:00 2021-03-27 16:17:16.458 4 '' 00:00 2021-03-27 16:17:16.458 5 '' 00:00 2021-03-27 16:17:16.458 6 '' 00:00 2021-03-27 16:17:16.458 7 '' 00:00 2021-03-27 16:17:16.458 8 '' 00:00 2021-03-27 16:17:16.458 9 '' 00:00 2021-03-27 16:17:16.458 10 '' 00:00 2021-03-27 16:17:16.458 11 '' 00:00 2021-03-27 16:17:16.458 12 '' 00:00 2021-03-27 16:17:16.458 13 '' 00:00 2021-03-27 16:17:16.458 14 '' 00:00 2021-03-27 16:17:16.458 15 '' 00:00 2021-03-27 16:17:16.458 16 '' 00:00 2021-03-27 16:17:16.458 17 '' 00:00 2021-03-27 16:17:16.458 18 '' 00:00 2021-03-27 16:17:16.458 19 '' 00:00 2021-03-27 16:17:16.458 20 '' 00:00 2021-03-27 16:17:16.458 21 '' 00:00 2021-03-27 16:17:16.458 ... 90 '' 00:00 2021-03-27 16:17:16.458 91 '' 00:00 2021-03-27 16:17:16.458 92 '' 00:00 2021-03-27 16:17:16.458 93 '' 00:00 2021-03-27 16:17:16.458 94 '' 00:00 2021-03-27 16:17:16.458 95 '' 00:00 2021-03-27 16:17:16.458 96 '' 00:00 2021-03-27 16:17:16.458 97 '' 00:00 2021-03-27 16:17:16.458 98 '' 00:00 2021-03-27 16:17:16.458 99 '' 00:00 2021-03-27 16:17:16.458 100 '' 00:00 2021-03-27 16:17:16.459 Scope = 2021-05-01 2021-06-15 0 [/pre2]

SergKis: Dr. Oldwarez пишет Можно создать переменную типа DateTime, а в базу данных её как сохранить? Почитайте http://clipper.borda.ru/?1-4-20-00000851-000-0-0-1598558733

Петр: SergKis пишет: Что то не срослось ваш demo.prg Мне не удалось повторить в консольном приложении ваш удивительный результат. И не удалось откомпилировать "модифицированный под hmg сборку"

SergKis: Петр пишет И не удалось откомпилировать "модифицированный под hmg сборку" demo.hbp[pre2] # Keys compile #-prgflag=-w2 -es1 # Enable multi/single-thread Harbour VM -mt # Incremental-compilation mode -inc # folder where are all * .obj -workdir=OBJ # Name EXE-module -odemo # to list all * .prg demo.prg # project Resources #demo.rc # paths to the main and extension *.Lib -lminigui [/pre2] Compile.bat[pre2] ..\..\..\BATCH\hbmk2.bat demo.hbp [/pre2]

SergKis: PS C:\MiniGuiBcc58\BATCH\minigui.cfg[pre2] # Basic configuration for Compile.bat MG_CMP=harbour MG_BCC=c:\borland\bcc58 MG_ROOT=c:\miniguiBcc58 [/pre2]

Петр: Петр пишет: И не удалось откомпилировать "модифицированный под hmg сборку" call ..\..\..\batch\compile.bat demo /c %1 %2 %3 %4 %5 %6 %7 %8 %9 Забыл /c удалить. Удалось Прикольно.. Закомментировал //#include "hmg.ch" Добавил /c И результат ожидаемый. Я честно не знаю, что там в minigui сломалось. В топку?!

Петр: Замените FIELD по тексту на _FIELD Из топки или подождать?

SergKis: Петр пишет Я честно не знаю, что там в minigui сломалось. В топку?! К сожалению, в топке и уже давно консольные и wvt приложения Вот я и говорю, что со строковыми проще USE ... INDEX ON _FIELD->UID+_FILED->DATASTART TAG START INDEX ON _FIELD->UID+_FILED->DATAEND TAG END INDEX ON _FIELD->UIDNO TAG ID Запрос в GETBOX-ах cUID := ... dDT1 := ... dDT2 := ... SET SCOPE TO cUID+DtoS(dDT1), cUID+DtoS(dDt2) GO TOP

SergKis: Петр пишет Замените FIELD по тексту на _FIELD Хотел из совсем убрать, причем сразу, но не сделал ... и да, сработало, но осталось не заполнено время в DUE ...

SergKis: PS Показ в колонках решается через oCol:cPicture := "@R 9999-99-99 99:99:99"

Петр: Петр пишет: Замените FIELD по тексту на _FIELD Да, некрасиво получается FIELD->UID := "5981ddad-f421-47ea-bd2d-f442865b" + STRZERO(nI, 4) => _HMG_SYSDATA[404] := '->UID := "5981ddad-f421-47ea-bd2d-f442865b" + STRZERO(nI, 4)'

Петр: Что-то мы отвлеклись от темы. 6. Значит что у нас осталось: FOR vs. FOR EACH, никогда не используйте File() для проверки доступности файла - короче мелочи. 7. Давайте вернемся к hb_Run().. 8. Научиться работать с xml пошло бы на пользу общему делу. 9. Не скажу. 10. Переход на libcurl - нужен или нет.

Dr. Oldwarez: Петр пишет: Переход на libcurl - нужен или нет. Нужен, но потом. Сейчас вот вопрос какой: [pre2]curl -k --user USERNAME:PASSWORD -X PUT -H "Content-Type: text/calendar; charset=utf-8" -d "$a" "https://owncloud.jujens.eu/remote.php/caldav/calendars/jujens/omis/<mon-fichier.ics>"[/pre2] Обязательно надо, чтобы каждому событию создавали свой ics? Или можно все события в один ICS загнать?

Петр: Dr. Oldwarez пишет: Или можно все события в один ICS загнать Если все события принадлежат одному календарю - можно и даже нужно. P.S. Оказывается не все сервера поддерживают такую ситуацию.

SergKis: Dr. Oldwarez пишетИли можно все события в один ICS загнать? Вы можете все календари со всеми событиями загнать в одну базу и меть на поле календарь уникальный TAG, сделав SET ORDER на этот TAG, получите список календарей в тсб, выбрав, переключаете на др. TAG (<календарь>+<события>) и ставите scope на календарь, получите список событий календаря и т.д. Не ясно, что за календари с каким объемом данных на какой период отчетности, бесконечен во времени или сворачивается в архив. На эти вопросы себе ответьте в первую очередь

Dr. Oldwarez: SergKis пишет: Вы можете все календари со всеми событиями загнать в одну базу и меть на поле календарь уникальный TAG, сделав SET ORDER на этот TAG, получите список календарей в тсб, выбрав, переключаете на др. TAG (<календарь>+<события>) и ставите scope на календарь, получите список событий календаря и т.д. Не ясно, что за календари с каким объемом данных на какой период отчетности, бесконечен во времени или сворачивается в архив. На эти вопросы себе ответьте в первую очередь Очень простые календари вида BEGIN:VEVENT UID:20210401T073000Z-QWERTY-04357@abcdefg.com DTSTART:20210401T073000Z DTEND:20210401T161500Z SUMMARY:Visit to doctor DESCRIPTION:Vaccination against COVID-19, plague, cholera and Ebola LOCATION:Central hospital END:VEVENT

SergKis: Dr. Oldwarez пишет Очень простые календари вида Это событие календаря, 1 запись в базе (вроде, был еще параметр выполнение события дата-время, или ошибаюсь) календарей у вас 6 штук. Это личные календари событий или предприятия\организации. Объем календаря (в событиях-записях). И чего вы от них хотите ? Просто показ в табл. виде отчетов ? Цель ?

SergKis: PS Технические моменты вы прошли, попробовали, на вас вывалили инф. Как бы дальше сами, создайте dbf[ы] и пробуйте, щупайте.

Dr. Oldwarez: SergKis пишет: Это событие календаря, 1 запись в базе (вроде, был еще параметр выполнение события дата-время, или ошибаюсь) календарей у вас 6 штук. Это личные календари событий или предприятия\организации. Объем календаря (в событиях-записях). И чего вы от них хотите ? Просто показ в табл. виде отчетов ? Цель ? Да, просто занесения в DBF в целях синхронизации. 99% времени это работает в локальном режиме. А также обратно занесение полей из DBF в календарь. С последним некоторые проблемы. [pre2]curl -k --user USERNAME:PASSWORD -X PUT -H "Content-Type: text/calendar; charset=utf-8" -d "$a" "https://owncloud.jujens.eu/remote.php/caldav/calendars/jujens/omis/<mon-fichier.ics>"[/pre2] То есть тут <mon-fichier.ics> - это не локальный сгенерированный файл, а элемент календаря в сети. Как в него занести данные из локальных полей, даже если они сгенерированы в ЛОКАЛЬНЫЙ ics файл? типа C:\MYCALPROG\TEMP\EXPORT.ICS Как его в календарь с помощью CURL забросить?

SergKis: PS2 Есть еще STATUS:... нашел несколько значений STATUS:CONFIRMED STATUS:CANCLED STATUS:TENTATIVE возможно надо учесть ?

Dr. Oldwarez: SergKis пишет: PS2 Есть еще STATUS:... нашел несколько значений STATUS:CONFIRMED STATUS:CANCLED STATUS:TENTATIVE возможно надо учесть ? STATUS:CONFIRMED Но и без него можно обойтись Я уже сделал считывание из календаря в массив, который потом можно запихнуть в таблицу. Теперь моя последняя задача - запихивание данных из таблицы в календарь. После чего можно и отдохнуть.

SergKis: Dr. Oldwarez пишет Теперь моя последняя задача - запихивание данных из таблицы в календарь. В поиске попалось https://kb.kopano.io/display/WIKI/Importing+ICAL+.ics+files+into+Kopano тут https://curl.se/docs/manpage.html смотрим -T, --upload-file <file> This transfers the specified local file to the remote URL. If there is no file part in the specified URL, curl will append the local file name. NOTE that you must use a trailing / on the last directory to really prove to Curl that there is no file name or curl will think that your last directory name is the remote file name to use. That will most likely cause the upload operation to fail. If this is used on an HTTP(S) server, the PUT command will be used. ...

Dr. Oldwarez: SergKis пишет: В поиске попалось https://kb.kopano.io/display/WIKI/Importing+ICAL+.ics+files+into+Kopano тут https://curl.se/docs/manpage.html смотрим Спасибо. Завтра с утра попробую это

Петр: Dr. Oldwarez пишет: curl -k --user USERNAME:PASSWORD -X PUT -H "Content-Type: text/calendar; charset=utf-8" -d "$a" "https://owncloud.jujens.eu/remote.php/caldav/calendars/jujens/omis/<mon-fichier.ics>" То есть тут <mon-fichier.ics> - это не локальный сгенерированный файл, а элемент календаря в сети. Как в него занести данные из локальных полей, даже если они сгенерированы в ЛОКАЛЬНЫЙ ics файл? типа C:\MYCALPROG\TEMP\EXPORT.ICS Как его в календарь с помощью CURL забросить? Ну там же комментарии есть Q: In your post new event example, what is $a? A: Good question. It should contain the data you want to post, ie the content of you local ics file.

Dr. Oldwarez: Dr. Oldwarez пишет: SergKis пишет:  цитата: В поиске попалось https://kb.kopano.io/display/WIKI/Importing+ICAL+.ics+files+into+Kopano тут https://curl.se/docs/manpage.html смотрим И сделал... [pre2]curl -k --user USERNAME:PASSWORD -T C:\MINIGUI\PROGDIR\TEMPDIR\List.ics https://webmail.abcdef.com/rpc.php/calendars/aaaaa@abcdef.com/calendar~GhGVum1xWexaffEfhiVkNN7/[/pre2] Спасибо. Завтра с утра попробую это И получил [pre2]<?xml version="1.0" encoding="utf-8"?> <d:error xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"> <s:exception>Sabre\DAV\Exception\BadRequest</s:exception> <s:message>Every VEVENT in this object must have identical UIDs</s:message> <s:sabredav-version>1.8.12</s:sabredav-version> </d:error>[/pre2] Что это за identical UIDs?

Петр: Dr. Oldwarez пишет: Что это за identical UIDs? Что-то типа этого BEGIN:VEVENT UID:19970901T130000Z-123402@example.com DTSTAMP:19970901T130000Z DTSTART:19970401T163000Z Если вы обновляете события, то как их сервер находит? Правильно, с помощью UIDs

Dr. Oldwarez: Петр пишет: Петр пишет: Что-то типа этого BEGIN:VEVENT UID:19970901T130000Z-123402@example.com DTSTAMP:19970901T130000Z DTSTART:19970401T163000Z Если вы обновляете события, то как их сервер находит? Правильно, с помощью UIDs Знаю. Но я не хочу обновлять, хочу писать новые события. Тогда вообще лучше эти UIDы упразднить? Или как сделать, чтобы оно не искало события, а тупо писало их поверх существующих?

Петр: Dr. Oldwarez пишет: curl -k --user USERNAME:PASSWORD C:\MINIGUI\PROGDIR\TEMPDIR\List.ics https://webmail.abcdef.com/rpc.php/calendars/aaaaa]https://webmail.abcdef.com/rpc.php/calendars/aaaaa@abcdef.com/calendar~GhGVum1xWexaffEfhiVkNN7/ И запрос какой-то подозрительный, ни -X, ни -T, ни -d, ни --config..

Петр: Dr. Oldwarez пишет: Знаю. Но я не хочу обновлять, хочу писать новые По логике, вы не должны указывать UID, тогда сервер сгенерирует новый VEVENT и UID для него. Тогда вообще лучше эти UIDы упразднить? Или как сделать, чтобы оно не искало события, а тупо писало их поверх существующих? Это выше ваших сил. К счастью у разработчиков другое мнение.

Dr. Oldwarez: Петр пишет: Dr. Oldwarez пишет:  цитата: Знаю. Но я не хочу обновлять, хочу писать новые По логике, вы не должны указывать UID, тогда сервер сгенерирует новый VEVENT и UID для него.  цитата: Тогда вообще лучше эти UIDы упразднить? Или как сделать, чтобы оно не искало события, а тупо писало их поверх существующих? Это выше ваших сил. К счастью у разработчиков другое мнение. Выкинул UIDы из ICS-файла и получил [pre2]Every VEVENT component must have an UID[/pre2]

Dr. Oldwarez: Петр пишет: И запрос какой-то подозрительный, ни -X, ни -T, ни -d, ни --config.. -Т есть [pre2]curl -k --user USERNAME:PASSWORD -T C:\MINIGUI\MYPROG\TEMP\Terminliste.ics https://webmail.kleeblatt.com/rpc.php/calendars/USERNAME/calendar~GhGVum1xWexaffEfhiVkNN7/[/pre2]

Петр: Значит начнем с начала. Dr. Oldwarez пишет: Но как реализовать подключение к облачному сервису в Harbour/Minigui? Возможно ли это вообще? Возможно, но для этого нужно знать, что мы можем (и как) использовать из функционала Harbour и к чему нужно подключаться: какой сервер, протокол, версия, реализация некоторых моментов. С первым частично плохо. Как дела со вторым у топикстартера, для меня лично непонятно. Обратите внимание на посты №№257, 282 Dr. Oldwarez пишет: <?xml version="1.0" encoding="utf-8"?> <d:error xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"> <s:exception>Sabre\DAV\Exception\BadRequest</s:exception> <s:message>Every VEVENT in this object must have identical UIDs</s:message> <s:sabredav-version>1.8.12</s:sabredav-version> </d:error> Простой поиск быстро привод к Building a CalDAV client This document is a general howto on how to build a CalDAV client. The document is language-agnostic Совет: научились работать с curl, берите документацию и составляйте запросы. Есть вопросы как то или другое реализовать в Harbour - задавайте здесь.

Dr. Oldwarez: Петр пишет: Возможно, но для этого нужно знать, что мы можем (и как) использовать из функционала Harbour и к чему нужно подключаться: какой сервер, протокол, версия, реализация некоторых моментов. С первым частично плохо. Как дела со вторым у топикстартера, для меня лично непонятно. Ага, нет документации, не посмотрел, вот "частично плохо". Петр пишет: Совет: научились работать с curl, берите документацию и составляйте запросы. Есть вопросы как то или другое реализовать в Harbour - задавайте здесь. Так я уже подключал всё к CURL, считывал оттуда данные. И уже сохранил эти данные в DBF-формате. Осталось ТОЛЬКО ОДНО - забрасывание ics в календарь. И всё, прогу уже можно сдавать. Проблема, конкретно, не в программе, а в календаре. Установить его на генерацию UIDов. Он же принимал файлы без UIDов в ручном режиме

Петр: Dr. Oldwarez пишет: Установить его на генерацию UIDов. Он же принимал файлы без UIDов в ручном режиме Без комментариев. Вам же результат нужен, а не.. Найдите online uuid generator например Сгенерируйте uuid, загрузите себе в базу и пользуйтесь.

Dr. Oldwarez: Петр пишет: Без комментариев. Вам же результат нужен, а не.. Найдите online uuid generator например Сгенерируйте uuid, загрузите себе в базу и пользуйтесь. В смысле, загрузить вот ЭТО 2f4f6320-8fcc-11eb-8dcd-0242ac130003 в регистр и присваивать ВСЕМ строкам? Или для каждой строки новый генерить, но тут нужна уже встроенная функция.

Петр: Dr. Oldwarez пишет: В смысле, загрузить вот ЭТО Это от сайта вам подарок. А ниже вы можете указать количество и нажать загрузить в файл, выбрав 1 или 4 версии генератора. Оффлайн можете подключить библиотеку hbwin и использовать win_UuidCreateString LOCAL nRPCStatus ? win_UuidCreateString( @nRPCStatus ) ? nRPCStatus

Петр: Петр пишет: А ниже вы можете указать количество и нажать загрузить в файл, выбрав 1 или 4 версии генератора. А еще вы можете пользоваться API сайта и загрузить UUID с помощью CURL curl.exe -ouuid4.txt -k -X GET -H"text/plain; charset=utf-8" https://www.uuidgenerator.net/api/version4/500 или curl.exe -ouuid1.txt -k -X GET -H"text/plain; charset=utf-8" https://www.uuidgenerator.net/api/version1 PS. "text/plain; charset=utf-8" конечно же лучше заменить на "Content-Type: text/plain; charset=utf-8"

Dr. Oldwarez: Петр пишет: Оффлайн можете подключить библиотеку hbwin и использовать win_UuidCreateString LOCAL nRPCStatus ? win_UuidCreateString( @nRPCStatus ) ? nRPCStatus Подключил и сделал. То же самое [pre2] <?xml version="1.0" encoding="utf-8"?> <d:error xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"> <s:exception>Sabre\DAV\Exception\BadRequest</s:exception> <s:message>Every VEVENT in this object must have identical UIDs</s:message> <s:sabredav-version>1.8.12</s:sabredav-version> </d:error> [/pre2] Но UIDы в файле выглядят по-другому [pre2] BEGIN:VEVENT UID:89dbeac3-50de-4138-915a-1836802d0c9a DTSTAMP:20210328T211200Z DTSTART:20210331T090000Z DTEND:20210331T100000Z SUMMARY:abcd DESCRIPTION:qwerty LOCATION:Utopia END:VEVENT BEGIN:VEVENT UID:33892830-c61d-4279-91e8-3c69e9e13dd5 DTSTAMP:20210328T211200Z DTSTART:20210401T073000Z DTEND:20210401T161500Z SUMMARY:IT DONE DESCRIPTION:Blahblahblah LOCATION:Nowhere END:VEVENT [/pre2]

Петр: Каждый VEVENT в этом объекте должен иметь идентичный UID. Соотносится с информацией по ссылке выше (пост №1647) Каждый объект должен содержать только 1 событие или задачу. Попробуйте указать для всех VEVENT одинаковый UID и посмотреть, что из этой затеи получится.

Dr. Oldwarez: Петр пишет: Каждый объект должен содержать только 1 событие или задачу. Попробуйте указать для всех VEVENT одинаковый UID и посмотреть, что из этой затеи получится. Получилось то, что второе событие импортировалось, а первое - нет. Вывод: надо импортировать поодиночке в цикле

Dr. Oldwarez: И даже это не срабатывает Просто ничего не выводит и не импортирует!!!

Петр: curl -k -u USERNAME:PASSWORD -T localname.ics -X "PUT" https://webmail.kleeblatt.com/rpc.php/calendars/USERNAME/calendar~GhGVum1xWexaffEfhiVkNN7/remotename.ics где remotename == UID VEVENTа помещенного в remotename.ics BEGIN:VCALENDAR .. BEGIN:VEVENT .. UID: xxxxxxxx .. END:VEVENT END:VCALENDAR

Dr. Oldwarez: Петр пишет: curl -k -u USERNAME:PASSWORD -T localname.ics -X "PUT" https://webmail.kleeblatt.com/rpc.php/calendars/USERNAME/calendar~GhGVum1xWexaffEfhiVkNN7/remotename.ics где remotename == UID VEVENTа помещенного в remotename.ics BEGIN:VCALENDAR .. BEGIN:VEVENT .. UID: xxxxxxxx .. END:VEVENT END:VCALENDAR И так делал, а всё не получается. Localname.ics можно брать любое или тоже UID? "PUT" - закавычивать или нет?

Петр: Dr. Oldwarez пишет: "PUT" - закавычивать или нет? Нет Dr. Oldwarez пишет: Localname.ics можно брать любое Любое добавьте в командную строку -Dhead И покажите содержимое файла head здесь, и командную строку тоже без купюр. Ну конечно без личных данных

Dr. Oldwarez: Петр пишет: И покажите содержимое файла head здесь, и командную строку тоже купюр. Ну конечно без личных данных curl -k -u USERNAME:PASSWORD -T C:\MINIGUI\MYPROG\TEMP\qwerty.ics -X PUT https://webmail.kleeblatt.com/rpc.php/calendars/USERNAME/calendar~GhGVum1xWexaffEfhiVkNN7/817474de-31f6-446d-a09b-d4976f554e5b.ics Где 817474de-31f6-446d-a09b-d4976f554e5b.ics - это имя сгенерированное функцией генератора UID qwerty.ics - локальный файл в одно событие А куда здесь ставить -Dhead?

Петр: curl -Dhead -k ..

Dr. Oldwarez: Петр пишет: curl -Dhead -k .. Да. И тот же результат curl -Dhead -k -u USERNAME:PASSWORD -T C:\MINIGUI\MYPROG\TEMP\localfile.ics -X PUT https://webmail.kleeblatt.com/rpc.php/calendars/USERNAME/calendar~GhGVum1xWexaffEfhiVkNN7/817474de-31f6-446d-a09b-d4976f554e5b.ics И заголовок [pre2]HTTP/1.1 100 Continue HTTP/1.1 204 No Content Date: Mon, 29 Mar 2021 13:54:37 GMT Server: Apache X-Powered-By: PHP/7.3.27 Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate Pragma: no-cache Vary: Accept-Encoding Set-Cookie: PHPSESSID=blunlmi2m5vrs8g6gdahahrrfn; path=/ Set-Cookie: horde_secret_key=blunlmi2m5vrs8g6gdahahrrfn; path=/; domain=webmail.kleeblatt.com; HttpOnly Set-Cookie: default_horde_view=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; domain=webmail.kleeblatt.com X-Powered-By: PleskLin Content-Type: text/html; charset=UTF-8[/pre2]

Петр: В общем-то результат нормальный. HTTP/1.1 100 Continue HTTP/1.1 204 No Content Показывает когда файл уже сохранен на сервере и вы пытаетесь его сохранить снова. Зайдите в C:\MINIGUI\MYPROG\TEMP\localfile.ics, измените LOCATION, примените PUT, а потом GET к 817474de-31f6-446d-a09b-d4976f554e5b.ics P.S. 204 No Content

Dr. Oldwarez: Петр пишет: В общем-то результат нормальный. HTTP/1.1 100 Continue HTTP/1.1 204 No Content Показывает когда файл уже сохранен на сервере и вы пытаетесь его сохранить снова. Зайдите в C:\MINIGUI\MYPROG\TEMP\localfile.ics, измените LOCATION, примените PUT, а потом GET к 817474de-31f6-446d-a09b-d4976f554e5b.ics P.S. УРА!!! Заработало!

Dr. Oldwarez: Извините, оно не работает. Не заносит данные из DBF в календарь. Может, с библиотекой попробовать? Притом, что ICSы генерятся [pre]BEGIN:VCALENDAR VERSION:2.0 PRODID:-//AaaaBbbbCccc BEGIN:VEVENT UID:c60a4464-2dec-4f83-8a9f-02d2463352e8 DTSTAMP:20210428T154800Z DTSTART:20210601T154700Z DTEND:20210601T161700Z SUMMARY:Тест DESCRIPTION:Просто тест LOCATION: Урюпинск END:VEVENT END:VCALENDAR[/pre] [pre]curl -k -u '+cCALName+':'+cCALPWD+' -T '+cOutput+' -X PUT '+cCalName+IIF(RIGHT(cCalName,1)='/','','/')+cUID+'.ics[/pre] где cOutput - это и есть вышеупомянутый ics файл

Петр: Dr. Oldwarez пишет: Притом, что ICSы генерятся У Вас программа генерирует невалидные ics файлы iCalendar Validator Problem! Found 1 warning Warnings Line length should not be longer than 75 characters near line # 9 Reference: RFC 5545 3.1. Content Lines Problem! Found 1 error Errors Missing VCALENDAR object near line # 1 Reference: RFC 5545 3.4 iCalendar Object

Петр: И второе - Вы не анализируете ответ сервера (-Dhead и т.д.), а то бы сразу увидели что-то вроде HTTP/1.1 100 Continue HTTP/1.1 415 Unsupported Media Type

Dr. Oldwarez: Петр пишет: У Вас программа генерирует невалидные ics файлы iCalendar Validator Problem! Found 1 warning Warnings Line length should not be longer than 75 characters near line # 9 Reference: RFC 5545 3.1. Content Lines Problem! Found 1 error Errors Missing VCALENDAR object near line # 1 Reference: RFC 5545 3.4 iCalendar Object Уже исправил Вот теперь [pre2] Results File Size: 13 lines, 323 bytes Number of events found: 1 Success! No errors found. Permalinks are available when validating by URL. You can use a permalink to send a link of the validation results page using e-mail or web posting. Show source data BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Qwwwwerty//UUUIIIOOOPP BEGIN:VEVENT UID:09a8e21b-fb27-49eb-9479-5b9c8ad1ba57 DTSTAMP:20210429T185200Z DTSTART:20210601T150000Z DTEND:20210601T190000Z SUMMARY:MYNAME: Birthday DESCRIPTION:My Birthday LOCATION:Pizzeria END:VEVENT END:VCALENDAR [/pre2] Но всё равно ничего в календарь не попадает. Скажите, пожалуйста, а как Dhead вывести тоже в отдельный файл отчёта, чтобы потом его проанализировать

Петр: Dr. Oldwarez пишет: а как Dhead вывести тоже Не Dhead, а -Dhead -D, --dump-header <filename> Write the received headers to <filename> Читайте файл с именем head (или назовите его так как Вам нужно) и анализируйте. Но всё равно ничего в календарь не попадает УРА!!! Заработало! Что же у Вас заработало?

Dr. Oldwarez: Петр пишет: Не Dhead, а -Dhead -D, --dump-header <filename> Write the received headers to <filename> Читайте файл с именем head (или назовите его так как Вам нужно) и анализируйте. И вот он, этот файл, который я назвал wtf.txt [pre2] HTTP/1.1 100 Continue HTTP/1.1 401 Unauthorized Date: Mon, 03 May 2021 04:31:57 GMT Server: Apache X-Powered-By: PHP/7.3.27 Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate Pragma: no-cache WWW-Authenticate: Basic realm="Horde DAV Server" Vary: Accept-Encoding Set-Cookie: PHPSESSID=bub17ffsgkd7249csku3c3brkt; path=/ X-Powered-By: PleskLin Transfer-Encoding: chunked Content-Type: application/xml; charset=utf-8 [/pre2] Я так понял, это что-то с авторизацией. Но ведь логин и пароль при ручном вводе подходят! И почему что-то там истекло 19 ноября 1981 года? В чём же проблема?

Петр: Dr. Oldwarez пишет: И почему что-то там истекло 19 ноября 1981 года? В чём же проблема? Какая там проблема .. It's an attempt to disable caching. The date is the birthday of the developer Sascha Schumann who added the code. From session.c: Authors: Sascha Schumann <sascha@schumann.cx> Andrei Zmievski <andrei@php.net> // ... CACHE_LIMITER_FUNC(private) { ADD_HEADER("Expires: Thu, 19 Nov 1981 08:52:00 GMT"); CACHE_LIMITER(private_no_expire)(TSRMLS_C); } Этот заголовок (ещё `Pragma: no-cache`) добавляется автоматически, когда запускается сессия (`session_start()`) для отключения кэша. Дата, на самом деле, не важна, главное чтобы она была из прошлого, чтобы кэш считался невалидным. Curl кэширует запросы. Помните это.

Петр: Код робочей консольной программы для загрузки файлов календаря на сервер. Может кому-то будет интересно. [pre2] #include "hbmxml.ch" #include "ics.ch" #command ?C <message> => QOutClr( <message>, .T. ) #command ?! <message> => QOutClr( <message>, .F. ) #define DIR_UTILS ( hb_DirBase() + "utils" ) REQUEST HB_CODEPAGE_UTF8 MEMVAR mxml_error MEMVAR mxml_error_msg INIT PROCEDURE _OnInit() PUBLIC mxml_error PUBLIC mxml_error_msg FErase( hb_DirBase() + "_err.log" ) FErase( hb_DirBase() + "_header.log" ) FErase( hb_DirBase() + "_out.xml" ) // Проверка доступности cURL IF ! hb_FileExists( hb_DirSepAdd( DIR_UTILS ) + "curl.exe" ) ?! "#white+#Can't find #red+#curl.exe" ErrorLevel( 2 ) __Quit() ENDIF M->mxml_error := .F. M->mxml_error_msg := "" mxmlSetErrorCallback( @cb_error() ) RETURN /* */ PROCEDURE cb_error( cErrorMsg ) M->mxml_error_msg := cErrorMsg M->mxml_error := .T. RETURN /* */ FUNCTION cb_type( xmlNode ) LOCAL cType := mxmlElementGetAttr( xmlNode, "type" ) LOCAL nResult IF Empty( cType ) cType := mxmlGetElement( xmlNode ) ENDIF SWITCH Lower( cType ) CASE "s:exception" CASE "s:sabredav-version" nResult := MXML_TEXT EXIT CASE "s:message" nResult := MXML_OPAQUE EXIT OTHERWISE nResult := MXML_IGNORE EXIT ENDSWITCH RETURN nResult /* */ STATIC FUNCTION sf_GetEvent( aSource ) LOCAL nStart, nEnd, nLen, e1, e2 hb_default( @aSource, {} ) IF ! Empty( aSource ) nStart := nEnd := 0 FOR EACH e1 IN aSource IF e1[ ICS_PARAM ] = "BEGIN" FOR EACH e2 IN e1[ ICS_VALS ] IF hb_LeftEq( e2, "VEVENT" ) nStart := e1:__enumIndex() EXIT ENDIF NEXT ELSEIF e1[ ICS_PARAM ] = "END" FOR EACH e2 IN e1[ ICS_VALS ] IF hb_LeftEq( e2, "VEVENT" ) nEnd := e1:__enumIndex() EXIT ENDIF NEXT ENDIF IF nEnd > 0 EXIT ENDIF NEXT IF nStart != 0 .AND. nStart < nEnd nLen := nEnd - nStart + 1 RETURN ACopy( aSource, Array( nLen ), nStart, nLen ) ENDIF ENDIF RETURN {} /* */ STATIC FUNCTION sf_GetEventUID( aSource ) LOCAL nPos hb_default( @aSource, {} ) IF ! Empty( aSource ) nPos := AScan( aSource, {| e | e[ ICS_PARAM ] = "UID" } ) IF nPos > 0 RETURN aSource[ nPos ][ ICS_VALS ][ ICS_PARAM ] ENDIF ENDIF RETURN "" /* */ STATIC FUNCTION sf_GetError( cArg, cException, cMessage ) LOCAL xmlTree, xmlNode, xmlSubNode // Check arguments... IF ! HB_ISSTRING( cArg ) .OR. Empty( cArg ) RETURN .F. ENDIF xmlTree := iif( hb_LeftEq( cArg, "<" ), ; mxmlLoadString( NIL, cArg, @cb_type() ), mxmlLoadFile( NIL, cArg, @cb_type() ) ) IF M->mxml_error .OR. Empty( xmlTree ) RETURN .F. ENDIF xmlNode := mxmlFindElement( xmlTree, xmlTree, "d:error",,, MXML_DESCEND ) IF Empty( xmlNode ) mxmlDelete( xmlTree ) RETURN .F. ENDIF xmlSubNode := mxmlGetFirstChild( xmlNode ) DO WHILE ! Empty( xmlSubNode ) IF mxmlGetType( xmlSubNode ) == MXML_ELEMENT IF mxmlGetElement( xmlSubNode ) == "s:exception" cException := mxmlGetText( xmlSubNode ) ELSEIF mxmlGetElement( xmlSubNode ) == "s:message" cMessage := mxmlGetOpaque( xmlSubNode ) ENDIF ENDIF xmlSubNode := mxmlGetNextSibling( xmlSubNode ) ENDDO mxmlDelete( xmlTree ) RETURN .T. /* */ STATIC FUNCTION sf_GetHTTPStatusCode( cFile, cCode, cDesc ) LOCAL cStr := hb_MemoRead( cFile ) LOCAL aStr := hb_ATokens( cStr, hb_eol() ) LOCAL nAt := hb_RAScan( aStr, {| s | hb_LeftEq( s, 'HTTP' ) } ) IF nAt > 0 cStr := aStr[ nAt ] cCode := hb_tokenGet( cStr, 2, ' ' ) aStr := hb_ATokens( cStr, cCode ) cDesc := LTrim( aStr[ 2 ] ) RETURN .T. ENDIF RETURN .F. /* */ STATIC PROCEDURE Usage() OutStd( "Syntax: fput <file>" + hb_eol() + hb_eol() ) RETURN /* */ PROCEDURE Main( cFile ) LOCAL cStr LOCAL aEvent, w := 0, cUid LOCAL cOpt LOCAL cCmdLn := '%1$s\curl.exe -u "%2$s:%3$s" %4$s -T "%5$s" -X %6$s %7$s%8$s%9$s' LOCAL cUser := "b329767865754" LOCAL cPwd := "xk567vjh0t" LOCAL cCalDav := "https://dav.fruux.com/" LOCAL cHRef := "calendars/a3298166498/ae6b576a-981f-4697-8c00-ac043a96d075/" LOCAL cStdOut, cStdErr LOCAL cException := "", cMessage := "", cCode := "", cDescription := "" IF Empty( cFile ) Usage() ErrorLevel( 0 ) __Quit() ENDIF // Read iCalendar file cStr := hb_MemoRead( cFile ) // Get VEVENT aEvent := sf_GetEvent( IcsToArray2( cStr, @w ) ) IF w # 0 ?C "Warning:#white+# Missing newline at end of input" ENDIF // Get VEVENT UID cUid := sf_GetEventUID( aEvent ) IF Empty( cUid ) ?! "#white+#Do nothing for #red+#" + cFile ErrorLevel( 3 ) __Quit() ENDIF /* PUT /calendars/johndoe/home/somerandomstring.ics HTTP/1.1 Content-Type: text/calendar; charset=utf-8 BEGIN:VCALENDAR .... END:VCALENDAR */ cOpt := '--insecure --no-progress-meter ' cOpt += '--http1.1 ' cOpt += '--header "Content-Type: text/calendar; charset=utf-8" ' cOpt += '--dump-header _header.log ' cStr := RTrim( cUid ) + '.ics' cCmdLn := hb_StrFormat( cCmdLn, DIR_UTILS, cUser, cPwd, cOpt, cFile, "PUT", cCalDav, cHRef, cStr ) hb_processRun( cCmdLn, , @cStdOut, @cStdErr ) IF ! Empty( cStdErr ) hb_MemoWrit( hb_DirBase() + "_err.log", cStdErr ) ErrorLevel( 4 ) __Quit() ENDIF IF ! Empty( cStdOut ) hb_MemoWrit( hb_DirBase() + "_out.xml", cStdOut ) IF sf_GetHTTPStatusCode( '_header.log', @cCode, @cDescription ) ?C 'HTTP status code: #white+#' + cCode + '# (' + cDescription + ')' ENDIF IF sf_GetError( cStdOut, @cException, @cMessage ) ?! "#red+#" + cException + hb_eol() ?C "#white+#" + cMessage ErrorLevel( 5 ) __Quit() ENDIF IF M->mxml_error ?! "#red+#An error occured while parsing an XML document." + hb_eol() ?C "#white+#" + M->mxml_error_msg ErrorLevel( 6 ) __Quit() ENDIF ENDIF OutStd( "Done" + hb_eol() ) IF sf_GetHTTPStatusCode( '_header.log', @cCode, @cDescription ) ?C 'HTTP status code: #white+#' + cCode + '# (' + cDescription + ')' ENDIF RETURN [/pre2] P.S. Нет предела совершенству - это я знаю

Петр: Файл сборки [pre2] # fput.hbp -w3 #-es2 #-mt -I../../include -inc -workdir=.hbmk -ofput -strip -instpath=.\ #Libs -lhbmxml -lmxml -lics -L../../lib #Link with harbour-32-x64.dll #-shared fput.prg colorize.prg [/pre2]

Петр: colorize.prg [pre2] #include "color.ch" PROCEDURE QOutClr( cMessage, lNewLine ) LOCAL cDefColor, cDefFGClr, cCurFGClr, cCurBKClr, cClrPair STATIC hColors := { ; "black" => "N", ; "blue" => "B", ; "green" => "G", ; "cyan" => "BG", ; "red" => "R", ; "magenta" => "RB", ; "brown" => "GR", ; "white" => "W", ; "gray" => "N+", ; "blue+" => "B+", ; // b - Bright "green+" => "G+", ; "cyan+" => "BG+",; "red+" => "R+", ; "magenta+" => "RB+",; "yellow" => "GR+",; "white+" => "W+" ; } LOCAL cStr hb_default( @cMessage, "" ) hb_default( @lNewLine, .T. ) hb_HCaseMatch( hColors, .F. ) IF .NOT. Empty( cMessage ) // Save a colors scheme cDefColor := SetColor() cClrPair := GetClrPair( cDefColor, CLR_STANDARD + 1 ) cDefFGClr := GetClrFore( cClrPair ) cCurBKClr := GetClrBack( cClrPair ) cCurFGClr := cDefFGClr FOR EACH cStr IN hb_ATokens( cMessage, '#' ) // If a string between #-Tags is equal to any predefined Color, // and is equal to the default Foreground Color: set current Foreground Color IF hb_HHasKey( hColors, cStr ) .AND. cCurFGClr == cDefFGClr cCurFGClr := hColors[ cStr ] ELSE SetColor( SetClrPair( cDefColor, CLR_STANDARD + 1, cCurFGClr + '/' + cCurBKClr ) ) QQOut( cStr ) // Reset current Foreground Color cCurFGClr := cDefFGClr ENDIF NEXT IF lNewLine ? e"\n" ENDIF // Restore a colors scheme SetColor( cDefColor ) ENDIF RETURN [/pre2] ics.ch [pre2] #ifndef ICS_CH_ #define ICS_CH_ #define ICS_PARAM 1 #define ICS_PROPS 2 #define ICS_VALS 3 #endif /* ICS_CH_ */ [/pre2]

Dr. Oldwarez: Огромное спасибо! Обязательно попробую интегрировать в свою прогу! И с прошедшими праздниками!

Петр: Dr. Oldwarez пишет: Огромное спасибо! Обязательно попробую интегрировать в свою прогу! И с прошедшими праздниками! Был рад вам помочь. Если возникнут вопросы - обращайтесь. Спасибо!

Dr. Oldwarez: Петр пишет: #Libs -lhbmxml -lmxml -lics -lhbwin И сразу вопрос: где можно достать библиотеку ics.lib? У меня в директории C:\MINIGUI\HARBOUR\LIB есть hbmxml, mxml, hbwin. А вот ics.lib нет.

Петр: Dr. Oldwarez пишет: И сразу вопрос: где можно достать библиотеку ics.lib? ics.lib это библиотека из одной функции parse, написана на C. // This code is based on J Decker original code posted at // https://stackoverflow.com/questions/35003036/toolset-to-parse-icalendar-file-in-c Ну и плюс функция-обертка IcsToArray для использования с Harbour.

Петр: IcsToArray2 обладает тем же функционалом, написана на Harbour. [pre2] // This code is based on J Decker original code posted at // https://stackoverflow.com/questions/35003036/toolset-to-parse-icalendar-file-in-c // disclaimer : no support // code provided as a example of minimal things one can do. // format for lines is... // PARAMETER[;PARAM_PROPERTY..]:VALUE[,VALUE2..]\n #ifndef __DBG_PARTS__ #xtranslate hb_traceLog( [<x,...>] ) => #endif #define ICS_PARAMETER 1 #define ICS_PROPERTY 2 #define ICS_VALUE 3 FUNCTION IcsToArray2( cInput, i ) LOCAL nStart := 1 LOCAL nEnd := 1 LOCAL nState := ICS_PARAMETER LOCAL char, cParameter, cProperty, cValue LOCAL aIcs := {} LOCAL aProps := {} LOCAL aValues := {} LOCAL lInQuote := .F. hb_default( @cInput, "" ) IF hb_eol() $ cInput cInput := StrTran( cInput, hb_eol(), e"\n" ) ENDIF FOR EACH char IN cInput SWITCH nState CASE ICS_PARAMETER IF char == ":" .OR. char == ";" cParameter := SubStr( cInput, nStart, nEnd - nStart ) hb_traceLog( "PARAMETER:%s", cParameter ) IF char == ";" nState := ICS_PROPERTY ELSE nState := ICS_VALUE ENDIF nStart := nEnd + 1 ENDIF EXIT CASE ICS_PROPERTY IF char == '"' IF ! lInQuote lInQuote := .T. ELSE lInQuote := .F. EXIT ENDIF ENDIF IF lInQuote EXIT ENDIF IF char == ";" .OR. char == ":" cProperty := SubStr( cInput, nStart, nEnd - nStart ) hb_traceLog( "PROPERTY:%s", cProperty ) AAdd( aProps, cProperty ) IF char == ":" nState := ICS_VALUE ENDIF nStart := nEnd + 1 EXIT ENDIF EXIT CASE ICS_VALUE IF char == e"\n" .OR. char == "," cValue := SubStr( cInput, nStart, nEnd - nStart ) hb_traceLog( "VALUE:%s", cValue ) AAdd( aValues, cValue ) IF char == e"\n" AAdd( aIcs, { cParameter, AClone( aProps ), AClone( aValues ) } ) aProps := {} aValues := {} nState := ICS_PARAMETER ENDIF nStart := nEnd + 1 ENDIF EXIT ENDSWITCH nEnd++ NEXT IF nEnd != nStart i := 1 ENDIF RETURN aIcs [/pre2]

Петр: Пример использования [pre2] PROCEDURE Main( cFile ) LOCAL cStr := hb_MemoRead( cFile ) LOCAL aIcs, err := 0 aIcs := IcsToArray2( cStr, @err ) IF err == 0 ? hb_ValToExp( aIcs ), hb_eol() sp_Dump2( aIcs ) ENDIF RETURN STATIC PROCEDURE sp_Dump2( aSource ) LOCAL e1, e2 hb_default( @aSource, {} ) IF ! Empty( aSource ) FOR EACH e1 IN aSource ?? e1[ ICS_PARAM ] FOR EACH e2 IN e1[ ICS_PROPS ] ?? ";" + e2 NEXT FOR EACH e2 IN e1[ ICS_VALS ] IF e2:__enumIsFirst() ?? ":" + e2 ELSE ?? "," + e2 ENDIF NEXT ?? hb_eol() NEXT ENDIF RETURN [/pre2] P.S. Код в посте №1669 исправил для использования IcsToArray2 Также добавлена функция извлечения кода возврата и его описания sf_GetHTTPStatusCode .

Dr. Oldwarez: Спасибо за функцию. Только теперь вопрос: как передать имя файла в процедуру Import2Cal (бывшая Main), чтобы не выдавало такого вот сбоя. [pre2]Error BASE/1081 Argument error: + Args: [1] = U [2] = C /[/pre2] У меня cFile:=cTempDIR+'\Test.ics', где cTempDIR - директория временных файлов, прописанная в INI PS: Поздравляю с днём Победы

Петр: Dr. Oldwarez пишет: PS: Поздравляю с днём Победы Спасибо. И Вас и всех остальных форумчан с Праздником! Только теперь вопрос: как передать имя файла в процедуру Import2Cal Не совсем понял вопрос Import2Cal( cFile ). И зачем вам директория временных файлов. Error BASE/1081 Argument error: + Args: [1] = U [2] = C / cFile:=cTempDIR+'\Test.ics' TYPE( cTempDIR ) == "U" (Args: [1] = U) и складывать его с '\Test.ics' ([2] = C) нельзя Лучше так cFile:=hb_defaultValue(hb_DirSepAdd(cTempDIR), hb_DirTemp())+'Test.ics'

Dr. Oldwarez: Петр пишет: Import2Cal( cFile ). И зачем вам директория временных файлов. А куда ещё временные ics-ы девать?

Петр: Dr. Oldwarez пишет: А куда ещё временные ics-ы девать? Ну вам виднее. Я обхожусь обычной субдиректорией в директории программы. И файлы там не временные, а копии файлов на сервере, как и рекомендуют разработчики.

Dr. Oldwarez: Error BASE 1081 уже убрал. Но теперь curl ругается на опцию --no-progress-meter [pre2]curl: option --no-progress-meter: is unknown curl: try 'curl --help' for more information[/pre2]

Dima: Dr. Oldwarez Может CURL древний [pre2] Fixed in 7.67.0 - November 6 2019 Changes: curl: added --no-progress-meter [/pre2]

Dr. Oldwarez: Dima пишет: Dr. Oldwarez Может CURL древний Fixed in 7.67.0 - November 6 2019 Changes: curl: added --no-progress-meter Да, древний. И не обновляется, несмотря на прямую замену. Лучше давайте уберём эту опцию

Dr. Oldwarez: Петр пишет: IcsToArray2 обладает тем же функционалом, написана на Harbour. Пока решил прочесать программу и обнаружил, что эта функция не работает. Выдаёт пустой массив

Петр: Dr. Oldwarez пишет: Выдаёт пустой массив Да, если строка на входе не валидная. Можно и в ошибку времени исполнения свалиться, но так лучше. И в дальнейшем приводите самодостаточные примеры или хотя бы часть своего кода, чтобы был какой-то предмет для разговора.

Dr. Oldwarez: Бывший main [pre2]FUNCTION Import2Cal() LOCAL cStr LOCAL cCmdLn := 'curl.exe -u "%1$s:%2$s" %3$s -T "%4$s" -X %5$s %6$s%7$s' LOCAL cOpt:='',aICS LOCAL aEvent,w:=0, cUid LOCAL cFile:=hb_defaultValue(cTempLW,"")+'\Termin.ics' LOCAL cStdOut, cStdErr cStr:=hb_MemoRead( cFile ) aICS:=IcsToArray2(cStr,@w ) aEvent := sf_GetEvent( IcsToArray2(cStr,@w ) ) MsgInfo(STR(LEN(aICS))) cUid := sf_GetUID( aEvent ) cStr := RTrim( cUid ) + '.ics' cUid += ".ics" cOpt := '--insecure --no-progress-meter ' cOpt += '--http1.1 ' cOpt += '--header "Content-Type: text/calendar; charset=utf-8" ' cOpt += '--dump-header _header.log ' cCmdLn:=hb_StrFormat(cCmdLn, cCalLogin, cCalPwd, cOpt, cFile, "PUT", cCalName, cUid ) //MsgInfo(cCmdLn) hb_ProcessRun( cCmdLn,,@cStdOut,@cStdErr) IF ! Empty( cStdErr ) hb_MemoWrit( hb_DirBase() + "_err.log", cStdErr ) ENDIF IF ! Empty( cStdOut ) hb_MemoWrit( hb_DirBase() + "_out.xml", cStdOut ) ENDIF RETURN NIL [/pre2] [pre2] STATIC FUNCTION sf_GetEvent(aSource) LOCAL nStart, nEnd, nLen, e1, e2 hb_default(@aSource, {}) IF .NOT. Empty( aSource ) nStart := nEnd := 0 FOR EACH e1 IN aSource DESCEND IF e1[ICS_PARAMETER] = "BEGIN" FOR EACH e2 IN e1[ICS_VALUE] DESCEND IF hb_LeftEq(e2, "VEVENT") nStart := e1:__enumIndex() EXIT ENDIF NEXT ELSEIF e1[ICS_PARAMETER] = "END" FOR EACH e2 IN e1[ICS_VALUE] DESCEND IF hb_LeftEq(e2, "VEVENT") nEnd := e1:__enumIndex() EXIT ENDIF NEXT ENDIF IF nEnd > 0 EXIT ENDIF NEXT IF nStart != 0 .AND. nStart > nEnd nLen := nStart - nEnd + 1 RETURN ACopy(aSource, Array(nLen), nEnd, nLen) ENDIF ENDIF RETURN {} [/pre2] Я проверил, что пустой массив aEvent выдаёт именно sf_getevent. aICS длиной в 12 элементов, как и положено ICS_PARAMETER ICS_VALUE - они не из "ICS.CH"? И где его взять?

Dima: Dr. Oldwarez пишет: И не обновляется, несмотря на прямую замену Так не бывает..........ну если корректно заменил конечно :)

Петр: Dr. Oldwarez пишет: Я проверил, что пустой массив aEvent выдаёт именно sf_getevent. aICS длиной в 12 элементов, как и положено У вас sf_getevent для использования с IcsToArray Для IcsToArray2 sf_GetEvent() код нужно модифицировать Читайте внимательно Код в посте №1669 исправил для использования IcsToArray2

Петр: Dr. Oldwarez пишет: ICS_PARAMETER ICS_VALUE - они не из "ICS.CH"? И где его взять? ICS_PARAMETER см. мой пост №1675 если коротко [pre2] #include "ics.ch" #define ICS_PARAMETER ICS_PARAM #define ICS_PROPERTY ICS_PROPS #define ICS_VALUE ICS_VALS [/pre2]

Dr. Oldwarez: Петр пишет: #include "ics.ch" Я имел в виду файл заголовка ics.ch. В директории заголовочных файлов у меня его нет.

Петр: Dr. Oldwarez пишет: Я имел в виду файл заголовка ics.ch. Пост №1671

Dr. Oldwarez: Петр пишет: У вас sf_getevent для использования с IcsToArray Для IcsToArray2 sf_GetEvent() код нужно модифицировать Читайте внимательно Код в посте №1669 исправил для использования IcsToArray2 Спасибо! Всё исправил. И уже начало импортировать. Теперь только проблема отфильтровывания дубликатов.

Dr. Oldwarez: И снова здравствуйте. Всё уже вроде работает, но проблема теперь с часовыми поясами. При импорте из онлайн-календаря в локальную базу данных получается так, что все даты сходят на время по Гринвичу. А у заказчика UTC+1 зимой и UTC+2 летом. Как сделать, чтобы при импорте правильно подхватывало зимнее/летнее время? Какая есть функция для определения время летнее или зимнее? По ключу TZID и потом ниже выходит какая-то дикая абракадабра.

SergKis: Dr. Oldwarez пишет Какая есть функция для определения время летнее или зимнее? Использую такие ф-ии [pre2] *----------------------------------------------------------------------------* FUNCTION nUx2cDts( nUx, lDts ) // nUx := 1508186400 *----------------------------------------------------------------------------* LOCAL dDate, cDate, cTime DEFAULT lDts := .F. tDate := hb_datetime( 1970, 1, 1 ) + nUx / 86400 IF lDts ; RETURN tDate ENDIF cDate := DtoS( hb_TtoD( tDate ) ) cTime := hb_TtoC( tDate ) cTime := Subs( cTime, At(" ", cTime) + 1 ) cTime := StrTran( cTime, ":", "" ) cTime := StrTran( cTime, ".", "" ) RETURN ( cDate + cTime ) *----------------------------------------------------------------------------* FUNCTION Gmt2Utc( cDate, lTrans, nHour ) *----------------------------------------------------------------------------* LOCAL tDtm, tUtc, cTm1, cTm2 LOCAL dDat, dNew, cTim LOCAL nTime, cTime LOCAL lHour := .T. // "+" LOCAL lDate := Empty(cDate) STATIC s_nHour DEFAULT cDate := DtoS(Date())+StrTran(Time(), ":", "") IF ISLOGICAL(nHour) ; lHour := nHour ; nHour := Nil ELSEIF ISCHAR(nHour) ; lHour := "+" $ nHour ; nHour := Nil ENDIF dDat := StoD(left(cDate, 8)) dNew := dDat cTim := Transform(Val(subs(cDate, 9, 6)), "99:99:99") IF s_nHour == Nil .or. lDate cTm1 := cTm2 := "" tDtm := hb_DateTime() tUtc := hb_TSToUTC( tDtm ) HB_TTOD( tDtm, @cTm1, "hh:mm:ss" ) HB_TTOD( tUtc, @cTm2, "hh:mm:ss" ) s_nHour := Val(cTm1) - Val(cTm2) ENDIF DEFAULT nHour := iif( lHour, s_nHour, -(s_nHour) ) , lTrans := .T. nTime := Secs(cTim) + nHour*60*60 cTime := SecToTime(nTime + iif( nTime < 0, 24*60*60, 0 )) IF nHour < 0 ; dNew -= iif( Secs(cTime) > Secs(cTim ), 1, 0 ) ELSE ; dNew += iif( Secs(cTim ) > Secs(cTime), 1, 0 ) ENDIF cDate := DtoS( dNew ) + StrTran( cTime, ":", "" ) IF lTrans ; cDate := Transform( cDate, "@R 9999-99-99 99:99:99" ) ENDIF RETURN cDate [/pre2] Использование [pre2] oCol := :GetColumn("R_DTM") ; oCol:nAlign := DT_CENTER oCol:cPicture := "@R 9999-99-99 99:99:99" oCol:nWidth := oCol:ToWidth(subs(oCol:cPicture, 2)) oCol:bDecode := {|cd| Gmt2Utc(cd, .F.) } oCol:cHeading := StrTran(ot:cDtms, "\", CRLF) ... по кнопке OK! для GETBOX-ов периода даты и периода времени :Event(10, {|ow,ky,cn| LOCAL o := Sys.Cargo, ot := Sys.Cargo:oBaseText LOCAL oObj := ow:Cargo:oObject, aTmp, cTmp, cDt1, cDt2, cFil, cObj LOCAL aObj := {}, cGm1 LOCAL aNam := {}, cGm2 LOCAL cNam := This.Events.Value LOCAL dDt1 := This.PeriodFrom.Value LOCAL dDt2 := This.PeriodTo.Value LOCAL cTm1 := This.TimesFrom.Value LOCAL cTm2 := This.TimesTo.Value ... cTm1 := left(trim(cTm1)+repl("0", 6), 6) cTm2 := left(trim(cTm2)+repl("0", 6), 6) cDt1 := hb_DtoC(dDt1, "YYYYMMDD")+cTm1 cDt2 := hb_DtoC(dDt2, "YYYYMMDD")+cTm2 cGm1 := Gmt2Utc(cDt1, .F., .F.) cGm2 := Gmt2Utc(cDt2, .F., .F.) ... [/pre2]

Dr. Oldwarez: SergKis пишет: *----------------------------------------------------------------------------* FUNCTION nUx2cDts( nUx, lDts ) // nUx := 1508186400 *----------------------------------------------------------------------------* LOCAL dDate, cDate, cTime DEFAULT lDts := .F. tDate := hb_datetime( 1970, 1, 1 ) + nUx / 86400 IF lDts ; RETURN tDate ENDIF cDate := DtoS( hb_TtoD( tDate ) ) cTime := hb_TtoC( tDate ) cTime := Subs( cTime, At(" ", cTime) + 1 ) cTime := StrTran( cTime, ":", "" ) cTime := StrTran( cTime, ".", "" ) RETURN ( cDate + cTime ) lDts - Date Shift, я так понимаю. nUx - это какое-то астрономическое число, но что оно означает? Тут идёт приплюсовка часа к времени, но как определить точно, это зимнее (от последнего воскресенья октября до последнего воскресенья марта) или летнее время?

SergKis: Dr. Oldwarez пишет nUx - это какое-то астрономическое число, но что оно означает? см. http://clipper.borda.ru/?1-3-0-00000631-000-0-0-1618297675 возможно эта ф-я не нужна. но как определить точно, это зимнее (от последнего воскресенья октября до последнего воскресенья марта) или летнее время? ф-я Gmt2Utc(...) определяет разницу gmt и местного времени. Определяйте сами запуская Gmt2Utc(NIL) хоть каждый час разницу зимнего или летнего времени. В показанном варианте ф-ии дата timestamp (gmt) строковая без разделителей, т.к. именно такая хранится в базе dbf для упрощения работы с индексами (данные получены из FireBird ф-ей nUx2cDts( nUx )). За месяц ~2 500 000 записей, базы годовые.

Dr. Oldwarez: SergKis пишет: *----------------------------------------------------------------------------* FUNCTION Gmt2Utc( cDate, lTrans, nHour ) *----------------------------------------------------------------------------* Итак, попытаюсь расшифровать: cDate - входная дата и время по Гринвичу lTrans - указание формата вывода ГГГГ-ММ-ДД ЧЧ:ММ:СС 2021-07-26 12:30:00 Что здесь делает статический s_nhour? А параметр nHour - это заданный сдвиг относительно Гринвича?

SergKis: hb_TSToUTC( [<tLocalTime>] ) ➜ tUtcTime -> converts a local time tTIMESTAMP to UTC time. hb_DateTime() -> local time tTIMESTAMP cDate - Utc время из базы тип char, значение без миллисекунд, т.е. '20201203102045' s_nHour - разница в часах локального времени и Utc времени, т.е. для нас: 3 - часа для летнего, 2 - для зимнего времени вычисляем при входе в программу и можно переустановить s_nHour в каком то режиме (по таймеру, каждый час) используется дальше как константа для отображения данных базы в локальном времени (в TsBrowse) и после GETBOX запросов периода дат, времени (локальное время задается), приводим ко времени Utc для работы с базой (SET SCOPE ..., SET FILTER ... и т.д.) lTrans используется для TsBrowse отображения в колонках или Label, если не задаем Picture Пример [pre2] @ y, x LABEL Period VALUE ot:cPer+":" WIDTH oDlu:W1 HEIGHT h FONT "FormNorm" BACKCOLOR aBClr x += This.Period.Width @ y, x LABEL PerFrom VALUE ot:cPers WIDTH oDlu:W1 HEIGHT h FONT "FormNorm" BACKCOLOR aBClr CENTERALIGN x += This.PerFrom.Width + 2 @ y, x GETBOX PeriodFrom OBJ oGet WIDTH oDlu:D3 HEIGHT h VALUE CtoD("") ; ACTION {|| _wPost(5, , This.Name) } IMAGE "view" PICTURE Nil ; BUTTONWIDTH h FONT "FormNorm" BACKCOLOR BClr ; ON INIT {|og| og:lOnGotFocusSelect := .T. } oGet:OnF5 := {|og| _wPost(5, , og:cControlName) } // VK_F5 x += This.PeriodFrom.Width + 2 @ y, x LABEL PerTo VALUE ot:cPerp WIDTH oDlu:W1 HEIGHT h FONT "FormNorm" BACKCOLOR aBClr CENTERALIGN x += This.PerTo.Width + 2 @ y, x GETBOX PeriodTo OBJ oGet WIDTH oDlu:D3 HEIGHT h VALUE CtoD("") ; ACTION {|| _wPost(6, , This.Name) } IMAGE "view" PICTURE Nil ; BUTTONWIDTH h FONT "FormNorm" BACKCOLOR BClr ; ON INIT {|og| og:lOnGotFocusSelect := .T. } oGet:OnF5 := {|og| _wPost(6, , og:cControlName) } // VK_F5 y := This.Period.Row + This.Period.Height + nGh x := This.Period.Col @ y, x LABEL Times VALUE ot:cTime+":" WIDTH oDlu:W1 HEIGHT h FONT "FormNorm" BACKCOLOR aBClr x += This.Times.Width @ y, x LABEL TimeFrom VALUE ot:cPers WIDTH oDlu:W1 HEIGHT h FONT "FormNorm" BACKCOLOR aBClr CENTERALIGN x += This.TimeFrom.Width + 2 @ y, x GETBOX TimesFrom OBJ oGet WIDTH oDlu:D3 HEIGHT h VALUE space(6) ; PICTURE "@R 99:99:99" VALID {|og,hw2| bValid(og, hw2) } ; BUTTONWIDTH h FONT "FormNorm" BACKCOLOR BClr ; ON INIT {|og| og:lOnGotFocusSelect := .T. } x += This.TimesFrom.Width + 2 @ y, x LABEL TimeTo VALUE ot:cPerp WIDTH oDlu:W1 HEIGHT h FONT "FormNorm" BACKCOLOR aBClr CENTERALIGN x += This.TimeTo.Width + 2 @ y, x GETBOX TimesTo OBJ oGet WIDTH oDlu:D3 HEIGHT h VALUE space(6) ; PICTURE "@R 99:99:99" VALID {|og,hw2| bValid(og, hw2) } ; BUTTONWIDTH h FONT "FormNorm" BACKCOLOR BClr ; ON INIT {|og| og:lOnGotFocusSelect := .T. } ... :Event(10, {|ow,ky,cn| // OK button LOCAL o := Sys.Cargo, ot := Sys.Cargo:oBaseText LOCAL oObj := ow:Cargo:oObject, aTmp, cTmp, cDt1, cDt2, cFil, cObj LOCAL aObj := {}, cGm1 LOCAL aNam := {}, cGm2 LOCAL cNam := This.Events.Value LOCAL dDt1 := This.PeriodFrom.Value LOCAL dDt2 := This.PeriodTo.Value LOCAL cTm1 := This.TimesFrom.Value LOCAL cTm2 := This.TimesTo.Value LOCAL oBuf := oKeyData() oBuf:cOwn := "" oBuf:cObj := "" oBuf:cPer := "" oBuf:cTim := "" oBuf:lRowId := This.RowID.Value FOR EACH aTmp IN oObj:GetAll() ; AAdd(aObj, aTmp[1]) NEXT IF Empty(aObj) cObj := This.ObjNum.Value IF Empty(cObj) MsgStop("Empty objects !", "ERROR") This.ObjNum.SetFocus DO EVENTS RETURN Nil ELSE aObj := { Tr0(cObj, 12) } ENDIF ENDIF DO EVENTS IF Empty(dDt1) MsgStop("Period: Date error or empty date from !", "ERROR") This.PeriodFrom.SetFocus DO EVENTS RETURN Nil ENDIF IF Empty(dDt2) MsgStop("Period: Date error or empty date to !", "ERROR") This.PeriodTo.SetFocus DO EVENTS RETURN Nil ENDIF IF dDt1 > dDt2 MsgStop("Period: Date from > Date to !", "ERROR") This.PeriodFrom.SetFocus DO EVENTS RETURN Nil ENDIF cTm1 := left(trim(cTm1)+repl("0", 6), 6) cTm2 := left(trim(cTm2)+repl("0", 6), 6) cDt1 := hb_DtoC(dDt1, "YYYYMMDD")+cTm1 cDt2 := hb_DtoC(dDt2, "YYYYMMDD")+cTm2 lEmpty := Empty(Val(cTm1)) .and. Empty(Val(cTm2)) IF lEmpty cGm1 := Gmt2Utc(cDt1 , .F., .F.) cDt2 := hb_DtoC(dDt2+1, "YYYYMMDD")+cTm2 cGm2 := Gmt2Utc(cDt2 , .F., .F.) ELSE cGm1 := Gmt2Utc(cDt1, .F., .F.) cGm2 := Gmt2Utc(cDt2, .F., .F.) ENDIF cNam := trim(cNam) IF !Empty(cNam) FOR EACH cTmp IN hb_ATokens(cNam, ";") IF !Empty(cTmp) ; AAdd( aNam, upper(alltrim(cTmp)) ) ENDIF NEXT ENDIF IF Len(aObj) > 1 ; aObj := ASort(aObj) ENDIF oBuf:aObj := aClone(aObj) oBuf:cOwn := "" oBuf:cObj := "" oBuf:cPer := hb_DtoC(dDt1, "DD.MM.YYYY")+"."+" - "+hb_DtoC(dDt2, "DD.MM.YYYY")+"." IF !Empty(Val(cTm1)) .and. !Empty(Val(cTm2)) oBuf:cTim := Transform(cTm1, "@R 99:99:99")+" - "+Transform(cTm2, "@R 99:99:99") ENDIF oMain:StatusBar:Say(ot:cWait, 1) ; DO EVENTS cFil := Select_God( cGm1, cGm2, aObj, aNam, lEmpty ) oMain:StatusBar:Say("", 1) ; DO EVENTS IF !Empty(cFil) .and. hb_FileExists(cFil+".dbf") wSelectEvent(cFil, oBuf) ENDIF RETURN Nil }) ... [/pre2]

Dr. Oldwarez: SergKis пишет: s_nHour - разница в часах локального времени и Utc времени, т.е. для нас: 3 - часа для летнего, 2 - для зимнего времени вычисляем при входе в программу А как именно его вычисляют? Или всё же прописывают в файле INI?

SergKis: Dr. Oldwarez пишет как именно его вычисляют? В тексте ф-ии[pre2] IF s_nHour == Nil .or. lDate cTm1 := cTm2 := "" tDtm := hb_DateTime() tUtc := hb_TSToUTC( tDtm ) HB_TTOD( tDtm, @cTm1, "hh:mm:ss" ) HB_TTOD( tUtc, @cTm2, "hh:mm:ss" ) s_nHour := Val(cTm1) - Val(cTm2) ENDIF [/pre2]

Dr. Oldwarez: Итак, у меня всё, вроде работает, но вот у шефа с той же прогой файл listall_ics.xml, который должен использоваться для считывания данных из онлайн-календаря выдаёт такую вот хрень. Я так понимаю, это шеф неправильно ввёл свои имя пользователя и пароль. [pre2]<?xml version="1.0" encoding="utf-8"?> <d:error xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"> <s:exception>Sabre\DAV\Exception\NotAuthenticated</s:exception> <s:message>Username or password does not match</s:message> <s:sabredav-version>1.8.12</s:sabredav-version> </d:error>[/pre2]

Петр: Dr. Oldwarez пишет: Я так понимаю, это шеф неправильно ввёл свои имя пользователя и пароль. Да

Dr. Oldwarez: И снова здравствуйте! Там у шефа был пароль с запретным символом, попросил заменить, потом в условии фильтра немного порылся. Вроде, работает, но только на стационарных компах. Если событие онлайн-календаря вводят с телефона - жутко коверкает даты и время, кроме конечной даты. Стартовая дата всегда - 27 октября 1996 год. Время коверкает в обеих случаях. В чём разница между событием, введённым с компа и таковым с мобилки? [pre2] IF aCalStr[nBuf]="DTSTART" cDateTime:=SUBSTR(aCalStr[nBuf],AT(":",aCalStr[nBuf])+1) dStartDate:=STOD(LEFT(cDateTime,8)) cStartTime:=STRZERO(VAL(SUBSTR(cDateTime,10,2))+nTimeZone,2)+":"+SUBSTR(cDateTime,12,2) [/pre2] Тут, как видите, в качестве разделителя используется двоеточие. Возможно, в мобильниках какой-то другой символ, но как это влияет на календарь? Upd: с мобильника то же двоеточие, но даты искажаются до 1916, 1917 года. Upd2: в случае, если событие создано на андроидном мобильнике, но потом изменено и сохранено на компьютере - всё в порядке. Дата передаётся правильно, даже если меняют только текст.



полная версия страницы