Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac8468ca76 | ||
|
|
014422e0b6 | ||
|
|
02c92db1e5 | ||
|
|
92f2703788 | ||
|
|
6648523b04 | ||
|
|
de178b0ceb | ||
|
|
086db21318 | ||
|
|
167dc1dfb1 | ||
|
|
ab516628af | ||
|
|
33dd2fb757 | ||
|
|
8660ea6173 | ||
|
|
911b8864ca |
No files matched your search
@@ -0,0 +1,7 @@
|
|||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
.dockerignore
|
||||||
|
Dockerfile
|
||||||
|
files/flyer_template.html
|
||||||
|
files/flyer_map.png
|
||||||
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
name: Build and Push PHP Container Image
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main] # or master / your default branch
|
|
||||||
# Optional: also on tags
|
|
||||||
# tags: ['v*']
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-and-push:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
# Recommended for Docker build tools on many self-hosted runners
|
|
||||||
container:
|
|
||||||
image: catthehacker/ubuntu:act-latest
|
|
||||||
env:
|
|
||||||
REGISTRY: gitea.example.com # ← your Gitea hostname (no https://)
|
|
||||||
IMAGE_NAME: ${{ gitea.repository }} # owner/repo style, or hard-code owner/image
|
|
||||||
# Or: IMAGE_NAME: myuser/php-website
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up QEMU (for multi-arch, optional)
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
# Only needed if your Gitea registry is HTTP / self-signed:
|
|
||||||
# with:
|
|
||||||
# config-inline: |
|
|
||||||
# [registry."gitea.example.com"]
|
|
||||||
# http = true
|
|
||||||
# insecure = true
|
|
||||||
|
|
||||||
- name: Login to Gitea Container Registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
|
||||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
|
||||||
|
|
||||||
- name: Extract metadata / tags
|
|
||||||
id: meta
|
|
||||||
run: |
|
|
||||||
echo "VERSION=$(git describe --tags --always 2>/dev/null || echo ${{ gitea.sha }})" >> $GITHUB_OUTPUT
|
|
||||||
# or simply use latest + sha
|
|
||||||
|
|
||||||
- name: Build and push
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ./Dockerfile
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
|
||||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ gitea.sha }}
|
|
||||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.VERSION }}
|
|
||||||
# platforms: linux/amd64,linux/arm64 # uncomment for multi-arch
|
|
||||||
# cache-from: type=registry,ref=...
|
|
||||||
# cache-to: type=registry,ref=...,mode=max
|
|
||||||
@@ -1,23 +1,45 @@
|
|||||||
Options +FollowSymLinks
|
Options +FollowSymLinks
|
||||||
|
|
||||||
#RewriteEngine on
|
RewriteEngine on
|
||||||
|
|
||||||
#RewriteCond %{HTTP_HOST} !^www\.dialyse-berlin\.net [NC]
|
# Redirect duplicate homepage URLs (/main.html and /index.php) to canonical root /
|
||||||
#RewriteRule ^(.*)$ https://www.%{HTTP_HOST}/$1 [R=301,L]
|
RewriteRule ^main\.html$ / [R=301,L]
|
||||||
|
RewriteCond %{THE_REQUEST} \s/+index\.php[\s?] [NC]
|
||||||
#RewriteCond %{HTTP_HOST} ^www\.dialyse-berlin\.net [NC]
|
RewriteCond %{QUERY_STRING} ^$
|
||||||
#RewriteCond %{HTTPS} !=on
|
RewriteRule ^index\.php$ / [R=301,L]
|
||||||
#RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]
|
|
||||||
|
|
||||||
|
# Clean URL routing for .html pages
|
||||||
RewriteCond %{REQUEST_FILENAME} !-f
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
RewriteCond %{QUERY_STRING} ([a-zA-Z0-9-_.=&+%]*)
|
RewriteCond %{QUERY_STRING} ([a-zA-Z0-9-_.=&+%]*)
|
||||||
RewriteRule ^([0-9a-zA-Z\-]+).html$ index.php?action=$1&%1 [L]
|
RewriteRule ^([0-9a-zA-Z\-]+).html$ index.php?action=$1&%1 [L]
|
||||||
RewriteRule ^captcha.png$ captcha.php [L]
|
|
||||||
|
# Custom 404 handler
|
||||||
|
ErrorDocument 404 /index.php?action=404
|
||||||
|
|
||||||
|
# Advertise HTTP/3 (h3) support via Alt-Svc header
|
||||||
|
<IfModule mod_headers.c>
|
||||||
|
Header setIfEmpty Alt-Svc "h3=\":443\"; ma=86400"
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
# Gzip / Deflate Compression for Core Web Vitals & PageSpeed
|
||||||
|
<IfModule mod_deflate.c>
|
||||||
|
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css application/xml application/xhtml+xml application/rss+xml application/javascript application/x-javascript application/ld+json image/svg+xml
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
# Browser Caching for Static Assets
|
||||||
|
<IfModule mod_expires.c>
|
||||||
|
ExpiresActive On
|
||||||
|
ExpiresByType image/jpeg "access plus 1 month"
|
||||||
|
ExpiresByType image/png "access plus 1 month"
|
||||||
|
ExpiresByType image/gif "access plus 1 month"
|
||||||
|
ExpiresByType image/x-icon "access plus 1 month"
|
||||||
|
ExpiresByType image/svg+xml "access plus 1 month"
|
||||||
|
ExpiresByType text/css "access plus 1 month"
|
||||||
|
ExpiresByType application/javascript "access plus 1 month"
|
||||||
|
ExpiresByType application/pdf "access plus 7 days"
|
||||||
|
ExpiresByType font/ttf "access plus 1 year"
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
<Files .htaccess>
|
<Files .htaccess>
|
||||||
order allow,deny
|
Require all denied
|
||||||
deny from all
|
|
||||||
</Files>
|
</Files>
|
||||||
|
|
||||||
|
|
||||||
# Allow Robots.txt to pass through muss noch eingebunden werden
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{}
|
|
||||||
@@ -1,260 +0,0 @@
|
|||||||
# Design Modernization - Before & After
|
|
||||||
|
|
||||||
## Visual Improvements
|
|
||||||
|
|
||||||
### Header & Navigation
|
|
||||||
|
|
||||||
**BEFORE:**
|
|
||||||
- Static header images taking up space
|
|
||||||
- Horizontal menu with poor mobile experience
|
|
||||||
- No mobile navigation option
|
|
||||||
- Fixed-width layout
|
|
||||||
|
|
||||||
**AFTER:**
|
|
||||||
- Professional gradient header with logo
|
|
||||||
- Sticky navigation that stays visible while scrolling
|
|
||||||
- Mobile hamburger menu for small screens
|
|
||||||
- Responsive container-based layout
|
|
||||||
|
|
||||||
### Color Scheme
|
|
||||||
|
|
||||||
**BEFORE:**
|
|
||||||
- Red gradient buttons (#FF5961 to #B2121B)
|
|
||||||
- Basic black and white text
|
|
||||||
- No visual hierarchy through color
|
|
||||||
|
|
||||||
**AFTER:**
|
|
||||||
- Professional blue primary (#0066cc)
|
|
||||||
- Red accent color (#ff6b35) for calls-to-action
|
|
||||||
- Light blue backgrounds for emphasis (#e6f0ff)
|
|
||||||
- Consistent color variables throughout
|
|
||||||
|
|
||||||
### Typography
|
|
||||||
|
|
||||||
**BEFORE:**
|
|
||||||
- 14px base font size (too small)
|
|
||||||
- Basic sans-serif (Open Sans embedded)
|
|
||||||
- Limited heading hierarchy
|
|
||||||
- Inconsistent margins
|
|
||||||
|
|
||||||
**AFTER:**
|
|
||||||
- 16px base font size (better readability)
|
|
||||||
- Modern system font stack (faster loading)
|
|
||||||
- Clear H1-H6 hierarchy
|
|
||||||
- Consistent 1.6 line-height for readability
|
|
||||||
- Better spacing throughout
|
|
||||||
|
|
||||||
### Forms
|
|
||||||
|
|
||||||
**BEFORE:**
|
|
||||||
- Wide 50% columns (awkward on mobile)
|
|
||||||
- Basic text inputs
|
|
||||||
- No focus states
|
|
||||||
- Poor mobile layout
|
|
||||||
|
|
||||||
**AFTER:**
|
|
||||||
- Responsive grid layout (full width on mobile)
|
|
||||||
- Modern input styling with borders and shadows
|
|
||||||
- Blue focus states with visual feedback
|
|
||||||
- Better spacing and alignment
|
|
||||||
- Mobile-optimized form layout
|
|
||||||
|
|
||||||
### Images & Gallery
|
|
||||||
|
|
||||||
**BEFORE:**
|
|
||||||
- Float-based positioning
|
|
||||||
- Inline styles
|
|
||||||
- No responsive sizing
|
|
||||||
- Basic no styling
|
|
||||||
|
|
||||||
**AFTER:**
|
|
||||||
- Centered display on mobile
|
|
||||||
- max-width: 300px default (responsive)
|
|
||||||
- Box shadows for depth
|
|
||||||
- Border radius for modern look
|
|
||||||
- Proper spacing
|
|
||||||
|
|
||||||
### Tables
|
|
||||||
|
|
||||||
**BEFORE:**
|
|
||||||
- Basic table with minimal styling
|
|
||||||
- No visual hierarchy
|
|
||||||
- Hard to read
|
|
||||||
|
|
||||||
**AFTER:**
|
|
||||||
- Gradient header with white text
|
|
||||||
- Alternating row backgrounds
|
|
||||||
- Hover effects for interactivity
|
|
||||||
- Proper padding and spacing
|
|
||||||
- Professional appearance
|
|
||||||
|
|
||||||
### Buttons
|
|
||||||
|
|
||||||
**BEFORE:**
|
|
||||||
- Basic styled buttons
|
|
||||||
- No hover effects
|
|
||||||
- Inconsistent sizing
|
|
||||||
|
|
||||||
**AFTER:**
|
|
||||||
- Modern button styling
|
|
||||||
- Hover states with color change and shadow
|
|
||||||
- Transform effect on hover (slight lift)
|
|
||||||
- Consistent padding
|
|
||||||
- Professional appearance
|
|
||||||
|
|
||||||
### Messages & Alerts
|
|
||||||
|
|
||||||
**BEFORE:**
|
|
||||||
- Simple colored boxes
|
|
||||||
- No clear visual hierarchy
|
|
||||||
- Basic styling
|
|
||||||
|
|
||||||
**AFTER:**
|
|
||||||
- Professional alert styling
|
|
||||||
- Success: Green (#d4edda)
|
|
||||||
- Error: Red (#f8d7da)
|
|
||||||
- Clear typography
|
|
||||||
- Better spacing
|
|
||||||
|
|
||||||
### Footer
|
|
||||||
|
|
||||||
**BEFORE:**
|
|
||||||
- Simple text footer
|
|
||||||
- No styling
|
|
||||||
- Minimal information
|
|
||||||
|
|
||||||
**AFTER:**
|
|
||||||
- Gradient background matching header
|
|
||||||
- White text for contrast
|
|
||||||
- Organized information layout
|
|
||||||
- Professional appearance
|
|
||||||
|
|
||||||
### Mobile Experience
|
|
||||||
|
|
||||||
**BEFORE:**
|
|
||||||
- Not mobile-friendly
|
|
||||||
- No viewport meta tag
|
|
||||||
- Fixed widths
|
|
||||||
- Poor touch targets
|
|
||||||
|
|
||||||
**AFTER:**
|
|
||||||
- Fully responsive design
|
|
||||||
- Viewport meta tag for proper mobile scaling
|
|
||||||
- Touch-friendly button sizes (minimum 1rem)
|
|
||||||
- Mobile navigation menu
|
|
||||||
- Stack-based layout on small screens
|
|
||||||
|
|
||||||
### Responsive Breakpoints
|
|
||||||
|
|
||||||
**BEFORE:**
|
|
||||||
- Not responsive (fixed 900px width)
|
|
||||||
- Same layout on all devices
|
|
||||||
|
|
||||||
**AFTER:**
|
|
||||||
- Mobile: < 768px
|
|
||||||
- Single column
|
|
||||||
- Hamburger menu
|
|
||||||
- Full-width containers
|
|
||||||
- Tablet: 768px - 1024px
|
|
||||||
- Optimized spacing
|
|
||||||
- 2-column forms
|
|
||||||
- Desktop: > 1024px
|
|
||||||
- Full multi-column layout
|
|
||||||
- Side-by-side elements
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Technology Changes
|
|
||||||
|
|
||||||
### CSS Framework
|
|
||||||
|
|
||||||
**BEFORE:**
|
|
||||||
- `/css/frontend.css` - 600+ lines of mixed styles
|
|
||||||
- Outdated CSS patterns (float layouts)
|
|
||||||
- No organization
|
|
||||||
- Color hardcoded throughout
|
|
||||||
|
|
||||||
**AFTER:**
|
|
||||||
- `/css/modern.css` - 850+ lines, well-organized
|
|
||||||
- Modern CSS features (CSS Grid, Flexbox, custom properties)
|
|
||||||
- Organized into clear sections
|
|
||||||
- Single source of truth for colors (CSS variables)
|
|
||||||
- Mobile-first approach
|
|
||||||
|
|
||||||
### HTML Structure
|
|
||||||
|
|
||||||
**BEFORE:**
|
|
||||||
- Older HTML structure
|
|
||||||
- Many inline styles
|
|
||||||
- Background images for design
|
|
||||||
- No semantic elements
|
|
||||||
|
|
||||||
**AFTER:**
|
|
||||||
- HTML5 semantic elements (<header>, <nav>, <main>, <footer>)
|
|
||||||
- Minimal inline styles
|
|
||||||
- CSS-based design (no image backgrounds)
|
|
||||||
- Proper heading hierarchy
|
|
||||||
|
|
||||||
### JavaScript
|
|
||||||
|
|
||||||
**BEFORE:**
|
|
||||||
- jQuery for DOM manipulation
|
|
||||||
- Basic interaction only
|
|
||||||
- Modal handling
|
|
||||||
|
|
||||||
**AFTER:**
|
|
||||||
- Modern vanilla JavaScript (with jQuery fallback)
|
|
||||||
- Mobile menu toggle
|
|
||||||
- Enhanced interactions
|
|
||||||
- Better event handling
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Performance Benefits
|
|
||||||
|
|
||||||
1. **Faster Load Time**: Removed background images from CSS
|
|
||||||
2. **Better Mobile Performance**: Mobile-first CSS prioritizes essential styles
|
|
||||||
3. **Cleaner HTML**: Removed inline styles
|
|
||||||
4. **Modern Caching**: CSS variables reduce repeated values
|
|
||||||
5. **Accessibility**: Semantic HTML improves screen reader support
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Accessibility Improvements
|
|
||||||
|
|
||||||
- ✅ Proper heading hierarchy (H1-H6)
|
|
||||||
- ✅ Semantic HTML5 elements
|
|
||||||
- ✅ Color contrast meets WCAG standards
|
|
||||||
- ✅ Focus states on buttons and links
|
|
||||||
- ✅ Mobile-friendly touch targets
|
|
||||||
- ✅ Proper form labels
|
|
||||||
- ✅ Alt text on images
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Browser Compatibility
|
|
||||||
|
|
||||||
The modernized site works perfectly on:
|
|
||||||
|
|
||||||
| Browser | Version | Status |
|
|
||||||
|---------|---------|--------|
|
|
||||||
| Chrome | Latest | ✅ Full Support |
|
|
||||||
| Firefox | Latest | ✅ Full Support |
|
|
||||||
| Safari | Latest | ✅ Full Support |
|
|
||||||
| Edge | Latest | ✅ Full Support |
|
|
||||||
| Mobile Chrome | Latest | ✅ Full Support |
|
|
||||||
| Mobile Safari | Latest | ✅ Full Support |
|
|
||||||
| IE 11 | 11+ | ⚠️ Partial (no CSS Grid) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
The modernization transformed your website from an outdated design to a professional, responsive, modern experience. All functionality is preserved while the user experience is significantly enhanced, especially on mobile devices.
|
|
||||||
|
|
||||||
**Key Metrics:**
|
|
||||||
- 📱 Mobile-friendly: YES
|
|
||||||
- 🎨 Modern design: YES
|
|
||||||
- ⚡ Fast loading: YES
|
|
||||||
- ♿ Accessible: YES
|
|
||||||
- 🔒 All features preserved: YES
|
|
||||||
@@ -1,23 +1,15 @@
|
|||||||
FROM php:8.5-apache
|
FROM php:8.5-apache-trixie
|
||||||
|
|
||||||
# Install common extensions (example)
|
# Use official production php.ini and configure timezone & security settings
|
||||||
# RUN docker-php-ext-install
|
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" \
|
||||||
|
&& printf "date.timezone = Europe/Berlin\nexpose_php = Off\nsession.cookie_httponly = 1\nsession.use_strict_mode = 1\n" > "$PHP_INI_DIR/conf.d/99-custom.ini"
|
||||||
|
|
||||||
# Enable Apache modules if needed
|
# Enable Apache modules (including HTTP/2) and harden server signature
|
||||||
RUN a2enmod rewrite
|
RUN a2enmod rewrite headers expires deflate http2 \
|
||||||
|
&& printf "ServerTokens Prod\nServerSignature Off\nProtocols h2 h2c http/1.1\n" > /etc/apache2/conf-available/security-hardening.conf \
|
||||||
|
&& a2enconf security-hardening
|
||||||
|
|
||||||
# Copy application code
|
# Copy application code
|
||||||
COPY . /var/www/html/
|
COPY --chown=www-data:www-data . /var/www/html/
|
||||||
|
|
||||||
# Disable the legacy HTTPS redirect rules from .htaccess so the container stays on plain HTTP
|
EXPOSE 80
|
||||||
# RUN sed -i '/RewriteCond %{HTTP_HOST} !^www\\.dialyse-berlin\\.net \[NC\]/,+2d; /RewriteCond %{HTTP_HOST} ^www\\.dialyse-berlin\\.net \[NC\]/,+2d' /var/www/html/.htaccess || true \
|
|
||||||
# && a2dismod ssl || true
|
|
||||||
|
|
||||||
# Set permissions
|
|
||||||
RUN chown -R www-data:www-data /var/www/html
|
|
||||||
|
|
||||||
# Optional: install Composer dependencies if you have a composer.json
|
|
||||||
# COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
|
||||||
# RUN composer install --no-dev --optimize-autoloader
|
|
||||||
|
|
||||||
EXPOSE 80
|
|
||||||
@@ -1,188 +0,0 @@
|
|||||||
# Website Modernization Summary
|
|
||||||
## Dialysezentrum Berlin-Mahlsdorf
|
|
||||||
|
|
||||||
### Project Completion ✅
|
|
||||||
|
|
||||||
Your nephrology practice website has been successfully modernized with a responsive, professional design.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## What Changed
|
|
||||||
|
|
||||||
### 1. **Modern Design Framework**
|
|
||||||
- **New CSS File**: `/css/modern.css` - A comprehensive modern stylesheet with:
|
|
||||||
- Mobile-first responsive design
|
|
||||||
- Modern color scheme (professional blue with red accent)
|
|
||||||
- CSS custom properties (variables) for easy customization
|
|
||||||
- Smooth transitions and animations
|
|
||||||
- Professional shadows and rounded corners
|
|
||||||
|
|
||||||
### 2. **Responsive Layout**
|
|
||||||
- **Mobile-Friendly**: Fully responsive on all devices (phones, tablets, desktops)
|
|
||||||
- **Hamburger Navigation**: Mobile menu appears on small screens
|
|
||||||
- **Sticky Navigation**: Menu stays accessible while scrolling
|
|
||||||
- **Modern Header**: Professional gradient header with logo
|
|
||||||
- **Flexible Content**: All content adapts beautifully to any screen size
|
|
||||||
|
|
||||||
### 3. **Improved User Experience**
|
|
||||||
- **Better Typography**: Modern font stack and better readability
|
|
||||||
- **Improved Forms**: Modern form styling with better focus states
|
|
||||||
- **Professional Tables**: Styled tables with alternating rows and hover effects
|
|
||||||
- **Image Gallery**: Enhanced gallery display with better styling
|
|
||||||
- **Modern Buttons**: Styled buttons with hover effects and transitions
|
|
||||||
|
|
||||||
### 4. **Semantic HTML**
|
|
||||||
- **Clean Structure**: Updated all PHP templates with semantic HTML5
|
|
||||||
- **Better Accessibility**: Proper heading hierarchy and semantic elements
|
|
||||||
- **Organized Content**: Clear sections and better content organization
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Files Modified
|
|
||||||
|
|
||||||
| File | Changes |
|
|
||||||
|------|---------|
|
|
||||||
| `/css/modern.css` | **NEW** - Modern responsive stylesheet |
|
|
||||||
| `/view/frontend.php` | New header, footer, and responsive layout |
|
|
||||||
| `/view/navigation.php` | Updated links to routing system |
|
|
||||||
| `/view/main.php` | Reorganized with sections and modern styling |
|
|
||||||
| `/view/services.php` | Better organization with h3 headings |
|
|
||||||
| `/view/team.php` | Improved list formatting |
|
|
||||||
| `/view/practice.php` | Modern table styling and layout |
|
|
||||||
| `/view/contact.php` | Responsive map and contact info |
|
|
||||||
| `/view/contact-form.php` | Modern form with grid layout |
|
|
||||||
| `/view/imprint.php` | Semantic HTML structure |
|
|
||||||
| `/view/terms.php` | Better data privacy presentation |
|
|
||||||
| `/view/error.php` | Helpful 404 page |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Features Preserved ✅
|
|
||||||
|
|
||||||
All existing functionality has been maintained:
|
|
||||||
|
|
||||||
- ✅ Contact form with validation
|
|
||||||
- ✅ Image galleries with Lightbox lightbox plugin
|
|
||||||
- ✅ Google Maps integration
|
|
||||||
- ✅ Holiday announcement modal
|
|
||||||
- ✅ Multi-page routing system
|
|
||||||
- ✅ All German text content
|
|
||||||
- ✅ SMTP email functionality
|
|
||||||
- ✅ Session management
|
|
||||||
- ✅ Captcha system (disabled in form)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Color Scheme
|
|
||||||
|
|
||||||
The new design uses a professional medical color palette:
|
|
||||||
|
|
||||||
- **Primary Color**: #0066cc (Professional Blue)
|
|
||||||
- **Primary Dark**: #004a99 (Deep Blue for hover states)
|
|
||||||
- **Accent Color**: #ff6b35 (Medical Red)
|
|
||||||
- **Background**: #f8f9fa (Light Gray)
|
|
||||||
- **Text**: #2c3e50 (Dark Gray for readability)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Responsive Breakpoints
|
|
||||||
|
|
||||||
The design is fully responsive at all screen sizes:
|
|
||||||
|
|
||||||
- **Mobile**: < 768px (hamburger menu, single column)
|
|
||||||
- **Tablet**: 768px - 1024px (optimized layout)
|
|
||||||
- **Desktop**: > 1024px (full multi-column experience)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## How to Customize
|
|
||||||
|
|
||||||
### Change Colors
|
|
||||||
Edit the CSS variables in `/css/modern.css`:
|
|
||||||
|
|
||||||
```css
|
|
||||||
:root {
|
|
||||||
--primary-color: #0066cc; /* Change this */
|
|
||||||
--accent-color: #ff6b35; /* And this */
|
|
||||||
/* ... other variables ... */
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Adjust Layout
|
|
||||||
Modify grid and container sizes in the CSS file.
|
|
||||||
|
|
||||||
### Add More Content
|
|
||||||
All page templates follow the same modern structure. Simply add content following the existing pattern.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Browser Support
|
|
||||||
|
|
||||||
The new design works on:
|
|
||||||
- ✅ Chrome/Edge (latest)
|
|
||||||
- ✅ Firefox (latest)
|
|
||||||
- ✅ Safari (latest)
|
|
||||||
- ✅ Mobile browsers (iOS Safari, Chrome Mobile)
|
|
||||||
- ✅ IE 11+ (with some feature degradation)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Performance Improvements
|
|
||||||
|
|
||||||
- Cleaner CSS (modern framework approach)
|
|
||||||
- Better semantic HTML (faster parsing)
|
|
||||||
- No unnecessary images in design
|
|
||||||
- Optimized styling for faster rendering
|
|
||||||
- Mobile-first approach = better mobile performance
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Next Steps (Optional Enhancements)
|
|
||||||
|
|
||||||
If you want to further enhance the site:
|
|
||||||
|
|
||||||
1. **Add Team Photos**: Update team.php to display doctor/staff photos
|
|
||||||
2. **Blog/News Section**: Add news updates about the practice
|
|
||||||
3. **Online Appointment Booking**: Integrate appointment system
|
|
||||||
4. **Patient Portal**: Add login for patient information access
|
|
||||||
5. **Testimonials Section**: Showcase patient feedback
|
|
||||||
6. **Enhanced Analytics**: Track visitor behavior
|
|
||||||
7. **Multi-language Support**: Add English version
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Testing Checklist
|
|
||||||
|
|
||||||
- [x] Mobile responsive (test on phone/tablet)
|
|
||||||
- [x] Navigation works on mobile (hamburger menu)
|
|
||||||
- [x] Forms are functional
|
|
||||||
- [x] Links navigate correctly
|
|
||||||
- [x] Images display properly
|
|
||||||
- [x] All pages accessible
|
|
||||||
- [x] Contact form works
|
|
||||||
- [x] Google Maps loads
|
|
||||||
- [x] Lightbox galleries function
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Support & Customization
|
|
||||||
|
|
||||||
The CSS is well-organized with clear sections:
|
|
||||||
- Reset & Base Styles
|
|
||||||
- Typography
|
|
||||||
- Layout & Container
|
|
||||||
- Header & Navigation
|
|
||||||
- Forms & Buttons
|
|
||||||
- Content Area
|
|
||||||
- Footer
|
|
||||||
- Utilities
|
|
||||||
|
|
||||||
Each section is labeled for easy navigation and modification.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
Your website is now modern, professional, and mobile-responsive. The design maintains all existing functionality while providing a contemporary look that builds confidence with patients. The responsive layout ensures excellent experience on all devices, from smartphones to desktop computers.
|
|
||||||
|
|
||||||
**Your site is ready to go! 🎉**
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
$captchaText = sha1(microtime());
|
|
||||||
$captchaText = base64_encode($captchaText);
|
|
||||||
$captchaText = substr($captchaText, 1, 5);
|
|
||||||
|
|
||||||
$_SESSION['key'] = strtolower($captchaText);
|
|
||||||
|
|
||||||
$fonts = ["cheapink.ttf"];
|
|
||||||
$fontsizes = array(25);
|
|
||||||
$yCorr = array(25);
|
|
||||||
|
|
||||||
$img = ImageCreateFromPNG('img/captcha.png');
|
|
||||||
$color = ImageColorAllocate($img, 40, 40, 40);
|
|
||||||
|
|
||||||
$index = rand(0, count($fonts)-1);
|
|
||||||
$ttf = 'fonts/' . $fonts[$index];
|
|
||||||
$ttfsize = $fontsizes[$index];
|
|
||||||
|
|
||||||
$angle = rand(2, 6);
|
|
||||||
$t_x = rand(10, 50);
|
|
||||||
$t_y = 24 + $yCorr[$index];
|
|
||||||
|
|
||||||
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
|
|
||||||
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
|
|
||||||
header("Cache-Control: no-store, no-cache, must-revalidate");
|
|
||||||
header("Cache-Control: post-check=0, pre-check=0", false);
|
|
||||||
header("Pragma: no-cache");
|
|
||||||
|
|
||||||
header("Accept-Ranges: bytes");
|
|
||||||
header("Content-type: image/png");
|
|
||||||
ImageTtfText($img, $ttfsize, $angle, $t_x, $t_y, $color, $ttf, $captchaText);
|
|
||||||
ImagePNG($img);
|
|
||||||
ImageDestroy($img);
|
|
||||||
@@ -11,8 +11,8 @@ return [
|
|||||||
'before' => '2W',
|
'before' => '2W',
|
||||||
'from' => '2022-12-27',
|
'from' => '2022-12-27',
|
||||||
'until' => '2025-05-02',
|
'until' => '2025-05-02',
|
||||||
'text' => 'Wir suchen aktuell eine(n) Gesundheits- oder Krankenpfleger-(in)/MFA (m/w/d) für den Dialysebereich, sowie eine Küchenkraft/ Dialyseassistenz in Voll- oder Teilzeit. <br>
|
'text' => 'Wir suchen aktuell eine(n) Gesundheits- oder Krankenpfleger(in) / MFA (m/w/d) für den Dialysebereich sowie eine Küchenkraft / Dialyseassistenz in Voll- oder Teilzeit. <br>
|
||||||
Gründliche Einarbeitung gewährleistet! Melden Sie sich gern unter <a href="mailto:kontakt@dialyse-berlin.de">kontakt@dialyse-berlin.de</a>. <br>
|
Gründliche Einarbeitung gewährleistet! Melden Sie sich gern unter <a href="mailto:kontakt@dialyse-berlin.net">kontakt@dialyse-berlin.net</a>. <br>
|
||||||
Wir freuen uns schon Sie kennen zu lernen!'
|
Wir freuen uns schon, Sie kennenzulernen!'
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
@@ -6,11 +6,12 @@ require_once __DIR__ . '/lib/Session.php';
|
|||||||
$session = new Session();
|
$session = new Session();
|
||||||
|
|
||||||
function validate($data) {
|
function validate($data) {
|
||||||
return !empty($data['name']) && !empty($data['email']) && !empty($data['phone']) && !empty($data['message']);
|
$hasContact = !empty(trim($data['email'] ?? '')) || !empty(trim($data['phone'] ?? ''));
|
||||||
|
return !empty(trim($data['name'] ?? '')) && $hasContact && !empty(trim($data['message'] ?? ''));
|
||||||
}
|
}
|
||||||
|
|
||||||
function isBot($data) {
|
function isBot($data) {
|
||||||
return !empty($data['company']) || ($data['check'] != 'send') && (time() - $data['timestamp']) <= 5;
|
return !empty($data['company']) || (($data['check'] ?? '') != 'send' && (time() - (int)($data['timestamp'] ?? 0)) <= 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
if($_POST && count($_POST) > 0) {
|
if($_POST && count($_POST) > 0) {
|
||||||
@@ -21,16 +22,18 @@ if($_POST && count($_POST) > 0) {
|
|||||||
if(!validate($_POST)) {
|
if(!validate($_POST)) {
|
||||||
$session->set('form', $_POST);
|
$session->set('form', $_POST);
|
||||||
$session->set('alert-class', 'alert-danger');
|
$session->set('alert-class', 'alert-danger');
|
||||||
$session->set('alert', 'Nicht alle Pflichtfelder ausgefüllt.');
|
$session->set('alert', 'Bitte füllen Sie alle Pflichtfelder aus und geben Sie mindestens eine E-Mail-Adresse oder Telefonnummer an.');
|
||||||
} else {
|
} else {
|
||||||
$ip = $_SERVER['REMOTE_ADDR'];
|
$ip = $_SERVER['REMOTE_ADDR'];
|
||||||
|
|
||||||
$mail = new SendMail();
|
$mail = new SendMail();
|
||||||
$mail->setReplyTo($_POST['email'], $_POST['name']);
|
if(!empty(trim($_POST['email'] ?? ''))) {
|
||||||
|
$mail->setReplyTo(trim($_POST['email']), trim($_POST['name']));
|
||||||
|
}
|
||||||
$mail->setTo('kontakt@dialyse-berlin.net', 'Kontaktformular');
|
$mail->setTo('kontakt@dialyse-berlin.net', 'Kontaktformular');
|
||||||
// $mail->setTo('dominic.zirpel@web.de', 'Kontaktformular');
|
// $mail->setTo('dominic.zirpel@web.de', 'Kontaktformular');
|
||||||
$mail->setSubject('Kontaktformular - Dialyse Homepage');
|
$mail->setSubject('Kontaktformular - Dialyse Homepage');
|
||||||
$mail->setBody(sprintf("Name: %s %s\nTelefon: %s\nIP: %s\nE-Mail: %s\n-----------------------------\n%s", $_POST['surname'], $_POST['name'], $_POST['phone'], $ip, $_POST['email'], $_POST['message']));
|
$mail->setBody(sprintf("Name: %s %s\nTelefon: %s\nIP: %s\nE-Mail: %s\n-----------------------------\n%s", $_POST['surname'] ?? '', $_POST['name'] ?? '', $_POST['phone'] ?? '', $ip, $_POST['email'] ?? '', $_POST['message'] ?? ''));
|
||||||
if($mail->send()) {
|
if($mail->send()) {
|
||||||
$session->remove('form');
|
$session->remove('form');
|
||||||
$session->set('alert-class', 'alert-success');
|
$session->set('alert-class', 'alert-success');
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
.lb-loader,.lightbox{text-align:center;line-height:0;position:absolute;left:0}body.lb-disable-scrolling{overflow:hidden}.lightboxOverlay{position:absolute;top:0;left:0;z-index:9999;background-color:#000;filter:alpha(Opacity=80);opacity:.8;display:none}.lightbox{width:100%;z-index:10000;font-weight:400;outline:0}.lightbox .lb-image{display:block;height:auto;max-width:inherit;max-height:none;border-radius:3px;border:4px solid #fff}.lightbox a img{border:none}.lb-outerContainer{position:relative;width:250px;height:250px;margin:0 auto;border-radius:4px;background-color:#fff}.lb-outerContainer:after{content:"";display:table;clear:both}.lb-loader{top:43%;height:25%;width:100%}.lb-cancel{display:block;width:32px;height:32px;margin:0 auto;background:url(../img/lightbox/loading.gif) no-repeat}.lb-nav{position:absolute;top:0;left:0;height:100%;width:100%;z-index:10}.lb-container>.nav{left:0}.lb-nav a{outline:0;background-image:url(data:image/gif;base64,R0lGODlhAQABAPAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==)}.lb-next,.lb-prev{height:100%;cursor:pointer;display:block}.lb-nav a.lb-prev{width:34%;left:0;float:left;background:url(../img/lightbox/prev.png) left 48% no-repeat;filter:alpha(Opacity=0);opacity:0;-webkit-transition:opacity .6s;-moz-transition:opacity .6s;-o-transition:opacity .6s;transition:opacity .6s}.lb-nav a.lb-prev:hover{filter:alpha(Opacity=100);opacity:1}.lb-nav a.lb-next{width:64%;right:0;float:right;background:url(../img/lightbox/next.png) right 48% no-repeat;filter:alpha(Opacity=0);opacity:0;-webkit-transition:opacity .6s;-moz-transition:opacity .6s;-o-transition:opacity .6s;transition:opacity .6s}.lb-nav a.lb-next:hover{filter:alpha(Opacity=100);opacity:1}.lb-dataContainer{margin:0 auto;padding-top:5px;width:100%;border-bottom-left-radius:4px;border-bottom-right-radius:4px}.lb-dataContainer:after{content:"";display:table;clear:both}.lb-data{padding:0 4px;color:#ccc}.lb-data .lb-details{width:85%;float:left;text-align:left;line-height:1.1em}.lb-data .lb-caption{font-size:13px;font-weight:700;line-height:1em}.lb-data .lb-caption a{color:#4ae}.lb-data .lb-number{display:block;clear:left;padding-bottom:1em;font-size:12px;color:#999}.lb-data .lb-close{display:block;float:right;width:30px;height:30px;background:url(../img/lightbox/close.png) top right no-repeat;text-align:right;outline:0;filter:alpha(Opacity=70);opacity:.7;-webkit-transition:opacity .2s;-moz-transition:opacity .2s;-o-transition:opacity .2s;transition:opacity .2s}.lb-data .lb-close:hover{cursor:pointer;filter:alpha(Opacity=100);opacity:1}
|
.lb-loader,.lightbox{text-align:center;line-height:0}body.lb-disable-scrolling{overflow:hidden}.lightboxOverlay{position:absolute;top:0;left:0;z-index:9999;background-color:#000;opacity:.8;display:none}.lightbox{position:absolute;left:0;width:100%;z-index:10000;font-weight:400;outline:0}.lightbox .lb-image{display:block;height:auto;max-width:inherit;max-height:none;border-radius:3px;border:4px solid #fff}.lightbox a img{border:none}.lb-outerContainer{position:relative;width:250px;height:250px;margin:0 auto;border-radius:4px;background-color:#fff}.lb-loader,.lb-nav{position:absolute;left:0}.lb-outerContainer:after{content:"";display:table;clear:both}.lb-loader{top:43%;height:25%;width:100%}.lb-cancel{display:block;width:32px;height:32px;margin:0 auto;background:url(../img/lightbox/loading.gif) no-repeat}.lb-nav{top:0;height:100%;width:100%;z-index:10}.lb-container>.nav{left:0}.lb-nav a{outline:0;background-image:url(data:image/gif;base64,R0lGODlhAQABAPAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==)}.lb-next,.lb-prev{height:100%;cursor:pointer;display:block}.lb-nav a.lb-prev{width:34%;left:0;float:left;background:url(../img/lightbox/prev.png) left 48% no-repeat;opacity:0;-webkit-transition:opacity .6s;-moz-transition:opacity .6s;-o-transition:opacity .6s;transition:opacity .6s}.lb-nav a.lb-prev:hover{opacity:1}.lb-nav a.lb-next{width:64%;right:0;float:right;background:url(../img/lightbox/next.png) right 48% no-repeat;opacity:0;-webkit-transition:opacity .6s;-moz-transition:opacity .6s;-o-transition:opacity .6s;transition:opacity .6s}.lb-nav a.lb-next:hover{opacity:1}.lb-dataContainer{margin:0 auto;padding-top:5px;width:100%;border-bottom-left-radius:4px;border-bottom-right-radius:4px}.lb-dataContainer:after{content:"";display:table;clear:both}.lb-data{padding:0 4px;color:#ccc}.lb-data .lb-details{width:85%;float:left;text-align:left;line-height:1.1em}.lb-data .lb-caption{font-size:13px;font-weight:700;line-height:1em}.lb-data .lb-caption a{color:#4ae}.lb-data .lb-number{display:block;clear:left;padding-bottom:1em;font-size:12px;color:#999}.lb-data .lb-close{display:block;float:right;width:30px;height:30px;background:url(../img/lightbox/close.png) top right no-repeat;text-align:right;outline:0;opacity:.7;-webkit-transition:opacity .2s;-moz-transition:opacity .2s;-o-transition:opacity .2s;transition:opacity .2s}.lb-data .lb-close:hover{cursor:pointer;opacity:1}
|
||||||
@@ -1,723 +0,0 @@
|
|||||||
/* ============================================
|
|
||||||
Modern Responsive Design - Nephrology Practice
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--primary-color: #0066cc;
|
|
||||||
--primary-dark: #004a99;
|
|
||||||
--primary-light: #e6f0ff;
|
|
||||||
--accent-color: #ff6b35;
|
|
||||||
--text-color: #2c3e50;
|
|
||||||
--text-light: #5a6c7d;
|
|
||||||
--bg-color: #ffffff;
|
|
||||||
--bg-light: #f8f9fa;
|
|
||||||
--border-color: #dee2e6;
|
|
||||||
--shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
--shadow-lg: 0 8px 16px rgba(0, 0, 0, 0.15);
|
|
||||||
--transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
Reset & Base Styles
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
html, body {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 1.6;
|
|
||||||
color: var(--text-color);
|
|
||||||
background-color: var(--bg-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
border: 0;
|
|
||||||
max-width: 100%;
|
|
||||||
height: auto;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: var(--primary-color);
|
|
||||||
text-decoration: none;
|
|
||||||
transition: var(--transition);
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: var(--primary-dark);
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
Typography
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
|
||||||
font-weight: 600;
|
|
||||||
line-height: 1.2;
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
color: var(--text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 2.5rem;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 2rem;
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
margin-bottom: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
font-size: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
h1 {
|
|
||||||
font-size: 2rem;
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
font-size: 1.25rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
Layout & Container
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
.container {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 0 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.container {
|
|
||||||
padding: 0 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
Header
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
#outer-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header {
|
|
||||||
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-dark) 100%);
|
|
||||||
color: white;
|
|
||||||
padding: 1rem 0;
|
|
||||||
box-shadow: var(--shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
#header-content {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header-logo {
|
|
||||||
font-size: 1.8rem;
|
|
||||||
font-weight: 700;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header-logo-subtitle {
|
|
||||||
font-size: 0.9rem;
|
|
||||||
opacity: 0.9;
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-image {
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
max-height: 300px;
|
|
||||||
object-fit: cover;
|
|
||||||
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-dark) 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
#header {
|
|
||||||
padding: 1.5rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header-logo {
|
|
||||||
font-size: 1.4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header-logo-subtitle {
|
|
||||||
font-size: 0.85rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
Navigation
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
#menu {
|
|
||||||
background-color: var(--bg-color);
|
|
||||||
border-bottom: 3px solid var(--primary-color);
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
z-index: 1000;
|
|
||||||
box-shadow: var(--shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
#menu ul {
|
|
||||||
list-style-type: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
#menu ul li {
|
|
||||||
display: inline-flex;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#menu ul li a {
|
|
||||||
display: block;
|
|
||||||
color: var(--text-color);
|
|
||||||
padding: 1rem 1.5rem;
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: 500;
|
|
||||||
border-bottom: 3px solid transparent;
|
|
||||||
transition: var(--transition);
|
|
||||||
}
|
|
||||||
|
|
||||||
#menu ul li a:hover,
|
|
||||||
#menu ul li a.active {
|
|
||||||
color: var(--primary-color);
|
|
||||||
background-color: var(--primary-light);
|
|
||||||
border-bottom-color: var(--primary-color);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mobile Menu Toggle */
|
|
||||||
#menu-toggle {
|
|
||||||
display: none;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
color: var(--text-color);
|
|
||||||
font-size: 1.5rem;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
#menu ul {
|
|
||||||
flex-direction: column;
|
|
||||||
max-height: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
transition: max-height 0.3s ease;
|
|
||||||
position: absolute;
|
|
||||||
top: 100%;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
background: var(--bg-color);
|
|
||||||
border-bottom: 3px solid var(--primary-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
#menu ul.active {
|
|
||||||
max-height: 500px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#menu ul li {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#menu ul li a {
|
|
||||||
border: none;
|
|
||||||
border-left: 4px solid transparent;
|
|
||||||
padding: 1rem 1.5rem;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#menu ul li a:hover,
|
|
||||||
#menu ul li a.active {
|
|
||||||
border-left-color: var(--primary-color);
|
|
||||||
background-color: var(--primary-light);
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#menu-toggle {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
#menu {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
Content Area
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
#outer-content {
|
|
||||||
flex: 1;
|
|
||||||
padding: 3rem 0;
|
|
||||||
background-color: var(--bg-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
#inner-content {
|
|
||||||
max-width: 900px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 2rem;
|
|
||||||
background: var(--bg-color);
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: var(--shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
#outer-content {
|
|
||||||
padding: 2rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#inner-content {
|
|
||||||
padding: 1.5rem;
|
|
||||||
margin: 0 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
Footer
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
#footer {
|
|
||||||
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-dark) 100%);
|
|
||||||
color: white;
|
|
||||||
padding: 2rem;
|
|
||||||
text-align: center;
|
|
||||||
border-top: 3px solid var(--accent-color);
|
|
||||||
margin-top: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#footer a {
|
|
||||||
color: #e6f0ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
#footer a:hover {
|
|
||||||
color: white;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
Messages
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
.errorMessage,
|
|
||||||
.alert-danger {
|
|
||||||
padding: 1rem;
|
|
||||||
border: 1px solid #f5c6cb;
|
|
||||||
border-radius: 6px;
|
|
||||||
background: #f8d7da;
|
|
||||||
color: #721c24;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.successMessage,
|
|
||||||
.alert-success {
|
|
||||||
padding: 1rem;
|
|
||||||
border: 1px solid #c3e6cb;
|
|
||||||
border-radius: 6px;
|
|
||||||
background: #d4edda;
|
|
||||||
color: #155724;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert {
|
|
||||||
padding: 1rem;
|
|
||||||
border-radius: 6px;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
Forms
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
form {
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="text"],
|
|
||||||
input[type="email"],
|
|
||||||
input[type="phone"],
|
|
||||||
input[type="tel"],
|
|
||||||
input[type="password"],
|
|
||||||
textarea,
|
|
||||||
select {
|
|
||||||
width: 100%;
|
|
||||||
padding: 0.75rem;
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 1rem;
|
|
||||||
font-family: inherit;
|
|
||||||
transition: var(--transition);
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="text"]:focus,
|
|
||||||
input[type="email"]:focus,
|
|
||||||
input[type="phone"]:focus,
|
|
||||||
input[type="tel"]:focus,
|
|
||||||
input[type="password"]:focus,
|
|
||||||
textarea:focus,
|
|
||||||
select:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: var(--primary-color);
|
|
||||||
box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
resize: vertical;
|
|
||||||
min-height: 150px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ktColumn {
|
|
||||||
width: calc(50% - 1rem);
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ktColumn:nth-child(even) {
|
|
||||||
margin-left: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.ktColumn {
|
|
||||||
width: 100%;
|
|
||||||
margin-left: 0 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
Buttons
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
input[type="submit"],
|
|
||||||
input[type="button"],
|
|
||||||
button,
|
|
||||||
.btn {
|
|
||||||
padding: 0.75rem 1.5rem;
|
|
||||||
background: var(--primary-color);
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: var(--transition);
|
|
||||||
display: inline-block;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="submit"]:hover,
|
|
||||||
input[type="button"]:hover,
|
|
||||||
button:hover,
|
|
||||||
.btn:hover {
|
|
||||||
background: var(--primary-dark);
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: var(--shadow-lg);
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="button"]:nth-of-type(1) {
|
|
||||||
background: var(--border-color);
|
|
||||||
color: var(--text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="button"]:nth-of-type(1):hover {
|
|
||||||
background: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
#kontaktbuttons {
|
|
||||||
display: flex;
|
|
||||||
gap: 1rem;
|
|
||||||
margin-top: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#kontaktbuttons input[type="button"],
|
|
||||||
#kontaktbuttons input[type="submit"] {
|
|
||||||
flex: 1;
|
|
||||||
padding: 1rem;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
#kontaktbuttons {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
Lists & Content
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
ul {
|
|
||||||
margin-left: 2rem;
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul li {
|
|
||||||
margin-bottom: 0.75rem;
|
|
||||||
line-height: 1.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul ul {
|
|
||||||
margin-top: 0.75rem;
|
|
||||||
margin-bottom: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
box-shadow: var(--shadow);
|
|
||||||
border-radius: 6px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
table td {
|
|
||||||
padding: 1rem;
|
|
||||||
border-bottom: 1px solid var(--border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
table tr:last-child td {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
table tr.tdHead,
|
|
||||||
table tr:first-child {
|
|
||||||
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-dark) 100%);
|
|
||||||
color: white;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
table tr:nth-child(even) {
|
|
||||||
background-color: var(--primary-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
table tr:hover {
|
|
||||||
background-color: #f0f7ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.kontakt table tr.tdHead td {
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.kontakt table td span {
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
Images & Gallery
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
.floatLeftImage,
|
|
||||||
.floatRightImage {
|
|
||||||
margin: 1rem;
|
|
||||||
padding: 8px;
|
|
||||||
background: white;
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-radius: 6px;
|
|
||||||
box-shadow: var(--shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
.floatLeftImage {
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.floatRightImage {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.floatLeftImage,
|
|
||||||
.floatRightImage {
|
|
||||||
float: none;
|
|
||||||
margin: 1rem auto;
|
|
||||||
max-width: 300px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.floatLeftImage img,
|
|
||||||
.floatRightImage img {
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alignCenter {
|
|
||||||
text-align: center;
|
|
||||||
clear: both;
|
|
||||||
margin: 2rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alignCenter a {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0.5rem;
|
|
||||||
border-radius: 6px;
|
|
||||||
overflow: hidden;
|
|
||||||
box-shadow: var(--shadow);
|
|
||||||
transition: var(--transition);
|
|
||||||
}
|
|
||||||
|
|
||||||
.alignCenter a:hover {
|
|
||||||
transform: translateY(-4px);
|
|
||||||
box-shadow: var(--shadow-lg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.alignCenter img {
|
|
||||||
width: 150px;
|
|
||||||
height: 150px;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alignRight {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clear {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
Maps
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
.profile-map {
|
|
||||||
border-radius: 6px;
|
|
||||||
box-shadow: var(--shadow);
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
Holiday Dialog
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
#holiday-wrapper {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
z-index: 2000;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.holiday-bg {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background: rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.holiday-dialog {
|
|
||||||
position: relative;
|
|
||||||
background: white;
|
|
||||||
padding: 2rem;
|
|
||||||
border-radius: 8px;
|
|
||||||
max-width: 500px;
|
|
||||||
box-shadow: var(--shadow-lg);
|
|
||||||
animation: slideUp 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes slideUp {
|
|
||||||
from {
|
|
||||||
transform: translateY(20px);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: translateY(0);
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.holiday-dialog p {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.holiday-dialog p:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
Responsive Utilities
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 1.25rem;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stack {
|
|
||||||
display: inline-block;
|
|
||||||
min-width: 70px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alignCenter {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media print {
|
|
||||||
#header,
|
|
||||||
#menu,
|
|
||||||
#footer {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#outer-content {
|
|
||||||
background: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
#inner-content {
|
|
||||||
box-shadow: none;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 81 KiB |
@@ -0,0 +1,657 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Praxis-Flyer | Dialysezentrum Berlin-Mahlsdorf</title>
|
||||||
|
<style>
|
||||||
|
@page {
|
||||||
|
size: A4 portrait;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*, *::before, *::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Open Sans', Arial, Helvetica, sans-serif;
|
||||||
|
color: #1e293b;
|
||||||
|
background: #ffffff;
|
||||||
|
line-height: 1.36;
|
||||||
|
font-size: 8.2pt;
|
||||||
|
-webkit-print-color-adjust: exact;
|
||||||
|
print-color-adjust: exact;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
width: 210mm;
|
||||||
|
height: 297mm;
|
||||||
|
position: relative;
|
||||||
|
padding: 9mm 11.5mm 8.5mm;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header */
|
||||||
|
.flyer-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding-bottom: 2.5mm;
|
||||||
|
border-bottom: 2px solid #0284c7;
|
||||||
|
margin-bottom: 3mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-contact {
|
||||||
|
text-align: right;
|
||||||
|
font-size: 8pt;
|
||||||
|
color: #07384d;
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-contact strong {
|
||||||
|
color: #07384d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-contact .email-link {
|
||||||
|
color: #0284c7;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Motto Banner */
|
||||||
|
.motto-banner {
|
||||||
|
background: linear-gradient(135deg, #07384d 0%, #0b4f6c 100%);
|
||||||
|
color: #ffffff;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 2.8mm 4.2mm;
|
||||||
|
margin-bottom: 3mm;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 3mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.motto-text {
|
||||||
|
font-size: 9.6pt;
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: italic;
|
||||||
|
color: #f0f9ff;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.motto-sub {
|
||||||
|
font-size: 7.4pt;
|
||||||
|
color: #bae6fd;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Physicians Row */
|
||||||
|
.physicians-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 3mm;
|
||||||
|
margin-bottom: 3.2mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.physician-card {
|
||||||
|
background: #f8fafc;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-left: 3.5px solid #0284c7;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 2.4mm 3mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.physician-name {
|
||||||
|
font-size: 8.8pt;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #07384d;
|
||||||
|
line-height: 1.22;
|
||||||
|
}
|
||||||
|
|
||||||
|
.physician-role {
|
||||||
|
font-size: 7.3pt;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #0284c7;
|
||||||
|
margin-top: 0.5mm;
|
||||||
|
line-height: 1.22;
|
||||||
|
}
|
||||||
|
|
||||||
|
.physician-meta {
|
||||||
|
font-size: 6.7pt;
|
||||||
|
color: #64748b;
|
||||||
|
margin-top: 0.8mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main 2-Column Content Grid */
|
||||||
|
.content-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 3.6mm;
|
||||||
|
flex: 1;
|
||||||
|
margin-bottom: 3.2mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-stack {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 3mm;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-card {
|
||||||
|
background: #f8fafc;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 3mm 3.6mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-card.card-grow {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
font-size: 8.8pt;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #07384d;
|
||||||
|
margin-bottom: 1.8mm;
|
||||||
|
padding-bottom: 1.1mm;
|
||||||
|
border-bottom: 1px solid #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header svg {
|
||||||
|
width: 13px;
|
||||||
|
height: 13px;
|
||||||
|
fill: #0284c7;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lists & Details */
|
||||||
|
.check-list {
|
||||||
|
list-style: none;
|
||||||
|
font-size: 7.7pt;
|
||||||
|
color: #334155;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-list li {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 12px;
|
||||||
|
margin-bottom: 1.25mm;
|
||||||
|
line-height: 1.32;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-list li:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-list li::before {
|
||||||
|
content: "✓";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
color: #0284c7;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 7.9pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-note {
|
||||||
|
display: block;
|
||||||
|
font-size: 7.1pt;
|
||||||
|
color: #475569;
|
||||||
|
margin-top: 0.3mm;
|
||||||
|
line-height: 1.28;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Schedule rows */
|
||||||
|
.sched-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-around;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sched-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 7.7pt;
|
||||||
|
padding: 1.4mm 0;
|
||||||
|
border-bottom: 1px dashed #e2e8f0;
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sched-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sched-title {
|
||||||
|
font-weight: 700;
|
||||||
|
color: #07384d;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sched-hours {
|
||||||
|
text-align: right;
|
||||||
|
color: #1e293b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-tag {
|
||||||
|
background: #e0f2fe;
|
||||||
|
color: #0369a1;
|
||||||
|
font-size: 6.3pt;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 0.5px 3.5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Locations */
|
||||||
|
.loc-block {
|
||||||
|
margin-bottom: 2.4mm;
|
||||||
|
font-size: 7.7pt;
|
||||||
|
line-height: 1.36;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loc-block:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loc-head-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 0.5mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loc-head {
|
||||||
|
font-weight: 700;
|
||||||
|
color: #07384d;
|
||||||
|
font-size: 8.2pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loc-badge {
|
||||||
|
background: #e0f2fe;
|
||||||
|
color: #0369a1;
|
||||||
|
font-size: 6.6pt;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 0.4mm 1.8mm;
|
||||||
|
border-radius: 99px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loc-body {
|
||||||
|
color: #475569;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loc-body strong {
|
||||||
|
color: #07384d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Anfahrt & Map */
|
||||||
|
.anfahrt-flex {
|
||||||
|
display: flex;
|
||||||
|
gap: 3.2mm;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.anfahrt-text {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 7.5pt;
|
||||||
|
color: #475569;
|
||||||
|
line-height: 1.32;
|
||||||
|
}
|
||||||
|
|
||||||
|
.anfahrt-text p {
|
||||||
|
margin-bottom: 1.2mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.anfahrt-text p:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-thumb {
|
||||||
|
width: 35mm;
|
||||||
|
height: 40mm;
|
||||||
|
object-fit: contain;
|
||||||
|
border: 1px solid #cbd5e1;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: #ffffff;
|
||||||
|
padding: 1mm;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Photo Showcase Card */
|
||||||
|
.photo-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 2.6mm;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-item {
|
||||||
|
position: relative;
|
||||||
|
border-radius: 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid #cbd5e1;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 55mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-item img {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
object-position: center center;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-caption {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: linear-gradient(to top, rgba(7, 56, 77, 0.9), rgba(7, 56, 77, 0));
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 6.6pt;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 3.5mm 2.2mm 1.2mm;
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bottom Callout & Footer */
|
||||||
|
.footer-callout {
|
||||||
|
background: linear-gradient(135deg, #07384d 0%, #0284c7 100%);
|
||||||
|
color: #ffffff;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 2.8mm 4.2mm;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 3mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.callout-left {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.callout-title {
|
||||||
|
font-size: 8.6pt;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 0.4mm;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.callout-sub {
|
||||||
|
font-size: 7pt;
|
||||||
|
color: #e0f2fe;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.callout-channels {
|
||||||
|
display: flex;
|
||||||
|
gap: 4.5mm;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.callout-item {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.callout-item small {
|
||||||
|
display: block;
|
||||||
|
font-size: 5.8pt;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #bae6fd;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.callout-item .channel-val {
|
||||||
|
font-size: 9pt;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #ffffff;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flyer-bottom-info {
|
||||||
|
border-top: 1px solid #e2e8f0;
|
||||||
|
padding-top: 1.3mm;
|
||||||
|
margin-top: 1.5mm;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
gap: 0.4mm;
|
||||||
|
font-size: 6.4pt;
|
||||||
|
color: #64748b;
|
||||||
|
line-height: 1.28;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="page">
|
||||||
|
<!-- Top Brand Header -->
|
||||||
|
<div class="flyer-header">
|
||||||
|
<div class="brand">
|
||||||
|
<img src="file:///d:/web-repo2/img/logo_dialysezentrum.png" style="height: 15mm; width: auto; object-fit: contain;" alt="Dialysezentrum Berlin-Mahlsdorf">
|
||||||
|
</div>
|
||||||
|
<div class="header-contact">
|
||||||
|
<div>Tel. Dialyse: <strong>030 / 566 24 84</strong> • Ambulanz: <strong>030 / 566 25 39</strong></div>
|
||||||
|
<div>E-Mail: <span class="email-link">kontakt@dialyse-berlin.net</span> • Web: <strong>www.dialyse-berlin.net</strong></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Motto Banner -->
|
||||||
|
<div class="motto-banner">
|
||||||
|
<div class="motto-text">„Die individuelle Dialyse im grünen Gürtel von Berlin“</div>
|
||||||
|
<div class="motto-sub">Seit 1992 • 2 Standorte (34 Plätze) • Feriendialyse • Barrierefrei</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Physicians Row -->
|
||||||
|
<div class="physicians-row">
|
||||||
|
<div class="physician-card">
|
||||||
|
<div class="physician-name">Dr. med. Bianca Bischoff</div>
|
||||||
|
<div class="physician-role">Fachärztin für Innere Medizin & Nephrologie</div>
|
||||||
|
<div class="physician-meta">Approbation 2002 • Ärztekammer Berlin</div>
|
||||||
|
</div>
|
||||||
|
<div class="physician-card">
|
||||||
|
<div class="physician-name">Dr. med. Juliane Hoffzimmer</div>
|
||||||
|
<div class="physician-role">Fachärztin für Innere Medizin & Nephrologie</div>
|
||||||
|
<div class="physician-meta">Approbation 2009 • Ärztekammer Berlin</div>
|
||||||
|
</div>
|
||||||
|
<div class="physician-card">
|
||||||
|
<div class="physician-name">Dr. med. Claudia Körner</div>
|
||||||
|
<div class="physician-role">Fachärztin für Innere Medizin</div>
|
||||||
|
<div class="physician-meta">Approbation 2004 • Ärztekammer Berlin</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Main 2-Column Content -->
|
||||||
|
<div class="content-grid">
|
||||||
|
<!-- LEFT COLUMN: Leistungen, Komfort & Behandlungszeiten -->
|
||||||
|
<div class="col-stack">
|
||||||
|
<!-- Leistungen -->
|
||||||
|
<div class="section-card">
|
||||||
|
<div class="card-header">
|
||||||
|
<svg viewBox="0 0 24 24"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-2 10h-4v4h-2v-4H7v-2h4V7h2v4h4v2z"/></svg>
|
||||||
|
Leistungsspektrum & Medizintechnik
|
||||||
|
</div>
|
||||||
|
<ul class="check-list">
|
||||||
|
<li><strong>Nephrologische Ambulanz:</strong> Umfassende Diagnostik & Therapie von Nierenerkrankungen</li>
|
||||||
|
<li>Optimale <strong>Blutdruck- & Blutzuckereinstellung</strong> als wesentlicher Schwerpunkt</li>
|
||||||
|
<li>Spezialisierte Betreuung <strong>niereninsuffizienter Diabetiker</strong></li>
|
||||||
|
<li><strong>Sonographie</strong> (Ultraschall Nieren/Abdomen), EKG & 24-Std.-Blutdruckmessung</li>
|
||||||
|
<li>
|
||||||
|
<strong>Hämodialyse (HD) & Hämodiafiltration (HDF / Online-HDF):</strong>
|
||||||
|
<span class="sub-note">Nikkiso DBB-07 & DBB-EXA mit <strong>Haemo-Master (BV-UFC/COC)</strong> zur aktiven Blutvolumen-, Ultrafiltrations- & Leitfähigkeitsregelung</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Fresenius BCM (Body Composition Monitor):</strong>
|
||||||
|
<span class="sub-note">Bioelektrische Präzisionsmessung zur exakten Bestimmung des Optimalgewichts</span>
|
||||||
|
</li>
|
||||||
|
<li><strong>High- & Low-Flux-Dialysatoren</strong>, moderne Osmoseanlage & Praxislabor</li>
|
||||||
|
<li><strong>Transplantationsvorbereitung</strong> & Nachsorge • <strong>Ernährungsberatung</strong></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Feriendialyse & Komfort -->
|
||||||
|
<div class="section-card">
|
||||||
|
<div class="card-header">
|
||||||
|
<svg viewBox="0 0 24 24"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>
|
||||||
|
Feriendialyse & Patientenkomfort
|
||||||
|
</div>
|
||||||
|
<ul class="check-list">
|
||||||
|
<li><strong>Ganzjährige Feriendialysen</strong> für Berlin-Besucher und Urlauber</li>
|
||||||
|
<li>Vollständig <strong>barrierefreier Zugang</strong> für bewegungseingeschränkte Patienten</li>
|
||||||
|
<li>Erfahrenes Team aus <strong>3 Fachärztinnen und 23 Pflege- & Servicekräften</strong></li>
|
||||||
|
<li>Wohlfühlatmosphäre mit <strong>kostenlosem WLAN, TV & Imbiss- und Getränkeservice</strong></li>
|
||||||
|
<li>Enge Kooperation mit Haus- & Fachärzten sowie umliegenden Krankenhäusern</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Öffnungszeiten -->
|
||||||
|
<div class="section-card card-grow">
|
||||||
|
<div class="card-header">
|
||||||
|
<svg viewBox="0 0 24 24"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z"/></svg>
|
||||||
|
Behandlungs- & Sprechzeiten
|
||||||
|
</div>
|
||||||
|
<div class="sched-list">
|
||||||
|
<div class="sched-item">
|
||||||
|
<div class="sched-title">Dialyse (Mo, Mi, Fr):</div>
|
||||||
|
<div class="sched-hours">
|
||||||
|
<div><span class="badge-tag">1. Schicht</span> 07:00 – 13:00 Uhr</div>
|
||||||
|
<div><span class="badge-tag">2. Schicht</span> 13:00 – 18:00 Uhr</div>
|
||||||
|
<div><span class="badge-tag">3. Schicht</span> 14:30 – 20:00 Uhr</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sched-item">
|
||||||
|
<div class="sched-title">Dialyse (Di, Do, Sa):</div>
|
||||||
|
<div class="sched-hours"><span class="badge-tag">1. Schicht</span> 07:00 – 13:00 Uhr</div>
|
||||||
|
</div>
|
||||||
|
<div class="sched-item">
|
||||||
|
<div class="sched-title">Ambulanz (Mo, Mi, Fr):</div>
|
||||||
|
<div class="sched-hours">08:30 – 11:30 • n. Vereinb. 14:00 – 17:00</div>
|
||||||
|
</div>
|
||||||
|
<div class="sched-item">
|
||||||
|
<div class="sched-title">Ambulanz (Di, Do):</div>
|
||||||
|
<div class="sched-hours">08:30 – 12:00 Uhr</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- RIGHT COLUMN: Standorte, Anfahrt & Praxis-Einblicke -->
|
||||||
|
<div class="col-stack">
|
||||||
|
<!-- Standorte -->
|
||||||
|
<div class="section-card">
|
||||||
|
<div class="card-header">
|
||||||
|
<svg viewBox="0 0 24 24"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5a2.5 2.5 0 0 1 0-5 2.5 2.5 0 0 1 0 5z"/></svg>
|
||||||
|
Unsere Standorte in Berlin
|
||||||
|
</div>
|
||||||
|
<div class="loc-block">
|
||||||
|
<div class="loc-head-row">
|
||||||
|
<div class="loc-head">Zentrumsdialyse (Hauptpraxis & Ambulanz)</div>
|
||||||
|
<span class="loc-badge">16 Plätze</span>
|
||||||
|
</div>
|
||||||
|
<div class="loc-body">
|
||||||
|
<strong>Thorner Straße 1 • 12623 Berlin-Mahlsdorf</strong><br>
|
||||||
|
Telefon Dialyse: <strong>030 / 566 24 84</strong><br>
|
||||||
|
Telefon Ambulanz: <strong>030 / 566 25 39</strong><br>
|
||||||
|
E-Mail: <strong style="color: #0284c7;">kontakt@dialyse-berlin.net</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="loc-block" style="border-top: 1px dashed #e2e8f0; padding-top: 2mm;">
|
||||||
|
<div class="loc-head-row">
|
||||||
|
<div class="loc-head">Satellitendialyse Berlin-Marzahn</div>
|
||||||
|
<span class="loc-badge">18 Plätze</span>
|
||||||
|
</div>
|
||||||
|
<div class="loc-body">
|
||||||
|
<strong>Blumberger Damm 130 • 12685 Berlin</strong><br>
|
||||||
|
Eingang: Pritzhagener Weg 21 • Tel.: <strong>030 / 566 24 84</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Anfahrt & Skizze -->
|
||||||
|
<div class="section-card">
|
||||||
|
<div class="card-header">
|
||||||
|
<svg viewBox="0 0 24 24"><path d="M20.5 3l-.16.03L15 5.1 9 3 3.36 4.9c-.21.07-.36.25-.36.48V20.5c0 .28.22.5.5.5l.16-.03L9 18.9l6 2.1 5.64-1.9c.21-.07.36-.25-.36-.48V3.5c0-.28-.22-.5-.5-.5zM15 19l-6-2.11V5l6 2.11V19z"/></svg>
|
||||||
|
Anfahrt & Parken (Thorner Straße 1)
|
||||||
|
</div>
|
||||||
|
<div class="anfahrt-flex">
|
||||||
|
<div class="anfahrt-text">
|
||||||
|
<p><strong>ÖPNV:</strong> S-Bahn <strong>S5</strong> oder Regionalbahn bis <strong>S-Bhf. Berlin-Mahlsdorf</strong>, von dort wenige Minuten Fußweg oder per Bus.</p>
|
||||||
|
<p><strong>Pkw:</strong> Über die <strong>B1 / B5 (Alt-Mahlsdorf)</strong> in die Hönower Str., weiter über Waldowstr. und Wilhelmsmühlenweg in die Thorner Str. 1.</p>
|
||||||
|
<p style="color: #0284c7; font-weight: 600;">✓ Gute Parkmöglichkeiten direkt vor der Praxis vorhanden.</p>
|
||||||
|
</div>
|
||||||
|
<img src="file:///d:/web-repo2/files/flyer_map.png" class="map-thumb" alt="Lageplan">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Praxis-Einblicke (2 Bilder) -->
|
||||||
|
<div class="section-card card-grow">
|
||||||
|
<div class="card-header">
|
||||||
|
<svg viewBox="0 0 24 24"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/></svg>
|
||||||
|
Einblicke in unsere Praxis
|
||||||
|
</div>
|
||||||
|
<div class="photo-grid">
|
||||||
|
<div class="photo-item" style="background: url('file:///d:/web-repo2/img/gallery/praxis1.jpg') 92% 65% / cover no-repeat;">
|
||||||
|
<div class="photo-caption">Praxisgebäude • Thorner Str. 1</div>
|
||||||
|
</div>
|
||||||
|
<div class="photo-item" style="background: url('file:///d:/web-repo2/img/gallery/leistung1.jpg') 44% 55% / cover no-repeat;">
|
||||||
|
<div class="photo-caption">Dialyseplatz im Grünen</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bottom Action Callout -->
|
||||||
|
<div class="footer-callout">
|
||||||
|
<div class="callout-left">
|
||||||
|
<div class="callout-title">Haben Sie Fragen oder wünschen Sie einen Termin?</div>
|
||||||
|
<div class="callout-sub">Nehmen Sie direkt Kontakt auf • Online-Anfrage: www.dialyse-berlin.net</div>
|
||||||
|
</div>
|
||||||
|
<div class="callout-channels">
|
||||||
|
<div class="callout-item">
|
||||||
|
<small>Tel. Dialyse</small>
|
||||||
|
<div class="channel-val">030 / 566 24 84</div>
|
||||||
|
</div>
|
||||||
|
<div class="callout-item">
|
||||||
|
<small>Tel. Ambulanz</small>
|
||||||
|
<div class="channel-val">030 / 566 25 39</div>
|
||||||
|
</div>
|
||||||
|
<div class="callout-item">
|
||||||
|
<small>E-Mail-Kontakt</small>
|
||||||
|
<div class="channel-val">kontakt@dialyse-berlin.net</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bottom Info Line -->
|
||||||
|
<div class="flyer-bottom-info">
|
||||||
|
<div>Dialysezentrum Berlin-Mahlsdorf • Zentrum für Nephrologie und Dialyse • Dr. med. Bianca Bischoff & Dr. med. Juliane Hoffzimmer</div>
|
||||||
|
<div>Thorner Str. 1, 12623 Berlin • Blumberger Damm 130, 12685 Berlin</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 547 KiB |
|
Before Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 703 KiB |
|
Before Width: | Height: | Size: 134 KiB |
|
Before Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 894 B After Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 165 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 368 KiB After Width: | Height: | Size: 194 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 282 KiB After Width: | Height: | Size: 131 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 269 KiB After Width: | Height: | Size: 134 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 329 KiB After Width: | Height: | Size: 159 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 259 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 363 KiB After Width: | Height: | Size: 345 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 501 KiB After Width: | Height: | Size: 436 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 213 KiB After Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 259 KiB After Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 195 KiB After Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 903 B |
|
Before Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 255 B |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 248 B |
|
Before Width: | Height: | Size: 155 B |
|
Before Width: | Height: | Size: 211 B |
|
Before Width: | Height: | Size: 237 B |
@@ -48,6 +48,7 @@ switch ($action) {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
http_response_code(404);
|
http_response_code(404);
|
||||||
|
$action = '404';
|
||||||
$content = $loadFile('view/error.php');
|
$content = $loadFile('view/error.php');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +1,79 @@
|
|||||||
function initialize() {
|
/**
|
||||||
var geocoder = new google.maps.Geocoder();
|
* Contact and Map Helpers
|
||||||
|
* Dialysezentrum Berlin-Mahlsdorf
|
||||||
|
*/
|
||||||
|
|
||||||
geocoder.geocode( { 'address': 'Thorner Straße 1,12623 Berlin'}, function(results, status) {
|
function sending(user, domain, tld) {
|
||||||
if(status == google.maps.GeocoderStatus.OK) {
|
user = user || 'kontakt';
|
||||||
var myOptions = {
|
domain = domain || 'dialyse-berlin';
|
||||||
zoom: 15,
|
tld = tld || 'net';
|
||||||
center: results[0].geometry.location,
|
window.location.href = 'mailto:' + user + '@' + domain + '.' + tld;
|
||||||
mapTypeId: google.maps.MapTypeId.ROADMAP,
|
return true;
|
||||||
mapTypeControl: false,
|
}
|
||||||
navigationControl: true,
|
|
||||||
navigationControlOptions: {style: google.maps.NavigationControlStyle.SMALL}
|
|
||||||
};
|
|
||||||
|
|
||||||
var map = new google.maps.Map(document.getElementById("googlemap"), myOptions);
|
function initObfuscatedEmails() {
|
||||||
|
// Dynamically reconstruct obfuscated email links and text for real visitors
|
||||||
|
$('.mail-obf').each(function() {
|
||||||
|
var $el = $(this);
|
||||||
|
var u = $el.attr('data-usr') || $el.data('usr');
|
||||||
|
var d = $el.attr('data-dom') || $el.data('dom');
|
||||||
|
var t = $el.attr('data-tld') || $el.data('tld');
|
||||||
|
if (u && d && t) {
|
||||||
|
var email = u + '@' + d + '.' + t;
|
||||||
|
$el.attr('href', 'mailto:' + email);
|
||||||
|
var $val = $el.find('.mail-val');
|
||||||
|
if ($val.length) {
|
||||||
|
$val.removeClass('c-rev').text(email);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
var marker = new google.maps.Marker({
|
$('.mail-obf-text').each(function() {
|
||||||
position: results[0].geometry.location,
|
var $el = $(this);
|
||||||
map: map
|
var u = $el.attr('data-usr') || $el.data('usr');
|
||||||
});
|
var d = $el.attr('data-dom') || $el.data('dom');
|
||||||
} else {
|
var t = $el.attr('data-tld') || $el.data('tld');
|
||||||
alert("Geocode was not successful for the following reason: " + status);
|
if (u && d && t) {
|
||||||
|
var email = u + '@' + d + '.' + t;
|
||||||
|
var $val = $el.find('.mail-val');
|
||||||
|
if ($val.length) {
|
||||||
|
$val.removeClass('c-rev').text(email);
|
||||||
|
} else {
|
||||||
|
$el.text(email);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize();
|
$(function() {
|
||||||
|
initObfuscatedEmails();
|
||||||
|
});
|
||||||
|
|
||||||
function sending() {
|
function showMapLocation(loc) {
|
||||||
var text = "%6D%61%69%6C%74%6F:%6B%6F%6E%74%61%6B%74@%64%69%61%6C%79%73%65%2D%62%65%72%6C%69%6E%2E%6E%65%74";
|
var $frame = $('#mapFrame');
|
||||||
|
var $headerTitle = $('#mapHeaderTitle');
|
||||||
|
var $addrText = $('#mapAddressText');
|
||||||
|
var $extLink = $('#mapExternalLink');
|
||||||
|
var $btnMahlsdorf = $('#btnMapMahlsdorf');
|
||||||
|
var $btnSatellit = $('#btnMapSatellit');
|
||||||
|
|
||||||
location.href = decodeURI(text);
|
if (loc === 'satellit') {
|
||||||
|
$frame.attr('src', 'https://maps.google.com/maps?q=Blumberger+Damm+130,+12685+Berlin&t=&z=15&ie=UTF8&iwloc=&output=embed');
|
||||||
return true;
|
$frame.attr('title', 'Karte Satellitendialyse Berlin-Marzahn');
|
||||||
|
$headerTitle.html('Standortkarte • Berlin-Marzahn');
|
||||||
|
$addrText.text('Blumberger Damm 130 (Eingang Pritzhagener Weg 21), 12685 Berlin');
|
||||||
|
$extLink.attr('href', 'https://www.google.com/maps/place/Dialysezentrum+Berlin-Mahlsdorf+LC/@52.5428128,13.5716785,17z/data=!3m1!4b1!4m6!3m5!1s0x47a84991f9d5111b:0xda0b4785760166c5!8m2!3d52.5428128!4d13.5716785!16s%2Fg%2F1ts_5mmj');
|
||||||
|
|
||||||
|
$btnMahlsdorf.removeClass('btn-primary').addClass('btn-outline-primary').attr('aria-pressed', 'false');
|
||||||
|
$btnSatellit.removeClass('btn-outline-primary').addClass('btn-primary').attr('aria-pressed', 'true');
|
||||||
|
} else {
|
||||||
|
$frame.attr('src', 'https://maps.google.com/maps?q=Thorner+Stra%C3%9Fe+1,+12623+Berlin&t=&z=15&ie=UTF8&iwloc=&output=embed');
|
||||||
|
$frame.attr('title', 'Karte Zentrumsdialyse Berlin-Mahlsdorf');
|
||||||
|
$headerTitle.html('Standortkarte • Berlin-Mahlsdorf');
|
||||||
|
$addrText.text('Thorner Straße 1, 12623 Berlin');
|
||||||
|
$extLink.attr('href', 'https://www.google.com/maps/place/Dialysezentrum+Berlin-Mahlsdorf/@52.5078291,13.6092903,17z/data=!3m1!4b1!4m6!3m5!1s0x47a84991f9c57f7f:0xad4486f559442067!8m2!3d52.5078291!4d13.6092903!16s%2Fg%2F1tdqghgn');
|
||||||
|
|
||||||
|
$btnMahlsdorf.removeClass('btn-outline-primary').addClass('btn-primary').attr('aria-pressed', 'true');
|
||||||
|
$btnSatellit.removeClass('btn-primary').addClass('btn-outline-primary').attr('aria-pressed', 'false');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
/*!
|
|
||||||
* jQuery JavaScript Library v3.4.1
|
|
||||||
* https://jquery.com/
|
|
||||||
*
|
|
||||||
* Includes Sizzle.js
|
|
||||||
* https://sizzlejs.com/
|
|
||||||
*
|
|
||||||
* Copyright JS Foundation and other contributors
|
|
||||||
* Released under the MIT license
|
|
||||||
* https://jquery.org/license
|
|
||||||
*
|
|
||||||
* Date: 2019-05-01T21:04Z
|
|
||||||
*/
|
|
||||||
!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";function c(a,b,c){c=c||ia;var d,e,f=c.createElement("script");if(f.text=a,b)for(d in wa)(e=b[d]||b.getAttribute&&b.getAttribute(d))&&f.setAttribute(d,e);c.head.appendChild(f).parentNode.removeChild(f)}function d(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?oa[pa.call(a)]||"object":typeof a}function e(a){var b=!!a&&"length"in a&&a.length,c=d(a);return!ua(a)&&!va(a)&&("array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a)}function f(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()}function g(a,b,c){return ua(b)?ya.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?ya.grep(a,function(a){return a===b!==c}):"string"!=typeof b?ya.grep(a,function(a){return na.call(b,a)>-1!==c}):ya.filter(b,a,c)}function h(a,b){for(;(a=a[b])&&1!==a.nodeType;);return a}function i(a){var b={};return ya.each(a.match(Ja)||[],function(a,c){b[c]=!0}),b}function j(a){return a}function k(a){throw a}function l(a,b,c,d){var e;try{a&&ua(e=a.promise)?e.call(a).done(b).fail(c):a&&ua(e=a.then)?e.call(a,b,c):b.apply(void 0,[a].slice(d))}catch(a){c.apply(void 0,[a])}}function m(){ia.removeEventListener("DOMContentLoaded",m),a.removeEventListener("load",m),ya.ready()}function n(a,b){return b.toUpperCase()}function o(a){return a.replace(Na,"ms-").replace(Oa,n)}function p(){this.expando=ya.expando+p.uid++}function q(a){return"true"===a||"false"!==a&&("null"===a?null:a===+a+""?+a:Sa.test(a)?JSON.parse(a):a)}function r(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Ta,"-$&").toLowerCase(),"string"==typeof(c=a.getAttribute(d))){try{c=q(c)}catch(a){}Ra.set(a,b,c)}else c=void 0;return c}function s(a,b,c,d){var e,f,g=20,h=d?function(){return d.cur()}:function(){return ya.css(a,b,"")},i=h(),j=c&&c[3]||(ya.cssNumber[b]?"":"px"),k=a.nodeType&&(ya.cssNumber[b]||"px"!==j&&+i)&&Va.exec(ya.css(a,b));if(k&&k[3]!==j){for(i/=2,j=j||k[3],k=+i||1;g--;)ya.style(a,b,k+j),(1-f)*(1-(f=h()/i||.5))<=0&&(g=0),k/=f;k*=2,ya.style(a,b,k+j),c=c||[]}return c&&(k=+k||+i||0,e=c[1]?k+(c[1]+1)*c[2]:+c[2],d&&(d.unit=j,d.start=k,d.end=e)),e}function t(a){var b,c=a.ownerDocument,d=a.nodeName,e=ab[d];return e||(b=c.body.appendChild(c.createElement(d)),e=ya.css(b,"display"),b.parentNode.removeChild(b),"none"===e&&(e="block"),ab[d]=e,e)}function u(a,b){for(var c,d,e=[],f=0,g=a.length;f<g;f++)d=a[f],d.style&&(c=d.style.display,b?("none"===c&&(e[f]=Qa.get(d,"display")||null,e[f]||(d.style.display="")),""===d.style.display&&$a(d)&&(e[f]=t(d))):"none"!==c&&(e[f]="none",Qa.set(d,"display",c)));for(f=0;f<g;f++)null!=e[f]&&(a[f].style.display=e[f]);return a}function v(a,b){var c;return c=void 0!==a.getElementsByTagName?a.getElementsByTagName(b||"*"):void 0!==a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&f(a,b)?ya.merge([a],c):c}function w(a,b){for(var c=0,d=a.length;c<d;c++)Qa.set(a[c],"globalEval",!b||Qa.get(b[c],"globalEval"))}function x(a,b,c,e,f){for(var g,h,i,j,k,l,m=b.createDocumentFragment(),n=[],o=0,p=a.length;o<p;o++)if((g=a[o])||0===g)if("object"===d(g))ya.merge(n,g.nodeType?[g]:g);else if(fb.test(g)){for(h=h||m.appendChild(b.createElement("div")),i=(cb.exec(g)||["",""])[1].toLowerCase(),j=eb[i]||eb._default,h.innerHTML=j[1]+ya.htmlPrefilter(g)+j[2],l=j[0];l--;)h=h.lastChild;ya.merge(n,h.childNodes),h=m.firstChild,h.textContent=""}else n.push(b.createTextNode(g));for(m.textContent="",o=0;g=n[o++];)if(e&&ya.inArray(g,e)>-1)f&&f.push(g);else if(k=Ya(g),h=v(m.appendChild(g),"script"),k&&w(h),c)for(l=0;g=h[l++];)db.test(g.type||"")&&c.push(g);return m}function y(){return!0}function z(){return!1}function A(a,b){return a===B()==("focus"===b)}function B(){try{return ia.activeElement}catch(a){}}function C(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)C(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),!1===e)e=z;else if(!e)return a;return 1===f&&(g=e,e=function(a){return ya().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=ya.guid++)),a.each(function(){ya.event.add(this,b,e,d,c)})}function D(a,b,c){if(!c)return void(void 0===Qa.get(a,b)&&ya.event.add(a,b,y));Qa.set(a,b,!1),ya.event.add(a,b,{namespace:!1,handler:function(a){var d,e,f=Qa.get(this,b);if(1&a.isTrigger&&this[b]){if(f.length)(ya.event.special[b]||{}).delegateType&&a.stopPropagation();else if(f=ka.call(arguments),Qa.set(this,b,f),d=c(this,b),this[b](),e=Qa.get(this,b),f!==e||d?Qa.set(this,b,!1):e={},f!==e)return a.stopImmediatePropagation(),a.preventDefault(),e.value}else f.length&&(Qa.set(this,b,{value:ya.event.trigger(ya.extend(f[0],ya.Event.prototype),f.slice(1),this)}),a.stopImmediatePropagation())}})}function E(a,b){return f(a,"table"Line truncated
|
|
||||||
* Sizzle CSS Selector Engine v2.3.4
|
|
||||||
* https://sizzlejs.com/
|
|
||||||
*
|
|
||||||
* Copyright JS Foundation and other contributors
|
|
||||||
* Released under the MIT license
|
|
||||||
* https://js.foundation/
|
|
||||||
*
|
|
||||||
* Date: 2019-04-08
|
|
||||||
*/
|
|
||||||
function(a){function b(a,b,c,d){var e,f,g,h,i,j,k,m=b&&b.ownerDocument,o=b?b.nodeType:9;if(c=c||[],"string"!=typeof a||!a||1!==o&&9!==o&&11!==o)return c;if(!d&&((b?b.ownerDocument||b:P)!==H&&G(b),b=b||H,J)){if(11!==o&&(i=ta.exec(a)))if(e=i[1]){if(9===o){if(!(g=b.getElementById(e)))return c;if(g.id===e)return c.push(g),c}else if(m&&(g=m.getElementById(e))&&N(b,g)&&g.id===e)return c.push(g),c}else{if(i[2])return _.apply(c,b.getElementsByTagName(a)),c;if((e=i[3])&&w.getElementsByClassName&&b.getElementsByClassName)return _.apply(c,b.getElementsByClassName(e)),c}if(w.qsa&&!V[a+" "]&&(!K||!K.test(a))&&(1!==o||"object"!==b.nodeName.toLowerCase())){if(k=a,m=b,1===o&&la.test(a)){for((h=b.getAttribute("id"))?h=h.replace(xa,ya):b.setAttribute("id",h=O),j=A(a),f=j.length;f--;)j[f]="#"+h+" "+n(j[f]);k=j.join(","),m=ua.test(a)&&l(b.parentNode)||b}try{return _.apply(c,m.querySelectorAll(k)),c}catch(b){V(a,!0)}finally{h===O&&b.removeAttribute("id")}}}return C(a.replace(ia,"$1"),b,c,d)}function c(){function a(c,d){return b.push(c+" ")>x.cacheLength&&delete a[b.shift()],a[c+" "]=d}var b=[];return a}function d(a){return a[O]=!0,a}function e(a){var b=H.createElement("fieldset");try{return!!a(b)}catch(a){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function f(a,b){for(var c=a.split("|"),d=c.length;d--;)x.attrHandle[c[d]]=b}function g(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)for(;c=c.nextSibling;)if(c===b)return-1;return a?1:-1}function h(a){return function(b){return"input"===b.nodeName.toLowerCase()&&b.type===a}}function i(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function j(a){return function(b){return"form"in b?b.parentNode&&!1===b.disabled?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&Aa(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function k(a){return d(function(b){return b=+b,d(function(c,d){for(var e,f=a([],c.length,b),g=f.length;g--;)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function l(a){return a&&void 0!==a.getElementsByTagName&&a}function m(){}function n(a){for(var b=0,c=a.length,d="";b<c;b++)d+=a[b].value;return d}function o(a,b,c){var d=b.dir,e=b.next,f=e||d,g=c&&"parentNode"===f,h=R++;return b.first?function(b,c,e){for(;b=b[d];)if(1===b.nodeType||g)return a(b,c,e);return!1}:function(b,c,i){var j,k,l,m=[Q,h];if(i){for(;b=b[d];)if((1===b.nodeType||g)&&a(b,c,i))return!0}else for(;b=b[d];)if(1===b.nodeType||g)if(l=b[O]||(b[O]={}),k=l[b.uniqueID]||(l[b.uniqueID]={}),e&&e===b.nodeName.toLowerCase())b=b[d]||b;else{if((j=k[f])&&j[0]===Q&&j[1]===h)return m[2]=j[2];if(k[f]=m,m[2]=a(b,c,i))return!0}return!1}}function p(a){return a.length>1?function(b,c,d){for(var e=a.length;e--;)if(!a[e](b,c,d))return!1;return!0}:a[0]}function q(a,c,d){for(var e=0,f=c.length;e<f;e++)b(a,c[e],d);return d}function r(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;h<i;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function s(a,b,c,e,f,g){return e&&!e[O]&&(e=s(e)),f&&!f[O]&&(f=s(f,g)),d(function(d,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=d||q(b||"*",h.nodeType?[h]:h,[]),s=!a||!d&&b?p:r(p,m,a,h,i),t=c?f||(d?a:o||e)?[]:g:s;if(c&&c(s,t,h,i),e)for(j=r(t,n),e(j,[],h,i),k=j.length;k--;)(l=j[k])&&(t[n[k]]=!(s[n[k]]=l));if(d){if(f||a){if(f){for(j=[],k=t.length;k--;)(l=t[k])&&j.push(s[k]=l);f(null,t=[],j,i)}for(k=t.length;k--;)(l=t[k])&&(j=f?ba(d,l):m[k])>-1&&(d[j]=!(g[j]=l))}}else t=r(t===g?t.splice(o,t.length):t),f?f(null,g,t,i):_.apply(g,t)})}function t(a){for(var b,c,d,e=a.length,f=x.relative[a[0].type],g=f||x.relative[" "],h=f?1:0,i=o(function(a){return a===b},g,!0),j=o(function(a){return ba(b,a)>-1},g,!0),k=[function(a,c,d){var e=!f&&(d||c!==D)||((b=c).nodeType?i(a,c,d):j(a,c,d));return b=null,e}];h<e;h++)if(c=x.relative[a[h].type])k=[o(p(k),c)];else{if(c=x.filter[a[h].type].apply(null,a[h].matches),c[O]){for(d=++h;d<e&&!x.relative[a[d].type];d++);return s(h>1&&p(k),h>1&&n(a.slice(0,h-1).concat({value:" "===a[h-2].type?"*":""})).replace(ia,"$1"),c,h<d&&t(a.slice(h,d)),d<e&&t(a=a.slice(d)),d<e&&n(a))}k.push(c)}return p(k)}function u(a,c){var e=c.length>0,f=a.length>0,g=function(d,g,h,i,j){var k,l,m,n=0,o="0",p=d&&[],q=[],s=D,t=d||f&&x.find.TAG("*",j),u=Q+=null==s?1:Math.random()||.1,v=t.length;for(j&&(D=g===H||g||j);o!==v&&null!=(k=t[o]);o++){if(f&&k){for(l=0,g||k.ownerDocument===H||(G(k),h=!J);m=a[l++];)if(m(k,g||H,h)){i.push(k);break}j&&(Q=u)}e&&((k=!m&&k)&&n--,d&&p.push(k))}if(n+=o,e&&o!==n){for(l=0;m=c[l++];)m(p,q,g,h);if(d){if(n>0)for(;o--;)p[o]||q[o]||(q[o]=Z.call(i));q=r(q)}_.apply(i,q),j&&!d&&q.length>0&&n+c.length>1&&b.uniqueSort(i)}return j&&(Q=u,D=s),p};return e?d(g):g}var v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O="sizzle"+1*new Date,P=a.document,Q=0,R=0,S=c(),T=c(),U=c(),V=c(),W=function(a,b){return a===b&&(F=!0),0},X={}.hasOwnProperty,Y=[],Z=Y.pop,$=Y.push,_=Y.push,aa=Y.slice,ba=function(a,b){for(var c=0,d=a.length;c<Line truncated
|
|
||||||
* Lightbox v2.11.3
|
|
||||||
* by Lokesh Dhakar
|
|
||||||
*
|
|
||||||
* More info:
|
|
||||||
* http://lokeshdhakar.com/projects/lightbox2/
|
|
||||||
*
|
|
||||||
* Copyright Lokesh Dhakar
|
|
||||||
* Released under the MIT license
|
|
||||||
* https://github.com/lokesh/lightbox2/blob/master/LICENSE
|
|
||||||
*
|
|
||||||
* @preserve
|
|
||||||
*/
|
|
||||||
function(a,b){"function"==typeof define&&define.amd?define(["jquery"],b):"object"==typeof exports?module.exports=b(require("jquery")):a.lightbox=b(a.jQuery)}(this,function(a){function b(b){this.album=[],this.currentImageIndex=void 0,this.init(),this.options=a.extend({},this.constructor.defaults),this.option(b)}return b.defaults={albumLabel:"Image %1 of %2",alwaysShowNavOnTouchDevices:!1,fadeDuration:600,fitImagesInViewport:!0,imageFadeDuration:600,positionFromTop:50,resizeDuration:700,showImageNumberLabel:!0,wrapAround:!1,disableScrolling:!1,sanitizeTitle:!1},b.prototype.option=function(b){a.extend(this.options,b)},b.prototype.imageCountLabel=function(a,b){return this.options.albumLabel.replace(/%1/g,a).replace(/%2/g,b)},b.prototype.init=function(){var b=this;a(document).ready(function(){b.enable(),b.build()})},b.prototype.enable=function(){var b=this;a("body").on("click","a[rel^=lightbox], area[rel^=lightbox], a[data-lightbox], area[data-lightbox]",function(c){return b.start(a(c.currentTarget)),!1})},b.prototype.build=function(){if(!(a("#lightbox").length>0)){var b=this;a('<div id="lightboxOverlay" tabindex="-1" class="lightboxOverlay"></div><div id="lightbox" tabindex="-1" class="lightbox"><div class="lb-outerContainer"><div class="lb-container"><img class="lb-image" src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" alt=""/><div class="lb-nav"><a class="lb-prev" aria-label="Previous image" href="" ></a><a class="lb-next" aria-label="Next image" href="" ></a></div><div class="lb-loader"><a class="lb-cancel"></a></div></div></div><div class="lb-dataContainer"><div class="lb-data"><div class="lb-details"><span class="lb-caption"></span><span class="lb-number"></span></div><div class="lb-closeContainer"><a class="lb-close"></a></div></div></div></div>').appendTo(a("body")),this.$lightbox=a("#lightbox"),this.$overlay=a("#lightboxOverlay"),this.$outerContainer=this.$lightbox.find(".lb-outerContainer"),this.$container=this.$lightbox.find(".lb-container"),this.$image=this.$lightbox.find(".lb-image"),this.$nav=this.$lightbox.find(".lb-nav"),this.containerPadding={top:parseInt(this.$container.css("padding-top"),10),right:parseInt(this.$container.css("padding-right"),10),bottom:parseInt(this.$container.css("padding-bottom"),10),left:parseInt(this.$container.css("padding-left"),10)},this.imageBorderWidth={top:parseInt(this.$image.css("border-top-width"),10),right:parseInt(this.$image.css("border-right-width"),10),bottom:parseInt(this.$image.css("border-bottom-width"),10),left:parseInt(this.$image.css("border-left-width"),10)},this.$overlay.hide().on("click",function(){return b.end(),!1}),this.$lightbox.hide().on("click",function(c){"lightbox"===a(c.target).attr("id")&&b.end()}),this.$outerContainer.on("click",function(c){return"lightbox"===a(c.target).attr("id")&&b.end(),!1}),this.$lightbox.find(".lb-prev").on("click",function(){return 0===b.currentImageIndex?b.changeImage(b.album.length-1):b.changeImage(b.currentImageIndex-1),!1}),this.$lightbox.find(".lb-next").on("click",function(){return b.currentImageIndex===b.album.length-1?b.changeImage(0):b.changeImage(b.currentImageIndex+1),!1}),this.$nav.on("mousedown",function(a){3===a.which&&(b.$nav.css("pointer-events","none"),b.$lightbox.one("contextmenu",function(){setTimeout(function(){this.$nav.css("pointer-events","auto")}.bind(b),0)}))}),this.$lightbox.find(".lb-loader, .lb-close").on("click",function(){return b.end(),!1})}},b.prototype.start=function(b){function c(a){d.album.push({alt:a.attr("data-alt"),link:a.attr("href"),title:a.attr("data-title")||a.attr("title")})}var d=this,e=a(window);e.on("resize",a.proxy(this.sizeOverlay,this)),this.sizeOverlay(),this.album=[];var f,g=0,h=b.attr("data-lightbox");if(h){f=a(b.prop("tagName")+'[data-lightbox="'+h+'"]');for(var i=0;i<f.length;i=++i)c(a(f[i])),f[i]===b[0]&&(g=i)}else if("lightbox"===b.attr("rel"))c(b);else{f=a(b.prop("tagName")+'[rel="'+b.attr("rel")+'"]');for(var j=0;j<f.length;j=++j)c(a(f[j])),f[j]===b[0]&&(g=j)}var k=e.scrollTop()+this.options.positionFromTop,l=e.scrollLeft();this.$lightbox.css({top:k+"px",left:l+"px"}).fadeIn(this.options.fadeDuration),this.options.disableScrolling&&a("body").addClass("lb-disable-scrolling"),this.changeImage(g)},b.prototype.changeImage=function(b){var c=this,d=this.album[b].link,e=d.split(".").slice(-1)[0],f=this.$lightbox.find(".lb-image");this.disableKeyboardNav(),this.$overlay.fadeIn(this.options.fadeDuration),a(".lb-loader").fadeIn("slow"),this.$lightbox.find(".lb-image, .lb-nav, .lb-prev, .lb-next, .lb-dataContainer, .lb-numbers, .lb-caption").hide(),this.$outerContainer.addClass("animating");var g=new Image;g.onload=function(){var h,i,j,k,l,m;f.attr({alt:c.album[b].alt,src:d}),a(g),f.width(g.width),f.height(g.height),m=a(window).width(),l=a(window).height(),k=m-c.containerPadding.left-c.containerPadding.right-c.imageBorderWidth.left-c.imageBorderWidth.right-20,j=l-c.containerPadding.top-c.containerPadding.bottom-c.imageBorderWidLine truncated
|
|
||||||
//# sourceMappingURL=lightbox-plus-jquery.min.map
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"version":3,"sources":["lightbox-plus-jquery.js"],"names":["global","factory","module","exports","document","w","Error","window","this","noGlobal","DOMEval","code","node","doc","i","val","script","createElement","text","preservedScriptAttributes","getAttribute","setAttribute","head","appendChild","parentNode","removeChild","toType","obj","class2type","toString","call","isArrayLike","length","type","isFunction","isWindow","nodeName","elem","name","toLowerCase","winnow","elements","qualifier","not","jQuery","grep","nodeType","indexOf","filter","sibling","cur","dir","createOptions","options","object","each","match","rnothtmlwhite","_","flag","Identity","v","Thrower","ex","adoptValue","value","resolve","reject","noValue","method","promise","done","fail","then","apply","undefined","slice","completed","removeEventListener","ready","fcamelCase","all","letter","toUpperCase","camelCase","string","replace","rmsPrefix","rdashAlpha","Data","expando","uid","getData","data","rbrace","test","JSON","parse","dataAttr","key","rmultiDash","e","dataUser","set","adjustCSS","prop","valueParts","tween","adjusted","scale","maxIterations","currentValue","css","initial","unit","cssNumber","initialInUnit","rcssNum","exec","style","start","end","getDefaultDisplay","temp","ownerDocument","display","defaultDisplayMap","body","showHide","show","values","index","dataPriv","get","isHiddenWithinTree","getAll","context","tag","ret","getElementsByTagName","querySelectorAll","merge","setGlobalEval","elems","refElements","l","buildFragment","scripts","selection","ignored","tmp","wrap","attached","j","fragment","createDocumentFragment","nodes","rhtml","rtagName","wrapMap","_default","innerHTML","htmlPrefilter","lastChild","childNodes","firstChild","textContent","push","createTextNode","inArray","isAttached","rscriptType","returnTrue","returnFalse","expectSync","safeActiveElement","activeElement","err","on","types","selector","fn","one","origFn","event","off","arguments","guid","add","leverageNative","el","namespace","handler","notAsync","result","saved","isTrigger","special","delegateType","stopPropagation","stopImmediatePropagation","preventDefault","trigger","extend","Event","prototype","manipulationTarget","content","children","disableScript","restoreScript","removeAttribute","cloneCopyEvent","src","dest","pdataOld","pdataCur","udataOld","udataCur","events","hasData","access","handle","fixInput","rcheckableType","checked","defaultValue","domManip","collection","args","callback","concat","first","hasScripts","iNoClone","valueIsFunction","support","checkClone","rchecked","self","eq","html","map","clone","contains","_evalUrl","noModule","nonce","rcleanScript","remove","keepData","cleanData","curCSS","computed","width","minWidth","maxWidth","getStyles","getPropertyValue","pixelBoxStyles","rnumnonpx","rboxStyle","addGetHookIf","conditionFn","hookFn","vendorPropName","capName","cssPrefixes","emptyStyle","finalPropName","final","cssProps","vendorProps","setPositiveNumber","subtract","matches","Math","max","boxModelAdjustment","dimension","box","isBorderBox","styles","computedVal","extra","delta","cssExpand","ceil","getWidthOrHeight","boxSizingNeeded","boxSizingReliable","valueIsBorderBox","offsetProp","parseFloat","getClientRects","Tween","easing","init","schedule","inProgress","hidden","requestAnimationFrame","setTimeout","fx","interval","tick","createFxNow","fxNow","Date","now","genFx","includeWidth","which","attrs","height","opacity","createTween","animation","Animation","tweeners","defaultPrefilter","props","opts","toggle","hooks","oldfire","propTween","restoreDisplay","isBox","anim","orig","dataShow","queue","_queueHooks","unqueued","empty","fire","always","rfxtypes","isEmptyObject","overflow","overflowX","overflowY","propFilter","specialEasing","Array","isArray","cssHooks","expand","properties","stopped","prefilters","deferred","Deferred","currentTime","remaining","startTime","duration","percent","tweens","run","notifyWith","resolveWith","originalProperties","originalOptions","stop","gotoEnd","rejectWith","bind","progress","complete","timer","stripAndCollapse","join","getClass","classesToArray","buildParams","prefix","traditional","rbracket","addToPrefiltersOrTransports","structure","dataTypeExpression","func","dataType","dataTypes","unshift","inspectPrefiltersOrTransports","jqXHR","inspect","selected","inspected","prefilterOrFactory","dataTypeOrTransport","seekingTransport","transports","ajaxExtend","target","deep","flatOptions","ajaxSettings","ajaxHandleResponses","s","responses","ct","finalDataType","firstDataType","contents","shift","mimeType","getResponseHeader","converters","ajaxConvert","response","isSuccess","conv2","current","conv","prev","responseFields","dataFilter","split","throws","state","error","arr","getProto","Object","getPrototypeOf","hasOwn","hasOwnProperty","fnToString","ObjectFunctionString","version","rtrim","jquery","constructor","toArray","num","pushStack","prevObject","last","len","sort","splice","copy","copyIsArray"Line truncated
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/*!
|
||||||
|
* Lightbox v2.11.5
|
||||||
|
* by Lokesh Dhakar
|
||||||
|
*
|
||||||
|
* More info:
|
||||||
|
* http://lokeshdhakar.com/projects/lightbox2/
|
||||||
|
*
|
||||||
|
* Copyright Lokesh Dhakar
|
||||||
|
* Released under the MIT license
|
||||||
|
* https://github.com/lokesh/lightbox2/blob/master/LICENSE
|
||||||
|
*
|
||||||
|
* @preserve
|
||||||
|
*/
|
||||||
|
!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],b):"object"==typeof exports?module.exports=b(require("jquery")):a.lightbox=b(a.jQuery)}(this,function(a){function b(b){this.album=[],this.currentImageIndex=void 0,this.init(),this.options=a.extend({},this.constructor.defaults),this.option(b)}return b.defaults={albumLabel:"Image %1 of %2",alwaysShowNavOnTouchDevices:!1,fadeDuration:600,fitImagesInViewport:!0,imageFadeDuration:600,positionFromTop:50,resizeDuration:700,showImageNumberLabel:!0,wrapAround:!1,disableScrolling:!1,sanitizeTitle:!1},b.prototype.option=function(b){a.extend(this.options,b)},b.prototype.imageCountLabel=function(a,b){return this.options.albumLabel.replace(/%1/g,a).replace(/%2/g,b)},b.prototype.init=function(){var b=this;a(document).ready(function(){b.enable(),b.build()})},b.prototype.enable=function(){var b=this;a("body").on("click","a[rel^=lightbox], area[rel^=lightbox], a[data-lightbox], area[data-lightbox]",function(c){return b.start(a(c.currentTarget)),!1})},b.prototype.build=function(){if(!(a("#lightbox").length>0)){var b=this;a('<div id="lightboxOverlay" tabindex="-1" class="lightboxOverlay"></div><div id="lightbox" tabindex="-1" class="lightbox"><div class="lb-outerContainer"><div class="lb-container"><img class="lb-image" src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" alt=""/><div class="lb-nav"><a class="lb-prev" role="button" tabindex="0" aria-label="Previous image" href="" ></a><a class="lb-next" role="button" tabindex="0" aria-label="Next image" href="" ></a></div><div class="lb-loader"><a class="lb-cancel" role="button" tabindex="0"></a></div></div></div><div class="lb-dataContainer"><div class="lb-data"><div class="lb-details"><span class="lb-caption"></span><span class="lb-number"></span></div><div class="lb-closeContainer"><a class="lb-close" role="button" tabindex="0"></a></div></div></div></div>').appendTo(a("body")),this.$lightbox=a("#lightbox"),this.$overlay=a("#lightboxOverlay"),this.$outerContainer=this.$lightbox.find(".lb-outerContainer"),this.$container=this.$lightbox.find(".lb-container"),this.$image=this.$lightbox.find(".lb-image"),this.$nav=this.$lightbox.find(".lb-nav"),this.containerPadding={top:parseInt(this.$container.css("padding-top"),10),right:parseInt(this.$container.css("padding-right"),10),bottom:parseInt(this.$container.css("padding-bottom"),10),left:parseInt(this.$container.css("padding-left"),10)},this.imageBorderWidth={top:parseInt(this.$image.css("border-top-width"),10),right:parseInt(this.$image.css("border-right-width"),10),bottom:parseInt(this.$image.css("border-bottom-width"),10),left:parseInt(this.$image.css("border-left-width"),10)},this.$overlay.hide().on("click",function(){return b.end(),!1}),this.$lightbox.hide().on("click",function(c){"lightbox"===a(c.target).attr("id")&&b.end()}),this.$outerContainer.on("click",function(c){return"lightbox"===a(c.target).attr("id")&&b.end(),!1}),this.$lightbox.find(".lb-prev").on("click",function(){return 0===b.currentImageIndex?b.changeImage(b.album.length-1):b.changeImage(b.currentImageIndex-1),!1}),this.$lightbox.find(".lb-next").on("click",function(){return b.currentImageIndex===b.album.length-1?b.changeImage(0):b.changeImage(b.currentImageIndex+1),!1}),this.$nav.on("mousedown",function(a){3===a.which&&(b.$nav.css("pointer-events","none"),b.$lightbox.one("contextmenu",function(){setTimeout(function(){this.$nav.css("pointer-events","auto")}.bind(b),0)}))}),this.$lightbox.find(".lb-loader, .lb-close").on("click keyup",function(a){if("click"===a.type||"keyup"===a.type&&(13===a.which||32===a.which))return b.end(),!1})}},b.prototype.start=function(b){function c(a){d.album.push({alt:a.attr("data-alt"),link:a.attr("href"),title:a.attr("data-title")||a.attr("title")})}var d=this,e=a(window);e.on("resize",a.proxy(this.sizeOverlay,this)),this.sizeOverlay(),this.album=[];var f,g=0,h=b.attr("data-lightbox");if(h){f=a(b.prop("tagName")+'[data-lightbox="'+h+'"]');for(var i=0;i<f.length;i=++i)c(a(f[i])),f[i]===b[0]&&(g=i)}else if("lightbox"===b.attr("rel"))c(b);else{f=a(b.prop("tagName")+'[rel="'+b.attr("rel")+'"]');for(var j=0;j<f.length;j=++j)c(a(f[j])),f[j]===b[0]&&(g=j)}var k=e.scrollTop()+this.options.positionFromTop,l=e.scrollLeft();this.$lightbox.css({top:k+"px",left:l+"px"}).fadeIn(this.options.fadeDuration),this.options.disableScrolling&&a("body").addClass("lb-disable-scrolling"),this.changeImage(g)},b.prototype.changeImage=function(b){var c=this,d=this.album[b].link,e=d.split(".").slice(-1)[0],f=this.$lightbox.find(".lb-image");this.disableKeyboardNav(),this.$overlay.fadeIn(this.options.fadeDuration),a(".lb-loader").fadeIn("slow"),this.$lightbox.find(".lb-image, .lb-nav, .lb-prev, .lb-next, .lb-dataContainer, .lb-numbers, .lb-caption").hide(),this.$outerContainer.addClass("animating");var g=new Image;g.onload=function(){var h,i,j,k,l,m;f.attr({alt:c.album[b].alt,src:d}),a(g),f.width(g.width),f.height(g.height);var n=g.width/g.height;m=a(windLine truncated
|
||||||
|
//# sourceMappingURL=lightbox.min.map
|
||||||
@@ -36,4 +36,91 @@ class Helper {
|
|||||||
|
|
||||||
return ($dialogShowFrom <= $today && $until > $today);
|
return ($dialogShowFrom <= $today && $until > $today);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate an obfuscated email link to protect against automated spambot harvesting.
|
||||||
|
* Raw HTML contains no plain email address or mailto: link.
|
||||||
|
* Splitting into data attributes with CSS bidi-override fallback renders correctly visually,
|
||||||
|
* while JavaScript dynamically activates the live mailto link for real human visitors.
|
||||||
|
*
|
||||||
|
* @param string $email
|
||||||
|
* @param string|null $label Optional text/label before or wrapping the email
|
||||||
|
* @param array $attrs Optional HTML attributes (class, style, title, icon, etc.)
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function obfuscateEmail($email, $label = null, $attrs = []) {
|
||||||
|
if (strpos($email, '@') === false) {
|
||||||
|
return htmlspecialchars($email);
|
||||||
|
}
|
||||||
|
list($user, $domainPart) = explode('@', $email, 2);
|
||||||
|
$lastDot = strrpos($domainPart, '.');
|
||||||
|
if ($lastDot !== false) {
|
||||||
|
$domain = substr($domainPart, 0, $lastDot);
|
||||||
|
$tld = substr($domainPart, $lastDot + 1);
|
||||||
|
} else {
|
||||||
|
$domain = $domainPart;
|
||||||
|
$tld = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$reversed = strrev($email);
|
||||||
|
$baseClass = isset($attrs['class']) ? $attrs['class'] . ' mail-obf' : 'mail-obf';
|
||||||
|
$title = isset($attrs['title']) ? ' title="' . htmlspecialchars($attrs['title']) . '"' : '';
|
||||||
|
$style = isset($attrs['style']) ? ' style="' . htmlspecialchars($attrs['style']) . '"' : '';
|
||||||
|
$id = isset($attrs['id']) ? ' id="' . htmlspecialchars($attrs['id']) . '"' : '';
|
||||||
|
|
||||||
|
$content = '';
|
||||||
|
if (isset($attrs['icon']) && !empty($attrs['icon'])) {
|
||||||
|
$content .= $attrs['icon'] . ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($label !== null && $label !== '') {
|
||||||
|
$content .= htmlspecialchars($label) . ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
$content .= '<span class="mail-val c-rev">' . htmlspecialchars($reversed) . '</span>';
|
||||||
|
|
||||||
|
return sprintf(
|
||||||
|
'<a href="javascript:void(0);" class="%s" data-usr="%s" data-dom="%s" data-tld="%s"%s%s%s>%s</a>',
|
||||||
|
htmlspecialchars($baseClass),
|
||||||
|
htmlspecialchars($user),
|
||||||
|
htmlspecialchars($domain),
|
||||||
|
htmlspecialchars($tld),
|
||||||
|
$title,
|
||||||
|
$style,
|
||||||
|
$id,
|
||||||
|
$content
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obfuscate plain text email (non-link).
|
||||||
|
*/
|
||||||
|
public static function obfuscateEmailText($email, $attrs = []) {
|
||||||
|
if (strpos($email, '@') === false) {
|
||||||
|
return htmlspecialchars($email);
|
||||||
|
}
|
||||||
|
list($user, $domainPart) = explode('@', $email, 2);
|
||||||
|
$lastDot = strrpos($domainPart, '.');
|
||||||
|
if ($lastDot !== false) {
|
||||||
|
$domain = substr($domainPart, 0, $lastDot);
|
||||||
|
$tld = substr($domainPart, $lastDot + 1);
|
||||||
|
} else {
|
||||||
|
$domain = $domainPart;
|
||||||
|
$tld = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$reversed = strrev($email);
|
||||||
|
$baseClass = isset($attrs['class']) ? $attrs['class'] . ' mail-obf-text' : 'mail-obf-text';
|
||||||
|
$style = isset($attrs['style']) ? ' style="' . htmlspecialchars($attrs['style']) . '"' : '';
|
||||||
|
|
||||||
|
return sprintf(
|
||||||
|
'<span class="%s" data-usr="%s" data-dom="%s" data-tld="%s"%s><span class="mail-val c-rev">%s</span></span>',
|
||||||
|
htmlspecialchars($baseClass),
|
||||||
|
htmlspecialchars($user),
|
||||||
|
htmlspecialchars($domain),
|
||||||
|
htmlspecialchars($tld),
|
||||||
|
$style,
|
||||||
|
htmlspecialchars($reversed)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
* @copyright 2012 - 2020 Marcus Bointon
|
* @copyright 2012 - 2020 Marcus Bointon
|
||||||
* @copyright 2010 - 2012 Jim Jagielski
|
* @copyright 2010 - 2012 Jim Jagielski
|
||||||
* @copyright 2004 - 2009 Andy Prevost
|
* @copyright 2004 - 2009 Andy Prevost
|
||||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
* @license https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License
|
||||||
* @note This program is distributed in the hope that it will be useful - WITHOUT
|
* @note This program is distributed in the hope that it will be useful - WITHOUT
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|||||||
@@ -1,139 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PHPMailer - PHP email creation and transport class.
|
|
||||||
* PHP Version 5.5.
|
|
||||||
*
|
|
||||||
* @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
|
|
||||||
*
|
|
||||||
* @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
|
|
||||||
* @author Jim Jagielski (jimjag) <jimjag@gmail.com>
|
|
||||||
* @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
|
|
||||||
* @author Brent R. Matzelle (original founder)
|
|
||||||
* @copyright 2012 - 2020 Marcus Bointon
|
|
||||||
* @copyright 2010 - 2012 Jim Jagielski
|
|
||||||
* @copyright 2004 - 2009 Andy Prevost
|
|
||||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
|
||||||
* @note This program is distributed in the hope that it will be useful - WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace PHPMailer\PHPMailer;
|
|
||||||
|
|
||||||
use League\OAuth2\Client\Grant\RefreshToken;
|
|
||||||
use League\OAuth2\Client\Provider\AbstractProvider;
|
|
||||||
use League\OAuth2\Client\Token\AccessToken;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OAuth - OAuth2 authentication wrapper class.
|
|
||||||
* Uses the oauth2-client package from the League of Extraordinary Packages.
|
|
||||||
*
|
|
||||||
* @see http://oauth2-client.thephpleague.com
|
|
||||||
*
|
|
||||||
* @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
|
|
||||||
*/
|
|
||||||
class OAuth implements OAuthTokenProvider
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* An instance of the League OAuth Client Provider.
|
|
||||||
*
|
|
||||||
* @var AbstractProvider
|
|
||||||
*/
|
|
||||||
protected $provider;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current OAuth access token.
|
|
||||||
*
|
|
||||||
* @var AccessToken
|
|
||||||
*/
|
|
||||||
protected $oauthToken;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The user's email address, usually used as the login ID
|
|
||||||
* and also the from address when sending email.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $oauthUserEmail = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The client secret, generated in the app definition of the service you're connecting to.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $oauthClientSecret = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The client ID, generated in the app definition of the service you're connecting to.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $oauthClientId = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The refresh token, used to obtain new AccessTokens.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $oauthRefreshToken = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OAuth constructor.
|
|
||||||
*
|
|
||||||
* @param array $options Associative array containing
|
|
||||||
* `provider`, `userName`, `clientSecret`, `clientId` and `refreshToken` elements
|
|
||||||
*/
|
|
||||||
public function __construct($options)
|
|
||||||
{
|
|
||||||
$this->provider = $options['provider'];
|
|
||||||
$this->oauthUserEmail = $options['userName'];
|
|
||||||
$this->oauthClientSecret = $options['clientSecret'];
|
|
||||||
$this->oauthClientId = $options['clientId'];
|
|
||||||
$this->oauthRefreshToken = $options['refreshToken'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a new RefreshToken.
|
|
||||||
*
|
|
||||||
* @return RefreshToken
|
|
||||||
*/
|
|
||||||
protected function getGrant()
|
|
||||||
{
|
|
||||||
return new RefreshToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a new AccessToken.
|
|
||||||
*
|
|
||||||
* @return AccessToken
|
|
||||||
*/
|
|
||||||
protected function getToken()
|
|
||||||
{
|
|
||||||
return $this->provider->getAccessToken(
|
|
||||||
$this->getGrant(),
|
|
||||||
['refresh_token' => $this->oauthRefreshToken]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a base64-encoded OAuth token.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getOauth64()
|
|
||||||
{
|
|
||||||
//Get a new token if it's not available or has expired
|
|
||||||
if (null === $this->oauthToken || $this->oauthToken->hasExpired()) {
|
|
||||||
$this->oauthToken = $this->getToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
return base64_encode(
|
|
||||||
'user=' .
|
|
||||||
$this->oauthUserEmail .
|
|
||||||
"\001auth=Bearer " .
|
|
||||||
$this->oauthToken .
|
|
||||||
"\001\001"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PHPMailer - PHP email creation and transport class.
|
|
||||||
* PHP Version 5.5.
|
|
||||||
*
|
|
||||||
* @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
|
|
||||||
*
|
|
||||||
* @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
|
|
||||||
* @author Jim Jagielski (jimjag) <jimjag@gmail.com>
|
|
||||||
* @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
|
|
||||||
* @author Brent R. Matzelle (original founder)
|
|
||||||
* @copyright 2012 - 2020 Marcus Bointon
|
|
||||||
* @copyright 2010 - 2012 Jim Jagielski
|
|
||||||
* @copyright 2004 - 2009 Andy Prevost
|
|
||||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
|
||||||
* @note This program is distributed in the hope that it will be useful - WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace PHPMailer\PHPMailer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OAuthTokenProvider - OAuth2 token provider interface.
|
|
||||||
* Provides base64 encoded OAuth2 auth strings for SMTP authentication.
|
|
||||||
*
|
|
||||||
* @see OAuth
|
|
||||||
* @see SMTP::authenticate()
|
|
||||||
*
|
|
||||||
* @author Peter Scopes (pdscopes)
|
|
||||||
* @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
|
|
||||||
*/
|
|
||||||
interface OAuthTokenProvider
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Generate a base64-encoded OAuth token ensuring that the access token has not expired.
|
|
||||||
* The string to be base 64 encoded should be in the form:
|
|
||||||
* "user=<user_email_address>\001auth=Bearer <access_token>\001\001"
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getOauth64();
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
* @copyright 2012 - 2020 Marcus Bointon
|
* @copyright 2012 - 2020 Marcus Bointon
|
||||||
* @copyright 2010 - 2012 Jim Jagielski
|
* @copyright 2010 - 2012 Jim Jagielski
|
||||||
* @copyright 2004 - 2009 Andy Prevost
|
* @copyright 2004 - 2009 Andy Prevost
|
||||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
* @license https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License
|
||||||
* @note This program is distributed in the hope that it will be useful - WITHOUT
|
* @note This program is distributed in the hope that it will be useful - WITHOUT
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
@@ -152,8 +152,7 @@ class PHPMailer
|
|||||||
* Only supported in simple alt or alt_inline message types
|
* Only supported in simple alt or alt_inline message types
|
||||||
* To generate iCal event structures, use classes like EasyPeasyICS or iCalcreator.
|
* To generate iCal event structures, use classes like EasyPeasyICS or iCalcreator.
|
||||||
*
|
*
|
||||||
* @see http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
|
* @see https://kigkonsult.se/iCalcreator/
|
||||||
* @see http://kigkonsult.se/iCalcreator/
|
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
@@ -254,7 +253,7 @@ class PHPMailer
|
|||||||
* You can set your own, but it must be in the format "<id@domain>",
|
* You can set your own, but it must be in the format "<id@domain>",
|
||||||
* as defined in RFC5322 section 3.6.4 or it will be ignored.
|
* as defined in RFC5322 section 3.6.4 or it will be ignored.
|
||||||
*
|
*
|
||||||
* @see https://tools.ietf.org/html/rfc5322#section-3.6.4
|
* @see https://www.rfc-editor.org/rfc/rfc5322#section-3.6.4
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
@@ -357,6 +356,13 @@ class PHPMailer
|
|||||||
*/
|
*/
|
||||||
public $AuthType = '';
|
public $AuthType = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SMTP SMTPXClient command attributes
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $SMTPXClient = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of the PHPMailer OAuthTokenProvider interface.
|
* An implementation of the PHPMailer OAuthTokenProvider interface.
|
||||||
*
|
*
|
||||||
@@ -381,7 +387,7 @@ class PHPMailer
|
|||||||
* 'DELAY' will notify you if there is an unusual delay in delivery, but the actual
|
* 'DELAY' will notify you if there is an unusual delay in delivery, but the actual
|
||||||
* delivery's outcome (success or failure) is not yet decided.
|
* delivery's outcome (success or failure) is not yet decided.
|
||||||
*
|
*
|
||||||
* @see https://tools.ietf.org/html/rfc3461 See section 4.1 for more information about NOTIFY
|
* @see https://www.rfc-editor.org/rfc/rfc3461.html#section-4.1 for more information about NOTIFY
|
||||||
*/
|
*/
|
||||||
public $dsn = '';
|
public $dsn = '';
|
||||||
|
|
||||||
@@ -461,7 +467,7 @@ class PHPMailer
|
|||||||
* Only applicable when sending via SMTP.
|
* Only applicable when sending via SMTP.
|
||||||
*
|
*
|
||||||
* @see https://en.wikipedia.org/wiki/Variable_envelope_return_path
|
* @see https://en.wikipedia.org/wiki/Variable_envelope_return_path
|
||||||
* @see http://www.postfix.org/VERP_README.html Postfix VERP info
|
* @see https://www.postfix.org/VERP_README.html Postfix VERP info
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
@@ -544,10 +550,10 @@ class PHPMailer
|
|||||||
* The function that handles the result of the send email action.
|
* The function that handles the result of the send email action.
|
||||||
* It is called out by send() for each email sent.
|
* It is called out by send() for each email sent.
|
||||||
*
|
*
|
||||||
* Value can be any php callable: http://www.php.net/is_callable
|
* Value can be any php callable: https://www.php.net/is_callable
|
||||||
*
|
*
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* bool $result result of the send action
|
* bool $result result of the send action
|
||||||
* array $to email addresses of the recipients
|
* array $to email addresses of the recipients
|
||||||
* array $cc cc email addresses
|
* array $cc cc email addresses
|
||||||
* array $bcc bcc email addresses
|
* array $bcc bcc email addresses
|
||||||
@@ -574,6 +580,10 @@ class PHPMailer
|
|||||||
* May be a callable to inject your own validator, but there are several built-in validators.
|
* May be a callable to inject your own validator, but there are several built-in validators.
|
||||||
* The default validator uses PHP's FILTER_VALIDATE_EMAIL filter_var option.
|
* The default validator uses PHP's FILTER_VALIDATE_EMAIL filter_var option.
|
||||||
*
|
*
|
||||||
|
* If CharSet is UTF8, the validator is left at the default value,
|
||||||
|
* and you send to addresses that use non-ASCII local parts, then
|
||||||
|
* PHPMailer automatically changes to the 'eai' validator.
|
||||||
|
*
|
||||||
* @see PHPMailer::validateAddress()
|
* @see PHPMailer::validateAddress()
|
||||||
*
|
*
|
||||||
* @var string|callable
|
* @var string|callable
|
||||||
@@ -653,6 +663,14 @@ class PHPMailer
|
|||||||
*/
|
*/
|
||||||
protected $ReplyToQueue = [];
|
protected $ReplyToQueue = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the need for SMTPUTF8 has been detected. Set by
|
||||||
|
* preSend() if necessary.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $UseSMTPUTF8 = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The array of attachments.
|
* The array of attachments.
|
||||||
*
|
*
|
||||||
@@ -750,7 +768,7 @@ class PHPMailer
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
const VERSION = '6.7.1';
|
const VERSION = '6.12.0';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Error severity: message only, continue processing.
|
* Error severity: message only, continue processing.
|
||||||
@@ -795,7 +813,7 @@ class PHPMailer
|
|||||||
* The maximum line length supported by mail().
|
* The maximum line length supported by mail().
|
||||||
*
|
*
|
||||||
* Background: mail() will sometimes corrupt messages
|
* Background: mail() will sometimes corrupt messages
|
||||||
* with headers headers longer than 65 chars, see #818.
|
* with headers longer than 65 chars, see #818.
|
||||||
*
|
*
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
@@ -896,7 +914,7 @@ class PHPMailer
|
|||||||
}
|
}
|
||||||
//Is this a PSR-3 logger?
|
//Is this a PSR-3 logger?
|
||||||
if ($this->Debugoutput instanceof \Psr\Log\LoggerInterface) {
|
if ($this->Debugoutput instanceof \Psr\Log\LoggerInterface) {
|
||||||
$this->Debugoutput->debug($str);
|
$this->Debugoutput->debug(rtrim($str, "\r\n"));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1065,7 +1083,7 @@ class PHPMailer
|
|||||||
* be modified after calling this function), addition of such addresses is delayed until send().
|
* be modified after calling this function), addition of such addresses is delayed until send().
|
||||||
* Addresses that have been added already return false, but do not throw exceptions.
|
* Addresses that have been added already return false, but do not throw exceptions.
|
||||||
*
|
*
|
||||||
* @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
|
* @param string $kind One of 'to', 'cc', 'bcc', or 'Reply-To'
|
||||||
* @param string $address The email address
|
* @param string $address The email address
|
||||||
* @param string $name An optional username associated with the address
|
* @param string $name An optional username associated with the address
|
||||||
*
|
*
|
||||||
@@ -1104,19 +1122,22 @@ class PHPMailer
|
|||||||
$params = [$kind, $address, $name];
|
$params = [$kind, $address, $name];
|
||||||
//Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
|
//Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
|
||||||
//Domain is assumed to be whatever is after the last @ symbol in the address
|
//Domain is assumed to be whatever is after the last @ symbol in the address
|
||||||
if (static::idnSupported() && $this->has8bitChars(substr($address, ++$pos))) {
|
if ($this->has8bitChars(substr($address, ++$pos))) {
|
||||||
if ('Reply-To' !== $kind) {
|
if (static::idnSupported()) {
|
||||||
if (!array_key_exists($address, $this->RecipientsQueue)) {
|
if ('Reply-To' !== $kind) {
|
||||||
$this->RecipientsQueue[$address] = $params;
|
if (!array_key_exists($address, $this->RecipientsQueue)) {
|
||||||
|
$this->RecipientsQueue[$address] = $params;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} elseif (!array_key_exists($address, $this->ReplyToQueue)) {
|
||||||
|
$this->ReplyToQueue[$address] = $params;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} elseif (!array_key_exists($address, $this->ReplyToQueue)) {
|
|
||||||
$this->ReplyToQueue[$address] = $params;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
//We have an 8-bit domain, but we are missing the necessary extensions to support it
|
||||||
|
//Or we are already sending to this address
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1154,6 +1175,15 @@ class PHPMailer
|
|||||||
*/
|
*/
|
||||||
protected function addAnAddress($kind, $address, $name = '')
|
protected function addAnAddress($kind, $address, $name = '')
|
||||||
{
|
{
|
||||||
|
if (
|
||||||
|
self::$validator === 'php' &&
|
||||||
|
((bool) preg_match('/[\x80-\xFF]/', $address))
|
||||||
|
) {
|
||||||
|
//The caller has not altered the validator and is sending to an address
|
||||||
|
//with UTF-8, so assume that they want UTF-8 support instead of failing
|
||||||
|
$this->CharSet = self::CHARSET_UTF8;
|
||||||
|
self::$validator = 'eai';
|
||||||
|
}
|
||||||
if (!in_array($kind, ['to', 'cc', 'bcc', 'Reply-To'])) {
|
if (!in_array($kind, ['to', 'cc', 'bcc', 'Reply-To'])) {
|
||||||
$error_message = sprintf(
|
$error_message = sprintf(
|
||||||
'%s: %s',
|
'%s: %s',
|
||||||
@@ -1205,7 +1235,7 @@ class PHPMailer
|
|||||||
* Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available.
|
* Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available.
|
||||||
* Note that quotes in the name part are removed.
|
* Note that quotes in the name part are removed.
|
||||||
*
|
*
|
||||||
* @see http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
|
* @see https://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
|
||||||
*
|
*
|
||||||
* @param string $addrstr The address list string
|
* @param string $addrstr The address list string
|
||||||
* @param bool $useimap Whether to use the IMAP extension to parse the list
|
* @param bool $useimap Whether to use the IMAP extension to parse the list
|
||||||
@@ -1356,6 +1386,7 @@ class PHPMailer
|
|||||||
* * `pcre` Use old PCRE implementation;
|
* * `pcre` Use old PCRE implementation;
|
||||||
* * `php` Use PHP built-in FILTER_VALIDATE_EMAIL;
|
* * `php` Use PHP built-in FILTER_VALIDATE_EMAIL;
|
||||||
* * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
|
* * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
|
||||||
|
* * `eai` Use a pattern similar to the HTML5 spec for 'email' and to firefox, extended to support EAI (RFC6530).
|
||||||
* * `noregex` Don't use a regex: super fast, really dumb.
|
* * `noregex` Don't use a regex: super fast, really dumb.
|
||||||
* Alternatively you may pass in a callable to inject your own validator, for example:
|
* Alternatively you may pass in a callable to inject your own validator, for example:
|
||||||
*
|
*
|
||||||
@@ -1400,7 +1431,6 @@ class PHPMailer
|
|||||||
* * IPv6 literals: 'first.last@[IPv6:a1::]'
|
* * IPv6 literals: 'first.last@[IPv6:a1::]'
|
||||||
* Not all of these will necessarily work for sending!
|
* Not all of these will necessarily work for sending!
|
||||||
*
|
*
|
||||||
* @see http://squiloople.com/2009/12/20/email-address-validation/
|
|
||||||
* @copyright 2009-2010 Michael Rushton
|
* @copyright 2009-2010 Michael Rushton
|
||||||
* Feel free to use and redistribute this code. But please keep this copyright notice.
|
* Feel free to use and redistribute this code. But please keep this copyright notice.
|
||||||
*/
|
*/
|
||||||
@@ -1427,6 +1457,24 @@ class PHPMailer
|
|||||||
'[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
|
'[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
|
||||||
$address
|
$address
|
||||||
);
|
);
|
||||||
|
case 'eai':
|
||||||
|
/*
|
||||||
|
* This is the pattern used in the HTML5 spec for validation of 'email' type
|
||||||
|
* form input elements (as above), modified to accept Unicode email addresses.
|
||||||
|
* This is also more lenient than Firefox' html5 spec, in order to make the regex faster.
|
||||||
|
* 'eai' is an acronym for Email Address Internationalization.
|
||||||
|
* This validator is selected automatically if you attempt to use recipient addresses
|
||||||
|
* that contain Unicode characters in the local part.
|
||||||
|
*
|
||||||
|
* @see https://html.spec.whatwg.org/#e-mail-state-(type=email)
|
||||||
|
* @see https://en.wikipedia.org/wiki/International_email
|
||||||
|
*/
|
||||||
|
return (bool) preg_match(
|
||||||
|
'/^[-\p{L}\p{N}\p{M}.!#$%&\'*+\/=?^_`{|}~]+@[\p{L}\p{N}\p{M}](?:[\p{L}\p{N}\p{M}-]{0,61}' .
|
||||||
|
'[\p{L}\p{N}\p{M}])?(?:\.[\p{L}\p{N}\p{M}]' .
|
||||||
|
'(?:[-\p{L}\p{N}\p{M}]{0,61}[\p{L}\p{N}\p{M}])?)*$/usD',
|
||||||
|
$address
|
||||||
|
);
|
||||||
case 'php':
|
case 'php':
|
||||||
default:
|
default:
|
||||||
return filter_var($address, FILTER_VALIDATE_EMAIL) !== false;
|
return filter_var($address, FILTER_VALIDATE_EMAIL) !== false;
|
||||||
@@ -1560,9 +1608,26 @@ class PHPMailer
|
|||||||
$this->error_count = 0; //Reset errors
|
$this->error_count = 0; //Reset errors
|
||||||
$this->mailHeader = '';
|
$this->mailHeader = '';
|
||||||
|
|
||||||
|
//The code below tries to support full use of Unicode,
|
||||||
|
//while remaining compatible with legacy SMTP servers to
|
||||||
|
//the greatest degree possible: If the message uses
|
||||||
|
//Unicode in the local parts of any addresses, it is sent
|
||||||
|
//using SMTPUTF8. If not, it it sent using
|
||||||
|
//punycode-encoded domains and plain SMTP.
|
||||||
|
if (
|
||||||
|
static::CHARSET_UTF8 === strtolower($this->CharSet) &&
|
||||||
|
($this->anyAddressHasUnicodeLocalPart($this->RecipientsQueue) ||
|
||||||
|
$this->anyAddressHasUnicodeLocalPart(array_keys($this->all_recipients)) ||
|
||||||
|
$this->anyAddressHasUnicodeLocalPart($this->ReplyToQueue) ||
|
||||||
|
$this->addressHasUnicodeLocalPart($this->From))
|
||||||
|
) {
|
||||||
|
$this->UseSMTPUTF8 = true;
|
||||||
|
}
|
||||||
//Dequeue recipient and Reply-To addresses with IDN
|
//Dequeue recipient and Reply-To addresses with IDN
|
||||||
foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) {
|
foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) {
|
||||||
$params[1] = $this->punyencodeAddress($params[1]);
|
if (!$this->UseSMTPUTF8) {
|
||||||
|
$params[1] = $this->punyencodeAddress($params[1]);
|
||||||
|
}
|
||||||
call_user_func_array([$this, 'addAnAddress'], $params);
|
call_user_func_array([$this, 'addAnAddress'], $params);
|
||||||
}
|
}
|
||||||
if (count($this->to) + count($this->cc) + count($this->bcc) < 1) {
|
if (count($this->to) + count($this->cc) + count($this->bcc) < 1) {
|
||||||
@@ -1571,6 +1636,10 @@ class PHPMailer
|
|||||||
|
|
||||||
//Validate From, Sender, and ConfirmReadingTo addresses
|
//Validate From, Sender, and ConfirmReadingTo addresses
|
||||||
foreach (['From', 'Sender', 'ConfirmReadingTo'] as $address_kind) {
|
foreach (['From', 'Sender', 'ConfirmReadingTo'] as $address_kind) {
|
||||||
|
if ($this->{$address_kind} === null) {
|
||||||
|
$this->{$address_kind} = '';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
$this->{$address_kind} = trim($this->{$address_kind});
|
$this->{$address_kind} = trim($this->{$address_kind});
|
||||||
if (empty($this->{$address_kind})) {
|
if (empty($this->{$address_kind})) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1723,9 +1792,8 @@ class PHPMailer
|
|||||||
//This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
|
//This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
|
||||||
//A space after `-f` is optional, but there is a long history of its presence
|
//A space after `-f` is optional, but there is a long history of its presence
|
||||||
//causing problems, so we don't use one
|
//causing problems, so we don't use one
|
||||||
//Exim docs: http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html
|
//Exim docs: https://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html
|
||||||
//Sendmail docs: http://www.sendmail.org/~ca/email/man/sendmail.html
|
//Sendmail docs: https://www.sendmail.org/~ca/email/man/sendmail.html
|
||||||
//Qmail docs: http://www.qmail.org/man/man8/qmail-inject.html
|
|
||||||
//Example problem: https://www.drupal.org/node/1057954
|
//Example problem: https://www.drupal.org/node/1057954
|
||||||
|
|
||||||
//PHP 5.6 workaround
|
//PHP 5.6 workaround
|
||||||
@@ -1863,7 +1931,7 @@ class PHPMailer
|
|||||||
*/
|
*/
|
||||||
protected static function isPermittedPath($path)
|
protected static function isPermittedPath($path)
|
||||||
{
|
{
|
||||||
//Matches scheme definition from https://tools.ietf.org/html/rfc3986#section-3.1
|
//Matches scheme definition from https://www.rfc-editor.org/rfc/rfc3986#section-3.1
|
||||||
return !preg_match('#^[a-z][a-z\d+.-]*://#i', $path);
|
return !preg_match('#^[a-z][a-z\d+.-]*://#i', $path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1890,7 +1958,7 @@ class PHPMailer
|
|||||||
/**
|
/**
|
||||||
* Send mail using the PHP mail() function.
|
* Send mail using the PHP mail() function.
|
||||||
*
|
*
|
||||||
* @see http://www.php.net/manual/en/book.mail.php
|
* @see https://www.php.net/manual/en/book.mail.php
|
||||||
*
|
*
|
||||||
* @param string $header The message headers
|
* @param string $header The message headers
|
||||||
* @param string $body The message body
|
* @param string $body The message body
|
||||||
@@ -1920,9 +1988,8 @@ class PHPMailer
|
|||||||
//This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
|
//This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
|
||||||
//A space after `-f` is optional, but there is a long history of its presence
|
//A space after `-f` is optional, but there is a long history of its presence
|
||||||
//causing problems, so we don't use one
|
//causing problems, so we don't use one
|
||||||
//Exim docs: http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html
|
//Exim docs: https://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html
|
||||||
//Sendmail docs: http://www.sendmail.org/~ca/email/man/sendmail.html
|
//Sendmail docs: https://www.sendmail.org/~ca/email/man/sendmail.html
|
||||||
//Qmail docs: http://www.qmail.org/man/man8/qmail-inject.html
|
|
||||||
//Example problem: https://www.drupal.org/node/1057954
|
//Example problem: https://www.drupal.org/node/1057954
|
||||||
//CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
|
//CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
|
||||||
|
|
||||||
@@ -1997,6 +2064,38 @@ class PHPMailer
|
|||||||
return $this->smtp;
|
return $this->smtp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide SMTP XCLIENT attributes
|
||||||
|
*
|
||||||
|
* @param string $name Attribute name
|
||||||
|
* @param ?string $value Attribute value
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function setSMTPXclientAttribute($name, $value)
|
||||||
|
{
|
||||||
|
if (!in_array($name, SMTP::$xclient_allowed_attributes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (isset($this->SMTPXClient[$name]) && $value === null) {
|
||||||
|
unset($this->SMTPXClient[$name]);
|
||||||
|
} elseif ($value !== null) {
|
||||||
|
$this->SMTPXClient[$name] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get SMTP XCLIENT attributes
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getSMTPXclientAttributes()
|
||||||
|
{
|
||||||
|
return $this->SMTPXClient;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send mail via SMTP.
|
* Send mail via SMTP.
|
||||||
* Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
|
* Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
|
||||||
@@ -2019,12 +2118,20 @@ class PHPMailer
|
|||||||
if (!$this->smtpConnect($this->SMTPOptions)) {
|
if (!$this->smtpConnect($this->SMTPOptions)) {
|
||||||
throw new Exception($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
|
throw new Exception($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
|
||||||
}
|
}
|
||||||
|
//If we have recipient addresses that need Unicode support,
|
||||||
|
//but the server doesn't support it, stop here
|
||||||
|
if ($this->UseSMTPUTF8 && !$this->smtp->getServerExt('SMTPUTF8')) {
|
||||||
|
throw new Exception($this->lang('no_smtputf8'), self::STOP_CRITICAL);
|
||||||
|
}
|
||||||
//Sender already validated in preSend()
|
//Sender already validated in preSend()
|
||||||
if ('' === $this->Sender) {
|
if ('' === $this->Sender) {
|
||||||
$smtp_from = $this->From;
|
$smtp_from = $this->From;
|
||||||
} else {
|
} else {
|
||||||
$smtp_from = $this->Sender;
|
$smtp_from = $this->Sender;
|
||||||
}
|
}
|
||||||
|
if (count($this->SMTPXClient)) {
|
||||||
|
$this->smtp->xclient($this->SMTPXClient);
|
||||||
|
}
|
||||||
if (!$this->smtp->mail($smtp_from)) {
|
if (!$this->smtp->mail($smtp_from)) {
|
||||||
$this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
|
$this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
|
||||||
throw new Exception($this->ErrorInfo, self::STOP_CRITICAL);
|
throw new Exception($this->ErrorInfo, self::STOP_CRITICAL);
|
||||||
@@ -2117,6 +2224,7 @@ class PHPMailer
|
|||||||
$this->smtp->setDebugLevel($this->SMTPDebug);
|
$this->smtp->setDebugLevel($this->SMTPDebug);
|
||||||
$this->smtp->setDebugOutput($this->Debugoutput);
|
$this->smtp->setDebugOutput($this->Debugoutput);
|
||||||
$this->smtp->setVerp($this->do_verp);
|
$this->smtp->setVerp($this->do_verp);
|
||||||
|
$this->smtp->setSMTPUTF8($this->UseSMTPUTF8);
|
||||||
if ($this->Host === null) {
|
if ($this->Host === null) {
|
||||||
$this->Host = 'localhost';
|
$this->Host = 'localhost';
|
||||||
}
|
}
|
||||||
@@ -2187,10 +2295,17 @@ class PHPMailer
|
|||||||
$this->smtp->hello($hello);
|
$this->smtp->hello($hello);
|
||||||
//Automatically enable TLS encryption if:
|
//Automatically enable TLS encryption if:
|
||||||
//* it's not disabled
|
//* it's not disabled
|
||||||
|
//* we are not connecting to localhost
|
||||||
//* we have openssl extension
|
//* we have openssl extension
|
||||||
//* we are not already using SSL
|
//* we are not already using SSL
|
||||||
//* the server offers STARTTLS
|
//* the server offers STARTTLS
|
||||||
if ($this->SMTPAutoTLS && $sslext && 'ssl' !== $secure && $this->smtp->getServerExt('STARTTLS')) {
|
if (
|
||||||
|
$this->SMTPAutoTLS &&
|
||||||
|
$this->Host !== 'localhost' &&
|
||||||
|
$sslext &&
|
||||||
|
$secure !== 'ssl' &&
|
||||||
|
$this->smtp->getServerExt('STARTTLS')
|
||||||
|
) {
|
||||||
$tls = true;
|
$tls = true;
|
||||||
}
|
}
|
||||||
if ($tls) {
|
if ($tls) {
|
||||||
@@ -2307,6 +2422,7 @@ class PHPMailer
|
|||||||
'smtp_detail' => 'Detail: ',
|
'smtp_detail' => 'Detail: ',
|
||||||
'smtp_error' => 'SMTP server error: ',
|
'smtp_error' => 'SMTP server error: ',
|
||||||
'variable_set' => 'Cannot set or reset variable: ',
|
'variable_set' => 'Cannot set or reset variable: ',
|
||||||
|
'no_smtputf8' => 'Server does not support SMTPUTF8 needed to send to Unicode addresses',
|
||||||
];
|
];
|
||||||
if (empty($lang_path)) {
|
if (empty($lang_path)) {
|
||||||
//Calculate an absolute path so it can work if CWD is not here
|
//Calculate an absolute path so it can work if CWD is not here
|
||||||
@@ -2656,7 +2772,7 @@ class PHPMailer
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4
|
//Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4
|
||||||
//https://tools.ietf.org/html/rfc5322#section-3.6.4
|
//https://www.rfc-editor.org/rfc/rfc5322#section-3.6.4
|
||||||
if (
|
if (
|
||||||
'' !== $this->MessageID &&
|
'' !== $this->MessageID &&
|
||||||
preg_match(
|
preg_match(
|
||||||
@@ -2821,7 +2937,9 @@ class PHPMailer
|
|||||||
$bodyEncoding = $this->Encoding;
|
$bodyEncoding = $this->Encoding;
|
||||||
$bodyCharSet = $this->CharSet;
|
$bodyCharSet = $this->CharSet;
|
||||||
//Can we do a 7-bit downgrade?
|
//Can we do a 7-bit downgrade?
|
||||||
if (static::ENCODING_8BIT === $bodyEncoding && !$this->has8bitChars($this->Body)) {
|
if ($this->UseSMTPUTF8) {
|
||||||
|
$bodyEncoding = static::ENCODING_8BIT;
|
||||||
|
} elseif (static::ENCODING_8BIT === $bodyEncoding && !$this->has8bitChars($this->Body)) {
|
||||||
$bodyEncoding = static::ENCODING_7BIT;
|
$bodyEncoding = static::ENCODING_7BIT;
|
||||||
//All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
|
//All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
|
||||||
$bodyCharSet = static::CHARSET_ASCII;
|
$bodyCharSet = static::CHARSET_ASCII;
|
||||||
@@ -3458,7 +3576,8 @@ class PHPMailer
|
|||||||
/**
|
/**
|
||||||
* Encode a header value (not including its label) optimally.
|
* Encode a header value (not including its label) optimally.
|
||||||
* Picks shortest of Q, B, or none. Result includes folding if needed.
|
* Picks shortest of Q, B, or none. Result includes folding if needed.
|
||||||
* See RFC822 definitions for phrase, comment and text positions.
|
* See RFC822 definitions for phrase, comment and text positions,
|
||||||
|
* and RFC2047 for inline encodings.
|
||||||
*
|
*
|
||||||
* @param string $str The header value to encode
|
* @param string $str The header value to encode
|
||||||
* @param string $position What context the string will be used in
|
* @param string $position What context the string will be used in
|
||||||
@@ -3467,6 +3586,11 @@ class PHPMailer
|
|||||||
*/
|
*/
|
||||||
public function encodeHeader($str, $position = 'text')
|
public function encodeHeader($str, $position = 'text')
|
||||||
{
|
{
|
||||||
|
$position = strtolower($position);
|
||||||
|
if ($this->UseSMTPUTF8 && !("comment" === $position)) {
|
||||||
|
return trim(static::normalizeBreaks($str));
|
||||||
|
}
|
||||||
|
|
||||||
$matchcount = 0;
|
$matchcount = 0;
|
||||||
switch (strtolower($position)) {
|
switch (strtolower($position)) {
|
||||||
case 'phrase':
|
case 'phrase':
|
||||||
@@ -3581,7 +3705,7 @@ class PHPMailer
|
|||||||
* without breaking lines within a character.
|
* without breaking lines within a character.
|
||||||
* Adapted from a function by paravoid.
|
* Adapted from a function by paravoid.
|
||||||
*
|
*
|
||||||
* @see http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
|
* @see https://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
|
||||||
*
|
*
|
||||||
* @param string $str multi-byte text to wrap encode
|
* @param string $str multi-byte text to wrap encode
|
||||||
* @param string $linebreak string to use as linefeed/end-of-line
|
* @param string $linebreak string to use as linefeed/end-of-line
|
||||||
@@ -3637,7 +3761,7 @@ class PHPMailer
|
|||||||
/**
|
/**
|
||||||
* Encode a string using Q encoding.
|
* Encode a string using Q encoding.
|
||||||
*
|
*
|
||||||
* @see http://tools.ietf.org/html/rfc2047#section-4.2
|
* @see https://www.rfc-editor.org/rfc/rfc2047#section-4.2
|
||||||
*
|
*
|
||||||
* @param string $str the text to encode
|
* @param string $str the text to encode
|
||||||
* @param string $position Where the text is going to be used, see the RFC for what that means
|
* @param string $position Where the text is going to be used, see the RFC for what that means
|
||||||
@@ -4047,6 +4171,79 @@ class PHPMailer
|
|||||||
$this->CustomHeader = [];
|
$this->CustomHeader = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear a specific custom header by name or name and value.
|
||||||
|
* $name value can be overloaded to contain
|
||||||
|
* both header name and value (name:value).
|
||||||
|
*
|
||||||
|
* @param string $name Custom header name
|
||||||
|
* @param string|null $value Header value
|
||||||
|
*
|
||||||
|
* @return bool True if a header was replaced successfully
|
||||||
|
*/
|
||||||
|
public function clearCustomHeader($name, $value = null)
|
||||||
|
{
|
||||||
|
if (null === $value && strpos($name, ':') !== false) {
|
||||||
|
//Value passed in as name:value
|
||||||
|
list($name, $value) = explode(':', $name, 2);
|
||||||
|
}
|
||||||
|
$name = trim($name);
|
||||||
|
$value = (null === $value) ? null : trim($value);
|
||||||
|
|
||||||
|
foreach ($this->CustomHeader as $k => $pair) {
|
||||||
|
if ($pair[0] == $name) {
|
||||||
|
// We remove the header if the value is not provided or it matches.
|
||||||
|
if (null === $value || $pair[1] == $value) {
|
||||||
|
unset($this->CustomHeader[$k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace a custom header.
|
||||||
|
* $name value can be overloaded to contain
|
||||||
|
* both header name and value (name:value).
|
||||||
|
*
|
||||||
|
* @param string $name Custom header name
|
||||||
|
* @param string|null $value Header value
|
||||||
|
*
|
||||||
|
* @return bool True if a header was replaced successfully
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function replaceCustomHeader($name, $value = null)
|
||||||
|
{
|
||||||
|
if (null === $value && strpos($name, ':') !== false) {
|
||||||
|
//Value passed in as name:value
|
||||||
|
list($name, $value) = explode(':', $name, 2);
|
||||||
|
}
|
||||||
|
$name = trim($name);
|
||||||
|
$value = (null === $value) ? '' : trim($value);
|
||||||
|
|
||||||
|
$replaced = false;
|
||||||
|
foreach ($this->CustomHeader as $k => $pair) {
|
||||||
|
if ($pair[0] == $name) {
|
||||||
|
if ($replaced) {
|
||||||
|
unset($this->CustomHeader[$k]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (strpbrk($name . $value, "\r\n") !== false) {
|
||||||
|
if ($this->exceptions) {
|
||||||
|
throw new Exception($this->lang('invalid_header'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$this->CustomHeader[$k] = [$name, $value];
|
||||||
|
$replaced = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an error message to the error container.
|
* Add an error message to the error container.
|
||||||
*
|
*
|
||||||
@@ -4058,7 +4255,7 @@ class PHPMailer
|
|||||||
if ('smtp' === $this->Mailer && null !== $this->smtp) {
|
if ('smtp' === $this->Mailer && null !== $this->smtp) {
|
||||||
$lasterror = $this->smtp->getError();
|
$lasterror = $this->smtp->getError();
|
||||||
if (!empty($lasterror['error'])) {
|
if (!empty($lasterror['error'])) {
|
||||||
$msg .= $this->lang('smtp_error') . $lasterror['error'];
|
$msg .= ' ' . $this->lang('smtp_error') . $lasterror['error'];
|
||||||
if (!empty($lasterror['detail'])) {
|
if (!empty($lasterror['detail'])) {
|
||||||
$msg .= ' ' . $this->lang('smtp_detail') . $lasterror['detail'];
|
$msg .= ' ' . $this->lang('smtp_detail') . $lasterror['detail'];
|
||||||
}
|
}
|
||||||
@@ -4102,7 +4299,7 @@ class PHPMailer
|
|||||||
$result = $_SERVER['SERVER_NAME'];
|
$result = $_SERVER['SERVER_NAME'];
|
||||||
} elseif (function_exists('gethostname') && gethostname() !== false) {
|
} elseif (function_exists('gethostname') && gethostname() !== false) {
|
||||||
$result = gethostname();
|
$result = gethostname();
|
||||||
} elseif (php_uname('n') !== false) {
|
} elseif (php_uname('n') !== '') {
|
||||||
$result = php_uname('n');
|
$result = php_uname('n');
|
||||||
}
|
}
|
||||||
if (!static::isValidHost($result)) {
|
if (!static::isValidHost($result)) {
|
||||||
@@ -4127,7 +4324,7 @@ class PHPMailer
|
|||||||
empty($host)
|
empty($host)
|
||||||
|| !is_string($host)
|
|| !is_string($host)
|
||||||
|| strlen($host) > 256
|
|| strlen($host) > 256
|
||||||
|| !preg_match('/^([a-zA-Z\d.-]*|\[[a-fA-F\d:]+\])$/', $host)
|
|| !preg_match('/^([a-z\d.-]*|\[[a-f\d:]+\])$/i', $host)
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -4141,10 +4338,49 @@ class PHPMailer
|
|||||||
//Is it a valid IPv4 address?
|
//Is it a valid IPv4 address?
|
||||||
return filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false;
|
return filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false;
|
||||||
}
|
}
|
||||||
//Is it a syntactically valid hostname (when embeded in a URL)?
|
//Is it a syntactically valid hostname (when embedded in a URL)?
|
||||||
return filter_var('http://' . $host, FILTER_VALIDATE_URL) !== false;
|
return filter_var('https://' . $host, FILTER_VALIDATE_URL) !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the supplied address uses Unicode in the local part.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function addressHasUnicodeLocalPart($address)
|
||||||
|
{
|
||||||
|
return (bool) preg_match('/[\x80-\xFF].*@/', $address);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether any of the supplied addresses use Unicode in the local part.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function anyAddressHasUnicodeLocalPart($addresses)
|
||||||
|
{
|
||||||
|
foreach ($addresses as $address) {
|
||||||
|
if (is_array($address)) {
|
||||||
|
$address = $address[0];
|
||||||
|
}
|
||||||
|
if ($this->addressHasUnicodeLocalPart($address)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the message requires SMTPUTF8 based on what's known so far.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function needsSMTPUTF8()
|
||||||
|
{
|
||||||
|
return $this->UseSMTPUTF8;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an error message in the current language.
|
* Get an error message in the current language.
|
||||||
*
|
*
|
||||||
@@ -4553,7 +4789,7 @@ class PHPMailer
|
|||||||
* Multi-byte-safe pathinfo replacement.
|
* Multi-byte-safe pathinfo replacement.
|
||||||
* Drop-in replacement for pathinfo(), but multibyte- and cross-platform-safe.
|
* Drop-in replacement for pathinfo(), but multibyte- and cross-platform-safe.
|
||||||
*
|
*
|
||||||
* @see http://www.php.net/manual/en/function.pathinfo.php#107461
|
* @see https://www.php.net/manual/en/function.pathinfo.php#107461
|
||||||
*
|
*
|
||||||
* @param string $path A filename or path, does not need to exist as a file
|
* @param string $path A filename or path, does not need to exist as a file
|
||||||
* @param int|string $options Either a PATHINFO_* constant,
|
* @param int|string $options Either a PATHINFO_* constant,
|
||||||
@@ -4788,7 +5024,7 @@ class PHPMailer
|
|||||||
* Uses the 'relaxed' algorithm from RFC6376 section 3.4.2.
|
* Uses the 'relaxed' algorithm from RFC6376 section 3.4.2.
|
||||||
* Canonicalized headers should *always* use CRLF, regardless of mailer setting.
|
* Canonicalized headers should *always* use CRLF, regardless of mailer setting.
|
||||||
*
|
*
|
||||||
* @see https://tools.ietf.org/html/rfc6376#section-3.4.2
|
* @see https://www.rfc-editor.org/rfc/rfc6376#section-3.4.2
|
||||||
*
|
*
|
||||||
* @param string $signHeader Header
|
* @param string $signHeader Header
|
||||||
*
|
*
|
||||||
@@ -4800,7 +5036,7 @@ class PHPMailer
|
|||||||
$signHeader = static::normalizeBreaks($signHeader, self::CRLF);
|
$signHeader = static::normalizeBreaks($signHeader, self::CRLF);
|
||||||
//Unfold header lines
|
//Unfold header lines
|
||||||
//Note PCRE \s is too broad a definition of whitespace; RFC5322 defines it as `[ \t]`
|
//Note PCRE \s is too broad a definition of whitespace; RFC5322 defines it as `[ \t]`
|
||||||
//@see https://tools.ietf.org/html/rfc5322#section-2.2
|
//@see https://www.rfc-editor.org/rfc/rfc5322#section-2.2
|
||||||
//That means this may break if you do something daft like put vertical tabs in your headers.
|
//That means this may break if you do something daft like put vertical tabs in your headers.
|
||||||
$signHeader = preg_replace('/\r\n[ \t]+/', ' ', $signHeader);
|
$signHeader = preg_replace('/\r\n[ \t]+/', ' ', $signHeader);
|
||||||
//Break headers out into an array
|
//Break headers out into an array
|
||||||
@@ -4832,7 +5068,7 @@ class PHPMailer
|
|||||||
* Uses the 'simple' algorithm from RFC6376 section 3.4.3.
|
* Uses the 'simple' algorithm from RFC6376 section 3.4.3.
|
||||||
* Canonicalized bodies should *always* use CRLF, regardless of mailer setting.
|
* Canonicalized bodies should *always* use CRLF, regardless of mailer setting.
|
||||||
*
|
*
|
||||||
* @see https://tools.ietf.org/html/rfc6376#section-3.4.3
|
* @see https://www.rfc-editor.org/rfc/rfc6376#section-3.4.3
|
||||||
*
|
*
|
||||||
* @param string $body Message Body
|
* @param string $body Message Body
|
||||||
*
|
*
|
||||||
@@ -4868,7 +5104,7 @@ class PHPMailer
|
|||||||
$DKIMquery = 'dns/txt'; //Query method
|
$DKIMquery = 'dns/txt'; //Query method
|
||||||
$DKIMtime = time();
|
$DKIMtime = time();
|
||||||
//Always sign these headers without being asked
|
//Always sign these headers without being asked
|
||||||
//Recommended list from https://tools.ietf.org/html/rfc6376#section-5.4.1
|
//Recommended list from https://www.rfc-editor.org/rfc/rfc6376#section-5.4.1
|
||||||
$autoSignHeaders = [
|
$autoSignHeaders = [
|
||||||
'from',
|
'from',
|
||||||
'to',
|
'to',
|
||||||
@@ -4974,7 +5210,7 @@ class PHPMailer
|
|||||||
}
|
}
|
||||||
//The DKIM-Signature header is included in the signature *except for* the value of the `b` tag
|
//The DKIM-Signature header is included in the signature *except for* the value of the `b` tag
|
||||||
//which is appended after calculating the signature
|
//which is appended after calculating the signature
|
||||||
//https://tools.ietf.org/html/rfc6376#section-3.5
|
//https://www.rfc-editor.org/rfc/rfc6376#section-3.5
|
||||||
$dkimSignatureHeader = 'DKIM-Signature: v=1;' .
|
$dkimSignatureHeader = 'DKIM-Signature: v=1;' .
|
||||||
' d=' . $this->DKIM_domain . ';' .
|
' d=' . $this->DKIM_domain . ';' .
|
||||||
' s=' . $this->DKIM_selector . ';' . static::$LE .
|
' s=' . $this->DKIM_selector . ';' . static::$LE .
|
||||||
|
|||||||
@@ -1,467 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PHPMailer POP-Before-SMTP Authentication Class.
|
|
||||||
* PHP Version 5.5.
|
|
||||||
*
|
|
||||||
* @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
|
|
||||||
*
|
|
||||||
* @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
|
|
||||||
* @author Jim Jagielski (jimjag) <jimjag@gmail.com>
|
|
||||||
* @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
|
|
||||||
* @author Brent R. Matzelle (original founder)
|
|
||||||
* @copyright 2012 - 2020 Marcus Bointon
|
|
||||||
* @copyright 2010 - 2012 Jim Jagielski
|
|
||||||
* @copyright 2004 - 2009 Andy Prevost
|
|
||||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
|
||||||
* @note This program is distributed in the hope that it will be useful - WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace PHPMailer\PHPMailer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PHPMailer POP-Before-SMTP Authentication Class.
|
|
||||||
* Specifically for PHPMailer to use for RFC1939 POP-before-SMTP authentication.
|
|
||||||
* 1) This class does not support APOP authentication.
|
|
||||||
* 2) Opening and closing lots of POP3 connections can be quite slow. If you need
|
|
||||||
* to send a batch of emails then just perform the authentication once at the start,
|
|
||||||
* and then loop through your mail sending script. Providing this process doesn't
|
|
||||||
* take longer than the verification period lasts on your POP3 server, you should be fine.
|
|
||||||
* 3) This is really ancient technology; you should only need to use it to talk to very old systems.
|
|
||||||
* 4) This POP3 class is deliberately lightweight and incomplete, implementing just
|
|
||||||
* enough to do authentication.
|
|
||||||
* If you want a more complete class there are other POP3 classes for PHP available.
|
|
||||||
*
|
|
||||||
* @author Richard Davey (original author) <rich@corephp.co.uk>
|
|
||||||
* @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
|
|
||||||
* @author Jim Jagielski (jimjag) <jimjag@gmail.com>
|
|
||||||
* @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
|
|
||||||
*/
|
|
||||||
class POP3
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The POP3 PHPMailer Version number.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
const VERSION = '6.7.1';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default POP3 port number.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
const DEFAULT_PORT = 110;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default timeout in seconds.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
const DEFAULT_TIMEOUT = 30;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* POP3 class debug output mode.
|
|
||||||
* Debug output level.
|
|
||||||
* Options:
|
|
||||||
* @see POP3::DEBUG_OFF: No output
|
|
||||||
* @see POP3::DEBUG_SERVER: Server messages, connection/server errors
|
|
||||||
* @see POP3::DEBUG_CLIENT: Client and Server messages, connection/server errors
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $do_debug = self::DEBUG_OFF;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* POP3 mail server hostname.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $host;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* POP3 port number.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $port;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* POP3 Timeout Value in seconds.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $tval;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* POP3 username.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $username;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* POP3 password.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $password;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resource handle for the POP3 connection socket.
|
|
||||||
*
|
|
||||||
* @var resource
|
|
||||||
*/
|
|
||||||
protected $pop_conn;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Are we connected?
|
|
||||||
*
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
protected $connected = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Error container.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $errors = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Line break constant.
|
|
||||||
*/
|
|
||||||
const LE = "\r\n";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Debug level for no output.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
const DEBUG_OFF = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Debug level to show server -> client messages
|
|
||||||
* also shows clients connection errors or errors from server
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
const DEBUG_SERVER = 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Debug level to show client -> server and server -> client messages.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
const DEBUG_CLIENT = 2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple static wrapper for all-in-one POP before SMTP.
|
|
||||||
*
|
|
||||||
* @param string $host The hostname to connect to
|
|
||||||
* @param int|bool $port The port number to connect to
|
|
||||||
* @param int|bool $timeout The timeout value
|
|
||||||
* @param string $username
|
|
||||||
* @param string $password
|
|
||||||
* @param int $debug_level
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function popBeforeSmtp(
|
|
||||||
$host,
|
|
||||||
$port = false,
|
|
||||||
$timeout = false,
|
|
||||||
$username = '',
|
|
||||||
$password = '',
|
|
||||||
$debug_level = 0
|
|
||||||
) {
|
|
||||||
$pop = new self();
|
|
||||||
|
|
||||||
return $pop->authorise($host, $port, $timeout, $username, $password, $debug_level);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Authenticate with a POP3 server.
|
|
||||||
* A connect, login, disconnect sequence
|
|
||||||
* appropriate for POP-before SMTP authorisation.
|
|
||||||
*
|
|
||||||
* @param string $host The hostname to connect to
|
|
||||||
* @param int|bool $port The port number to connect to
|
|
||||||
* @param int|bool $timeout The timeout value
|
|
||||||
* @param string $username
|
|
||||||
* @param string $password
|
|
||||||
* @param int $debug_level
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function authorise($host, $port = false, $timeout = false, $username = '', $password = '', $debug_level = 0)
|
|
||||||
{
|
|
||||||
$this->host = $host;
|
|
||||||
//If no port value provided, use default
|
|
||||||
if (false === $port) {
|
|
||||||
$this->port = static::DEFAULT_PORT;
|
|
||||||
} else {
|
|
||||||
$this->port = (int) $port;
|
|
||||||
}
|
|
||||||
//If no timeout value provided, use default
|
|
||||||
if (false === $timeout) {
|
|
||||||
$this->tval = static::DEFAULT_TIMEOUT;
|
|
||||||
} else {
|
|
||||||
$this->tval = (int) $timeout;
|
|
||||||
}
|
|
||||||
$this->do_debug = $debug_level;
|
|
||||||
$this->username = $username;
|
|
||||||
$this->password = $password;
|
|
||||||
//Reset the error log
|
|
||||||
$this->errors = [];
|
|
||||||
//Connect
|
|
||||||
$result = $this->connect($this->host, $this->port, $this->tval);
|
|
||||||
if ($result) {
|
|
||||||
$login_result = $this->login($this->username, $this->password);
|
|
||||||
if ($login_result) {
|
|
||||||
$this->disconnect();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//We need to disconnect regardless of whether the login succeeded
|
|
||||||
$this->disconnect();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connect to a POP3 server.
|
|
||||||
*
|
|
||||||
* @param string $host
|
|
||||||
* @param int|bool $port
|
|
||||||
* @param int $tval
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function connect($host, $port = false, $tval = 30)
|
|
||||||
{
|
|
||||||
//Are we already connected?
|
|
||||||
if ($this->connected) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//On Windows this will raise a PHP Warning error if the hostname doesn't exist.
|
|
||||||
//Rather than suppress it with @fsockopen, capture it cleanly instead
|
|
||||||
set_error_handler([$this, 'catchWarning']);
|
|
||||||
|
|
||||||
if (false === $port) {
|
|
||||||
$port = static::DEFAULT_PORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Connect to the POP3 server
|
|
||||||
$errno = 0;
|
|
||||||
$errstr = '';
|
|
||||||
$this->pop_conn = fsockopen(
|
|
||||||
$host, //POP3 Host
|
|
||||||
$port, //Port #
|
|
||||||
$errno, //Error Number
|
|
||||||
$errstr, //Error Message
|
|
||||||
$tval
|
|
||||||
); //Timeout (seconds)
|
|
||||||
//Restore the error handler
|
|
||||||
restore_error_handler();
|
|
||||||
|
|
||||||
//Did we connect?
|
|
||||||
if (false === $this->pop_conn) {
|
|
||||||
//It would appear not...
|
|
||||||
$this->setError(
|
|
||||||
"Failed to connect to server $host on port $port. errno: $errno; errstr: $errstr"
|
|
||||||
);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Increase the stream time-out
|
|
||||||
stream_set_timeout($this->pop_conn, $tval, 0);
|
|
||||||
|
|
||||||
//Get the POP3 server response
|
|
||||||
$pop3_response = $this->getResponse();
|
|
||||||
//Check for the +OK
|
|
||||||
if ($this->checkResponse($pop3_response)) {
|
|
||||||
//The connection is established and the POP3 server is talking
|
|
||||||
$this->connected = true;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log in to the POP3 server.
|
|
||||||
* Does not support APOP (RFC 2828, 4949).
|
|
||||||
*
|
|
||||||
* @param string $username
|
|
||||||
* @param string $password
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function login($username = '', $password = '')
|
|
||||||
{
|
|
||||||
if (!$this->connected) {
|
|
||||||
$this->setError('Not connected to POP3 server');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (empty($username)) {
|
|
||||||
$username = $this->username;
|
|
||||||
}
|
|
||||||
if (empty($password)) {
|
|
||||||
$password = $this->password;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Send the Username
|
|
||||||
$this->sendString("USER $username" . static::LE);
|
|
||||||
$pop3_response = $this->getResponse();
|
|
||||||
if ($this->checkResponse($pop3_response)) {
|
|
||||||
//Send the Password
|
|
||||||
$this->sendString("PASS $password" . static::LE);
|
|
||||||
$pop3_response = $this->getResponse();
|
|
||||||
if ($this->checkResponse($pop3_response)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disconnect from the POP3 server.
|
|
||||||
*/
|
|
||||||
public function disconnect()
|
|
||||||
{
|
|
||||||
// If could not connect at all, no need to disconnect
|
|
||||||
if ($this->pop_conn === false) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->sendString('QUIT' . static::LE);
|
|
||||||
|
|
||||||
// RFC 1939 shows POP3 server sending a +OK response to the QUIT command.
|
|
||||||
// Try to get it. Ignore any failures here.
|
|
||||||
try {
|
|
||||||
$this->getResponse();
|
|
||||||
} catch (Exception $e) {
|
|
||||||
//Do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
//The QUIT command may cause the daemon to exit, which will kill our connection
|
|
||||||
//So ignore errors here
|
|
||||||
try {
|
|
||||||
@fclose($this->pop_conn);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
//Do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up attributes.
|
|
||||||
$this->connected = false;
|
|
||||||
$this->pop_conn = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a response from the POP3 server.
|
|
||||||
*
|
|
||||||
* @param int $size The maximum number of bytes to retrieve
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected function getResponse($size = 128)
|
|
||||||
{
|
|
||||||
$response = fgets($this->pop_conn, $size);
|
|
||||||
if ($this->do_debug >= self::DEBUG_SERVER) {
|
|
||||||
echo 'Server -> Client: ', $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send raw data to the POP3 server.
|
|
||||||
*
|
|
||||||
* @param string $string
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
protected function sendString($string)
|
|
||||||
{
|
|
||||||
if ($this->pop_conn) {
|
|
||||||
if ($this->do_debug >= self::DEBUG_CLIENT) { //Show client messages when debug >= 2
|
|
||||||
echo 'Client -> Server: ', $string;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fwrite($this->pop_conn, $string, strlen($string));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks the POP3 server response.
|
|
||||||
* Looks for for +OK or -ERR.
|
|
||||||
*
|
|
||||||
* @param string $string
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
protected function checkResponse($string)
|
|
||||||
{
|
|
||||||
if (strpos($string, '+OK') !== 0) {
|
|
||||||
$this->setError("Server reported an error: $string");
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add an error to the internal error store.
|
|
||||||
* Also display debug output if it's enabled.
|
|
||||||
*
|
|
||||||
* @param string $error
|
|
||||||
*/
|
|
||||||
protected function setError($error)
|
|
||||||
{
|
|
||||||
$this->errors[] = $error;
|
|
||||||
if ($this->do_debug >= self::DEBUG_SERVER) {
|
|
||||||
echo '<pre>';
|
|
||||||
foreach ($this->errors as $e) {
|
|
||||||
print_r($e);
|
|
||||||
}
|
|
||||||
echo '</pre>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an array of error messages, if any.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getErrors()
|
|
||||||
{
|
|
||||||
return $this->errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* POP3 connection error handler.
|
|
||||||
*
|
|
||||||
* @param int $errno
|
|
||||||
* @param string $errstr
|
|
||||||
* @param string $errfile
|
|
||||||
* @param int $errline
|
|
||||||
*/
|
|
||||||
protected function catchWarning($errno, $errstr, $errfile, $errline)
|
|
||||||
{
|
|
||||||
$this->setError(
|
|
||||||
'Connecting to the POP3 server raised a PHP warning:' .
|
|
||||||
"errno: $errno errstr: $errstr; errfile: $errfile; errline: $errline"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
* @copyright 2012 - 2020 Marcus Bointon
|
* @copyright 2012 - 2020 Marcus Bointon
|
||||||
* @copyright 2010 - 2012 Jim Jagielski
|
* @copyright 2010 - 2012 Jim Jagielski
|
||||||
* @copyright 2004 - 2009 Andy Prevost
|
* @copyright 2004 - 2009 Andy Prevost
|
||||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
* @license https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License
|
||||||
* @note This program is distributed in the hope that it will be useful - WITHOUT
|
* @note This program is distributed in the hope that it will be useful - WITHOUT
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
@@ -35,7 +35,7 @@ class SMTP
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
const VERSION = '6.7.1';
|
const VERSION = '6.12.0';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SMTP line break constant.
|
* SMTP line break constant.
|
||||||
@@ -51,11 +51,18 @@ class SMTP
|
|||||||
*/
|
*/
|
||||||
const DEFAULT_PORT = 25;
|
const DEFAULT_PORT = 25;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The SMTPs port to use if one is not specified.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
const DEFAULT_SECURE_PORT = 465;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum line length allowed by RFC 5321 section 4.5.3.1.6,
|
* The maximum line length allowed by RFC 5321 section 4.5.3.1.6,
|
||||||
* *excluding* a trailing CRLF break.
|
* *excluding* a trailing CRLF break.
|
||||||
*
|
*
|
||||||
* @see https://tools.ietf.org/html/rfc5321#section-4.5.3.1.6
|
* @see https://www.rfc-editor.org/rfc/rfc5321#section-4.5.3.1.6
|
||||||
*
|
*
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
@@ -65,7 +72,7 @@ class SMTP
|
|||||||
* The maximum line length allowed for replies in RFC 5321 section 4.5.3.1.5,
|
* The maximum line length allowed for replies in RFC 5321 section 4.5.3.1.5,
|
||||||
* *including* a trailing CRLF line break.
|
* *including* a trailing CRLF line break.
|
||||||
*
|
*
|
||||||
* @see https://tools.ietf.org/html/rfc5321#section-4.5.3.1.5
|
* @see https://www.rfc-editor.org/rfc/rfc5321#section-4.5.3.1.5
|
||||||
*
|
*
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
@@ -145,19 +152,28 @@ class SMTP
|
|||||||
/**
|
/**
|
||||||
* Whether to use VERP.
|
* Whether to use VERP.
|
||||||
*
|
*
|
||||||
* @see http://en.wikipedia.org/wiki/Variable_envelope_return_path
|
* @see https://en.wikipedia.org/wiki/Variable_envelope_return_path
|
||||||
* @see http://www.postfix.org/VERP_README.html Info on VERP
|
* @see https://www.postfix.org/VERP_README.html Info on VERP
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
public $do_verp = false;
|
public $do_verp = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to use SMTPUTF8.
|
||||||
|
*
|
||||||
|
* @see https://www.rfc-editor.org/rfc/rfc6531
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $do_smtputf8 = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The timeout value for connection, in seconds.
|
* The timeout value for connection, in seconds.
|
||||||
* Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2.
|
* Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2.
|
||||||
* This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
|
* This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
|
||||||
*
|
*
|
||||||
* @see http://tools.ietf.org/html/rfc2821#section-4.5.3.2
|
* @see https://www.rfc-editor.org/rfc/rfc2821#section-4.5.3.2
|
||||||
*
|
*
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
@@ -180,17 +196,29 @@ class SMTP
|
|||||||
*/
|
*/
|
||||||
protected $smtp_transaction_id_patterns = [
|
protected $smtp_transaction_id_patterns = [
|
||||||
'exim' => '/[\d]{3} OK id=(.*)/',
|
'exim' => '/[\d]{3} OK id=(.*)/',
|
||||||
'sendmail' => '/[\d]{3} 2.0.0 (.*) Message/',
|
'sendmail' => '/[\d]{3} 2\.0\.0 (.*) Message/',
|
||||||
'postfix' => '/[\d]{3} 2.0.0 Ok: queued as (.*)/',
|
'postfix' => '/[\d]{3} 2\.0\.0 Ok: queued as (.*)/',
|
||||||
'Microsoft_ESMTP' => '/[0-9]{3} 2.[\d].0 (.*)@(?:.*) Queued mail for delivery/',
|
'Microsoft_ESMTP' => '/[0-9]{3} 2\.[\d]\.0 (.*)@(?:.*) Queued mail for delivery/',
|
||||||
'Amazon_SES' => '/[\d]{3} Ok (.*)/',
|
'Amazon_SES' => '/[\d]{3} Ok (.*)/',
|
||||||
'SendGrid' => '/[\d]{3} Ok: queued as (.*)/',
|
'SendGrid' => '/[\d]{3} Ok: queued as (.*)/',
|
||||||
'CampaignMonitor' => '/[\d]{3} 2.0.0 OK:([a-zA-Z\d]{48})/',
|
'CampaignMonitor' => '/[\d]{3} 2\.0\.0 OK:([a-zA-Z\d]{48})/',
|
||||||
'Haraka' => '/[\d]{3} Message Queued \((.*)\)/',
|
'Haraka' => '/[\d]{3} Message Queued \((.*)\)/',
|
||||||
'ZoneMTA' => '/[\d]{3} Message queued as (.*)/',
|
'ZoneMTA' => '/[\d]{3} Message queued as (.*)/',
|
||||||
'Mailjet' => '/[\d]{3} OK queued as (.*)/',
|
'Mailjet' => '/[\d]{3} OK queued as (.*)/',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allowed SMTP XCLIENT attributes.
|
||||||
|
* Must be allowed by the SMTP server. EHLO response is not checked.
|
||||||
|
*
|
||||||
|
* @see https://www.postfix.org/XCLIENT_README.html
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public static $xclient_allowed_attributes = [
|
||||||
|
'NAME', 'ADDR', 'PORT', 'PROTO', 'HELO', 'LOGIN', 'DESTADDR', 'DESTPORT'
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The last transaction ID issued in response to a DATA command,
|
* The last transaction ID issued in response to a DATA command,
|
||||||
* if one was detected.
|
* if one was detected.
|
||||||
@@ -261,7 +289,8 @@ class SMTP
|
|||||||
}
|
}
|
||||||
//Is this a PSR-3 logger?
|
//Is this a PSR-3 logger?
|
||||||
if ($this->Debugoutput instanceof \Psr\Log\LoggerInterface) {
|
if ($this->Debugoutput instanceof \Psr\Log\LoggerInterface) {
|
||||||
$this->Debugoutput->debug($str);
|
//Remove trailing line breaks potentially added by calls to SMTP::client_send()
|
||||||
|
$this->Debugoutput->debug(rtrim($str, "\r\n"));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -274,6 +303,7 @@ class SMTP
|
|||||||
switch ($this->Debugoutput) {
|
switch ($this->Debugoutput) {
|
||||||
case 'error_log':
|
case 'error_log':
|
||||||
//Don't output, just log
|
//Don't output, just log
|
||||||
|
/** @noinspection ForgottenDebugOutputInspection */
|
||||||
error_log($str);
|
error_log($str);
|
||||||
break;
|
break;
|
||||||
case 'html':
|
case 'html':
|
||||||
@@ -352,7 +382,7 @@ class SMTP
|
|||||||
}
|
}
|
||||||
//Anything other than a 220 response means something went wrong
|
//Anything other than a 220 response means something went wrong
|
||||||
//RFC 5321 says the server will wait for us to send a QUIT in response to a 554 error
|
//RFC 5321 says the server will wait for us to send a QUIT in response to a 554 error
|
||||||
//https://tools.ietf.org/html/rfc5321#section-3.1
|
//https://www.rfc-editor.org/rfc/rfc5321#section-3.1
|
||||||
if ($responseCode === 554) {
|
if ($responseCode === 554) {
|
||||||
$this->quit();
|
$this->quit();
|
||||||
}
|
}
|
||||||
@@ -385,7 +415,9 @@ class SMTP
|
|||||||
$errstr = '';
|
$errstr = '';
|
||||||
if ($streamok) {
|
if ($streamok) {
|
||||||
$socket_context = stream_context_create($options);
|
$socket_context = stream_context_create($options);
|
||||||
set_error_handler([$this, 'errorHandler']);
|
set_error_handler(function () {
|
||||||
|
call_user_func_array([$this, 'errorHandler'], func_get_args());
|
||||||
|
});
|
||||||
$connection = stream_socket_client(
|
$connection = stream_socket_client(
|
||||||
$host . ':' . $port,
|
$host . ':' . $port,
|
||||||
$errno,
|
$errno,
|
||||||
@@ -400,7 +432,9 @@ class SMTP
|
|||||||
'Connection: stream_socket_client not available, falling back to fsockopen',
|
'Connection: stream_socket_client not available, falling back to fsockopen',
|
||||||
self::DEBUG_CONNECTION
|
self::DEBUG_CONNECTION
|
||||||
);
|
);
|
||||||
set_error_handler([$this, 'errorHandler']);
|
set_error_handler(function () {
|
||||||
|
call_user_func_array([$this, 'errorHandler'], func_get_args());
|
||||||
|
});
|
||||||
$connection = fsockopen(
|
$connection = fsockopen(
|
||||||
$host,
|
$host,
|
||||||
$port,
|
$port,
|
||||||
@@ -464,7 +498,9 @@ class SMTP
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Begin encrypted connection
|
//Begin encrypted connection
|
||||||
set_error_handler([$this, 'errorHandler']);
|
set_error_handler(function () {
|
||||||
|
call_user_func_array([$this, 'errorHandler'], func_get_args());
|
||||||
|
});
|
||||||
$crypto_ok = stream_socket_enable_crypto(
|
$crypto_ok = stream_socket_enable_crypto(
|
||||||
$this->smtp_conn,
|
$this->smtp_conn,
|
||||||
true,
|
true,
|
||||||
@@ -555,7 +591,7 @@ class SMTP
|
|||||||
}
|
}
|
||||||
//Send encoded username and password
|
//Send encoded username and password
|
||||||
if (
|
if (
|
||||||
//Format from https://tools.ietf.org/html/rfc4616#section-2
|
//Format from https://www.rfc-editor.org/rfc/rfc4616#section-2
|
||||||
//We skip the first field (it's forgery), so the string starts with a null byte
|
//We skip the first field (it's forgery), so the string starts with a null byte
|
||||||
!$this->sendCommand(
|
!$this->sendCommand(
|
||||||
'User & Password',
|
'User & Password',
|
||||||
@@ -629,7 +665,7 @@ class SMTP
|
|||||||
}
|
}
|
||||||
|
|
||||||
//The following borrowed from
|
//The following borrowed from
|
||||||
//http://php.net/manual/en/function.mhash.php#27225
|
//https://www.php.net/manual/en/function.mhash.php#27225
|
||||||
|
|
||||||
//RFC 2104 HMAC implementation for php.
|
//RFC 2104 HMAC implementation for php.
|
||||||
//Creates an md5 HMAC.
|
//Creates an md5 HMAC.
|
||||||
@@ -697,7 +733,7 @@ class SMTP
|
|||||||
* Send an SMTP DATA command.
|
* Send an SMTP DATA command.
|
||||||
* Issues a data command and sends the msg_data to the server,
|
* Issues a data command and sends the msg_data to the server,
|
||||||
* finalizing the mail transaction. $msg_data is the message
|
* finalizing the mail transaction. $msg_data is the message
|
||||||
* that is to be send with the headers. Each header needs to be
|
* that is to be sent with the headers. Each header needs to be
|
||||||
* on a single line followed by a <CRLF> with the message headers
|
* on a single line followed by a <CRLF> with the message headers
|
||||||
* and the message body being separated by an additional <CRLF>.
|
* and the message body being separated by an additional <CRLF>.
|
||||||
* Implements RFC 821: DATA <CRLF>.
|
* Implements RFC 821: DATA <CRLF>.
|
||||||
@@ -725,7 +761,7 @@ class SMTP
|
|||||||
$lines = explode("\n", str_replace(["\r\n", "\r"], "\n", $msg_data));
|
$lines = explode("\n", str_replace(["\r\n", "\r"], "\n", $msg_data));
|
||||||
|
|
||||||
/* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
|
/* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
|
||||||
* of the first line (':' separated) does not contain a space then it _should_ be a header and we will
|
* of the first line (':' separated) does not contain a space then it _should_ be a header, and we will
|
||||||
* process all lines before a blank line as headers.
|
* process all lines before a blank line as headers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -768,7 +804,7 @@ class SMTP
|
|||||||
//Send the lines to the server
|
//Send the lines to the server
|
||||||
foreach ($lines_out as $line_out) {
|
foreach ($lines_out as $line_out) {
|
||||||
//Dot-stuffing as per RFC5321 section 4.5.2
|
//Dot-stuffing as per RFC5321 section 4.5.2
|
||||||
//https://tools.ietf.org/html/rfc5321#section-4.5.2
|
//https://www.rfc-editor.org/rfc/rfc5321#section-4.5.2
|
||||||
if (!empty($line_out) && $line_out[0] === '.') {
|
if (!empty($line_out) && $line_out[0] === '.') {
|
||||||
$line_out = '.' . $line_out;
|
$line_out = '.' . $line_out;
|
||||||
}
|
}
|
||||||
@@ -886,7 +922,15 @@ class SMTP
|
|||||||
* $from. Returns true if successful or false otherwise. If True
|
* $from. Returns true if successful or false otherwise. If True
|
||||||
* the mail transaction is started and then one or more recipient
|
* the mail transaction is started and then one or more recipient
|
||||||
* commands may be called followed by a data command.
|
* commands may be called followed by a data command.
|
||||||
* Implements RFC 821: MAIL <SP> FROM:<reverse-path> <CRLF>.
|
* Implements RFC 821: MAIL <SP> FROM:<reverse-path> <CRLF> and
|
||||||
|
* two extensions, namely XVERP and SMTPUTF8.
|
||||||
|
*
|
||||||
|
* The server's EHLO response is not checked. If use of either
|
||||||
|
* extensions is enabled even though the server does not support
|
||||||
|
* that, mail submission will fail.
|
||||||
|
*
|
||||||
|
* XVERP is documented at https://www.postfix.org/VERP_README.html
|
||||||
|
* and SMTPUTF8 is specified in RFC 6531.
|
||||||
*
|
*
|
||||||
* @param string $from Source address of this message
|
* @param string $from Source address of this message
|
||||||
*
|
*
|
||||||
@@ -895,10 +939,11 @@ class SMTP
|
|||||||
public function mail($from)
|
public function mail($from)
|
||||||
{
|
{
|
||||||
$useVerp = ($this->do_verp ? ' XVERP' : '');
|
$useVerp = ($this->do_verp ? ' XVERP' : '');
|
||||||
|
$useSmtputf8 = ($this->do_smtputf8 ? ' SMTPUTF8' : '');
|
||||||
|
|
||||||
return $this->sendCommand(
|
return $this->sendCommand(
|
||||||
'MAIL FROM',
|
'MAIL FROM',
|
||||||
'MAIL FROM:<' . $from . '>' . $useVerp,
|
'MAIL FROM:<' . $from . '>' . $useSmtputf8 . $useVerp,
|
||||||
250
|
250
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -964,6 +1009,25 @@ class SMTP
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send SMTP XCLIENT command to server and check its return code.
|
||||||
|
*
|
||||||
|
* @return bool True on success
|
||||||
|
*/
|
||||||
|
public function xclient(array $vars)
|
||||||
|
{
|
||||||
|
$xclient_options = "";
|
||||||
|
foreach ($vars as $key => $value) {
|
||||||
|
if (in_array($key, SMTP::$xclient_allowed_attributes)) {
|
||||||
|
$xclient_options .= " {$key}={$value}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$xclient_options) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return $this->sendCommand('XCLIENT', 'XCLIENT' . $xclient_options, 250);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send an SMTP RSET command.
|
* Send an SMTP RSET command.
|
||||||
* Abort any transaction that is currently in progress.
|
* Abort any transaction that is currently in progress.
|
||||||
@@ -1124,7 +1188,9 @@ class SMTP
|
|||||||
} else {
|
} else {
|
||||||
$this->edebug('CLIENT -> SERVER: ' . $data, self::DEBUG_CLIENT);
|
$this->edebug('CLIENT -> SERVER: ' . $data, self::DEBUG_CLIENT);
|
||||||
}
|
}
|
||||||
set_error_handler([$this, 'errorHandler']);
|
set_error_handler(function () {
|
||||||
|
call_user_func_array([$this, 'errorHandler'], func_get_args());
|
||||||
|
});
|
||||||
$result = fwrite($this->smtp_conn, $data);
|
$result = fwrite($this->smtp_conn, $data);
|
||||||
restore_error_handler();
|
restore_error_handler();
|
||||||
|
|
||||||
@@ -1227,7 +1293,9 @@ class SMTP
|
|||||||
while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
|
while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
|
||||||
//Must pass vars in here as params are by reference
|
//Must pass vars in here as params are by reference
|
||||||
//solution for signals inspired by https://github.com/symfony/symfony/pull/6540
|
//solution for signals inspired by https://github.com/symfony/symfony/pull/6540
|
||||||
set_error_handler([$this, 'errorHandler']);
|
set_error_handler(function () {
|
||||||
|
call_user_func_array([$this, 'errorHandler'], func_get_args());
|
||||||
|
});
|
||||||
$n = stream_select($selR, $selW, $selW, $this->Timelimit);
|
$n = stream_select($selR, $selW, $selW, $this->Timelimit);
|
||||||
restore_error_handler();
|
restore_error_handler();
|
||||||
|
|
||||||
@@ -1314,6 +1382,26 @@ class SMTP
|
|||||||
return $this->do_verp;
|
return $this->do_verp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable or disable use of SMTPUTF8.
|
||||||
|
*
|
||||||
|
* @param bool $enabled
|
||||||
|
*/
|
||||||
|
public function setSMTPUTF8($enabled = false)
|
||||||
|
{
|
||||||
|
$this->do_smtputf8 = $enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get SMTPUTF8 use.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getSMTPUTF8()
|
||||||
|
{
|
||||||
|
return $this->do_smtputf8;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set error messages and codes.
|
* Set error messages and codes.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ class SendMail {
|
|||||||
/* @var PHPMailer $mail */
|
/* @var PHPMailer $mail */
|
||||||
$mail = new PHPMailer(true);
|
$mail = new PHPMailer(true);
|
||||||
|
|
||||||
|
$mail->CharSet = 'UTF-8';
|
||||||
$mail->isSMTP();
|
$mail->isSMTP();
|
||||||
$mail->isHTML();
|
$mail->isHTML();
|
||||||
//$mail->SMTPDebug = SMTP::DEBUG_LOWLEVEL;
|
//$mail->SMTPDebug = SMTP::DEBUG_LOWLEVEL;
|
||||||
@@ -81,12 +82,12 @@ class SendMail {
|
|||||||
}
|
}
|
||||||
$mail->setFrom($this->config['smtp']['username']);
|
$mail->setFrom($this->config['smtp']['username']);
|
||||||
$mail->Subject = $this->subject;
|
$mail->Subject = $this->subject;
|
||||||
$mail->Body = htmlentities($this->body, ENT_QUOTES);
|
$mail->Body = nl2br(htmlentities($this->body, ENT_QUOTES, 'UTF-8'));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return $mail->send();
|
return $mail->send();
|
||||||
} catch(\Exception $ex) {
|
} catch(\Exception $ex) {
|
||||||
var_dump($ex);
|
error_log($ex->getMessage());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||