В походах, как и в любом деле, через некоторое время оказывается, что то, что знаешь и считаешь само собой разумеющимся, для других - иногда новость или откровение. Или "о, я об этом даже не подумал!". А объяснить, что к чему и почему, зачастую уже тоже не можешь, поскольку давно забыл проблемы и вопросы начального этапа и просто не способен их вспомнить. То есть можешь (ответы есть в голове), но некому задать правильные вопросы. И что желательно - вовремя. И вспоминаешь и отвечаешь на них уже в процессе. А потом снова забываешь.
И вот поэтому, когда в руки попадаются начинающие туристы со всем ворохом первых вопросов, надо спешить и записывать ответы. Потому что это оказывается полезным для всех.
Все советы тщательно выстраданы и бережно оттестированы на собственной персоне.
Крупы, сахар, чай, кофе перепаковываются в пластиковые бутылки с широким горлом (в которое проходит чайная ложка). Удобны бутылки из-под соков "Биола" на 0.5 л и меньше (меньше - это в основном для чая и кофе).
такие бутылки можно не бояться забыть оставить ночью в темноте возле костра или в тамбуре палатки: звери и насекомые не заберутся и под дождём не промокнет. Однако если ночью выпадет снег, вы будете ругаться матом на тех, кто оставил их возле костра. Поэтому лучше не оставляйте. У каждой вещи должно быть своё место и она должна находиться там, а не где попало. Чтобы вы могли наощупь и в темноте без проблем в своих вещах ориентироваться, а при сборе достаточно было взять рюкзак, а не бегать по всему лагерю;
альтернативный способ - канцелярская "прищепка". Главное открыть пачку аккуратно, чтобы можно было тщательно закрыть отверстие, несколько раз согнув упаковку и надёжно закрепив прищепкой. Начатую пачку можно нести в рюкзаке внутри котелка, чашки или внутри коробки с жёсткими стенками (пищевые контейнеры, походные закрытые котелки типа армейских). Круп и каш это не касается: способ в основном для небольших запасов чая и кофе;
сахар кубиками в походе не очень удобен: он всё равно крошится, объём занимает приличный, перепаковывать его сложно. Хотя удобен тем, что его легко равномерно отмерить и разделить между участниками.
Крупы и каши ОЧЕНЬ РЕКОМЕНДУЮ перед походом расфасовывать в индивидуальные порции. Покупаете на базаре упаковку из сотни небольших (10 x 15 см) кулечков с зип-локом, вооружаетесь весами поточнее (надо будет отмерять порции по 80-100 грамм), столовой ложкой, терпением и списком рациона... Дальше всё понятно. Потраченные усилия (небольшие на самом деле) окупаются удобствами в самых разных ситуациях: и при внезапной переорганизации группы, и в одиночных походах, спасают от неразберихи или необходимости иметь толкового завхоза (кто что несет, как равномерно разгружать участников, как быстро собрать комплект еды для приготовления), и от необходимости бороться с неудобными большими упаковками (они превращаются в удобные атомарные: взял, использовал, забыл). Ну и при аварийных ситуациях вы не останетесь вдруг без всех круп или вообще без еды. Или с очень-очень промокшей и негодной едой.
Уклоняться от употребления сахара и особенно соли в походах, мотивируя это "не ем" и "худею" я категорически не советую. Единственная веская причина уклоняться - если вас от этих продуктов порвет в клочья. Или превратитесь в зеленое бревно и вас надо будет нести. Соль интенсивно теряется организмом при физических нагрузках. Если потери не восполнять, довольно быстро возникнут проблемы с усвоением воды. И сопутствующие неприятности в виде плохого самочувствия, утомляемости, плохой реакции, голова не соображает и т.п. И ещё будете пить много и часто, но напиться и жажду утолить не сможете. Хотите с этим побаловаться - дома перед зеркалом пожалуйста. Где привычные нагрузки - это дойти от дома/офиса до маршрутки или магазина.
Сахар - это углеводы. Их ваш организма сжигает первыми при нагрузках, если не вдаваться в детали. Поэтому в походе можно и нужно бросаться на сладкое, если тянет. Слушайте свой организм, в общем. Он не дурак. А если дурак - слушайте более опытных туристов. Или спортсменов. Но не тех спортсменов, которые спортсмены, а тех, которые физкультурники. И выглядят здоровыми и бодрыми, а не круглыми и большими. Большие могут лишнего насоветовать.
Еда в первый и последний день похода. Обычно поезда в Крым идут ночью и приходят утром. Поэтому
в походную раскладку обычно не включается завтрак первого дня, а также ужин (а может и обед) последнего дня похода - в это время группа обычно находится в цивилизации и костры разводить для приготовления пищи просто негде. Поэтому рассчитывать надо на питание в цивилизации или сухими перекусами (орехи, изюм, печенье и т.п.);
завтракаем обычно либо своими запасами в поезде или в маршрутке/троллейбусе, либо на вокзале в городе: в кафе (макдональдс), киосках и т.п. можно купить поесть-попить по мелочам;
где-нибудь недалеко от цивилизации под конец похода может очень захотеться посидеть в кафе, поэтому желательно иметь запас денег и на такие случаи. Примерно по 50-100 грн за раз.
Посуда
Вилка в походе не нужна. Нужны КЛМН: кружка, ложка, миска, нож.
посуду желательно паковать недалеко от верха рюкзака, т.к. кружка и нож могут понадобиться в дороге и на перекусах;
то же самое касается туалетной бумаги. Она должна соседствовать с посудой;
некоторым кажется, что ножей можно иметь один-два на всю группу. Но нож применяется не только на стоянке и не только для еды. Он может понадобиться для того, чтобы отрезать лейкопластырь в пути, когда вся группа растянута и искать, у кого нож - нереально. И больно. И долго. В общем, нож желательно иметь каждому. Как и спички в непромокаемой упаковке и одноразовую зажигалку.
Зайдите на базар вместо спортивных и туристических магазинов: посуда из нержавейки весит как пух и очень удобна. И дешёвая. Тяжёлые термокружки -- штука довольно бесполезная. Поэтому на любителя. Лучше взять с собой тонкую и невесомую кружку из нержавейки. Её при желании можно поставить на огонь, чтобы вскипятить немного воды (чего не сделаешь термокружкой).
Термобельё и термоноски -- штука очень полезная, в отличие от термокружек.
Одежда
по просьбам трудящихся информирую: кто не уверен, что из одежды брать:
всегда помните, что вам это нести на себе и не один день
может быть холодно и погода вообще штука непредсказуемая
минимум - штаны, шорты, футболка, свитер, носки, шапка+головной убор от солнца, перчатки, УДОБНАЯ и РАЗНОШЕННАЯ обувь, сменная обувь на время стоянок для отдыха ног. при желании и лёгком весе удваиваем или утраиваем всё это:)
Помните, что свитер удваивать не надо :)
По курткам, если сомневаетесь, что брать, совет такой: она должна быть полегче-потоньше (чаще всего вы её будете нести в рюкзаке), но защищать от ветра. Налетает ветер на плато -- надеваете на футболку куртку-ветровку и идёте в ней. Холодно - надеваете свитер и куртку. Дождь - надевается сверху дождевик (плащ из клеёнки - самый бюджетный и самый лёгкий и компактный вариант для Крыма).
Сменная обувь -- это не шлёпанцы. Стоянки -- это не асфальт и паркет, а опавшая листва, ветки и камни, а бегать на стоянке придется за хворостом, за водой и в туалет, и не всегда это рядом и по прямой, а по склону в тапочках ходить неудобно. В принципе можно, особенно если знаете, куда идёте. Всегда можно надеть и ходовые ботинки. Просто шансов просохнуть у них в этом случае будет меньше.
Шорты очень желательны -- на майские почти всегда очень тепло. Если возьмёте максимально простые и незатейливые спортивные шорты (без карманов, а-ля спортивные трусы), их можно легко комбинировать со штанами (утром понюхал небо, надел шорты под штаны, потеплело -- снял штаны, идёшь в шортах).
Самый оптимальный способ понять, что надевать - на стоянке перед выходом на маршрут вам должно быть немного прохладно, а не тепло. То есть, к примеру, выходите утром, а в шортах и футболке кажется прохладно - это замечательно. Наденьте сверху спорт. штаны и ветровку (куртку-штормовку), пока собираете лагерь и завтракаете, а перед выходом снимите и положите их в голове рюкзака. Начнёте идти - будет тепло. А на остановках набрасывайте куртку-штормовку из рюкзака, если спина мокрая и ветер.
todo: 50/10, 45/15, остановки - обязательно снимается рюкзак (проветривание спины и отдых плечам и спине)
Штаны должны быть лёгкие и защищающие от ветра. Это главное. Ещё лучше быстросохнущие. В идеале -- легкие спортивные. Я предпочитаю в походах современную синтетику: бабушкины сказки о пользе х/б материалов устарели лет так на 50 как минимум. Современная спортивная синтетика не раздражает, быстро сохнет и мало весит, ну и не мнётся в качестве бонуса. Поэтому в походе гораздо удобней.
Тёплые штаны во время ходьбы на маршруте в мае не понадобятся - вам будет жарко и некомфортно. В джинсах можно пойти в такой поход, но нежелательно. Когда они намокают, становятся очень тяжёлыми и очень долго сохнут. И в таком состоянии натрут вам всё, что можно. Попадёте под дождь или оступитесь, переходя брод - намучаетесь потом в них. И днём под солнцем и с рюкзаком будете идти -- поясница у вас джинсы промочит гарантированно.
Вечером и ночью в мае может быть прохладно, поэтому думайте сами с оглядкой на свои ощущения, что и как будете надевать на стоянках вечером и утром.
Температура на высоте ниже, чем на уровне моря! Не забывайте, что у моря - ровно, песок и жарко. На высоте - помимо естественного уменьшения температуры (примерно 0.5-0.7 градуса каждые 100 метров по вертикали) есть ещё ВЕТЕР на плато и перевалах, а также тенистые и сырые леса, где заметно прохладней, чем на берегу моря. То есть, что мы имеем? В Крыму высота гуляний по горам - примерно 1 км над уровнем моря. Если в мае на побережье 20-25 градусов днём и около 10 ночью, то на высоте Ай-Петри в это же время будет уже градусов 15 днём и в лучшем случае около 5-6 ночью.
Про ветер на плато и перевалах я не зря написал большими буквами. Там ситуация такая: идёте вы по склону горы в футболке, солнце печёт, жара, тишина, благодать. достигаете вершины того, куда шли - и оказываетесь натурально в таком ветре, что дышать мешает и чуть с ног не сбивает. И надо весьма проворно одеваться в флиску/свитер/штормовку, иначе слишком холодно, а то и искать шапку, если баффа не хватает. Бафф в таких условиях гораздо удобней на шее и лице (чтобы закрывал рот и нос, а может и уши с затылком).
Ночью ночуют нередко возле воды: ручья, озера. А там прохладнее. Даже если мощный родник недалеко - почва вокруг довольно влажная и прохлада чувствуется не только вблизи источника. Ну и если это лес, тем более высокий, куда днём лучи солнца не особенно достают, чтобы прогреть почву - ночевать в таких условиях вдруг оказывается не очень комфортно и холодно. Поэтому помните что наверху - примерно на 5-10 градусов холоднее. И если днём вы это не заметите, то ночью - вполне.
Аптечка
Минимум личных медикаментов:
1-2 упаковки таблеток активированного угля;
несколько таблеток, которые вам от головной боли помогают (цитрамон / анальгин / пиркофен);
солнцезащитный крем (лучше один на 3-5 человек).
Желательно ещё:
наборчик бактерицидных лейкопластырей (в небольших плоских упаковках или коробочках продают, они там разной формы для разных участков тела);
эластичный бинт (нужен не всегда, но лучше по одному у каждого иметь).
На случай мозолей в дополнение к набору мелких пластырей можно взять один пластырь в виде ленты и маленькую индивидуальную упаковку ваты (они размером с пачку влажных салфеток). (Вот здесь вам и пригодится персональный ножик из комплекта КЛМН).
Может это и будет в общей аптечке, но лучше индивидуально каждому иметь небольшой комплект. А ещё очень полезно иметь хотя бы одни маленькие ножницы на группу.
<
p>
Гигиена
Берите много туалетной бумаги (у каждого должен быть полный рулон с собой): ей хорошо мыть посуду сразу после еды, может помочь при растопке костра, служит носовым платком и для вытирания рук. Ну и по назначению придётся применять иногда.
Не берите полотенце. Толку от него в походе - ноль. Промокнуть лицо после умывания можно и футболкой. Если кто-то рискнёт купаться -- под конец похода у вас будут грязные футболки, которыми вполне можно вытереться.
Для гигиены лучше взять упаковки влажных салфеток из расчёта как минимум пара салфеток в день: вечером лицо и ноги протереть (вам с этими ногами спать в палатке), ну и 1-2 салфетки в течение дня могут уйти на жирные руки и т.п.
<
p>
Туалет
В туалет желательно ходить парами :) Один страхует, чтобы ищущие хворост для костра не пошли в строну сидящего в засаде товарища. Если сидите в засаде и замечаете движущегося в вашем направлении человека - не стесняйтесь проорать волшебное слово "Занято!". Иначе стесняться придётся потом, когда вас заметят, это гораздо менее приятно. Те, кто не первый раз в походах, обычно соображают, что к чему, орут "Понял" и разворачиваются.
Для большой группы и на днёвках очень желательно по приходу сразу организовать ОДНО отхожее место, вырыв узкую достаточно глубокую ямку и закопав её перед уходом со стоянки. Это гораздо экологичней. К тому же кучки и бумажки не украшают лес и стоянки, если вы не в курсе. А судя по состоянию Крыма, многие не в курсе.
<
p>
Спальник
Насчёт спальников и холодных ночей. Не планируйте спать в них в одежде. В спальнике быстро согреваешься, а ночью обычно потеешь. Если ваша одежда -- обычная х/б ткань, вы проснётесь в 3-4 утра мокрыми и замёрзшими. Не верите - приготовьте часы и попробуйте.
Спать желательно раздетым (бельё, футболка), тогда спальник отводит излишнюю влагу наружу, оставляя тело сухим и в тепле. В холодные ночи -- класть куртку и свитер СВЕРХУ на спальник. Это гарантированно греет, в отличие от попыток начинающих надеть на себя всю одежду вечером и залезть в спальник.
Если вечером холодно -- можно залезть в спальник в одежде, чтобы согреться, но через время обязательно снимите её перед сном.
Отмазка: если видите опытных туристов, спящих в спальнике в одежде, не спешите с выводами (насчёт них или меня). Они скорей всего в термобелье, а спальник у них весит с пол-кило (ваш раз в 4-5 больше). То есть это совсем другие материалы и другие условия. Они сознательно идут на экономию веса и комбинирование функций вещей в походах. Термобельё - это вобще отдельная и обширная тема. В общем, не спешите с выводами. Никакой ошибки тут нет.
<
p>
Упаковка вещей
todo: что снизу, что сверху, куда тяжёлое, куда кухонную утварь, тапочки, сахар, чай, перекус, кружку
Кому нужны полиэтиленовые пакеты с зип-локом (многоразового использования, герметичные)
В объявленном на сходке магазине на Философской есть пакеты 30х40см. Продаются только упаковкой 100 шт - 45,50 грн. Думаю, одной упаковки на всех неэкипированных должно хватить. Могу купить, а при дележной встрече раздать. Думаю, пакетов по 10 на лицо будет достаточно.
Какие-то большие они: туда и ноутбук поместится. Мне интересны несколько штук (2-6 наверное) -- для карт, документов пригодятся.
А более мелких нет? Кстати, такие кульки с зипом продаются на Озёрке. Мы такие мелкие кулёчки покупали для индивидуальной расфасовки еды
Если поход возле воды, то во избежание сюрпризов лучше найти на Озёрке большие "мешки для сахара". Они полиэтиленовые, но габаритами с рюкзак примерно. Берётся 2 таких мешка, заполняются наполовину (чтобы оставшийся хвост надёжно завязать крепкой верёвкой, согнув его предварительно). В один - то, что понадобится только вечером на стоянке, в верхний -- то что может потребоваться днём при смене погоды. А в остальном всё пакуется как обычно, только аккуратно, чтобы мешок не порвать. Изнутри рюкзак получается защищён полиэтиленом. Вещи не намокнут ни под дождями, ни даже если вдруг свалится в реку ненадолго.
Но более мелкие мешки и кульки тоже нужны: и для разграничения вещей (для этого и обычные АТБ-шные и т.п. кульки подойдут), и для отделения грязной и мокрой одежды. Но для этого можно взять упаковку крепких мусорных пакетов: за 2-4 грн обычно получаешь 30 пакетов, если правильно помню.
В пакет зип-лок большой свитер не засунешь, но другая одежда (футболки, майки, штаны, рубашки и остальная мелочь) запросто помещается. Они удобны ещё тем что выдавив воздух из них и застегнув вещь занимает меньший объём.
Особой водонепроницаемости не требуется, лучше о весе не забывать. Самый универсальный способ - большие мусорные пакеты. Весят и стоят мало, в одной упаковке 30 мешков. Или 1-2 больших полиэтиленовых пакета размером с рюкзак. Главное - защитить от намокания спальник.
<
p>
Палатка
Палатки -- советую исходить из расчёта 50 см ширины на человека. Без проблем можно спать на спине, не касаясь соседей.
Палатку в дороге можно разделить между 2-3 людьми: один несёт тент (1-1.5 кг), другой внутреннюю палатку (~1.5 кг), третий колышки и дуги (400-600 грамм).
Палатку желательно нести внутри рюкзака. Снаружи она может быть повреждена колючками и ветками, а нести её в руках - неудобно. Хотя если вы ходите без трекинговых палок, можете носить и в руках.
В случае 10-15 человек удобней несколько вместительных палаток (если есть возможность) вместо кучи 2-местных: во-первых, на стоянках часто не так уж много ровного места под палатки, и поставить 3-4 больших палатки гораздо легче, чем 5-7 маленьких, а во-вторых, выбранные даты и места гарантируют наличие огромного количества людей на стоянках. Это интересно (если вы не мизантроп), но неудобно, если вам негде поставить палатки.
<
p>
Рюкзак
Если есть возможность взять большой рюкзак (например 100-литровый вместо 60-литрового) -- берите не раздумывая. Чем больше рюкзак, тем меньше проблем с его упаковкой. В идеале снаружи не должно ничего крепиться: ни карематы, ни палатки, ни тем более спальники. Если порвать каремат о кустарник или камни -- ещё не трагедия, то проколотая палатка или намокший или порванный спальник вас не порадуют. Соседей ваших тоже, поскольку им также придётся терпеть неудобства и как-то спасать вас от холода.
К тому же дождевые накидка на рюкзак часто не рассчитаны на что-то, висящее снаружи. Вообще-то от такой накидки можно вообще отказаться, если ВСЕ вещи внутри рюкзака лежат в больших крепких герметичных полиэтиленовых мешках. Мелочь в боковых кармашках и верхнем кармане желательно тоже.
<
p>
Клещи
Клещи. За 6-7 лет походов вроде бы видел на себе всего одного клеща. И то не уверен до сих пор, что это он был -- может неглубокая заноза (мы тогда вечно в заросли терновника попадали, были везде исцарапаны - может мелкая щепка неглубоко в кожу встряла). При том, что я хожу почти всё время в шортах и от травы на обочинах не шарахаюсь (вчера читал, что там их рассадники), впрочем как и от кустов. Хотя говорят, это индивидуально: одних клещи охотно едят, других нет.
Внимательного осмотра себя и задних частей тел соседей вполне достаточно. Вот пинцет в аптечку не забудьте. С ним гораздо проще ковыряться в случае обнаружения засады. Только вот живого клеща не вздумайте пинцетом вытаскивать. Живого очень аккуратно и постепенно, в несколько подходов, вытаскивают тонкой ниткой или волосом, сделав петлю.
Напоследок цитата: "Случаи клещевого вирусного энцефалита среди людей регистрируются почти ежегодно в Крыму и Волынской области. В 2009 году их было зарегистрировано 9." Когда оцените количество туристов, гуляющих на майские по Крыму... В общем, шансов подцепить гадость от клещей у вас невелика.
<
p>
Трекинговые палки
Трекинговые палки позволяют СУЩЕСТВЕННО разгрузить ваши ноги.
На их освоение и понимание придётся потратить несколько дней. Впрочем, оно того стоит. Легче и быстрее всего научиться работать и понимать палки при ходьбе в гору. Легче всего почувствовать от них пользу и разгрузку - на спусках. А получать от палок удовольствие и помощь при ходьбе по равнине получается примерно через неделю плотного с ними знакомства. Когда уже понял, зачем руки в походе. И насколько сильно они могут разгрузить ноги вместо того, чтоб болтаться при переходах без дела.
Но на равнине не стоит увлекаться палками: надо почаще их убирать и ходить как все, а не как паук. Не отвыкайте от привычного чувства равновесия под грузом, иначе грозит зависимость от палок и неприятные ощущения при их отсутствии.
Исправлен внешний вид и проблема с Шоппикой: теперь не надо модифицировать common/header.tpl, перенося в него строчку с включением скрипта Fancybox из аналогичного файла темы Default (см. bugtracker, #2).
Единственная проблема, которая там осталась - при использовании галочки согласия покупателя с условиями я пока не могу вывести текст в окно PrettyPhoto, как сделано в стандартной форме оформления заказа. Если кто силён в jQuery - буду благодарен за подсказку. Этот яваскрипт мне уже весь мозг выел. PrettyPhoto ругается на неправильную картинку, хотя отличий от стандартной формы оформления заказа я при беглом осмотре не увидел. Придётся искать-читать про премудрости преттифото. А лучше отключите эту галочку и не мучайте своих покупателей. Всё равно в 2-недельный срок товар вернуть можно по закону, несмотря ни на какие галочки.
P.S. Архив ещё не рассылал. Чуть позже сделаю.
UPD 22 Dec. Со вчерашнего дня по семейным делам пришлось срочно бегать по аптекам и больницам, поэтому обновление задерживается.
По команде, в которой указывается диапазон коммитов, создаёт папку .deployment с готовым деревом и изменившимися файлами. И список удаленных файлов, которые придётся удалить вручную (если пользуетесь FTP клиентом) или через SSH на сервере (если есть такая возможность).
У скрипта есть маленькая особенность: запускать его надо в корне репозитория (там, где расположена скрытая папка `.git`). Не в подпапках из любого места репо: там он просто отработает вхолостую.
[Git, SSH] При полноценном хостинге (с SSH доступом)
Судя по всему, у меня сделано один в один как в http://habrahabr.ru/blogs/Git/127213/, поэтому не вижу смысла описывать то же самое. У меня немного отличается структура папок проектов, но это несущественно. Суть проста - на сервере лежит как Git-репозиторий, так и обычная рабочая копия (на которую смотрит веб-сервер). Точнее, на одну из папок репозитория - public_html. Потому что в репозитории хранится ещё документация, служебные скрипты, тестовые и чистые SQL дампы.
И при новых коммитах от разработчиков (git push) репозиторий по хуку делает автоматически две операции - обновление локальной серверной копии (git pull origin dev) и копирование набора файлов из config_sets (здесь у меня хранятся файлы, специфические для разных конфигураций: для одного разработчика, для другого, для dev2-windows, dev2-linux, для production1, production-dev и так далее, если надо ещё больше). Понятна идея? Требуемый набор конфигов просто перезаписывается поверх того, что есть в репозитории (а туда могут попасть и локальные конфиги девелоперов, если они не исключены через .gitignore), и получается чистая и настроенная конфигурация. Быстро, без чек-листов, ручных проверок-исправлений и условий-ветвлений с множеством девелоперских конфигов (зачем они на сервере?).
Естественно, на сервере настроено использование SSH-ключей, чтобы избавиться от необходимоси ввода пароля после git pull.
Есть, конечно, мелкие особенности - за неделю мы уже наступили на пару граблей и может это стоило бы описать.
При модерируемых комменатриях от анонимов иногда возникают наплывы спам-ботов, которые мешают.
Для начала я выделил розовым фоном запрещенные комментарии. Это уже заметно облегчило разборки.
Но вот просто удалять спамерские комментарии мне не хочется. Шаблон у меня такой: если это разовое - то удалю и забуду, пусть тешатся, а если где-то бот поселился - хотелось бы подождать и убедиться, что это регулярное явление и забанить IP. Из-за одного спам-комментария банить весь IP как-то чересчур. Но сейчас приходится заниматься этим вручную.
Пригодился бы подсчёт количества удаленных спам-комменатриев с определенного IP. Чтобы я мог удалять их сразу (десяток в день удалить несложно и руками), но информация об их авторстве не пропадала впустую.
И через отметку галочек удалять или разрешать комментарии тоже очень не нравится: я обычно разрешаю 1-2 комментария. А для этого надо их отметить, промотать список до конца (спамеров гораздо больше успевает накопиться) и там не ошибиться с кнопкой (потому что пока проматываешь, иногда забывается - это я сейчас разрешить полезные хотел и их отметил? Или запретить/удалить бесполезное?).
Мне гораздо больше подошл бы вариант с парой ссылок возле комментария: разрешить, запретить, удалить.
Добавлена возможность настройки точности чисел при выводе веса и размеров (количество знаков после зяпятой). Регулируется парой цифр в языковых файлах - там же, где и десятичные разделители и разделители тысяч (точка, запятая, пробел, апостроф и т.п. - кому что нравится).
commit b8607fb31b2d9c414506c513b8fb4b8593cbc214
Author: Ruslan Brest <rb@labtodo.com>
Date: Sun Dec 4 14:12:47 2011 +0200
[+] Возможность настройки точности чисел при выводе веса и размеров (количество знаков после зяпятой)
diff --git a/public_html/admin/language/english/english.php b/public_html/admin/language/english/english.php
index 5a0223c..71bb46d 100644
--- a/public_html/admin/language/english/english.php
+++ b/public_html/admin/language/english/english.php
@@ -7,6 +7,8 @@ $_['date_format_long'] = 'l dS F Y';
$_['time_format'] = 'h:i:s A';
$_['decimal_point'] = '.';
$_['thousand_point'] = ',';
+$_['length_decimals'] = 2;
+$_['weight_decimals'] = 2;
// Text
$_['text_yes'] = 'Yes';
diff --git a/public_html/admin/language/russian/russian.php b/public_html/admin/language/russian/russian.php
index d68a762..2cdbc95 100644
--- a/public_html/admin/language/russian/russian.php
+++ b/public_html/admin/language/russian/russian.php
@@ -7,6 +7,8 @@ $_['date_format_long'] = 'l, d F Y';
$_['time_format'] = 'H:i:s';
$_['decimal_point'] = '.';
$_['thousand_point'] = ',';
+$_['length_decimals'] = 0;
+$_['weight_decimals'] = 0;
// Text
$_['text_yes'] = 'Да';
diff --git a/public_html/catalog/language/english/english.php b/public_html/catalog/language/english/english.php
index cdf17cf..524e1d9 100644
--- a/public_html/catalog/language/english/english.php
+++ b/public_html/catalog/language/english/english.php
@@ -7,6 +7,8 @@ $_['date_format_long'] = 'l dS F Y';
$_['time_format'] = 'h:i:s A';
$_['decimal_point'] = '.';
$_['thousand_point'] = ',';
+$_['length_decimals'] = 2;
+$_['weight_decimals'] = 2;
// Text
$_['text_home'] = 'Home';
diff --git a/public_html/catalog/language/russian/russian.php b/public_html/catalog/language/russian/russian.php
index 48829a5..f69f200 100644
--- a/public_html/catalog/language/russian/russian.php
+++ b/public_html/catalog/language/russian/russian.php
@@ -7,6 +7,8 @@ $_['date_format_long'] = 'l d F Y';
$_['time_format'] = 'H:i:s';
$_['decimal_point'] = '.';
$_['thousand_point'] = '';
+$_['length_decimals'] = 0;
+$_['weight_decimals'] = 0;
// Text
$_['text_home'] = 'Главная';
diff --git a/public_html/system/library/length.php b/public_html/system/library/length.php
index 03ca441..3d02025 100644
--- a/public_html/system/library/length.php
+++ b/public_html/system/library/length.php
@@ -5,6 +5,7 @@ final class Length {
public function __construct($registry) {
$this->db = $registry->get('db');
$this->config = $registry->get('config');
+ $this->language = $registry->get('language');
$length_class_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "length_class mc LEFT JOIN " . DB_PREFIX . "length_class_description mcd ON (mc.length_class_id = mcd.length_class_id) WHERE mcd.language_id = '" . (int)$this->config->get('config_language_id') . "'");
@@ -41,9 +42,9 @@ final class Length {
public function format($value, $length_class_id, $decimal_point = '.', $thousand_point = ',') {
if( 0 == $value ) return "";
if (isset($this->lengths[$length_class_id])) {
- return number_format($value, 2, $decimal_point, $thousand_point) . $this->lengths[$length_class_id]['unit'];
+ return number_format($value, $this->language->get('length_decimals'), $decimal_point, $thousand_point) . $this->lengths[$length_class_id]['unit'];
} else {
- return number_format($value, 2, $decimal_point, $thousand_point);
+ return number_format($value, $this->language->get('length_decimals'), $decimal_point, $thousand_point);
}
}
}
diff --git a/public_html/system/library/weight.php b/public_html/system/library/weight.php
index d55fdf9..cda9d4d 100644
--- a/public_html/system/library/weight.php
+++ b/public_html/system/library/weight.php
@@ -5,6 +5,7 @@ final class Weight {
public function __construct($registry) {
$this->db = $registry->get('db');
$this->config = $registry->get('config');
+ $this->language = $registry->get('language');
$weight_class_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "weight_class wc LEFT JOIN " . DB_PREFIX . "weight_class_description wcd ON (wc.weight_class_id = wcd.weight_class_id) WHERE wcd.language_id = '" . (int)$this->config->get('config_language_id') . "'");
@@ -36,9 +37,9 @@ final class Weight {
public function format($value, $weight_class_id, $decimal_point = '.', $thousand_point = ',') {
if( 0 == $value ) return "";
if (isset($this->weights[$weight_class_id])) {
- return number_format($value, 2, $decimal_point, $thousand_point) . $this->weights[$weight_class_id]['unit'];
+ return number_format($value, $this->language->get('weight_decimals'), $decimal_point, $thousand_point) . $this->weights[$weight_class_id]['unit'];
} else {
- return number_format($value, 2, $decimal_point, $thousand_point);
+ return number_format($value, $this->language->get('weight_decimals'), $decimal_point, $thousand_point);
}
}
}
По умолчанию они почему-то не выводятся в характеристиках товара и увидеть их можно только на странице сравнения.
Вывести подготовленные данные (вес и размеры товара) можно в другом месте шаблона (например вверху, рядом с моделью и бонусными баллами) -- думаю, не составит труда переставить вывод в более удобное место самостоятельно.
Изменения для обеих версий одинаковые. Просто на вкладке для 1.5.1.3 более полная версия: включает в себя изменение системных библиотек для более аккуратного вывода нулевых значений веса и размеров. В версии 1.5.1.2 я это изменение вносил давно и забыл про него написать.
Основная причина - неудобство предлагаемого способа обновления между релизами. Мне не хватает diff-ов и легкого способа их получения для чистых версий, чтобы иметь возможность накатить эти изменения вручную на модифицированный движок. Обновляться заменой файлов мне чрезвычайно неудобно.
Если кто-то хочет присоединиться к переводу на английский или правке украинского - теперь есть куда:
Исключительно языковые файлы живут и переводятся в соответствующих ветках репозитория https://github.com/g3d/MaxSite-TC (сейчас там украинский в ветке master и английская заготовка в ветке english).
Участвовать в корректировке переводов может любой человек, даже понятия не имеющий и не желающий разбираться с Git-ом: достаточно оставлять комментарии к строкам файлов при их просмотре. Тем, кто дружит с Git-ом, объяснять наверное ничего не надо.
Файлы скачивать можно там же: переключиться на соответствующую ветку ("Current branch" справа) и скачать ZIP.
Если автор MaxSite CMS переберётся на GitHub - это значительно упростит дело: можно будет предлагать/публиковать дополнения гораздо проще. Пока что я предполагаю держать этот репозиторий (по крайней мере основную ветку) только для "чистых" релизов версий CMS. И пускать разработчиков только с условием коммитить любые дополнения и модификации в соседние ветки, но не в master. Master branch сейчас - исключительно для оригиналов и получения списка отличий между ними.
ALTER TABLE `opencart_product`
CHANGE `length` `length` DECIMAL(7,2) NOT NULL DEFAULT '0.00',
CHANGE `width` `width` DECIMAL(7,2) NOT NULL DEFAULT '0.00',
CHANGE `height` `height` DECIMAL(7,2) NOT NULL DEFAULT '0.00',
CHANGE `weight` `weight` DECIMAL(7,2) NOT NULL DEFAULT '0.00'
Префикс таблиц "opencart_" меняете на свой, если он используется.
Установка (1.5.1.x)
Запишите это в текстовыый файл, например 999.sql.txt.
Измените префикс таблиц на свой.
Сделайте на всякий случай резервную копию базы данных (Система / Резервные копии / все таблицы должны быть отмечены / кнопка Бэкап). Вообще делайте их почаще, это полезно.
Там же в поле "Востановить базу данных" выберите файл 999.sql.txt и нажмите кнопку "Восстановить".
Теперь можете идти в редактирование товаров и проверять вес и размеры, например 15000 грамм. Если вам вдруг не хватит и этого количества цифр и позарез надо вводить вес в граммах, а не килограмах или тоннах - измените в этом коде цифры 7 на что-то большее (например 10) и повторите процедуру.
Иногда (гораздо реже) натыкаюсь на 12:12 или 13:13. И ещё 59. Причем не могу сказать, что на часы я часто смотрю - ну 2, ну 3 раза в день могу взгляд бросить. И весь этот год так. Раньше случалось, но воспринималось как: "Ух ты! Смотрите скорей, пока не исчезло!". А сейчас это настолько часто, что уже: "Ох, опять?".
Вот недавно: просыпаюсь - 9:00. Ну вот почему 00, а? Как это так точно? :) Я без будильника встаю. Думаю, ну вот, опять же будет 9:11, как пить дать. Умываюсь, зубы, кухня, то-сё, цветок полил, забыл уже про часы - возвращаюсь ноут включить, перекладываю автоматически телефон с постели на стол - опа, в руках 09:11 светится! Ну как по заказу. Вот почему я не через 5 минут вернулся, или через 20? Чай там заварить или ещё что подобное.
Что это, а?
И почему я не натыкаюсь на 18:28 например? Кусок e ничем не хуже 03:14 :) Вселенная, а другие константы мне показывать будут?
Магазин молодой, так что цифры соответствующие. В люди вышел в сентябре (может ближе к концу - в 20 числах; точно не помню). Посещаемость небольшая, но мы над этим работаем и она понемногу стабильно растёт. Заказов было немного, и во многом это заслуга монстрообразной формы оформления заказа. А не только малой посещаемости.
Недавно я поставил укороченную форму оформления заказа (на выходных) и поэтому решил сохранить предыдущую историю как иллюстрацию и чтобы иметь возможность визуально убедиться во влиянии этого шага оформления покупки. На картинках - данные до её установки.
На нашей статистике видно, что из 72 человек, более-менее созревших что-то купить, сумели выдержать все испытания формы заказа всего 7. Очень наглядно. Из них наверное около половины - мы сами: в этот период точно были тестовые оформления заказа для проверки.
Funnel conversion rate: 7.37%. То есть почти 93% не выдерживают до конца и уходят без оформления покупки.
Некоторые уходят бесповоротно, часть возвращается к просмотру корзины (это скорей всего мы по большей части - владельцы и разработчики). Часть уходит на просмотр условий доставки. То есть их не хватает на этой старнице - и короткая форма заказа это отчасти решает.
Так что теперь остаётся подождать несколько месяцев (хотя бы 3) и узнать, насколько изменится уровень конверсии.
В понедельник (а короткая форма была установлена на выходных) мы получили сразу 4 заказа в один день. Посмотрев на статистику предыдущих 3 месяцев, вы наверное догадаетесь, что нас это приятно удивило. Это могло оказаться и совпадением, но тем не менее форма однозначно сыграла роль на своём этапе. Причём если до этого у нас были заказы на совсем небольшие суммы (около 200 грн / ~$25 USD), то в понедельник помимо роста их количества выросла существенно и сумма заказов (раз в 5).
Предварительные выводы за неделю работы короткой формы заказа
Тенденция видна уже даже за такой короткий промежуток (ещё не прошло и недели):
Эффективность укороченной формы в 4 раза выше
Что можно увидеть? Во-первых, заметно, что люди стали покупать быстрее: очень мало просматривают корзину и сомневаются - 90% перешли сразу к заказу/оплате и быстро его завершили. Обычно в заказе 1-2 позиции. То есть люди принимают решение и покупают из каталога.
Заметно и то, что эффективность "воронки продаж" выросла в 4 раза: было 7%, стало 29%. Время ещё уточнит и сгладит цифры. Пока такие :) Скоро у нас будет выкладка подготовленных зимних товаров, так что есть надежда на рост продаж сезонных категорий.
UPD 2011-11-26. На самом деле на картинке есть неточность: на шаге "shopping cart view" показано 23 посетителя на входе, 21 вышли, 2 прошли дальше. На самом деле 11 "вышло" на Quickcheckout - не успел отредактировать пути. Попросту забыл про них, только сейчас вспомнил об этом. И двое вернулись на тот же просмотр корзины. То есть на следующий шаг воронки прошло не 2 (8.70%), а 13 посетителей. И не на тот шаг, что на картинке, а на "параллельный" quickcheckout (который я забыл и его здесь нет). А на картинке - значит, один заказ в этот период я зацепил ещё с оформлением по старой форме. Плюс-минус день наверное промазал. В итоге более правильная картина: 23 => 13 => 9. И эффективность соответственно выше.
Для 1.5.1.2 и 1.5.1.3 код ниже. На 1.5.1.3 не проверял - если есть отличия и не работает, сообщите.
Небольшие изменения вносились раньше, поэтому могут быть небольшие отличия между тем, что вы видите у себя, и здесь (например, thumb_w, thumb_h, cut_descr) -- не обращайте на них внимания. Вставляемый код неизменный как для 1.5.1.2, так и для 1.5.1.3, номера строк совпадают - главное найти место для вставки, а их легко идентифицировать по остальными имеющимся признакам.
При публикации изменений используется формат DIFF. С его помощью можно перенести небольшие изменения к себе вручную (см. ниже). Или автоматически: командами git apply rb-patch.diff или patch -p2 < rb-patch.diff (находясь в корневой папке магазина - там, где расположены каталоги admin и catalog).
Предварительно, конечно же, скопировав и сохранив в файл опубликованный патч и отрезав первые несколько строк на русском с пояснениями про diff.
Это список изменений. В начале строк - индикаторы: что убрать (минусы, красный цвет) в старом файле и что добавить (плюсики, зелёный цвет).
В каких именно файлах - указано утроенным значком (---, +++)
@@ -106,10 +106,12 @@ -- строки, т.е. примерно на 106 строке вы должны найти похожий кусок кода, перед и после блока изменений обычно выводится пара строк текста. "Примерно 106" - потому что я множество правок вношу в свой магазин и содержимое файлов может не совпадать с оригинальным OpenCart 1.5.1.1 или с тем правками, которые внесены у вас до меня.
Правки индицируются значками "-" и "+" в самом начале строки: те, что с минусом, были убраны (они должны совпадать с тем, что вы у себя видите перед внесением изменений), а те, что с плюсом -- добавлены в это место. Поэтому можно руками внести и проконтролировать все изменения.
В данном случае надо закомментировать небольшую часть в файле admin/controller/catalog/attribute.php и модифицировать одну строку в admin/model/catalog/attribute.php (заменить её на несколько новых, которые можно скопировать и убрать плюсики в начале строк).
См. также
Улучшение сортировки на витрине Опенкарт - ещё одна статья с примером описания "обычным языком" и diff-файлом. До сих пор считаете длинное описание более понятным, чем diff? Не перестаю удивляться таким заявлениям.
Сделал вот такую укороченную форму заказа (скриншоты ниже). Упрощённое гостевое оформление заказа на одной странице.
Преимущества (они же и недостатки):
Предельно компактно и быстро, не утомляет покупателя. По статистике на каждом лишнем шаге при оформлении заказа сходят с дистанции 20% покупателей;
Всё на виду и получилось весьма удобно и наглядно;
Предполагается оплата исключительно методом "Cash on delivery" (оплата наличными по факту доставки). Всё остальное решается по телефону и комментариями;
В админке нет никаких настроек (это касаемо предыдущего пункта; используется только cash-on-delivery способ, и он указан в коде и должен быть разрешен).
Нет никакого учёта всего, что связано с адресом и гео-информацией (включая налоги, так как они зависят от адреса)
Модуль совместим только с версиями 1.5.1.*. Под более новые адаптировать не планирую, т.к. альтернатив уже существует большое количество и можно взять готовое решение. Создавать ещё одно решение будет также стоить денег, причины постарался описать в отдельной статье (см. также в конце, в списке связанных статей). Есть версия под Opencart 1.4.9.x (ocStore 0.2.2 / 0.2.0). Ниже описаны недостатки и возможности.
Удобнее всего купить модуль быстрого оформления заказа на Opencart.com. Там архив сразу доступен для скачивания и не надо ждать, пока я его отправлю. Также проще будет забирать обновления в любой удобный момент - вы будете получать уведомления от сервиса по email;
Вручную за 10WMZ, или по курсу в WMR или WMU. Или 10$ через Paypal (там же принимаются банковские карты). В комментариях к платежу укажите свой email, на который надо выслать ZIP архив с модулем.
Вручную я высылаю архив сразу после того, как узнаю о платеже. Обычно это занимает не более суток. Чаще гораздо меньше, но случаи разные бывают и иногда я могу быть недоступен. Webmoney не присылает мне никаких уведомлений на email (единственный из сервисов, кстати), поэтому о платеже там лучше написать дополнительно на email или через форму обратной связи в блоге. Иначе узнать о платеже там я могу лишь по чистой случайности.
Поэтому рекомендую довериться автоматике на Opencart.com. Там немного дороже, но покупка удобнее для последующих обновлений. И может оказаться быстрее, чем с моим участием.
p>Версия Quickcheckout для Opencart 1.5.1 поставляется в 2 версиях:
более старая (QC) не имеет выбора способов оплаты (покупателю надо выбрать только доставку), но может быть гораздо удобней тем магазинам, которые торгуют локально по городу и доставляют товары по стране, используя единственный способ оплаты - из рук в руки;
Этот вариант не заменяет никакие файлы Opencart;
QCPM (с поддержкой способов оплаты, payment methods) существует для версии Опенкарт 1.5.1.3.
этот вариант заменяет часть стандартных файлов - более подробная информация в README в соответствующей папке. Присылайте неизвестные мне модули или содержимое этой папки (tpl файлы) - я их модифицирую для QC/PM и включу tpl в архив
Admin: Дополнения / Доставка:
Будут использоваться только те модули, в которых установлена географическая зона ВСЕ РЕГИОНЫ. Это связано с тем, что из модуля полностью убрано всё, что связано с географической информацией.
Внимание: модуль "Доставка в зависимости от веса" не будет работать -- у него отсутствуют настройки для всех регионов.
Если вы используете вес товаров - включите вместо него способ "Доставка по городу Citylink".
Если вес не нужен - попробуйте "Фиксированную стоимость доставки" или другой подходящий способ.
Если пользователь зарегистрирован и предварительно залогинился, его данные подставятся в форму (имя, email, телефон) и заказ будет связан с этим покупателем. Планируется добавить возможность быстрой регистрации, но сроки неизвестны.
Поддерживаются Shoppica и Spicylicious темы оформления - для них в архиве есть отдельные папки. По вопросам поддержки других специфических тем оформления, на которых форма заказа выглядит плохо - обращайтесь.
В версии Quickcheckout для Opencart 1.4.9.x (ocStore 0.2.2 / 0.2.0) есть и оплата, и доставка, но не проверялась работа с зарегистрированными пользователями - было сделано только гостевое оформление. Эта версия заменяет стандартные файлы опенкарт, поэтому если используется другая тема оформления -будьте аккуратны при установке и сделайте предварительно резервные копии ваших файлов.
Вебмани мне не присылает уведомления на email, поэтому лучше сообщить дополнительно (через форму обратной связи например), если архив нужен срочно. Если я на связи, то сразу же и отправлю. Остальные сервисы уведомляют. Или можно подождать - я в течение дня обычно высылаю, просматриваю почту регулярно. Автоматически на полном автопилоте можно купить на сайте opencart.com, но там чуть дороже. Зато можно в любой момент скачать архив (и апдейты), не дожидаясь ответа от меня.
Если нет электронных кошельков и желания платить карточкой - обычно можно без проблем пополнить WMR или WMU кошелёк наличными через платёжные терминалы.
2011-11-27. Исправлена ошибка в версии для Opencart 1.5.1.3. Всем, кто покупал на opencart.com, должно было прийти уведомление об обновлении. Кто покупал вручную, через Webmoney/Paypal - напишите, вышлю обновление для 1.5.1.3.
2011-11-30. Появилась тестовая версия для ocStore 0.2.0 / 0.2.2 (Opencart 1.4.9.?). Возможно, будет ещё дорабатываться. Но шансов мало - я вообще не планировал версиею для 1.4. Пока не проверена покупка зарегистрированным пользователем. И форма работает в 2 шага. Если будет время - попробую улучшить. Пока так, как на демо: http://ocs020.labtodo.com/
Позже собираюсь сделать такую же одностраничную форму со всеми опциями. Наверное все эти способы тоже кому-то, да нужны (нужны, да? Или нет?).
Ошибки или проблемы с установкой?
Если возникают проблемы с установкой или какие-то ошибки, которые не можете устранить после прочтения инструкции по установке - мне нужен как минимум адрес вашего сайта, где я могу посмотреть на работу формы и увидеть ошибки. Укажите адрес сайта и email, с которого покупали модуль, на странице Контакт.
<
p>Мелкие модификации также входят в список того, что я помогаю сделать при первичной настройке:
сделать другие поля обязательными или необязательными;
убрать или переименовать какие-то поля (например, поменять ФИО на что-то другое, заменить продолжение адреса на ближайшую станцию метро, удобный склад перевозчика или индекс).
Значительно ускорить процесс может наличие в письме параметров доступа к FTP и в админ-часть Опенкарт. Это позволит гораздо быстрее обнаружить и устранить ошибки в конфигурации (модулей оплаты и доставки, например) или неправильной установке, убедиться в том, что ничего не напутано с версиями, увидеть лог ошибок сразу же по мере их воспроизведения и после изменений. Объяснять в переписке, что именно попробовать, исправить или проверить - в большинстве случаев гораздо дольше и сложнее. Поэтому по возможности сразу их указывайте, если понимаете, что сами этим заниматься не будете.
Если поле "meta keywords" не заполнено в админке, Опенкарт его не выводит. Для улучшения ситуации заполним его автоматически, продублировав слова из названия товара.
В результате у нас будет что-то вроде:
Аппарат для приготовления блинов Bomann CM 2221 CB
Не идеал, но вроде бы лучше, чем ничего. Или ручное прописывание у всех товаров, хи-хи. В то же время вручную описать эти поля никто не мешает, и в случае их заполненности автоматика не вмешивается. Делать автозаполнение для "meta description" не решился, потому что одинаковое название и описание не приветствуется. Но кому хочется - могут дописать if( empty($this->data['description']) ) $this->data['description'] = $this->data['title'];
Для версий Opencart 1.5.x, включая 1.5.1.3; ocStore 1.0.1.
diff --git a/public_html/catalog/controller/common/header.php b/public_html/catalog/controller/common/header.php
index 79bf735..1904774 100644
--- a/public_html/catalog/controller/common/header.php
+++ b/public_html/catalog/controller/common/header.php
@@ -18,6 +18,15 @@ class ControllerCommonHeader extends Controller {
$this->data['direction'] = $this->language->get('direction');
$this->data['google_analytics'] = html_entity_decode($this->config->get('config_google_analytics'), ENT_QUOTES, 'UTF-8');
+ // SEO improvements:
+ // If keywords and description not set - use title here too
+ if( empty($this->data['keywords']) )
+ {
+ $words = explode(' ', $this->data['title']);
+ if(count($words) > 1) $this->data['keywords'] = implode(',', $words) . ',';
+ $this->data['keywords'] .= $this->data['title'];
+ }
+
$this->language->load('common/header');
if (isset($this->request->server['HTTPS']) && (($this->request->server['HTTPS'] == 'on') || ($this->request->server['HTTPS']
Загрузите скрипт по этой ссылке, отредактируйте его нижнюю часть после слов PUT YOUR CODE HERE, вот эту:
(function () {
...
/* PUT YOUR CODE HERE */
loadJquery({
url: 'http://code.jquery.com/jquery-latest.min.js',
timeout: 5000, //ms
success: function ($) {
$('h1').css('border', '2px solid red');
},
error: function () {
alert("Can't load jQuery.");
}
});
}());
Преимущества скрипта:
кроссбраузерность
основан на коде jquery 1.7.2
отлов исключений при помощи try/catch
возможность указания таймаута загрузки
загрузка не будет производиться, если jQuery уже загружен.
простота использования для конечного пользователя: просто пишите свой код в success/error-колбеки
отсутствие конфликтов с другими js-библиотеками типа prototype.js
скрипт проходит jslint-валидацию
Теперь больше информации для тех, кто хочет узнать подробности.
Скрипт состоит из двух частей: loadScript и loadJquery.
loadScript
Функция практически полностью сделана на основе кода jQuery 1.7.2, актуальной в данный момент. Загрузка скрипта производится посредством добавления тега script в head. Производится контроль исключений при помощи try/catch, теоретически не должно быть никаких ошибок яваскрипта. Предусмотрена возможность указания тайм-аута, по достижению которого будет вызван error-callback.
Пример использования.
loadScript({
url: 'http://example.com/script.js', //script url
timeout: 5000, //optional timeout in ms
success: function () {
alert('Script loaded callback.');
},
error: function () {
alert('Script was not loaded.');
}
});
loadJquery
Особенности функции loadJquery следующие:
загрузка jQuery не будет производиться, если jQuery уже загружен
используется .noConflict(), т.е. можно не беспокоиться о возникновении конфликтов с другой библиотекой типа prototype.js
Полный листинг скрипта
/*!
*
* loadJquery.js
* Load jQuery on demand from javascript
* Author: contact@slicezilla.com
* November 2011
*
* The script uses part of jQuery JavaScript Library v1.7
* http://jquery.com/
* Copyright 2011, John Resig
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
*/
/*global document, setTimeout, clearTimeout, alert, jQuery*/
(function () {
"use strict";
var loadScript = function (options) {
var script, transportTimeout,
head = document.head || document.getElementsByTagName('head')[0] || document.documentElement,
transport = {
send: function (url, successCallback) {
script = document.createElement('script');
script.async = 'async';
script.src = url;
// Attach handlers for all browsers
script.onload = script.onreadystatechange = function (event, isAbort) {
if (isAbort || !script.readyState || /loaded|complete/.test(script.readyState)) {
// Handle memory leak in IE
script.onload = script.onreadystatechange = null;
// Remove the script
if (head && script.parentNode) {
head.removeChild(script);
}
// Dereference the script
script = undefined;
//callback if not abort
if (!isAbort && typeof successCallback === 'function') {
clearTimeout(transportTimeout);
successCallback();
}
}
};
// Use insertBefore instead of appendChild to circumvent an IE6 bug.
// This arises when a base node is used (#2709 and #4378).
head.insertBefore(script, head.firstChild);
//head.appendChild(script);
},
abort: function (errorCallback) {
clearTimeout(transportTimeout);
if (script) {
script.onload(0, 1);
}
if (typeof errorCallback === 'function') {
errorCallback();
}
}
};
//init timeout
if (options.timeout > 0) {
transportTimeout = setTimeout(function () {
transport.abort(options.error);
}, options.timeout);
}
//get script
try {
transport.send(options.url, options.success);
} catch (e) {
transport.abort(options.error);
}
};
//function which will load jQuery using loadScript
function loadJquery(options) {
if (typeof jQuery !== 'undefined') {
//jQuery is loaded already, just perform business logic
if (typeof options.success === 'function') {
//pass jQuery as parameter, in order to have $ === jQuery in callback
//just in case that $.noConflict was used already, and $ !== jQuery
jQuery(options.success);
}
} else {
//jQuery was not loaded on a page
loadScript({
url: options.url,
timeout: options.timeout,
success: function () {
jQuery.noConflict();
if (typeof options.success === 'function') {
jQuery(options.success);
}
},
error: options.error
});
}
}
/* -------- PUT YOUR CODE HERE -------- */
loadJquery({
url: 'http://code.jquery.com/jquery-latest.min.js',
timeout: 5000, //ms
success: function ($) {
$('h1').css('border', '2px solid red');
},
error: function () {
alert("Can't load jQuery.");
}
});
}());
Не знал, для меня это оказалось приятным сюрпризом. Апгрейд до следующего релиза через сеть происходит очень долго - несколько утомительных часов, насколько помню. С возможными вопросами и проблемами посреди процесса. И это слабо зависит от скорости сети. Я обновлялся ближе к вечеру и пришлось на ночь ноутбук оставить докачивать и устанавливать, запретив впадать в спячку.
Через CD тоже процесс небыстрый, но гораздо меньше времени занимает. Так что советую попробовать ISO образ скачать, записать на болванку, и с неё уже обновляться.
Появилась потребность и желание воспользоваться для наполнения каталога услугами людей. Которые не всегда могут быть хорошо технически подкованы - то есть вероятность ошибок, будем считать, велика. И рисковать уже имеющимся контентом не хочется, даже если человек -- полный зайка и ничего такого не хотел. Допустим, не так понял или считал, что будет лучше по-другому.
В Opencart возможность ограничения доступа базовые: можно разрешить/запретить просмотр и редактирование разных сущностей группе пользователей. Но если человеку доступно редактирование товаров, то он может удалить или внести изменения в том числе и старые. И это ограничить нельзя.
Пока додумались до варианта продублировать админки и базы, чтобы редакторам были доступны только их товары. И периодически эти товары будут переноситься в основную базу, а дополнительная -- очищаться.
Проблемы видны, но пока кажутся вполне нормально решаемыми:
в новой базе надо продублировать информацию о категориях, производителях, атрибутах и опциях из старой (с сохранением тех же ID);
мы и в основной базе убрали возможность вносить новые атрибуты и опции - они под жёстким контролем и впустую не заводятся. На новой базе аналогично. Но простым редакторам помимо прав на изменение товаров и файл-менеджер даны ещё права на изменение категорий (чтобы могли добавлять новые и оформлять их описанием и картинкой);
перенос категорий - ну что ж, придётся сравнивать дампы этой таблицы до и после. И новые вставлять в основную базу с теми же идентификаторами категорий. А изменённые - исправлять вручную;
дальше перенос товаров (таблиц product и product_description). Пока кажется, что всё должно пройти без приключений (контроля и конвертирования идентификаторов).
Надеюсь, что я не очень просчитался в прогнозах :)
Интересно было бы также узнать, есть ли что-то у Даниэля в планах по этому поводу. Потому что если придуманный перенос кроме потенциальной опасности ошибиться будет ещё и неудобен, придётся что-то делать с дополнительными ограничениями и оставаться в пределах одной базы.
P.S. Платные модули:
http://www.opencart.com/index.php?route=extension/extension/info&extension_id=3992 - Give a usergroup read/write permissions for | sales orders by store | products | categories | stores | image folders. Bulk add, Mass copy products for every image in selected folder, images are added to new products. Additional images are supported.
Не рекомендуется применять! В 1.5.1.1 / 1.5.1.2 вроде бы работает, но отсутствия проблем никто не обещал.
Добавляем команду редактирования заказа в админке. Изменения для 1.5.1.2 и 1.5.1.3 идентичны. Но в 1.5.1.3 форма редактирования переделывается и на данный момент не работает полностью. То есть удалить товар может и получится, а вот добавить - вряд ли. Поэтому пользователям OpenCart 1.5.1.3 лучше подождать обновлений или следующей версии.
Для OpenCart 1.5.1.3 в админ-части в список товаров добавлена колонка SKU (по-русски -- артикул или складской номер). В отличие от логики авторов поиск возможен по любому вхождению искомых символов - они могут встречаться в любом месте строчки SKU, а не только с её начала. Не знаю, почему у них везде так, мне гораздо удобнее без этого ограничения.
На свежую установку Opencart 1.5.1.3 можно залить поверх изменённые файлы (которые вы найдёте в папке admin внутри архива): oc1513-admin-sku-column.zip25. Но если вы уже вносили свои изменения в эти файлы, надо вручную внести изменения (см. diff), чтобы не затереть предыдущие исправления.
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 11.04
Release: 11.04
Codename: natty
$ geany --version
geany 0.20 (built on Mar 11 2011 with GTK 2.24.1, GLib 2.28.2, GIO)
Днём предпочитаю что-то вроде Gedit или Tango-light. Только цвет комментариев изменил на серый: легко читающийся на белом, но не бросающийся в глаза.
Самое приятное - в этом наборе много тёмных цветовых тем, удобных для работы вечером и ночью. Пока не выбрал фаворитов, т.к. сейчас ночные посиделки с кодом редки, но планирую сильно сдвинуть рабочие часы на утро, так что вскоре снова пригодятся и будут опробованы.
Я удивлён таким чудовищным сроком. Sitemap.xml был добавлен 11-го числа на http://webmaster.yandex.ua/, и только сегодня (23 сентября) наконец-то сайт попал в поисковую выдачу, а надпись про "сайт успешно добавлен в очередь, ждите" сменилась информацией о том, что столько-то страниц проиндексировано.
Обычно люди говорят о 2-3 днях на индексацию.
Я уж думал, что мы попали в немилость -- предположим, посчитали злостными копипастерами. В той области, кстати, которой мы занялись, выглядеть оригинально по части текстов весьма затруднительно. По фотографиям тоже, поскольку основная масса фото берётся с сайтов производителей и, разумеется, в сотнях магазинов одинаковые. Но мы над этим работаем.
Для сравнения: Google понадобилось меньше дня. Я даже не собирался проверять раньше, чем через сутки, но приятель накатал всем членам команды вопль радости по email :) Мол, ура, всем радоваться - мы попали в поисковую выдачу. Он случайно обнаружил, я даже не успел сказать ему, что занялся поисковиками.
При активном использовании Git-а и переключений между разными ветками часто хочется видеть, в какой ветке находишься. Удобно добавить вывод названия branch-а в подсказку командной строки bash:
rb@rb-msi:~/projects/opencart-a4u (master)$
Для этого в ~/.bashrc находим строку, похожую на PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ ':
if [ "$color_prompt" = yes ]; then
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
else
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi
и изменяем (я закомментировал старую строку и добавил новую):
if [ "$color_prompt" = yes ]; then
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
else
# PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w$(git branch &>/dev/null; if [ $? -eq 0 ]; then echo " ($(git branch | grep '^*' |sed s/\*\ //))"; fi)\$ '
fi
Теперь при попадании в каталог внутри Git репозитория bash prompt будет показывать в скобках имя текущего branch-а, что выглядит примерно так:
Кому нравится способ ввода атрибутов товаров в OpenCart 1.5.1.2? Думаю, что никому: если их хотя бы десяток, начинаешь забывать, путаться или упорно пытаться вспомнить, как этот атрибут назвали ранее. Кабель? Шнур питания? Длина шнура? Ох... Почему по умолчанию везде в админке сделан поиск по "сначалам" названий (и в связях товаров, и в атрибутах, и в рекомендуемых, и... да везде, в общем) -- мне совершенно непонятно. Это неудобно. Мешает, утомляет, раздражает и тормозит.
Некоторым вначале даже может показаться, что в Опенкарт не работает добавление атрибутов: атрибуты ещё не заведены, в подсказке ничего не выпадает. Люди пытаются ввести своё название атрибута и ожидают, что оно сохранится. А оно не сохраняется. И логика поведения неочевидна. На самом деле надо предварительно зарегистрировать атрибуты (см. меню Каталог - Атрибуты), а затем в этих полях выбирать их из выпадающего списка по мере набора первых букв названий атрибутов.
В идеале я бы предпочёл видеть и редактировать атрибуты товаров в виде таблицы. Или хотя бы выпадающие списки. Почти как сейчас, но чтобы не приходилось мучительно вспоминать первую букву или держать рядом страницу со списком. Ну а пока таблицы нет, можно существенно облегчить себе ввод атрибутов, внеся совсем небольшое исправление.
Которое реализует:
поиск любого вхождения введённых символов, а не только с начала названий атрибутов;
при вводе "." в пустое поле показывается полный список атрибутов, остаётся только выбрать необходимый.
У нас в магазине сейчас заведено около 20 атрибутов -- облегчение даже на таком количестве колоссальное.
diff --git a/public_html/admin/controller/catalog/attribute.php b/public_html/admin/controller/catalog/attribute.php
index 637ab6c..8097fc1 100644
--- a/public_html/admin/controller/catalog/attribute.php
+++ b/public_html/admin/controller/catalog/attribute.php
@@ -432,6 +432,7 @@ class ControllerCatalogAttribute extends Controller {
}
}
+ /*
$sort_order = array();
foreach ($json as $key => $value) {
@@ -439,6 +440,7 @@ class ControllerCatalogAttribute extends Controller {
}
array_multisort($sort_order, SORT_ASC, $json);
+ */
$this->load->library('json');
diff --git a/public_html/admin/model/catalog/attribute.php b/public_html/admin/model/catalog/attribute.php
index 1e438ef..019b155 100644
--- a/public_html/admin/model/catalog/attribute.php
+++ b/public_html/admin/model/catalog/attribute.php
@@ -35,7 +35,10 @@ class ModelCatalogAttribute extends Model {
$sql = "SELECT *, (SELECT agd.name FROM " . DB_PREFIX . "attribute_group_description agd WHERE agd.attribute_group_id = a.attribute_group_id AND agd.language_id = '" . (int)$this->config->get('config_language_id') . "') AS attribute_group FROM " . DB_PREFIX . "attribute a LEFT JOIN " . DB_PREFIX . "attribute_description ad ON (a.attribute_id = ad.attribute_id) WHERE ad.language_id = '" . (int)$this->config->get('config_language_id') . "'";
if (isset($data['filter_name']) && !is_null($data['filter_name'])) {
- $sql .= " AND LCASE(ad.name) LIKE '" . $this->db->escape(mb_strtolower($data['filter_name'], 'UTF-8')) . "%'";
+ if( $data['filter_name'] == '.' )
+ $sql .= " AND LCASE(ad.name) LIKE '%'";
+ else
+ $sql .= " AND LCASE(ad.name) LIKE '%" . $this->db->escape(mb_strtolower($data['filter_name'], 'UTF-8')) . "%'";
}
if (isset($data['filter_attribute_group_id']) && !is_null($data['filter_attribute_group_id'])) {
Модификации для 1.5.1.3
diff --git a/upload/admin/model/catalog/attribute.php b/upload/admin/model/catalog/attribute.php
index f38a92e..1d6c8e1 100644
--- a/upload/admin/model/catalog/attribute.php
+++ b/upload/admin/model/catalog/attribute.php
@@ -35,7 +35,10 @@ class ModelCatalogAttribute extends Model {
$sql = "SELECT *, (SELECT agd.name FROM " . DB_PREFIX . "attribute_group_description agd WHERE agd.attribute_group_id = a.attribute_group_id AND agd.language_id = '" . (int)$this->config->get('config_language_id') . "') AS attribute_group FROM " . DB_PREFIX . "attribute a LEFT JOIN " . DB_PREFIX . "attribute_description ad ON (a.attribute_id = ad.attribute_id) WHERE ad.language_id = '" . (int)$this->config->get('config_language_id') . "'";
if (!empty($data['filter_name'])) {
- $sql .= " AND LCASE(ad.name) LIKE '" . $this->db->escape(utf8_strtolower($data['filter_name'])) . "%'";
+ if( $data['filter_name'] == '.' )
+ $sql .= " AND LCASE(ad.name) LIKE '%'";
+ else
+ $sql .= " AND LCASE(ad.name) LIKE '%" . $this->db->escape(utf8_strtolower($data['filter_name'])) . "%'";
}
if (!empty($data['filter_attribute_group_id'])) {
Модификации для 1.5.6
Вообще-то никакой разницы с 1513 нет - просто слегка отличается исходный код (убрали зачем-то LCASE). Поэтому на всякий случай привожу diff для этой версии:
diff --git a/public_html/admin/model/catalog/attribute.php b/public_html/admin/model/catalog/attribute.php
index e527efe..77d8a8b 100644
--- a/public_html/admin/model/catalog/attribute.php
+++ b/public_html/admin/model/catalog/attribute.php
@@ -35,7 +35,10 @@ class ModelCatalogAttribute extends Model {
$sql = "SELECT *, (SELECT agd.name FROM " . DB_PREFIX . "attribute_group_description agd WHERE agd.attribute_group_id = a.attribute_group_id AND agd.language_id = '" . (int)$this->config->get('config_language_id') . "') AS attribute_group FROM " . DB_PREFIX . "attribute a LEFT JOIN " . DB_PREFIX . "attribute_description ad ON (a.attribute_id = ad.attribute_id) WHERE ad.language_id = '" . (int)$this->config->get('config_language_id') . "'";
if (!empty($data['filter_name'])) {
- $sql .= " AND ad.name LIKE '" . $this->db->escape($data['filter_name']) . "%'";
+ if( $data['filter_name'] == '.' )
+ $sql .= " AND ad.name LIKE '%'";
+ else
+ $sql .= " AND LCASE(ad.name) LIKE '%" . $this->db->escape($data['filter_name']) . "%'";
}
if (!empty($data['filter_attribute_group_id'])) {
См. также коментарий #28 - для тех, кто "не понимает, что такое дифф", там в виде "было / стало".
Смотреть можно в любом видео-плеере, обычно сейчас все умеют кроме "Open File" что-то вроде "Open Location", "Open URL", "Open Network Location" и т.п.
Поток примерно 20-50 килобайт в секунду, 640x360. Иногда можно послушать переговоры астронавтов с Землёй :)
UPD: включил в 17:50 - вовсю идут переговоры по-русски вперемешку с английским.
Камер несколько, смотрят в разные стороны - время от времени происходит переключение.
Johnson Space Center - NASA-ISS-push Streaming ISS Video
P.S. Доложили, что Totem 2.28.2 (устанавливаемый в Ubuntu по умолчанию видео-плеер) не показывает поток. С Gnome-mplayer и VLC проблем не замечено. Под Windows не пробовал, но если что-то имеющееся под рукой не воспроизводит -- могу порекомендовать GOM Player (с этим плеером у меня приключений за долгие годы не было). Ну и VLC существует под Windows.
UPD 2015-01: Статья написана в 2011 году для актуальных тогда версий OpenCart. Для более новых описанные изменения могут не подойти. Скорей всего описанные изменения актуальны для всех oc151x (делалось на них), может быть до oc153x (не уверен). Есть информация о том, что для v1564 не подходит. Я не пользовался всей этой линейкой версий и соответственно не делал для них изменения. Если не получается разобраться, можно нанять меня, за 10-15 WMZ сделаю для вашей версии.
Если используется тема, отличная от default, изменения надо производить в её файлах, разумеется (css, tpl). И только в случае отсутствия их в используемой теме их следует искать в default. И в этом случае может понадобиться индивидуальный подход, поскольку CSS может значительно отличаться. Так что будьте особенно аккуратны, делайте резервные копии изменяемых файлов.
Имейте в виду, что оригинальное решение, принятое в Опенкарт, очень неоптимальное по производительности и при большом количестве категорий и товаров этот блок надо переделать и сделать совершенно по-другому. Здесь показан способ, использующий стандартный механизм Опенкарт.
diff --git a/public_html/catalog/controller/common/header.php b/public_html/catalog/controller/common/header.php
index 21bfe6f..a603a5f 100644
--- a/public_html/catalog/controller/common/header.php
+++ b/public_html/catalog/controller/common/header.php
@@ -200,10 +200,11 @@ class ControllerCommonHeader extends Controller {
$children_data[] = array(
'name' => $child['name'] . ' (' . $product_total . ')',
- 'href' => $this->url->link('product/category', 'path=' . $category['category_id'] . '_' . $child['category_id'])
- );
+ 'href' => $this->url->link('product/category', 'path=' . $category['category_id'] . '_' . $child['category_id']),
+ 'children' => $this->getChildrenData($child['category_id'], $category['category_id']) // rb, 2011-09-03: menu 3rd level
+ );
}
-
+
// Level 1
$this->data['categories'][] = array(
'name' => $category['name'],
@@ -237,6 +238,28 @@ class ControllerCommonHeader extends Controller {
}
$this->render();
- }
+ }
+
+ // http://rb.labtodo.com/, 2011-09-03: menu 3rd level
+ private function getChildrenData( $ctg_id, $path_prefix )
+ {
+ $children_data = array();
+ $children = $this->model_catalog_category->getCategories($ctg_id);
+
+ foreach ($children as $child) {
+ $data = array(
+ 'filter_category_id' => $child['category_id'],
+ 'filter_sub_category' => true
+ );
+
+ $product_total = $this->model_catalog_product->getTotalProducts($data);
+
+ $children_data[] = array(
+ 'name' => $child['name'] . ' (' . $product_total . ')',
+ 'href' => $this->url->link('product/category', 'path=' . $path_prefix . '_' . $child['category_id'])
+ );
+ }
+ return $children_data;
+ }
}
?>
\ No newline at end of file
diff --git a/public_html/catalog/view/theme/default/stylesheet/stylesheet.css b/public_html/catalog/view/theme/default/stylesheet/stylesheet.css
index ecee15b..3f89efa 100644
--- a/public_html/catalog/view/theme/default/stylesheet/stylesheet.css
+++ b/public_html/catalog/view/theme/default/stylesheet/stylesheet.css
@@ -343,7 +343,9 @@ label {
#menu > ul > li > a.active {
background: #000;
}
-#menu > ul > li > a {
+#menu > ul > li > a
+/*#menu > ul > li > div > ul > li > a*/
+{
font-size: 13px;
color: #FFF;
line-height: 14px;
@@ -353,7 +355,8 @@ label {
z-index: 6;
position: relative;
}
-#menu > ul > li > div {
+#menu > ul > li > div
+{
display: none;
background: #FFFFFF;
position: absolute;
@@ -366,16 +369,39 @@ label {
border-radius: 0px 0px 5px 5px;
background: url('../image/menu.png');
}
-#menu > ul > li:hover > div {
+#menu > ul > li > div > ul > li > div
+{
+ display: none;
+ background: #FFFFFF;
+ position: absolute;
+ z-index: 5;
+ padding: 5px;
+ margin-left: 50px;
+ border: 1px solid #000000;
+ -webkit-border-radius: 0px 0px 5px 5px;
+ -moz-border-radius: 0px 0px 5px 5px;
+ -khtml-border-radius: 0px 0px 5px 5px;
+ border-radius: 0px 0px 5px 5px;
+ background: url('../image/menu.png');
+}
+#menu > ul > li:hover > div,
+#menu > ul > li:hover > div > ul > li:hover > div
+{
display: table;
}
-#menu > ul > li > div > ul {
+#menu > ul > li > div > ul,
+#menu > ul > li > div > ul > li > div > ul
+{
display: table-cell;
}
-#menu > ul > li ul + ul {
+#menu > ul > li ul + ul,
+#menu > ul > li ul > li ul + ul
+{
padding-left: 20px;
}
-#menu > ul > li ul > li > a {
+#menu > ul > li ul > li > a,
+#menu > ul > li ul > li ul > li > a
+{
text-decoration: none;
padding: 4px;
color: #FFFFFF;
@@ -386,9 +412,12 @@ label {
#menu > ul > li ul > li > a:hover {
background: #000000;
}
-#menu > ul > li > div > ul > li > a {
+#menu > ul > li > div > ul > li > a,
+#menu > ul > li > div > ul > li > div > ul > li > a
+{
color: #FFFFFF;
}
+
.breadcrumb {
color: #CCCCCC;
margin-bottom: 10px;
diff --git a/public_html/catalog/view/theme/default/template/common/header.tpl b/public_html/catalog/view/theme/default/template/common/header.tpl
index eef4ae7..d93120a 100644
--- a/public_html/catalog/view/theme/default/template/common/header.tpl
+++ b/public_html/catalog/view/theme/default/template/common/header.tpl
@@ -123,7 +123,17 @@ DD_belatedPNG.fix('#logo img');
<?php $j = $i + ceil(count($category['children']) / $category['column']); ?>
<?php for (; $i < $j; $i++) { ?>
<?php if (isset($category['children'][$i])) { ?>
- <li><a href="<?php echo $category['children'][$i]['href']; ?>"><?php echo $category['children'][$i]['name']; ?></a></li>
+ <li><a href="<?php echo $category['children'][$i]['href']; ?>"><?php echo $category['children'][$i]['name']; ?></a>
+ <?php if( $category['children'][$i]['children'] ) { ?>
+ <div>
+ <ul>
+ <?php foreach( $category['children'][$i]['children'] as $menu3item ) { ?>
+ <li><a href="<?php echo $menu3item['href']; ?>"><?php echo $menu3item['name']; ?></a></li>
+ <?php } ?>
+ </ul>
+ </div>
+ <?php } ?>
+ </li>
<?php } ?>
<?php } ?>
</ul>
Проделав это один раз, можно добавить в свой бекап файл ~/.gitconfig, в котором настройки и хранятся. Или скопируйте его в Дропбокс, а в домашнем каталоге оставьте симлинк на этот файл.
И не забывайте про существование возможности авторизоваться на гит-сервере с репозиториями с помощью своего публичного SSH ключа: пара генерируется один раз, публичный ключ раздаёте тем, кто должен вас узнавать и пускать без стука, и забываете про ввод паролей.
Если нужен gitk, то перед вторым запуском (первый под Линуксом нужен, чтобы слегка удивиться, проморгаться и взбодриться) исправьте в ~/.gitk интерфейсные шрифты:
set mainfont {{andale mono} 9}
set textfont {{andale mono} 9}
set uifont {clean 10 bold}
set tabstop 4
Чтобы модуль категорий показывал все подкатегории развёрнуто, а не только выбранную, надо изменить CSS стиль шаблона. Если используется шаблон 'default', то:
diff --git a/public_html/catalog/view/theme/default/stylesheet/stylesheet.css b/public_html/catalog/view/theme/default/stylesheet/stylesheet.css
index 3f89efa..690b824 100644
--- a/public_html/catalog/view/theme/default/stylesheet/stylesheet.css
+++ b/public_html/catalog/view/theme/default/stylesheet/stylesheet.css
@@ -730,7 +730,8 @@ a.button:hover span {
color: #333;
}
.box-category > ul > li ul {
- display: none;
+ /* display: none; */
+ font-size: 0.8em;
}
.box-category > ul > li a.active {
font-weight: bold;
Пример того, как модуль категорий будет выглядеть, можно видеть на демонстрационном видео:
чтобы вместо описания товара в каталоге выводился определённый атрибут товара (краткое описание товара). Интересует для OpenCart 1.5.1
Это несложно, изменения в двух файлах приведены ниже.
Как можно заметить, в файле catalog/controller/product/category.php я добавил текст атрибута к описанию. Не совсем то, что просили, но если надо выводить только атрибут -- думаю, несложно догадаться, как это сделать (заменить строчку на $descr_plaintext = $result['attribute_text'];), а несколько предыдущих строк там могут быть убраны. В общем, при наличии минимальных познаний или аккуратно методом тыка это будет несложно сделать, поэтому не описываю.
Изменения базируются на моём коде, а не чистом OpenCart v1.5: в этом месте была правка, но она задокументирована в упомянутой статье и минимальна. Поэтому, думаю, проблем с нахождением места и внесением изменений не возникнет.
Требуется заменить переменную $attr_id_to_descr = 0; на идентификатор своего атрибута, который должен выводиться. Его можно найти в админ-части магазина, внимательно изучив ссылку редактирования нужного нам атрибута: в адресе после токена (token=...) присутствует переменная ...&attribute_id=9 (например). Вот этот идентификатор и надо указать вместо нуля.
diff --git a/public_html/catalog/controller/product/category.php b/public_html/catalog/controller/product/category.php
index 54ad9bc..f94cc43 100644
--- a/public_html/catalog/controller/product/category.php
+++ b/public_html/catalog/controller/product/category.php
@@ -185,6 +185,8 @@ class ControllerProductCategory extends Controller {
{
$descr_plaintext = mb_substr($descr_plaintext, 0, $cut_descr_symbols, 'UTF-8') . ' …';
}
+ if( !empty($result['attribute_text']) )
+ $descr_plaintext .= '<hr />' . $result['attribute_text'];
$this->data['products'][] = array(
'product_id' => $result['product_id'],
'thumb' => $image,
diff --git a/public_html/catalog/model/catalog/product.php b/public_html/catalog/model/catalog/product.php
index 595bfc6..2b19a72 100644
--- a/public_html/catalog/model/catalog/product.php
+++ b/public_html/catalog/model/catalog/product.php
@@ -11,13 +11,40 @@ class ModelCatalogProduct extends Model {
$customer_group_id = $this->config->get('config_customer_group_id');
}
- $query = $this->db->query("SELECT DISTINCT *, pd.name AS name, p.image, m.name AS manufacturer, (SELECT price FROM " . DB_PREFIX . "product_discount pd2 WHERE pd2.product_id = p.product_id AND pd2.customer_group_id = '" . (int)$customer_group_id . "' AND pd2.quantity = '1' AND ((pd2.date_start = '0000-00-00' OR pd2.date_start < NOW()) AND (pd2.date_end = '0000-00-00' OR pd2.date_end > NOW())) ORDER BY pd2.priority ASC, pd2.price ASC LIMIT 1) AS discount, (SELECT price FROM " . DB_PREFIX . "product_special ps WHERE ps.product_id = p.product_id AND ps.customer_group_id = '" . (int)$customer_group_id . "' AND ((ps.date_start = '0000-00-00' OR ps.date_start < NOW()) AND (ps.date_end = '0000-00-00' OR ps.date_end > NOW())) ORDER BY ps.priority ASC, ps.price ASC LIMIT 1) AS special, (SELECT points FROM " . DB_PREFIX . "product_reward pr WHERE pr.product_id = p.product_id AND customer_group_id = '" . (int)$customer_group_id . "') AS reward, (SELECT ss.name FROM " . DB_PREFIX . "stock_status ss WHERE ss.stock_status_id = p.stock_status_id AND ss.language_id = '" . (int)$this->config->get('config_language_id') . "') AS stock_status, (SELECT wcd.unit FROM " . DB_PREFIX . "weight_class_description wcd WHERE p.weight_class_id = wcd.weight_class_id AND wcd.language_id = '" . (int)$this->config->get('config_language_id') . "') AS weight_class, (SELECT lcd.unit FROM " . DB_PREFIX . "length_class_description lcd WHERE p.length_class_id = lcd.length_class_id AND lcd.language_id = '" . (int)$this->config->get('config_language_id') . "') AS length_class, (SELECT AVG(rating) AS total FROM " . DB_PREFIX . "review r1 WHERE r1.product_id = p.product_id AND r1.status = '1' GROUP BY r1.product_id) AS rating, (SELECT COUNT(*) AS total FROM " . DB_PREFIX . "review r2 WHERE r2.product_id = p.product_id AND r2.status = '1' GROUP BY r2.product_id) AS reviews FROM " . DB_PREFIX . "product p LEFT JOIN " . DB_PREFIX . "product_description pd ON (p.product_id = pd.product_id) LEFT JOIN " . DB_PREFIX . "product_to_store p2s ON (p.product_id = p2s.product_id) LEFT JOIN " . DB_PREFIX . "manufacturer m ON (p.manufacturer_id = m.manufacturer_id) WHERE p.product_id = '" . (int)$product_id . "' AND pd.language_id = '" . (int)$this->config->get('config_language_id') . "' AND p.status = '1' AND p.date_available <= NOW() AND p2s.store_id = '" . (int)$this->config->get('config_store_id') . "'");
-
+ $attr_id_to_descr = 0;
+ if( 0 != $attr_id_to_descr )
+ {
+ $sql_attr1 = " p.product_id AS product_id, pa.text as attribute_text, ";
+ $sql_attr2 = " LEFT JOIN " . DB_PREFIX . "product_attribute pa ON (p.product_id = pa.product_id AND pa.attribute_id=" . $attr_id_to_descr .')';
+ }
+ else
+ {
+ $sql_attr1 = "";
+ $sql_attr2 = "";
+ }
+ $query = $this->db->query("SELECT DISTINCT *, pd.name AS name, p.image, m.name AS manufacturer,". $sql_attr1
+ ." (SELECT price FROM " . DB_PREFIX . "product_discount pd2 WHERE pd2.product_id = p.product_id AND pd2.customer_group_id = '" . (int)$customer_group_id . "' AND pd2.quantity = '1' AND ((pd2.date_start = '0000-00-00' OR pd2.date_start < NOW()) AND (pd2.date_end = '0000-00-00' OR pd2.date_end > NOW())) ORDER BY pd2.priority ASC, pd2.price ASC LIMIT 1) AS discount,"
+ ." (SELECT price FROM " . DB_PREFIX . "product_special ps WHERE ps.product_id = p.product_id AND ps.customer_group_id = '" . (int)$customer_group_id . "' AND ((ps.date_start = '0000-00-00' OR ps.date_start < NOW()) AND (ps.date_end = '0000-00-00' OR ps.date_end > NOW())) ORDER BY ps.priority ASC, ps.price ASC LIMIT 1) AS special,"
+ ." (SELECT points FROM " . DB_PREFIX . "product_reward pr WHERE pr.product_id = p.product_id AND customer_group_id = '" . (int)$customer_group_id . "') AS reward,"
+ ." (SELECT ss.name FROM " . DB_PREFIX . "stock_status ss WHERE ss.stock_status_id = p.stock_status_id AND ss.language_id = '" . (int)$this->config->get('config_language_id') . "') AS stock_status,"
+ ." (SELECT wcd.unit FROM " . DB_PREFIX . "weight_class_description wcd WHERE p.weight_class_id = wcd.weight_class_id AND wcd.language_id = '" . (int)$this->config->get('config_language_id') . "') AS weight_class,"
+ ." (SELECT lcd.unit FROM " . DB_PREFIX . "length_class_description lcd WHERE p.length_class_id = lcd.length_class_id AND lcd.language_id = '" . (int)$this->config->get('config_language_id') . "') AS length_class,"
+ ." (SELECT AVG(rating) AS total FROM " . DB_PREFIX . "review r1 WHERE r1.product_id = p.product_id AND r1.status = '1' GROUP BY r1.product_id) AS rating,"
+ ." (SELECT COUNT(*) AS total FROM " . DB_PREFIX . "review r2 WHERE r2.product_id = p.product_id AND r2.status = '1' GROUP BY r2.product_id) AS reviews"
+ ." FROM " . DB_PREFIX . "product p"
+ ." LEFT JOIN " . DB_PREFIX . "product_description pd ON (p.product_id = pd.product_id)"
+ ." LEFT JOIN " . DB_PREFIX . "product_to_store p2s ON (p.product_id = p2s.product_id)"
+ ." LEFT JOIN " . DB_PREFIX . "manufacturer m ON (p.manufacturer_id = m.manufacturer_id)"
+ . $sql_attr2
+ ." WHERE p.product_id = '" . (int)$product_id . "' AND pd.language_id = '" . (int)$this->config->get('config_language_id')
+ ."' AND p.status = '1' AND p.date_available <= NOW() AND p2s.store_id = '" . (int)$this->config->get('config_store_id') . "'");
+
if ($query->num_rows) {
return array(
'product_id' => $query->row['product_id'],
'name' => $query->row['name'],
'description' => $query->row['description'],
+ 'attribute_text' => ( (0 == $attr_id_to_descr) ? '' : $query->row['attribute_text'] ),
'meta_description' => $query->row['meta_description'],
'meta_keyword' => $query->row['meta_keyword'],
'model' => $query->row['model'],
Примечание: вообще-то пока сделал сортировку не по имени, а по ID производителей: нам этого сейчас хватает. Чтобы сделать именно по названию, нужно небольшое дополнение. Возможно, добавлю его позже.
Изменяемые файлы:
catalog/controller/product/category.php
catalog/language/russian/product/category.php
catalog/model/catalog/product.php
Изменения (diff):
diff --git a/public_html/catalog/controller/product/category.php b/public_html/catalog/controller/product/category.php
index 9ef6331..54ad9bc 100644
--- a/public_html/catalog/controller/product/category.php
+++ b/public_html/catalog/controller/product/category.php
@@ -260,6 +260,19 @@ class ControllerProductCategory extends Controller {
'value' => 'p.model-DESC',
'href' => $this->url->link('product/category', 'path=' . $this->request->get['path'] . '&sort=p.model&order=DESC' . $url)
);
+
+ $this->data['sorts'][] = array(
+ 'text' => $this->language->get('text_manufacturer_asc'),
+ 'value' => 'p.manufacturer_id-ASC',
+ 'href' => $this->url->link('product/category', 'path=' . $this->request->get['path'] . '&sort=p.manufacturer_id&order=ASC' . $url)
+ );
+
+ $this->data['sorts'][] = array(
+ 'text' => $this->language->get('text_manufacturer_desc'),
+ 'value' => 'p.manufacturer_id-DESC',
+ 'href' => $this->url->link('product/category', 'path=' . $this->request->get['path'] . '&sort=p.manufacturer_id&order=DESC' . $url)
+ );
+
$url = '';
diff --git a/public_html/catalog/language/russian/product/category.php b/public_html/catalog/language/russian/product/category.php
index 4e28d8c..762c91e 100644
--- a/public_html/catalog/language/russian/product/category.php
+++ b/public_html/catalog/language/russian/product/category.php
@@ -1,5 +1,5 @@
-<?php
-// Text
+<?php
+// Text
$_['text_refine'] = 'Уточнение поиска';
$_['text_product'] = 'Товары';
$_['text_error'] = 'Категория не найдена!';
@@ -25,5 +25,7 @@ $_['text_rating_asc'] = 'Рейтинг (начиная с низкого)';
$_['text_rating_desc'] = 'Рейтинг (начиная с высокого)';
$_['text_model_asc'] = 'Модель (А- Я)';
$_['text_model_desc'] = 'Модель (Я - А)';
+$_['text_manufacturer_asc'] = 'Производитель (А- Я)';
+$_['text_manufacturer_desc'] = 'Производитель (Я - А)';
$_['text_limit'] = 'Показать:';
?>
\ No newline at end of file
diff --git a/public_html/catalog/model/catalog/product.php b/public_html/catalog/model/catalog/product.php
index c963f3e..8d90fc6 100644
--- a/public_html/catalog/model/catalog/product.php
+++ b/public_html/catalog/model/catalog/product.php
@@ -68,7 +68,7 @@ class ModelCatalogProduct extends Model {
$product_data = $this->cache->get('product.' . $cache . '.' . $customer_group_id);
if (!$product_data) {
- $sql = "SELECT p.product_id, (SELECT AVG(rating) AS total FROM " . DB_PREFIX . "review r1 WHERE r1.product_id = p.product_id AND r1.status = '1' GROUP BY r1.product_id) AS rating FROM " . DB_PREFIX . "product p LEFT JOIN " . DB_PREFIX . "product_description pd ON (p.product_id = pd.product_id) LEFT JOIN " . DB_PREFIX . "product_to_store p2s ON (p.product_id = p2s.product_id) WHERE pd.language_id = '" . (int)$this->config->get('config_language_id') . "' AND p.status = '1' AND p.date_available <= NOW() AND p2s.store_id = '" . (int)$this->config->get('config_store_id') . "'";
+ $sql = "SELECT p.product_id, p.manufacturer_id, (SELECT AVG(rating) AS total FROM " . DB_PREFIX . "review r1 WHERE r1.product_id = p.product_id AND r1.status = '1' GROUP BY r1.product_id) AS rating FROM " . DB_PREFIX . "product p LEFT JOIN " . DB_PREFIX . "product_description pd ON (p.product_id = pd.product_id) LEFT JOIN " . DB_PREFIX . "product_to_store p2s ON (p.product_id = p2s.product_id) WHERE pd.language_id = '" . (int)$this->config->get('config_language_id') . "' AND p.status = '1' AND p.date_available <= NOW() AND p2s.store_id = '" . (int)$this->config->get('config_store_id') . "'";
if (isset($data['filter_name']) && $data['filter_name']) {
if (isset($data['filter_description']) && $data['filter_description']) {
@@ -113,7 +113,8 @@ class ModelCatalogProduct extends Model {
'p.price',
'rating',
'p.sort_order',
- 'p.date_added'
+ 'p.date_added',
+ 'p.manufacturer_id'
);
if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
Eclipse - монстрообразинко. Шевелится неторопливо, интерфейс разлапистый и на любителя. Сперва поставил полный (sudo apt-get install eclipse), а из него - последний доступный PDT (3.x.x) через Help - Install new software. Получил постоянные ошибки при попытках открыть PHP файлы, доустановил какой-то WST (на него были намёки в сообщениях об ошибках). Никаких изменений в лучшую сторону.
Вчера не выдержал, удалил всё и попробовал ограничиться малым: оставил только минимум эклипса (sudo apt-get remove eclipse && sudo apt-get autoremove && sudo apt-get install eclipse-platform), а PDT взял с Galileo репозитория (там только 2.x.x какая-то версия доступна, слово Galileo присутствовало на заставке Эклипса при запуске, хотя второй репозиторий не сам же собой появился). Наконец-то после всех его рестартов увидел Эклипс в работоспособном состоянии. Хотя на некоторых PHP файлах его всё равно плющит (например, он не может открыть index.php из приложения Yii фреймворка).
Поживёт пока. Знакомство с Yii Framework пока только добавляет плюсов к впечатлениям о CodeIgniter. С Eclipse пользоваться и изучать Yii и всю его перенавороченную иерархию странностей будет полегче, надеюсь.
Chive is a next generation MySQL database management tool. The web-based user interface supports most of the common operations needed by software developers and database admins (databases, tables, indices, keys, triggers, views, routines, privileges management, import/export).
Распробовал Git: он действительно оказался во многом гораздо удобнее, чем SVN. В первый день мне не очень понравилась большая, чем в Subversion, атомарность действий и необходимость каждый раз добавлять изменённые файлы в коммит. Я понимал, что это сулит и чем может быть полезно, но сперва показалось неудобным: ты машина — ты и следи/добавляй, раз всё известно, зачем мне лишняя рутина? Позже у коммита обнаружился ключ -a вдобавок к "git add .", так что по этому поводу ворчать стало некуда. Ну а потом всё-таки понравился этот мелочный контроль (можно некоторые файлы держать у себя изменёнными для экспериментов и не пускать их в репозиторий).
Что нравится и заметно в первую очередь в Git после SVN, так это:
деление на оффлайн и онлайн работу: здесь можно комфортно коммитить сколько угодно локально, затем слить всё пачкой из дома или из офиса. Мне как любителю работать с ноутбуком и нелюбителю зря переплачивать за мобильный трафик это очень удобно (хотя основная причина скорее быстрая разрядка батарей телефона при постоянном или частом использовании GPRS/EDGE/3G). См. также пункт 5 этого списка;
служебных папок нет по всему дереву каталогов (как в SVN), есть только одна в корне проекта (репозитория). При работе с SVN приходилось перед массовой заливкой на сервер не забывать очищать дерево каталогов от этих .svn -- и места занимают прилично, да и не дело их на сервере держать. А если и держать, то надо не забывать блокировать через .htaccess. См. также пункт 5;
в SVN локальная рабочая копия очень жёстко привязана к абсолютному пути на диске, поэтому любая попытка скопировать или перенести папки внутри копии обычными средствами, а не командами SVN, гарантируют массу приключений и неприятных открытий;
branch и merge устроены гораздо проще и логичней -- пользоваться ими хочется сразу же. В SVN они вроде бы тоже есть, но столько лишнего надо в голове держать, что писать километровые пути просто сил никаких нет. В итоге при работе в командах до десятка человек мы работали только с транком и пользовались буквально 3 командами: checkout первый раз и update/commit в процессе. Хватало. Но git branch -- это очень даже. Концепция и сценарии работы очень понравились, они гораздо удобней и при самостоятельной работе, и для 2 человек. Не говоря уже про более обширные команды;
простота и удобство создания репозиториев: буквально зашёл в папку с проектом, git init && git add . && git commit -m 'initial commit' -- и вот у нас уже новый репозиторий. Не связанный пока никак с онлайн, но зато уже сразу можно работать с кодом, удобно следить за изменениями и пользоваться ветками. Процесс заведения репозиториев в SVN у меня был гораздо дольше, ну и онлайн в случае SVN обязателен, что для меня далеко не всегда плюс: иногда это здорово мешает. Так что я очень доволен этой разницей.
служебные файлы SVN занимают столько же места, сколько и отслеживаемые, удваивая таким образом объём папки проекта (было 100 мегабайт - стало 200). Что с ветками - не знаю, не пользовался ими под SVN. Репозиторий Git'а раза в 2 с хвостиком МЕНЬШЕ отслеживаемого проекта. Из очень приятного: этот репозиторий (папку .git в корне проекта) очень легко сбекапить или перенести на другой компьютер, где его можно восстановить даже в глухой деревне без онлайна (git checkout master -- и из пустоты рядом с .git мгновенно возникает весь проект). См. также пункты 1 и 2.
Есть и существенный недостаток. При использовании Git каждый человек держит у себя всю копию репозитория. У нас при работе в Proxistep была актуальна проблема больших графических файлов. С ними в команде работал обычно один, от силы два человека: художник-дизайнер и верстальщик. И верстальщику в итоге нужна, условно говоря, всего одна картинка после всех согласований с клиентом. А дизайнеру совершенно не нужен код. А остальным разработчикам (3-4 человека) -- дизайн. Наблюдать и выкачивать все эти изменения и версии дизайна было неудобно и тяжело. Это тормозило и было большинству команды совершенно неинтересно, отнимало время, а в некоторых проектах - весьма заметное место на диске. К тому же обычным состоянием разработчика являлось участие в нескольких текущих проектах и часть недавних проектов тоже лежала в состоянии быстрой готовности.
Сейчас, конечно, полегче - скорости доступа к интернет заметно выросли и объёмы HDD растут год от года. Но иногда это может стать проблемой.
SVN в принципе способен частично решить эту проблему: в нём, в отличие от Git, можно делать checkout-ы и commit-ы отдельных папок репозитория, поэтому на первый взгляд достаточно всего лишь разумно разделить по каталогам работу. Проблема лишь в том, что некоторым ролям нужны разные наборы каталогов, и если кто-то оказался "на стыке", сделать одним коммитом изменения в 2 разных каталогах уже не получится: придётся видеть в общем репозитории 2 коммита вместо одного. Проблема на самом деле мелкая и скорее эстетическая, но на всякий случай стоит иметь в виду.
Для повседневной работы мне хватает с головой командной строки git'а и Meld для сравнения, но на всякий случай скажу про знакомые мне GUI инструменты для работы с Git:
И не забудьте про git-svn! Этот пакет даёт нам в распоряжение такую замечательную команду, как git svn clone <SVN_repo_URL> /path/to/git/repo, которая позволит создать Git-репозиторий из SVN-репо, сохранив всю историю коммитов. Безусловно полезная при миграции вещь.
Пригласили в проект, где используется Git VCS и Yii Framework. Поэтому на некоторое время объявляю себя потерянным для мира, но открытым для полезной информации по этим темам. Советуйте, если есть что-то полезное на ваш взгляд.
Yii обещает прям какую-то гигантскую производительность. Пока ничего такого не заметил: имеющаяся минимальная заготовка проекта на Yii на глаз шевелится медленней моих CodeIgniter проектов, местами более нагруженных неоптимальными запросами (о которых я знаю и в планах стоит поправить логику в нескольких местах, но и без этого работает замечательно. Даже у хостера, который пока отказывается включть mod_deflate у апача).
Через Paypal можно заплатить человеку, даже если у вас самих нет счёта в PayPal. Достаточно всего лишь пластиковой карты, которой можно расплачиваться в интернет. А это есть наверное у каждого в наше время.
К сожалению, платёжный интерфейс пайпэла не очень-то очевиден и удобен, поэтому немного опишу процесс. Кнопку Donate можно использовать для перечисления любой удобной вам суммы. Если нажать на неё, удерживая при этом на клавиатуре Shift, то окно Paypal откроется в новом окне: возможно, так вам будет удобнее изучить, что к чему.
Первую же страницу, которая появляется после нажатия кнопки Donate, надо прокрутить вверх: в расположенной там форме надо просто ввести сумму (Donation Amount), которую хотите перевести человеку (что за человек - более-менее понятно по его ID в левом верхнем углу: в моём случае это адрес почты на GMail) и нажать кнопку Update Totals.
Теперь в левой части страницы более чётко видны эмблемы популярных платёжных карт. Нам именно туда и надо: рядом с ними находится надпись на английском, перевод которой звучит так: "У Вас нет счёта в PayPal? Используйте кредитную/дебетовую карту или банковский перевод (там, где он доступен)". Дело в том, что для разных стран доступны разные варианты осуществления платежа, но пластиковая карта как наиболее распространённый способ обычно доступен повсеместно. Так вот, самая главная ссылка расположена здесь и является наверное самой незаметной на странице: нажимаем на Continue.
Попадаем на более знакомо выглядящую страницу. Осталось выбрать вашу страну...
И вот наконец-то можно выбрать более удобный язык интерфейса (в правом верхнем углу)
Ну а дальше всё более-менее очевидно и понятно
Думаю, дальше пояснения не требуются. Если вы просто экспериментировали и всё стало понятно, можно закрыть страшную страницу. Или перечислить мне мешок денег :)
Добавляем визуальную индикацию текущей выбранной категории в главном меню магазина. Учитывается только первая указанная в пути категория, возможные альтернативы не обрабатываются.
Это решение также убирает "черные ромбики" и иероглифы в конце русских описаний товаров в каталоге. В других местах делается аналогично.
Вероятно, вас тоже удивляет, почему описания товаров, которые выводятся при просмотре каталога магазина, так бездарно и коротко обрезаются. Ведь даже в стандартном дизайне OpenCart v1.5 там могло бы поместиться ещё 2-3 таких же строки с весьма полезной информацией как для покупателя, так и для поисковых систем. Впрочем, и для внешнего вида стандартного шаблона польза немалая.
В основном, конечно, выигрывает посетитель сайта: уменьшается необходимость открывать каждый товар, а у владельцев магазина появляется возможность вынести в начало описания самую важную информацию и дать покупателю удобный способ просмотра основных характеристик похожих товаров уже на этапе ознакомления с ассортиментом, при навигации.
Вот как выглядит страница каталога с описаниями товаров до и после предлагаемой модификации:
<
p>Чтобы увеличить длину описаний товаров на страницах каталога, необходимо:
найти поиском строку "substr" (она там одна, OpenCart v1.5.0.5 — 1.5.1.1). Вы увидите небольшой блок из десятка строчек, который начинается с $this->data['products'][] = array(. Перед этой строчкой вставляем 6 строк, приведённых ниже, сразу после этого списка;
Найденную строку 'description' => ... заменяем на 'description' => $descr_plaintext,
Длина описания регулируется переменной $cut_descr_symbols. В зависимости от дизайна вам может потребоваться изменить эту величину.
Что исправлено:
До модификации строки укорачивались до 100 символов. Описания длиной 300-500 символов выглядят гораздо лучше;
символы ".." добавлялись даже к полностью поместившимся описаниям: сейчас символ заменён на HTML-ное троеточие и добавляется только к обрезанным описаниям;
ну и mbstring, разумеется. Про которую англоязычные авторы часто забывают.
Аналогичным образом стоит сразу же изменить файлы:
catalog/controller/product/search.php
catalog/controller/product/special.php
catalog/controller/product/compare.php
catalog/controller/product/manufacturer.php
То есть найти похожие места (например, по строке "substr") и внести аналогичные изменения.
Они закомментированы (символ "#" в начале строки). Раскомментируйте их все (в идеале). Если возникнет ошибка - закомментируйте и пробуйте включать по одной строке. Ту, на которой возникает ошибка, оставьте закомментированной. Возможно, это поможет решить проблему проще и быстрее.
Просто не у всех хостеров это работает. Либо спросите в техподдержке, как этот код в .htaccess правильно указать и возможно ли это. У некоторых вместо php_value применяется php_admin_value, у кого-то запрещено совсем. А у кого-то это делается через php.ini или может через панель управления.
Ну вот, приплыли. А я держу Firefox считай только из-за него.
http://web-standards.ru/news/384/
Джон Бартон (John J Barton), один из главных разработчиков Firebug, прекращает работу над популярным отладчиком и уходит из IBM в Google для работы вместе с командой Chrome «над новым поколением инструментов для разработки». В своём прощальном письме Джон говорит, что не видит смысла конкурировать с отладчиками, встроенными в браузеры.
Мне Firefox нужен из-за HTML Validator, Firebug и немного YSlow for Firebug. А основным броузером уже лет 10 является Opera. Медленным Firefox'ом после Оперы пользоваться очень трудно, а для исправления интерфейса (чтобы сделать работу удобней и на ноутбуке, и на десктопе) надо ещё найти и поставить неизвестное количество плагинов, которые его явно не ускоряют.
Перевёл пользовательскую часть OpenCart v1.5.0.5. Админку не трогал: на её перевод сейчас нет времени. Возможные проблемы и неточности:
Wishlist перевёл как закладки, ничего более подходящего и короткого не придумывается, а по смыслу подходит прекрасно;
в районе налогов скорей всего ошибка: в оригинале было "Ex Tax". Я не знаю, что это такое, хотя может оказаться, что "excluding tax", т.е. без НДС или что-то в этом роде. В общем, в этих местах я писал "Без НДС""НДС", и скорей всего это неправильно. Мы в своём магазине ещё будем это смотреть и проверять с партнёрами, а я пока предупреждаю, что здесь может быть засада.
Мне не понравилось стандартное оформление сообщений пары способов доставки (2 практически одинаковые строчки) при оформлении заказа, поэтому принял несколько спорное решение: включил цену доставки прямо в сообщение. То есть цена указана прямо в языковых файлах и изменить её из админки не удастся. Получилось замечательно, но имейте этот момент в виду. В языковых файлах всего в паре мест должна встречаться строчка "грн". Поэтому достаточно найти и убрать упоминания "грн" и конкретной стоимости в 4 файлах:
catalog/language/russian/shipping/free.php
catalog/language/russian/shipping/flat.php
admin/language/russian/shipping/free.php
admin/language/russian/shipping/flat.php
Базовым вариантом послужил русский перевод пользователя alsrmurad от 22 июня 2011 года. Но многие строки там были предельно кошмарны и с ошибками: то ли машинный перевод, то ли переводчик не русский, а просто знает язык недостаточно хорошо. Или банально спешил выложить. Во всяком случае я по-любому благодарен автору за частично сделанный перевод: это сэкономило немало времени и избавило от приличной части рутинных операций.
UPD 2011-07-29: Очередное обновление замеченных ошибок, неточностей и корявостей. На этом я прощаюсь с версией 1.5.0.5 и мигрирую на 1.5.1.1 - они очень похожи, но мелкие отличия в языковых файлах есть. Собираюсь держать свою версию перевода 1.5.1 здесь же рядом с 1.5.0.5, и может быть на этот раз будет время заняться административной частью. Я присоединился к команде переводчиков ocStore (русской сборки OpenCart) на assembla.com (см. выше про SVN), поэтому частично мои правки попадают туда (коммичу не все свои варианты, и местами там не все варианты нравятся), частично многое я возьму оттуда. Но так как там обновляется только текущая версия перевода, а ветки по конкретным версиям не хранятся, я решил продолжать держать у себя те версии, с которыми работаю, в виде отдельных архивов.
UPD 2011-09-16: Исправлен ошибочный перевод в админке, также ранее вносились мелкие изменения.
UPD: Украинским переводом занимаются парни с opencart-ua.org
подарите любую небольшую сумму денег (WM, YM, QIWI, PayPal/Visa/Mactercard). Это позволит перераспределить рабочее время и уделить больше внимания переводу и новым публикациям в блоге;
зарегистрируйтесь по моей партнерской ссылке в Dropbox - мы оба получим дополнительные +250 мегабайт для хранения файлов.
сообщайте об ошибках и предлагайте варианты в Issues - этот вариант не требует никаких специальных знаний;
для небольших исправлений можете найти необходимый файл и отредактировать его или оставить комментарий к конкретным строкам;
пользователи, знакомые с работой Git, могут пользоваться пулл-реквестами для предложения своих исправлений и дополнений.
Установка
Скопируйте содержимое архива (папки admin и catalog) в каталог интернет-магазина.
Зайдите в админку и добавьте ещё один язык, русский: System / Localisation / Languages / Insert. Все поля появившейся формы должны быть пустые. Заполните, как показано ниже:
Language Name: Русский
Code: ru
Locale: ru,ru_RU,ru_RU.UTF-8
Image: ru.png
Directory: russian
Filename: russian
Status: Enabled
Sort Order: 1
и нажмите кнопку Save
Проверьте работоспособность добавленного языка: перейдите в магазин и переключитесь там на русский язык. Есил всё отображается без сообщений без ошибок и по-русски, можете переходить к следующему пункту. Если возникают какие-либо ошибки - не переключайте язык админки на русский ни в коем случае, пока не разберётесь с причиной и не исправите ошибки.
перейдите на вкладку System / Settings / [Edit] / Local и выберите в выпадающих списках Language и Administration Language параметр "Русский" (чтобы созданный язык использовался в интерфейсе магазина и в админ-части)
Если у вас проблема с установкой, и не можете справиться
Если не можете найти ошибку и есть возможность - дайте мне доступ к FTP и админке Опенкарта, я проверю. Приватное сообщение мне можно отправить со страницы Контакт.
Если разбираетесь, что к чему у хостера -- лучше сделать для меня временную учетную запись для доступа к FTP/базе (и в Opencart тоже можно заодно отдельный логин в группе администраторов), чтобы не раздавать свой основной логин/пароль. А после того, как всё закончится - вы просто удаляете этот временный доступ.
Желательно, по возможности, чтобы в имени пользователя и пароле к FTP не использовались символы `@` и `:` - они сильно замедляют мне работу, приходится пользоваться неудобными инструментами.
Написал плагин для MaxSite CMS, который добавляет кнопку Facebook Like внизу страниц.
Настройки в админке ещё не все внёс. Пока хочу найти причину, почему кнопка Like работает не на всех страницах?!?! (Update: уже работает, видимо это были временные проблемы Facebook.) Может дело в самом блоге, которому всего пара дней от роду? Но раз некоторые страницы добавляются, то дело не в коде кнопки и не в плагине, поэтому выкладываю AS IS.
Странно, что его нет в стандартном комплекте, ведь на http://maxsite.org/ эти кнопки присутствуют. Ну и гуглом, разумеется, ничего не нашёл.
По умолчанию поиск в OpenCart производится только по названиям продуктов. Что довольно неудобно для покупателя и не оправдано, если продуктов на сайте не так уж и много (думаю, на нескольких сотнях наименований никакой разницы не будет заметно).
Поиск по описаниям и подкатегориям
На странице расширенного поиска есть возможность включить поиск по описаниям товаров и во всех категориях, но по умолчанию эти опции отключены и чаще всего пользователь видит отсутствие результатов.
Для того, чтобы исправить поведение поиска по умолчанию в версиях OpenCart от v1.5.0.5 до 1.5.2 (а может и в более поздних, но на данный момент я знаю только про эти), надо отредактировать файл catalog/view/javascript/common.js:
Opera Dragonfly (инструмент для веб-разработчиков) в какой-то момент стала веб-приложением и без наличия интернета нельзя воспользоваться удобным инструментарием. Что весьма неудобно, если вы любитель отлипнуть от розетки и кодить где-то, сидя с ноутбуком.
Ситуацию, однако, можно исправить. Для этого надо установить Dragonfly локально, скачав архив с http://dragonfly.opera.com/app/zips/ и указать местоположение файла client-en.xml в настройках Developer Tools.
По умолчанию там адрес https://dragonfly.opera.com/app/
"Как бы автоскролл" лично мне часто гораздо удобнее построчной прокрутки стрелками курсора, не говоря уже о постраничном (пробелом или PgUp/PgDn) при чтении больших текстов. Я жму Alt-Down и текст аккуратно едет, подставляя глазам новые порции без резких скачков. То же самое можно делать и мышью (средней кнопкой, так называемый Panning; под линуксами где-то была настройка, подо что отдать среднюю кнопку - под этот pan&scroll или привычную линуксоидам вставку выделенного текста).
Но есть такие штуки, как ноутбуки, пуза-колени, кресла, шезлонги. Далеко не всегда мышь подключена или ей удобно пользоваться, а вот клавиатура есть и обычно этот способ мне гораздо удобней. А без него начинаются страдания (на всяких странных популярных броузерах, если надо ими пользоваться вместо Оперы).
Ctrl-F8 (скрытие/показ адресной строки) -- способ освободить приличный кусок экрана на ноутбуках и нетбуках, где вертикальное пространство на вес золота. Или быстро восстановить панель адреса во всяких попап-окошках с картинкой, где от тебя скрывают адрес страницы или изображения, а его вопреки желанию автора хочется посмотреть или скопировать.
Покажу инструкцию на пальцах, чтобы меньше писать. Нам сюда:
Ctrl-F12, Advanced , Shortcuts, Keyboard setup
Затем редактируем текущий клавиатурный профиль, открываем в нём секцию "Browser Window"
и добавлем пару новых строчек:
Down Alt
Scroll, 3
F8 ctrl
Set alignment, "document toolbar", 6 | Set alignment, "document toolbar", 0
Предварительно, конечно же, лучше проверить, не используются ли уже эти сочетания -- поле быстрого поиска там вверху виднеется, вот им и пользуемся, попробовав ввести туда, например, "down" или "f8".
Вы не поверите, но есть ещё сайты, которые верят в мифы, придуманные полиграфистами, о том, что шрифты с засечками (serif) удобны и легко читаются даже с экранов. Мои глаза верят в обратное: для экранов -- исключительно sans-шрифты. Тот же Ubuntu Font Family, на который я сейчас плотно подсел (а заодно и подсадил некоторых друзей).
У меня от оригинала, да на маленьком экране, ощущение одно: серые расплывающиеся мелкие буквы, которые тяжело читать и сквозь них надо пробираться. Ни о каком порхании и лёгком восприятии речь не идёт. В основных статьях там чуть получше (Times New Roman побольше размером и выглядит более чёрным, но читать мне его всё равно очень неприятно: глаза постоянно цепляются и спотыкаются о буквы, а не о смысл).
<
p>Итого: выводы каждый может сделать для себя сам, вкусы, DPI и мониторы на столах и в карманах у всех разные, но решение для броузера Opera простое:
создаём CSS файл, внутри которого может быть всего одна строчка: body, .content, p, a { font-family: Ubuntu, Sans !important; font-size: 100% !important }
на сайте, где за буквами не видно информации -- F12, Edit Site Preferences, Display, My style sheet. И выбираем там наш файл
всё
Если не помогает, значит там со стилями дизайнеры уж очень расстарались и надо смотреть внимательней. Но в большинстве случаев должен помочь и этот простой CSS. Разумеется, любители Times New Roman могут таким же образом сменить неудобные им санс-серифы на шрифты с засечками.