Jump to content
Search In
  • More options...
Find results that contain...
Find results in...

Leaderboard

Popular Content

Showing content with the highest reputation since 05/24/2024 in Blog Entries

  1. А мы скажем - хитрость и наглость не делают воришку нормальным человеком! Тут господин @spectre обратил внимание на удивительный, хоть и вполне ожидаемый, прецедент. Жил-был некий мудень, который в один прекрасный день меньше года назад решил, что продавать ворованное - лучше, чем работать и создавать что-то свое. Тогда в его наглую, но не очень умную голову, пришла идея создать очередную варезную помойку. Помойка, естественно, быстро попала ко мне в базу https://warez.rip/, ничем особым не отличалась от своих друзей, но наш герой начал читать умные книжки. Он бы мог, конечно, в свое время вложиться в Финико, как один наш известный коллега - крипто-трейдер, но так уж получилось, что наш герой успел украсть на других помойках только записи курсов бизнес-молодости и других гуру успешного успеха в e-commerce, а курсов крипто-трейдинга от Паши в наличии еще не было. Так что, понимая бесперспективность конкуренции с другими варезными помойками, более старыми и популярными, решил пойти по другому пути. Как вы знаете, если конкуренция слишком большая – надо как-то отстраняться от конкурентов, либо концентрируясь на более узкой нише, либо вообще заявить, что все эти конкуренты тебе не ровня, потому что ты - Д’Артаньян! Вот наш герой и подумал, а почему бы не прикинуться легальной площадкой? Да не просто прикинуться, а еще и обзавестись инструментами, которые будут показывать, что другие варезные помойки - это обычные варезные помойки, а вот его помойка - это ого-го какая легальная площадка! Да вот беда, угнать у меня WarezRip не так то просто, поэтому наш воришка, недолго думая, просто скопировал все его тексты и логику проверки, исключив свой помоечный ресурс из общей базы, чтобы при его проверке на его же ресурсе все видели, будто бы он является легальной площадкой: Но этого ему показалось мало, поэтому он дополнительно украл у @spectre его проверочный модуль, убрал оттуда упоминание своей уютной помоечки, а затем додумался его выложить на оф. площадке (ненадолго). К слову, если вы еще не скачали оригинальный модуль - самое время это сделать, чтобы проверить свои сайты: Раз наш воришка хотел популярности, сделаем его популярным Ну а всем нормальным людям напоминаю - избегайте варезных помоек, уважайте чужой труд, чтобы люди уважали и ваш собственный, и не забывайте откуда можно качать дополнения для OpenCart:
    9 points
  2. Причин может быть масса от конфига сервера, до тяжелых запросов к бд, проблемы с dns, cdn и тд Возьмем то случай, когда ваш VPS имеет много ресурсов и настроен серьезным специалистом, а значит проблема на стороне опенкарта и плагинов, которые были установлены Обычно первым делом включают лог тяжелых запросов. Опенкарт в момент создания таблицы выбирает тип таблиц ENGINE=MyISAM. Этот тип может блокировать таблицы, именно поэтому в логе можно встретить простейшие запросы выполняющиеся по 2+ секунды. Лучше перейти на innodb. На просторах гитхаба есть скрипт, который поможет сменить движок хранилища, а так же добавить индексы к таблицам. Кстати, индексы он добавляет всем столбцам, которые содержат подстроку "_id" https://github.com/lilalaunesau/opencart-turbo Но одним логом сыт не будешь и поэтому я написал решение, которое покажет время работы каждого контроллера на странице. Предварительно желательно отключить кеширование mysql. За это отвечают такие параметры конфигурации как query_cache_size = 0 query_cache_type = 0 С скрина, который прикреплен к этому посту можно сделать вывод, что менюшка не кешируется и грузится около 640мс Один из моих модулей отрабатывает за 100мс, что тоже не есть хорошо, но с учетом того что на этой странице таблица из 10+ товаров, то норм Футер тоже можно закешировать сэкономив около 300мс Если этот пост набирает 5 комментов, то выложу это решение. Напоследок хочу сказать, что продакшн это святое и любые тесты и замеры нужно делать на дев, тест или локальном окружении под присмотром профессионалов. Если я где-то некорректно выразился, то пишите поправим
    4 points
  3. Усім привіт, просто тест на уважність, тут випадково виявив не баг, а фічу в перекладі української мови ocStore 3, що на сторінці авторизації, майже у всіх, хто качав саме останню версію, а може і раніше, бо не зрозуміло коли саме зробили помилку с перекладом, там вказано замість покупців — покупуів. Я перевірив, думав, що косяк тільки на моїх деяких проєктах, але виявилось, що ні. Таке є багато де. не буду тикати посиланнями. Просто перевір свій сайт, або сайт клієнта та посміхнися). Так от хочу запитатися, скільки нас таких? P.S. Нікого не хочу ображати, а просто підняти настрій і прибрати баг)).
    3 points
  4. Якщо на комп'ютерi ще не встановлено node.js його потрiбно завантажити з офсайту та встановити як звичайний додаток. Короткий посібник: 1) Спершу потрiбно вiдкрити консоль, та перейти до проекту наприклад, cd D:\xampp\htdocs\www\multiedit-pro\ 2) Переходимо до розгорнування проекту, вводимо в консолi: npm create vite@latest 3) Назва проекту: Якщо на цьому етапi ви розгортаєте новий чистий проект, то можна вказати його назву. Але в нашому випадку, для роботи поряд з CMS OpenCart замiсть назви, потрiбно поставити крапку тодi з'явиться запитання, i вам потрiбно буде: 3) Обрати Ignore files and continue, щоб залишити вже iснуючi файли. 4) Обрати шаблон я обираю Vue. 5) Обрати налаштування наприклад JavaScript Готово! 6) Для подальшої роботи підтягуємо залежнi модулi npm i ...тут буде багато iнфи) А ось i результат: Проект створили, та рушiй ocStore не зачепили! (Вiн знаходиться в папцi upload). А тепер можна й подивитись на результат, та отримати крапельку дофаміну: npm run dev, або npm run preview! Офiцiйний сайт Vite!
    3 points
  5. Опенкарт існує вже десятки років, а в ньому, до чого часу, існує "не баг а фіча" з сесією. Точніше з кукою сесії. По-перше вона існую тільки поки відкритий браузер. Що дуже дивно. По-друге. Навіть якщо ви зміните час життя самої куки, то це вам не допоможе. Бо кука буде діяти, не з поточного часу, а з моменту її створення 0_____о А все через це https://www.php.net/manual/ru/function.session-set-cookie-params.php#100657 Вирішуеться це просто Замість session_set_cookie_params(0, '/'); session_start(); Потрібно використовувати $lifetime = 8640000; session_start(); setcookie(session_name(),session_id(),time()+$lifetime,'/');
    3 points
  6. В Украине нет авторитетной базы проверенных дропшиппинг поставщиков. И, что весьма странно, нет даже форумов, где эту тему обсуждают. Соответственно, поднимаю вопрос здесь, на нашем форуме, который так или иначе посвящен теме интернет-магазинов. На этом сайте имеются в наличии шаблоны для интернет-магазина. Короче, грех проходить мимо такого добра... Остается выбрать поставщика и чуток впрячься в дело. Кстати, запустить сайт — это только полдела. Крайне важно разобраться с трафиком. И если вы не хотите долгие месяцы ждать результаты от SEO, берите все в свои руки и вникните в тему таргета, тем более что там сейчас скидка -50%. В Топе гугла находятся не самые лучшие поставщики по схеме дропшиппинг в Украине Найти дропшиппинг поставщика можно прямо в поиске Google. Правда есть парочка "но". К примеру, есть в топе выдаче поставщик bigopt.com. Выглядит все цивильненько: каталог производителей или поставщиков и их продукции, в общем все дела. Но, чтобы о сотрудничестве, надо заплатить (символически, конечно, но надо). А по факту, редкий поставщик потом отвечает на сообщения. О другом поставщике из топа выдачи есть негативные отзывы. Мол, задерживают выплаты или доставку и тому подобное. Особое внимание стоит обратить на тех, кто прямо рекламируется по данному запросу. Было дело напоролся на сайтец, который при регистрации требует много личных данных, а в каталоге - какой-то шлак. Поставщики по схеме дропшиппинга с OpenCart под капотом Вот эта часть самая приятная. Наша гордость, так сказать :) Потому что она показывает, насколько крутым может быть OpenCart. Понятно, что для своих так сказать, в статье отводится первое место. Так что, если у вас сайт на опенкарт и вы работаете по дропу (как поставщик) -- не забудьте написать адрес своего сайта в комментах, чтобы больше людей узнали о вас! Естественно, я потом добавлю сайт в этот список. https://drivex.ua/ (автомобильный свет) https://ddtuning.com.ua/ (авто тюнінг) — Новый сайт в списке https://kitabooki.com.ua/ (восточная культура) https://babyshops.com.ua/ (детские товары) https://malishpl.org.ua/ (детские товары) https://silvero.com.ua/ (иконы из Греции и Италии) — Новый сайт в списке https://meopt.com.ua/ (несколько тематик, еще вариант шаблона этой же студии) https://fanatka.com.ua/ (обувь) https://sezon.ua/ - (обувь мужская и женская) https://look4stuff.com.ua/ (одежда, шаблон тоже из наших краев -- Moneymaker2 всего за $40 и тоже нафаршированный функционалом) https://letsshop.com.ua/ (одежда) https://bolyar.com.ua/ (одежда, куртки женские) https://ager.ua/ (одежда) https://larionoff.com.ua/ (одежда) https://yasmi.com.ua/ (одежда) https://timeofstyle.com/ (одежда) https://lmm.in.ua/ (разные категории товаров) — Новый сайт в списке https://tep.ua/ (товары для дома, хоя, думаю, этот бренд не нуждается в представлении) https://loveyouhome.ua/ (товары для дома) https://elektreka.com.ua/ (электрика) ... { место зарезервировано под ваш сайт! Пишите адрес в комменты! } Крупные дропшиппинг-поставщики (товары разной направленности) https://msdrop.com.ua/ (має позитивні відгуки від декількох учасників спільноти) https://dropship-b2b.com.ua/ https://www.websklad.biz.ua/ http://texnano.com.ua/ Ну и другие поставщики с разбивкой по тематике Если поставщик специализируется на одной теме, большая вероятность встретиться непосредственно с прямым поставщиком, а в некоторых нишах даже с производителем. Авто тема https://ddtuning.com.ua/ Аксесуари (гаманці, сумки, годинникі тощо) https://supersumka.com.ua/ Детские товары https://royaltoys.com.ua/ — игрушки Поставщики дропшиппинг Украина одежда https://fashion-girl.ua/ https://milanova.com.ua/ https://glem.com.ua/ https://lurex.in.ua/ https://www.mardgleb.com/ https://www.zemal.com.ua/ https://sewel.ua/ https://kotton.com.ua/ https://fason-m.com.ua/ https://issaplus.com/ Дом, кухня, сад https://metelka.com.ua/ Дропшиппинг детская одежда https://valeotrikotage.com/ Гаджети и аксессуары, электроника https://aspor.ua https://smart-b2b.com.ua/ https://itsellopt.com.ua/uk https://megaslon.com.ua/ Зоотовары https://suziria.ua/ https://ua.bycollar.com/ Развлечения https://brushme.com.ua/ - картины по номерам https://yalinka-karpat.com.ua/ - новогодние елки Черный список дропшиппинг поставщиков по мнению форумчан Следующие сайты имеют отрицательные отзывы от участника сообщества в комментариях к этой статье. https://yavshoke.ua/ https://zima.com.ua/ https://tech-technics.com.ua/ P.S. Смотрю, что чуть ли не каждый второй-третий и четвертый дропшиппинг поставщик Украины работает на OpenCart. Ну оно и не странно, наверное. Нормальный движок, все таки, поэтому так. P.P.S. Дописывать буду по мере отклика желающих разместиться в списке
    2 points
  7. Доброго дня! Останнiм часом раз-пораз з'являються дискусії на тему застаріння CMS OpenCart. Порушимо і тут цю тему, але не розкритикувати, чи похвалити, а пропонуючи! Як розгорнути проект з сучасним FrontEnd ми писали ранiше... Зараз-же розглянемо посібник з прикладами, де спробуємо подружити Element Plus та OpenCart модуль для адмiн панелi Створемо додаток "Управління категоріями OpenCart" 1) Клієнтська частина ../upload/admin/view/template/extension/module/multi_categories.twig {{ header }} {{ column_left }} <div id="content"> <div class="page-header"> <div class="container-fluid"> <div class="pull-right"> <a href="{{ cancel }}" data-toggle="tooltip" title="{{ button_cancel }}" class="btn btn-default"><i class="fa fa-reply"></i></a> </div> <h1>{{ heading_title }}</h1> <ul class="breadcrumb"> {% for breadcrumb in breadcrumbs %} <li><a href="{{ breadcrumb.href }}">{{ breadcrumb.text }}</a></li> {% endfor %} </ul> </div> </div> <div class="container-fluid"> <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title"><i class="fa fa-pencil"></i> {{ breadcrumb_last }}</h3> </div> <div class="panel-body"> <div id="app"></div> </div> </div> </div> </div> <script type="module" crossorigin src="{{ base }}view/javascript/product_list.js"></script> <link rel="stylesheet" crossorigin href="{{ base }}view/javascript/product_list.css"> <style> html { height: auto } </style> {{ footer }} 2) Клієнтська частина source for Vue Element-plus ../src/App.vue <template> <el-tree style="max-width: 600px" :data="products" node-key="category_id" > <template #default="{ node, data }"> <span class="custom-tree-node"> <span>{{ data.sort_order }}) {{ node.label }}</span> <span> <a :href="openCategory(data)" target="_blank">_<i class="fa fa-pencil"></i></a> </span> </span> </template> </el-tree> </template> <script setup> import { onMounted, ref } from 'vue'; let productPath = ''; const products = ref({}); const openCategory = (data) => { return data.href.replaceAll('&amp;', '&'); } const appInit = (list = []) => { products.value = list; } const getProducts = async () => { if (!productPath) { console.debug('The error for determining the path to get data!'); } else { let response = await fetch( productPath ); appInit( await response.json() ); } } const appUrl = (route) => { // Sanitize the call route = route?.replace(/[^a-zA-Z0-9_\/]/i, '') || ''; if (!route) return ''; // production or development (variable set: in root on .env.development file) const inputUrl = new URL( import.meta.env.PROD ? document?.location?.href : import.meta.env.VITE_APP_HOME_PATH ); inputUrl?.searchParams?.set('route', route); return inputUrl?.href || ''; } onMounted(() => { productPath = appUrl( 'extension/module/multi_categories/get_categories' ); getProducts(); }) </script> <style> .custom-tree-node { align-items: center; column-gap: 9px; display: flex; flex: 1; justify-content: space-between } </style> 3) Контроллер ../upload/admin/controller/extension/module/multi_categories.php <?php // Read more: https://opencartforum.com/files/developer/678008-sha class ControllerExtensionModuleMultiCategories extends Controller { private $error = []; private $edit_link = ''; public function index() { $this->load->language('extension/module/multi_categories'); $this->document->setTitle( $this->language->get('heading_title') ); $this->getForm(); } protected function getForm() { $data = []; $data['header'] = $this->load->controller('common/header'); $data['column_left'] = $this->load->controller('common/column_left'); $data['footer'] = $this->load->controller('common/footer'); $this->response->setOutput($this->load->view('extension/module/multi_categories', $data)); } public function get_categories() { $this->response->addHeader('Content-Type: application/json'); if (!$this->validate()) $this->response->setOutput([]); $this->load->model('extension/module/multi_categories'); $categories = $this->model_extension_module_multi_categories->get_categories(); $this->edit_link = $this->url->link( 'catalog/category/edit', 'user_token=' . $this->session->data['user_token'], true ); $this->response->setOutput(json_encode( $this->tree( $categories ) )); } private function tree($list, $parent_id = 0) { $children_list = []; foreach ($list as $item) { if ((int)$parent_id !== (int)$item['parent_id']) continue; $item['children'] = $this->tree( $list, $item['category_id'] ); $children_list[] = $this->itemBuild( $item ); } return $children_list; } private function itemBuild($data = []) { $data['label'] = strip_tags(html_entity_decode( $data['name'], ENT_QUOTES, 'UTF-8' )); $data['href'] = $this->edit_link . '&amp;category_id=' . $data['category_id']; return $data; } private function validate() { if (!$this->user->hasPermission('access', 'extension/module/multi_categories')) { $this->error['warning'] = $this->language->get('error_permission'); } return !$this->error; } } 4) Модель ../upload/admin/model/extension/module/multi_categories.php <?php // Read more: https://opencartforum.com/files/developer/678008-sha class ModelExtensionModuleMultiCategories extends Model { public function get_categories() { $query = $this->db->query("SELECT cd.name, c.category_id, c.parent_id, c.sort_order FROM " . DB_PREFIX . "category c LEFT JOIN " . DB_PREFIX . "category_description cd ON (c.category_id = cd.category_id) WHERE cd.language_id = '" . (int)$this->config->get('config_language_id') . "' ORDER BY c.parent_id, c.sort_order, cd.name;"); return isset($query->num_rows) ? $query->rows : []; } } 5) Мова ../upload/admin/language/en-gb/extension/module/multi_categories.php <?php // Heading $_['heading_title'] = 'Categories'; Сучасні бібліотеки та фреймворки пропонують широкий вибір готового функціоналу, наприклад, ви легко можете додати можливість перетягування елементів <el-tree ... draggable ... > //... </el-tree> та події <el-tree @node-drag-start="dragStart" @node-drag-enter="dragEnter" @node-drag-leave="dragLeave" @node-drag-over="dragOver" @node-drag-end="dragEnd" @node-drop="drop" > // ... </el-tree> та з легкістю створити модуль зручного сортування дерева категорій перетягуванням OpenCart методом "Drag and Drop". А з нашими прикладами ще й безкоштовно!!! Дякуємо, за прочитання! Тут ви зможете знайти наші модулі, та щє-й зі знижкою!) якщо використаєте купон 678008-30 Ось що вийшло Скачати готовий модуль! пропозиції та оцiнка вітаються!)
    2 points
  8. Понимаю, что немного не по теме интернет-магазинов и Opencart, но все же думаю, что эта тема будет интересна многим из нашего дружного сообщества. Ещё давным-давно, когда я занимался только SEO-продвижением сайтов мне пришла идея сделать сайт под продажу ссылок или же под рекламу Google AdSense. Я сделал такой сайт, выполнил все требования, чтоб стать партнером Google, когда увидел заветное "Ваш сайт прошел модерацию, зарабатывайте свои миллионы", я чуть не упал со стула от восторга. Но со временем я начал понимать, что все это не сильно прибыльная штука. У меня был сайт с разборами песен, аккордами и табами для игры на электрогитаре. Контент был на ру и украинском языках. Естественно большая часть трафика была из рф. Так вот за год я заработал свои заветные 6 долларов и забил на это дело) Но в своей практике я все больше начал натыкаться на сайты клиентов, которые заточены под рекламу, они были готовы платить большие деньги за доработки и SEO и говорили, что это их основной доход. Сайты были на англ языке. Итак у меня созрело несколько вопросов: 1. Среди нас есть те, кто занимается подобным искусством? 2. Если да, то как вам результат и есть ли смысл начинать подобный проект в 2024 году (понятное дело, что он будет или полностью на украинском или англ языке)? 3. Много инфы по поводу того, что гугл понижает сайты с контентом от Chat, но если он будет качественным и немного подредаченным, то есть вероятность того, что Гугл не будет ругаться на подобное? Если начну что-то подобное, то буду дополнять эту тему результатами. Всем спасибо за внимание) Думаю, что очень многие мечтают о пассивном доходе) Пишите все, что думаете по этому поводу в комментариях.
    2 points
  9. Нещодавно мав дуже неприємну розмову з одним крадієм, якій у своїй зухвалості дійшов до того, що відкрито вказує на своєму сайті про те, що викладає чужі модулі, бо наче має право згідно ліцензії GPL самого OpenCart. З урахуванням того, що OpenCart дійсно розповсюджується під вільною ліцензією GPLv3, можна навіть нав'язати ідею, що це автори з форуму порушують ліцензію, накладаючи обмеження на використання своїх творів. Але давайте почитаємо щось трохи більше, ніж “GPL — це вільна ліцензія, яка дає право на розповсюдження коду”, адже там 11 сторінок тексту https://www.duralex.org/licenses/gplru.pdf (російською мовою). Також є ціла купа тексту тлумачень. І в додаток існують певні норми законів (на хвилиночку!). На що діє ліцензія насправді Ліцензія діє на оригінальні програми, модифіковані версії та похідні роботи. В основному все розглядається в контексті похідного коду, але не дизайну (!). А що там про розширення? В тексті самої ліцензії — в принципі майже нічого не сказано. Слово "extension" зустрічається в тексті ліцензії лише раз в частині 5: Збірка охопленого твору разом з іншими окремими та незалежними творами, які за своєю природою не є його розширеннями і не поєднані з ним таким чином, щоб утворювати більшу програму, у томі носія зберігання або розповсюдження називається "агрегатом", якщо сама збірка та її авторське право не використовуються для обмеження доступу або правових можливостей користувачів збірки понад те, що дозволяють окремі твори. Ну і ще цікавий момент: Включення охопленого (ліцензією - п.а.) твору до агрегату не спричиняє поширення цієї Ліцензії на інші частини агрегату. Цей абзац наводить мене на думку, що подібна збірка, де є програма з розширеннями + незалежний компонент має постачатися у вигляді "все разом". І навіть в таком вигляді, допускається включення незалежних та захищених частин. Але модулі з форуму НЕ постачаються разом з Опенкартом, і відповідно, вони не утворюють єдиного продукту на одному носії, якщо говорити про "доставку" до клієнта. В тлумачені сказано: якщо основна програма динамічно підключає плагіни, і вони взаємно викликають функції та обмінюються структурами даних, ми вважаємо, що вони утворюють одну єдину комбіновану програму, яку слід трактувати як розширення основної програми, так і плагінів. Якщо основна програма динамічно підключає плагіни, але комунікація між ними обмежена викликом функції 'main' плагіна з деякими опціями та очікуванням її повернення, це є прикордонним випадком - https://www.gnu.org/licenses/gpl-faq.ru.html#GPLPlugins В цьому контексті цікава справа MySQL vs. Progress Software. Суд вирішив, що динамічне зв'язування само по собі не робить програму похідною роботою, якщо вона не містить копій коду з бібліотеки MySQL. Коротше кажучи, навіть в розумінні GPL, якщо створити окрему бібліотеку модуля та помістити там низькорівневу рутину (без використання методів OpenCart), то така бібліотека "не заражається" ліцензією GPLv3 і може мати окрему ліцензію та не підлягає вільному розповсюдженю. Але мова не лише про бібліотеку. На що GPL однозначно НЕ діє У поясненнях до ліцензії GPLv3 чітко вказано, що вона НЕ розповсюджується на: Шрифти (https://www.gnu.org/licenses/gpl-faq.ru.html#FontException) HTML (Як особливе виключення до GPL, будь-який HTML-файл, який просто викликає функції з цього коду і для цієї мети включає його за посиланням, вважається окремою роботою з точки зору авторського права.) https://www.gnu.org/licenses/gpl-faq.html#WMS Стилі - тут й самому тупому варезнику має бути зрозумілим, що жоден кастомний файл стилів не наслідує нічого від опенкарт, він надає інструкції напряму браузеру, і підключається через HTML Декларативні файли install.xml - по такій логіці не є програмою взагалі. Він ж декларативний. Документація (керівництва, інструкції) не є вихідним кодом і на них не накладаються ті самі умови розповсюдження - https://www.gnu.org/licenses/gpl-faq.ru.html#WhyNotGPLForManuals. З точки зору закону це також окремий авторський твір Зображення (іконки) - це точно не Source Code програми Прямо вказано, що деякі плагіни НЕ утворюють тісне зв'язування, і, відповідно, на них ліцензія не розповсюджується - https://www.gnu.org/licenses/gpl-faq.ru.html#GPLPlugins В багатьох випадках куплені модулі містять комбінацію різного роду файлів, і деякі з цих файлів 100% не підпадають під вільне розповсюдження. Тому не потрібно розповідати казочки про те, що вільна ліцензія виправдовує крадіжку. Чи дозволяється використання комбінованих ліцензій? Так, основним нововведенням GPLv3 (у порівнянні з GPLv2), є можливість поєднання різних ліцензій. Тобто різні частини тієї самої програми можна ліцензувати на різних умовах, в тому числі можна використовувати комбінацію GPLv3 та пропрієтарної ліцензії на окремі частини модуля. В ліцензії є чіткі умови розповсюдження вільного коду Ліцензія не лише надає права, але й вимагає виконувати деякі зобов'язання. Тож навіть до тих творів, до яких дійсно застосовується принцип вільного розповсюдження, накладаються певні вимоги. В контексті збереження вказання авторства В пункті 4 ліцензії зазначено, що в разі розповсюдження, необхідно передавати “точну копію”. Ви можете передавати точні копії вихідного коду Програми, як ви його отримуєте, на будь-якому носії, за умови, що ви явно і належним чином розміщуєте на кожній копії відповідне повідомлення про авторські права; Пункт 5 ліцензії каже, що в разі модифікації: а) Робота повинна містити помітні повідомлення, які вказують, що ви її змінювали, та наводять відповідну дату. Окрім того нагадаємо, що згідно закону про авторське право існують майнові та немайнові права. Загалом концепії копілефт не посягають на немайнові права автора. А в законі чітко прописано, що право на ім'я є невідторгним. Тому “відповідне повідомлення про авторські права” має містити не лише сповіщення про GPL, а й сповіщення про авторство на будь якій копії модуля. В контексті продажу власного коду Також потрібно повідомляти, що саме з якою ліцензією розповсюджується. Яку юридичну силу має ліцензія GPLv3? Адепти GPL стверджують, що пропрієтарні ліцензії “перетягують ковдру” до правовласників, порушуючи права користувачів. А звідки ми знаємо, що вільна ліцензія не робить те саме в іншу сторону? Чому ми маємо сліпо довіряти тексту ліцензії, яку написала одна сторона, якщо ми знаємо, що ця сторона напряму зацікавлена в пропаганді певної ідеї? Варто задуматися, а що взагалі означає слово “ліцензія”. По суті це договір ліцензування між двома сторонами (правовласник та користувач). Як ми розуміємо, будь якій договір має відповідати місцевому законодавству. На профільному сайті, якій пояснює ідеї “копілефту”, розміщена ціла стаття про похідні твори (https://copyleft.org/guide/comprehensive-gpl-guidech5.html), в якій визнається, що положення ліцензії GPL можуть вступати в конфлікт з національними законами. Там прямо написано: “Саме похідний характер твору щодо вільного програмного забезпечення вимагає дотримання умов відповідної ліцензії. Тому виникає питання: що саме є "похідним твором"?” Також там визнається, що суди США більше схиляються до визначення “похідного твору” в законі про авторське право і мають власні методи оцінки похідності. І в законі США, і в українському законі про авторське право програми розглядаються як "набір інструкцій", а не як місце в пам'яті. Так само в обох юрисдикціях є чітке визначення похідного твору, якій має бути "заснований на одному або кількох попередніх творах" (в законі США) та "твір, що є творчою переробкою іншого існуючого твору" (в законі України). Подібним чином похідні твори визначає й eстонське законодавство (форум зареєстрвоаний саме в цій юрисдикції). Тобто, похідний твір з точки зору законодавства не може взятися з нізвідки, він має бути зміненою версією чогось, що вже існувало. І якщо такого модуля не було в опенкарті, то він вже не може вважатися похідним твором. А закон має більшу юридичну силу, ніж ліцензія GPL. Тому визначення похідних творів в розумінні GPL не відповідають дійсному законодавству (!) Також викликає сумніви тон тлумачень ліцензії GPL. Вони всюди, де прямо не застосовується GPL, рекомендують просто надавати інші дозвільні ліцензії, уникаючи фраз, що насправді, якщо може застосовуватися інша ліцензія, то вона може бути і пропрієтарною. А це вже викривлення фактів на користь конкретної ідеї. Як визначають похідність творів суди США на практиці? У вже згаданій статті на сайті copyleft.org надається опис того, як суди в США визначають, чи є програма похідною чи ні. Першочергово відсіюється все, що не може бути захищеним. Адже авторське право не поширюється на ідеї, процедури, процеси, системи, методи роботи, концепції або принципи, що містяться в оригінальній програмі. Саме реалізація цього правила, що вимагає "відфільтрувати" непідлягаючі захисту елементи, є найчастішою причиною розбіжностей у судовій практиці. Наскільки я зрозумів вміст описаних методів, похідні твори визначаються за 2 одночасними критеріями: Програма має подібний декларативний опис Виконувані функції мають подібне вираження в коді Суд аналізує, чи скопійовані захищені елементи і наскільки вони суттєві у загальній структурі програми. Якщо захищені елементи, що містяться у другій програмі, є значущими, вона визнається похідною від першої. Якщо ж скопійовані елементи є незначними, друга програма не вважається похідною. На основі цієї статті в мене взагалі виникло враження, що похідність програми стає правовим питанням, коли в одну програму скопійовано код з іншої майже рівноцінної програми, і вони є конкурентними... Або інша площина — це коли виробники технічних пристроїв беруть за основу код під GPL, і при цьому забороняються користувачам вносити зміни, тобто привласнили чужий готовий код і модифікували його так, щоб заборонити його редагування, ну або не надали похідний код (справи BusyBox, Tivo, Samsung). Немає захисту для «методів роботи» Найбільш цікавий пункт тієї статті на сайті copileft.org — “Немає захисту для «методів роботи»”. Тобто "дослівне копіювання ієрархії команд меню або будь-який інший «метод роботи» не може бути основою для визначення того, що одна робота є похідною від іншої". Найцікавішим прикладом застосування цього принципу є справа Oracle проти Google. Верховний Суд США ухвалив рішення на користь Google, визнавши використання Java API "добропорядним використанням" (fair use), хоча ухилився від остаточного визначення, чи є декларативний код незахищеним як "метод роботи", залишивши це питання відкритим. Але юридично більш однозначною в цьому питання є справа SAS Institute проти World Programming Ltd (C-406/10) в юрисдикції ЄС, коли суд постановив, що одна програма може вільно використовувати API іншої програми, бо це є використання функціональності програмного забезпечення без копіювання його коду. Також варта уваги справа VMware проти Крістофа Хеллвіга (2015–2019, Німеччина). Там була ситуація, що в одному продукті одночасно містився і закритий власний код, і відкриті частини коду Linux. Суд не дійшов до висновку що продукт VMware був похідною роботою. Тобто з модулями ми маємо таку штуку: Ніде в тексті ліцензії чітко не визначений статус розширень, які постачаються окремо від самої програми. А трактування адептів GPL — це лише їх власна думка, не підтверджена жодним судовим прецедентом Модуль не йде у порівняння з декларативним описом всієї системи, тобто він виконує якусь іншу функцію, яка іноді навіть відсутня в самому OpenCart Маємо в модулях виклики системних методів OpenCart ($this->db->query(), $this->load->model() тощо), і це можна розглядати як виклик "методів роботи" (методів системного API), а не як копіювання безпосередньої реалізації цих методів (назва методу ≠ його реалізація) Власна логіка деяких модулів суттєво більша, ніж виклик стандартних методів OpenCart в тому модулі Таким чином, ми можемо стверджувати, що модуль, якій не має модифікатору — може не вважатися похідним твором від опенкарту. А потім спадає на думку, що OCMOD — це визначений системою API. Адже API — це не лише про REST API (!). Як зазначено у Wikipedia, у багатьох випадках API є частиною набору розробки програмного забезпечення (SDK), якій в свою чергу є набором “засобів розробки, утиліт і документації, який дає програмістам змогу створювати прикладні програми за визначеною технологією або для певної платформи”. Хоча, очевидно, що в разі, коли модуль вмішується в логіку стандартних функцій опенкарту, то він змінює поведінку програми та використовує спільний простір пам'яті. З точки зору GPL — це важливо, а ось з точки зору законодавства — ні. Суперечливий статус модулів В тексті ліцензії слово plugin в принципі не зустрічається, хоча саме про плагіни надається тлумачення. Проте варто пам'ятати, що тлумачення саме по собі наче й немає юридичної сили. Також у визначеннях є термін "Охоплений твір", на якій діє ліцензія. Це означає або немодифіковану Програму, або твір, створений на основі Програми. Ніяких розширень, які можуть бути встановлені в програму окремо, там не згадується. Окрім того за визначенням "Модифікувати" твір "означає скопіювати або змінити весь твір чи його частину у спосіб, що потребує дозволу відповідно до законодавства про авторське право, за винятком створення точної копії. Отриманий твір називається "модифікованою версією" початкового твору або твором, "створеним на основі" початкового твору". Так, можна сказати, що модулі модифікують твір. Але в момент передачі клієнту модуль — це окремі файли, які ще нічого не модифікували і не є частиною існуючої на той момент програми на сервері клієнта. Згідно закону — це окремий твір зі своїм набором інструкцій. Розробник не створює у своєму файлі модифікацію системи опенкарт. Ядро модифікується лише після встановлення користувачем в систему. Хоча наразі не існує судового прецеденту, якій би поставив крапку саме в аспекті автоматичного застосування GPL до розширень та шаблонів, доволі красномовним є той факт, що навіть глибоко віруючи прихильники GPL (на кшталт Метта Мулленвіґа) ніяк не наважаться на судову справу проти маркетплейсів або авторів шаблонів саме в питаннях дотримання цієї ліцензії (наприклад проти Envato або Кріса Пірсона), хоча в інших питаннях вони йдуть до суду. Це дає підстави припустити, що навіть ці найвідданіші адепти розуміють юридичну сумнівність примусового застосування GPL до розширень та шаблонів... Унікальна особливість українського законодавства (!) Стаття 33 закону України Про авторське право і суміжні права чітко вказує, що передача права на використання (тобто ліцензія) вважається дійсною лише в разі, якщо: Угода заключена в письмовій формі (+ формат договору приєднання з чіткою ідентифікацією сторін) Автор отримав авторську винагороду Цей пункт фактично відміняє посягання GPL на вільну передачу модулів, навіть в тому разі, якщо колись ми таки отримаємо прецедент, що в якійсь іншій країні суд зобов'яже авторів розширень випускати свої твори під тієї самою ліцензією, що й сама система. P.S. Важливо чітко розуміти, що навіть якщо OpenCart (продукт А) поширюється за ліцензією GPL, то це ще не означає, що модулі (Продукт Б) також повністю підпадають під цю ліцензію і не містять захищених авторським правом частин. Тож якщо ви крадете, це ваш вибір. Але GPL тут взагалі ні до чого!
    1 point
  10. Відгуки про SendPulse SendPulse — це платформа, яка допомагає бізнесу спілкуватися з клієнтами через email, SMS, push-сповіщення та месенджери. Я хочу створити на цій сторінці простір для відгуків про SendPulse, щоб кожен міг поділитися враженнями. Що нам пропонує сервіс: Багатоканальність: об’єднує всі канали зв’язку в одному місці. Автоматизація: налаштуйте ланцюжки повідомлень із Automation 360. Простота: конструктор без кодування для листів і лендінгів. Інтеграція: є безкоштовний модуль для OpenCart та підтримка інших платформ. Доступність: безкоштовний тариф до 15 000 листів на місяць. SendPulse ідеально підходить для малого та середнього бізнесу. Кому цікаво почати роботу з сервісом, можу поділитися знижкою. Діліться своєю думкою нижче у коментарях.
    1 point
  11. По умолчанию фильтр сортирует подобные значение "до 2 м" > "до 1000 м" как строки Проблема возникает из-за того, sql запрос "до 2м" + 0 возвращает 0 Задача найти блоки фильтра в которых все значения содержат числа и отсортировать их в правильном порядке Написал скрипт, который находит фильтры имеющие только числовые значения и проставляет им необходимую сортировку Перед тестирование скрипта делайте бекап таблицы oc_ocfilter_filter_value function sort_ocfilter() { $pattern = '/\-?\d+([.,]\d+)?/'; $final_groups = []; $groups = []; $batchSize = 5500; $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "ocfilter_filter_value fv LEFT JOIN " . DB_PREFIX . "ocfilter_filter_value_description fvd ON (fv.value_id = fvd.value_id AND fv.source = fvd.source) WHERE fvd.language_id = '" . (int)$this->config->get('config_language_id') . "'"); foreach ($query->rows as $row) { if (array_search($row['filter_id'], $exclude_filter_id) === FALSE) { $groups[$row['filter_id']][] = $row; } } foreach ($groups as $key => $group) { $numeric = 0; $alphabet = 0; foreach ($group as $val) { if (preg_match($pattern, $val['name'], $matches1)) { $numeric++; } else { $alphabet++; } } if ($alphabet != 0) { continue; } uasort($groups[$key], function ($a, $b) use ($pattern) { if (preg_match($pattern, $a['name'], $matches1) && preg_match($pattern, $b['name'], $matches2)) { return (float)str_replace(',', '.', $matches1[0]) <=> (float)str_replace(',', '.', $matches2[0]); } else { return $a['name'] <=> $b['name']; } }); $final_groups[$key] = $groups[$key]; } foreach ($final_groups as $group) { $counter = 0; foreach ($group as $val) { $counter++; $sqlQueries[] = "WHEN value_id = " . $val['value_id'] . " THEN " . $counter . " "; //$this->db->query($sql); } } $chunks = array_chunk($sqlQueries, $batchSize); foreach ($chunks as $batch) { $combinedQuery = implode(" ", $batch); $combinedQuery = "UPDATE " . DB_PREFIX . "ocfilter_filter_value SET sort_order = (CASE " . $combinedQuery . " ELSE sort_order END)"; $this->db->query($combinedQuery); } echo "<PRE>";var_dump($final_groups); }
    1 point
  12. Все мы знаем что вокруг системы опенкарт крутится огромное количество всяких чертей и мошенников, которые воруют у разработчиков модули и продают их типа на своих "легальных" площадках Но вот эта ситуация прямо сильно меня возмутила Есть такой сайтик https://d***v-op****cart.com/ во главе с каким-то мутным хером который пытался угрожать мне в личку что он выложит все мои модули Начинался с ломаного симпла, ну это хоть как-то можно оправдать во время войны мол русня стырим ниче страшного. Но потом стали тырить и уже все у своих, мне кажется что кто-то немного охренел Ребятки даже открыли ФОП ) Сорокін Дмитро Ігорович UA903220010000026004320073989 ИНН 3485102952 Чем продолжают копать себе ямку) Также там трутся очень многие ребята с нашего форума, кстати сегодня там перекличка, ищут "крыс" кто рассказывает людям про то что это ссаные пираты и там нет ни одного легального дополнения Кто-то любит экономить 200 грн и получить хз знает что, закодированное мутными херами и без поддержки Самое интересное что некоторые модули там стоят дороже чем легальные от автора, но не суть самый топ это Новая Почта и Укрпочта от @Prorab337 Microdata pro от @Exploits Примерно все модули от @chukcha Некоторые модули от @SergeTkach Мультиязык от @markimax Ocfilter в личке от @SooR Список большой Всем кто хочет сэкономить рекомендую прочитать эту статью А что вы думаете по этому поводу? PS Некоторые козыри есть у нас в рукаве и до определенного момента мы их показывать не будем, но я рекомендую задуматься
    1 point
  13. Мене звуть Ярослав. Я власник декількох інтернет магазинів і офлайн магазинів і островків у торгових центрах України. Перший мій магазин був на WP, але то було прям дуже давно і не правда. Згодом я перейшов на OpenCart (радувався як дитя). Але більш поглинаючись і розвиваючись приходилось все частіше звертатися до сторонніх розробників. Щось вивести, якийсь модуль адаптувати і ще багато подібного. В 2013 я відкрив свій перший офлайн магазин, і у мене стояла задача вести облік товарів. Тоді на мій шлях попалась програма для ПК GrossBee, я вічливо називав її "Бджілка". Так тоді вже була 1с, але я як і всі на початку вважав, що вона сильно велика для мене (багато непотрібного функціоналу). Мучаючись з синхронізацією своєї Бджілки з OpenCart (доді доводилося робити звіт в ексель, додавати розділювачі, переносити в csv, змінювати кодування та імпортувати залишки через якийсь модуль). І це приходилося робити декілька разів на день. Короче кажучи бідаааа. Буквально через 3 місяці такої роботи я вже шукав готові рішення та розробника. Але готового не було, а робробники або не могли або не хотіли або за мільйон долларів. Тоді я почав вивчати PHP і знаючи, що саме мені потрібно почав писати собі облік товарів всередині OpenCart. Враховуючи, що досвіду взагалі не було - писав як міг (вся система була на основі костилів які працювали за рахунок інших костилів). Йшли роки, я потрошку розвивав функціонал, звіти, доступи для продавців та дуже багато іншого (тисячі строчок коду та тестів). В результаті мої магазини почали працювати на 95% автоматично (замовлення товарів на підставі залишків і продажів, розрахунок і видача зарплатні і бонусів співробітникам, необхідна сумма автоматично списувалась з моєї карти ПриватБанку і зараховувалась на карту співробітника). Короче кажучи було прям ідеально. Також більшість моїх знайомих знали, а деякі і бачили програму. Вона в принципі робить все автоматично, і працювати можна через звичайний браузер. І частенько просили налаштувати для них таку ж. Але я відмовляв, бо написано дуже криво і дуже індивідуально (все писалося напряму в код, під особисто мої потреби та без налаштувань). А також все це ще був OpenCart 1.5 В результаті моєю мрією стало переписати для актуального OpenCart та з чистим кодом (вже дойшов до певного рівня та досвіду, коли не стидно за свій код). Але то бажання не було то часу. І тут вирішив всеж таки ввязатися в це дійство. І назва йому FISHKA))) Про Модуль 1.0 Fishka - Облік товарів всередині OpenCart Головне з чого треба почати, що цей модуль в залежності від попиту і потреби буде постійно оновлюватись. Базовий Функціонал модулю 1.0 Fishka - Облік товарів всередині OpenCart Підрозділи (тож саме що і склади або магазини) - тобто місце де є залишки товарів, і звідти можна робити продажі, переміщення тощо. Створення Підрозділу Редагування Назви Підрозділу, Статусу та головної каси Перегляд залишків товарів по підрозділам Каси - тут я думаю все зрозуміло, якщо ні, подивіться або поклацайте на демо магазині. Створення кас Редагування Назви Каси та Статусу Перегляд балансів Перегляд виписки по касі (щоб розуміти чому саме такий баланс) Додавання операцій вручну (наприклад списання прибутку, або любих інших операцій). Допускається як мінус так і плюс. Приходні Накладні - тут я думаю все зрозуміло, якщо ні, подивіться або поклацайте на демо магазині. Створення Приходних Накладних Перегляд створених Приходних Накладних Перегляд конкретної Приходної Накладної Відображення автору документу, типу документу та підрозділу Видатні Накладні (Розхідні Накладні) - тут я думаю все зрозуміло, якщо ні, подивіться або поклацайте на демо магазині. Створення Видатних Накладних Перегляд створених Видатних Накладних Перегляд конкретної Видатної Накладної Відображення автору документу, типу документу та підрозділу Переміщення - тут я думаю все зрозуміло, якщо ні, подивіться або поклацайте на демо магазині. Створення Накладних Переміщення Збереження автору документу, типу документу та підрозділу Звіти (Функції) - тут і буде вся магія. На підставі створених документів (касові операції, приходні та видатні накладні, переміщення). Можна створювати або замовляти звіти або функції. Базовий звіт -> Продажі по Дню - Базовий звіт (подивіться або поклацайте на демо магазині) Базовий звіт -> Товари - відкривається на натисканні на ID товару (відображення руху товару - коли, за скільки купили, коли продали, скільки залишилось, скільки заробили) (подивіться або поклацайте на демо магазині) Базовий звіт -> Продажі по Товарам - Це звіт по проданим товарам з фільтрами (подивіться або поклацайте на демо магазині) Звіти можуть бути розроблені платно чи безкоштовно пишіть мені ЗВІТИ БУДУТЬ ПОСТІЙНО ДОДАВАТИСЬ!!! Увага в данний момент модуль знаходиться в режимі постійних оновлень функціоналу!
    1 point
  14. Salesdrive - Українська CRM-система, яку можна безкоштовно зв'язати з OpenCart / ocStore На форумі є відгуки про KeyCRM, але немає про Sales. Salesdrive надають рідний безкоштовний модуль інтеграції з відкритим кодом та оперативну підтримку. Напишіть хто працював з даною CRM, які враження про систему, модуль, підтримку
    1 point
  15. Якщо ви використовуєте панелі управління серверами VestaCP/Hestia в режимі Nginx+php-fpm без Apache (то ви вже молодці :). І плануєте встановити (або вже використовуєте) багатомовність з префіксами виду /uk/url.html то перед вами постане одна проблема, яка пов'язана з кривим дефолтним конфігом Nginx в цих панелях. Проблема полягає в тому що сервер не буде обробляти запити типу /uk/index.php?route= і видаватиме помилку 404. Лікуємо. Перед if (!-f $document_root$fastcgi_script_name) { return 404; } в location ~ [^/]\.php(/|$) { потрібно записати if (!-e $request_filename) { rewrite ^/(.+)$ /index.php?_route_=$1 last; }
    1 point
  16. Надоело во время разработки дергать обновление модификаторов перед каждым обновлением страницы ? Я нашел в себе силы и написал решение. работает по хоткею CTRL + B на витрине сайта Единственное условие для работы модификатора - нужно быть авторизованным в админке https://github.com/kjpower/Refresh-opencart-page-with-modif/blob/main/upload/system/refresh_page_with_modif.ocmod.xml работает на 2.3.х и 3.х если полезно то ставь лайк и беги покупать мои дополнения
    1 point
  17. Я хочу затронуть тему очередей, которые используются повсеместно в интернет-проектах, но в Opencart данная тема особо не освещена. Представьте, что вам нужно прямо сейчас сделать выгрузку для какого-то маркетаплейса, вот только в выгрузке у вас, например, 50к товаров. Крон у вас работает раз в сутки, а менеджер магазина не подключится по ssh и не будет запускать консольный скрипт выгрузки. В подобной ситуации вам поможет система очередей. Очереди - это не конкретная технология, а только принцип. В самом примитивном виде у нас есть издатель и подписчик (producer и worker). Издатель добавляет в очередь данные, по сути инициирует выполнение какого-то действия. А воркер непосредственно выполняет нужную нам работу. Между ними присутствует обменник, который хранит сообщения и распределяет их между воркерами. При этом мы можем использовать несколько воркеров (обработчиков) для одной задачи. Где можно использовать очереди 1. Асинхронное (фоновое) выполнение задач. Допустим, что при оформлении заказа мы отправляем письма о новом заказе через внешний SMTP-сервер, админу кидаем уведомление в телеграм, а также экспортируем данные о заказе в CRM. Какие проблемы нас поджидают: синхронное выполнение каждого процесса - пользователь будет ждать, пока отработает последовательно каждый из процессов зависимость от внешних сервисов - они вполне могут работать нестабильно и все это время клиент будет видеть экран загрузки, вместо быстрого оформления заказа. Вместо этого мы создаем три отдельных очереди (для email, телеграма и CRM) и просто кидаем в них сообщения. Пользователь успешно оформляет заказ, а в это время процессы отправки писем и уведомлений выполняются параллельно в фоне. Также можно: генерировать выгрузки в маркетплейсы по запросу пользователя, делать импорт/экспорт товаров, строить карты сайта, отчеты и т.д. 2. Имитация многопоточности. PHP "из коробки" однопоточный, но с помощью очередей можно сделать несколько обработчиков одной задачи. Примеры: Импорт товаров - у нас есть исходный эксель-файл с товарами. Распарсить файл и получить данные можно довольно быстро, а вот импорт данных потребует больших ресурсов, т.к. нужно выполнять вставку данных во множество таблиц. Исходя из этого разбиваем процесс импорта на два этапа: на первом этапе парсим эксель файл и в результате парсинга каждой строки получаем результирующий набор данных, нужный для создания товара. Этот набор данных мы кидаем в очередь для импорта. создаем обработчик, который будет заниматься только импортом готовых данных. При этом мы можем запустить одновременно 5 таких обработчиков (или сколько позволят ваши ресурсы) и система очередей будет распределять задачи по импорту товаров между данными обработчиками. В итоге вместо построчного импорта товаров мы получаем 5 параллельных обработчиков импорта. Данную технику можно применить для парсинга товаров. От теории перейдем к практике. Система очередей на RabbitMQ Выше я уже писал, что система очередей - это лишь архитектурный принцип и он может быть реализован с помощью множества технологий: крон и база данных, хранилище типа Redis и т.д., но я выбрал в качестве примера специалированное ПО RabbitMQ. Кролик содержит множество возможностей: гибкую маршрутизацию, масштабируемость, хранилище сообщений и т.д. Я использую Docker в качестве среды для локальной разработки, поэтому покажу, как установить RabbitMQ именно в этой среде. Про Docker я писал в этой статье: https://opencartforum.com/blogs/entry/383-zapusk-i-otladka-opencart-s-pomoschyu-docker-i-xdebug/ Установка RabbitMQ с помощью docker compose 1. В docker-compose.yml добавляем сервис: rabbitmq: image: rabbitmq:3-management-alpine hostname: my-rabbit volumes: - ./rabbitmq/etc/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf - ./rabbitmq/data:/var/lib/rabbitmq/mnesia/rabbit@my-rabbit - ./rabbitmq/logs:/var/log/rabbitmq/log ports: - 5672:5672 - 15672:15672 2. В корневой директории Docker-проекта создаем папку rabbitmq и в ней создаем три папки: data, etc, logs для хранилища данных, конфигов и логов. В папке rabbitmq/etc создаем файл конфига rabbitmq.conf с содержимым: loopback_users.guest = true listeners.tcp.default = 5672 management.listener.port = 15672 management.listener.ssl = false Далее как обычно билдим все в Docker для установки нового сервиса и запускаем контейнеры: docker-compose up -d --build Для администрирования RabbitMQ поставляется с панелью, которая будет доступна по адресу: http://localhost:15672. В данной панели можно смотреть, какие очереди выполняются, получить сообщения, очистить очереди т.д. Пароль и логин для доступа: guest / guest. Естественно, на рабочем сервере нужно удалить гостевого пользователя и добавить нового админа. Использование RabbitMQ c Opencart Написал простую библиотеку https://github.com/ozzzi/opencart-rabbitmq Для ее работы нужно: 1. Установить зависимость: composer require php-amqplib/php-amqplib . В библиотеке предполагается, что папка vendor будет лежать на уровень выше, чем корневая директория opencart. 2. Добавить в config.php конфиг RabbitMQ: define('RABBITMQ_HOST', 'host'); define('RABBITMQ_PORT', '5672'); define('RABBITMQ_USER', 'guest'); define('RABBITMQ_PASS', 'guest'); Если речь идет о исполнении в докер-контейнере, то в качестве хоста нужно указать айпи контейнера (читаем предыдущую статью о докере). Затем в опенкарте можем использовать библиотеку: $this->load->library('queue'); $this->queue->addTask('queueName', ['some' => 'data']); Первый параметр в методе addTask - имя очереди, а второй параметр (опциональный) - данные, которые нужно передать в воркер. Примеры Далее я приведу примеры использования очередей для отправки почты, импорта товаров из Excel, и парсинга сайтов. 1. Отправка email Полный код: https://github.com/ozzzi/email_service В коде Opencart-а меняем родной код отправки email-ов на: $this->load->library('queue'); $this->queue->addTask('email', ['data' => 'examlpe_data']); Т.е. мы добавили в очередь email данные для обработки: кому, от кого и что отсылаем. За обработку данной очереди отвечает воркер: cli/email_notify.php. Вы должны сами реализовать получение конфигов для SMTP-сервера. В панели управления RabbitMQ можно увидеть, что во вкладке очередей появилась новая очередь и одно сообщение. Чтобы очередь его отработала, воркер cli/email_notify.php должен быть запущен. Для примера мы запустим его вручную внутри контейнера, а в конце я объясню, как сделать запуск автоматическим. Смотрим имена контейнеров: docker ps Нас интересует, контейнер с PHP-FPM. Чтобы войти в него выполняем: docker exec -it container_name bash Далее переходим в папку cli и стандартно запускаем скрипт: php email_notify.php Если все прошло успешно, в панели кроля вы увидите, что очередь email очистилась. Значит сообщение ушло получателю. 2. Импорт товаров из Excel Полный код: https://github.com/ozzzi/excel_import_worker В админской части вам нужно реализовать загрузку файла, передать конфиг настроек полей и подключить загрузчик композер: '/vendor/autoload.php' Затем реализовываем наш загрузчик (producer): use App\Service\ExcelParser; $file = 'price.xlsx'; $setting = [ 'category' => 'B', 'name' => 'C', 'model' => 'D', 'price' => 'G', 'quantity' => 'H', 'manufacturer' => 'F', 'description' => 'M', ]; $this->load->library('queue'); $excelService = new ExcelParser($setting, $file); foreach ($excelService->parse() as $product) { $this->queue->addTask('import', ['product' => $product]); } В итоге после чтения каждой строки мы добавляем в очередь import объект с данными товара. Чтобы обработать все сообщение из очереди "импорт" запускаем воркер: cli/product_import.php (App\Service\ProductImport - вам нужно реализовать самим ). Чтобы процесс пошел "бодрей", можно запустить параллельно несколько воркеров product_import.php. 3. Парсер сайта в несколько потоков Полный код: https://github.com/ozzzi/html_parser_worker Для примера возьмем первый попавшийся магазин стройматериалов (ссылка в коде) и спарсим все товары из категории "Сухие смеси". Разделим процесс парсинга на два этапа: Получение списка ссылок на карточки товаров. За один запрос мы можем получить все ссылки на товары для первой страницы. Если страниц пагинации в категории 5 и в среднем на загрузку страницы уходит 1 секунда, то за 5 секунд мы получим все ссылки на товары в данной категории. Парсинг страниц товаров в несколько потоков. Т.к. каждая карточка товара загружается условную секунду, то данный процесс нам нужно распараллелить, запустив несколько воркеров. Запускаем скрипт cli/parser_category.php, который будет отправлять в очередь parse_product ссылки на товары. Затем запустим несколько воркеров cli/import_worker.php. В результате ProductParser возвращает сущность Товар, которая импортируется с помощью сервиса ProductImport, с которым вы уже знаете, что делать. Надеюсь, что вы поняли, как применять очереди в таком простом виде. Теперь расскажу, как запускать воркеры как демоны в автоматическом режиме (чтобы они работали постоянно и перезапускались сами вместе с системой). Один из способов - supervisor. Установка для Debian\Ubuntu стандартная: apt-get install supervisor Добавляем конфиг для запуска конкретного скрипта. Например, добавим конфиг для запуска воркера в виде двух процессов: Создаем файл: /etc/supervisor/conf.d/worker.conf [program:worker] command=php /path/to/script.php stdout_logfile=/var/log/worker.log autostart=true autorestart=true user=www-data stopsignal=KILL process_name=%(program_name)s_%(process_num)02d numprocs=2 Перезагружаем supervisor: service supervisor restart Если вам скучно, обязательно попробуйте очереди.
    1 point
  18. Вы когда-нибудь забывали внести изменения в базу данных после публикации нового функционала? А еще эти кеши модификаторов, которые нужно не забыть обновить. В этой статье я опишу, как автоматизировать процесс деплоя проекта и избавить себя от рутины. Данная статья предполагает, что вы используете Git как систему контроля версий, GitHub как хранилище вашего репозитория и также у вас есть SSH доступ к серверу, на котором можно запустить composer. В качестве основы для нашей автоматизации мы будем использовать GitHub Actions, который позволяет запускать различные процессы внутри контейнеров. Что нам понадобится сделать: консольные скрипты для очистки модификаторов и кеша системы систему миграций для БД Консольные команды Конечно, можно использовать исходный код опенкарта и на основе него написать скрипты, но это не наш метод. Мы напишем собственные велосипеды и за основу возьмем Symfony Console. Устанавливаем нужные зависимости: composer require symfony/console Создаем директорию src для нашего приложения (лучше всего размещать такой код на уровень выше вашего index.php) В composer.json, в разделе автозагрузки добавим код для задания пространства имен нашего приложения: "autoload": { "psr-4": { "App\\": "путь к папке/src/", } }, "require": { ...список ваших зависимостей..., } Создадим папки в директории src для будущего кода: Commands, Models, Services, Traits Структура папки src Создадим сервисы для очистки директорий кеша Базовый класс <?php declare(strict_types=1); namespace App\Services\Clear; use FilesystemIterator; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; class BaseClearService { protected const SKIP_FILE = 'index.html'; public function clear(string $dir): void { $directories = new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS); $files = new RecursiveIteratorIterator($directories, RecursiveIteratorIterator::CHILD_FIRST); foreach ($files as $file) { if ($file->isFile() && $file->getFilename() === self::SKIP_FILE) { continue; } $file->isDir() ? rmdir($file->getRealPath()) : unlink($file->getRealPath()); } } } Сервис очистка системного кеша <?php declare(strict_types=1); namespace App\Services\Clear; class CacheClearService extends BaseClearService { public function process(): void { $this->clear(DIR_CACHE); } } Сервис очистки старых модификаторов <?php declare(strict_types=1); namespace App\Services\Clear; class ModificationClearService extends BaseClearService { public function process(): void { $this->clear(DIR_MODIFICATION); } } ModificationService - мне было лень переписывать код, взял из опенкарта как есть. Первый сервис очищает директорию, а второй создает модифицируемые файлы. Для создания файлов нам нужно из базы получить список модификаторов. Для этой цели можно использовать разные подходы, но т.к. я использую в других проектах Laravel, я задействую для работы с базой Eloquent . Установим пакет: composer require illuminate/database Создаем файл модели модификаций: <?php declare(strict_types=1); namespace App\Models; use Illuminate\Database\Eloquent\Model; class Modification extends Model { protected $table = 'modification'; protected $primaryKey = 'modification_id'; public $timestamps = false; } Файлы для работы с базой данных <?php namespace App\Traits; trait Singleton { public static $instance; public static function getInstance(): self { if (empty(self::$instance)) { self::$instance = new static(); } return self::$instance; } private function __clone() { } private function __wakeup() { } } Теперь непосредственно создадим классы консольных команд для очистки системного кеша <?php declare(strict_types=1); namespace App\Commands; use App\Services\CacheClearService; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; class CacheClearCommand extends Command { protected function configure(): void { $this->setName('cache:clear') ->setDescription('Clear system cache'); } protected function execute(InputInterface $input, OutputInterface $output): int { try { (new CacheClearService())->process(); } catch (\Throwable $e) { $output->writeln('<error>Directory error</error>'); return Command::FAILURE; } $output->writeln('<info>Modification cache cleared successfully</info>'); return Command::SUCCESS; } } И для очистки кеша модификаторов <?php declare(strict_types=1); namespace App\Commands\Opencart; use App\Models\Database; use App\Services\ModificationClearService; use App\Services\ModificationService; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; class ModificationClearCommand extends Command { protected function configure(): void { $this->setName('cache:modification') ->setDescription('Clear modification cache'); } protected function execute(InputInterface $input, OutputInterface $output): int { try { Database::getInstance(); (new ModificationClearService())->process(); (new ModificationService())->process(); } catch (\Throwable $e) { $output->writeln('<error>Directory deleting error</error>'); return Command::FAILURE; } $output->writeln('<info>Modification cache cleared successfully</info>'); return Command::SUCCESS; } } Соберем все в общее консольное приложение <?php declare(strict_types=1); namespace App\Commands; use App\Commands\CacheClearCommand; use App\Commands\ModificationClearCommand; use Exception; use Symfony\Component\Console\Application; class AppConsole { /** * @throws Exception */ public function run(): void { $app = new Application(); $app->add(new ModificationClearCommand()); $app->add(new CacheClearCommand()); $app->run(); } } И создадим файл console в корне, который будет запускать саму консоль #!/usr/bin/env php <?php require dirname(__DIR__) . '/путь к/vendor/autoload.php'; require __DIR__ . '/путь к/admin/config.php'; use App\Commands\AppConsole; (new AppConsole())->run(); Делаем его исполняемым: chmod +x console Теперь при запуске команды php console мы получим список доступных команд: php console cache:clear - очистка системного кеша php console cache:modification - очистка модификаторов Таким образом можно создать другие команды: для сжатия картинок, для бэкапов базы и т.д. Миграции баз данных Данный подход позволяет фиксировать все изменения базы в PHP-коде. Следовательно, мы можем в Git хранить всю историю изменений базы данных. Мы будем использовать библиотеку phinx. Установим ее: composer require robmorgan/phinx Далее запускаем команду для создания конфигурационного файла phinx.php ./vendor/bin/phinx init Создадим папку db/migrations - тут будут храниться файлы миграций. В конфиге phinx.php прописываем путь для папки, где будут лежать файлы миграций и параметры подключения к БД <?php return [ 'paths' => [ 'migrations' => '%%PHINX_CONFIG_DIR%%/путь до папки db/migrations', 'seeds' => '%%PHINX_CONFIG_DIR%%/db/seeds' ], 'environments' => [ 'default_migration_table' => 'phinxlog', 'default_environment' => 'development', 'development' => [ 'adapter' => 'mysql', 'host' => 'host db', 'name' => 'opencart', 'user' => 'admin', 'pass' => '1234567', 'port' => '3306', 'table_prefix' => 'oc_', 'charset' => 'utf8mb4', ], ], 'version_order' => 'creation' ]; Ключ environments позволяет прописывать различные подключения к базе на проде или тестовом сервере и потом при запуске указывать, для какого окружения нужно запускать миграции. Чтобы создать миграцию, запустите команду create и укажите название класса миграции, в названии которого будет отображена суть (для какой таблицы мы создаем миграцию). Например, создадим миграцию для таблицы с постами: ./vendor/bin/phinx create PostsTableMigration После чего в папке db/migrations появится файл с временной меткой в названии файла и именем миграции с snake case. Заполним файл: <?php declare(strict_types=1); use Phinx\Migration\AbstractMigration; final class DocsMigration extends AbstractMigration { public function change(): void { $table = $this->table('docs'); $table->addColumn('route', 'string') ->addColumn('description', 'text') ->addColumn('created', 'datetime', ['default' => 'CURRENT_TIMESTAMP']) ->addIndex(['route'], ['unique' => true]) ->create(); } } Тут все просто, указываем название таблицы, колонки и их типы, задаем индексы. Для каждой таблицы автоматически создается автоинкрементное поле id. Полную инструкцию по написанию миграций можно найти на оф. сайте: https://book.cakephp.org/phinx/0/en/migrations.html Для запуска миграций воспользуемся командой: ./vendor/bin/phinx migrate После запуска данного скрипта в таблице phinxlog (настраивается в параметре default_migration_table из файла phinx.php) появится запись о примененной миграции. GitHub Actions Остался последний и самый главный этап - настройка на стороне GitHub. Создаем в корне проекта файл (указываю полный путь) .github/workflows/deploy.yml. Здесь будут указаны все задачи, которые должны будут запускаться внутри контейнеров на стороне GitHub. name: Deploy on: push: branches: [ main ] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Deploy to server uses: appleboy/ssh-action@master with: host: ${{ secrets.HOST }} username: ${{ secrets.USERNAME }} key: ${{ secrets.KEY }} port: ${{ secrets.PORT }} script: | cd /путь к корню проекта ./.scripts/deploy.sh ./vendor/bin/phinx migrate Тут мы указываем на что будет реагировать наш Action. В данном случае на пуш в ветку main on: push: branches: [ main ] jobs - непосредственно описываем задачи deploy - имя задачи appleboy/ssh-action@master - инструмент для запуска удаленных команд по SSH. Ниже указаны параметры для аутентификации и список выполняемых команд: - cd - переходим в корень проекта - ./.scripts/deploy.sh - запускаем баш-скрипт с нашими командами (опишем ниже) - ./vendor/bin/phinx migrate - запускаем миграции appleboy/ssh-action@master - для аутентификации можно использовать ssh-юзера и пароль, но я указал настройки для входа по ключу. Для этого зайдем по SSH на сервер, куда мы будем деплоить проект и создадим связку ключей: ssh-keygen -t rsa -b 4096 -C "[email protected]" cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys Скопируем приватный ключ cat ~/.ssh/id_rsa Теперь нужно заполнить секретные ключи в GitHub. Переходим в наш репозиторий: Settings -> Secrets and variables -> Actions и добавляем наши секреты: HOST - хост сервера PORT - порт сервера KEY - скопированный ранее приватный ключ USERNAME - имя пользователя на сервере Создаем баш-скрипт .scripts/deploy.sh для запуска консольных команд, которые мы создавали в начале статьи. #!/bin/bash set -e echo "Deployment started ..." git pull php console cache:modification php console cache:clear echo "Deployment finished!" Тут мы забираем все изменения из git-репозитория и чистим кеши нашими консольными скриптами. Сюда можно добавить команду для включения режима обслуживания на время деплоя, но это оставлю вам для самостоятельного выполнения. Разрешаем запуск данного скрипта chmod +x deploy.sh Окончательная структура директорий Теперь мы можем запушить новые изменения в репозиторий и увидим, как во вкладке Actions на GitHub появится новая задача. Мы можем нажать на нее и посмотреть детально на все происходящие процессы в контейнерах. Если произойдет ошибка, то вам на почту придет письмо, что Action завершился неудачно. Сюда также можно добавить проверки на codestyle, запуск тестов и построить полноценное CI/CD, но это пока не про Opencart...
    1 point
  19. Коротка відповідть: ні, не варто, ні в якому разі! Звісно, моя думка дуже суб`ективна, та не охоплює всіх нюансів роботи із сервісом. Але якщо коротко передати суть, то вони розглядають підприємця так, наче мають дати тобі в борг, а не будуть заробляти на тому, що працюєшь ти. А сама велика дурість, що вони не оголошують причини, чому вони не активують мерчанта. А ось і відгуки -- https://www.otzyvua.net/fondy P.S. Е набагато кращі варіанти, де все читко, зрозуміло і без необхідності доводити, що ти "не верблюд" та прикладати виписки по іншим еквайринговим системам. P.P.S. К примеру, WayForPay нормальный.
    1 point
  20. Если вы до сих пор в качестве тестовой среды используете OpenServer или Хостинг+FTP, а единственным инструментом для дебага является var_dump, то самое время попробовать Docker и xDebug. Docker - ПО для запуска сервисов в изолированных контейнерах. Т.е. для запуска приложения мы можем создать необходимое количество контейнеров: веб-сервер, интерпретатор PHP, запустить базу данных. Нам не нужно захламлять систему всеми этими сервисами, все будет установлено внутри докера. При этом мы можем использовать для разных проектов разный набор версий php, mysql и т.д. В отличии от полноценных виртуальных машин (VirtualBox + Vagrant) нам не нужно скачивать образ операционной системы и ставить каждый раз полноценные версии сервисов. В докере используются облегченные версии ПО и если в одном проекте вы уже использовали PHP 7.3, то для другого проекта не нужно будет ничего качать и заново пересоздавать. Вместе с Docker мы будем использовать дополнительный инструмент од названием Docker Compose. Он помогаем с помощью одного конфигурационного файла управлять группой контейнеров. Вместе с Docker мы будем использовать дополнительный инструмент од названием Docker Compose. Он помогаем с помощью одного конфигурационного файла управлять группой контейнеров. Что мы установим для нашего проекта: PHP-FPM 7.3 Nginx MySQL 5.7 Adminer С докером определились, он поможет за 5 минут создать тестовое окружение с нужным набор сервисов. XDebug - дополнение к PHP, которое помогает производить отладку и профилирование кода. В статье будет описана работа совместно с IDE: PhpStorm и VS Code. Приступим к практике. Установка Docker Docker: https://docs.docker.com/get-docker/ Docker Compose: https://docs.docker.com/compose/install/ Надеюсь, git у вас уже установлен (если нет, читайте блог https://opencartforum.com/blogs/entry/326-git-i-opencart-dlya-samyh-malenkih). Переходим в директорию проекта и клонируем репозиторий: git clone [email protected]:ozzzi/docker-opencart.git . В данном репозитории содержится конфигурация для запуска Opencart. Структура директорий: db - файлы базы данных hosts - конфигурационный файл nginx. Конфигурационный файл настроен на использование домена oc.dev images - файлы для создания образов Dockerfile - файл конфигурации образа php.ini - конфиг PHP mods - директория для загрузки конфигов дополнений logs - логи различных сервисов. По умолчанию настроен только лог для Nginx .env - переменные окружения (в нем хранятся конфиги для MySQL). docker-compose.yml - конфигурационный файл Docker Compose Разберем файл docker-compose.yml services - список сервисов (контейнеров). Для каждого сервиса (nginx, mysql и т.д.) принято создавать отдельный контейнер. Список основных директив: image - образ для создания контейнера. Образы хранятся тут: https://hub.docker.com build - путь к Dockerfile для создания кастомного образа. Например, для PHP нам нужно установить ряд дополнений, поэтому нам нужно создать собственный образ. MySQL, Nginx мы вполне можем использовать без изменений. container_name - имя контейнера ports - пробрасываем порты из контейнера на порты локального компьютера (порт компьютера:порт контейнера). volumes - связываем папки на локальном компьютере и директории в контейнере. Используем для пробрасывания конфигов и файлов в контейнер, а также для сохранения данных сервисов в контейнере после перезагрузки (базы данных и т.д.). Перейдем к настройке PHP в Dockerfile-е. FROM - импортируем базовый образ из docker hub. RUN - запускаем консольные команды Linux внутри контейнера для установки нужных пакетов: curl, zip и т.д. ADD - импортируем файлы конфигов php и конфиг xDebug в контейнер WORKDIR - рабочая директория Команды для управления нашими контейнерами: docker-compose up - запуск контейнеров docker-compose up -d - тоже самое, но можно не держать открытым окно терминала docker-compose up -d --build - собираем проект и запускаем, после внесения изменений также нужно запускать с данным ключем. docker-compose stop - останавливаем контейнеры docker-compose down - останавливаем и удаляем контейнеры docker-compose ps - выводит список запущенных контейнеров Установка опенкарт Файлы движка нужно закинуть в директорию www/oc.dev. Чтобы домен был доступен по адресу oc.dev, нужно править файл hosts: Ubuntu: sudo gedit(ваш текстовый редактор) /etc/hosts Windows: windows\system32\drivers\etc\hosts Вносим запись: 127.0.0.1 oc.dev Данные для доступа к базе данных хранятся в файле .env (название базы, пользователь и пароль), а в качестве имени хоста мы используем имя контейнера базы данных: db. Устанавливаем Opencart как обычно. сли у вас есть проблемы с доступом к файлам и папкам (логи, кеш), вам помогут эти команды: sudo chown -R $USER:$USER directory - устанавливает текущего пользователя владельцем директории (в контейнере скрипты исполняются от пользователя www-data) sudo chmod -R 777 directiroy - права 777 для директории Настройка xDebug Файлы конфигурации хранятся по адресу: /images/php/mods/xdebug.ini Здесь нас интересует одна опция xdebug.remote_host. В ней нужно указать ip-адрес контейнера. Для Windows и Mac можно указать равным host.docker.internal Для Linux на запущенном контейнере набираем в консоли: docker inspect php-fpm | grep IPAddress Полученный IP прописываем в конфиг xdebug.ini, в моем случае: xdebug.remote_host = 172.25.0.2 (или 172.25.0.1, если не заведется отладчик) Остановим и перезапустим наши контейнеры с опцией --build. Настройка PHPStorm для работы с xDebug 1. Заходим в меню Run/Edit Configuration. Добавляем тип конфига "PHP Remote Debug" 2. Нужно добавить Server Name - имя сервера вводим любое. Нужно поставить галочку Use path mappings, значения должны быть такими: слева (File/Directory) - путь до папки на локальном компьютере, куда копировали Opencart. справа (Absolute path on the server) - путь внутри контейнера: /var/www/oc.dev 3. Заполняем название конфигурации (любое). Вносим метку, на которую будет реагировать IDE: PHPSTORM. 4. Проверим работу xDebug нажатием на ссылку Validate. Приступаем к отладке Для примера мы хотим узнать, какие модули отображаются на главной странице в позиции Top. Открываем файл catalog/controller/common/content_top.php. Нас заинтересовала строка 49, в которой можно посмотреть код модуля. Возле номера строки ставим точку остановки (Breakpoint) левой кнопкой мыши. Чтобы вызвать работу отладчика выбираем конфигурацию, которую мы добавилил в 3-м пункте, жмем на Debug 'Docker' (зеленый жук) в панели справа,сверху и включаем прослушку Start Listaning for PHP Debug Connections (трубка). Обновляем главную страницу сайта. Должен сработать отладчик и вы увидите: Окно отладки: 1. Данной кнопкой можно поменять брекпойнты и вернуться назад к отладке. 2. Кнопка просмотра точек остановки. 3. Синие стрелки вверх/вниз позволяют пробегать построчно по коду (вверх/вниз) 4. Все доступные переменные на момент остановки скрипта. 5. Стек вызова - можем проследить полный путь вызова. В окне отладки мы видим все доступные переменные в указанной нами точке. Мы поставили точку в цикле и скрипт останавливается в первой итерации. Но если мы хотим посмотреть значения переменных в определенной итерации, нажмем правой кнопкой мыши на точке остановки и зададим условие для ее срабатывания. В моем случае остановимся, когда отображается модуль Featured. Настройка VS Code для работы с xDebug 1. У вас должно быть установлено дополнение PHP Debug 2. Breakpoints ставятся возле нумерации строк. 3. Включаем окно отладки (Ctrl + Shift + D) и нажимаем на ссылку create a launch.json file. Код launch.json { "version": "0.2.0", "configurations": [ { "name": "Listen for XDebug", "type": "php", "request": "launch", "port": 9000, "log": true, "externalConsole": false, "pathMappings" : { "/var/www/oc.dev/": "${workspaceFolder}" } } ] } 4. Отладка: Запуск отладчика Панель управления. Здесь также можно остановить и обновить отладку, а также пробежаться по строчкам кода. Здесь отображаются все переменные. Стек вызова. Надеюсь, что эти советы помогут вам вести более продуктивную разработку и отладку кода.
    1 point
  21. Я получил некий фидбэк от прошлой статьи, и меня просили сделать более подробный гайд по инициализации и установки git. Итак дано чистый сервер с установленным opencart. Для начала нам нужно установить git, так как я на сервере использую ubuntu я воспользуюсь командой, внимание git должен быть установле везде где вы работает с кодом, будь то это продакшен(боевой) сервер тестовый или локальный пк apt install git на вашем же сервере используется другой пакетный менеджер, вам нужно установить с помощью него в centos например это yum install git На локальном пк можно под windows можно установить данных софт gitforwindows Далее нам по хорошему зарегестрировать на одном из сервисов это может быть github или gitlab я использую в большей степени последний. С регистрацией там все просто. Далее нам нужно создать новый репозиторий кликаем по New project пишем название репозитория, обычно это название сайта, по желанию пишем описание проекта, и выбраем каким будет репозиторий приватным или публичным. Далее gitlab нам сам предлагает, что нужно сделать для инициализации git для начала устанавливаем глобальные конфиги, чтобы видеть кто и его контакт для связи, обычно это операция делается один раз после установки git Переходим в консоль, в моем случае это наш тестовый сервер и пишем последовательно 2 команды далее, так как у нас уже развернут opencart первый вариант который предлагает нам gitlab не подходит, нам больше подходит второй варинат. Но все по порядку для начала перйдем в папку с нашим проектом cd /path/to/you/site итак мы в корневой дирректории нашего сайта теперь мы проинициализируем git командой git init gttи Далее можем сразу указать удаленный репозиторий как нам и предалагает gitlab Далее мы создаим файл .gitignore в него нужно прописать файлы и папки которые мы не хотим загружать в репозиторий который гит будет игнорировать и не будет трогать не при каких обстоятельствах, например config. не нужно чтобы кто-то знал пути и достпы к бд сайта или любых других подключений создаем файл любым удобным для вас образом я воспользуюсь консолю берем за основу gitignore из официального git репозитория opencart но с некоторой модификацией ссылку на файл осталю конце статьи Далее нам нужно добавть содержимое рабочей директории в индекс (staging area) для последующего коммита. git add . пишем команду чтобы все файлы добавить в индекс ну и сделаем наш первый комит git commit -m "Initial commit" Готово теперь у нас есть комит можем его залить на удаленный репозиторий git push -u origin master но при попытке запушить, возникает ошибка, а все потому что мы не настроили общение gitlab и нашего сервера по ssh ключам, сейчас мы это исправим Для начала нам нужно сгененрировать ssh ключ на сервере командой предварительно заменив email на свой ssh-keygen -t rsa -b 4096 -C "[email protected]" утилита предложит нам путь до файла оставляем по умолчанию, и ввести пароль для passphrase но он может быть пустым, его не обязательно заполнять. далее нам нужен публичный ключ его можно получить командой cat ~/.ssh/id_rsa.pub ну собственно куда ssh-keygen его и сгенерировал. Копируем наш ключ и идем в gitlab setting > ssh Добавляем наш ключик и делаем заново команду git push -u origin master и вуаля мы залили наш проект на сайт заметим что конфиги не попали в наш репозиторий. теперь мы имеем ветку master это ветку нажна только для продакшена в нее что либо комитить считается мовитоном, елси не идет стихийная разработка сайта. Давайте создадим dev ветку сайта в которой будут попадать все наши изменения для dev сервера. все ветка для разработки создана, теперь например у меня встала задача на сайте поманять фон на всем сайте и сменить подпись в футере сайта Для начала опишем эту задачу, идум в issue и создаем новое issue Назвать ишью лучше по порядку начиная с #1 #2 .... #90000 это удобно для самого gitlab чтобы закрыть ишью будет достаточно последний комит назвать "Close #issue" c числами же это проще всего. Итак ишью создано теперь я как разработчик клонирую репозиторий к себе не локальный пк командой git clone [email protected]:stick.qwe/opencart.git где ссылка на наш репозиторий произвожу настройку своей локальной среды разработки будь то openserver docker lamp xamp что угодно, для того чтобы развернуть сайт. Я буду использовать ide phpstorm вы же можете использовать любой редактор кода который вам нравится Для начала нам нужно переключиться на ветку dev далее нам нужно создать новую ветку с названием нашего issue отсавляем галочку на checkout branch в правом нижнем углу у нас нужная нам ветка сделанная из dev ветки начинаем выполнять нашу работу итак мы выполнили один пункт нашей задачи давайте закомитим его средствами phpStorm идем VCS -> git -> commit file Открывается окно в котором мы пишем название нашего коммита что мы сделали в этом изменении обычно я в начало добавляю так же название нашего ишью для систематизации мы видим в каких файлах были произведены изменения и что они верны жмем commit phpStorm говорит нам о warning в файле стилей не обращаем внимание, на это и жмем commit и продолжаем выполнять нашу следающую задачу задача выполнена, осталось только сделать последний commit идем туда же vsc->git->commit file называем его именно так тем самым помогаю gitlab самому за нас закрыть issue и нажимаем commit and Push мы видим наши коммиты и отправляем их на сервер и идем в gitlab и проставим галочки над выполненными задачами мы видим что содалась новая ветка нашего разработчика теперь нам нужно объеденить ветку #1 > dev а потом dev > master для этого нам нужно создать merge request Gitlab нам предлагает нашу ветку смержить из #1 в мастер но это не правильно, нам нужно смержить в dev нажимает в Change branches и меняем target branch на dev здесь в принципе ничего не меняем, ставим галочку на удаление исходной ветки и нажимаем sudmit merge request далее нажимаем merge так же не забываем закрывать наше issue кнопкой close issue теперь мы можем залить нашу dev ветку на дев сервер и оттестировать полностью, но так как сейчас у нас как бы ее нет, мы зальем наши правки сразу на продакшен сервер, но для начала создадим новый merge request теперь уже из dev > master делаем тоже самое с одним исключением это убираем галочку с удаления ветки merge request прошел успешно теперь у нас осталались 2 ветки с измененым кодом и нашими новыми коммитами далее мы идем на наш продакшен сервер переходим в папку с проектом и выполняем одну команду. git pull видим что наши файлы обновились проверяем в браузере вот видим наши изменения. Тоже самое и с дев сервером единственное перед pull необходимо сменить ветку на дев севрере git checkout dev и так же сделать git pull. Подытожим. Сегодня мы научились базовой работе с git и ведения проекта, это только базовые принципы работы с гит, его возможности куда больше, чем описно в статье, но не все сразу. как говорится ссылка gitlab репозиторий с .gitgnore https://gitlab.com/stick.qwe/opencart
    1 point
  22. Всем привет! Сегодня мы разберем получение товаров из категории Opencart в Android-приложение через JSON. Для начала не будем использовать сторонние библиотеки, а сделаем все нативным образом, чтобы ознакомиться с базовыми принципами обмена данными. Итак, поехали Сперва нужно определить выдачу массива товаров в JSON-объект из магазина. Открываем catalog/controller/product/category.php И в цикле выдачи данных для товаров добавляем свои запросы. Перед $data['products'][] = array( Добавляем // изображение для списка, размер 100х100 if ($result['image']) { $json_image = $this->model_tool_image->resize($result['image'], 100, 100); } else { $json_image = $this->model_tool_image->resize('placeholder.png', 100, 100); } // создаем массив данных для каждого товара // получаем имя, путь изображения, описание и цену $data['json-products'][] = array( 'name' => $result['name'], 'thumb' => $json_image, 'description' => utf8_substr(trim(strip_tags(html_entity_decode($result['description'], ENT_QUOTES, 'UTF-8'))), 0, $this->config->get('theme_' . $this->config->get('config_theme') . '_product_description_length')) . '..', 'price' => $price, ); Дальше ищем response $this->response->setOutput($this->load->view('product/category', $data)); И заменяем на // формируем массив products $data['json_products'] = array( 'products' => $data['json-products'], ); // и отдаем его в json при запросе в адресной строке &json_products // для этого поставим условие запроса if (isset( $this->request->get['json_products'])) { $this->response->setOutput(json_encode($data['json_products'])); } else { $this->response->setOutput($this->load->view('product/category', $data)); } Таким образом, теперь при запросе к сайту по адресу мой-сайт/index.php?route=product/category&path=20&json_products мы будем получать массив данных вида: {"products":[ {"name":"Apple Cinema30'", "thumb":"http:\/\/store.url\/image\/cache\/catalog\/demo\/apple_cinema_30-100x100.jpg", "description":"The 30-inch Apple Cinema HD Display delivers an amazing 2560 x 1600 pixel resolution. Designed speci..", "price":"$100.00"}, {"name":"Canon EOS 5D", "thumb":"http:\/\/store.url\/image\/cache\/catalog\/demo\/canon_eos_5d_1-100x100.jpg", "description":"Canon's press material for the EOS 5D states that it 'defines (a) new D-SLR category', while we're n..", "price":"$100.00"}, . . . , . . . , ]} С серверной частью закончили, переходим к программной части приложения. Что мы будем делать? Получим JSON данные из url Разберем эти данные и актуализируем с listView(textView) Скачаем картинки, кешируем их в приложении и актуализируем по позициям в listView(imageView) Для начала не забудьте дать приложению разрешение на использование сети (в манифесте): <uses-permission android:name="android.permission.INTERNET"/> Также в папку res/drawable мы поместим заглушку для изображений(до того, как они спарсятся) blank.png (100x100px) Теперь разметка. Для простоты будем работать с activity_main и стандартным listView activity_main.xml: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <ListView android:id="@+id/lv_products" android:layout_width="wrap_content" android:layout_height="wrap_content" tools:context=".MainActivity" /> </RelativeLayout> Здесь все просто - в релятивную разметку мы поместили listView c id=lv_products Теперь создадим кастомную разметку для этого listView lv_layout.xml: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:id="@+id/tv_product" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:layout_marginRight="5dp" android:layout_marginTop="5dp" android:textColor="#195F74" android:textSize="20sp" android:textStyle="bold" /> <ImageView android:id="@+id/iv_thumb" android:layout_width="100dp" android:layout_height="100dp" android:layout_below="@id/tv_product" android:layout_centerVertical="true" android:contentDescription="@string/str_iv_thumb" android:padding="5dp" android:scaleType="fitXY" /> <TextView android:id="@+id/tv_description" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/tv_product" android:layout_toRightOf="@id/iv_thumb" android:textSize="16sp" /> <TextView android:id="@+id/tv_price" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/tv_description" android:layout_marginBottom="10dp" android:gravity="right" android:layout_marginRight="5dp" android:textColor="#cc3333" android:textSize="20sp" android:textStyle="bold"/> </RelativeLayout> С разметкой закончили — приступаем к коду Создаем файл ProductsJSONParser.java (это и будет класс парсера) package com.opencart.ocproducstlist; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.content.res.Resources; /** класс парсера JSON данных */ public class ProductsJSONParser { // Получаем JSONObject и возвращаем List public List<HashMap<String, Object>> parse(JSONObject jObject) { JSONArray jProducts = null; try { // Здесь элементы из массива 'products', который мы получаем из контроллера jProducts = jObject.getJSONArray("products"); } catch (JSONException e) { e.printStackTrace(); } // применяем getProducts к массиву объекта JSON // теперь каждый объект - это товар return getProducts(jProducts); } private List<HashMap<String, Object>> getProducts(JSONArray jProducts) { int productCount = jProducts.length(); List<HashMap<String, Object>> productList = new ArrayList<HashMap<String, Object>>(); HashMap<String, Object> product = null; // разбираем товары по одному и добавляем к объекту List for (int i = 0; i < productCount; i++) { try { // вызываем getProduct и парсим, добавляем product = getProduct((JSONObject) jProducts.get(i)); productList.add(product); } catch (JSONException e) { e.printStackTrace(); } } return productList; } // Разбираем JSON-объект product private HashMap<String, Object> getProduct(JSONObject jProduct) { HashMap<String, Object> product = new HashMap<String, Object>(); String name = ""; String thumb = ""; String description = ""; String price = ""; try { // обратите внимание на метод .replaceAll // без него разные "непечатные" символы будут отображаться неправильно name = jProduct.getString("name").replaceAll(""", "\""); thumb = jProduct.getString("thumb"); description = jProduct.getString("description").replaceAll(""", "\"").replaceAll("’", "\'"); price = jProduct.getString("price"); product.put("product", name); // здесь сперва ставим заглушку product.put("thumb", R.drawable.blank); product.put("thumb_path", thumb); product.put("description", description); product.put("price", price); } catch (JSONException e) { e.printStackTrace(); } return product; } } Ну а теперь MainActivity package com.opencart.ocproducstlist; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.HashMap; import java.util.List; import org.json.JSONObject; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.widget.ListView; import android.widget.SimpleAdapter; public class MainActivity extends Activity { ListView mListView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // URL с нашими JSON-данными String strUrl = "http://мойсайт.com/index.php?route=product/category&path=20&json_products"; // определяем задачу по загрузке // и запускаем ее с нашим url DownloadTask downloadTask = new DownloadTask(); downloadTask.execute(strUrl); // ссылаемся на ListView в activity_main mListView = (ListView) findViewById(R.id.lv_products); } /** метод загрузки данных из url */ private String downloadUrl(String strUrl) throws IOException { String data = ""; InputStream iStream = null; try { URL url = new URL(strUrl); // Создаем http соединение, соединяемся и считываем данные HttpURLConnection urlConnection = (HttpURLConnection) url .openConnection(); urlConnection.connect(); iStream = urlConnection.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader( iStream)); StringBuffer sb = new StringBuffer(); String line = ""; while ((line = br.readLine()) != null) { sb.append(line); } data = sb.toString(); br.close(); } catch (Exception e) { Log.d("Exception while downloading url", e.toString()); } finally { iStream.close(); } return data; } /** Асинхронно скачиваем json */ private class DownloadTask extends AsyncTask<String, Integer, String> { String data = null; @Override protected String doInBackground(String... url) { try { data = downloadUrl(url[0]); } catch (Exception e) { Log.d("Background Task", e.toString()); } return data; } @Override protected void onPostExecute(String result) { // закончили в non-ui ListViewLoaderTask listViewLoaderTask = new ListViewLoaderTask(); // начинаем парсить listViewLoaderTask.execute(result); } } /** Асинхронно парсим данные и кидаем в listView */ private class ListViewLoaderTask extends AsyncTask<String, Void, SimpleAdapter> { JSONObject jObject; // парсим в non-ui @Override protected SimpleAdapter doInBackground(String... strJson) { try { jObject = new JSONObject(strJson[0]); ProductsJSONParser productsJsonParser = new ProductsJSONParser(); productsJsonParser.parse(jObject); } catch (Exception e) { Log.d("JSON Exception1", e.toString()); } // Инстанцируем класс парсера ProductsJSONParser productsJsonParser = new ProductsJSONParser(); // Список для сохранения List<HashMap<String, Object>> products = null; try { // Получаем спарсеные данные в List (наш список) products = productsJsonParser.parse(jObject); } catch (Exception e) { Log.d("Exception", e.toString()); } // Ключи, которые используем в hashMap String[] from = { "product", "thumb", "description", "price" }; // и айдишники, используемые в listView int[] to = { R.id.tv_product, R.id.iv_thumb, R.id.tv_description, R.id.tv_price }; // задаем адаптер // и закидываем ключи в айдишники SimpleAdapter adapter = new SimpleAdapter(getBaseContext(), products, R.layout.lv_layout, from, to); return adapter; } /** doInBackground выполнен - займемся картинками */ @Override protected void onPostExecute(SimpleAdapter adapter) { // Задаем адаптер для listview mListView.setAdapter(adapter); for (int i = 0; i < adapter.getCount(); i++) { HashMap<String, Object> hm = (HashMap<String, Object>) adapter .getItem(i); String imgUrl = (String) hm.get("thumb_path"); ImageLoaderTask imageLoaderTask = new ImageLoaderTask(); HashMap<String, Object> hmDownload = new HashMap<String, Object>(); hm.put("thumb_path", imgUrl); hm.put("position", i); // запускаем ImageLoaderTask для скачивания // и актуализации картинок в listview imageLoaderTask.execute(hm); } } } /** Асинхронно качаем картинки и помещаем в listView */ private class ImageLoaderTask extends AsyncTask<HashMap<String, Object>, Void, HashMap<String, Object>> { @Override protected HashMap<String, Object> doInBackground( HashMap<String, Object>... hm) { InputStream iStream = null; String imgUrl = (String) hm[0].get("thumb_path"); int position = (Integer) hm[0].get("position"); URL url; try { url = new URL(imgUrl); // создаем соединение и подключаемся HttpURLConnection urlConnection = (HttpURLConnection) url .openConnection(); urlConnection.connect(); // считываем данные iStream = urlConnection.getInputStream(); // директория кеширования File cacheDirectory = getBaseContext().getCacheDir(); // временно сохраняем картинку в кеш-дир File tmpFile = new File(cacheDirectory.getPath() + "/ocpl_" + position + ".png"); // поток в кеш-файл FileOutputStream fOutStream = new FileOutputStream(tmpFile); // из потока в картинку Bitmap b = BitmapFactory.decodeStream(iStream); // пишем файл в темп (png) b.compress(Bitmap.CompressFormat.PNG, 100, fOutStream); // сбрасываем и закрываем поток fOutStream.flush(); fOutStream.close(); // создаем hashMap для передачи картинки // в listview, в соответствии с позицией HashMap<String, Object> hmBitmap = new HashMap<String, Object>(); // сохраняем путь к картинке // и позицию картинки в listview hmBitmap.put("thumb", tmpFile.getPath()); hmBitmap.put("position", position); // возвращаем объект с картинкой и позицией return hmBitmap; } catch (Exception e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(HashMap<String, Object> result) { // теперь получаем путь и позицию String path = (String) result.get("thumb"); int position = (Integer) result.get("position"); // задаем адаптер SimpleAdapter adapter = (SimpleAdapter) mListView.getAdapter(); // забираем объекты из hashMap // с соответствующей позицией в listview HashMap<String, Object> hm = (HashMap<String, Object>) adapter .getItem(position); // заменяем текущий путь (сейчас "заглушка" - res/drawable/blank.png) hm.put("thumb", path); // и сообщаем listView об изенении содержимого adapter.notifyDataSetChanged(); } } } Запускаем приложение и любуемся результатом: До новых встреч
    1 point
×
×
  • Create New...

Important Information

On our site, cookies are used and personal data is processed to improve the user interface. To find out what and what personal data we are processing, please go to the link. If you click "I agree," it means that you understand and accept all the conditions specified in this Privacy Notice.