Cotonti https://www.cotonti.com Open Source PHP Content Management Framework Cotonti en Sun, 19 Jul 2026 12:57:38 -0000 Controllers Controllers are part of the MVC architecture. They are objects of classes extending from cot\controllers\BaseController and are responsible for processing requests and generating responses. In particular, after taking over the control from applications, controllers will analyze incoming request data, retrieve the necessary data from the database, pass the results to the templating engine, and finally generate outgoing responses.

Controllersare an alternative to the moduleandstandalonehooks.

Actions

Controllers are composed of actions which are the most basic units that end users can address and request for execution. A controller can have one or multiple actions.

Actionsaremethods of the controllerclassstartingwith the prefixaction.

The following example shows a post controller with two actions: view and create:

namespace cot\modules\post\controllers;

use cot\controllers\BaseController;
use cot\exceptions\NotFoundHttpException;

class PostController extends BaseController
{
   public function actionView(): string
   {
        $postId = cot_import('id', 'G', 'INT');
    
       $post = PostRepository::getById($id);
       if ($post === null) {
           throw new NotFoundHttpException;
       }
       $template = cot_tplfile('blog.post');
        $t = new XTemplate($template);
        $t->assign([
            'ID' => $post['id'],
            'TITLE' => htmlspecialchars($post['title']),
            'TEXT' => $post['text'],
        ]);    
        
        $t->parse('MAIN');
        return $t->text('MAIN');
   }
   
   public function actionCreate()
   {
       $title = cot_import('title', 'P', 'TXT');
        $text = cot_import('text', 'P', 'TXT');
        
        Cot::$db->insert(Cot::$db->posts, ['title' => $title, 'text' => $text]);
        $id = Cot::$db->lastInsertId();
        
        cot_redirect(cot_url('post', ['n' => 'post', 'a' => 'view', 'id' => $id], '', true));
   }
}

In the view action (defined by the actionView() method), the code first loads the data according to the requested post ID. If the data is successfully loaded, the code will render it using the template engine. Otherwise, an exception will be thrown.

In the create action (defined by the actionCreate() method), the code retrieves the data from the request and saves it. Then, it redirects the browser to the view action with the ID of the newly created post.

Routes

End users address actions through the so-called routes. A route is a string that consists of the following parts:

- Extension code: GET parameter e;
- Controller ID: GET parameter n. A string that uniquely identifies the controller among all other controllers of the same extension;
- Action ID: GET parameter a. A string that uniquely identifies the action among all other actions of the same controller.

Routes take the following format:

https://your-domain.tld?e=extensionCode&n=ControllerID&a=ActionID

So if a user requests with the URL https://your-domain.tld?e=blog&n=posts&a=view&id=123, the view action in theposts controller of the blog extension will be called.

Creating Controllers

Controllers should extend from cot\controllers\BaseController or its child classes.

The following code defines a post controller:

namespace cot\modules\blog\controllers;

use cot\controllers\BaseController;

class PostController extends BaseController
{
}

The controllers are locatedin the controllersfolderin the rootfolder of the extension. The controllers of the adminpart of the site are locatedin the controllers/adminfolder.

Controller IDs

Usually, a controller is designed to handle the requests regarding a particular type of resource. For this reason, controller IDs are often nouns referring to the types of the resources that they are handling. For example, you may use article as the ID of a controller that handles article data.

By default, controller IDs should contain these characters only: English letters in lower case, digits, underscores and hyphens. For example, article and post-comment are both valid controller IDs, while article?, PostComment, admin\post are not.

Controller Class Naming

Controller class names can be derived from controller IDs according to the following procedure:

  1. Turn the first letter in each word separated by hyphens into upper case
  2. Remove hyphens
  3. Append the suffix Controller
  4. Prepend the controller namespace

The following are some examples, assuming the controller namespace takes the default value cot\modules\blog\controllers

  • article becomes cot\modules\blog\controllers\ArticleController;
  • post-comment becomes cot\modules\blog\controllers\PostCommentController;

Controller classes must be autoloadable. For this reason, in the above examples, the article controller class should be saved in the file modules/blog/controllers/ArticleController.php

Default Controller

If the route does not contain a controller ID, IndexController will be called if it exists.

Creating Actions

Creating actions can be as simple as defining the so-called action methods in a controller class. An action method is a public method whose name starts with the word action. The return value of the action method is the response data that will be sent to the end user. The following code defines two actions, index and hello-world

namespace cot\modules\blog\controllers;

use cot\controllers\BaseController;

class PostController extends BaseController
{
   public function actionIndex(): string
   {
       $template = cot_tplfile('blog.index');
       $t = new XTemplate($template);
        
       $t->parse('MAIN');
       return $t->text('MAIN');
   }
   
   public function actionHelloWorld(): string
   {
       return 'Hello World';
   }
}

Action IDs

An action is often designed to perform a particular manipulation of a resource. For this reason, action IDs are usually verbs, such as view, update, etc.

By default, action IDs should contain these characters only: English letters in lower case, digits, underscores, and hyphens (you can use hyphens to separate words). For example, view, update2, and comment-post are all valid action IDs, while view? and Update are not.

You can create actions in two ways: inline actions and standalone actions. An inline action is defined as a method in the controller class, while a standalone action is a class extending cot\controllers\BaseAction or its child classes. Inline actions take less effort to create and are often preferred if you have no intention to reuse these actions. Standalone actions, on the other hand, are mainly created to be used in different controllers.

Inline Actions

Inline actions refer to the actions that are defined in terms of action methods as we just described.

The names of the action methods are derived from action IDs according to the following procedure:

  1. Turn the first letter in each word of the action ID into upper case.
  2. Remove hyphens.
  3. Prepend the prefix action.

For example, index becomes actionIndex, and hello-world becomes actionHelloWorld.

The names of the action methods are case-sensitive. If you have a method named ActionIndex, it will not be considered as an action method, and as a result, the request for the index action will result in an exception. Also note that action methods must be public. A private or protected method does NOT define an inline action.

Inline actions are the most commonly defined actions because they take little effort to create. However, if you plan to reuse the same action in different places, or if you want to redistribute an action, you should consider defining it as a standalone action.

Standalone Actions

Standalone actions are defined in terms of action classes extending cot\controllers\BaseAction or its child classes.

To use a standalone action, you should declare it in the action map by overriding the cot\controllers\BaseController::actions() method in your controller classes like the following:

public static function actions(): array
{
   return [
       // declares "error" action using a class name
       'error' => cot\modules\blog\controllers\actions\ErrorAction::class,
       
       // declares "view" action using a configuration array
       'view' => [
           'class' => cot\modules\blog\controllers\actions\ViewAction::class,
           'viewPrefix' => '',
       ],
   ];
}

As you can see, the actions() method should return an array whose keys are action IDs and values the corresponding action class names or configurations. Unlike inline actions, action IDs for standalone actions can contain arbitrary characters, as long as they are declared in the actions() method.

To create a standalone action class, you should extend cot\controllers\BaseAction or a child class, and implement a public method named run(). The role of the run() method is similar to that of an action method. For example,

namespace cot\modules\blog\controllers\actions;

use cot\controllers\BaseAction;

class HelloWorldAction extends BaseAction
{
   public function run(): string
   {
       return "Hello World";
   }
}

Action Results

The return value of an action method or of the run() method of a standalone action is significant. It stands for the result of the corresponding action.

The return value can be a string that will be used as the body of the response sent to the user.

The following example shows how an action can redirect the user's browser to a new URL:

public function actionForward()
{
   // redirect the user browser to https://example.com
   cot_redirect('https://example.com');
}

Default Action

Each controller has a default action specified via thecot\controllers\BaseController::$defaultAction property. When a route contains the controller ID only, it implies that the default action of the specified controller is requested.

By default, the default action is set as index. If you want to change the default value, simply override this property in the controller class, like the following:

namespace cot\modules\blog\controllers;

use cot\controllers\BaseController;

class PostController extends BaseController
{
   public static $defaultAction = 'home';
   
   public function actionHome()
   {
       return "Hello World";
   }
}

Controller Lifecycle

When processing a request, an application will create a controller based on the requested route. The controller will then undergo the following lifecycle to fulfill the request:

  1. The controller calls the beforeAction() method. If the method returns false, the action will be canceled.
  2. The controller runs the action.
  3. The controller calls the afterAction() method, which passes the result of the action. This method can be used for post-processing the result.
  4. After receiving the result, the application will send it to the user.
]]>
Tue, 28 Apr 2026 08:51:00 -0000
Cotonti Verona 1.0.0 released Whats new:
  • Server events - notify the front end of server events without reloading the page.
  • Pop-up notification about a new private message
  • Controllers
  • Bootstrap and Select2 on board
  • Modal windows and pop-up notifications
  • Error handler - will catch the exception and return the correct headers to the browser
  • \cot\exceptions\ForbiddenHttpException will return a 403 error, and \cot\exceptions\NotFoundHttpException will return a 404 error.
  • The comments plugin has been completely rewritten.
  • A list of recent comments is now available on the main page of the admin panel.
  • Cot::$sys['baseDir'] - Path to the application's root folder.
  • Ckeditor has been updated to version 5.

 

Read more on the release page.

Thanks to everyone who participated in the work on this release, programming and testing

 

For more detailed info see:

]]>
Mon, 27 Apr 2026 07:47:00 -0000
Cotonti Verona 1.0.0 About Verona

If you are a new to Cotonti — please read a brief info about this CMF and its requirements.

See detailed instructions for first-time install or updating guides.

Release info

1.0 release.

Whats new:

  • Server events - notify the front end of server events without reloading the page.
  • Pop-up notification about a new private message
  • Controllers
  • Bootstrap and Select2 on board
  • Modal windows and pop-up notifications
  • Error handler - will catch the exception and return the correct headers to the browser
  • \cot\exceptions\ForbiddenHttpException will return a 403 error, and \cot\exceptions\NotFoundHttpException will return a 404 error.
  • The comments plugin has been completely rewritten.
  • A list of recent comments is now available on the main page of the admin panel.
  • Cot::$sys['baseDir'] - Path to the application's root folder.
  • Ckeditor has been updated to version 5.

 

New tags


modules\forums\tpl\forums.editpost.tpl:

FORUMS_EDITPOST_TITLE
FORUMS_EDITPOST_BREADCRUMBS
FORUMS_EDITPOST_FORM_TOPIC_TITTLE
FORUMS_EDITPOST_FORM_TOPIC_DESCRIPTION
FORUMS_EDITPOST_FORM_TOPIC_EXTRA_FILED
FORUMS_EDITPOST_FORM_TOPIC_EXTRA_FILED_TITLE
FORUMS_EDITPOST_FORM_ACTION
FORUMS_EDITPOST_FORM_TEXT
FORUMS_EDITPOST_EDIT_TIMEOUT
FORUMS_EDITPOST_FORM_EXTRA_FILED
FORUMS_EDITPOST_FORM_EXTRA_FILED_TITLE
FORUMS_EDITPOST_PFS
FORUMS_EDITPOST_SFS
extrafields: FORUMS_EDITPOST_FORM_TOPIC_XXX
extrafields: FORUMS_EDITPOST_FORM_XXX

 

modules\forums\tpl\forums.newtopic.tpl

FORUMS_NEWTOPIC_TITLE - now it is a page title
FORUMS_NEWTOPIC_BREADCRUMBS
FORUMS_NEWTOPIC_FORM_ACTION
FORUMS_NEWTOPIC_FORM_TITLE
FORUMS_NEWTOPIC_FORM_DESCRIPTION
FORUMS_NEWTOPIC_FORM_TEXT
FORUMS_NEWTOPIC_EDIT_TIMEOUT
FORUMS_NEWTOPIC_FORM_EXTRA_FILED
FORUMS_NEWTOPIC_FORM_EXTRA_FILED_TITLE
FORUMS_NEWTOPIC_FORM_PRIVATE
FORUMS_NEWTOPIC_FORM_TOPIC_EXTRA_FILED
FORUMS_NEWTOPIC_FORM_TOPIC_EXTRA_FILED_TITLE
FORUMS_NEWTOPIC_PFS
FORUMS_NEWTOPIC_SFS
extrafields: FORUMS_NEWTOPIC_FORM_XXX
extrafields: FORUMS_NEWTOPIC_FORM_TOPIC_XXX

 

modules/forums/inc/forums.topics.php

Standard pagination tags: PREVIOUS_PAGE, CURRENT_PAGE, NEXT_PAGE etc.
 

modules\forums\tpl\forums.posts.tpl:

FORUMS_POSTS_LOCK_LABEL
FORUMS_POSTS_NEWPOST_PFS
FORUMS_POSTS_NEWPOST_SFS

 

modules\page\tpl\page.add.tpl

PAGEADD_BREADCRUMBS

 

modules\users\tpl\users.register.tpl

USERS_REGISTER_BREADCRUMBS

 

