Votre voix sur les lois du jour - A civic engagement platform that allows French and EU citizens to vote on legislation being debated in the European Parliament and French National Assembly.
- Overview
- Features
- Tech Stack
- Installation
- Configuration
- Project Structure
- API Documentation
- Admin Panel
- Security
- Troubleshooting
- Contributing
- License
Constituant is a lightweight civic engagement web application that enables citizens to:
- View current legislative bills from EU Parliament and French National Assembly
- Cast their opinion (For, Against, Abstain) on each bill
- See real-time aggregate voting results
- Access full legislative texts
The platform is designed with a mobile-first approach and uses progressive enhancement for an optimal experience across all devices.
- Real-time Voting: Cast votes on current legislation with instant feedback
- Aggregate Results: See voting statistics with visual progress bars
- Dual Legislative Tracking: Follow both EU and French legislation
- Mobile-First Design: Optimized for smartphones, scales beautifully to desktop
- Anonymous Voting: IP-based vote tracking (one vote per IP per bill)
- Urgency Indicators: Visual badges for urgent votes (today, this week)
- Direct Access: Links to official legislative texts
- Progressive Enhancement: Works without JavaScript (basic functionality)
- Responsive Design: Single codebase from 320px to 4K displays
- Accessibility: WCAG 2.1 AA compliant with ARIA labels
- Dark Mode: Automatic theme switching based on system preferences
- Fast Performance: < 2s load time on 3G, optimized assets
- SEO Optimized: Semantic HTML, meta tags, and structured data
- Rate Limiting: Protection against vote spam (10 votes/hour per IP)
- Admin Panel: Simple interface to manage bills
- Frontend: Vanilla HTML5, CSS3, JavaScript (ES6+)
- Backend: PHP 8.0+
- Database: MySQL 5.7+ / MariaDB 10.2+
- Server: Apache 2.4+ (O2switch shared hosting optimized)
- No Frameworks: Pure vanilla code for maximum performance and minimal dependencies
- PHP 8.0 or higher
- MySQL 5.7+ or MariaDB 10.2+
- Apache with mod_rewrite enabled
- Composer (optional, for future dependencies)
Upload the contents of the public_html directory to your web server's public directory (typically public_html or www).
For O2switch:
# Via FTP/SFTP, upload to:
/home/YOUR_USERNAME/public_html/- Log in to your cPanel
- Go to MySQL Databases
- Create a new database (e.g.,
constituant) - Create a database user and grant all privileges
- Note down: database name, username, and password
- Go to phpMyAdmin in cPanel
- Select your database
- Click Import tab
- Choose
database/schema.sql - Click Go
The schema will create two tables (bills and votes) and insert sample data for testing.
Edit public_html/config/database.php:
const DB_HOST = 'localhost'; // Usually 'localhost' for shared hosting
const DB_NAME = 'constituant'; // Your database name
const DB_USER = 'constituant_user'; // Your database username
const DB_PASS = 'Dev_Password123!'; // Your database passwordEdit public_html/config/config.php:
define('ADMIN_PASSWORD', 'your_secure_password_here');constituant2024 immediately!
Edit public_html/config/config.php:
define('SITE_URL', 'https://constituant.fr'); // Your actual domain# Make sure PHP can write logs (if needed)
chmod 755 public_html/
chmod 644 public_html/*.php
chmod 644 public_html/config/*.php
chmod 644 public_html/api/*.php- Visit your domain:
https://yourdomain.com - You should see the landing page with sample bills
- Test voting on a bill
- Visit
/admin/and log in with your admin password
For enhanced security, move sensitive config to environment variables:
Create public_html/.env (outside public directory if possible):
DB_HOST=localhost
DB_NAME=constituant
DB_USER=constituant_user
DB_PASS=Dev_Password123!
ADMIN_PASSWORD=your_admin_passwordUncomment these lines in public_html/.htaccess:
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]If your frontend is on a different domain, enable CORS in config/config.php:
define('API_CORS_ENABLED', true);constituant/
├── database/
│ └── schema.sql # Database schema and sample data
├── public_html/ # Web root (upload this to server)
│ ├── index.php # Main landing page
│ ├── .htaccess # Apache configuration
│ ├── robots.txt # SEO and bot rules
│ ├── admin/
│ │ └── index.php # Admin panel
│ ├── api/
│ │ ├── get-votes.php # GET endpoint for bills
│ │ ├── cast-vote.php # POST endpoint to vote
│ │ ├── get-results.php # GET endpoint for stats
│ │ └── add-bill.php # Admin: manage bills
│ ├── assets/
│ │ ├── css/
│ │ │ ├── style.css # Main styles
│ │ │ └── mobile.css # Responsive styles
│ │ ├── js/
│ │ │ ├── app.js # Main app logic
│ │ │ └── voting.js # Voting functionality
│ │ └── images/
│ │ ├── logo.svg # Site logo
│ │ └── flags/ # Country flags
│ └── config/
│ ├── database.php # DB connection
│ └── config.php # App configuration
└── README.md # This file
Get all bills with vote statistics.
Query Parameters:
level(optional): Filter byeu,france, orall(default:all)status(optional): Filter byupcoming,voting_now, orcompleted
Response:
{
"success": true,
"bills": [
{
"id": "eu-dsa-2024",
"title": "Digital Services Act - Amendment 247",
"summary": "...",
"full_text_url": "https://...",
"level": "eu",
"chamber": "European Parliament",
"vote_datetime": "2024-12-15 14:00:00",
"status": "upcoming",
"urgency": {
"is_soon": true,
"label": "Vote aujourd'hui",
"urgency": "urgent"
},
"votes": {
"for": 234,
"against": 45,
"abstain": 21,
"total": 300
},
"percentages": {
"for": 78,
"against": 15,
"abstain": 7
},
"user_voted": "for"
}
],
"count": 1
}Cast a vote on a bill.
Request Body:
{
"bill_id": "eu-dsa-2024",
"vote_type": "for"
}Vote Types: for, against, abstain
Response (Success):
{
"success": true,
"message": "Vote enregistré avec succès",
"vote": {
"bill_id": "eu-dsa-2024",
"bill_title": "...",
"vote_type": "for",
"action": "created"
}
}Response (Error):
{
"success": false,
"error": "Vous avez déjà voté sur ce projet de loi"
}Get detailed vote statistics for a specific bill.
Query Parameters:
bill_id(required): Bill ID
Response:
{
"success": true,
"bill_id": "eu-dsa-2024",
"bill_title": "...",
"votes": {
"for": 234,
"against": 45,
"abstain": 21,
"total": 300
},
"percentages": {
"for": 78,
"against": 15,
"abstain": 7
},
"timeline": [...],
"updated_at": "2024-12-05 14:30:00"
}Add, update, or delete bills.
Request Body:
{
"admin_password": "your_password",
"bill": {
"id": "eu-dsa-2024",
"title": "...",
"summary": "...",
"full_text_url": "...",
"level": "eu",
"chamber": "European Parliament",
"vote_datetime": "2024-12-15 14:00:00",
"status": "upcoming"
},
"action": "create"
}Actions: create, update, delete
Access the admin panel at: https://yourdomain.com/admin/
Default Password: constituant2024 (
- View all bills with vote counts
- Add new bills
- Edit existing bills
- Delete bills (also deletes associated votes)
- Real-time validation
- Click "Ajouter un projet"
- Fill in required fields:
- ID: Unique identifier (lowercase, hyphens only, e.g.,
eu-dsa-2024) - Title: Bill title (max 500 chars)
- Summary: Brief description
- Level: EU or France
- Chamber: e.g., "European Parliament" or "Assemblée Nationale"
- Vote Date/Time: When the official vote takes place
- Full Text URL (optional): Link to official legislation
- Status: upcoming, voting_now, or completed
- ID: Unique identifier (lowercase, hyphens only, e.g.,
- Click "Enregistrer"
- SQL Injection Prevention: PDO prepared statements
- XSS Prevention:
htmlspecialchars()on all output - CSRF Protection: Token validation on admin forms
- Rate Limiting: 10 votes per hour per IP
- Input Validation: All user input sanitized and validated
- Secure Headers: X-Content-Type-Options, X-Frame-Options, X-XSS-Protection
- Password Protection: Admin panel requires password
- File Protection:
.htaccessblocks access to sensitive files - Session Security: HTTP-only, secure cookies
- Error Handling: Detailed errors logged, generic errors shown to users
- Change default admin password immediately
- Use HTTPS in production (Let's Encrypt free SSL)
- Keep PHP and MySQL updated
- Regular database backups
- Monitor access logs for suspicious activity
- Consider IP whitelisting for admin panel
- Use strong database passwords
- Implement proper user authentication (OAuth, JWT)
- Add CAPTCHA for vote submissions
- Two-factor authentication for admin
- Database encryption for sensitive data
- Implement Content Security Policy (CSP)
Error: "Unable to connect to database"
Solutions:
- Verify database credentials in
config/database.php - Ensure database exists and user has privileges
- Check if MySQL is running:
service mysql status - Verify hostname (usually
localhostfor shared hosting)
Solutions:
- Enable error display temporarily (only for debugging):
ini_set('display_errors', 1); error_reporting(E_ALL);
- Check PHP error logs in cPanel
- Verify PHP version is 8.0+
- Check file permissions (755 for directories, 644 for files)
Solutions:
- Check browser console for JavaScript errors
- Verify API endpoint is accessible:
/api/cast-vote.php - Check if IP is being detected correctly
- Ensure not hitting rate limit (10 votes/hour)
- Check database connection
Solutions:
- Verify admin password in
config/config.php - Clear browser cookies
- Check if sessions are enabled:
session_status() - Verify session directory is writable
Solutions:
- Check file paths in
index.php - Verify CSS files exist in
/assets/css/ - Clear browser cache (Ctrl + F5)
- Check
.htaccessis not blocking CSS files
Solutions:
- Check network tab in browser DevTools
- Verify
/api/get-votes.phpreturns data - Check database has bills:
SELECT * FROM bills - Ensure JavaScript is enabled in browser
- Minification: Consider minifying CSS and JS for production
- Caching: Browser caching configured in
.htaccess(1 year for static assets) - Compression: gzip compression enabled via mod_deflate
- Database Indexing: Indexes on frequently queried columns
- Lazy Loading: Consider lazy loading images in future
- CDN: Consider using a CDN for static assets
- Enable OPcache in PHP
- Use HTTP/2 if available
- Optimize images (use WebP format)
- Consider Redis for session storage
- Implement service workers for offline support
Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Commit your changes:
git commit -m 'Add amazing feature' - Push to the branch:
git push origin feature/amazing-feature - Open a Pull Request
- PHP: Follow PSR-12 coding standard
- JavaScript: Use ES6+ features, semicolons required
- CSS: Use BEM methodology for class names
- Comments: Write clear, concise comments for complex logic
This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
This means:
- ✅ You can use, modify, and distribute this software freely
- ✅ You must make source code available if you run this on a server
- ✅ Any modifications must also be open-source under AGPL-3.0
- ✅ You must include the original license and copyright notice
See the LICENSE file for full details.
- European Parliament and French National Assembly for legislative data
- O2switch for reliable shared hosting
- The open-source community for inspiration
- Issues: GitHub Issues
- Email: [email protected]
- Documentation: Wiki
- Email notifications for new bills
- User accounts with vote history
- Social sharing features
- Advanced analytics dashboard
- Multi-language support (EN, FR, DE, ES)
- Mobile apps (iOS, Android)
- Real-time notifications
- Integration with official legislative APIs
- AI-powered bill summaries
- Community discussion forums
Made with ❤️ for democracy and civic engagement
Last updated: 2024-12-05