Activating your account ...
Error activating your account.
You have successfully activated your account. Redirecting to login ...
It is a PHP framework and a SaaS tool which allows to build custom websites and CMS extremely fast.
Bottom up approach
Create your data model. To each table's column assign an HTML control. Click Build and Download the whole prebuilt system in a matter of seconds.
Why another framework
Unlike other frameworks out there, this one is simple to use, has a low learning curve and you will be very fast in developing websites. Programming with such a framework will be fun again.
No support is needed, because it was conceived in a way that developers will be able to follow and easily modify all the aspects from the request to the response without any major issues.
Accreditation required
Because the service is free, the following code snippet must be clearly visible to visitors of your customized website => Developed with <a href="">pBullet</a>
An email was sent to you. Only after the verification link is clicked will you be able to login. Check your spam folder just in case.

Sign Up


What information do we collect?

Only your registration details which are needed for running the website.

What do we use your information for?

No special purpose. Mainly to activate your access to this website.

How do we protect your information?

Your information is protected by encryption and secure access.

Do we use cookies?

No (Cookies are small files that a site or its service provider transfers to your computers hard drive through your Web browser (if you allow) that enables the sites or service providers systems to recognize your browser and capture and remember certain information). We might use cookies in the future to understand and save your preferences for future visits. If you prefer, you can choose to have your computer warn you each time a cookie is being sent, or you can choose to turn off all cookies via your browser settings. Like most websites, if you turn your cookies off, some of our services may not function properly. However, you can still place orders by contacting customer service.

Do we disclose any information to outside parties?

We do not sell, trade, or otherwise transfer to outside parties your personally identifiable information. This does not include trusted third parties who assist us in operating our website, conducting our business, or servicing you, so long as those parties agree to keep this information confidential. We may also release your information when we believe release is appropriate to comply with the law, enforce our site policies, or protect ours or others rights, property, or safety. However, non-personally identifiable visitor information may be provided to other parties for marketing, advertising, or other uses.

Third party links

Occasionally, at our discretion, we may include or offer third party products or services on our website. These third party sites have separate and independent privacy policies. We therefore have no responsibility or liability for the content and activities of these linked sites. Nonetheless, we seek to protect the integrity of our site and welcome any feedback about these sites.

Data Protection Directive Compliance

Because we value your privacy we have taken the necessary precautions to be in compliance with the Data Protection Directive - Directive 95/46/EC. We therefore will not distribute your personal information to outside parties without your consent.

Online Privacy Policy Only

This online privacy policy applies only to information collected through our website and not to information collected offline.

Your Consent

By using our site, you consent to our web site privacy policy.

Changes to our Privacy Policy

If we decide to change our privacy policy, we will post those changes on this page.

Contacting Us

If there are any questions regarding this privacy policy you may contact us using the link below.

Terms and Conditions

Welcome to our website. If you continue to browse and use this website, you are agreeing to comply with and be bound by the following terms and conditions of use, which together with our privacy policy that governs our relationship with you in relation to this website. If you disagree with any part of these terms and conditions, please do not use our website.

The term ‘our’ or ‘us’ or ‘we’ refers to the owner of the website. The term ‘you’ refers to the user or viewer of our website.

Disclaimer and General Terms & Conditions

The use of this website is subject to the following terms of use:
  • All content on this website is our copyright.
  • This website contains material which is owned by or licensed to us. This material includes, but is not limited to, the design, layout, look, appearance and graphics. Reproduction is prohibited other than in accordance with the copyright notice, which forms part of these terms and conditions.
  • The content of this website is for your general information and use only. It is subject to change without notice.
  • We aim to provide a website that is accurate, useful, easy to use, and that respects your privacy.
  • The downloaded code which you receive can be customized and used for commercial purposes in return for attribution. The following attribution is mandatory and must be clearly visible when your customized website is running => Developed with <a href="" >pBullet</a>
  • You may not use any material to reverse engineer this website.
  • You may not open source any parts of the downloaded code.
  • Your use of any information or materials on this website is entirely at your own risk, for which we shall not be liable. It shall be your own responsibility to ensure that any products, services or information available through this website meet your specific requirements.
  • All trademarks reproduced in this website, which are not the property of, or licensed to the operator, are acknowledged on the website.
  • This website does not use cookies to monitor browsing preferences.
  • An Internet Protocol (IP) address is a set of electronic numbers that is automatically assigned to your device when you browse the internet. Our webservers may log the IP address and we may use this data to conduct system administration and to analyse website usage and performance. If you access our website through mobile or other devices, we may also collect your unique device identifier.
  • From time to time, this website may also include links to/from other websites. These links are provided for your convenience to provide further information on the product or services that may be useful to you. We have no responsibility for the content of the linked website(s).
  • In case of questions or enquiries, please contact us though a contact form provided on this website. With all legitimate requests we try to respond within one month.
  • Your use of this website and any dispute arising out of such use of the website is subject to the laws of Switzerland.