modules\users\tpl\users.tpl

USERS_ACTIVE_FILTERS

 

plugins\contact\tpl\contact.tpl

CONTACT_TITLE

 

plugins\tags\tpl\tags.tpl

TAGS_TITLE
TAGS_BREADCRUMBS

 

system/admin/tpl/admin.extensions.tpl

ADMIN_EXTENSIONS_ADMIN_URL
ADMIN_EXTENSIONS_STRUCTURE_URL

 

Changed tags:
 

modules/page/tpl/page.tpl

PAGE_COMMENTS -> PAGE_COMMENTS_LINK
PAGE_COMMENTS_DISPLAY -> PAGE_COMMENTS

 

modules/page/tpl/page.list.tpl

LIST_COMMENTS -> LIST_COMMENTS_LINK
LIST_COMMENTS_DISPLAY -> LIST_COMMENTS
LIST_ROW_COMMENTS -> LIST_ROW_COMMENTS_LINK

 

modules/polls/tpl/polls.index.tpl

IPOLLS_COMMENTS -> IPOLLS_COMMENTS_LINK

 

modules/polls/tpl/polls.tpl

POLLS_COMMENTS -> POLLS_COMMENTS_LINK
POLLS_COMMENTS_DISPLAY -> POLLS_COMMENTS

 

plugins/recentitems/tpl/recentitems.pages.tpl

PAGE_ROW_COMMENTS -> PAGE_ROW_COMMENTS_LINK

 

Deprecated tags:


system/admin/tpl/admin.extensions.tpl

ADMIN_EXTENSIONS_JUMPTO_URL_TOOLS
ADMIN_EXTENSIONS_JUMPTO_URL_STRUCT

 

modules\forums\tpl\forums.editpost.tpl

FORUMS_EDITPOST_TOPICTITTLE
FORUMS_EDITPOST_TOPICDESCRIPTION
FORUMS_EDITPOST_TOPIC_EXTRAFLD
FORUMS_EDITPOST_TOPIC_EXTRAFLD_TITLE
FORUMS_EDITPOST_PAGETITLE
FORUMS_EDITPOST_SEND
FORUMS_EDITPOST_TEXT
FORUMS_EDITPOST_EDITTIMEOUT
FORUMS_EDITPOST_EXTRAFLD
FORUMS_EDITPOST_EXTRAFLD_TITLE
FORUMS_EDITPOST_MYPFS
extrafields: FORUMS_EDITPOST_TOPIC_XXX
extrafields: FORUMS_EDITPOST_XXX

 

modules\forums\tpl\forums.newtopic.tpl

FORUMS_NEWTOPIC_PAGETITLE
FORUMS_NEWTOPIC_SEND
FORUMS_NEWTOPIC_TITLE
FORUMS_NEWTOPIC_DESC
FORUMS_NEWTOPIC_TEXT
FORUMS_NEWTOPIC_EDITTIMEOUT
FORUMS_NEWTOPIC_EXTRAFLD
FORUMS_NEWTOPIC_EXTRAFLD_TITLE
FORUMS_NEWTOPIC_ISPRIVATE
FORUMS_NEWTOPIC_TOPIC_EXTRAFLD
FORUMS_NEWTOPIC_TOPIC_EXTRAFLD_TITLE
FORUMS_NEWTOPIC_MYPFS
extrafields: FORUMS_NEWTOPIC_XXX
extrafields: FORUMS_NEWTOPIC_TOPIC_XXX

 

modules/forums/inc/forums.topics.php

FORUMS_TOPICS_PAGES
FORUMS_TOPICS_PAGEPREV
FORUMS_TOPICS_PAGENEXT
FORUMS_TOPICS_PAGELAST
FORUMS_TOPICS_PAGECURRENT
FORUMS_TOPICS_PAGETOTAL
FORUMS_TOPICS_PAGEONPAGE
FORUMS_TOPICS_PAGEENTRIES

 

modules\forums\tpl\forums.posts.tpl

FORUMS_POSTS_NEWPOST_MYPFS

 

Removed tags


modules/forums/tpl/forums.editpost.tpl

FORUMS_EDITPOST_TOP_TAGS
FORUMS_EDITPOST_TOP_TAGS_HINT

 

modules/forums/tpl/forums.newtopic.tpl

FORUMS_NEWTOPIC_TOP_TAGS
FORUMS_NEWTOPIC_TOP_TAGS_HINT

 

modules/page/tpl/page.add.tpl

PAGEADD_TOP_TAGS
PAGEADD_TOP_TAGS_HINT

 

modules/page/tpl/page.edit.tpl

PAGEEDIT_TOP_TAGS
PAGEEDIT_TOP_TAGS_HINT

 

Removed tag blocks
 

modules/page/tpl/page.add.tpl
modules/page/tpl/page.edit.tpl
plugins/i18n/tpl/i18n.page.tpl

TAGS

use <!-- IF {PAGEEDIT_FORM_TAGS} --> instead
 

modules/forums/tpl/forums.editpost.tpl

FORUMS_NEWTOPIC_TAGS

use <!-- IF {FORUMS_NEWTOPIC_FORM_TAGS} --> instead
 

modules/forums/tpl/forums.editpost.tpl

FORUMS_EDITPOST_TAGS

use <!-- IF {FORUMS_EDITPOST_FORM_TAGS} --> instead
 

 

For more detailed info see:

Updating from previous versions

  • For updating from previous Siena version — see instructions here.
]]>
Thu, 23 Apr 2026 14:38:00 -0000
Reading Time Plugin Reading Time lets you add estimated page reading time to your Cotonti posts.

Install and configure

  1. Download plugin from the repository, unpack the contents and upload to the plugins folder of the remote server.
  2. Configure plugin settings (field lists, reading speed & optional tags) as needed.
  3. Place TPL-tags in the templates.

TPL-tags generated by the plugin:

All TPL-tags are included in the array produced by the cot_generate_pagetags() function:

  • READING_TIME ("XX minutes" format)
  • READING_TIME_DIG ("XX" format)
  • READING_TIME_TXT ("minutes" format)

Additional pagetags:

  • LENGTH_META_TITLE (length of meta title)
  • LENGTH_META_DESC (length of meta description)
  • LENGTH_TITLE (length of page title)
  • LENGTH_DESCRIPTION (length of page description)
  • LENGTH_TEXT (length of page text)

You can place those in the corresponding templates using prefixes PAGE_, PAGE_ROW_, LIST_ROW_ etc.

]]>
Fri, 16 Jan 2026 09:49:00 -0000
CKEditor 4 Since Cotonti 0.9.26+ we are having CKEditor 5 in the box. Those of you guys who prefer to stick with version 4 can download it as a separate plugin using different name - ckeditor4. This way you will not overwrite it while applying officlal update where the folder with CKE5 is named 'ckeditor'.

Installation:

  1. Download, upload to the plugins folder and install via admin panel
  2. Select ckeditor4 parser in the HTML Parser plugin config
]]>
Mon, 22 Dec 2025 11:12:00 -0000
Faker What is FakerPHP used for?

The FakerPHP library is used to generate fake data for testing and debugging purposes via its properties called formatters. The Faker plugin utilizes the library to generate fake:

  • pages.
  • comments.
  • user accounts.

Installation

  1. Install FakerPHP via Composer: composer require fakerphp/faker
  2. Copy the 'faker' folder to your Cotonti plugins folder.
  3. Install it via Administration / Extensions.
  4. Configure path to FakerPHP and Locale via plugin config

Use

The following options are available to generate fake pages:

  • number of pages to create,
  • page title length,
  • page description length,
  • page_text length,
  • page section.

Page date is set randomly between the first user registration date and the current date.

The following options are available to generate fake comments:

  • number of comments to create,
  • comment text length.

Comment date is set randomly between the first user registration date and the current date. Target page is selected randomly.

The following options are available to generate fake user accounts:

  • number of accounts to create,
  • user group.

Credits

  • Francois Zaninotto for his Faker library
  • Twibie for the first Cotonti implementation
]]>
Sun, 14 Dec 2025 15:18:00 -0000
Top Tags This is a simple plugin-function that is used to output most popular hashtags with the following options:

  1. Customizable output template
  2. Variable number of hashtags
  3. Optional caching

Installation is simple:

  • Download plugin
  • Upload it into the plugins folder
  • Install plugin from within the admin panel
  • Place callback function(s) & customize output template(s)

Callback function

The plugin outputs hashtags via sedby_toptags() function with the following arguments:

sedby_toptags($tpl = 'toptags', $items = 10, $tt_cache = '' $tt_ttl = 0)
  • $tpl defines template file (default toptags.tpl comes with the plugin package);
  • $items defines number of hashtags;
  • $tt_cache and $tt_ttl (if used together) turn on caching for the period of time in seconds specified by $tt_ttl.

Note: the function can use memory cache only if it is enabled in the config.php:

$cfg['cache'] = true;

Otherwise it will generate the output code each time the function is called.

]]>
Fri, 28 Nov 2025 16:42:00 -0000
Varnish Cache plugin Supercharge your Cotonti website with Varnish Cache integration! This plugin seamlessly connects your Cotonti CMS with Varnish Cache, a powerful HTTP accelerator that dramatically improves website performance and reduces server load. Features include auto.

Copy the unarchived file to /plugins/ in the root directory.

Go to the plugins section in the admin panel and activate Varnish Cache.

 

Enter Varnish cache ports in plugin settings.

defaults:

Varnish host:127.0.0.0.1

Varnish admin port:80

Admin port:6082

Server port:6081

Github page: https://github.com/slaweally/Cotonti-varnish-cache/

Author: Ali Çömez / Slaweally

]]>
Mon, 23 Jun 2025 12:37:00 -0000
Reactions İcon

 

Reactions plugin allows users to give emoji-based reactions to content on pages, forums and comments.

It offers fast installation and seamless integration thanks to its fully compatible structure with Cotonti infrastructure.
Daily and per-user limit options.

Installation:
Upload the downloaded file to the plugins folder -> Admin Panel -> Plugins -> reactions -> install

Then add the plugin tags where you want the reactions to appear (such as page - forum):
Forums;
{FORUMS_POSTS_ROW_REACTIONS_BUTTONS}
{FORUMS_POSTS_ROW_REACTIONS_COUNTS}

Pages;
{PAGE_REACTIONS_BUTTONS}
{PAGE_REACTIONS_COUNTS}

You can make updates to the css file according to your request and adapt it to your own theme.

 

]]>
Sun, 27 Apr 2025 10:23:00 -0000
Nexus Cotonti theme Nexus. Modern and mobile-friendly.

Author: Rootali

Github: https://github.com/Cotonti-Themes/nexus

Install

  • Download and copy the "nexus" folder into the "themes" folder of your website.
  • Set $cfg['defaulttheme'] = 'nexus'; in the datas/config.php file.

Theme Github:

Rootali: https://github.com/slaweallx/Nexus

Cotonti Theme: https://github.com/Cotonti-Themes/nexus

]]>
Tue, 22 Apr 2025 11:22:00 -0000
Turkish (TR) Language Pack for Cotonti Siena Turkish (TR) language pack for Cotonti Siena 0.9.25

Github: https://github.com/Cotonti-Langs/turkish


Please contact me by PM if you have any suggestions or corrections of this translation.

Installation:
1. Unpack the downloaded archive.
2. Copy the folders from the archive to the root directory of your website.
3. Open datas/config.php
4. Find

$cfg['defaultlang'] = 'en';

5.Replace

$cfg['defaultlang'] = 'tr';

 

]]>
Wed, 02 Apr 2025 04:38:43 -0000
Cloudflare Turnstile

Features

  • Protects user registration, login, and comments from spam bots.
  • Simple integration into existing Cotonti forms.
  • Admin-configurable through Cotonti admin panel.
  • Modern and secure user verification without compromising user experience.

Requirements

  • Cotonti Siena v0.9.25 or later
  • Cloudflare Turnstile Site Key and Secret Key (Get keys here)

Installation

  • Upload the plugin folder turnstile into your Cotonti plugins directory: Setup is very simple, just add the following validation field to login and input fields like register, comment.

         <!-- Cloudflare Turnstile Captcha -->
         <!-- IF {USERS_AUTH_VERIFY_IMG} -->
         <tr>
             <td colspan="2" class="textcenter">
                 {USERS_AUTH_VERIFY_IMG}
                 {USERS_AUTH_VERIFY_INPUT}
             </td>
         </tr>
         <!-- ENDIF -->
    

     

    Github: Cloudflare Turnstile Plugin

    Plugin Page: Cotonti Cloudflare Turnstile Plugin

]]>
Sat, 08 Mar 2025 05:12:00 -0000
Accepting payments Accepting payments.

When developing e-commerce extensions, such as an online store, paid services or consultations, etc., you need to arrange payment acceptance, which will be convenient to use.

This can be implemented using the "Payments" module.

