diff --git a/kernel/boot/init.php b/kernel/boot/init.php index 4e8a3410..01e8b111 100644 --- a/kernel/boot/init.php +++ b/kernel/boot/init.php @@ -114,6 +114,7 @@ include(PATH_KERNEL.'page.class.php'); include(PATH_KERNEL.'url.class.php'); include(PATH_KERNEL.'login.class.php'); include(PATH_KERNEL.'parsedown.class.php'); +include(PATH_KERNEL.'parsedownextra.class.php'); include(PATH_KERNEL.'security.class.php'); // Include Helpers Classes @@ -145,7 +146,7 @@ $dbUsers = new dbUsers(); $dbTags = new dbTags(); $Site = new dbSite(); $Url = new Url(); -$Parsedown = new Parsedown(); +$Parsedown = new ParsedownExtra(); $Security = new Security(); // HTML PATHs diff --git a/kernel/parsedownextra.class.php b/kernel/parsedownextra.class.php new file mode 100644 index 00000000..be6966d2 --- /dev/null +++ b/kernel/parsedownextra.class.php @@ -0,0 +1,526 @@ +BlockTypes[':'] []= 'DefinitionList'; + $this->BlockTypes['*'] []= 'Abbreviation'; + + # identify footnote definitions before reference definitions + array_unshift($this->BlockTypes['['], 'Footnote'); + + # identify footnote markers before before links + array_unshift($this->InlineTypes['['], 'FootnoteMarker'); + } + + # + # ~ + + function text($text) + { + $markup = parent::text($text); + + # merge consecutive dl elements + + $markup = preg_replace('/<\/dl>\s+
\s+/', '', $markup); + + # add footnotes + + if (isset($this->DefinitionData['Footnote'])) + { + $Element = $this->buildFootnoteElement(); + + $markup .= "\n" . $this->element($Element); + } + + return $markup; + } + + # + # Blocks + # + + # + # Abbreviation + + protected function blockAbbreviation($Line) + { + if (preg_match('/^\*\[(.+?)\]:[ ]*(.+?)[ ]*$/', $Line['text'], $matches)) + { + $this->DefinitionData['Abbreviation'][$matches[1]] = $matches[2]; + + $Block = array( + 'hidden' => true, + ); + + return $Block; + } + } + + # + # Footnote + + protected function blockFootnote($Line) + { + if (preg_match('/^\[\^(.+?)\]:[ ]?(.*)$/', $Line['text'], $matches)) + { + $Block = array( + 'label' => $matches[1], + 'text' => $matches[2], + 'hidden' => true, + ); + + return $Block; + } + } + + protected function blockFootnoteContinue($Line, $Block) + { + if ($Line['text'][0] === '[' and preg_match('/^\[\^(.+?)\]:/', $Line['text'])) + { + return; + } + + if (isset($Block['interrupted'])) + { + if ($Line['indent'] >= 4) + { + $Block['text'] .= "\n\n" . $Line['text']; + + return $Block; + } + } + else + { + $Block['text'] .= "\n" . $Line['text']; + + return $Block; + } + } + + protected function blockFootnoteComplete($Block) + { + $this->DefinitionData['Footnote'][$Block['label']] = array( + 'text' => $Block['text'], + 'count' => null, + 'number' => null, + ); + + return $Block; + } + + # + # Definition List + + protected function blockDefinitionList($Line, $Block) + { + if ( ! isset($Block) or isset($Block['type'])) + { + return; + } + + $Element = array( + 'name' => 'dl', + 'handler' => 'elements', + 'text' => array(), + ); + + $terms = explode("\n", $Block['element']['text']); + + foreach ($terms as $term) + { + $Element['text'] []= array( + 'name' => 'dt', + 'handler' => 'line', + 'text' => $term, + ); + } + + $Block['element'] = $Element; + + $Block = $this->addDdElement($Line, $Block); + + return $Block; + } + + protected function blockDefinitionListContinue($Line, array $Block) + { + if ($Line['text'][0] === ':') + { + $Block = $this->addDdElement($Line, $Block); + + return $Block; + } + else + { + if (isset($Block['interrupted']) and $Line['indent'] === 0) + { + return; + } + + if (isset($Block['interrupted'])) + { + $Block['dd']['handler'] = 'text'; + $Block['dd']['text'] .= "\n\n"; + + unset($Block['interrupted']); + } + + $text = substr($Line['body'], min($Line['indent'], 4)); + + $Block['dd']['text'] .= "\n" . $text; + + return $Block; + } + } + + # + # Header + + protected function blockHeader($Line) + { + $Block = parent::blockHeader($Line); + + if (preg_match('/[ #]*{('.$this->regexAttribute.'+)}[ ]*$/', $Block['element']['text'], $matches, PREG_OFFSET_CAPTURE)) + { + $attributeString = $matches[1][0]; + + $Block['element']['attributes'] = $this->parseAttributeData($attributeString); + + $Block['element']['text'] = substr($Block['element']['text'], 0, $matches[0][1]); + } + + return $Block; + } + + # + # Markup + + protected function blockMarkupComplete($Block) + { + if ( ! isset($Block['void'])) + { + $Block['markup'] = $this->processTag($Block['markup']); + } + + return $Block; + } + + # + # Setext + + protected function blockSetextHeader($Line, array $Block = null) + { + $Block = parent::blockSetextHeader($Line, $Block); + + if (preg_match('/[ ]*{('.$this->regexAttribute.'+)}[ ]*$/', $Block['element']['text'], $matches, PREG_OFFSET_CAPTURE)) + { + $attributeString = $matches[1][0]; + + $Block['element']['attributes'] = $this->parseAttributeData($attributeString); + + $Block['element']['text'] = substr($Block['element']['text'], 0, $matches[0][1]); + } + + return $Block; + } + + # + # Inline Elements + # + + # + # Footnote Marker + + protected function inlineFootnoteMarker($Excerpt) + { + if (preg_match('/^\[\^(.+?)\]/', $Excerpt['text'], $matches)) + { + $name = $matches[1]; + + if ( ! isset($this->DefinitionData['Footnote'][$name])) + { + return; + } + + $this->DefinitionData['Footnote'][$name]['count'] ++; + + if ( ! isset($this->DefinitionData['Footnote'][$name]['number'])) + { + $this->DefinitionData['Footnote'][$name]['number'] = ++ $this->footnoteCount; # » & + } + + $Element = array( + 'name' => 'sup', + 'attributes' => array('id' => 'fnref'.$this->DefinitionData['Footnote'][$name]['count'].':'.$name), + 'handler' => 'element', + 'text' => array( + 'name' => 'a', + 'attributes' => array('href' => '#fn:'.$name, 'class' => 'footnote-ref'), + 'text' => $this->DefinitionData['Footnote'][$name]['number'], + ), + ); + + return array( + 'extent' => strlen($matches[0]), + 'element' => $Element, + ); + } + } + + private $footnoteCount = 0; + + # + # Link + + protected function inlineLink($Excerpt) + { + $Link = parent::inlineLink($Excerpt); + + $remainder = substr($Excerpt['text'], $Link['extent']); + + if (preg_match('/^[ ]*{('.$this->regexAttribute.'+)}/', $remainder, $matches)) + { + $Link['element']['attributes'] += $this->parseAttributeData($matches[1]); + + $Link['extent'] += strlen($matches[0]); + } + + return $Link; + } + + # + # ~ + # + + protected function unmarkedText($text) + { + $text = parent::unmarkedText($text); + + if (isset($this->DefinitionData['Abbreviation'])) + { + foreach ($this->DefinitionData['Abbreviation'] as $abbreviation => $meaning) + { + $pattern = '/\b'.preg_quote($abbreviation, '/').'\b/'; + + $text = preg_replace($pattern, ''.$abbreviation.'', $text); + } + } + + return $text; + } + + # + # Util Methods + # + + protected function addDdElement(array $Line, array $Block) + { + $text = substr($Line['text'], 1); + $text = trim($text); + + unset($Block['dd']); + + $Block['dd'] = array( + 'name' => 'dd', + 'handler' => 'line', + 'text' => $text, + ); + + if (isset($Block['interrupted'])) + { + $Block['dd']['handler'] = 'text'; + + unset($Block['interrupted']); + } + + $Block['element']['text'] []= & $Block['dd']; + + return $Block; + } + + protected function buildFootnoteElement() + { + $Element = array( + 'name' => 'div', + 'attributes' => array('class' => 'footnotes'), + 'handler' => 'elements', + 'text' => array( + array( + 'name' => 'hr', + ), + array( + 'name' => 'ol', + 'handler' => 'elements', + 'text' => array(), + ), + ), + ); + + uasort($this->DefinitionData['Footnote'], 'self::sortFootnotes'); + + foreach ($this->DefinitionData['Footnote'] as $definitionId => $DefinitionData) + { + if ( ! isset($DefinitionData['number'])) + { + continue; + } + + $text = $DefinitionData['text']; + + $text = parent::text($text); + + $numbers = range(1, $DefinitionData['count']); + + $backLinksMarkup = ''; + + foreach ($numbers as $number) + { + $backLinksMarkup .= ' '; + } + + $backLinksMarkup = substr($backLinksMarkup, 1); + + if (substr($text, - 4) === '

') + { + $backLinksMarkup = ' '.$backLinksMarkup; + + $text = substr_replace($text, $backLinksMarkup.'

', - 4); + } + else + { + $text .= "\n".'

'.$backLinksMarkup.'

'; + } + + $Element['text'][1]['text'] []= array( + 'name' => 'li', + 'attributes' => array('id' => 'fn:'.$definitionId), + 'text' => "\n".$text."\n", + ); + } + + return $Element; + } + + # ~ + + protected function parseAttributeData($attributeString) + { + $Data = array(); + + $attributes = preg_split('/[ ]+/', $attributeString, - 1, PREG_SPLIT_NO_EMPTY); + + foreach ($attributes as $attribute) + { + if ($attribute[0] === '#') + { + $Data['id'] = substr($attribute, 1); + } + else # "." + { + $classes []= substr($attribute, 1); + } + } + + if (isset($classes)) + { + $Data['class'] = implode(' ', $classes); + } + + return $Data; + } + + # ~ + + protected function processTag($elementMarkup) # recursive + { + # http://stackoverflow.com/q/1148928/200145 + libxml_use_internal_errors(true); + + $DOMDocument = new DOMDocument; + + # http://stackoverflow.com/q/11309194/200145 + $elementMarkup = mb_convert_encoding($elementMarkup, 'HTML-ENTITIES', 'UTF-8'); + + # http://stackoverflow.com/q/4879946/200145 + $DOMDocument->loadHTML($elementMarkup); + $DOMDocument->removeChild($DOMDocument->doctype); + $DOMDocument->replaceChild($DOMDocument->firstChild->firstChild->firstChild, $DOMDocument->firstChild); + + $elementText = ''; + + if ($DOMDocument->documentElement->getAttribute('markdown') === '1') + { + foreach ($DOMDocument->documentElement->childNodes as $Node) + { + $elementText .= $DOMDocument->saveHTML($Node); + } + + $DOMDocument->documentElement->removeAttribute('markdown'); + + $elementText = "\n".$this->text($elementText)."\n"; + } + else + { + foreach ($DOMDocument->documentElement->childNodes as $Node) + { + $nodeMarkup = $DOMDocument->saveHTML($Node); + + if ($Node instanceof DOMElement and ! in_array($Node->nodeName, $this->textLevelElements)) + { + $elementText .= $this->processTag($nodeMarkup); + } + else + { + $elementText .= $nodeMarkup; + } + } + } + + # because we don't want for markup to get encoded + $DOMDocument->documentElement->nodeValue = 'placeholder\x1A'; + + $markup = $DOMDocument->saveHTML($DOMDocument->documentElement); + $markup = str_replace('placeholder\x1A', $elementText, $markup); + + return $markup; + } + + # ~ + + protected function sortFootnotes($A, $B) # callback + { + return $A['number'] - $B['number']; + } + + # + # Fields + # + + protected $regexAttribute = '(?:[#.][-\w]+[ ]*)'; +} diff --git a/languages/he_IL.json b/languages/he_IL.json new file mode 100644 index 00000000..570b8fa3 --- /dev/null +++ b/languages/he_IL.json @@ -0,0 +1,214 @@ +{ + "language-data": + { + "native": "עברית", + "english-name": "Hebrew", + "last-update": "2015-11-16", + "author": "hxii", + "email": "paul@paulglushak.com", + "website": "http://paulglushak.com" + }, + + "username": "שם משתמש", + "password": "סיסמא", + "confirm-password": "אימות סיסמא", + "editor": "עורך", + "dashboard": "לוח מחוונים", + "role": "תפקיד", + "post": "הודעה", + "posts": "הודעות", + "users": "משתמשים", + "administrator": "מנהל", + "add": "הוסף", + "cancel": "ביטול", + "content": "תוכן", + "title": "כותרת", + "no-parent": "אין הורה", + "edit-page": "ערוך דף", + "edit-post": "ערוך הודעה", + "add-a-new-user": "הוספת משתמש חדש", + "parent": "הורה", + "friendly-url": "כתובת URL", + "description": "תאור", + "posted-by": "נכתב על ידי", + "tags": "תגיות", + "position": "מיקום", + "save": "שמור", + "draft": "טיוטה", + "delete": "מחק", + "registered": "רשום", + "Notifications": "התראות", + "profile": "פרופיל", + "email": "דואל", + "settings": "הגדרות", + "general": "כללי", + "advanced": "מתקדם", + "regional": "אזורי", + "about": "אודות", + "login": "כניסה", + "logout": "יציאה", + "manage": "ניהול", + "themes": "ערכות נושא", + "prev-page": "דף קודם", + "next-page": "דף הבא", + "configure-plugin": "הגדרת תוסף", + "confirm-delete-this-action-cannot-be-undone": "אתה בטוח שברצונך למחוק? לא ניתן לבטל פעולה זו.", + "site-title": "כותרת האתר", + "site-slogan": "סלוגן האתר", + "site-description": "תאור האתר", + "footer-text": "טקסט תחתון", + "posts-per-page": "הודעות לדף", + "site-url": "כתובת האתר", + "writting-settings": "הגדרות כתיבה", + "url-filters": "URL filters", + "page": "דף", + "pages": "דפים", + "home": "בית", + "welcome-back": "ברוכ\ה השב\ה", + "language": "שפה", + "website": "אתר", + "timezone": "אזור זמן", + "locale": "שפה", + "new-post": "הודעה חדשה", + "new-page": "דף חדש", + "html-and-markdown-code-supported": "HTML ו-Markdown נתמך", + "manage-posts": "ניהול הודעות", + "published-date": "תאריך פרסום", + "modified-date": "תאריך שינוי", + "empty-title": "כותרת ריקה", + "plugins": "תוספים", + "install-plugin": "התקנת תוסף", + "uninstall-plugin": "מחיקת תוסף", + "new-password": "סיסמא חדשה", + "edit-user": "עריכת משתמש", + "publish-now": "פרסם עכשיו", + "first-name": "שם פרטי", + "last-name": "שם משפחה", + "bludit-version": "גרסת Bludit", + "powered-by": "מופעל על ידי", + "recent-posts": "הודעות אחרונות", + "manage-pages": "ניהול דפים", + "advanced-options": "הגדרות מתקדמות", + "user-deleted": "המשתמש נמחק", + "page-added-successfully": "הדף הוסף בהצלחה", + "post-added-successfully": "ההודעה הוספה בהצלחה", + "the-post-has-been-deleted-successfully": "ההודעה נמחקה", + "the-page-has-been-deleted-successfully": "הדף נמחק", + "username-or-password-incorrect": "שם משתמש או סיסמא אינם נכונים", + "database-regenerated": "מסד הנתונים חודש", + "the-changes-have-been-saved": "ההגדרות נשמרו", + "enable-more-features-at": "אפשר יותר תכונות ב", + "username-already-exists": "שם משתמש כבר קיים", + "username-field-is-empty": "שדה שם המשתמש ריק", + "the-password-and-confirmation-password-do-not-match":"הסיסמאות אינן זהות", + "user-has-been-added-successfully": "המשתמש הוסף בהצלחה", + "you-do-not-have-sufficient-permissions": "אין לך הרשאות מתאימות לצפייה בדף זה. אנא צור קשר עם המנהל.", + "settings-advanced-writting-settings": "הגדרות->מתקדם->הגדרות כתיבה", + "new-posts-and-pages-synchronized": "הודעות ודפים חדשים סונכרנו.", + "you-can-choose-the-users-privilege": "באפשרותך להגדיר את הרשאות המשתמש. רק העורך יכול לכתוב הודעות וליצור דפים.", + "email-will-not-be-publicly-displayed": "כתובת הדואל לא תוצג. נדרשת עבור איפוס הסיסמא ועבור התראות.", + "use-this-field-to-name-your-site": "השתמש בשדה זה בכדי לתת שם לאתר שלך. השם יופיע בראשו של כל דף.", + "use-this-field-to-add-a-catchy-phrase": "השתמש בשדה זה בכדי לתת לאתרך סלוגן.", + "you-can-add-a-site-description-to-provide": "השתמש בשדה זה בכדי לספר בקצרה על אתרך.", + "you-can-add-a-small-text-on-the-bottom": "באפשרותך להוסיף טקסט קצר בתחתית העמוד.", + "number-of-posts-to-show-per-page": "מספר ההודעות שיש להראות בכל עמוד", + "the-url-of-your-site": "כתובת ה-URL של האתר", + "add-or-edit-description-tags-or": "הוסף או שנה את התאור, תגיות ואת כתובת ה-URL.", + "select-your-sites-language": "בחר את שפת האתר", + "select-a-timezone-for-a-correct": "בחר את אזור הזמן בו אתה נמצא", + "you-can-use-this-field-to-define-a-set-of": "באפשרותך להתמש בשדה זה בכדי להגדיר פרמטרים הקשורים לשפה, מדינה והגדרות נוספות.", + "you-can-modify-the-url-which-identifies":"באפשרותך לשנות את כתובת ה-URL של הודעה או דף בכדי שיהיו קריאים. עד 150 תוים.", + "this-field-can-help-describe-the-content": "בשדה זה ניתן לרשום תאור קצר של התוכן. עד 150 תוים.", + + "delete-the-user-and-all-its-posts":"מחק את המשתמש ואת כל הודעותיו", + "delete-the-user-and-associate-its-posts-to-admin-user": "מחק את המשתמש והעבר את כל הודעותיו למנהל", + "read-more": "המשך לקרוא", + "show-blog": "הראה בלוג", + "default-home-page": "דף בית ברירת מחדל", + "version": "גרסא", + "there-are-no-drafts": "לא נמצא טיוטות.", + "create-a-new-article-for-your-blog":"צור הודעה חדשה לבלוג שלך.", + "create-a-new-page-for-your-website":"צור דף חדש לאתר שלך.", + "invite-a-friend-to-collaborate-on-your-website":"הזמן חבר להשתתף באתרך.", + "change-your-language-and-region-settings":"שינוי הגדרות שפה ומיקום.", + "language-and-timezone":"שפה ואזור זמן", + "author": "כותב", + "start-here": "התחל כאן", + "install-theme": "התקנת ערכת נושא", + "first-post": "הודעה ראשונה", + "congratulations-you-have-successfully-installed-your-bludit": "**Bludit** הותקן בהצלחה!", + "whats-next": "מה הלאה?", + "manage-your-bludit-from-the-admin-panel": "נהל את Bludit מ[דף הניהול](./admin/)", + "follow-bludit-on": "עקוב אחר Bludit", + "visit-the-support-forum": "בקר ב[פורום](http://forum.bludit.com) לתמיכה", + "read-the-documentation-for-more-information": "קרא את ה[מסמכים](http://docs.bludit.com) למידע נוסף", + "share-with-your-friends-and-enjoy": "שתף עם חבריך", + "the-page-has-not-been-found": "הדף לא נמצא.", + "error": "שגיאה", + "bludit-installer": "התקנת Bludit", + "welcome-to-the-bludit-installer": "ברוכים הבאים להתקנת Bludit", + "complete-the-form-choose-a-password-for-the-username-admin": "מלא את הפרטים ובחר סיסמא למשתמש « admin »", + "password-visible-field": "סיסמא, שדה גלוי", + "install": "התקנה", + "choose-your-language": "בחר שאת שפתך", + "next": "הבא", + "the-password-field-is-empty": "שדה הסיסמא ריק", + "your-email-address-is-invalid":"כתובת הדואל אינה נכונה", + "proceed-anyway": "המשך בכל זאת", + "drafts":"טיוטות", + "ip-address-has-been-blocked": "כתובת IP נחסמה", + "try-again-in-a-few-minutes": "אנא נסה בעוד מספר דקות", + "date": "תאריך", + + "scheduled": "מתוכנן", + "publish": "פרסם", + "please-check-your-theme-configuration": "אנא בדוק את הגדרות ערכת הנושא.", + "plugin-label": "תווית תוסף", + "enabled": "מאופשר", + "disabled": "מבוטל", + "cli-mode": "מצב CLI", + "command-line-mode": "Command Line Mode", + "enable-the-command-line-mode-if-you-add-edit": "אפשר את מצב CLI אם ברצונך ליצור, לשנות ולמחוק הודעות ודפים דרך המערכת", + + "configure": "הגדרה", + "uninstall": "ביטול התקנה", + "change-password": "שינוי סיסמא", + "to-schedule-the-post-just-select-the-date-and-time": "בכדי לתכנן הודעה, הגדר את הזמן והתאריך.", + "write-the-tags-separated-by-commas": "רשום תוויות מופרדות בפסיק", + "status": "סטטוס", + "published": "מפורסם", + "scheduled-posts": "הודעות מתוכננות", + "statistics": "סטטיסטיקה", + "name": "שם", + "email-account-settings":"הגדרות חשבון דואל", + "sender-email": "דואל שולח", + "emails-will-be-sent-from-this-address":"הודעות דואל ישלחו מכתובת זו", + "bludit-login-access-code": "BLUDIT - קוד גישת כניסה", + "check-your-inbox-for-your-login-access-code":"אנא בדוק בתיבת הדואל שלך לקוד גישה", + "there-was-a-problem-sending-the-email":"הייתה בעיה בשליחת המייל", + "back-to-login-form": "חזרה לדף כניסה", + "send-me-a-login-access-code": "שלח לי קוד גישה", + "get-login-access-code": "קבל קוד גישה", + "email-notification-login-access-code": "

זוהי התראה מאתרך {{WEBSITE_NAME}}

לקבלת קוד גישה, אנא לחץ על הקישור:

{{LINK}}

", + "there-are-no-scheduled-posts": "אין הודעות מתוכננות.", + "show-password": "הראה סיסמא", + "edit-or-remove-your=pages": "ערוך או מחק דפים", + "edit-or-remove-your-blogs-posts": "ערוך או מחק הודעות.", + "general-settings": "הגדרות כלליות", + "advanced-settings": "הגדרות מתקדמות", + "manage-users": "ניהול משתמשים", + "view-and-edit-your-profile": "ראה וערוך פרופיל אישי", + + "password-must-be-at-least-6-characters-long": "הסיסמא חייבת להיות בעלת 5 תויים לפחות", + "images": "תמונות", + "upload-image": "העלה תמונה", + "drag-and-drop-or-click-here": "גרור תמונה או לחץ כאן", + "insert-image": "הכנס תמונה", + "supported-image-file-types": "סוגי קבצי תמונה נתמכים", + "date-format": "פורמט תאריך", + "time-format": "פורמט שעה", + "chat-with-developers-and-users-on-gitter":"שוחח עם מפתחים ומשתמשים ב[Gitter](https://gitter.im/dignajar/bludit)", + + "this-is-a-brief-description-of-yourself-our-your-blog":"זהו תאור קצר עליך או על אתרך. בכדי לשנות אותו נווט לדף הניהול->הגדרות->תוספים והגדר את תוסף about", + "profile-picture": "תמונת פרופיל" +}