@rglabs/butterfly 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/docs-page-layout.md +12 -0
- package/CLAUDE.md +167 -1
- package/README.md +2 -22
- package/dist/commands/setup.js +15 -1
- package/dist/commands/view-report.d.ts +8 -0
- package/dist/commands/view-report.js +89 -0
- package/dist/index.js +9 -1
- package/docs/IMPLEMENTATION_PROMPT.md +252 -0
- package/docs/butterfly_upload_api_docs-2.md +255 -0
- package/docs/objects/listing-query.md +78 -0
- package/docs/pdf-generation.md +376 -0
- package/package.json +1 -1
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# Butterfly Dosya Yükleme Dokümantasyonu
|
|
2
|
+
|
|
3
|
+
## Genel Bakış
|
|
4
|
+
|
|
5
|
+
Butterfly DXP'de dosya yükleme iki yöntemle yapılabilir:
|
|
6
|
+
|
|
7
|
+
1. **Admin Panel Endpoint (Önerilen)** — Session-based auth, custom report sayfalarında ve admin panelinde kullanılır
|
|
8
|
+
2. **REST API Endpoint** — Token-based auth, harici sistemler ve mobil uygulamalar için
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Yöntem 1: Admin Panel File Helper (Önerilen)
|
|
13
|
+
|
|
14
|
+
Custom report sayfaları veya admin panelindeki JavaScript kodlarından dosya yüklemek için bu yöntem kullanılır. Kullanıcı zaten admin paneline giriş yaptığı için ek authentication gerekmez.
|
|
15
|
+
|
|
16
|
+
### Endpoint
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
POST /admin/ajax/file_helper/upload_file?alias=ALIAS
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Parametreler
|
|
23
|
+
|
|
24
|
+
| Parametre | Tip | Zorunlu | Açıklama |
|
|
25
|
+
|-----------|-----|---------|----------|
|
|
26
|
+
| `file_upload` | File | Evet | Yüklenecek dosya (input name **mutlaka** `file_upload` olmalı) |
|
|
27
|
+
| `uuid` | String | Evet | Benzersiz tanımlayıcı (örn: `upload-` + timestamp) |
|
|
28
|
+
| `original_filename` | String | Evet | Orijinal dosya adı |
|
|
29
|
+
| `total_file_size` | Number | Evet | Dosya boyutu (bytes) |
|
|
30
|
+
| `alias` | String | Evet | Storage alias (URL query parameter olarak gönderilir) |
|
|
31
|
+
|
|
32
|
+
### Storage Alias Yapılandırması
|
|
33
|
+
|
|
34
|
+
Alias'lar `cms_file_uploads` tablosunda tanımlıdır. Her alias şunları kontrol eder:
|
|
35
|
+
- İzin verilen dosya uzantıları (`extensions` alanı, virgülle ayrılmış)
|
|
36
|
+
- Maksimum dosya boyutu
|
|
37
|
+
- Depolama konumu
|
|
38
|
+
|
|
39
|
+
**Alias yönetimi:** Admin panel → File Storage menüsü veya `cms_file_uploads` tablosu üzerinden.
|
|
40
|
+
|
|
41
|
+
**Yaygın alias'lar:**
|
|
42
|
+
- `content` — Genel içerik (görsel, medya)
|
|
43
|
+
- `import` — İçe aktarma dosyaları (varsayılan: json, xlsx, xls; ihtiyaca göre genişletilebilir)
|
|
44
|
+
|
|
45
|
+
**Yeni uzantı eklemek için:**
|
|
46
|
+
```bash
|
|
47
|
+
butterfly-cli record edit cms_file_uploads --id <alias_id> --data '{"extensions": "mevcut,yeni,uzantilar"}'
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### JavaScript Örneği (Custom Report Sayfası)
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
function uploadFile(file) {
|
|
54
|
+
return new Promise(function(resolve, reject) {
|
|
55
|
+
var fd = new FormData();
|
|
56
|
+
fd.append('file_upload', file);
|
|
57
|
+
fd.append('uuid', 'upload-' + Date.now());
|
|
58
|
+
fd.append('original_filename', file.name);
|
|
59
|
+
fd.append('total_file_size', file.size);
|
|
60
|
+
|
|
61
|
+
$.ajax({
|
|
62
|
+
url: '/admin/ajax/file_helper/upload_file?alias=import',
|
|
63
|
+
type: 'POST',
|
|
64
|
+
data: fd,
|
|
65
|
+
processData: false,
|
|
66
|
+
contentType: false,
|
|
67
|
+
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
|
68
|
+
success: function(response) {
|
|
69
|
+
if (response.success && response.filename) {
|
|
70
|
+
resolve(response.filename);
|
|
71
|
+
} else {
|
|
72
|
+
rg_alert(response.message || 'Dosya yüklenemedi.');
|
|
73
|
+
reject('Upload failed');
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
error: function() {
|
|
77
|
+
rg_alert('Dosya yükleme sırasında hata oluştu.');
|
|
78
|
+
reject('Upload error');
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Yanıt Formatı
|
|
86
|
+
|
|
87
|
+
**Başarılı:**
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"success": true,
|
|
91
|
+
"filename": "dosya_adi_islenmis.jpg",
|
|
92
|
+
"full_path": "/storage/path/dosya_adi_islenmis.jpg"
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Hata:**
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"success": false,
|
|
100
|
+
"message": "Dosya tipi izin verilmiyor."
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Dosya Kaydı ile Entegrasyon (İki Aşamalı)
|
|
105
|
+
|
|
106
|
+
Dosya yükleme ve kayıt oluşturma iki ayrı adımda yapılır:
|
|
107
|
+
|
|
108
|
+
```javascript
|
|
109
|
+
// 1. Dosyayı yükle → filename al
|
|
110
|
+
var filename = await uploadFile(file);
|
|
111
|
+
|
|
112
|
+
// 2. Kaydı oluştur, filename'i veri olarak gönder
|
|
113
|
+
$.ajax({
|
|
114
|
+
url: '/admin/ajax/cms_object/operation?do=tablo_adi__add',
|
|
115
|
+
type: 'POST',
|
|
116
|
+
data: {
|
|
117
|
+
csrf_token: csrfToken,
|
|
118
|
+
parent_id: parentId,
|
|
119
|
+
dosya: filename, // file_upload field'ın column adı
|
|
120
|
+
aciklama: 'Açıklama'
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Object Spec Yapılandırması
|
|
126
|
+
|
|
127
|
+
`file_upload` tipindeki field'ın `val_1` parametresi alias adını belirtir:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"type": "file_upload",
|
|
132
|
+
"val_1": "import"
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Dosya Önizleme Linki Oluşturma
|
|
137
|
+
|
|
138
|
+
Upload edilen dosyalar veritabanında sadece **relative path** olarak saklanır (örn: `26-03/08/dosya.png`). Tam URL oluşturmak için alias'ın `path` değeri gerekir.
|
|
139
|
+
|
|
140
|
+
**Twig'de base path'i al:**
|
|
141
|
+
```twig
|
|
142
|
+
{% set importAlias = db().table('cms_file_uploads').where('alias', 'import').first() %}
|
|
143
|
+
{% set importBasePath = importAlias ? importAlias.path : '/static/import/' %}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**JS'e aktar:**
|
|
147
|
+
```html
|
|
148
|
+
<script>
|
|
149
|
+
window._data = {
|
|
150
|
+
importBasePath: '{{ importBasePath }}'
|
|
151
|
+
};
|
|
152
|
+
</script>
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**JS'de tam URL oluştur:**
|
|
156
|
+
```javascript
|
|
157
|
+
var basePath = data.importBasePath || '/static/import/';
|
|
158
|
+
var fullUrl = basePath + record.dosya;
|
|
159
|
+
// Örnek: /static/import/26-03/08/dosya.png
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Path yapısı:**
|
|
163
|
+
- `cms_file_storages.base_url` → Storage base URL (örn: `/static`)
|
|
164
|
+
- `cms_file_uploads.path` → Alias path (örn: `/static/import/`)
|
|
165
|
+
- Veritabanındaki `dosya` değeri → Relative path (örn: `26-03/08/dosya.png`)
|
|
166
|
+
- **Tam URL** = `cms_file_uploads.path` + `dosya` değeri
|
|
167
|
+
|
|
168
|
+
### Önemli Notlar
|
|
169
|
+
|
|
170
|
+
- **Input name:** Mutlaka `file_upload` olmalı, field'ın column adı (örn: `dosya`) DEĞİL
|
|
171
|
+
- **Authentication:** Admin session cookie'leri otomatik gönderilir, ek header gerekmez
|
|
172
|
+
- **Alias zorunlu:** URL'de `?alias=ALIAS` belirtilmeli, yoksa hata verir
|
|
173
|
+
- **Tek dosya:** Bu endpoint tek seferde bir dosya yükler, birden fazla dosya için döngü kullanın
|
|
174
|
+
- **Dosya uzantısı kontrol:** Alias'ta tanımlı uzantılarla eşleşmeyen dosyalar reddedilir
|
|
175
|
+
- **Dosya linki:** Veritabanında sadece relative path saklanır, görüntülemek için alias path'i ile birleştirin
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Yöntem 2: REST API (Harici Sistemler İçin)
|
|
180
|
+
|
|
181
|
+
Harici uygulamalar, mobil uygulamalar veya otomatik süreçler için REST API kullanılır.
|
|
182
|
+
|
|
183
|
+
### Ön Gereksinimler
|
|
184
|
+
|
|
185
|
+
1. **API Erişimi:** Admin panelinde kullanıcı profilinde "API Access" aktif edilmeli
|
|
186
|
+
2. **API Key:** `/admin/user_auth/list` sayfasından API key oluşturulmalı
|
|
187
|
+
|
|
188
|
+
### Authentication
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
# Token oluştur
|
|
192
|
+
curl -X POST /rest/auth/generateToken \
|
|
193
|
+
-H "Content-Type: application/x-www-form-urlencoded" \
|
|
194
|
+
-d "api_key=YOUR_API_KEY&password=YOUR_PASSWORD"
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Yanıt:**
|
|
198
|
+
```json
|
|
199
|
+
{
|
|
200
|
+
"success": true,
|
|
201
|
+
"token": "session_token",
|
|
202
|
+
"expires_at": "2024-12-31 23:59:59"
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Dosya Yükleme
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
curl -X POST /rest/upload/image \
|
|
210
|
+
-H "BUTTERFLY_API_KEY: api-key-here" \
|
|
211
|
+
-H "BUTTERFLY_ACCESS_TOKEN: access-token-here" \
|
|
212
|
+
-F "files[]=@/path/to/image.jpg" \
|
|
213
|
+
-F "alias=content" \
|
|
214
|
+
-F "sub_folder=gallery"
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**Parametreler:**
|
|
218
|
+
- `files[]` (file array, zorunlu): Yüklenecek dosyalar
|
|
219
|
+
- `alias` (string, opsiyonel): Storage alias (varsayılan: 'content')
|
|
220
|
+
- `sub_folder` (string, opsiyonel): Alt klasör
|
|
221
|
+
|
|
222
|
+
**Yanıt:**
|
|
223
|
+
```json
|
|
224
|
+
{
|
|
225
|
+
"success": true,
|
|
226
|
+
"result": [
|
|
227
|
+
{
|
|
228
|
+
"full_path": "https://cdn.example.com/content/gallery/image.jpg",
|
|
229
|
+
"filename": "image.jpg",
|
|
230
|
+
"webp": "https://cdn.example.com/content/gallery/image.webp"
|
|
231
|
+
}
|
|
232
|
+
]
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### REST API Hata Durumları
|
|
237
|
+
|
|
238
|
+
| Hata | Açıklama |
|
|
239
|
+
|------|----------|
|
|
240
|
+
| `"invalid request"` | api_key veya password eksik |
|
|
241
|
+
| `"invalid parameters"` | API key bulunamadı, API erişimi kapalı, yanlış şifre |
|
|
242
|
+
| 401 Unauthorized | Geçersiz veya süresi dolmuş token |
|
|
243
|
+
| 422 Unprocessable | Dosya tipi veya boyut sınırı aşıldı |
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Hangi Yöntemi Kullanmalı?
|
|
248
|
+
|
|
249
|
+
| Senaryo | Yöntem |
|
|
250
|
+
|---------|--------|
|
|
251
|
+
| Custom report sayfasında dosya yükleme | Admin Panel (Yöntem 1) |
|
|
252
|
+
| Object edit sayfasında file_upload field | Otomatik (Butterfly yönetir) |
|
|
253
|
+
| Harici uygulama / mobil uygulama | REST API (Yöntem 2) |
|
|
254
|
+
| Cron job / workflow / otomasyon | REST API (Yöntem 2) |
|
|
255
|
+
| Admin paneli içi JavaScript | Admin Panel (Yöntem 1) |
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Object Listing Query
|
|
2
|
+
|
|
3
|
+
Each object can have a `listing_query.bfy` file that customizes the listing page behavior and appearance.
|
|
4
|
+
|
|
5
|
+
## File Location
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
butterfly-resources/objects/[butterfly|app]/[table_name]/listing_query.bfy
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Header Block
|
|
12
|
+
|
|
13
|
+
Use the `{% block header %}` to display custom content at the top of the listing page, above the data table.
|
|
14
|
+
|
|
15
|
+
### Basic Usage
|
|
16
|
+
|
|
17
|
+
```twig
|
|
18
|
+
{% block header %}
|
|
19
|
+
<div class="alert alert-info">
|
|
20
|
+
Custom content displayed at the top of the listing page
|
|
21
|
+
</div>
|
|
22
|
+
{% endblock %}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Example with Statistics
|
|
26
|
+
|
|
27
|
+
```twig
|
|
28
|
+
{% block header %}
|
|
29
|
+
{% set stats = db().table('orders').selectRaw('COUNT(*) as total, SUM(amount) as sum').first() %}
|
|
30
|
+
<div class="row mb-3">
|
|
31
|
+
<div class="col-md-4">
|
|
32
|
+
<div class="card">
|
|
33
|
+
<div class="card-body text-center">
|
|
34
|
+
<h5 class="card-title">Total Orders</h5>
|
|
35
|
+
<p class="display-6">{{ stats.total }}</p>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
<div class="col-md-4">
|
|
40
|
+
<div class="card">
|
|
41
|
+
<div class="card-body text-center">
|
|
42
|
+
<h5 class="card-title">Total Amount</h5>
|
|
43
|
+
<p class="display-6">{{ stats.sum|number_format(2) }} TL</p>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
{% endblock %}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Example with Filters
|
|
52
|
+
|
|
53
|
+
```twig
|
|
54
|
+
{% block header %}
|
|
55
|
+
<div class="card mb-3">
|
|
56
|
+
<div class="card-body">
|
|
57
|
+
<form method="get" class="row g-3">
|
|
58
|
+
<div class="col-md-3">
|
|
59
|
+
<label class="form-label">Status</label>
|
|
60
|
+
<select name="status" class="form-select">
|
|
61
|
+
<option value="">All</option>
|
|
62
|
+
<option value="active">Active</option>
|
|
63
|
+
<option value="inactive">Inactive</option>
|
|
64
|
+
</select>
|
|
65
|
+
</div>
|
|
66
|
+
<div class="col-md-3 d-flex align-items-end">
|
|
67
|
+
<button type="submit" class="btn btn-primary">Filter</button>
|
|
68
|
+
</div>
|
|
69
|
+
</form>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
{% endblock %}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Related
|
|
76
|
+
|
|
77
|
+
- [Objects Overview](./README.md)
|
|
78
|
+
- [Creating Objects](./creating.md)
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
# Butterfly PDF Oluşturma Dokümantasyonu
|
|
2
|
+
|
|
3
|
+
## Genel Bakış
|
|
4
|
+
|
|
5
|
+
Butterfly DXP, `pdf_templates` tablosundaki Twig şablonlarından PDF dosyaları oluşturmak için yerleşik bir PDF sistemi sunar. Sistem arka planda `https://pdf.butterfly.dev/get` adresindeki PDF sunucusunu kullanır.
|
|
6
|
+
|
|
7
|
+
## Mimari
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
[İstek] → /pdf/create veya /pdf/view
|
|
11
|
+
→ pdf_templates tablosundan şablon çek (system_name ile)
|
|
12
|
+
→ Twig render (Input parametreleri değişken olarak geçer)
|
|
13
|
+
→ HTML'i PDF sunucusuna gönder
|
|
14
|
+
→ /create: PDF'i storage'a kaydet, dosya bilgisi dön
|
|
15
|
+
→ /view: PDF'i tarayıcıda inline göster
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Endpoint'ler
|
|
19
|
+
|
|
20
|
+
### 1. PDF Oluştur ve Kaydet (`/pdf/create`)
|
|
21
|
+
|
|
22
|
+
PDF oluşturur, storage alias'a kaydeder, dosya bilgisi döner.
|
|
23
|
+
|
|
24
|
+
**URL:** `POST /pdf/create`
|
|
25
|
+
|
|
26
|
+
**Parametreler:**
|
|
27
|
+
|
|
28
|
+
| Parametre | Tip | Zorunlu | Açıklama |
|
|
29
|
+
|-----------|-----|---------|----------|
|
|
30
|
+
| `template_name` | string | Evet | `pdf_templates.system_name` değeri |
|
|
31
|
+
| `filename` | string | Hayır | Çıktı dosya adı (varsayılan: `document.pdf`) |
|
|
32
|
+
| `alias` | string | Hayır | Storage alias (varsayılan: `pdf`) |
|
|
33
|
+
| *diğer parametreler* | any | Hayır | Template'e değişken olarak geçer |
|
|
34
|
+
|
|
35
|
+
**Başarılı Yanıt:**
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"success": true,
|
|
39
|
+
"full_path": "https://butterfly.rg/static/pdf/26-03/08/harcama-3.pdf",
|
|
40
|
+
"filename": "26-03/08/harcama-3.pdf"
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Hata Yanıtları:**
|
|
45
|
+
```json
|
|
46
|
+
{"success": false, "message": "PDF tasarımı seçiniz."}
|
|
47
|
+
{"success": false, "message": "PDF tasarımı bulunamadı."}
|
|
48
|
+
{"success": false, "message": "Hata: ...twig render hatası..."}
|
|
49
|
+
{"success": false, "message": "PDF oluşturulurken bir hata oluştu..."}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 2. PDF Önizleme (`/pdf/view`)
|
|
53
|
+
|
|
54
|
+
PDF oluşturur ve tarayıcıda inline gösterir (kaydetmez). Yazdır/önizleme için idealdir.
|
|
55
|
+
|
|
56
|
+
**URL:** `GET /pdf/view`
|
|
57
|
+
|
|
58
|
+
**Parametreler:** `/pdf/create` ile aynı. Ek olarak template'e `preview: true` değişkeni geçer.
|
|
59
|
+
|
|
60
|
+
**Yanıt:** `Content-Type: application/pdf` ile doğrudan PDF binary.
|
|
61
|
+
|
|
62
|
+
## pdf_templates Tablosu
|
|
63
|
+
|
|
64
|
+
| Kolon | Açıklama |
|
|
65
|
+
|-------|----------|
|
|
66
|
+
| `system_name` | Şablonun benzersiz tanımlayıcısı (endpoint'ten `template_name` ile eşleşir) |
|
|
67
|
+
| `name` | Şablon görünen adı |
|
|
68
|
+
| `template` | Twig + HTML şablon içeriği |
|
|
69
|
+
|
|
70
|
+
## Storage Alias Yapılandırması
|
|
71
|
+
|
|
72
|
+
PDF dosyaları `cms_file_uploads` tablosundaki alias ayarına göre kaydedilir.
|
|
73
|
+
|
|
74
|
+
**`pdf` alias yoksa oluştur:**
|
|
75
|
+
```bash
|
|
76
|
+
butterfly-cli record add cms_file_uploads --data '{
|
|
77
|
+
"alias": "pdf",
|
|
78
|
+
"path": "/static/pdf/",
|
|
79
|
+
"extensions": "pdf",
|
|
80
|
+
"cms_file_storage_id": 1
|
|
81
|
+
}'
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Kayıtlı PDF'lerin tam URL'i: `cms_file_uploads.path` + `response.filename`
|
|
85
|
+
Örnek: `/static/pdf/` + `26-03/08/harcama-3.pdf` = `/static/pdf/26-03/08/harcama-3.pdf`
|
|
86
|
+
|
|
87
|
+
## Şablon Yazma Kuralları
|
|
88
|
+
|
|
89
|
+
### Permission Bypass (KRİTİK)
|
|
90
|
+
|
|
91
|
+
`/pdf/create` ve `/pdf/view` endpoint'leri admin context dışında çalışır. Standart `db().table()` çağrıları permission hatası verir:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
"You don't have permission to view TABLE_NAME object"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Çözüm:** `db('default', false)` kullan — ikinci parametre `false` permission kontrolünü devre dışı bırakır.
|
|
98
|
+
|
|
99
|
+
```twig
|
|
100
|
+
{# ❌ YANLIŞ — permission hatası verir #}
|
|
101
|
+
{% set r = db().table('tablo').where('id', id).first() %}
|
|
102
|
+
|
|
103
|
+
{# ✅ DOĞRU — permission bypass #}
|
|
104
|
+
{% set r = db('default', false).table('tablo').where('id', id).first() %}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
> **Not:** Bu sadece PDF template'lerinde kullanılmalıdır. Normal report/object kodlarında standart `db()` kullanmaya devam edin.
|
|
108
|
+
|
|
109
|
+
### Değişken Erişimi
|
|
110
|
+
|
|
111
|
+
Template'e gönderilen tüm parametreler doğrudan Twig değişkeni olarak erişilebilir:
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
POST /pdf/create?template_name=sablonAdi&id=5&extra_param=deger
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Template içinde:
|
|
118
|
+
```twig
|
|
119
|
+
{{ id }} {# 5 #}
|
|
120
|
+
{{ extra_param }} {# deger #}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Inline CSS Zorunluluğu
|
|
124
|
+
|
|
125
|
+
PDF sunucusu harici CSS dosyalarını yüklemez. **Tüm stiller `<style>` etiketi içinde veya inline olmalıdır.**
|
|
126
|
+
|
|
127
|
+
```html
|
|
128
|
+
{# ❌ YANLIŞ — PDF'te çalışmaz #}
|
|
129
|
+
<link href="https://cdn.jsdelivr.net/npm/tailwindcss..." rel="stylesheet">
|
|
130
|
+
|
|
131
|
+
{# ✅ DOĞRU #}
|
|
132
|
+
<style>
|
|
133
|
+
body { font-family: Arial, sans-serif; font-size: 12px; }
|
|
134
|
+
.section { background: #f3f4f6; padding: 6px 12px; font-weight: 700; }
|
|
135
|
+
table { width: 100%; border-collapse: collapse; }
|
|
136
|
+
</style>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Temel Şablon Yapısı
|
|
140
|
+
|
|
141
|
+
```html
|
|
142
|
+
<html>
|
|
143
|
+
<head>
|
|
144
|
+
<meta charset="utf-8">
|
|
145
|
+
<style>
|
|
146
|
+
body { font-family: Arial, sans-serif; font-size: 12px; color: #333; margin: 20px 40px; }
|
|
147
|
+
h1 { font-size: 18px; text-align: center; }
|
|
148
|
+
.section { background: #f3f4f6; padding: 6px 12px; font-weight: 700; font-size: 13px;
|
|
149
|
+
border-left: 4px solid #ef4444; margin: 14px 0 8px; }
|
|
150
|
+
table { width: 100%; border-collapse: collapse; margin-bottom: 10px; }
|
|
151
|
+
.info-table td { padding: 4px 8px; font-size: 12px; }
|
|
152
|
+
.info-table .lbl { font-weight: 600; width: 140px; color: #555; }
|
|
153
|
+
.data-table th { background: #f3f4f6; padding: 6px 8px; text-align: left; font-size: 11px;
|
|
154
|
+
font-weight: 700; border: 1px solid #d1d5db; }
|
|
155
|
+
.data-table td { padding: 5px 8px; border: 1px solid #d1d5db; font-size: 11px; }
|
|
156
|
+
.footer { margin-top: 20px; border-top: 1px solid #d1d5db; padding-top: 8px;
|
|
157
|
+
font-size: 10px; color: #999; text-align: center; }
|
|
158
|
+
</style>
|
|
159
|
+
</head>
|
|
160
|
+
<body>
|
|
161
|
+
{% set r = db('default', false).table('ana_tablo').where('id', id).first() %}
|
|
162
|
+
{% if r is empty %}
|
|
163
|
+
<p>Kayıt bulunamadı.</p>
|
|
164
|
+
{% else %}
|
|
165
|
+
{% set children = db('default', false).table('child_tablo').where('parent_id', id).get() %}
|
|
166
|
+
|
|
167
|
+
<h1>BAŞLIK</h1>
|
|
168
|
+
|
|
169
|
+
<div class="section">Bilgiler</div>
|
|
170
|
+
<table class="info-table">
|
|
171
|
+
<tr><td class="lbl">Alan Adı</td><td>{{ r.alan_adi }}</td></tr>
|
|
172
|
+
</table>
|
|
173
|
+
|
|
174
|
+
{% if children|length > 0 %}
|
|
175
|
+
<div class="section">Alt Kayıtlar</div>
|
|
176
|
+
<table class="data-table">
|
|
177
|
+
<tr><th>Kolon 1</th><th>Kolon 2</th></tr>
|
|
178
|
+
{% for c in children %}
|
|
179
|
+
<tr><td>{{ c.kolon1 }}</td><td>{{ c.kolon2 }}</td></tr>
|
|
180
|
+
{% endfor %}
|
|
181
|
+
</table>
|
|
182
|
+
{% endif %}
|
|
183
|
+
|
|
184
|
+
<div class="footer">Bu belge sistem tarafından otomatik oluşturulmuştur.</div>
|
|
185
|
+
{% endif %}
|
|
186
|
+
</body>
|
|
187
|
+
</html>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Entegrasyon Örnekleri
|
|
191
|
+
|
|
192
|
+
### Custom Report Sayfasından Yazdır Butonu
|
|
193
|
+
|
|
194
|
+
**HTML (query_code.bfy):**
|
|
195
|
+
```html
|
|
196
|
+
{% if isEdit %}
|
|
197
|
+
<button onclick="printPdf()">
|
|
198
|
+
<i class="fas fa-print mr-1"></i> Yazdır
|
|
199
|
+
</button>
|
|
200
|
+
{% endif %}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**JS (script.js):**
|
|
204
|
+
```javascript
|
|
205
|
+
function printPdf() {
|
|
206
|
+
if (!data.isEdit || !data.recordId) {
|
|
207
|
+
rg_alert('Lütfen önce formu kaydediniz.');
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
window.open('/pdf/view?template_name=SABLON_ADI&id=' + data.recordId
|
|
211
|
+
+ '&filename=dosya-' + data.recordId + '.pdf', '_blank');
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### PDF Oluştur ve Kayda Yaz
|
|
216
|
+
|
|
217
|
+
Formu göndermeden önce PDF oluşturup dosya yolunu ana kayda yazmak için:
|
|
218
|
+
|
|
219
|
+
```javascript
|
|
220
|
+
function createPdf(recordId) {
|
|
221
|
+
return new Promise(function(resolve, reject) {
|
|
222
|
+
$.ajax({
|
|
223
|
+
url: '/pdf/create',
|
|
224
|
+
type: 'POST',
|
|
225
|
+
data: {
|
|
226
|
+
template_name: 'SABLON_ADI',
|
|
227
|
+
id: recordId,
|
|
228
|
+
filename: 'dosya-' + recordId + '.pdf',
|
|
229
|
+
alias: 'pdf'
|
|
230
|
+
},
|
|
231
|
+
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
|
232
|
+
success: function(response) {
|
|
233
|
+
if (response.success && response.filename) {
|
|
234
|
+
var pdfPath = response.full_path || '/static/pdf/' + response.filename;
|
|
235
|
+
// PDF yolunu ana kayda yaz
|
|
236
|
+
$.ajax({
|
|
237
|
+
url: '/admin/ajax/cms_object/operation?do=TABLO__edit',
|
|
238
|
+
type: 'POST',
|
|
239
|
+
data: { csrf_token: csrfToken, id: recordId, pdf_dosya: pdfPath },
|
|
240
|
+
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
|
241
|
+
success: function() { resolve(pdfPath); },
|
|
242
|
+
error: function() { resolve(pdfPath); }
|
|
243
|
+
});
|
|
244
|
+
} else {
|
|
245
|
+
rg_alert(response.message || 'PDF oluşturulamadı.');
|
|
246
|
+
reject('PDF failed');
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
error: function() {
|
|
250
|
+
rg_alert('PDF oluşturma sırasında hata oluştu.');
|
|
251
|
+
reject('PDF error');
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### State Machine Transition ile PDF Link'ini E-postaya Ekle
|
|
259
|
+
|
|
260
|
+
PDF dosyası JS tarafında oluşturulup `pdf_dosya` field'ına yazıldıktan sonra, transition action_code'unda bu değer okunabilir:
|
|
261
|
+
|
|
262
|
+
```twig
|
|
263
|
+
{# action_code.bfy #}
|
|
264
|
+
{% set tpl = db().table('cms_email_templates').where('alias', 'template-alias').first() %}
|
|
265
|
+
{% set pdfDosya = getValue('pdf_dosya') %}
|
|
266
|
+
{% set vars = {
|
|
267
|
+
'talep_no': getValue('talep_no'),
|
|
268
|
+
'ad_soyad': getValue('ad_soyad'),
|
|
269
|
+
'pdf_link': pdfDosya
|
|
270
|
+
? '<p><strong>PDF Doküman:</strong> <a href="' ~ pdfDosya ~ '" style="color:#3b82f6;">Formu İndir</a></p>'
|
|
271
|
+
: ''
|
|
272
|
+
} %}
|
|
273
|
+
{% set replacements = {} %}
|
|
274
|
+
{% for key, val in vars %}
|
|
275
|
+
{% set replacements = replacements|merge({('{{ ' ~ key ~ ' }}'): val}) %}
|
|
276
|
+
{% endfor %}
|
|
277
|
+
{{ sendEmail(recipient, tpl.subject|replace(replacements), tpl.content|replace(replacements)) }}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
E-posta template'ine `{{ pdf_link }}` placeholder'ı eklemeyi unutmayın.
|
|
281
|
+
|
|
282
|
+
### Tipik Akış (Kaydet → PDF → Gönder)
|
|
283
|
+
|
|
284
|
+
```javascript
|
|
285
|
+
function submitForm() {
|
|
286
|
+
rg_confirm('Göndermek istediğinize emin misiniz?', function() {
|
|
287
|
+
saveForm().then(function(recordId) { // 1. Formu kaydet
|
|
288
|
+
return createPdf(recordId).then(function() { // 2. PDF oluştur + kayda yaz
|
|
289
|
+
return triggerTransition(recordId, transitionId); // 3. Transition tetikle
|
|
290
|
+
});
|
|
291
|
+
}).then(function() {
|
|
292
|
+
window.location.href = '/admin/cms_report/view/DASHBOARD_ID';
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## Sıfırdan PDF Entegrasyonu Adımları
|
|
299
|
+
|
|
300
|
+
### 1. Storage Alias Oluştur (bir kez)
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
butterfly-cli record add cms_file_uploads --data '{
|
|
304
|
+
"alias": "pdf",
|
|
305
|
+
"path": "/static/pdf/",
|
|
306
|
+
"extensions": "pdf",
|
|
307
|
+
"cms_file_storage_id": 1
|
|
308
|
+
}'
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### 2. pdf_templates Objesi Oluştur (bir kez)
|
|
312
|
+
|
|
313
|
+
Eğer `pdf_templates` objesi yoksa:
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
# Object
|
|
317
|
+
butterfly-cli record add objects --data '{
|
|
318
|
+
"table_name": "pdf_templates",
|
|
319
|
+
"name": "PDF Şablonları",
|
|
320
|
+
"has_item": 0,
|
|
321
|
+
"auto_increment_column_name": "id"
|
|
322
|
+
}'
|
|
323
|
+
# Object ID'yi not al, aşağıda OBJECT_ID olarak kullan
|
|
324
|
+
|
|
325
|
+
# Field'lar
|
|
326
|
+
butterfly-cli record add object_specs --data '{"object_id":OBJECT_ID,"name":"System Name","column_name":"system_name","type":"string","required":1,"list_column":1,"edit_order_no":1}'
|
|
327
|
+
butterfly-cli record add object_specs --data '{"object_id":OBJECT_ID,"name":"Şablon Adı","column_name":"name","type":"string","required":1,"list_column":1,"edit_order_no":2}'
|
|
328
|
+
butterfly-cli record add object_specs --data '{"object_id":OBJECT_ID,"name":"HTML Şablon","column_name":"template","type":"code","edit_order_no":3}'
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### 3. Şablon Kaydı Ekle
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
butterfly-cli record add pdf_templates --data '{
|
|
335
|
+
"system_name": "sablonun-adi",
|
|
336
|
+
"name": "Şablonun Görünen Adı",
|
|
337
|
+
"template": "<html>...şablon HTML...</html>"
|
|
338
|
+
}'
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### 4. Ana Tabloya `pdf_dosya` Field Ekle (opsiyonel)
|
|
342
|
+
|
|
343
|
+
PDF dosya yolunu kayıtta saklamak istiyorsan:
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
butterfly-cli record add object_specs --data '{
|
|
347
|
+
"object_id": ANA_OBJECT_ID,
|
|
348
|
+
"name": "PDF Dosya",
|
|
349
|
+
"column_name": "pdf_dosya",
|
|
350
|
+
"type": "string",
|
|
351
|
+
"edit_order_no": 99
|
|
352
|
+
}'
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### 5. JS Fonksiyonlarını Ekle
|
|
356
|
+
|
|
357
|
+
`printPdf()` ve `createPdf()` fonksiyonlarını script.js'e ekle (yukarıdaki örneklere bakın).
|
|
358
|
+
|
|
359
|
+
### 6. Yazdır Butonunu Forma Ekle
|
|
360
|
+
|
|
361
|
+
```html
|
|
362
|
+
{% if isEdit %}
|
|
363
|
+
<button onclick="printPdf()"><i class="fas fa-print mr-1"></i> Yazdır</button>
|
|
364
|
+
{% endif %}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
## Sık Karşılaşılan Hatalar
|
|
368
|
+
|
|
369
|
+
| Hata | Neden | Çözüm |
|
|
370
|
+
|------|-------|-------|
|
|
371
|
+
| `"PDF tasarımı bulunamadı."` | `template_name` ile eşleşen `system_name` yok | `pdf_templates` tablosunu kontrol et |
|
|
372
|
+
| `"You don't have permission to view X object"` | Template'te `db()` kullanılmış | `db('default', false)` kullan |
|
|
373
|
+
| PDF oluştu ama değerler boş | Template'e parametre geçilmemiş veya `db()` sorgusu hatalı | `id` parametresinin gönderildiğini ve sorgunun doğru olduğunu kontrol et |
|
|
374
|
+
| PDF sunucu hatası (HTTP != 200) | HTML geçersiz veya PDF sunucusu erişilemiyor | HTML'i `/pdf/view` ile test et, hata mesajını incele |
|
|
375
|
+
| Stiller PDF'te görünmüyor | Harici CSS kullanılmış | Tüm stilleri `<style>` etiketi içine al, inline CSS kullan |
|
|
376
|
+
| `"Sayfa Bulunamadı"` | `/admin/pdf/create` veya `/admin/ajax/pdf/create` kullanılmış | Doğru URL: `/pdf/create` (admin prefix'siz) |
|