π HumHub Integration
OnigiriJS is purpose-built for HumHub modules, providing seamless integration with HumHub's architecture, PJAX system, and module API.
Why OnigiriJS for HumHub?
- Automatic PJAX re-mounting for HumHub navigation
- Built-in CSRF protection compatible with HumHub
- Reactive components that work with HumHub's jQuery
- Event system that integrates with HumHub events
- Local storage with proper prefixing
- Easy module initialization
Quick Start
1. Add OnigiriJS to Your Module
// File structure
modules/onigiriwidget/
βββ assets/
β βββ OnigiriWidgetAsset.php
βββ controllers/
βββ models/
βββ resources/
β βββ js/onigiri-core.js
β βββ js/onigiri-events.js
β βββ js/onigiri-components.js
β βββ js/onigiri-security.js
β βββ js/onigiri-ajax.js
β βββ js/onigiri-storage.js
β βββ js/onigiri-humhub.js
β βββ js/widget.js
βββ views/
βββ Module.php
2. Register in AssetBundle
<?php
// assets/OnigiriWidgetAsset.php
namespace humhub\modules\onigiriwidget\assets;
use yii\web\AssetBundle;
class OnigiriWidgetAsset extends AssetBundle
{
public $sourcePath = '@onigiriwidget/resources';
public $js = [
'js/onigiri-core.js',
'js/onigiri-events.js',
'js/onigiri-components.js',
'js/onigiri-security.js',
'js/onigiri-ajax.js',
'js/onigiri-storage.js',
'js/onigiri-humhub.js',
'js/widget.js'
];
}
?>
3. Create Your Widget
// resources/js/widget.js
Onigiri.humhub('onigiriWidget', {
selector: '.onigiri-widget',
data: {
items: [],
loading: false,
error: null
},
methods: {
async loadItems() {
this.loading = true;
this.error = null;
try {
const data = await Onigiri.get(
humhub.config.get('baseUrl') + '/onigiriwidget/api/items'
);
this.items = data;
} catch (error) {
this.error = 'Failed to load items';
console.error(error);
} finally {
this.loading = false;
}
}
},
created() {
console.log('π Widget created!');
// Initialize security with HumHub CSRF
Onigiri.security.init();
},
mounted() {
console.log('π Widget mounted!');
this.loadItems();
}
});
4. Add Widget to View
<?php
// views/index.php
use humhub\modules\onigiriwidget\assets\OnigiriWidgetAsset;
OnigiriWidgetAsset::register($this);
?>
<div class="onigiri-widget">
<h2>π Onigiri Widget</h2>
<div id="widget-content"></div>
</div>
HumHub Module Configuration
Component Options
Onigiri.humhub('moduleName', {
selector: '.my-widget', // Widget selector
autoInit: true, // Auto-initialize (default: true)
pjax: true, // Auto-remount on PJAX (default: true)
// Standard component options
data: { /* ... */ },
methods: { /* ... */ },
computed: { /* ... */ },
watchers: { /* ... */ },
// Lifecycle hooks
created() { /* ... */ },
mounted() { /* ... */ }
});
Disable Auto-Init
// Manual initialization
Onigiri.humhub('customWidget', {
selector: '.custom-widget',
autoInit: false, // Don't auto-mount
data: { count: 0 }
});
// Initialize manually when needed
$(document).on('custom:event', function() {
const widget = Onigiri.humhub.customWidget;
widget.mount('.custom-widget');
});
Disable PJAX Re-mounting
// Don't remount on PJAX navigation
Onigiri.humhub('staticWidget', {
selector: '.static-widget',
pjax: false, // Won't remount on PJAX
mounted() {
// Only runs once on initial page load
}
});
Working with HumHub API
Making API Calls
const widget = new Onigiri.prototype.Component({
methods: {
async loadData() {
// Get base URL from HumHub config
const baseUrl = humhub.config.get('baseUrl');
// Make request with CSRF
const data = await Onigiri.ajax({
url: baseUrl + '/onigiri/api/data',
method: 'GET',
headers: {
'X-CSRF-Token': humhub.config.get('csrf.token')
}
});
return data;
},
async saveData(item) {
const baseUrl = humhub.config.get('baseUrl');
await Onigiri.post(
baseUrl + '/onigiri/api/save',
item
);
}
}
});
Using HumHub Configuration
// Access HumHub configuration
const userId = humhub.config.get('user.id');
const userGuid = humhub.config.get('user.guid');
const csrfToken = humhub.config.get('csrf.token');
const baseUrl = humhub.config.get('baseUrl');
// Use in component
Onigiri.humhub('userWidget', {
data: {
userId: humhub.config.get('user.id'),
preferences: {}
},
created() {
// Load user-specific data
this.loadPreferences();
},
methods: {
async loadPreferences() {
const key = \`user_\${this.userId}_prefs\`;
this.preferences = Onigiri.storage.get(key, {});
}
}
});
HumHub Events Integration
Listen to HumHub Events
Onigiri.humhub('eventWidget', {
mounted() {
// Listen to HumHub ready event
$(document).on('humhub:ready', () => {
console.log('π HumHub is ready!');
this.init();
});
// Listen to PJAX events
$(document).on('pjax:success', () => {
console.log('π PJAX navigation completed');
this.refresh();
});
// Listen to custom HumHub events
$(document).on('humhub:content:updated', () => {
console.log('Content updated');
this.reload();
});
}
});
Emit HumHub Events
const widget = Onigiri.humhub('customWidget', {
methods: {
save() {
// Save data...
// Trigger HumHub event
$(document).trigger('humhub:modules:onigiri:saved', {
itemId: this.currentItem.id
});
}
}
});
// Other modules can listen
$(document).on('humhub:modules:onigiri:saved', function(e, data) {
console.log('Onigiri saved:', data.itemId);
});
PJAX Integration
Automatic Re-mounting
// Widget automatically remounts after HumHub PJAX navigation
Onigiri.humhub('pjaxWidget', {
selector: '.pjax-widget',
pjax: true, // Default behavior
mounted() {
// Called on initial load AND after each PJAX navigation
console.log('π Widget (re)mounted');
this.initialize();
},
methods: {
initialize() {
// Re-initialize component state
this.loadData();
this.setupEventListeners();
}
}
});
Clean Up on PJAX
Onigiri.humhub('cleanupWidget', {
data: {
timers: [],
listeners: []
},
mounted() {
// Set up timers
const timer = setInterval(() => {
this.checkStatus();
}, 5000);
this.timers.push(timer);
},
beforeDestroy() {
// Clean up timers before PJAX navigation
this.timers.forEach(timer => clearInterval(timer));
this.timers = [];
console.log('π Cleaned up before PJAX');
}
});
Complete Module Example
PHP Controller
<?php
// controllers/ApiController.php
namespace humhub\modules\onigiriwidget\controllers;
use humhub\components\Controller;
use yii\web\Response;
class ApiController extends Controller
{
public $enableCsrfValidation = true;
public function actionItems()
{
\Yii::$app->response->format = Response::FORMAT_JSON;
return [
['id' => 1, 'name' => 'Salmon Onigiri', 'price' => 3.50],
['id' => 2, 'name' => 'Tuna Onigiri', 'price' => 3.50],
['id' => 3, 'name' => 'Umeboshi Onigiri', 'price' => 3.00]
];
}
public function actionSave()
{
\Yii::$app->response->format = Response::FORMAT_JSON;
$data = \Yii::$app->request->post();
// Validate and save...
return ['success' => true, 'id' => 123];
}
}
?>
JavaScript Component
// resources/js/onigiri-shop.js
Onigiri.humhub('onigiriShop', {
selector: '.onigiri-shop',
data: {
items: [],
cart: [],
loading: false,
error: null
},
computed: {
cartTotal() {
return this.cart.reduce((sum, item) => {
return sum + (item.price * item.quantity);
}, 0);
},
cartCount() {
return this.cart.reduce((sum, item) => {
return sum + item.quantity;
}, 0);
}
},
methods: {
async loadItems() {
this.loading = true;
this.error = null;
try {
const baseUrl = humhub.config.get('baseUrl');
this.items = await Onigiri.get(
baseUrl + '/onigiriwidget/api/items'
);
} catch (error) {
this.error = 'Failed to load menu';
console.error(error);
} finally {
this.loading = false;
}
},
addToCart(item) {
const existing = this.cart.find(i => i.id === item.id);
if (existing) {
existing.quantity++;
} else {
this.cart.push({ ...item, quantity: 1 });
}
this.saveCart();
// Show notification
humhub.modules.ui.status.success(
\`π Added \${item.name} to cart!\`
);
},
removeFromCart(itemId) {
this.cart = this.cart.filter(i => i.id !== itemId);
this.saveCart();
},
saveCart() {
const userId = humhub.config.get('user.id');
Onigiri.storage.set(\`cart_\${userId}\`, this.cart);
},
loadCart() {
const userId = humhub.config.get('user.id');
this.cart = Onigiri.storage.get(\`cart_\${userId}\`, []);
},
async checkout() {
const baseUrl = humhub.config.get('baseUrl');
try {
const result = await Onigiri.post(
baseUrl + '/onigiriwidget/api/checkout',
{ items: this.cart }
);
humhub.modules.ui.status.success(
'π Order placed successfully!'
);
this.cart = [];
this.saveCart();
} catch (error) {
humhub.modules.ui.status.error(
'Checkout failed: ' + error.message
);
}
}
},
watchers: {
cart(newCart) {
// Update cart badge
O('.cart-badge').text(this.cartCount);
}
},
created() {
console.log('π Onigiri Shop created!');
// Initialize security
Onigiri.security.init({
csrfToken: humhub.config.get('csrf.token')
});
// Load saved cart
this.loadCart();
},
mounted() {
console.log('π Onigiri Shop mounted!');
// Load menu items
this.loadItems();
// Render initial UI
this.render();
},
render() {
const container = O('.onigiri-shop');
if (this.loading) {
container.html('<p>Loading menu... π</p>');
return;
}
if (this.error) {
container.html(\`<p class="error">\${this.error}</p>\`);
return;
}
let html = '<h2>π Menu</h2><div class="items">';
this.items.forEach(item => {
html += \`
<div class="menu-item" data-id="\${item.id}">
<h3>\${item.name}</h3>
<p>$\${item.price.toFixed(2)}</p>
<button class="add-to-cart" data-id="\${item.id}">
Add to Cart
</button>
</div>
\`;
});
html += '</div>';
if (this.cart.length > 0) {
html += \`
<div class="cart">
<h3>Cart (\${this.cartCount} items)</h3>
<p>Total: $\${this.cartTotal.toFixed(2)}</p>
<button class="checkout-btn">Checkout</button>
</div>
\`;
}
container.html(html);
// Bind events
O('.add-to-cart').on('click', (e) => {
const id = parseInt(O(e.target).data('id'));
const item = this.items.find(i => i.id === id);
if (item) this.addToCart(item);
});
O('.checkout-btn').on('click', () => {
this.checkout();
});
}
});
View Template
<?php
// views/index.php
use humhub\modules\onigiriwidget\assets\OnigiriShopAsset;
OnigiriShopAsset::register($this);
?>
<div class="panel panel-default">
<div class="panel-heading">
π Onigiri Shop
</div>
<div class="panel-body">
<div class="onigiri-shop">
<!-- Widget content rendered here -->
</div>
</div>
</div>
HumHub UI Integration
Using HumHub Status Messages
// Success message
humhub.modules.ui.status.success('π Saved successfully!');
// Error message
humhub.modules.ui.status.error('Failed to save');
// Info message
humhub.modules.ui.status.info('Loading data...');
// In component
Onigiri.humhub('widget', {
methods: {
async save() {
try {
await Onigiri.post('/api/save', this.data);
humhub.modules.ui.status.success('π Saved!');
} catch (error) {
humhub.modules.ui.status.error('Save failed');
}
}
}
});
Using HumHub Modals
// Open modal with content
humhub.modules.ui.modal.global.load('/onigiri/modal/view');
// In component
O('.show-details').on('click', function() {
const id = O(this).data('id');
humhub.modules.ui.modal.global.load(
\`/onigiriwidget/details?id=\${id}\`
);
});
π Pro Tip: OnigiriJS automatically handles HumHub's PJAX navigation, so your components will remount correctly after page transitions!
Best Practices for HumHub Modules
- Always initialize security with HumHub's CSRF token
- Use HumHub's base URL for API calls
- Store user-specific data with user ID prefix
- Clean up timers/listeners in beforeDestroy
- Use HumHub's UI components for consistency
- Test with PJAX navigation enabled
- Handle missing jQuery gracefully
- Use HumHub events for inter-module communication
- Follow HumHub's module structure
- Provide fallbacks for disabled JavaScript
Debugging
// Enable debug logging
Onigiri.humhub('debugWidget', {
created() {
console.log('π Widget created');
console.log('User ID:', humhub.config.get('user.id'));
console.log('CSRF Token:', humhub.config.get('csrf.token'));
},
mounted() {
console.log('π Widget mounted');
console.log('Element:', this.$el);
},
beforeDestroy() {
console.log('π Widget destroying');
}
});
β Development Roadmap
Track the progress of OnigiriJS modules. Tasks are marked complete by the development team.
OnigiriJS Module Roadmap
Implementation progress of planned modules
6 / 21 completed (29%)
onigiri-state
Shared global & scoped state management
onigiri-directives
Declarative DOM bindings (o-show, o-model, etc.)
onigiri-resource
REST-style data models over AJAX
onigiri-observe
Intersection & Mutation observer helpers
onigiri-humhub-ui
Standard HumHub UI abstractions (modal, notify, confirm)
onigiri-lifecycle
Component lifecycle hooks
onigiri-guard
Debounce, throttle, single-run guards
onigiri-scroll
Scroll save/restore & helpers (PJAX-friendly)
onigiri-permission
Client-side permission awareness
onigiri-portal
DOM teleport / overlay mounting
onigiri-router
Micro router (non-SPA, PJAX-first)
onigiri-sanitize
HTML & input sanitization
onigiri-shortcut
Keyboard shortcut manager
onigiri-queue
Sequential async task runner
onigiri-gesture
Touch & swipe helpers
onigiri-devtools
Debugging & inspection helpers
onigiri-plugin
Plugin registration system
onigiri-time
Relative time & timezone utilities
onigiri-emojis
Emoji Picker and Manager
onigiri-tasks
Task Management
onigiri-polls
Polls creation and management
Note: Task completion is managed by the OnigiriJS development team.