This module can be integrated with any online payment system: PayPal, UKassa, bank payment systems, etc.
To create a payment, add the following code in the controller of your extension after the order is generated:

$amount = 500; // Payment amount. Pennies are transferred as a fractional part.
$area = 'orderType'; // type of payment (order or service code)
$options = [
    // Optional. When paying for periodic services, if the period is not stored 
    // in your extension, the expiration date of the paid service can be stored 
    // in this field.
    'time' => $months * 30 * 24 * 60 * 60,
    
    // Optional. Description or purpose of the payment.
    'desc' => 'Order description',
    
    // Optional. The order ID on the side of the extension being developed or the user ID.
    // Can be used to identify the payment by your extension.
    'code' => $orderId,
    
    // Optional. Where to redirect the user after successful payment.
    // If not specified, the user will be redirected to their payment history.
    // https://domain.tld/payments?m=balance&n=history
    'redirect' => cot_url('extension-code', ['m' => 'orders', 'id' => $orderId], '', true)
]

cot_payments_create_order($area, $amount, $options);

A pair of values $area and $options['code'] should uniquely identify the payment so that your extension can correctly automatically activate the paid service after payment.

The cot_payments_create_order() function will create a payment and redirect the user to the payment page, where the user will be able to select one of the installed payment system plugins and make a payment.

After making the payment, the user will be redirected to a page with his payments history and there will be shown a message about a successful payment or an error.

Payments

If the $options['redirect'], parameter was passed when creating the payment, then in case of successful payment, user will be redirected to the specified url.

At the moment when the payment status changes to "Paid" the payments.payment.success hook is triggered.
Your extension should handle this hook to automatically activate the paid service. An array with payment data is available to the hook handler. This is an array with data from the cot_payments table containing the payment record being processed..

Please note that this hook is usually called at the moment when the payment system calls the web-hook with payment notification. The request is made from the  payment system's server  and the user making the payment has nothing to do with this request and the session of this user is unavailable at this moment. Therefore, the handler of this hook should not output any data or headers and should not make any redirects.

To display any information to the user, you need to use the URL passed through $options['redirect'], if the standard one is not enough. Usually, when a user goes on this URL, the payment has already been processed and the result can be displayed to the user.

To test your extension integration with the payment module, you can use the Nullbilling plugin. It is a plugin with a test payment system that does not make any payments, but simulates a successful payment.

Connecting payment systems

You can add a new payment system by installing the appropriate plugin. If there is no ready-made one, then you can write your own.

Let's consider creating such a plugin.

The plugin must register itself as a payment system plugin. To do this, it must process the payments.billing.register hook and add its data to the $cot_billings array. For example, like this:

if (
    Cot::$cfg['plugin']['myPaymentSystem']['on']
    && !empty(Cot::$cfg['plugin']['myPaymentSystem']['apiKey'])
    && !empty(Cot::$cfg['plugin']['myPaymentSystem']['secret'])
) {
    $cot_billings['myPaymentSystem'] = [
        'plug' => 'myPaymentSystem',
        'title' => Cot::$L['tbank_title'],
        'icon' => Cot::$cfg['plugins_dir'] . '/myPaymentSystem/images/logo.png',
    ];
}

If the payment system sends payment notification (web-hook) using the POST method, it is necessary to disable xss protection for the web-hook handler, otherwise Cotonti will not accept the request from the payment system. To do this, you need to make the input hook handler with the following code:

if (
    isset($_GET['e'])
    && $_GET['e'] === 'myPaymentSystem'
    && isset($_GET['a'])
    && $_GET['a'] === 'notify'
    && $_SERVER['REQUEST_METHOD'] === 'POST'
) {
    define('COT_NO_ANTIXSS', 1) ;
    Cot::$cfg['referercheck'] = false;
}

Add $_GET parameters for which the payment notification (web-hook) handler is available to the if condition.

The plugin has a standalone hook handler, but it does not output any data to the user, but uses it for other purposes. The "Open" button for this plugin is not needed in the admin panel in the list of extensions. Let's put it away. To do this, add the code to the admin.extensions.plug.list.loop hook handler:

if ($type === COT_EXT_TYPE_PLUGIN && $code === 'myPaymentSystem') {
    $t->assign([
        'ADMIN_EXTENSIONS_JUMPTO_URL' => null,
    ]);
}

And to admin.extensions.details hook:

if ($type === COT_EXT_TYPE_PLUGIN && $code === 'myPaymentSystem') {
    $standalone = null;
    $t->assign([
        'ADMIN_EXTENSIONS_JUMPTO_URL' => $standalone,
    ]);
}

If you need to display any reference information for the site administrator on the plugin settings page in the admin panel, for example, the settings that need to be made in the personal account of the payment system itself, use the admin.config.edit.tags hook

if ($o === COT_EXT_TYPE_PLUGIN && $p === 'myPaymentSystem') {
    $adminHelp = '... Справка по настройке платежной системы ...';
}

And will make the standalone hook handler. It solves 2 tasks, depending on the passed parameters:

1) On the payment method selection page, when clicking on the payment system link, the user is redirecting to this page. The GET parameter pid  contains the local payment ID in the cot_payments table.

Get the payment data:

// Get payment ID
$paymentId = cot_import('pid', 'G', 'INT');
if (!$paymentId) {
    cot_die_message(404);
}

// Get pyment data from DB
$payment = PaymentRepository::getById($paymentId);
if (!$payment) {
    cot_die_message(404);
}

// Check if the payment status allows to make a payment
cot_block(in_array($payment['pay_status'], PaymentDictionary::ALLOW_PAYMENT_STATUSES, true));

The further scenario depends on the API of the payment system. It usually consists of the following: send a request to the payment system to initialize the payment on its side. In response, the payment system returns the URL to which the user must be redirected to make the payment.

Many payment systems allow to transfer URLs in the payment initialization parameters, where needed to redirect the user after payment. You can get them using the following methods: successful payment url - PaymentService::getSuccessUrl($PaymentId) and the fail payment URL - PaymentService::getFailUrl($paymentId).

Before redirecting the user to the payment system's website to make a payment, set the local payment status "In progress" and specify which payment system processes the payment.

PaymentService::setStatus(
    $paymentId,
    PaymentDictionary::STATUS_PROCESS,
    'myPaymentSystem',
    PaymentDictionary::METHOD_CARD,
    $psPaymentId
);

The last 2 parameters are optional. The penultimate one indicates that the payment will be made with bank card, and the last one is the payment identifier passed to the payment system.

Some payment systems require you to pass a unique payment ID each time. And in the case when the payment failed and the user tries to pay again, the old payment ID is no longer accepted. For such payment systems, your plugin must generate a unique payment ID. For example, like this:

$psPaymentId = $paymentId . '-' . time();

We save it in the cot_payments table, passing the 5th parameter to PaymentService::setStatus().

2) When a user makes a payment on the payment system's website, before returning the user back to your site, the payment system sends a payment notification (web hook) with the payment status. It needs to be processed. There is no need to do this in the standalone hook, you can also do it in the ajax hook.

When the notification from the payment system has been processed and the checks have been completed in accordance with the documentation for the payment system, it is necessary to set the local payment status to the "Paid"

PaymentService::setStatus(
    $paymentId,
    PaymentDictionary::STATUS_PAID,
    'myPaymentSystem',
    null,
    null,
    $transactionId
)

here we skip the parameters $paymentMethod, $paymentSystemPaymentId - they were set earlier. But we pass $transactionId (optional) - the identifier of the transaction (or payment) on the payment system side. Most payment systems pass it with payment notification (web-hook).

]]>
Mon, 07 Oct 2024 18:16:00 -0000
Using captcha in extensions When developing an extension, it is often necessary to display some form to unregistered users: a feedback form, "order a call", ask a question to support, etc. Such forms can be filled out and sent to the server by spam bots, thereby filling the database with garbage. A captcha is a very effective way to protect against such bots.

It makes no sense to require registered users to fill out a captcha because bots cannot log in to the site and "spam" all forms. Therefore, you need to display the captcha only in those forms that can be filled by the guests.

Using a captcha in your extensions is pretty simple.

To display a captcha in any form:

Add such code to the your extension's controller:

// Initialize the template engine
$t = new XTemplate($pathToTemlateFile);

// ... some code of your extension ...

// Check if user is not logged in and at least one captcha is installed on the site:
if (Cot::$usr['id'] === 0 && !empty($cot_captcha)) {
	// Output captcha to the template
	$t->assign(cot_generateCaptchaTags(null, 'rverify', 'FEEDBACK_FORM_'));
}

Add tags to the template for captcha output:

<!-- IF {FEEDBACK_FORM_VERIFY_IMG} -->
<div>
	{FEEDBACK_FORM_VERIFY_IMG} {FEEDBACK_FORM_VERIFY_INPUT}
</div>	
<!-- ENDIF -->

And finally, when processing the data sent by the user, you need to check whether the captcha is filled in correctly:

if (Cot::$usr['id'] === 0 && !empty($cot_captcha)) {
	$rverify = cot_import('rverify', 'P', 'TXT');
	if (!cot_captcha_validate($rverify)) {
		// The captcha is not filled correctly
		cot_error('captcha_verification_failed', 'rverify');
	}
}
]]>
Mon, 07 Oct 2024 04:56:00 -0000
SecurImage Captcha

Securimage CAPTCHA plugin for Cotonti CMF. Protects your site from spam bots with image captcha.

Uses the Securimage class (from Drew Phillips)

Authors:

Github: https://github.com/Alex300/SecurImageCaptcha-for-Cotonti

Plugin page on author's site: https://lily-software.com/free-scripts/cotonti-securImage-captcha

Opportunities:

  • You can use this captcha wherever it is used on your site: in the registration form, comments, feedback, etc.
  • The image can be updated without reloading the page
  • Those who cannot read the code can listen to the voice code

A sample of the captcha's work can be viewed, for example, in the registration form of this site.

Installation:

  • copy the captcha folder from the archive to the plugins folder on your hosting
  • install the plugin in the ite admin panel
  • in the site admin panel, under Configuration -> Security -> Captcha, select default "Captcha"

To change the image settings (font, size, background image, noises, etc.), edit the file /inc/imageshow.php

Using captchas in your plugins:

Similar to using any other captcha.

 

]]>
Sun, 06 Oct 2024 05:33:00 -0000
Freelance Exchange bundle Freelance Exchange bundle for CMF Cotonti. With this bundle, you can organize any exchange to search for performers for various jobs. Its  functionality provides flexible options for operation and further development.

Authors: Bulat Yusupov и CMSWorks team, Cotonti team
GitHub: https://github.com/Cotonti-Extensions/freelance

Main features

User accounts with their personal pages (contact information is displayed on the personal page, as well as lists of published projects, works in the portfolio and in the marketplace); Catalog of jobs (job projects), the ability to publish jobs. Jobs search with filtering by region and keywords; Catalog of freelancers and employers sorted by specialization; Payment module with internal user accounts and the ability to replenish and pay for services; Included payment systems: Interkassa, Robokassa and WebMoney (separate plugins) and the ability to easily connect other payment systems; Paid service "PRO account"; Paid service "Paid place on the main page" (Users who have paid for this service are displayed on the main page of the exchange); Secure payment service; Reviews and ratings.

Extensions from this bundle can be installed on existing sites and used individually and only those ones that you need.

Or you can prepare a "build" that will use the built-in Cotonti installer to selectively install extensions from this bundle with the standard Cotonti extensions. This particular option is described below.

Preparing for install

This is not a ready to use website build. To install it, you need the current version of CMF Cotonti, which can be downloaded in the Download section.

  • Download Cotonti. Unpack the archive into the directory of the future site.
  • Download Freelance Exchange bundle and unpack it into the same directory in which Cotonti is unpacked.
  • Set the write permissions for folders and subfolders in the /data directory: /datas/avatars, /data/cache (and all subfolders), /datas/extflds, /datas/photos, /datas/thumbs, /datas/users

Install

  • Open your browser and follow the link: http://your-domain.tld/install.php (replace your-domain.tld with your domain)
  • Follow the on-screen instructions until the installation is complete. During installation, select the flance installation script and bootlance theme.
  • During installation, you will be prompted to select modules and plugins. The most basic extensions that are necessary for the operation of the exchange are ticked, but you can also select the rest if necessary.
  • In the file datas/config.php set the option $cfg['customfuncs'] = true;
  • Be sure to configure the Usergroupselector plugin if your site will divide users into different groups, for example, employers and freelancers. In the settings of this plugin, you need to specify which groups will be available for users to choose when registering or in the profile. If you need to create another user group, then go to the "Users" section of the admin panel.
  • In order to be able to attach files and images to projects (as well as to suggestions in the description and in the portfolio), you must also install the Mavatars and Mavatarslance plugins, which also come as part of the bundle.
  • Initially, your site will be empty. You can create your own categories yourself in the "Structure" section of the admin panel.

Learn more about the possibilities of the Freelance Exchange bundle

Jobs

Projects module

Any user can publish their job project on the site. In the job project, you can specify the location, city, price, section in the catalog (area of activity), the title of the project and its description. You can also attach various files to the project description. Freelancers who are interested in the published job can leave their suggestions on the job project page. The employer can choose the contractor or refuse the project to any Freelancer who left an offer. An employer or Freelancer who has submitted a job project proposal can correspond directly on the project page.

Marketplace

Market module
A sectionforplacingready-to-sellgoodsorservices of the user.Eachproductcontains:title,description,imageandprice. Also, allproducts/services are categorizedin the catalogforeasysearch.

Accepting payments

Payments module

This module is a universal mechanism that allows you to accept payments on your site.

Payments are accepted through Robokassa, Interkassa and WebMoney systems. Connecting other payment systems should not cause serious difficulties for developers.

You can use this module to accept payments in your extensions.

In addition, optionally, each user can have their own personal account on the site with which they can pay for paid services.

Users Portfolios

Folio module
Allows users to exhibit their work as a portfolio. Optionally, the works can be pre-moderated before publication.

Secure settlement system (Risk-free transactions)

Sbr plugin
The plugin allows you to organize on the Freelance Exchange website the opportunity for users to make transactions with each other with full coordination of all stages of work. In this case, the transaction budget is reserved on the website account before the start of all work and is paid to the Contractor after acceptance of the results of work on each stage of the transaction. In case of disputes, either party may contact the arbitration commission (the site administration). The Arbitration commission, by analyzing the internal correspondence on the transaction, makes an appropriate decision on the payment of the work performed to the Contractor, or on the return of the budget for the stage of the transaction to the Customer. The commission may also decide on a partial payment to the parties. In this way, all parties to the transaction are protected.

User roles (groups)

There are two parties involved in the traditional freelance exchange, these are freelancers and customers. But we've improved the system a bit and added the ability to expand the number of service members. Now you can create your own roles on the site and link them to user groups for which you can configure access rights to all sections of the site and define your capabilities for them.

Reviews

Any user of the service can leave feedback to other participants. The review can be either positive or negative, to choose from. Access to the publication of Reviews can be limited only within the framework of completed projects (configurable). By default, users can leave feedback to other users. If necessary, the feedback system can be configured so that reviews can be left only for completed orders, i.e., linked to the project.

User's ratings

In order to identify the most active users among the service participants, a rating system is functioning on the site. Whoever has a higher rating, that user is more visible on the site.

How points are awarded (default settings): For visiting the site: +1 point, For placing work in the portfolio: +5 points, For the status of the contractor for the project: +1 point, For receiving a rejection for the project: -1 point, For receiving a positive review: +20 points, For receiving a negative review: -20 points, For the purchase of a PRO account: +20% to the rating, For the purchase of a paid place on the main page: +20% to the rating. The specified points values can be changed in the site admin panel. The ability to display top users anywhere on the site has also been added.

Paid services

PRO (premium) account (price per month). A PRO account gives you the opportunity to stand out among other exchange participants with a special icon, as well as priority placement in the catalog of freelancers (employers); A paid place on the main page and in any other place on the site (price per placement). Ad blocks can be placed anywhere on the site and set different prices for the size of them. Placement takes place by shifting previously paid and for a period of 1 month. Prices can also be changed in the admin panel.

Categories

The site's admin panel allows you to edit categories for all parts of the site, including: Categories of projects, types of projects (for example: Regular, Vacancies, etc.), categories of freelancers, categories in the marketplace, categories of information pages of the site.

]]>
Tue, 24 Sep 2024 16:40:00 -0000
Database Introduction

Almost any web application interacts with a database. Cotonti makes this interaction simple and comprehensive.

Currently support is provided for MySQL 5.1+

Configuration

The database connection configuration is located in the application's configuration file datas/config.php. It is filled in automatically during Cotonti installation.

Running SQL Queries

You can get access to the Cotonti database connection instance with the facade class Cot. The Cotonti database object is built on top of the PDO and provides method for executing database queries and methods that simplify the construction of INSERT, UPDATE and DELETE queries.

You can execute a SQL query by taking the following steps:

  1. Create a plain SQL query;
  2. Bind parameters (optional);
  3. Call one of the SQL execution methods in PDOStatement class.

The following example shows various ways of fetching data from a database:

// Return a set of rows. Each row is an associative array of column names and values.
// an empty array is returned if the query returned no results
$posts = Cot::$db->query('SELECT * FROM post')->fetchAll();

// Return a single (first or next) row from a result set
// False is returned if the query has no result
$post = Cot::$db->query('SELECT * FROM post WHERE id=?', 1)->fetch();

// Returns a single column from the next row of a result set
// Or false if there are no more rows.
$titles = Cot::$db->query('SELECT title FROM post')->fetchColumn();
$count = Cot::$db->query('SELECT COUNT(*) FROM post')->fetchColumn();

For queries that do not return data, you should call the PDOStatement::execute() method instead. For example:

Cot::$db->query('UPDATE post SET status=1 WHERE id=1')->execute();

For INSERT, UPDATE and DELETE queries, instead of writing pure SQL, you can call the insert(), update(), delete() methods, respectively, to create the specified SQL constructs. For example:

// INSERT (table_name, column_values)
Cot::$db->insert(
    'posts', 
    [
        'titlle' => 'Lorem ipsum', 
        'text' => 'Morbi imperdiet tortor ut nisl ultricies finibus',
    ]
);
$newPostId = Cot::$db->lastInsertId();

// UPDATE (table_name, column_values, condition, params)
$condition = [
	"created_at < '" . date('Y-m-d H:i:s', time() - 3600) . "'",
	'category = :category',
];
$updatedRowsCount = Cot::$db->update(
    'posts', 
    ['status' => 1], 
    $condition, 
    ['category' => $category]
);

// DELETE (table_name, condition, params)
$deletedRowsCount = Cot::$db->delete(
    'posts', 
    'status = 0 AND category = ?', 
    $category
);

insert(), update() and delete() methods do not require escaping the table name. This will be done automatically.

insert() and update() methods do not require escaping array elements with data for insert/update.

update() and delete() methods accept a condition that can be a string with an SQL condition or an array of strings. In the case of an array, each string will be wrapped in parentheses and they are combined with the AND operator.

In the example above:

$condition = [
	"created_at < '2024-07-08 12:45:56'",
	'category = :category'
];

is equivalent to:

(created_at < '2024-07-08 12:45:56') AND (category = :category)

Binding Parameters

When creating a SQL query with parameters, you should almost always use parameter binding to prevent SQL injection attacks. For example:

$post = Cot::$db->query(
	'SELECT * FROM post WHERE category = ? AND status = ?',
	[$category, status]
)->fetch();

When executing the query, each question mark in accordance with its order will be safely replaced with the corresponding value from the array.

If the request has only one parameter, you can pass a scalar value instead of an array:

$post = Cot::$db->query('SELECT * FROM post WHERE category = ?', $category)->fetch();

You can use scalar parameters:

$post = Cot::$db->query(
	'SELECT * FROM post WHERE id = :id AND status = :status',
	['id' => $id, ':status' => status]
)->fetch();

Quoting Table and Column Names

Cot::$db->query(
    'SELECT * FROM ' . Cot::$db->quoteTableName(Cot::$db->pages) 
    . ' WHERE page_cat = ' . Cot::$db->quote($category)
)->fetchColumn();

Quoting methods:

  • Cot::$db->quoteTableName() or short version (Cot::$db->quoteT())
    Quotes a table name for use in a query. If the table name contains schema prefix, the prefix will also be properly quoted.
  • Cot::$db->quoteColumnName() or short version (Cot::$db->quoteC())
    Quotes a column name for use in a query. If the column name contains prefix, the prefix will also be properly quoted. If the column name is already quoted then this method will do nothing.
  • Cot::$db->quote()
    Quotes a string value for use in a query. It is recommended to use parameter binding instead of this method, if possible.

Database Transactions

You may use the Cot::$db->transaction() method to run a set of operations within a database transaction. If an exception is thrown within the transaction closure, the transaction will automatically be rolled back and the exception is re-thrown. If the closure executes successfully, the transaction will automatically be committed. You don't need to worry about manually rolling back or committing while using the transaction method:

Cot::$db->transaction(
	function(): void 
	{
		Cot::$db->update('posts', ['status' = 1], 'category = ?', $category);
		Cot::$db->delete('posts', 'status = 0']);
		// ... executing other SQL statements ...
	}
);

Manually Using Transactions

You can use transactions manually and have complete control over commits and rollbacks and error handling. The example above is equivalent to this:

Cot::$db->beginTransaction();
try {
    Cot::$db->update('posts', ['status' = 1], 'category = ?', $category);
    Cot::$db->delete('posts', 'status = 0']);
	// ... executing other SQL statements ...
    
    Cot::$db->commit();
} catch(\Throwable $e) {
    Cot::$db->rollBack();
	throw $e;
}

Specifying Isolation Level

Cotonti supports setting isolation levels for your transactions. By default, when starting a new transaction, it will use the default isolation level set by your database system. You can override the default isolation level as follows:

$isolationLevel = \cot\database\DataBaseDictionary::REPEATABLE_READ;

Cot::$db->transaction(
	function(): void 
	{
		....
	},
	$isolationLevel
);
 
// or

Cot::$db->beginTransaction($isolationLevel);

Handling Deadlocks

The Cot::$db->transaction() method accepts an optional third argument which defines the number of times a transaction should be retried when a deadlock occurs. Once these attempts have been exhausted, an exception will be thrown:

Cot::$db->transaction(
	function(): void 
	{
		....
	},
	$isolationLevel,
	5
);

Working with Database Schema

(section being updated)

Cotonti database object provides several methods to work with database schema:

  • tableExists($tableName) - check if table exists
  • fieldExists($tableName, $fieldName) - check if fieild exists
  • indexExists($tableName, $indexName, $indexColumns = []) - check if index exists
  • addIndex($tableName, $indexName, $indexColumns = []) - create Index. Here $indexColumns is either a string for a single column name or an array for single/multiple columns. $indexName will be used if empty.

Execution of SQL scripts

