Added galery

This commit is contained in:
2026-01-26 10:37:06 +01:00
parent d9b4c73baa
commit 33243fbe4a
31 changed files with 2508 additions and 229 deletions

View File

@@ -124,28 +124,41 @@
background: var(--dbm-bg-tertiary);
}
.tree-header:hover .tree-toggle {
background: rgba(0, 0, 0, 0.06);
}
.tree-header.active {
background: var(--dbm-bg-tertiary);
border-left-color: var(--dbm-accent);
}
.tree-toggle {
width: 16px;
height: 16px;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 4px;
margin-right: 2px;
margin-left: -4px;
color: var(--dbm-text-muted);
transition: var(--dbm-transition);
flex-shrink: 0;
border-radius: 3px;
cursor: pointer;
}
.tree-toggle:hover {
background: var(--dbm-bg-tertiary);
color: var(--dbm-text);
}
.tree-toggle svg,
.tree-toggle [data-lucide] {
width: 10px;
height: 10px;
width: 12px;
height: 12px;
transition: transform 0.15s ease;
pointer-events: none;
}
.tree-item.expanded > .tree-header .tree-toggle svg,
@@ -505,12 +518,7 @@ a.tree-label:hover {
overflow: hidden;
}
.sql-keyword { color: #555; font-weight: 600; }
.sql-function { color: #666; }
.sql-string { color: #888; }
.sql-number { color: #777; }
.sql-operator { color: #444; }
.sql-comment { color: #aaa; font-style: italic; }
/* SQL highlighting colors moved to sql/index.php view */
.dbm-sql-actions {
display: flex;

474
public/css/gallery.css Normal file
View File

@@ -0,0 +1,474 @@
.gallery-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.gallery-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
flex-wrap: wrap;
gap: 16px;
}
.gallery-header h1 {
margin: 0;
font-size: 24px;
font-weight: 600;
color: #333;
}
.gallery-actions {
display: flex;
gap: 8px;
}
.gallery-back {
display: inline-flex;
align-items: center;
gap: 8px;
color: #666;
text-decoration: none;
font-size: 14px;
transition: color 0.2s;
}
.gallery-back:hover {
color: #333;
}
.gallery-btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 16px;
border: none;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
text-decoration: none;
transition: all 0.2s;
}
.gallery-btn-primary {
background: #333;
color: #fff;
}
.gallery-btn-primary:hover {
background: #555;
}
.gallery-btn-secondary {
background: #f0f0f0;
color: #333;
}
.gallery-btn-secondary:hover {
background: #e0e0e0;
}
.gallery-btn-danger {
background: #dc3545;
color: #fff;
}
.gallery-btn-danger:hover {
background: #c82333;
}
.gallery-btn-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border-radius: 6px;
background: rgba(255, 255, 255, 0.9);
color: #666;
text-decoration: none;
transition: all 0.2s;
}
.gallery-btn-icon:hover {
background: #fff;
color: #333;
}
.gallery-btn-icon.gallery-btn-danger:hover {
background: #dc3545;
color: #fff;
}
.gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 16px;
}
.gallery-item {
position: relative;
aspect-ratio: 1;
border-radius: 8px;
overflow: hidden;
background: #f0f0f0;
text-decoration: none;
}
.gallery-item img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s;
}
.gallery-item:hover img {
transform: scale(1.05);
}
.gallery-item-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 16px;
background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
color: #fff;
opacity: 0;
transition: opacity 0.3s;
}
.gallery-item:hover .gallery-item-overlay {
opacity: 1;
}
.gallery-item-title {
display: block;
font-weight: 500;
font-size: 14px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.gallery-item-author {
display: block;
font-size: 12px;
opacity: 0.8;
margin-top: 4px;
}
.gallery-item-visibility {
display: inline-block;
font-size: 10px;
padding: 2px 6px;
border-radius: 4px;
margin-top: 4px;
}
.gallery-item-visibility.public {
background: rgba(40, 167, 69, 0.8);
}
.gallery-item-visibility.private {
background: rgba(255, 193, 7, 0.8);
color: #333;
}
.gallery-item-owned {
position: relative;
}
.gallery-item-owned .gallery-item-actions {
position: absolute;
top: 8px;
right: 8px;
display: flex;
gap: 4px;
opacity: 0;
transition: opacity 0.2s;
}
.gallery-item-owned:hover .gallery-item-actions {
opacity: 1;
}
.gallery-empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px 20px;
text-align: center;
}
.gallery-empty-icon {
color: #ccc;
margin-bottom: 16px;
}
.gallery-empty h3 {
margin: 0 0 8px;
font-size: 18px;
color: #333;
}
.gallery-empty p {
margin: 0;
color: #666;
}
.gallery-pagination {
display: flex;
justify-content: center;
align-items: center;
gap: 16px;
margin-top: 32px;
padding-top: 24px;
border-top: 1px solid #e0e0e0;
}
.gallery-pagination-info {
font-size: 14px;
color: #666;
}
.gallery-view {
display: grid;
grid-template-columns: 1fr 350px;
gap: 32px;
margin-top: 24px;
}
@media (max-width: 900px) {
.gallery-view {
grid-template-columns: 1fr;
}
}
.gallery-view-image {
background: #f0f0f0;
border-radius: 8px;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
.gallery-view-image img {
max-width: 100%;
max-height: 70vh;
object-fit: contain;
}
.gallery-view-info h1 {
margin: 0 0 16px;
font-size: 24px;
font-weight: 600;
color: #333;
}
.gallery-view-meta {
display: flex;
flex-direction: column;
gap: 12px;
padding-bottom: 16px;
border-bottom: 1px solid #e0e0e0;
margin-bottom: 16px;
}
.gallery-view-author,
.gallery-view-date,
.gallery-view-size {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: #666;
}
.gallery-view-author a {
color: #333;
text-decoration: none;
}
.gallery-view-author a:hover {
text-decoration: underline;
}
.gallery-view-description {
font-size: 14px;
line-height: 1.6;
color: #555;
margin-bottom: 24px;
}
.gallery-view-actions {
display: flex;
gap: 8px;
}
.gallery-form-container {
max-width: 600px;
margin: 0 auto;
}
.gallery-form-container h1 {
margin: 0 0 24px;
font-size: 24px;
font-weight: 600;
color: #333;
}
.gallery-form {
display: flex;
flex-direction: column;
gap: 20px;
}
.gallery-form-group {
display: flex;
flex-direction: column;
gap: 6px;
}
.gallery-form-label {
font-size: 14px;
font-weight: 500;
color: #333;
}
.gallery-form-input,
.gallery-form-textarea {
padding: 10px 12px;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 14px;
font-family: inherit;
transition: border-color 0.2s;
}
.gallery-form-input:focus,
.gallery-form-textarea:focus {
outline: none;
border-color: #333;
}
.gallery-form-textarea {
resize: vertical;
min-height: 100px;
}
.gallery-form-checkbox {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
font-size: 14px;
color: #333;
}
.gallery-form-checkbox input {
width: 18px;
height: 18px;
cursor: pointer;
}
.gallery-form-hint {
font-size: 12px;
color: #888;
}
.gallery-form-actions {
display: flex;
gap: 8px;
margin-top: 8px;
}
.gallery-upload-zone {
position: relative;
border: 2px dashed #ddd;
border-radius: 8px;
transition: all 0.2s;
min-height: 200px;
}
.gallery-upload-zone:hover,
.gallery-upload-zone.dragover {
border-color: #333;
background: #fafafa;
}
.gallery-upload-zone input[type="file"] {
position: absolute;
inset: 0;
opacity: 0;
cursor: pointer;
}
.gallery-upload-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px 20px;
color: #888;
text-align: center;
gap: 8px;
}
.gallery-upload-placeholder small {
font-size: 12px;
color: #aaa;
}
.gallery-upload-preview {
position: relative;
display: flex;
align-items: center;
justify-content: center;
padding: 16px;
}
.gallery-upload-preview img {
max-width: 100%;
max-height: 300px;
border-radius: 4px;
}
.gallery-upload-remove {
position: absolute;
top: 8px;
right: 8px;
width: 28px;
height: 28px;
border: none;
border-radius: 50%;
background: #333;
color: #fff;
font-size: 18px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.2s;
}
.gallery-upload-remove:hover {
background: #dc3545;
}
.gallery-edit-preview {
text-align: center;
margin-bottom: 24px;
}
.gallery-edit-preview img {
max-width: 200px;
max-height: 200px;
border-radius: 8px;
object-fit: cover;
}

View File

@@ -0,0 +1,12 @@
# Deny access to all files except images
<FilesMatch "\.(?!(jpg|jpeg|png|gif|webp)$)[^.]+$">
Deny from all
</FilesMatch>
# Disable directory listing
Options -Indexes
# Disable script execution
<FilesMatch "\.(php|php5|phtml|cgi|pl|py)$">
Deny from all
</FilesMatch>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -11,27 +11,40 @@ const DBManager = {
initTree() {
document.querySelectorAll('.tree-header').forEach(header => {
// Skip if already initialized
if (header.dataset.treeInit) return;
header.dataset.treeInit = 'true';
header.addEventListener('click', (e) => {
const item = header.closest('.tree-item');
const children = item.querySelector('.tree-children');
const href = header.dataset.href;
const toggle = e.target.closest('.tree-toggle');
// If clicking on toggle icon, just expand/collapse
// If clicking on toggle icon, just expand/collapse (don't navigate)
if (toggle) {
e.preventDefault();
e.stopPropagation();
// Lazy-load tables for databases if needed
if (item.dataset.db && children && !children.dataset.loaded) {
this.loadTables(item.dataset.db, children);
}
// Lazy-load columns for tables if needed
else if (item.dataset.table && children && !children.dataset.loaded) {
// Find the parent database name
const dbItem = item.closest('.tree-item[data-db]');
if (dbItem) {
this.loadColumns(dbItem.dataset.db, item.dataset.table, children);
}
}
this.toggleTreeItem(item);
return;
}
// If it's a table or database item with href, navigate directly
// If it's a database or table item with href, navigate
if (href) {
// For databases, also load tables if not loaded
// For databases, also expand and load tables
if (item.dataset.db && children && !children.dataset.loaded) {
this.loadTables(item.dataset.db, children);
}
@@ -68,6 +81,37 @@ const DBManager = {
}
},
async loadColumns(dbName, tableName, container) {
container.innerHTML = '<div class="tree-loading">Loading...</div>';
container.dataset.loaded = 'true';
try {
const response = await fetch(`${this.baseUrl}database/getColumns/${encodeURIComponent(dbName)}/${encodeURIComponent(tableName)}`);
const data = await response.json();
if (data.success && data.columns) {
let html = '';
data.columns.forEach(col => {
html += `
<div class="tree-item">
<div class="tree-header">
<span class="tree-icon ${col.Key === 'PRI' ? 'key' : 'column'}">
${col.Key === 'PRI' ? this.icons.key : this.icons.column}
</span>
<span class="tree-label">${this.escapeHtml(col.Field)}</span>
<span class="tree-badge">${this.escapeHtml(col.Type)}</span>
</div>
</div>
`;
});
container.innerHTML = html || '<div class="tree-empty">No columns</div>';
this.refreshIcons();
}
} catch (error) {
container.innerHTML = '<div class="tree-error">Failed to load</div>';
}
},
renderTableTreeItem(dbName, tableName, columns) {
const columnsHtml = columns.map(col => `
<div class="tree-item">