Разрабатываем социальную сеть на MODx Revolution

MODx развивается стремительными темпами, и я решил попробовать сделать социальную сеть на этом замечательном движке. Ведь в нем давно уже есть возможность работать с пользователями (регистрация, авторизация и пр.), встроенный механизм сообщений пользователей, он умеет работать с изображениями и много чего еще.

Для начала давайте разберемся с пользователями, дадим им возможность писать статьи, например, или добавлять свои события.

Регистрация пользователей


Для регистрации мы будем использовать сниппет Login.

[[!Register? &postHooks=`addUser` &usergroups=`Users` &submittedResourceId=`9` &activationResourceId=`11` &activationEmailSubject=`Подтвердите регистрацию на сайте` &validate=`email:email:required`]]

О самом сниппете написано уже много, покажу, что делает наш хук addUser:

Сниппет addUser
Сниппет addUser

<?php
$output = "";
$parent = 2;
$template = 3;

// Создаем ресурс для пользователя (его личная страничка)

if ($id = $modx->runSnippet('addResource', array(
	  'pagetitle' => $hook->getValue('username'),
	  'parent' => $parent,
	  'template' => $template,
	  'isfolder' => 1,
	  'published' => 1,
  	  'hidemenu' => 1
	))) {

// Записываем id странички пользователя в поле website
// (хотя можно было использовать и User Extended)
	
  $userId = $hook->getValue('register.user')->get('id');
  $user = $modx->getObject('modUserProfile', array('id' => $userId));
  $user->set('website',$id);
  $user->save();

// А в для самого ресурса создаем TV-параметр, где указываем id пользователя
  
  $modx->runSnippet('tv', array('id' => $id, 'tv' => 1, 'value' => $userId));  

  $modx->cacheManager->refresh();

  $output = $id;

} else {
  
  print '<h1>Can not to create the new user</h1><p>Please, tell us about this</p><h2>Contacts</h2><p>E-mail: <a href="mailto:admin@site.ru">admin@site.ru</a></p>';
  die();

}

return $output;


Сниппет addResource
Сниппет addResource

<?php
// Сниппет будет получать данные из формы методом POST
if (!$pagetitle)   $pagetitle   =  $_POST['title'];
if (!$longtitle)   $longtitle   =  $_POST['longtitle'];
if (!$content)     $content     =  $_POST['content'];
if (!$introtext)   $introtext   =  $_POST['introtext'];
if (!$description) $description =  $_POST['description'];
if (!$parent)      $parent      =  $_POST['parent'];
if (!$isfolder)    $isfolder    =  $_POST['isfolder'];
if (!$hidemenu)    $hidemenu    =  $_POST['hidemenu'];
if (!$template)    $template    =  $_POST['template'];
if (!$menutitle)   $menutitle   =  $_POST['menutitle'];
if (!$published)   $published   =  $_POST['published'];
if (!$publishedon) $publishedon =  date('Y-m-d H:i:s');

// Если некоторые значения не будут указаны,
// они будут установлены по умолчанию
if (!$parent)      $parent = 1;
if (!$template)    $template = 1;

// Устанавливаем префикс для псевдонима
switch ($template) {
  case 1: $prefix = "art"; break;		// Статья
  case 2: $prefix = "id"; break;		// Пользователь
  case 3: $prefix = "id"; break;		// Пользователь
  case 4: $prefix = "event"; break;		// Событие
  case 5: $prefix = "animal"; break;       // Домашнее животное
  default: $prefix = "id"; break;
}

// Создаем ресурс
$newResource = $modx->newObject('modResource');

// Заполняем нужные значения
$newResource->set('pagetitle',$pagetitle);
$newResource->set('longtitle',$longtitle);
$newResource->set('description',$description);
$newResource->set('introtext',$introtext);
$newResource->set('content',$content);
$newResource->set('menutitle',$menutitle);
$newResource->set('template',$template);
$newResource->set('isfolder',$isfolder);
$newResource->set('hidemenu',$hidemenu);
$newResource->set('parent',$parent);
$newResource->set('published',$published);
$newResource->set('alias',$parent.date('YmdHis'));
if ($published) $newResource->set('publishedon',$publishedon);


// Сохраняем ресурс
if ($newResource->save()) {
  $id = $newResource->get('id');
  $newResource->set('alias',$prefix.$id);
  $newResource->save();
// Очищаем кеш, чтобы изменения были видны сразу
  $modx->cacheManager->refresh();
  return $id;
} 