The CotDB::runScript($script, $translaction = false) method allows you to execute SQL scripts (contents of SQL files). Table names must be escaped with the MySQL escape symbol ` and have the cot_ prefix, which will be replaced with the prefix from the settings in datas/config.php.

]]>
Sun, 08 Sep 2024 14:51:00 -0000
Cotonti Siena 0.9.25 released This is a stability release that fixes bugs found after the release of 0.9.24. It is also a preparation before the 1.0 release.

Whats new:

  • Support for Php 5.x has been discontinued. The minimum Php version is 7.3
  • Database: transactions support with nesting control
  • cot_user_sqlExcludeBanned() for generate sql condition to exclude banned users. You can use it your extensions.
  • cot_generateCaptchaTags() captcha tag generation unification for the coTemplate
  • Legacy mode ($cfg['legacyMode']) to turn on deprecated features. You can use it if your extensions and/or theme are not fully compatible with the current Cotonti version. Turned off by default.
  • Many minor improvements and fixes

Read more on the release page.

Thanks to everyone who participated in the work on this release, programming and testing

 

For more detailed info see:

]]>
Tue, 03 Sep 2024 06:10:00 -0000
Cotonti Siena 0.9.25 About Siena

If you are a new to Cotonti — please read a brief info about this CMF and its requirements.

See detailed instructions for first-time install or updating guides.

 

Release info

This is a stability release that fixes bugs found after the release of 0.9.24. It is also a preparation before the 1.0 release.

Whats new:

  • Support for Php 5.x has been discontinued. The minimum Php version is 7.3
  • Database: transactions support with nesting control
  • cot_user_sqlExcludeBanned() for generate sql condition to exclude banned users. You can use it your extensions.
  • cot_generateCaptchaTags() captcha tag generation unification for the coTemplate
  • Legacy mode ($cfg['legacyMode']) to turn on deprecated features. You can use it if your extensions and/or theme are not fully compatible with the current Cotonti version. Turned off by default.
  • Many minor improvements and fixes

Template tags changes:

Unified captcha tags.

Unified pagination tags in pfs.admin.allpfs.tpl, polls.admin.tpl, comments.tools.tpl, ratings.admin.tpl, referers.admin.tpl, trashcan.admin.tpl:

{XXX_PAGINATION} -> {PAGINATION}
{XXX_PAGEPREV} -> {PREVIOUS_PAGE}
{XXX_PAGENEXT} -> {NEXT_PAGE}
{XXX_TOTALITEMS} -> {TOTAL_ENTRIES}
{XXX_ON_PAGE} -> {ENTRIES_ON_CURRENT_PAGE}

 

For more detailed info see:

 

Updating from previous versions

  • For updating from previous Siena version — see instructions here.
  • For upgrading from Genoa — see this document (you will also need files from cotonti-legacy to perform the upgrade).

 

 

]]>
Tue, 03 Sep 2024 05:58:00 -0000
Omnis Theme Omnis theme for Cotonti Siena

Omnis theme was built using Bootstrap 5.3.3 and Fontawesome Free 6.5.2. Styles are written with LESS, resulting css-files are minimized. Development and testing were done using Cotonti 0.9.25 beta, all TPL-tags comply with the new naming.

Theme Demo

Dependencies

In order to implement all theme features the following extensions shall be installed:

Official Package:

  • Index
  • Page
  • Users
  • CKEditor
  • Comments system
  • Contact
  • HTML Parser
  • HTML Purifier
  • Math Captcha (or an alternative)
  • Search
  • Tags
  • User Images

3rd party:

Once the installation is complete, create a page in the system section with alias blog and title Blog. This is where all posts will be aggregated using Pagelist plugin.

To highlight a post in the feed create a page extrafield named special_class (type input). When editing such page use specialclass in the edit form to make title bolder & image bigger.

How to use the theme

My viewpoint can be found at the Omnis officlal page (Russian). Nonetheless, since in the recent poll the majority voted for modifying ready-made themes, I see two options here:

Standard

After the installation, download Omnis plugin and load all custom lang-strings, resources and settings (like GTM container ID). This way you can update the theme from the Github repository without fearing to overwrite some of your custom data.

Naturally, you are encouraged to suggest and discuss new features that will provide new functions and capabilities to the theme.

As an Intermediate Product

Unpack css-files, modify LESS code, edit markup, install other plugins, and use Omnis to make your own modifications. No offense.

Todo

So far it's sticky header and font-packed icons.

]]>
Wed, 28 Aug 2024 08:00:00 -0000
Dark Blog Необходимые плагины :

Pagelist, Catlist, Comlist, Forman, Thanks, syntaxhighlighter, attach2, nevalidate, toplastseen, Friendly functions calls

в настройках сайта

 

 

Меню #6
{PHP.cfg.menu6} во всех файлах .tpl:    количество выводимых на главной новых статей. После превышения этого числа включается паджинация

Чтобы страница попала в этот список нужно настроить экстрополе : 

HTML для экстрополя такой (  на картинке выше ) : 

<input type="hidden" name="{$name}" value="{$value_off}" /><label><input type="checkbox" name="{$name}" value="{$value}"{$checked} /> {$title}</label>

 

После публикации статьи нужно зайти в её редактирование и установить галочку как на картинке ниже :

 

Меню #7
{PHP.cfg.menu7} во всех файлах .tpl: количество последних сообщений с форуса

 

Меню #8
{PHP.cfg.menu8} во всех файлах .tpl: количество отображаемых последних статей

 

Меню #9
{PHP.cfg.menu9} во всех файлах .tpl: оличество отображаемых последних комментариев 

 

Как выглядит  ->  здесь

]]>
Tue, 20 Aug 2024 20:17:00 -0000
Using Composer

Cotonti Siena 0.9.23 features Composer support. Composer is a tool for dependency management in PHP.

What is Composer

Composer is a dependency manager designed to simplify uploading and using 3rd party PHP libraries in your project. In instance, it can be used to add all necessary libs into an extension that is being developed.

composer.json is a text file where all side libs (packages) the project is based on are defined. Additionally it contains data on project info, PHP minimum version and required PHP extensions.

composer.lock contains a current list of all installed dependencies and their versions. The main purpose of this file is to save the complete environment in which the project is being developed and tested.

For example, if you work in a team you have to make sure that your colleague having downloaded (pull) the project from a git repository will receive the same environment and package versions that you use.

When a project is deployed to the live environment you have to make sure that it uses the same versions as in the development environment. This ensures that any location your project is deployed in is identical to the one used in the development and helps eliminate errors that might occur due to the versions updates.

Using Composer to Develop Extensions

First off, make sure that Composer is installed.

Next we will add the Guzzle client to a Cotonti extension. This ensures that both sync and async requests are sent to remote servers using the same interface. A great tool for integration with various APIs.

We will also use Flysystem to upload files into a remote storage.

These two are used only as an example with no connection between them.

Let’s begin.

We follow Guzzle installation manual to add section require into the file composer.json located in the project root:

"guzzlehttp/guzzle": "^7.8"

This would result in something like this:

"require": {
    "php": ">=5.6",
    "ext-gd": "*",
    "ext-mbstring": "*",
    "ext-json": "*",
    "ext-hash": "*",
    "ext-pdo": "*",
    "guzzlehttp/guzzle": "^7.8"
},

Next we execute the following code in the command line:

> composer update

This command updates all dependencies installed within the project to the latest version available (as per composer.json), installs new dependencies added to composer.json and removes those that no longer exist. Following that Composer updates composer.lock.

Now our HTTP client is ready for use.

Next we add the following code to our extension:

<?php

use GuzzleHttp\Client;

// Create client instance with base URI
$client = new Client(['base_uri' => 'https://foo.com/api/']);

// Send POST-request application/x-www-form-urlencoded to https://foo.com/api/test
$response = $client->request('POST', '/test', [
  'form_params' => [
      'field_name' => 'abc',
      'other_field' => '123',
      'nested_field' => [
          'nested' => 'hello',
      ],
  ],
]);

$body = $response->getBody();
echo $body;

Dead easy, is it?

Now we proceed to remote uploads.

As specified in the documentation we add section require to composer.json:

"league/flysystem": "^3.0",
"league/flysystem-aws-s3-v3": "^3.0"

Over again:

> composer update

Everything set. We can now use the library in our extension.

<?php

use Aws\S3\S3Client;
use League\Flysystem\AwsS3V3\AwsS3V3Adapter;
use League\Flysystem\Filesystem;

// For some reason AWS adapter works this way only
putenv('AWS_ACCESS_KEY_ID=my-access-key');
putenv('AWS_SECRET_ACCESS_KEY=my-secret-key');

/** @var S3ClientInterface $client */
$client = new S3Client([
    'version' => 'latest',
    'endpoint' => 'https://storage.yandexcloud.net',
    'region' => 'ru-central1',
]);

// The internal adapter
$adapter = new AwsS3V3Adapter(
    $client, // S3Client
    'test-new-bucket', // Bucket name
    'path/to/upload/dir' // path prefix
);


// FilesystemOperator готов и сконфигурирован. Можно использовать.
$fileSystem = new Filesystem($adapter);

// Запись в файл
$filesystem->write($path, $contents);

That is all. Do not forget to specify in the extension installation manual instruction to add the following lines to composer.json and run composer update:

"guzzlehttp/guzzle": "^7.8",
"league/flysystem": "^3.0",
"league/flysystem-aws-s3-v3": "^3.0"

Deploy Project into the Live Server

Our project is ready and is git-managed. Time to move it to the production server.

In the lib/vendor folder we got new subfolders with dependencies added by Composer. Let’s add them into the .gitignore – no need to use them in the repository and increase its size.

Now commit the changes and push them into the repository.

Next we login into the server via SSH and execute:

> git pull

Followed by:

> composer install

Unlike composer update this command shall install all dependencies listed in composer.lock and only the versions specified there. If this file is missing in the project root, this command shall act similar to composer update and use composer.json to install dependencies.

We’re done. You can now open the project in the browser and enjoy the result.

]]>
Fri, 24 May 2024 10:32:00 -0000
Project to Expire The time to project expiration is a string in the "2 days 18:10:45" format. The order and divider can be customized via resource string located in the inc/pte.rc.php

TPL-tags

projects.list.tpt:

...
<!-- BEGIN: PRJ_ROWS  -->
...
Project expires in: {PRJ_ROW_PTE_TO_EXPIRE}
...
<!-- END: PRJ_ROWS  -->
...

projects.tpl:

{PRJ_PTE_TO_EXPIRE}
]]>
Fri, 26 Apr 2024 05:56:00 -0000
Cotonti Siena 0.9.24.2 released Whats new:

  • СoTemplate. Support for multiple function arguments. Now you can use other tags as function arguments. You can write like this:
    {PHP|cot_url('page', 'c=news')|var_dump({PHP.L.Home}, $this, {PHP.cfg.mainurl}, {PHP|cot_url('page', 'c=news ')}, {HEADER_TITLE})}
    The $this support is left in place. You can still use function call chains.
  • СoTemplate. Strict comparison operators: === and !==
  • Fixed a bug where data from the database cache might not be loaded automatically.
  • Static cache improvements.
  • You can now use comments in Url editor's *.dat files
  • Forums API. A new function cot_forums_sqlExcludePrivateTopics() that generates an SQL condition to exclude private topics from the selection, based on the the current user's rights. You can use it in your extensions.
  • The page views counter now works even when the static page cache is enabled.
  • Captcha and posting comments by guests now work when static page cache is enabled.
  • Search. The default sorting is by date descending.
  • Fixed/added meta tags title, description and canonical url in some extensions
  • Function cot_generatePaginationTags() for uniform generation of pagination tags in extensions.
  • Extrafields. Field name validation.
  • CKEditor has been updated to version 4.22.1. In future releases, it will be replaced with version 5.
  • Refactoring of tags in templates has begun. Making tags more understandable and convenient. The old tags are kept for backward compatibility, but will be removed in the future. We recommend updating your themes.
  • The Composer folder has been moved to lib/vendor
  • 1st part of Tags plugin improvements
  • Improvements to the Recent items plugin
  • Other minor improvements
  • Fix for various errors found during operation.

Read more on the release page.

Thanks to everyone who participated in the work on this release, programming and testing

 

For more detailed info see:

]]>
Fri, 02 Feb 2024 12:43:00 -0000
Cotonti Siena 0.9.24.2 About Siena

If you are a new to Cotonti — please read a brief info about this CMF and its requirements.

See detailed instructions for first-time install or updating guides.

 

Release info

Whats new:

  • СoTemplate. Support for multiple function arguments. Now you can use other tags as function arguments. You can write like this:
    {PHP|cot_url('page', 'c=news')|var_dump({PHP.L.Home}, $this, {PHP.cfg.mainurl}, {PHP|cot_url('page', 'c=news ')}, {HEADER_TITLE})}
    The $this support is left in place. You can still use function call chains.
  • СoTemplate. Strict comparison operators: === and !==
  • Fixed a bug where data from the database cache might not be loaded automatically.
  • Static cache improvements.
  • You can now use comments in Url editor's *.dat files
  • Forums API. A new function cot_forums_sqlExcludePrivateTopics() that generates an SQL condition to exclude private topics from the selection, based on the the current user's rights. You can use it in your extensions.
  • The page views counter now works even when the static page cache is enabled.
  • Captcha and posting comments by guests now work when static page cache is enabled.
  • Search. The default sorting is by date descending.
  • Fixed/added meta tags title, description and canonical url in some extensions
  • Function cot_generatePaginationTags() for uniform generation of pagination tags in extensions.
  • Extrafields. Field name validation.
  • CKEditor has been updated to version 4.22.1. In future releases, it will be replaced with version 5.
  • Refactoring of tags in templates has begun. Making tags more understandable and convenient. The old tags are kept for backward compatibility, but will be removed in the future. We recommend updating your themes.
  • The Composer folder has been moved to lib/vendor
  • 1st part of Tags plugin improvements
  • Improvements to the Recent items plugin
  • New setting in config.php: $cfg['legacyMode']. If false, it disables all deprecated functionality.
  • Other minor improvements
  • Fix for various errors found during operation.

 

Template tags changes:

Unified captcha tags:

users.register.tpl
{USERS_REGISTER_VERIFYIMG} -> {USERS_REGISTER_VERIFY_IMG}
{USERS_REGISTER_VERIFYINPUT} -> {USERS_REGISTER_VERIFY_INPUT}

comments.tpl
{COMMENTS_FORM_VERIFYIMG} -> {COMMENTS_FORM_VERIFY_IMG}
{COMMENTS_FORM_VERIFY} -> {COMMENTS_FORM_VERIFY_INPUT}

contact.tpl
{CONTACT_FORM_VERIFY} -> {CONTACT_FORM_VERIFY_INPUT}

 

Unified pagination tags:
{<prefix>PAGINATION}
{<prefix>PREVIOUS_PAGE}
{<prefix>NEXT_PAGE}
{<prefix>CURRENT_PAGE}
{<prefix>TOTAL_ENTRIES}
{<prefix>ENTRIES_ON_CURRENT_PAGE}
{<prefix>ENTRIES_PER_PAGE}
{<prefix>TOTAL_PAGES}

<prefix> can be empty. Or not, if there are more than one paginations on the page.
For example on page.list.tpl:

{LIST_CAT_PAGINATION} for categories list and {PAGINATION} for pages list.


forums.posts.tpl
{FORUMS_POSTS_BREADCRUMBS} - new
{FORUMS_POSTS_SHORTTITLE} -> {FORUMS_POSTS_TITLE}
{FORUMS_POSTS_PAGETITLE} -> {PAGEEDIT_FORM_DESCRIPTION}


all user tags tags: {FORUMS_POSTS_ROW_USERXXX} -> {FORUMS_POSTS_ROW_USER_XXX}
e.g.
{FORUMS_POSTS_ROW_USERNAME} -> {FORUMS_POSTS_ROW_USER_NAME}
{FORUMS_POSTS_ROW_USERAVATAR} -> {FORUMS_POSTS_ROW_USER_AVATAR}
etc.

{PREVIOUS_PAGE}{PAGINATION}{NEXT_PAGE} and other pagination tags without prefix

 

polls.admin.tpl
{ADMIN_POLLS_ROW_POLL_URL_DEL} deprecated
{ADMIN_POLLS_ROW_POLL_DELETE_URL} - new
{ADMIN_POLLS_ROW_POLL_DELETE_CONFIRM_URL} - new

 

system/admin/tpl/admin.extensions.tpl
{ADMIN_EXTENSIONS_DETAILS_ROW_ORDER_INSTALLED}
{ADMIN_EXTENSIONS_DETAILS_ROW_HOOKS_INSTALLED}

 

page.list.tpl (same changes in cot_page_enum())
{LIST_CATTITLE} -> {LIST_BREADCRUMBS} - new
{LIST_BREADCRUMBS_SHORT} - new
{LIST_ROWCAT_URL} -> {LIST_CAT_ROW_URL}
{LIST_ROWCAT_TITLE} -> {LIST_CAT_ROW_TITLE}
{LIST_ROWCAT_COUNT} -> {LIST_CAT_ROW_COUNT}
{LIST_ROWCAT_DESC} -> {LIST_CAT_ROW_DESCRIPTION}

{LIST_CAT_XXX} - pagination for categories with prefix 'LIST_CAT_', e.g. {LIST_CAT_PREVIOUS_PAGE}, {LIST_CAT_PAGINATION}
And without prefix for pages list e.g. {PREVIOUS_PAGE}, {PAGINATION}.

Block LIST_ROWCAT -> LIST_CAT_ROW

{LIST_ROW_XXX} - page tags see below.


page.tpl, page.enum.tpl (from cot_generate_pagetags())
{PAGE_TITLE} now contains exactly the title, not the breadcrumbs
{PAGE_BREADCRUMBS} - new
{PAGE_LOCALSTATUS} -> {PAGE_LOCAL_STATUS}
{PAGE_SHORTTITLE} - deprecated
{PAGE_CATURL} -> {PAGE_CAT_URL}
{PAGE_CATTITLE} -> {PAGE_CAT_TITLE}
{PAGE_CATPATH} -> {PAGE_CAT_PATH}
{PAGE_CATPATH_SHORT} -> {PAGE_CAT_PATH_SHORT}
{PAGE_CATDESC} -> {PAGE_CAT_DESCRIPTION}
{PAGE_CATICON} -> {PAGE_CAT_ICON}
{PAGE_CAT_ICON_SRC} - new
{PAGE_DESC} -> {PAGE_DESCRIPTION}
{PAGE_DESC_OR_TEXT} -> {PAGE_DESCRIPTION_OR_TEXT}
{PAGE_DESC_OR_TEXT_CUT} -> {PAGE_DESCRIPTION_OR_TEXT_CUT}
{PAGE_OWNERID} -> {PAGE_OWNER_ID}
{PAGE_OWNERNAME} -> {PAGE_OWNER_NAME}
{PAGE_DATE} -> {PAGE_CREATED}
{PAGE_DATE_STAMP} -> {PAGE_CREATED_STAMP}
{PAGE_FILE_COUNT} -> {PAGE_FILE_DOWNLOADS}
{PAGE_FILE_COUNTTIMES} -> {PAGE_FILE_DOWNLOADS_TIMES}
{PAGE_COUNT} -> {PAGE_HITS}
{PAGE_NOTAVAILABLE} -> {PAGE_NOT_AVAILABLE}


page.add.tpl
{PAGEADD_FORM_DESC} -> {PAGEADD_FORM_DESCRIPTION}
{PAGEADD_FORM_OWNERID} -> {PAGEADD_FORM_OWNER_ID}

 

page.edit.tpl
{PAGEEDIT_FORM_LOCALSTATUS} -> {PAGEEDIT_FORM_LOCAL_STATUS}
{PAGEEDIT_FORM_DESC} -> {PAGEEDIT_FORM_DESCRIPTION}
{PAGEEDIT_FORM_OWNERID} -> {PAGEEDIT_FORM_OWNER_ID}
{PAGEEDIT_FORM_PAGECOUNT} -> {PAGEEDIT_FORM_HITS}
{PAGEEDIT_FORM_FILECOUNT} -> {PAGEEDIT_FORM_FILE_DOWNLOADS}


pm.list.tpl, pm.message.tpl
Pagination tags without prefix: {PREVIOUS_PAGE}{PAGINATION}{NEXT_PAGE}...

 

polls.admin.tpl
{ADMIN_POLLS_ROW_POLL_DELETE_CONFIRM_URL} - new
{ADMIN_POLLS_ROW_POLL_URL_DEL} -> {ADMIN_POLLS_ROW_POLL_DELETE_URL}


users.edit.php
{USERS_EDIT_TITLE} - now it is a page title
{USERS_EDIT_BREADCRUMBS} - new
{USERS_EDIT_DETAILSLINK} -> {USERS_EDIT_DETAILS_URL}
{USERS_EDIT_MAINGRP} -> {USERS_EDIT_MAIN_GROUP}

 

users.passrecover.tpl
{PASSRECOVER_TITLE} - now it is a page title
{PASSRECOVER_BREADCRUMBS} - new

 

users.profile.tpl
{USERS_PROFILE_TITLE} - now it is a page title
{USERS_PROFILE_BREADCRUMBS} - new
{USERS_PROFILE_DETAILSLINK} -> {USERS_PROFILE_DETAILS_URL}
{USERS_PROFILE_EDITLINK} -> {USERS_PROFILE_EDIT_URL}

 

users.tpl
Pagination tags without prefix: {PREVIOUS_PAGE}{PAGINATION}{NEXT_PAGE}...
{USERS_TOP_USERID} -> {USERS_TOP_USER_ID}
{USERS_TOP_MAINGRP} -> {USERS_TOP_MAIN_GROUP}
{USERS_TOP_REGDATE} -> {USERS_TOP_REGISTRATION_DATE}
{USERS_TOP_LOGCOUNT} -> {USERS_TOP_LOGINS_COUNT}
{USERS_TOP_GRPLEVEL} -> {USERS_TOP_GROUP_LEVEL}
{USERS_TOP_GRPTITLE} -> {USERS_TOP_GROUP_TITLE}
{USERS_TOP_POSTCOUNT} -> {USERS_TOP_POSTS_COUNT}
{USERS_TITLE} - now it is a page title
{USERS_BREADCRUMBS} - new
{USERS_TOP_FILTER_ACTION} -> {USERS_FILTERS_ACTION}
{USERS_FILTERS_PARAMS} - new
{USERS_TOP_FILTERS_COUNTRY} -> {USERS_FILTERS_COUNTRY}
{USERS_TOP_FILTERS_MAINGROUP} -> {USERS_FILTERS_MAIN_GROUP}
{USERS_TOP_FILTERS_GROUP} -> {USERS_FILTERS_GROUP}
{USERS_TOP_FILTERS_SEARCH} -> {USERS_FILTERS_SEARCH}
{USERS_TOP_FILTERS_SUBMIT} -> {USERS_FILTERS_SUBMIT}
{USERS_FILTERS_SORT}
{USERS_FILTERS_SORT_WAY_URL}
{USERS_FILTERS_SORT_WAY}


recentitems.tpl
Pagination tags without prefix: {PREVIOUS_PAGE}{PAGINATION}{NEXT_PAGE}...

 

recentitems.forums.index.tpl, recentitems.forums.tpl
{FORUM_ROW_FIRSTPOSTER} -> {FORUM_ROW_FIRST_POSTER}
{FORUM_ROW_LASTPOSTER} -> {FORUM_ROW_LAST_POSTER}
{FORUM_ROW_TIMEAGO} -> {FORUM_ROW_TIME_AGO}
{FORUM_ROW_POSTCOUNT} -> {FORUM_ROW_POSTS_COUNT}
{FORUM_ROW_CREATIONDATE} -> {FORUM_ROW_CREATED}
{FORUM_ROW_CREATIONDATE_STAMP} -> {FORUM_ROW_CREATED_STAMP}
{FORUM_ROW_REPLYCOUNT} -> {FORUM_ROW_REPLY_COUNT}
{FORUM_ROW_VIEWCOUNT} -> {FORUM_ROW_VIEWS_COUNT}
{FORUM_ROW_LASTPOSTURL} -> {FORUM_ROW_LAST_POST_URL}
{FORUM_ROW_MAXPAGES} -> {FORUM_ROW_MAX_PAGES}
{FORUM_ROW_DESC} -> {FORUM_ROW_DESCRIPTION}

First poster user tags: {FORUM_ROW_FIRST_POSTER_XXX}
Last poster user tags: {FORUM_ROW_LAST_POSTER_XXX}


recentitems.pages.tpl, recentitems.pages.index.tpl, search.tpl
see page.tpl (from cot_generate_pagetags())


tags.tpl
Pagination tags without prefix: {PREVIOUS_PAGE}{PAGINATION}{NEXT_PAGE}...

If you are not sure about the tag you need, you can use the Nemesis theme as a sample or look into the extension code that you need.
Also you can see a list of available tags and blocks on of the current template

 

For more detailed info see:

 

Updating from previous versions

  • For updating from previous Siena version — see instructions here.
  • For upgrading from Genoa — see this document (you will also need files from cotonti-legacy to perform the upgrade).

 

 

]]>
Sun, 28 Jan 2024 15:07:00 -0000
Editor.js Удобный, современный блочный редактор контента на основе Editor.js.

Github плагина: https://github.com/Edd-G/cot-editorjs

Что означает "блочный редактор"?

В отличие от классических редакторов, рабочее пространство которых состоит из одного редактируемого элемента, в блочном редакторе рабочее пространство состоит из отдельных блоков: абзацев, заголовков, изображений, галерей, списков, цитат и т. д.

В чем преимущества перед классическими?

  1. На выходе макисмально чистый и предсказуемый код: без лишних переносов строк, заполнителей, параграфов и т.д.
  2. Пользователь максимально сконцетрирован на созднии контента, точно зная, как это будет выглядеть на выходе.
  3. Есть десятки готовых к использованию блоков и простой API для создания любого блока, который вам нужен. Например, вы можете реализовать блоки для твитов, постов в Instagram, опросов, анкет и т. д.

Пример редактора

 

 

Особенности этого плагина

  1. В оригинале Editor.js на выходе дает JSON-объект с данными каждого блока. Этот плагин, по крайней мере в текущей версии, конвертирует JSON в чистый HTML, промаркированный специальными классами. Данные в базе хранятся именно в HTML.
    Я сделал это для обратной совместимости с другими редакторами, если вам вдруг вздумается переключиться на классический редактор, вы сможете легко это сделать.
  2. Плагин пока поддерживает только модуль Pages. И только 1 экземпляр редактора на странице.
  3. Плагин размечает создаваемые HTML блоки специальными классами. Если у вас уже есть контент, и вы установите данный редактор, то парсер не найдет нужных CSS классов для рабора.
    В этом случае Editor.js сам попробует разобрать HTML. В большинстве случаев это будет довольно криво.
  4. Загрузка изображений работает пока только с помощью @editorjs/simple-image. Это значит, что добавить картинку получится только бросив ссылку на нее в редактор. Конечно можно кидать ссылки локального сервера.
  5. В текущей версии не реализована загрузка файлов. По крайней мере пока не сделали API какого-то файлового менеджера.
  6. Существует проблема с предпросмотром некотрых ссылок в @editorjs/link. Это связано с тем, что ресурс по ссылке под защитой какого-то сервиса, вроде Cloudflare. 

Поддерживаемые блоки Editor.js

Подробнее о поддерживаемых блоках на Github.

Генерируемый плагином HTML

Header

<h2 class="prs-header prs_center">Lorem</h2>

Paragraph

<p class="prs-paragraph prs_center">
    <code class="inline-code">Pellentesque</code> 
    <i>malesuada fames</i> 
    <mark class="cdx-marker">tempus</mark>
</p>

Ordered List

<ol class="prs-list prs_ordered">
    <li></li>
</ol>

Table

<table class="prs-table prs_withheadings">
    <thead>
        <tr>
            <th>1</th><th>2</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>a</td><td>b</td>
        </tr>
    </tbody>
</table>

Остальные примеры на Github

Установка плагина

  1. Распакуйте в каталог плагинов
  2. Включите плагин в панели администратора
  3. Этот шаг нужен, если качали плагин в виде исходного кода с Github: запустите composer install внутри папки src в директории плагина (Composer должен быть установлен)
  4. Установите парсер разметки Editorjs в модуле конфигурации Страниц (модуль Pages)

Настройки

  1. Вы можете отключить ненужные блоки Editor.js в файле editorjs.editor.php
  2. Плагин использует свою копию HTMLpurifier. Настройте очистку данных приходящих с фронтенда с помощью правил HTML Purifier в файле sanitize-blocks-config.json Подробнее о правилах в этом файле
  3. Префикс CSS классов (по умолчанию prs) меняется в конфигурации плагина

В планах

  • Добавить выбор варианта хранения данных в базе: JSON/HTML
  • Добавить блоки для работы с изображениями и файлами
]]>
Sun, 07 Jan 2024 10:53:27 -0000
Nomerge This simple plugin lets you define forum topics where posts will not be merged. Use plugin config to specify topics number, comma-separated.

]]>
Thu, 30 Nov 2023 17:09:00 -0000
Syntax Highlighter Client side code highlighter. This is ready-to-use Cotonti port of SyntaxHighlighter by Alex Gorbatchev.

Authors:

GitHub: https://github.com/Cotonti-Extensions/syntax-highlighter

 

Installation

  • Download the plugin and extract "syntaxhighlighter" folder into your Cotonti plugins folder.
  • Go to Administration / Extensions and install the SyntaxHighlighter plugin.
  • Go to the plugin configuration and select the desired theme and set other options if you need

 

Usage

Usage with HTML parsing: To highlight source code with some specific language, use HTML tags like this:

<pre class="brush:language;">
    Your code here
</pre>

 

The list of available brushes and themes: https://github.com/syntaxhighlighter/syntaxhighlighter/wiki/Brushes-and-Themes

Plugin for CKEditor: https://ckeditor.com/cke4/addon/syntaxhighlight, https://github.com/dbrain/ckeditor-syntaxhighlight

 

Color themes

Plugin bundled with a pack of predefined color themes. But you can adjust some of it to suit you needs with Cotonti theme colors. To do that copy theme CSS file from syntaxhighlighter/lib/ folder to your theme sub folder named stylesthemes/theme-name/styles. File name shoud have prefix syntaxhighlighter-. For example custom theme file full name can be like this: themes/theme-name/styles/syntaxhighlighter-my-awesome-theme.css.
Now you can change it for your needs or create your own. All CSS files located in themes/theme-name/styles folder override default ones with same names.

 

Examples

Default theme:
SyntaxHighlighter

 

Cotonti theme:
SyntaxHighlighter Cotonti theme

]]>
Thu, 02 Nov 2023 11:06:00 -0000
Maintenance Alert How often do you forget to turn the Maintenance Mode back off once you finish updating your website? Thing is, when you’re done, there’s no indication that the website is actually blocked for unauthorized visits, including SE spiders.

Maintenance Alert is basically a global informer made to remind you to switch off Maintenance Mode.

Install as normal. No dependencies or configurations required.

When the MM is on, you get 2 indications:

  1. A standard warning in the Admin Home location
  2. Global alert message made as a Bootstrap Toast by default with a link to the Security section in the Admin Area

The alert message will appear everywhere, except the login location.

You can customize the alert message via message_alert.tpl template – for both front-end and back-end themes. To do that just put your templates in the plugins folders.

]]>
Wed, 25 Oct 2023 10:29:00 -0000
Customavatar This microplugin is used to modify default avatar picture and user avatar default class. Installation is basic. Once it is done, open plugin config and input required class (img-fluid by default) and/or change default plugin image filename if needed.

Obviously, you can do that manually by altering the relevant resource string in the userimages plugin and overwriting default avatar with your own. However, with each update you will have to do that over again.

]]>
Wed, 20 Sep 2023 16:42:00 -0000
Analogue Icon Pack Alternative icon pack for the Cotonti Siena. To be used primarily with the admin panel.

Analogue Icon Pack for Cotonti Siena

Features:

  • Solid and one-style icons
  • Fully compatible with the built-in Cotonti Siena admin panel
  • Automatic replacement for the built-in & some extra extensions
  • Easy customization via resource strings

Installation:

  1. Copy folder to the /images/icons/ folder
  2. Alter config.php as follows:
$cfg['defaulticons'] = 'analogue';	// Default icon pack

Analogue Icon Pack for Cotonti Siena
Analogue Icon Pack for Cotonti Siena
Analogue Icon Pack for Cotonti Siena

]]>
Thu, 20 Jul 2023 07:29:00 -0000
Cotonti Siena 0.9.23 released Whats new:

  • Added Composer support.
  • Added PSR-4: Autoloader for classes
  • Improved cot_mail() function to follow RFC 2822. Added support for several recipients, CC and BCC recipients and custom sender (from)
  • Added check for file existence for hooks
  • Implemented first part of logging system improvement
  • Install - prevent execution of more than 1 process at a time
  • CKEditor updated to v4.21.0
  • Plugins 'BBcode Parser' and 'MarkItUp!' are removed from standard 'box'
  • Fixed some bugs.

You can find removed plugins here: BBCode Parser and MarkItUp!

Read more on the release page.

Thanks to everyone who participated in the work on this release, programming and testing

 

For more detailed info see:

Learn more, download and don't forget to upgrade

]]>
Thu, 22 Jun 2023 09:02:00 -0000
Cotonti Siena 0.9.23 About Siena

If you are a new to Cotonti — please read a brief info about this CMF and its requirements.

See detailed instructions for first-time install or updating guides.

 

Release info

Whats new:

  • Added Composer support.
  • Added PSR-4: Autoloader for classes
  • Improved cot_mail() function to follow RFC 2822. Added support for several recipients, CC and BCC recipients and custom sender (from)
  • Added check for file existence for hooks
  • Implemented first part of logging system improvement
  • Install - prevent execution of more than 1 process at a time
  • CKEditor updated to v4.21.0
  • Plugins 'BBcode Parser' and 'MarkItUp!' are removed from standard 'box'
  • Fixed some bugs.

You can find removed plugins here: BBcode Parser and MarkItUp!

 

For more detailed info see:

 

Updating from previous versions

  • For updating from previous Siena version — see instructions here.
  • For upgrading from Genoa — see this document (you will also need files from cotonti-legacy to perform the upgrade).

 

]]>
Thu, 22 Jun 2023 08:35:00 -0000
Comlist Create comments lists (widgets) and publish them on the website. The widgets are generated using cot_comlist() function:

{PHP|cot_comlist($tpl, $items = 0, $order, $extra, $group = 0, $offset = 0, $pagination, $ajax_block, $cache_name, $cache_ttl)}

The function accepts the following parameters:

  • $tpl -- template name (comlist by default)
  • $items -- number of records (0 -- publish all)
  • $order -- SQL-sorting (com_id DESC by default)
  • $extra -- extra SQL-query
  • $group -- group comments by pages (0 by default)
  • $offset -- offset records (0 by default)
  • $pagination -- pagination parameter name for the URL
  • $ajax_block -- DOM block ID for ajax pagination
  • $cache_name -- Cache name
  • $cache_ttl -- Cache TTL

The installation is standard. The Comments plugin shall be preinstalled.

]]>
Wed, 14 Jun 2023 09:06:00 -0000
Nevalidate При регистрации нового пользователя плагин до отправки формы на сервер проверяет в базе наличие введёного имени и Email под которыми пытается зарегистрироваться пользователь и в случае если такое имя или Email уже есть то выдаёт предупреждение.

From GIT 

Просто установите плагин и пользуйтесь

]]>
Mon, 29 May 2023 07:09:00 -0000
Coding Style The following code style is used for Cotonti CMF core and official extensions development. If you want to pull-request code into the core, consider using it. We aren't forcing you to use this code style for your application based on Cotonti. Feel free to choose what suits you better.

General rules

This common rules consider all source code files in the project (HTML, PHP, CSS, JS, etc.) if other is not defined.

  • Code files MUST use only UTF-8 encoding without BOM.
  • A new line SHOULD be LF.
  • Indentation SHOULD be 4 spaces for indenting, not tabs.
  • No extra spaces at the end of lines (set up your text editor, so that it removes extra spaces when saving).

There is not a hard limit on line length. Each line of text in your code should be at most 120 characters long.  
Put the line break wherever it makes the most aesthetic sense, not necessarily the breaking point closest to 120 characters.

 

PHP guidelines

A brief view

…in addition to general rules

PHP code should follow the PSR-12, and the PSR-1 coding standards and the PSR-4 autoloading standard.

If you are using PhpStorm, it can help you to maintain the required code style. You can turn it on in SettingsEditorCode StylePHPSet fromPSR12.

  • PHP code MUST use either <?php or <?= opening tags and MUST NOT use the other tag variations such as <?.
  • In case file contains PHP only it should not have trailing ?>.
  • Use one space around all operators, except for increment/decrement, e.g. $a + $b, $string = $foo . 'bar', $i++, not $a+$b.
  • Use one space after comma $one, $two, but not $one,$two.
  • Use strict equality === (inequality !==).
  • It is recommended to enable strict typing in new files using the declare(strict_types=1); directive.
  • One-line comments should be started with // and not #.
  • Avoid using global variables. Don't use undefined variables.

Documentation

  • Refer to PHPDoc for documentation syntax.
  • Code without documentation is not allowed.
  • All class files must contain a "file-level" docblock at the top of each file and a "class-level" docblock immediately above each class.

File Header

All PHP files should contain some documentation info formatted as PHPDoc.

It is very good when the header describes the variables that are used in this file, but are defined somewhere else.

Standard header for new files (this template of the header must be included at the start of all Cotonti files):

/**
 * @package {PACKAGENAME}
 * @version {VERSION}
 * @copyright (c) 2008-2023 Cotonti Team
 * @license BSD License
 * 
 * @var array<string, mixed> $user
 * @var XTemplate $t
 */

@version is optional. For example, if you are developing some extension, you do not need to define @version tag as version info already set in extension setup file and may confuse other developers.

Classes

Do not forget to comment the class. Classes need a separate @package definition, it is the same as the header package name.

/**
 * Cotonti MySQL Database adapter class.
 * A compact extension to standard PHP PDO class with slight Cotonti-specific needs,
 * handy functions and query builder.
 * 
 * @package cotonti
 * @see http://www.php.net/manual/en/class.pdo.php
 *
 * @property-read int $affectedRows Number of rows affected by the most recent query
 * @property-read int $count Total query count
 * @property-read int $timeCount Total query execution time
 */
class MySQL extends Adapter

Methods and functions

Do not forget to comment the functions. Each function should have at least a comment of what this function does. Also, it is recommended to document the parameters too. Use @param, @return, @throws in that order.

/**
 * Returns total number of records contained in a table
 * @param string $tableName Table name
 * @return int
 * @throws Exception if the table name is exists
 */
public function countRows($tableName)

When the @param or @return attributes are redundant due to the use of native types, they can be omitted:

/**
 * Execute the job.
 */
public function handle(AudioProcessor $processor): void
{
    //
}

However, when the native type is generic, specify the generic type through the use of the @param or @return attributes:

/**
 * Get the attachments for the post.
 * @return array<int, Attachment>
 */
public function attachments(int $postId): array
{
    // ...
}

Class, Function and Variable naming

All names must be descriptive, but concise. Seeing the name of a class or variable, it should be clear what they are needed for. For example: $CurrentUser, getUserData($id).

Class names MUST be declared in PascalCase. For example: Controller, Model.

Constants MUST be declared in all upper case with underscore separators. For example: STATUS_PUBLISHED.

Directory/namespace names in lower case

We DO NOT USE underscores to denote class private properties and methods.

Variable naming

Variable names MUST be in camelCase. Each word, except the first one, must begin with a capital letter. For example: $currentUser is correct, but $current_user and $currentuser are not.

Situations when one-character variable names are allowed: language strings: $L, string resources: $R, and when the variable is an index for some looping construct.

Loop Indices:
In this case, the index of the outer loop should always be $i. If there's a loop inside that loop, its index should be $j, followed by $k, and so on. If the loop is being indexed by some already-existing variable with a meaningful name, this guideline does not apply, example:

for ($i = 0; $i < $outerSize; $i++) {
  for ($j = 0; $j < $innerSize; $j++) {
    foo($i, $j);
  }
}

Functions and methods names

Functions and methods names MUST be in camelCase. The exception is Cotonti core functions. Good function names are printLoginStatus(), getUserData(), etc.

In Cotonti we are using standard cot_ prefix for any function to avoid naming conflicts. It is standard for the core API to be reusable: cot_mailPrepareAddress($address).

But you are not forced for use cot_ prefix in your plugins, unless those functions are going to be reused as the core API. You can use your own prefix within your extension API.

Function Arguments are subject to the same guidelines as variable names.

Summary

The basic philosophy here is to not hurt code clarity for the sake of laziness. This has to be balanced by a little bit of common sense, though: printLoginStatusForAGivenUser() goes too far, for example - that function would be better named printUserLoginStatus(), or just printLoginStatus().

Types

All PHP types and values should be used lowercase. That includes true, false, null.

We use operators for type casting, not functions. And only their short forms. Put one space after the operator.

$value1 = (int) $argument1;     // right
$value2 = (bool) $argument2;    // right

$value3 = (integer) $argument3;  // wrong
$value4 = invtal($argument2, 8); // only if it is really necessary

Changing type of an existing variable is considered as a bad practice. Try not to write such code unless it is really necessary.

public function save(Transaction $transaction, int $argument2 = 100)
{
  $transaction = new Connection; // bad
  $argument2 = 200; // good
}

Strings

If string doesn't contain variables or single quotes, use single quotes.

$str = 'Like this.';

If string contains single quotes you can use double quotes to avoid extra escaping.

Variable substitution:

$str1 = "Hello $username!";
$str2 = "Hello {$username}!";

The following is not permitted:

$str3 = "Hello ${username}!";

Concatenation

Add spaces around dot when concatenating strings:

$name = 'Cotonti' . ' CMF';

When string is long format is the following:

$sql = 'SELECT * '
. 'FROM post '
. 'WHERE id = 121 ';

Arrays

For arrays we're using short array syntax.

$arr = [3, 14, 15, 'Cotonti', 'CMF'];

If there are too many elements for a single line, each element is located on a separate line. After the last element we put a comma.

$mail = [
  'to'  => 'user@domain.com',
  'from' => ['admin@site.com', 'SiteTitle'],
  'cc' => [['user2@example.com', 'User2'], 'user3@example.com', 'User4 <user4@example.com>'],
];

Control Structures

  • Control statement condition MUST have single space before and after parenthesis.
  • Operators inside of parenthesis SHOULD be separated by one space.
  • Opening brace MUST be on the same line.
  • The closing brace MUST be on the next line after the body.
  • Always use braces for single line statements.

if, elseif, else

if ($expr1) {
  // if body
} elseif ($expr2) {
  // elseif body
} else {
  // else body;
}

// The following is NOT allowed:
if (!$model && null === $event) throw new Exception('test');

The keyword elseif SHOULD be used instead of else if so that all control keywords look like single words.

Expressions in parentheses MAY be split across multiple lines, where each subsequent line is indented at least once. When doing so, the first condition MUST be on the next line. The closing parenthesis and opening brace MUST be placed together on their own line with one space between them. Boolean operators between conditions MUST always be at the beginning of the line.

if (
  $expr1
  && $expr2
) {
  // if body
}

Prefer avoiding else after return where it makes sense:

$result = $this->getResult();
if (empty($result)) {
  return false;
} else {
  // process result
}

is better as:

$result = $this->getResult();
if (empty($result)) {
  return false;
}

// process result

switch, case

A switch structure looks like the following. Note the placement of parentheses, spaces, and braces.

switch ($expr) {
  case 0:
    echo 'First case, with a break';
    break;
  case 1:
    echo 'Second case, which falls through';
    // no break
  case 2:
  case 3:
  case 4:
    echo 'Third case, return instead of break';
    return;
  default:
    echo 'Default case';
}

Ternary operator

Ternary operators should only be used to do very simple things. Preferably, they will only be used to do assignments, and not for function calls or anything complex at all. They can be harmful to readability if used incorrectly, so don't fall in love with saving typing by using them, examples:

// Bad place to use them
($i < $size && $j > $size) ? do_stuff($foo) : do_stuff($bar);

// OK place to use them
$min = ($i < $j) ? $i : $j;

Classes

The term "class" refers to all classes, interfaces, and traits.

  • The opening brace for the class MUST go on its own line; the closing brace for the class MUST go on the next line after the body.
  • Classes SHOULD be named using PascalCase.
  • Every class MUST have a documentation block that conforms to the PHPDoc.
  • There should be only one class in a single PHP file.
  • All classes should be namespaced.
  • Visibility MUST be declared on all properties and methods.
  • The extends and implements keywords MUST be declared on the same line as the class name. Lists of implements and, in the case of interfaces, extends MAY be split across multiple lines, where each subsequent line is indented once. When doing so, the first item in the list MUST be on the next line, and there MUST be only one interface per line.

Properties

Public and protected variables SHOULD be declared at the top of the class before any method declarations. Private variables should also be declared at the top of the class but may be added right before the methods that are dealing with them in cases where they are only related to a small subset of the class methods.

<?php

/**
 * File doc block
 */
 
namespace Vendor\Package;

use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

/**
 * Class doc block
 */
class ClassName extends ParentClass implements \ArrayAccess, \Countable
{
  public $publicProp1;
  public $publicProp2;

  protected $protectedProp;

  private $privateProp;


  public function someMethod()
  {
    // ...
  }
}

Methods and Functions

Opening brace of a function SHOULD be on the line after the function declaration, and the closing brace MUST go on the next line following the body.

In the argument list there MUST be one space after each comma.

function foo(int $arg1, string &$arg2, array $arg3 = [])
{
  // function body
}

Argument lists MAY be split across multiple lines, where each subsequent line is indented once. When doing so, the first item in the list MUST be on the next line, and there MUST be only one argument per line.

public function aVeryLongMethodName(
  ClassTypeHint $arg1,
  string &$arg2,
  array $arg3 = []
) {
  // method body
}

In the methods/functions declaration, always specify the native types of arguments and return values:

// Good
/**
 * Execute the job.
 */
public function handle(AudioProcessor $processor): void
{
    //
}


// Not good
/**
 * Execute the job.
 * @param AudioProcessor processor
 * @return void
 */
public function handle($processor)
{
    //
}

// Bad
/**
 * Execute the job.
 */
public function handle($processor)
{
    //
}

When making a method or function call, there MUST NOT be a space between the method or function name and the opening parenthesis, there MUST NOT be a space after the opening parenthesis, and there MUST NOT be a space before the closing parenthesis. In the argument list, there MUST NOT be a space before each comma, and there MUST be one space after each comma.

SQL guidelines

SQL Statements are often unreadable without some formatting, since they tend to be big at times. Though the formatting of sql statements adds a lot to the readability of code. SQL statements should be formatted in the following way, basically writing keywords:

$sql = 'SELECT p.*, u.* ' 
    . 'FROM ' . Cot::$db->pages . ' AS p ' .
    . 'LEFT JOIN ' . Cot::$db->pages . ' AS u ON u.user_id = p.page_ownerid '
    . "WHERE p.page_ownerid = 1 AND p.page_cat = 'news' " 
    . 'LIMIT 10';

Use parameters to safely substitute user-supplied data into the query (even if you are sure the variable cannot contain single quotes - never trust your input):

$result = Cot::$db->query(
  'SELECT * FROM ' . Cot::$db->forum_topics . ' WHERE ft_cat = :cat  AND ft_id = :topicId',
  ['cat' => $section, 'topicId' => $param]
);

Avoid DB specific SQL

  • The not equals operator, as defined by the SQL-92 standard, is <>. != is also supported by most databases, but it's better to use <>.
  • INSERT ... ON DUPLICATE KEY UPDATE - is a MySQL specific extension to SQL.
  • Backticks ` for table and column names are MySQL specific. Better use CotDB::quoteTableName() and CotDB::quoteColumnName() or abbreviations of these methods: CotDB::quoteT(), CotDB::quoteC().

