В gmail можно делать закладки на определенные письма/цепочки писем.
Обычно ссылка имеет вид
Например, ваши заказы хранятся в таблице и было-бы здорово сделать ссылку на gmail цепочку по каждой заявке.
Пока что я написал только часть скрипта. У меня база хранится в google spreadsheets, ну а у вас это может быть mySql или что-то еще.
Как вытащить данные из таблиц гугл вы можете посмотреть тут (на английском, но всё очень подробно и понятно).
Да и еще, пока я ковырялся с php, в google apps scripts появилась возможность работать с gmail, так что скорее всегоЯ не буду дописывать этот скрипт (ну там как всегда чего-то не работает, чего-то не хватает - нафиг - буду продолжать этот скрипт). Но если что-то нужно - спрашивайте.
Итак Я использую ZEND framework, а именно класс Zend/Mail/Protocol/Imap.php (версия, с которой Я работал) и дополняющий класс, который написал сам - shkurIMAPclassExtendsZendProtocolImap.php
И собственно сам скрипт: (есть версия посвежее - если надо спрашивайте - выложу)
Обычно ссылка имеет вид
https://mail.google.com/mail/#all/124e3d1d452af489,где "124e3d1d452af489" - уникальный id писма/цепочки писем.
Например, ваши заказы хранятся в таблице и было-бы здорово сделать ссылку на gmail цепочку по каждой заявке.
Пока что я написал только часть скрипта. У меня база хранится в google spreadsheets, ну а у вас это может быть mySql или что-то еще.
Как вытащить данные из таблиц гугл вы можете посмотреть тут (на английском, но всё очень подробно и понятно).
Да и еще, пока я ковырялся с php, в google apps scripts появилась возможность работать с gmail, так что скорее всего
Итак Я использую ZEND framework, а именно класс Zend/Mail/Protocol/Imap.php (версия, с которой Я работал) и дополняющий класс, который написал сам - shkurIMAPclassExtendsZendProtocolImap.php
И собственно сам скрипт: (есть версия посвежее - если надо спрашивайте - выложу)
<?php echo 'Не забудьте включить IMAP в настройках почты!'; $login = "user@exmple.com"; //обязательно вида user@domain.com $pass = "pass"; //require_once "Zend/Mail/Storage/Imap.php"; //кагбэ не нужен require_once "Zend/Mail/Protocol/Imap.php"; //require_once "Zend/Registry.php"; //кагбэ не нужен require_once "shkurIMAPclassExtendsZendProtocolImap.php"; // достаточно инициализировать только дочерний класс //$protocol = new Zend_Mail_Protocol_Imap('imap.gmail.com', 993, true); $protocol = new shkur_IMAP_class_Extends_Zend_Mail_Protocol_Imap('imap.gmail.com', 993, true); $protocol->login($login, $pass); // попробую написать чтобы скрипт сам выбирал папку All mail независимо от локлизации. utf-7 imap кривая кодировка $xlist = $protocol->xlist(); //print_r($xlist); $found = array_keys($protocol->getParentStack('\AllMail', $xlist)); $allMail = $found[0]; //$storage = new Zend_Mail_Storage_Imap($protocol); //кагбэ не нужен //$protocol->select("INBOX"); нам нужна вся почта а это чуть ниже абракадабра $protocol->select($allMail); // select надо вызывать после $storage = new Zend_Mail_Storage_Imap($protocol); иначе поиск смотрит в INBOX; метод echo $storage->getCurrentFolder(); в любом случае будет выводить INBOX - убиваем перфекциониста - идём по простому пути - работает и х_й с ним. //echo $storage->getCurrentFolder(); //ret INBOX в любом случае - неправильно что-то ну хрен с ним //echo '<br>'; echo "<pre>"; print_r($protocol->listMailbox()); echo "</pre>"; echo '<br>'; массив ящики //echo $protocol->fetch('X-GM-MSGID', $storage->getUniqueId(1)); // ret 1327644189674155015 то что надо! ура!!! $findthis = "test"; // qwertyuiop живалов&test echo 'ищщем → <b>'.$findthis.'</b><br>'; //$search = $protocol->search(array("charset utf-8 text", $findthis)); // вот так работает - это поиск через imap протокол низзя пробелы - можно & или | $search = $protocol->search(array("charset utf-8 X-GM-RAW", $findthis)); //более жадный поиск, например по запорсу живалов|test (это разные цепочки и разные письма) imap ни чего не найдёт, а гугло поиск даст и те и другие письма! echo "\nкол-во найденных писем по запросу $findthis = ".count($search)."<br>"; if (count($search) < 1 ){ exit('ни чего не найдено в почте. exit');} // или break, exit может прервать выполнение всего скрипта. //echo "\nsearch= "; print_r($search); //echo $decID = $protocol->fetch('X-GM-MSGID', $storage->getUniqueId($search[0])-3); //почему-то ставит 50 вместо 47 //echo $decID = $protocol->fetch('X-GM-MSGID', $search[0]); //вот так работает и без всяких getUniqueId $decID = $protocol->fetch('X-GM-THRID', $search); $decID = array_unique($decID); // убрать повторяющиеся значения - несколько писем в одной цепочке. //if ($decID) {} else {echo 'переменной нет';} //echo "\n$decID= "; print_r($decID); if ($decID){ // проверка на существование массива/переменной, т.к. если ни чего нет, то будет ошибка foreach foreach ($decID as $k => $v) { $hexID[]=$protocol->dec2hex($v); } } /** подготовим почву для ссылки, которая должна быть вида https://mail.google.com/mail/#all/13262ab3255034e5 * или если это hosted аккаунт, то (/a/ - должно быть!) https://mail.google.com/a/example.com/#all/124e3d1d452af489 */ $domain = substr(strtolower(stristr($login, '@')), 1); //голый домен ёпте! без@ if ($domain == 'gmail.com' or $domain == 'googlemail.com'){/*$domain = 'google.com';*/ $preUrl = 'https://mail.google.com/mail/';} else {$preUrl = 'https://mail.google.com/a/'.$domain;} if (count($hexID) == 1 ){ echo "\nнайдена одна единственная цепочка писем, это ОЧЕНЬ гуд :) \n"; $finalUrl = $preUrl.'#all/'.$hexID[0]; echo "<a href='$finalUrl'>$finalUrl</a>"; return ($hexID ? $finalUrl : 'FAIL → ссылка $hexID'); } if (count($hexID) > 1 ){ echo "\nнайдено несолько цепочек писем. применяем ярлык. ссылка должна быть на ярлык. ярлык это папка. если применить ярлык к письму, то он будет применен ко всей цепочке. Можно ли прменить ярлык к цепочке или надо применить его к каждому письму?\n"; /* если у всех писем один общий ярлык вида 'z/*' используй его и не добавляй новый ярлык - борьба с дубликатами. * именно 'z/*' потому что писма могут быть объеденены например ярлыком статуса, или каким-то другим общим ярлыком. * z это основной ярлык-папка с вложенными ярлыками, которые идут через дробь. Обязательно включить функцию вложенных * ярлыков в gmail lab (экспериментальные функции) и добавить руками корневой ярлык и (?один вложенный?) на всякий случай. */ $labels = $protocol->fetch('X-GM-LABELS', $search); //вытащить все ярлыки из почты по данному поисковому запросу //print_r($labels); echo "\n".count($labels); foreach ($labels as $k => $v){ $str[] = '$labels['.$k.']'; } $str = implode(', ', $str); // строка для сравнения массивов eval('$sameLabels = array_intersect('.$str.');'); // сравни массивы и покажи ярлыки присутствующие у всех писем. С доп. (_assoc) проверкой не находит все повторения ///*это test*/array_push($sameLabels, 'z/9', 'z/14', 'z/15', 'z/18'); //не использовать $array[] = value - кривые ключи! //echo '$sameLabels = '; print_r($sameLabels); function findZlabel($val) { if (($val{0}.$val{1}) == 'z/'){ //ярлыки для объединения начинаются с z/цифра... return ($val); } } $existingLabel = array_filter($sameLabels, "findZlabel"); //найди ярлыки вида z/* //echo '$existingLabel = '; print_r($existingLabel); if ($existingLabel = array_shift($existingLabel)){ // именно = а не == Ключи в массиве идут абы как т.к. использовали функцию array_intersect $finalUrl = $preUrl.'#label/'.urlencode($existingLabel); /** #label/z%2F0 */ echo "\nтру → используем существующий ярлык\n"; echo "<a href='$finalUrl'>$finalUrl</a>"; return ($existingLabel ? $finalUrl : 'FAIL → используем существующий ярлык'); } else { echo "\nнетру → создаём новый ярлык\n"; /** найдём какой использовать новый ярлык * TODO: найти неиспользуемый ярлык и либо удалить его, либо использовать. Как это можно сделать: * получаем список всех ярлыков, и проверям каждый на наличие в нём письма. Если ярлык ни где не испульзуется используем его. * Проблема в том что это будет использовать много ресурсов учитывая то, что ярлыков будет дахуя > 1000 * Может быть имеет смысл $newLabel делать не из последнего ярлыка, а с первого, но опять же ресурсы, ярлыков будет дахуя > 1000 */ $xlistz = $protocol->xlist("", "z/*"); $lastLabel = end(array_keys($xlistz)); $newLabel = $lastLabel; while (array_key_exists($newLabel, $xlistz)) { $newLabel = 'z/'.(str_replace('z/', '', $newLabel) + 1); } echo "\n\$newLabel = ".$newLabel; // применить новый ярлык ко всем письмам по данному запросу и выдать URL $store = $protocol->gmailLabel(array($newLabel), $search, null, '+', true); //echo ($store ? "\ntrue" : "\nfalse"); $finalUrl = $preUrl.'#label/'.urlencode($newLabel); /** #label/z%2F0 */ echo "\n<a href='$finalUrl'>$finalUrl</a>"; return ($store ? $finalUrl : 'FAIL → используем новый ярлык'); } } ?>
ну и пожалуй выложу класс который я написал:
<?php /* 13.09.2011 можно спамить сюда testtesttest73@gmail.com это алиас рабочего ящика это моя первая попытка написать класc пэхапэ дополняющий Zend_Mail_Protocol_Imap (версия $Id: Imap.php 18977 2009-11-14 14:15:59Z yoshida@zend.co.jp ) т.е. сам по себе этот клас не работает, только вместе с зендом. require_once "Zend/Mail/Protocol/Imap.php"; первым делом добавим сюда hex2dec and vice versa http://code.google.com/intl/ru-RU/apis/gmail/imap/ */ class shkur_IMAP_class_Extends_Zend_Mail_Protocol_Imap extends Zend_Mail_Protocol_Imap { /** * set flags label to threads * * @param string|array $flags flags to set, add or remove - see $mode * @param int $from message for items or start message if $to !== null * @param int|null $to if null only one message ($from) is fetched, else it's the * last message, INF means last message avaible * @param string|null $mode 'null' and '+' to add flags, '-' to remove flags, everything else sets the flags as given * @param bool $silent if false the return values are the new flags for the wanted messages * @return bool|array new flags if $silent is false, else true or false depending on success * @throws Zend_Mail_Protocol_Exception */ public function gmailLabel($flags, $from, $to = null, $mode = null, $silent = true){ $item = 'X-GM-LABELS'; ($mode == null ? $mode = '+' : null); if ($mode == '+' || $mode == '-') { $item = $mode . $item; } $flags = $this->escapeList($flags); if (is_array($from)) { $set = implode(',', $from); } else if ($to === null) { $set = (int)$from; } else if ($to === INF) { $set = (int)$from . ':*'; } else { $set = (int)$from . ':' . (int)$to; } $result = $this->requestAndResponse('STORE', array($set, $item, $flags), $silent); if ($silent) { return $result ? true : false; } $tokens = $result; $result = array(); foreach ($tokens as $token) { if ($token[1] != 'FETCH' || $token[2][0] != 'X-GM-LABELS') { continue; } $result[$token[0]] = $token[2][1]; } return $result; } /** * Gets the parent stack of a string array element if it is found within the * parent array * * This will not search objects within an array, though I suspect you could * tweak it easily enough to do that * * @param string $child The string array element to search for * @param array $stack The stack to search within for the child * @return array An array containing the parent stack for the child if found, * false otherwise */ function getParentStack($child, $stack) { foreach ($stack as $k => $v) { if (is_array($v)) { // If the current element of the array is an array, recurse it and capture the return $return = $this->getParentStack($child, $v); // If the return is an array, stack it and return it if (is_array($return)) { return array($k => $return); } } else { // Since we are not on an array, compare directly if ($v == $child) { // And if we match, stack it and return it return array($k => $child); } } } // Return false since there was nothing found return false; } /** * Gets the complete parent stack of a string array element if it is found * within the parent array * * This will not search objects within an array, though I suspect you could * tweak it easily enough to do that * * @param string $child The string array element to search for * @param array $stack The stack to search within for the child * @return array An array containing the parent stack for the child if found, * false otherwise */ function getParentStackComplete($child, $stack){ $return = array(); foreach ($stack as $k => $v) { if (is_array($v)){ // If the current element of the array is an array, recurse it // and capture the return stack $stack = $this->getParentStackComplete($child, $v); // If the return stack is an array, add it to the return if (is_array($stack) && !empty($stack)){ $return[$k] = $stack; } } else { // Since we are not on an array, compare directly if ($v == $child){ // And if we match, stack it and return it $return[$k] = $child; } } } // Return the stack return empty($return) ? false: $return; } /** list переделанный в Xlist * get mailbox list * * this method can't be named after the IMAP command 'LIST', as list is a reserved keyword * * @param string $reference mailbox reference for list * @param string $mailbox mailbox name match with wildcards * @return array mailboxes that matched $mailbox as array(globalName => array('delim' => .., 'flags' => ..)) * @throws Zend_Mail_Protocol_Exception */ public function xlist($reference = '', $mailbox = '*'){ $result = array(); $list = $this->requestAndResponse('XLIST', $this->escapeString($reference, $mailbox)); if (!$list || $list === true){ return $result; } foreach ($list as $item){ if (count($item) != 4 || $item[0] != 'XLIST'){ continue; } $result[$item[3]] = array('delim' => $item[2], 'flags' => $item[1]); } return $result; } /* Input: A decimal number as a String. Output: The equivalent hexadecimal number as a String.*/ function dec2hex($number){ $hexvalues = array('0','1','2','3','4','5','6','7', '8','9','a','b','c','d','e','f'); $hexval = ''; while($number != '0') { $hexval = $hexvalues[bcmod($number,'16')].$hexval; $number = bcdiv($number,'16',0); } return $hexval; } /* Input: A hexadecimal number as a String. Output: The equivalent decimal number as a String. */ function hex2dec($number){ $decvalues = array('0' => '0', '1' => '1', '2' => '2', '3' => '3', '4' => '4', '5' => '5', '6' => '6', '7' => '7', '8' => '8', '9' => '9', 'a' => '10', 'b' => '11', 'c' => '12', 'd' => '13', 'e' => '14', 'f' => '15'); $decval = '0'; $number = strrev($number); for($i = 0; $i < strlen($number); $i++){ $decval = bcadd(bcmul(bcpow('16',$i,0),$decvalues[$number{$i}]), $decval); } return $decval; } /** так как эта функция будет исключена в следующих версиях Zend я пихну её сюда. * do a search request * * This method is currently marked as internal as the API might change and is not * safe if you don't take precautions. * * @internal * @return array message ids array($this->escapeString($params)) - непонятная херня */ public function search(array $params){ $response = $this->requestAndResponse('SEARCH', $params); if (!$response) { return 'gotohell';//$response; } foreach ($response as $ids) { if ($ids[0] == 'SEARCH') { array_shift($ids); return $ids; } } return array(); } } ?>