Register & Login

  1. Register
  2. Activate your account via e-mail
  3. Login

Create your data model

  1. Click on the Create button
  2. Enter your project name and production domain.
  3. Click Save
  4. Click Add Table. No need to create the User table, it comes out of the box and can be later modified.
  5. Fill in the urimap and the name of the table. Urimap is just the link inside the cms. It is used to manage the table's data. When clicked it will show the table's data
  6. Click on the Plus button. This one is used to add columns to the table
  7. Fill in the column details. Column details are explained here
  8. Click Save
  9. Once finished click on the tick located on top of the table. That will validate the table. If no errors, it should be green.

Column details

Property Description
Column name Name of your column
Auto increment Set this value only on the id which should also be primary key
Primary Key Set if the column contains the primary key. Composite primary keys are not supported through the UI. However they can be manually adjusted once the code is downloaded.
Can be null If the column can contain null value. Useful for optional values.
Data Type Select from the list of data types compatible with MariaDB or MySQL
Alias The label which appears on the form inside the CMS
UI Control The control which will be used inside the CMS to manage the value of the column. Supported UI controls can be seen here. In case you wish to choose the control not on the list, choose one of the existing ones and replace it later on manually.
Foreign Table If the column contains a foreign key then you should select the referenced table
Foreign Key Id That is the column being referenced in the foreign table. The id of the foreign table
Foreign Display Value The user friendly value display to the user in the select control
Foreign Key on Update What occurs if the foreign key gets updates in the referenced table. Check the DB docs for more info on these values
Foreign Key on Delete What occurs if the foreign key gets deleted in the referenced table. Check the DB docs for more info on these values
Foreign Key Relationship Two types supported OneToMany and ManyToMany. Both are created with OneToMany relationship. Select OneToMany when the column contains the foreign key. Relationship types are described here

UI controls

Name Type
Hidden Can be only seen in the source code
NicEdit rich text editor More info about NicEdit can be found here
Tigra calendar A free cross-browser Javascript calendar control.
There is also the paid vesion with more features. More info can be found here

Relationship Types

Single table
Single table having columns without any relationships to other tables.
Single table
One to many relationship. Table A contains a column which references the id column of table B. In this case the best is to choose the Select as the UI Control, Foreign Table = TABLE B, Foreign Key Id = id, Foreign Display Value is one of the columns containing user friendly values to display from table B. Foreign Key Relationship = ONE TO MANY. Select will contain the id and user friendly column from table B in the UI.
Single table
Many to many relationship. Table C should contain the id, foreign key to table A and foreign key to table B. Column id is auto increment and primary key. Column fk_table_A has the Select as the UI Control, Foreign Table = TABLE A, Foreign Key Id = id from table A, Foreign Display Value is one of the columns containing user friendly values to display from table A, Foreign Key Relationship = ONE TO MANY.Column fk_table_B has the Select as the UI Control, Foreign Table = TABLE B, Foreign Key Id = id from table B, Foreign Display Value is one of the columns containing user friendly values to display from table B, Foreign Key Relationship = ONE TO MANY.

Build and Download

  1. Click on the Build button. If everything went well you should see the Download button.
  2. Click on the Download button. The whole prebuilt project will be downloaded in the zip format.

Software Dependencies

You will need the following software installed on your computer before starting with the development.

Name Description
Nginx Nginx will be used as our web application server. However you can use others like Apache or another PHP compatible web app server. More info on the installation of Nginx can be found here
PHP-FPM This one is needed to run the PHP scripts. Any version above 7.x.x should be ok. Probably also versions below 7. Installation instructions can be found here
Node Any version above 14.x.x should be ok. Installation instructions are here
Npm Installation instructions are here. NPM 8.x.x. or above.
Gulp Download link. Version 2.x.x should be ok.
Composer Can be downloaded from here. Version 2.x.x should be ok.
MariaDB or MySQL MariaDB can be downloaded here. If you would like to use MySQL for commercial purposes, verify its license. Seems it is no longer free for commercial purposes.