Javascript guidelines

  • Use JSDoc for classes, member variables, and methods
  • The documenting, commenting and naming rules are applicable from point 2. PHP
  • Don't use jQuery where you can do without it.
  • Use let rather than var to declare variables and const to declare constants.
  • Constants whose values are always hard-coded throughout the program are named in uppercase with an underscore as a word separator. For example: const COLOR_ORANGE = "#ffa500". Most variables are constants in a different sense: they don't change value after assignment. But with different function launches their value may vary. Such variables should use const and camelCase in the name.
  • Semicolons are always placed at the end of the line.
  • Always use strict equality === (inequality !==).

Strings

  • use single quotes: let single = 'single-quoted';.
  • If string contains single quotes you can use double quotes to avoid extra escaping.
  • For expressions substitute use backticks: alert(`1 + 2 = ${sum(1, 2)}.`); // 1 + 2 = 3..

Helpful links and tools

 

Previous coding style version

]]>
Sat, 20 May 2023 06:45:00 -0000
MarkItUp! markitup

Plain BBCode content source editor using jQuery.

Authors:

Github: https://github.com/Cotonti-Extensions/markitup

 

Installation

  • Download the plugin and extract "markitup" folder into your Cotonti plugins folder.
  • Go to Administration / Extensions and install the MarkitUp! plugin.
  • Install BBCode parser plugin
  • Set default markup parser BBcode in site settings: https://domaint.tld/admin.php?m=config&n=edit&o=core&p=main or for pages module: https://domaint.tld/admin.php?m=config&n=edit&o=module&p=page

     

markitup

]]>
Sat, 06 May 2023 05:01:00 -0000
BBCode Parser Previously (0.9.22 and early) was included in the standard Cotonti package

Customizable support for BBCodes and smilies parsing. Adds BBCode parser support to the contents.

Authors: Cotonti Team

Github: https://github.com/Cotonti-Extensions/bbcode

 

BBCode ("Bulletin Board Code") is a lightweight markup language used to format messages in many Internet forum software. It was first introduced in 1998. The available "tags" of BBCode are usually indicated by square brackets ([ and ]) surrounding a keyword, and are parsed before being translated into HTML

 

Installation

  • Download the plugin and extract "bbcode" folder into your Cotonti plugins folder.
  • Install the plugin in Administration panel
  • Set default markup parser in site settings: https://domaint.tld/admin.php?m=config&n=edit&o=core&p=main or for pages module: https://domaint.tld/admin.php?m=config&n=edit&o=module&p=page
  • Optionally install the MarkItUp! editor with bbcode support

 

BBCodes examples

BBCode Example in HTML/CSS Output
[b]bolded text[/b] <strong>bolded text</strong> bolded text
[i]italicized text[/i] <em>italicized text</em> italicized text
[u]underlined text[/u] <span style="text-decoration: underline;">underlined text</span> underlined text
[s]strikethrough text[/s] <span style="text-decoration: line-through;">strikethrough text</span> strikethrough text
[center]center align[/center] <div style="text-align:center">center align</div>  
[right]right align[/right] <div style="text-align:right">right align</div>  
[justify]justify align[/justify] <div style="text-align:justify">justify align</div>  
[url=https://en.wikipedia.org]English Wikipedia[/url] <a href="https://en.wikipedia.org">English Wikipedia</a> English Wikipedia
[img]https://upload.wikimedia.org/wikipedia/commons/7/70/Example.png[/img] <img src="https://upload.wikimedia.org/wikipedia/commons/7/70/Example.png" />
[spoiler]Text behind the spoiler[/spoiler]    
[hide]Text for authorized users[/hide]    

and much more...

All available bbcodes you can find in the plugin admin panel https://domaint.tld/admin.php?m=other&p=bbcode. You can also add your own ones.

 
 
]]>
Sat, 06 May 2023 05:01:00 -0000
Alpha Filters The Alpha Filters plugin gives you a flexible capability to filter pages & users by letters, numbers and letter combinations (syllable-based). You can also assign your own styles to the filter span elements.

 