return false;


Сниппет tv
Сниппет tv

<?php
if ($input) {
  // Либо выводим значение TV по id документа
  $q = $modx->newQuery('modTemplateVarResource');
  $q->select(array('value',
		   'tmplvarid',
		   'contentid'))->where(array('tmplvarid'=>$options,
					      'contentid'=>$input));	
  $r = $modx->getObject('modTemplateVarResource', $q);
  
  return $r->value;
  
} else {
  // либо устанавливаем значение TV
  if (!$id) {$id = $_POST['id'];}
  if (!$tv) $tv = $_POST['tv'];
  if (!$value) $value = $_POST['value'];
  if ($value == ' ') $value = '';
  
  if ($modx->getObject('modTemplateVarResource', array('contentid' => $id, 'tmplvarid' => $tv))) {
    $tvar = $modx->getObject('modTemplateVarResource', array('contentid' => $id, 'tmplvarid' => $tv));
    } else {
    $tvar = $modx->newObject('modTemplateVarResource');
    $tvar->set('contentid', $id);
    $tvar->set('tmplvarid', $tv);
  }
  
  $tvar->set('value', $value);
  
  $tvar->save();
  
}



После того, как пользователь подтвердил свой e-mail, направляем его на страничку редактирования информации о себе:

[[!FormIt? &hooks=`editUserPage` &successMessage=`Изменения сохранены`]]
[[$editPage]]

Соответственно, сниппет editUserPage
Сниппет editUserPage
Сниппет editUserPage

<?php
// Берем id ресурса из профиля пользователя
$id = $modx->user->getOne('Profile')->get('website');
$name = $_POST['name'];
if ($name == '') $name = ' ';

// И вносим изменения на страницу
if ($modx->runSnippet('editResource',array('resId' => $id, 'pagetitle' => $name, 'longtitle' => $name, 'menutitle' => $name, 'hidemenu' => 'show'))) {
foreach ($_POST as $key => $value) {
  switch ($key) {
    case 'com_address':  $tv = 2; break;
    case 'com_egrul': 	 $tv = 5; break;
    case 'com_email':	 $tv = 9; break;
    case 'com_federate': $tv = 4; break;
    case 'com_head':	 $tv = 10; break;
    case 'com_inn':	 $tv = 6; break;
    case 'com_kpp':	 $tv = 7; break;
    case 'com_phone':	 $tv = 3; break;
    case 'com_regon':	 $tv = 8; break;
    default: 		 $tv = 0; break;
  }
  if (!$value) $value = ' ';
  if ($tv) $modx->runSnippet('tv', array('id' => $id, 'tv' => $tv, 'value' => $value));
}
// Очищаем кеш, чтобы изменения были видны сразу
$modx->cacheManager->refresh();
return true;
}



Создаем статьи или события пользователя


Пусть у нас пользователи могут добавлять на свою страничку предстоящие и прошедшие события:

[[!FormIt? &hooks=`addEvent` &successMessage=`Изменения сохранены` &submitVar=`addEvent`]]
[[$addEventForm]]


Сниппет addEvent
Сниппет addEvent

<?php
// События будут дочерними ресурсами страницы пользователя
$parent = $modx->user->getOne('Profile')->get('website');
$name = $_POST['name'];
$date = explode('.',$_POST['event_date']);
$publishedon = $date[2]."-".$date[1]."-".$date[0]." 10:00:00";
$introtext = $_POST['description'];
if ($id = $modx->runSnippet('addResource',array('pagetitle' => $name, 'longtitle' => $name, 'introtext' => $introtext, 'menutitle' => $name, 'published' => 1, 'publishedon' => $publishedon, 'parent' => $parent, 'template' => 4, 'isfolder' => 1))) {

// Заполняем любые параметры события, которые нам нужны
foreach ($_POST as $key => $value) {
  switch ($key) {
    case 'event_address': 		$tv = 14; break;
    case 'event_type':			$tv = 12; break;
    case 'event_org':			$tv = 34; break;
    case 'event_peoples':		$tv = 24; break;
    default: 		  			$tv = 0; break;
  }
  if (!$value) $value = ' ';
  if ($tv) $modx->runSnippet('tv', array('id' => $id, 'tv' => $tv, 'value' => $value));
}

$modx->cacheManager->refresh();

// Переадресовываем на просмотр события
$url = "/companies/id".$parent."/event".$id;
header('location: '.$url); exit;
return true;
}