Zip Content

The downloaded zip contains the folder structure as explained below. Suppose you created a project called mysite and domain Two folders are in the zip with these names.
Name Description
db Contains the db.sql script compatible with MariaDB or MySQL. Connect to your database server, open and run it with your favorite SQL editor. You can also use MySQLWorkbench, which is free and can be downloaded here
html Contains a folder which should be publicly exposed to the visitors of your website. On Linux Nginx serves files by default from /var/www/html. Just drop the to /var/www/html/ The prebuilt system expects the folder to end with the html, otherwise the destPath in gulpfile.js needs to be modified.
nginx Contains a sample virtual host config file
mysite This one should not be publicly visible for security reasons and should be placed two folders higher from /var/www/html/ In this case to /var/www/mysite

Configuration steps

These are the steps needed before starting with the development.
  1. nano /etc/hosts and add the following line to the hosts file
  2. Create a virtual host in nginx. Google the instructions how to create one for your OS. The nginxVirtualHostConfig.txt which comes in the zip can be reused quickly by replacing the values marked inside the #TODO
  3. Restart the Nginx.
  4. Open the and modify your site's title
  5. In Terminal navigate to and execute
    chmod 766 files
    This will allow the uploaded files to be saved under the files directory, otherwise you might be receiving errors during file uploads.
  6. Navigate to mysite, open package.json and modify the value for the author key
  7. In Terminal navigate to mysite and run npm install
  8. Run gulp. Here you might get some errors about missing gulp dependencies located in gulpfile.js. If e.g. gulp-uglify would be throwing errors then install it by using one of the commands npm install -g gulp-uglify or npm install --save-dev gulp-uglify or google the errors. Similar goes for gulp-concat, gulp-clean-css, del, gulp-less, gulp-append-prepend
  9. Run composer install
  10. Visit the Mailjet website, register and get the key and the secret for your domain. Mailgun, which comes with the prebuilt system, can also be used. If you wish to use another mailing service, then replace the occurences of the Mailjet code.
  11. Also on the Mailjet website, make sure to add your domain under Sender domains & addresses. Make sure its status is Active.
  12. Enter the key and secret in the mysite/mail/mailjet/MailJetConfig.php
  13. Open the mysite/common/Executor.php and modify the database's DEV environment settings YOURDEVDBNAME, YOURDEVUSER, YOURDEVPASSWORD
  14. If you open you will notice there is the Google Captcha. You can either set it up or delete it. In index.php the following lines concern the Captcha <script src="" async defer></script> and <div class="g-recaptcha" data-sitekey="YOUR_SITE_KEY"></div>. The ContactController.php needs this value replaced YOURCAPTCHASECRET. You receive these values from reCAPTCHA Checkbox
  15. Open in the browser
  16. Try to register
  17. The activation e-mail was sent to the email provided in the registration form. Click on the activation link.
  18. Login with your credentials
  19. Now you can test the CMS, modify the colors, fonts, layout as you wish, add new pages. You have full control.

Create a page manually

Pages in pBullet are represented with sections. They are stacked on top of each other on the z axis as depicted below. The whole html gets loaded after the first response. Afterwards only JSON is used for communication between the browser and the server. This is called the single page application (SPA).


Below we will use a section with the id="samplePage" as an example. To create a page you will need the following:

Name Description
section Add a new section with the id="samplePage" in the index.php.
route Add a new route to routes.js Routes are composed of 5 properties described here
samplePage.less This one will contain all your CSS related to this page written in LESS. It should start with section#samplePage. Don't forget to include the z-index Consult an existing sample to see how it should look like. It should be imported to master.less if you will be creating the page outside the CMS otherwise to masterCms.less
samplePage.js This one will contain the Javascript related to this page. It should be included in the gulpfile.js. If you will be creating the page outside the CMS add it inside the function js() otherwise to jsCms().
SamplePageController.php Optional. Include it if your page will need some processing on the server side or access to the DB. Simply copy an existing controller from the folder mysite/controller. Make sure the controller extends the AbstractController class and that you modify also the name of the class inside the controller. Controller has the execute method which is invoked based on the act and subAct params received in JSON request. Modify these according to your wishes. Controller must be registered inside the inside the $listOfActions and properly included with the require_once statement. Controller resolution is described here
Before starting with the development run these two commands in Terminal
gulp watch
The first one will build fresh javascript and css files under the and The second command will listen to any changes you make to js or less files and automatically rebuild them. If you add a new js or less file to gulpfile.js or master(Cms).less make sure to Ctrl + C to stop the gulp watch and redo the two commands to pick up the added file.


Name Description
name This is just a name of your route. Assign something meaningful e.g. samplePage. Router can display pages based on the route name.
path Links can be created with this path e.g. /samplePage. If you have a link with this path, if clicked, it will open the corresponding page (section). It will also appear in the address bar.
sectionId This is the id of the page (section) you want to display once the link with this path has been clicked or if programmatically the router moved to a route with this name.
pageTitle The title which will be set when this page (section) will be shown.
initFunction Optional. The function which will be invoked every time when moving to this page. Specify a meaningful name. If set, this function should be included in samplePage.js.

Controller resolution

The following two parameters can be sent inside the JSON request. They are used to resolve the controller and its method passing the whole JSON to the method. Some of them are already used by the framework.

Name Description
act Stands for action. It is used to resolve the controller e.g. act=login will resolve to the LoginController
subAct Stands for sub action. It is used for method resolution inside the controller once the execute method is invoked. The following subAct values are used by the framework: add, edit, del, list, showItem, oneToMany

Request response cycle

Below is the typical flow between the browser and the server.

Request response cycle
  1. Browser sends the request to the server
  2. Server responds with the full html from index.php. From now on only JSON is sent between the browser and the server. Browser executes the following code which loads the registration page inside the initModules. If you wish to load another page, simply replace the "register" route as the input to the routerModule.
    $(document).ready(function () {
    ... });
  3. After the user clicks on the submit button, the browser sends a JSON request with act and subAct params to the server. The server resolves the controller from the act param to the RegisterController and invokes its execute method. Inside the execute the subAct will resolve in the invocation of the "register" method.
  4. Inside the register method the server will insert the user to the DB.
  5. The DB will provide the response to the server.
  6. The server returns the JSON response.


To debug you can use one of the following techniques.
  1. Uncomment these lines
    ini_set('display_errors', 1);
  2. echo $variableName
  3. var_dump($variableName)
  4. Configure XDebug with your IDE
  5. Use the Inspect tool inside the browser to troubleshoot JSON requests and their responses, javascript calls, css layout and html structure. Right click in your browser, Inspect

Before going to production

Comment out the below two lines
//ini_set('display_errors', 1);
Adjust the production host, dbname, user and password values in this line of code
$this->connection = new PDO('mysql:host=YOURDBIP;dbname=YOURDBNAME', 'YOURUSER', 'YOURPASSWORD');
Set the DEV and PROD domains
private static $DEV_DOMAIN = "dev.YOURDOMAIN";
private static $PROD_DOMAIN = "YOURDOMAIN";
Uncomment the following occurences
Search the whole project for the occurrences of TODO and replace where it makes sense excluding external dependencies

Add the Google Analytics or other tracking script to index.php

Inserting files in database

By default the uploaded files get created in the filesystem under This part is done here
$filePropertyNames = ["..."];
$this->handleFiles($this->jsonObject, $filePropertyNames);
For better loading performance it is recommended to upload them on another server like the CDN or multiple servers.

However if you really wish to insert the files in the database then comment or delete the two lines below and the $dataTypes must include PDO::PARAM_LOB for each uploaded file.

//$filePropertyNames = ["..."];
//$this->handleFiles($this->jsonObject, $filePropertyNames);
$dataTypes = array(':uploadFile' => PDO::PARAM_LOB);


This caching solution functions in the following way. The first time the browser downloads the SPA it downloads the whole html. Next time it will ask the server if anything changed. If nothing changed it will load the page from the browser cache. In order to achieve this the following modifications needs to be done.
include "enableCache.php";
<!doctype html>
<html lang="en">
etag on;
expires 30d;
add_header Cache-Control private;
Set the expired value according to your wishes. Restart the Nginx.

The above code will tell the browser to cache the index page for 30 days before asking for a fresh version. If you wish to force the browser to invalidate its cache just use the terminal command touch index.php or overwrite it with via SFTP. That will modify its timestamp. Next time the browser checks if there is any change on the server it will receive yes and load the most recent page. This technique is usually done after the deployment of a new website version to production.

Functional testing

Because the website is an SPA, it is possible to execute functional testing without requiring to learn a new programming language. The tests can be triggered based on the domain name or query string param value e.g.

Here is a small example on how to test the login page