Authors: esclkm, Kalnov Alexey, Cotonti Team

Github: https://github.com/Alex300/cotonti-alphafilters

 

Installation

  • Unpack to your plugins folder
  • Install the plugin in AdminCP
  • Add tags to page.list.tpl and/or users.tpl
  • Customize plugin configuration if necessary

 

Example for users.tpl:

<!-- IF {PHP|cot_plugin_active('alphafilters')} AND {PHP.cfg.plugin.alphafilters.turnOnUsers} == 1 -->
<div class="margin10 small text-center">
     {PHP.L.alphafilters_byFirstLetter}:
     {ALPHAFILTER_1} {ALPHAFILTER_2}
     <!-- IF {ALPHAFILTER_3} --><br />{ALPHAFILTER_3}<!-- ENDIF -->
     {ALPHAFILTER_RESET}
</div>
<!-- ENDIF -->

 

Plugin example:

 

]]>
Mon, 01 May 2023 08:04:00 -0000
Extensions updates Recently, the project participants have updated a number of extensions:

Recent comments for Administrators, Pageavatar, Similar Pages, Table of Contents, Files module, User's recent posts, User's pages

Also the Yukon admin panel theme was updated

Added new extension and theme: User's translate и Adminkacota

Read more details on the corresponding extensions pages.
Enjoy using them :)

]]>
Thu, 20 Apr 2023 08:24:00 -0000