В итоге

На нашем сайте пользователи могут зарегистрироваться, разместить информацию о себе, создавать события или писать статьи.

Что я планирую с этим делать

Дальше я планирую сделать для пользователей возможность загрузки фотографий, добавить функционал для отправки и чтения личных сообщений, как писал об этом Goobs, а так же возможность пользователей регистрироваться на понравившиеся события.
Поделиться публикацией

Комментарии 14

    0
    Сниппет addResource никуда не годится.

    Я уже говорил об этом, и повторяю снова — нужно использовать runProccessor для создания ресурсов и подобных системных действий.

    Это позволит не выставлять вручную дефолтные параметры (процессор будет брать их сам из настроек системы) и, что главнее, будут отрабатывать плагины!

    А плагины в вашем случае очень полезны — ибо позволят гибко фильтровать создаваемый контент, вести статистику и т.д.
      0
      Насколько мне известно, процессоры требуют, чтобы у пользователя были права на создание ресурса, и он был авторизован, а в тот момент, когда пользователь еще даже не зарегистрирован, использовать процессор у меня не получилось…
        0
        Расширить/изменить любой системный процессор можно вот так — смотрите checkPermissions().
          0
          Особо не вдавался в приведенную статью, но по мне это не совсем классно все, потому что контролировать код расширяемых классов гораздо сложнее, чем права отдельных юзеров через интерфейс, тем более если еще и несколько контекстов.

          Я делаю это по-другому:
          1. создаем нужные нам шаблон и политику безопасности.
          2. Создаем системного пользователя, для которого разрешаем те или иные политики.
          3. На входе в нужных нам процессорах просто перегружаем пользователя MODx (вот это на самом деле огроменная дыра в MODx, но с этим ничего не поделать)
          $modx->user = $modx->getObject('modUser', $id);

          И все. Для данного и последующих для него действий будут учитываться политики безопасности именно этого пользователя.

          Создайте раз такого пользователя и дальше без лишних костылей будете управлять доступами к контекста, группам ресурсов и т.п.
            0
            Я никогда не трогаю файлы системы, чтобы можно было без проблем обновить движок.

            Поэтому — расширяю системный класс или процессор, и уже в своем расширении встраиваю необходимый функционал.

            Эти class-based процессоры для того и придуманы, и я считаю их главным достижением Revolution 2.2.
              0
              Ну может быть. У каждого своя реализация.
              Я все-таки очень уважаю систему прав доступов, потому плясать от нее люблю.
                0
                Одно другому только помогает.

                Свой процессор, в нем проверка нужных вам прав — и клиент может обновлять движок и ваш компонент без головной боли.

                При расширении системного процессора, его проверка уже не будет работать, если вы замените ее своей — в этом и смысл, чтобы не хакать ядро.
                  0
                  ОК, покапаю как-нибудь на досуге.
          +1
          Илья, привет!

          Мои уроки не прошли даром? Плотно подсел на MODx? :-)
            0
            О, да — твои уроки были мощным толчком для меня))) Сейчас для ФСКН доделываем блоговую соц. сеть)
              0
              Рад, что не зря время тратил :-)
              А я вот решил с социалкой не заморачиваться, а поставить LiveStreet и сдружить с ним MODx.
              Читал?: habrahabr.ru/post/151540/
        0
        Я Revo не использовал сколько-нибудь серьезно, но Evo для чего-либо многопользовательского — это последний выбор, среди того, что доводилось применять.
        Увидел знакомые темы — &submittedResourceId=`9` &activationResourceId=`11`
        Вспомнил, ужаснулся.
        Просто любопытно — почему именно MODx? Со своей колокольни — в Drupal это проще сделать. И что еще более важно — проще модифицировать и развивать в дальнейшем. Есть еще «более лучшие» варианты для соцсети, наверно.
        Просто тяга к творчеству?
          0
          Тут показан простейший вариант «в лоб» со стандартными сниппетами.
          Это простейший вариант, который, конечно, не подходит для серьезного применения. Скорее — это просто пример гибкости расширений MODX =)

          А вообще, на Revo можно написать что угодно, с Evo его сравнивать как то даже неловко.
          0
          Я что-то прослоупочил упоминание о себе, спасибо)
          У себя в проекте я делал профили по-другому, примерно так. А ресурсами сделал основные страницы интерфейса.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое