An electronic document signing plugin for ITFlow, the open-source IT documentation and business management platform for MSPs. This plugin lets agents create documents and send them to clients for electronic signature via a secure web link.
- Document creation with rich text editor (TinyMCE) or PDF upload
- Guest signing page accessible via a secure, unique URL (no login required)
- Canvas-based signature capture with mouse and touch support
- Status lifecycle: Draft → Sent → Viewed → Signed / Declined / Expired
- PDF export with embedded signatures and Certificate of Completion via TCPDF
- Email delivery of signing links through ITFlow's existing mail system
- Auto-email signed copy to signer after signing, with PDF download link
- Full audit trail with timestamped history, IP addresses, and user agents
- SHA-256 integrity hashing for each signature (document + signature + email + timestamp)
- Agent notifications when a document is viewed, signed, or declined
- CSRF protection on all forms and state-changing operations
- RBAC integration using ITFlow's existing
module_salespermissions
This plugin uses ITFlow's agent/custom/ directory, the recommended location for custom modules. This approach:
- Does not modify any core ITFlow files -- all plugin files live in
agent/custom/,includes/,guest/, andjs/ - POST handler: Automatically discovered by
agent/custom/post.phpvia itsglob("post/*.php")pattern - Sidebar navigation: Injected into
agent/custom/includes/custom_side_nav.php(the custom module sidebar) - Survives ITFlow updates: Since files live in
agent/custom/with unique filenames, agit pullupdate will not overwrite them
This plugin was developed against ITFlow 0.6+. It depends on these ITFlow core functions and patterns:
| Dependency | Used For | Risk If Changed |
|---|---|---|
enforceUserPermission() |
RBAC access control on all agent pages | All agent pages will break |
validateCSRFToken() |
CSRF protection on all POST actions | All form submissions will break |
addToMailQueue() |
Sending signing link emails | Email delivery will break |
agent/custom/includes/inc_all_custom.php |
Custom module page bootstrapping | All agent pages will break |
agent/custom/post.php |
POST handler with glob() auto-discovery | Form submissions will break |
config.php |
Database connection, base URL, SMTP config | Everything will break |
$config_base_url |
Building guest signing URLs | Signing links will be wrong |
$session_user_id, $session_name |
Audit trail attribution | History entries will be incomplete |
- Function signature changes: If ITFlow renames or changes the parameters of
enforceUserPermission(),validateCSRFToken(), oraddToMailQueue(), the plugin will break. Run./verify.shafter every update. - Custom module restructuring: If ITFlow changes how
agent/custom/works (e.g., removesinc_all_custom.phporpost.php), the plugin pages will fail to load. - Database schema changes: The plugin JOINs against ITFlow core tables (
clients,contacts,companies,notifications). If these table names or column names change, queries will fail. - AdminLTE/Bootstrap version upgrades: The plugin uses Bootstrap 4 classes and AdminLTE markup. A major UI framework upgrade would break the layout.
- TCPDF removal or relocation: If
plugins/TCPDF/is removed or moved, PDF export will break. - TinyMCE removal or relocation: If
plugins/tinymce/is removed or moved, the rich text editor in document creation will not load. - Custom sidebar changes: If ITFlow changes
custom_side_nav.phpformat, the injected navigation may disappear. Re-runinstall.shto fix.
Run the verification script to check that nothing is broken:
./verify.sh /path/to/itflowThis checks all files, dependencies, database tables, and integration points. If something fails, re-running ./install.sh will typically fix it.
The installer validates your ITFlow installation, copies all files, runs the database migration, and verifies everything:
git clone https://github.com/life256/ITFlow_Doc_Sign.git
cd ITFlow_Doc_Sign
chmod +x install.sh
./install.shIf no path is provided, the installer will automatically search /var/www, /srv/www, and other common web directories for an ITFlow installation and ask you to confirm the detected location. You can also pass a path directly: ./install.sh /your/itflow/path
The installer will:
- Auto-detect your ITFlow installation (or accept a path argument)
- Verify the target directory is a real ITFlow installation
- Check for required PHP extensions and ITFlow core functions
- Back up any files it overwrites (to
.signable_backup_YYYYMMDD_HHMMSS/) - Copy all plugin files into
agent/custom/and other directories - Inject sidebar navigation into
custom_side_nav.php - Create the database tables (if MySQL CLI is available)
- Create the uploads directory with proper permissions
- Run a full verification check
The installer is idempotent -- safe to run multiple times.
- A working ITFlow installation (tested with ITFlow 0.6+)
- The
agent/custom/directory (present in ITFlow 0.6+) - MariaDB/MySQL database access
- PHP with
finfoextension (usually enabled by default)
If you prefer to install manually or the automated installer doesn't work for your environment:
mysql -u itflow_user -p itflow_database < setup/db_schema.sqlOr copy the contents of setup/db_schema.sql into phpMyAdmin.
ITFLOW=/path/to/your/itflow
# Shared functions
cp includes/functions_signable.php $ITFLOW/includes/
# Agent pages (into agent/custom/)
cp agent/custom/signable_documents.php $ITFLOW/agent/custom/
cp agent/custom/signable_document.php $ITFLOW/agent/custom/
cp agent/custom/ajax_signable.php $ITFLOW/agent/custom/
# Modals
mkdir -p $ITFLOW/agent/custom/modals/signable_document
cp agent/custom/modals/signable_document/*.php $ITFLOW/agent/custom/modals/signable_document/
# POST handlers (auto-discovered by agent/custom/post.php)
mkdir -p $ITFLOW/agent/custom/post
cp agent/custom/post/signable_document.php $ITFLOW/agent/custom/post/
cp agent/custom/post/signable_document_model.php $ITFLOW/agent/custom/post/
# Sidebar nav snippet
cp agent/custom/includes/signable_side_nav_snippet.php $ITFLOW/agent/custom/includes/
# Guest pages
cp guest/guest_sign_document.php $ITFLOW/guest/
cp guest/guest_download_signed_pdf.php $ITFLOW/guest/
# JavaScript
cp js/signature_pad.js $ITFLOW/js/mkdir -p $ITFLOW/uploads/signable_documents
chown www-data:www-data $ITFLOW/uploads/signable_documents
chmod 750 $ITFLOW/uploads/signable_documentsAdjust www-data to match your web server user (e.g., apache, nginx).
Edit $ITFLOW/agent/custom/includes/custom_side_nav.php and add the contents of agent/custom/includes/signable_side_nav_snippet.php before the closing </ul> tag. This adds the "Document Signing" section to the custom module sidebar.
- Log in to ITFlow as an agent with sales module write access.
- Navigate to the Custom section in ITFlow's sidebar.
- Look for Document Signing in the custom sidebar.
- Click "All Documents" and then "New Document" to create a test document.
- Use "Send for Signature" to email yourself the signing link.
- Open the link and verify the signing page loads correctly.
- Sign the document and confirm the signature appears in the agent detail view.
./uninstall.shThe uninstaller will auto-detect your ITFlow installation and ask you to confirm. You can also pass a path directly: ./uninstall.sh /your/itflow/path
This removes all plugin files and sidebar navigation. Database tables are preserved by default (to protect signing data). To also drop tables:
./uninstall.sh --drop-tablesThis plugin uses ITFlow's agent/custom/ directory, the recommended location for custom modules. It follows ITFlow's existing procedural, file-based architecture: PHP files for pages, Bootstrap/AdminLTE modals for forms, a centralized POST handler for actions, and raw mysqli for database access.
- Agent creates a document via the "New Document" modal on the Signable Documents list page. A 64-character cryptographic URL key is generated automatically.
- Agent sends the document using the "Send for Signature" action. This emails the client a link to the guest signing page and updates the status to "Sent".
- Client opens the link (
/guest/guest_sign_document.php?signable_document_id=X&url_key=Y). The status updates to "Viewed" and the agent receives a notification. - Client signs the document by entering their name, email, drawing their signature on a canvas, and checking the consent box. The signature (as a PNG data URL), signer identity, IP address, user agent, and an SHA-256 integrity hash are all recorded.
- Signed copy is emailed to the signer automatically, with a link to download the PDF (including Certificate of Completion).
- Client may alternatively decline the document, which sets the status to "Declined" and notifies agents.
- Agent can export a PDF at any time that includes the document content, embedded signatures, and a Certificate of Completion page.
| Component | File | Purpose |
|---|---|---|
| List page | agent/custom/signable_documents.php |
Filterable, paginated list of all signable documents |
| Detail page | agent/custom/signable_document.php |
View document content, signatures, history, and actions |
| Add modal | agent/custom/modals/signable_document/signable_document_add.php |
Create new document (TinyMCE editor, client/contact select, PDF upload) |
| Edit modal | agent/custom/modals/signable_document/signable_document_edit.php |
Edit draft documents |
| Send modal | agent/custom/modals/signable_document/signable_document_send.php |
Email document link with customizable subject/body |
| POST handler | agent/custom/post/signable_document.php |
All server-side actions: create, edit, send, archive, PDF export |
| Input model | agent/custom/post/signable_document_model.php |
Input sanitization and validation |
| AJAX handler | agent/custom/ajax_signable.php |
JSON API for populating edit/send modals |
| Sidebar nav | agent/custom/includes/signable_side_nav_snippet.php |
Custom sidebar navigation items |
| Guest page | guest/guest_sign_document.php |
Public signing page (no auth, validated by URL key) |
| Guest PDF download | guest/guest_download_signed_pdf.php |
Download signed PDF with Certificate of Completion |
| Shared functions | includes/functions_signable.php |
Status badges, history logging, hash generation, URL key generation, PDF generation |
| Signature pad | js/signature_pad.js |
Lightweight canvas signature capture (mouse + touch, velocity-based line width) |
| DB schema | setup/db_schema.sql |
Three tables (documents, signatures, history) |
signable_documents -- The main document record. Stores title, description, HTML content, status, URL key, dates, and links to client/contact/quote.
signable_document_signatures -- Each signature collected. Stores signer name, email, IP, user agent, the signature image (base64 PNG data URL), and an SHA-256 integrity hash.
signable_document_history -- Audit log. Every status change (Created, Sent, Viewed, Signed, Declined, Archived, Expired) is recorded with timestamp, description, and IP address.
- Guest access: Documents are accessed via a 64-character hex URL key (generated with
random_bytes(32)). No authentication is required -- the key itself serves as the access credential. - Agent access: All agent pages require login and check
enforceUserPermission('module_sales', level)where level 1 = read, 2 = write, 3 = delete/archive. - CSRF protection: All POST forms include a
csrf_tokenvalidated by ITFlow'svalidateCSRFToken(). - Input sanitization: All inputs are sanitized via
intval(),strip_tags(),mysqli_real_escape_string(), andfilter_var(). HTML content uses HTMLPurifier when available, or an allowlist of safe tags as fallback. - File uploads: PDF uploads are validated by both file extension AND MIME type (
finfo_file()). Filenames are sanitized to alphanumeric characters only. - Signature data: Limited to 500KB and must begin with
data:image/prefix. - Audit trail: IP addresses and user agents are recorded for every signature and status change.
ITFlow_Doc_Sign/
├── README.md # This file
├── install.sh # Automated installer
├── uninstall.sh # Automated uninstaller
├── verify.sh # Post-upgrade verification
├── setup/
│ └── db_schema.sql # Database migration (3 tables)
├── includes/
│ └── functions_signable.php # Shared PHP functions
├── agent/custom/
│ ├── signable_documents.php # Document list page
│ ├── signable_document.php # Document detail page
│ ├── ajax_signable.php # AJAX endpoint for modals
│ ├── includes/
│ │ └── signable_side_nav_snippet.php # Sidebar navigation snippet
│ ├── post/
│ │ ├── signable_document.php # POST handler (CRUD, send, archive, PDF)
│ │ └── signable_document_model.php # Input sanitization
│ └── modals/signable_document/
│ ├── signable_document_add.php # Create document modal
│ ├── signable_document_edit.php # Edit document modal
│ └── signable_document_send.php # Send for signature modal
├── guest/
│ ├── guest_sign_document.php # Public guest signing page
│ └── guest_download_signed_pdf.php # Guest PDF download (signed copy)
├── js/
│ └── signature_pad.js # Canvas signature capture library
└── uploads/
└── signable_documents/ # PDF upload storage (created during install)
The plugin fires customAction() at three lifecycle points, allowing you to extend behavior via ITFlow's custom action handler (/custom/custom_action_handler.php):
| Hook Name | Trigger | Parameter |
|---|---|---|
signable_document_create |
After a new document is created | $signable_document_id |
signable_document_send |
After a document is emailed for signing | $signable_document_id |
signable_document_signed |
After a guest signs a document | $signable_document_id |
This plugin relies on ITFlow core libraries that are already included in any standard ITFlow installation:
- TCPDF (
/plugins/TCPDF/) -- PDF generation - TinyMCE (
/plugins/tinymce/) -- Rich text editing - HTMLPurifier (
/plugins/HTMLPurifier/) -- XSS prevention (optional but recommended) - PHPMailer -- Email delivery (via ITFlow's
addToMailQueue()) - AdminLTE / Bootstrap 4 -- UI framework
- jQuery -- DOM manipulation and AJAX
No additional third-party libraries need to be installed. The signature pad (js/signature_pad.js) is a self-contained, dependency-free implementation included with this plugin.
This plugin is designed to work with ITFlow, which is licensed under the GPL-3.0 license.